Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cdb1535fa7 | ||
|
|
68bb6ab654 | ||
|
|
835fbb9ef6 | ||
|
|
9c6ca258e1 | ||
|
|
d573f771a8 | ||
|
|
035a219f9f | ||
|
|
19069f50ec | ||
|
|
5de21916a3 | ||
|
|
3d5a512d1e | ||
|
|
9f7a158367 | ||
|
|
1681de11d2 | ||
|
|
88d3be685e |
@@ -0,0 +1,33 @@
|
|||||||
|
name: Go
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
schedule:
|
||||||
|
# Run every 12 hours, at the 15 minute mark. E.g.
|
||||||
|
# 2020-11-29 00:15:00 UTC, 2020-11-29 12:15:00 UTC, 2020-11-30 00:15:00 UTC
|
||||||
|
- cron: '15 */12 * * *'
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
build:
|
||||||
|
name: Build and Unit Test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: ^1.13
|
||||||
|
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: go build -v ./...
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: go test -v -race -covermode=atomic -coverprofile=coverage.out ./...
|
||||||
|
|
||||||
|
- name: Upload Coverage
|
||||||
|
uses: shogo82148/actions-goveralls@v1
|
||||||
|
with:
|
||||||
|
path-to-profile: coverage.out
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
name: golangci-lint
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- v*
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
schedule:
|
||||||
|
# Run every 12 hours, at the 15 minute mark. E.g.
|
||||||
|
# 2020-11-29 00:15:00 UTC, 2020-11-29 12:15:00 UTC, 2020-11-30 00:15:00 UTC
|
||||||
|
- cron: '15 */12 * * *'
|
||||||
|
jobs:
|
||||||
|
golangci:
|
||||||
|
name: Lint Sourcecode
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Run golangci-lint
|
||||||
|
uses: golangci/golangci-lint-action@v2
|
||||||
|
with:
|
||||||
|
version: v1.35
|
||||||
-13
@@ -1,13 +0,0 @@
|
|||||||
language: go
|
|
||||||
go:
|
|
||||||
- 1.13
|
|
||||||
env:
|
|
||||||
- "PATH=/home/travis/gopath/bin:$PATH"
|
|
||||||
before_install:
|
|
||||||
- go get golang.org/x/lint/golint
|
|
||||||
- go get github.com/mattn/goveralls
|
|
||||||
script:
|
|
||||||
- go vet
|
|
||||||
- golint -set_exit_status
|
|
||||||
- go test -race -v
|
|
||||||
- $HOME/gopath/bin/goveralls -ignore main.go -v -service=travis-ci
|
|
||||||
+5
-4
@@ -1,16 +1,17 @@
|
|||||||
FROM golang:1.13-alpine AS builder
|
FROM golang:alpine AS builder
|
||||||
LABEL maintainer="joona@kuori.org"
|
LABEL maintainer="joona@kuori.org"
|
||||||
|
|
||||||
RUN apk add --update gcc musl-dev git
|
RUN apk add --update gcc musl-dev git
|
||||||
|
|
||||||
RUN go get github.com/joohoi/acme-dns
|
ENV GOPATH /tmp/buildcache
|
||||||
WORKDIR /go/src/github.com/joohoi/acme-dns
|
RUN git clone https://github.com/joohoi/acme-dns /tmp/acme-dns
|
||||||
|
WORKDIR /tmp/acme-dns
|
||||||
RUN CGO_ENABLED=1 go build
|
RUN CGO_ENABLED=1 go build
|
||||||
|
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
|
|
||||||
WORKDIR /root/
|
WORKDIR /root/
|
||||||
COPY --from=builder /go/src/github.com/joohoi/acme-dns .
|
COPY --from=builder /tmp/acme-dns .
|
||||||
RUN mkdir -p /etc/acme-dns
|
RUN mkdir -p /etc/acme-dns
|
||||||
RUN mkdir -p /var/lib/acme-dns
|
RUN mkdir -p /var/lib/acme-dns
|
||||||
RUN rm -rf ./config.cfg
|
RUN rm -rf ./config.cfg
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ Acme-dns provides a simple API exclusively for TXT record updates and should be
|
|||||||
|
|
||||||
So basically it boils down to **accessibility** and **security**.
|
So basically it boils down to **accessibility** and **security**.
|
||||||
|
|
||||||
|
For longer explanation of the underlying issue and other proposed solutions, see a blog post on the topic from EFF deeplinks blog: https://www.eff.org/deeplinks/2018/02/technical-deep-dive-securing-automation-acme-dns-challenge-validation
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
- Simplified DNS server, serving your ACME DNS challenges (TXT)
|
- Simplified DNS server, serving your ACME DNS challenges (TXT)
|
||||||
- Custom records (have your required A, AAAA, NS, etc. records served)
|
- Custom records (have your required A, AAAA, NS, etc. records served)
|
||||||
@@ -23,7 +25,7 @@ So basically it boils down to **accessibility** and **security**.
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
A Certbot authentication hook for acme-dns is available at: [https://github.com/joohoi/acme-dns-certbot](https://github.com/joohoi/acme-dns-certbot).
|
A client application for acme-dns with support for Certbot authentication hooks is available at: [https://github.com/acme-dns/acme-dns-client](https://github.com/acme-dns/acme-dns-client).
|
||||||
|
|
||||||
[](https://asciinema.org/a/94903)
|
[](https://asciinema.org/a/94903)
|
||||||
|
|
||||||
@@ -108,22 +110,6 @@ The method can be used to check readiness and/or liveness of the server. It will
|
|||||||
|
|
||||||
```GET /health```
|
```GET /health```
|
||||||
|
|
||||||
#### Example using a Kubernetes deployment
|
|
||||||
|
|
||||||
```
|
|
||||||
# ...
|
|
||||||
readinessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /health
|
|
||||||
port: 80
|
|
||||||
periodSeconds: 2
|
|
||||||
initialDelaySeconds: 2
|
|
||||||
failureThreshold: 3
|
|
||||||
successThreshold: 1
|
|
||||||
livenessProbe:
|
|
||||||
# same as for readinessProbe...
|
|
||||||
```
|
|
||||||
|
|
||||||
## Self-hosted
|
## Self-hosted
|
||||||
|
|
||||||
You are encouraged to run your own acme-dns instance, because you are effectively authorizing the acme-dns server to act on your behalf in providing the answer to the challenging CA, making the instance able to request (and get issued) a TLS certificate for the domain that has CNAME pointing to it.
|
You are encouraged to run your own acme-dns instance, because you are effectively authorizing the acme-dns server to act on your behalf in providing the answer to the challenging CA, making the instance able to request (and get issued) a TLS certificate for the domain that has CNAME pointing to it.
|
||||||
@@ -135,11 +121,20 @@ See the INSTALL section for information on how to do this.
|
|||||||
|
|
||||||
1) Install [Go 1.13 or newer](https://golang.org/doc/install).
|
1) Install [Go 1.13 or newer](https://golang.org/doc/install).
|
||||||
|
|
||||||
2) Install acme-dns: `go get github.com/joohoi/acme-dns/...`. This will install acme-dns to `~/go/bin/acme-dns`.
|
2) Build acme-dns:
|
||||||
|
```
|
||||||
|
git clone https://github.com/joohoi/acme-dns
|
||||||
|
cd acme-dns
|
||||||
|
export GOPATH=/tmp/acme-dns
|
||||||
|
go build
|
||||||
|
```
|
||||||
|
|
||||||
3) Edit config.cfg to suit your needs (see [configuration](#configuration)). `acme-dns` will read the configuration file from `/etc/acme-dns/config.cfg` or `./config.cfg`, or a location specified with the `-c` flag.
|
3) Move the built acme-dns binary to a directory in your $PATH, for example:
|
||||||
|
`sudo mv acme-dns /usr/local/bin`
|
||||||
|
|
||||||
4) If your system has systemd, you can optionally install acme-dns as a service so that it will start on boot and be tracked by systemd. This also allows us to add the `CAP_NET_BIND_SERVICE` capability so that acme-dns can be run by a user other than root.
|
4) Edit config.cfg to suit your needs (see [configuration](#configuration)). `acme-dns` will read the configuration file from `/etc/acme-dns/config.cfg` or `./config.cfg`, or a location specified with the `-c` flag.
|
||||||
|
|
||||||
|
5) If your system has systemd, you can optionally install acme-dns as a service so that it will start on boot and be tracked by systemd. This also allows us to add the `CAP_NET_BIND_SERVICE` capability so that acme-dns can be run by a user other than root.
|
||||||
|
|
||||||
1) Make sure that you have moved the configuration file to `/etc/acme-dns/config.cfg` so that acme-dns can access it globally.
|
1) Make sure that you have moved the configuration file to `/etc/acme-dns/config.cfg` so that acme-dns can access it globally.
|
||||||
|
|
||||||
@@ -155,7 +150,7 @@ See the INSTALL section for information on how to do this.
|
|||||||
|
|
||||||
7) Run acme-dns: `sudo systemctl start acme-dns.service`.
|
7) Run acme-dns: `sudo systemctl start acme-dns.service`.
|
||||||
|
|
||||||
5) If you did not install the systemd service, run `acme-dns`. Please note that acme-dns needs to open a privileged port (53, domain), so it needs to be run with elevated privileges.
|
6) If you did not install the systemd service, run `acme-dns`. Please note that acme-dns needs to open a privileged port (53, domain), so it needs to be run with elevated privileges.
|
||||||
|
|
||||||
### Using Docker
|
### Using Docker
|
||||||
|
|
||||||
@@ -211,7 +206,7 @@ You may want to test that acme-dns is working before using it for real queries.
|
|||||||
|
|
||||||
2) Call the `/register` API endpoint to register a test domain:
|
2) Call the `/register` API endpoint to register a test domain:
|
||||||
```
|
```
|
||||||
$ curl -X POST http://auth.example.org/register
|
$ curl -X POST https://auth.example.org/register
|
||||||
{"username":"eabcdb41-d89f-4580-826f-3e62e9755ef2","password":"pbAXVjlIOE01xbut7YnAbkhMQIkcwoHO0ek2j4Q0","fulldomain":"d420c923-bbd7-4056-ab64-c3ca54c9b3cf.auth.example.org","subdomain":"d420c923-bbd7-4056-ab64-c3ca54c9b3cf","allowfrom":[]}
|
{"username":"eabcdb41-d89f-4580-826f-3e62e9755ef2","password":"pbAXVjlIOE01xbut7YnAbkhMQIkcwoHO0ek2j4Q0","fulldomain":"d420c923-bbd7-4056-ab64-c3ca54c9b3cf.auth.example.org","subdomain":"d420c923-bbd7-4056-ab64-c3ca54c9b3cf","allowfrom":[]}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -221,7 +216,7 @@ $ curl -X POST \
|
|||||||
-H "X-Api-User: eabcdb41-d89f-4580-826f-3e62e9755ef2" \
|
-H "X-Api-User: eabcdb41-d89f-4580-826f-3e62e9755ef2" \
|
||||||
-H "X-Api-Key: pbAXVjlIOE01xbut7YnAbkhMQIkcwoHO0ek2j4Q0" \
|
-H "X-Api-Key: pbAXVjlIOE01xbut7YnAbkhMQIkcwoHO0ek2j4Q0" \
|
||||||
-d '{"subdomain": "d420c923-bbd7-4056-ab64-c3ca54c9b3cf", "txt": "___validation_token_received_from_the_ca___"}' \
|
-d '{"subdomain": "d420c923-bbd7-4056-ab64-c3ca54c9b3cf", "txt": "___validation_token_received_from_the_ca___"}' \
|
||||||
http://auth.example.org/update
|
https://auth.example.org/update
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: The `txt` field must be exactly 43 characters long, otherwise acme-dns will reject it
|
Note: The `txt` field must be exactly 43 characters long, otherwise acme-dns will reject it
|
||||||
@@ -279,6 +274,8 @@ tls_cert_privkey = "/etc/tls/example.org/privkey.pem"
|
|||||||
tls_cert_fullchain = "/etc/tls/example.org/fullchain.pem"
|
tls_cert_fullchain = "/etc/tls/example.org/fullchain.pem"
|
||||||
# only used if tls = "letsencrypt"
|
# only used if tls = "letsencrypt"
|
||||||
acme_cache_dir = "api-certs"
|
acme_cache_dir = "api-certs"
|
||||||
|
# optional e-mail address to which Let's Encrypt will send expiration notices for the API's cert
|
||||||
|
notification_email = ""
|
||||||
# CORS AllowOrigins, wildcards can be used
|
# CORS AllowOrigins, wildcards can be used
|
||||||
corsorigins = [
|
corsorigins = [
|
||||||
"*"
|
"*"
|
||||||
@@ -327,10 +324,11 @@ use for the renewal.
|
|||||||
- Posh-ACME: [https://github.com/rmbolger/Posh-ACME](https://github.com/rmbolger/Posh-ACME)
|
- Posh-ACME: [https://github.com/rmbolger/Posh-ACME](https://github.com/rmbolger/Posh-ACME)
|
||||||
- Sewer: [https://github.com/komuw/sewer](https://github.com/komuw/sewer)
|
- Sewer: [https://github.com/komuw/sewer](https://github.com/komuw/sewer)
|
||||||
- Traefik: [https://github.com/containous/traefik](https://github.com/containous/traefik)
|
- Traefik: [https://github.com/containous/traefik](https://github.com/containous/traefik)
|
||||||
- Windows ACME Simple (WACS): [https://github.com/PKISharp/win-acme](https://github.com/PKISharp/win-acme)
|
- Windows ACME Simple (WACS): [https://www.win-acme.com](https://www.win-acme.com)
|
||||||
|
|
||||||
### Authentication hooks
|
### Authentication hooks
|
||||||
|
|
||||||
|
- acme-dns-client with Certbot authentication hook: [https://github.com/acme-dns/acme-dns-client](https://github.com/acme-dns/acme-dns-client)
|
||||||
- Certbot authentication hook in Python: [https://github.com/joohoi/acme-dns-certbot-joohoi](https://github.com/joohoi/acme-dns-certbot-joohoi)
|
- Certbot authentication hook in Python: [https://github.com/joohoi/acme-dns-certbot-joohoi](https://github.com/joohoi/acme-dns-certbot-joohoi)
|
||||||
- Certbot authentication hook in Go: [https://github.com/koesie10/acme-dns-certbot-hook](https://github.com/koesie10/acme-dns-certbot-hook)
|
- Certbot authentication hook in Go: [https://github.com/koesie10/acme-dns-certbot-hook](https://github.com/koesie10/acme-dns-certbot-hook)
|
||||||
|
|
||||||
|
|||||||
@@ -25,14 +25,14 @@ func webRegisterPost(w http.ResponseWriter, r *http.Request, _ httprouter.Params
|
|||||||
var err error
|
var err error
|
||||||
aTXT := ACMETxt{}
|
aTXT := ACMETxt{}
|
||||||
bdata, _ := ioutil.ReadAll(r.Body)
|
bdata, _ := ioutil.ReadAll(r.Body)
|
||||||
if bdata != nil && len(bdata) > 0 {
|
if len(bdata) > 0 {
|
||||||
err = json.Unmarshal(bdata, &aTXT)
|
err = json.Unmarshal(bdata, &aTXT)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
regStatus = http.StatusBadRequest
|
regStatus = http.StatusBadRequest
|
||||||
reg = jsonError("malformed_json_payload")
|
reg = jsonError("malformed_json_payload")
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.WriteHeader(regStatus)
|
w.WriteHeader(regStatus)
|
||||||
w.Write(reg)
|
_, _ = w.Write(reg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,7 +44,7 @@ func webRegisterPost(w http.ResponseWriter, r *http.Request, _ httprouter.Params
|
|||||||
reg = jsonError("invalid_allowfrom_cidr")
|
reg = jsonError("invalid_allowfrom_cidr")
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.WriteHeader(regStatus)
|
w.WriteHeader(regStatus)
|
||||||
w.Write(reg)
|
_, _ = w.Write(reg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ func webRegisterPost(w http.ResponseWriter, r *http.Request, _ httprouter.Params
|
|||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.WriteHeader(regStatus)
|
w.WriteHeader(regStatus)
|
||||||
w.Write(reg)
|
_, _ = w.Write(reg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func webUpdatePost(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
func webUpdatePost(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||||
@@ -104,7 +104,7 @@ func webUpdatePost(w http.ResponseWriter, r *http.Request, _ httprouter.Params)
|
|||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.WriteHeader(updStatus)
|
w.WriteHeader(updStatus)
|
||||||
w.Write(upd)
|
_, _ = w.Write(upd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Endpoint used to check the readiness and/or liveness (health) of the server.
|
// Endpoint used to check the readiness and/or liveness (health) of the server.
|
||||||
|
|||||||
+2
-10
@@ -325,10 +325,6 @@ func TestApiUpdateWithCredentialsMockDB(t *testing.T) {
|
|||||||
func TestApiManyUpdateWithCredentials(t *testing.T) {
|
func TestApiManyUpdateWithCredentials(t *testing.T) {
|
||||||
validTxtData := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
validTxtData := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||||
|
|
||||||
updateJSON := map[string]interface{}{
|
|
||||||
"subdomain": "",
|
|
||||||
"txt": ""}
|
|
||||||
|
|
||||||
router := setupRouter(true, false)
|
router := setupRouter(true, false)
|
||||||
server := httptest.NewServer(router)
|
server := httptest.NewServer(router)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
@@ -370,7 +366,7 @@ func TestApiManyUpdateWithCredentials(t *testing.T) {
|
|||||||
{newUserWithValidCIDR.Username.String(), newUserWithValidCIDR.Password, newUserWithValidCIDR.Subdomain, validTxtData, 200},
|
{newUserWithValidCIDR.Username.String(), newUserWithValidCIDR.Password, newUserWithValidCIDR.Subdomain, validTxtData, 200},
|
||||||
{newUser.Username.String(), "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", newUser.Subdomain, validTxtData, 401},
|
{newUser.Username.String(), "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", newUser.Subdomain, validTxtData, 401},
|
||||||
} {
|
} {
|
||||||
updateJSON = map[string]interface{}{
|
updateJSON := map[string]interface{}{
|
||||||
"subdomain": test.subdomain,
|
"subdomain": test.subdomain,
|
||||||
"txt": test.txt}
|
"txt": test.txt}
|
||||||
e.POST("/update").
|
e.POST("/update").
|
||||||
@@ -385,10 +381,6 @@ func TestApiManyUpdateWithCredentials(t *testing.T) {
|
|||||||
|
|
||||||
func TestApiManyUpdateWithIpCheckHeaders(t *testing.T) {
|
func TestApiManyUpdateWithIpCheckHeaders(t *testing.T) {
|
||||||
|
|
||||||
updateJSON := map[string]interface{}{
|
|
||||||
"subdomain": "",
|
|
||||||
"txt": ""}
|
|
||||||
|
|
||||||
router := setupRouter(false, false)
|
router := setupRouter(false, false)
|
||||||
server := httptest.NewServer(router)
|
server := httptest.NewServer(router)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
@@ -425,7 +417,7 @@ func TestApiManyUpdateWithIpCheckHeaders(t *testing.T) {
|
|||||||
{newUserWithIP6CIDR, "2002:c0a7:0ff::0", 401},
|
{newUserWithIP6CIDR, "2002:c0a7:0ff::0", 401},
|
||||||
{newUserWithIP6CIDR, "2002:c0a8:d3ad:b33f:c0ff:33b4:dc0d:3b4d", 200},
|
{newUserWithIP6CIDR, "2002:c0a8:d3ad:b33f:c0ff:33b4:dc0d:3b4d", 200},
|
||||||
} {
|
} {
|
||||||
updateJSON = map[string]interface{}{
|
updateJSON := map[string]interface{}{
|
||||||
"subdomain": test.user.Subdomain,
|
"subdomain": test.user.Subdomain,
|
||||||
"txt": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}
|
"txt": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}
|
||||||
e.POST("/update").
|
e.POST("/update").
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ func Auth(update httprouter.Handle) httprouter.Handle {
|
|||||||
} else {
|
} else {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
w.Write(jsonError("forbidden"))
|
_, _ = w.Write(jsonError("forbidden"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ tls_cert_privkey = "/etc/tls/example.org/privkey.pem"
|
|||||||
tls_cert_fullchain = "/etc/tls/example.org/fullchain.pem"
|
tls_cert_fullchain = "/etc/tls/example.org/fullchain.pem"
|
||||||
# only used if tls = "letsencrypt"
|
# only used if tls = "letsencrypt"
|
||||||
acme_cache_dir = "api-certs"
|
acme_cache_dir = "api-certs"
|
||||||
|
# optional e-mail address to which Let's Encrypt will send expiration notices for the API's cert
|
||||||
|
notification_email = ""
|
||||||
# CORS AllowOrigins, wildcards can be used
|
# CORS AllowOrigins, wildcards can be used
|
||||||
corsorigins = [
|
corsorigins = [
|
||||||
"*"
|
"*"
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ var txtTablePG = `
|
|||||||
|
|
||||||
// getSQLiteStmt replaces all PostgreSQL prepared statement placeholders (eg. $1, $2) with SQLite variant "?"
|
// getSQLiteStmt replaces all PostgreSQL prepared statement placeholders (eg. $1, $2) with SQLite variant "?"
|
||||||
func getSQLiteStmt(s string) string {
|
func getSQLiteStmt(s string) string {
|
||||||
re, _ := regexp.Compile("\\$[0-9]")
|
re, _ := regexp.Compile(`\$[0-9]`)
|
||||||
return re.ReplaceAllString(s, "?")
|
return re.ReplaceAllString(s, "?")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,12 +68,12 @@ func (d *acmedb) Init(engine string, connection string) error {
|
|||||||
if versionString == "" {
|
if versionString == "" {
|
||||||
versionString = "0"
|
versionString = "0"
|
||||||
}
|
}
|
||||||
_, err = d.DB.Exec(acmeTable)
|
_, _ = d.DB.Exec(acmeTable)
|
||||||
_, err = d.DB.Exec(userTable)
|
_, _ = d.DB.Exec(userTable)
|
||||||
if Config.Database.Engine == "sqlite3" {
|
if Config.Database.Engine == "sqlite3" {
|
||||||
_, err = d.DB.Exec(txtTable)
|
_, _ = d.DB.Exec(txtTable)
|
||||||
} else {
|
} else {
|
||||||
_, err = d.DB.Exec(txtTablePG)
|
_, _ = d.DB.Exec(txtTablePG)
|
||||||
}
|
}
|
||||||
// If everything is fine, handle db upgrade tasks
|
// If everything is fine, handle db upgrade tasks
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -136,10 +136,10 @@ func (d *acmedb) handleDBUpgradeTo1() error {
|
|||||||
// Rollback if errored, commit if not
|
// Rollback if errored, commit if not
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback()
|
_ = tx.Rollback()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tx.Commit()
|
_ = tx.Commit()
|
||||||
}()
|
}()
|
||||||
_, _ = tx.Exec("DELETE FROM txt")
|
_, _ = tx.Exec("DELETE FROM txt")
|
||||||
for _, subdomain := range subdomains {
|
for _, subdomain := range subdomains {
|
||||||
@@ -165,8 +165,8 @@ func (d *acmedb) handleDBUpgradeTo1() error {
|
|||||||
func (d *acmedb) NewTXTValuesInTransaction(tx *sql.Tx, subdomain string) error {
|
func (d *acmedb) NewTXTValuesInTransaction(tx *sql.Tx, subdomain string) error {
|
||||||
var err error
|
var err error
|
||||||
instr := fmt.Sprintf("INSERT INTO txt (Subdomain, LastUpdate) values('%s', 0)", subdomain)
|
instr := fmt.Sprintf("INSERT INTO txt (Subdomain, LastUpdate) values('%s', 0)", subdomain)
|
||||||
_, err = tx.Exec(instr)
|
_, _ = tx.Exec(instr)
|
||||||
_, err = tx.Exec(instr)
|
_, _ = tx.Exec(instr)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,10 +178,10 @@ func (d *acmedb) Register(afrom cidrslice) (ACMETxt, error) {
|
|||||||
// Rollback if errored, commit if not
|
// Rollback if errored, commit if not
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback()
|
_ = tx.Rollback()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tx.Commit()
|
_ = tx.Commit()
|
||||||
}()
|
}()
|
||||||
a := newACMETxt()
|
a := newACMETxt()
|
||||||
a.AllowFrom = cidrslice(afrom.ValidEntries())
|
a.AllowFrom = cidrslice(afrom.ValidEntries())
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ func (d *DNSServer) handleRequest(w dns.ResponseWriter, r *dns.Msg) {
|
|||||||
d.readQuery(m)
|
d.readQuery(m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
w.WriteMsg(m)
|
_ = w.WriteMsg(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSServer) readQuery(m *dns.Msg) {
|
func (d *DNSServer) readQuery(m *dns.Msg) {
|
||||||
@@ -119,9 +119,7 @@ func (d *DNSServer) readQuery(m *dns.Msg) {
|
|||||||
authoritative = auth
|
authoritative = auth
|
||||||
}
|
}
|
||||||
m.MsgHdr.Rcode = rc
|
m.MsgHdr.Rcode = rc
|
||||||
for _, r := range rr {
|
m.Answer = append(m.Answer, rr...)
|
||||||
m.Answer = append(m.Answer, r)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m.MsgHdr.Authoritative = authoritative
|
m.MsgHdr.Authoritative = authoritative
|
||||||
@@ -208,9 +206,7 @@ func (d *DNSServer) answer(q dns.Question) ([]dns.RR, int, bool, error) {
|
|||||||
txtRRs, err = d.answerTXT(q)
|
txtRRs, err = d.answerTXT(q)
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
for _, txtRR := range txtRRs {
|
r = append(r, txtRRs...)
|
||||||
r = append(r, txtRR)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(r) > 0 {
|
if len(r) > 0 {
|
||||||
|
|||||||
@@ -11,9 +11,6 @@ import (
|
|||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
var resolv resolver
|
|
||||||
var server *dns.Server
|
|
||||||
|
|
||||||
type resolver struct {
|
type resolver struct {
|
||||||
server string
|
server string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,13 +92,12 @@ func main() {
|
|||||||
go startHTTPAPI(errChan, Config, dnsservers)
|
go startHTTPAPI(errChan, Config, dnsservers)
|
||||||
|
|
||||||
// block waiting for error
|
// block waiting for error
|
||||||
select {
|
for {
|
||||||
case err = <-errChan:
|
err = <-errChan
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Debugf("Shutting down...")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func startHTTPAPI(errChan chan error, config DNSConfig, dnsservers []*DNSServer) {
|
func startHTTPAPI(errChan chan error, config DNSConfig, dnsservers []*DNSServer) {
|
||||||
@@ -149,6 +148,7 @@ func startHTTPAPI(errChan chan error, config DNSConfig, dnsservers []*DNSServer)
|
|||||||
DNSProvider: &provider,
|
DNSProvider: &provider,
|
||||||
DNSChallengeOption: dnsopts,
|
DNSChallengeOption: dnsopts,
|
||||||
DefaultServerName: Config.General.Domain,
|
DefaultServerName: Config.General.Domain,
|
||||||
|
Email: Config.API.NotificationEmail,
|
||||||
Storage: &storage,
|
Storage: &storage,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -56,7 +56,7 @@ func TestMain(m *testing.M) {
|
|||||||
go dnsserver.Start(make(chan error, 1))
|
go dnsserver.Start(make(chan error, 1))
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
exitval := m.Run()
|
exitval := m.Run()
|
||||||
dnsserver.Server.Shutdown()
|
_ = dnsserver.Server.Shutdown()
|
||||||
DB.Close()
|
DB.Close()
|
||||||
os.Exit(exitval)
|
os.Exit(exitval)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,6 @@ type DNSConfig struct {
|
|||||||
Logconfig logconfig
|
Logconfig logconfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auth middleware
|
|
||||||
type authMiddleware struct{}
|
|
||||||
|
|
||||||
// Config file general section
|
// Config file general section
|
||||||
type general struct {
|
type general struct {
|
||||||
Listen string
|
Listen string
|
||||||
@@ -51,6 +48,7 @@ type httpapi struct {
|
|||||||
TLSCertPrivkey string `toml:"tls_cert_privkey"`
|
TLSCertPrivkey string `toml:"tls_cert_privkey"`
|
||||||
TLSCertFullchain string `toml:"tls_cert_fullchain"`
|
TLSCertFullchain string `toml:"tls_cert_fullchain"`
|
||||||
ACMECacheDir string `toml:"acme_cache_dir"`
|
ACMECacheDir string `toml:"acme_cache_dir"`
|
||||||
|
NotificationEmail string `toml:"notification_email"`
|
||||||
CorsOrigins []string
|
CorsOrigins []string
|
||||||
UseHeader bool `toml:"use_header"`
|
UseHeader bool `toml:"use_header"`
|
||||||
HeaderName string `toml:"header_name"`
|
HeaderName string `toml:"header_name"`
|
||||||
|
|||||||
@@ -59,13 +59,13 @@ func prepareConfig(conf DNSConfig) (DNSConfig, error) {
|
|||||||
|
|
||||||
func sanitizeString(s string) string {
|
func sanitizeString(s string) string {
|
||||||
// URL safe base64 alphabet without padding as defined in ACME
|
// URL safe base64 alphabet without padding as defined in ACME
|
||||||
re, _ := regexp.Compile("[^A-Za-z\\-\\_0-9]+")
|
re, _ := regexp.Compile(`[^A-Za-z\-\_0-9]+`)
|
||||||
return re.ReplaceAllString(s, "")
|
return re.ReplaceAllString(s, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func sanitizeIPv6addr(s string) string {
|
func sanitizeIPv6addr(s string) string {
|
||||||
// Remove brackets from IPv6 addresses, net.ParseCIDR needs this
|
// Remove brackets from IPv6 addresses, net.ParseCIDR needs this
|
||||||
re, _ := regexp.Compile("[\\[\\]]+")
|
re, _ := regexp.Compile(`[\[\]]+`)
|
||||||
return re.ReplaceAllString(s, "")
|
return re.ReplaceAllString(s, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -104,11 +104,11 @@ func TestFileCheckPermissionDenied(t *testing.T) {
|
|||||||
t.Error("Could not create temporary file")
|
t.Error("Could not create temporary file")
|
||||||
}
|
}
|
||||||
defer os.Remove(tmpfile.Name())
|
defer os.Remove(tmpfile.Name())
|
||||||
syscall.Chmod(tmpfile.Name(), 0000)
|
_ = syscall.Chmod(tmpfile.Name(), 0000)
|
||||||
if fileIsAccessible(tmpfile.Name()) {
|
if fileIsAccessible(tmpfile.Name()) {
|
||||||
t.Errorf("File should not be accessible")
|
t.Errorf("File should not be accessible")
|
||||||
}
|
}
|
||||||
syscall.Chmod(tmpfile.Name(), 0644)
|
_ = syscall.Chmod(tmpfile.Name(), 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFileCheckNotExists(t *testing.T) {
|
func TestFileCheckNotExists(t *testing.T) {
|
||||||
|
|||||||
+4
-5
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
@@ -25,11 +26,9 @@ func validKey(k string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func validSubdomain(s string) bool {
|
func validSubdomain(s string) bool {
|
||||||
_, err := uuid.Parse(s)
|
// URL safe base64 alphabet without padding as defined in ACME
|
||||||
if err == nil {
|
RegExp := regexp.MustCompile("^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$")
|
||||||
return true
|
return RegExp.MatchString(s)
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func validTXT(s string) bool {
|
func validTXT(s string) bool {
|
||||||
|
|||||||
+3
-1
@@ -55,7 +55,9 @@ func TestGetValidSubdomain(t *testing.T) {
|
|||||||
output bool
|
output bool
|
||||||
}{
|
}{
|
||||||
{"a097455b-52cc-4569-90c8-7a4b97c6eba8", true},
|
{"a097455b-52cc-4569-90c8-7a4b97c6eba8", true},
|
||||||
{"a-97455b-52cc-4569-90c8-7a4b97c6eba8", false},
|
{"a-97455b-52cc-4569-90c8-7a4b97c6eba8", true},
|
||||||
|
{"foo.example.com", false},
|
||||||
|
{"foo-example-com", true},
|
||||||
{"", false},
|
{"", false},
|
||||||
{"&!#!25123!%!'%", false},
|
{"&!#!25123!%!'%", false},
|
||||||
} {
|
} {
|
||||||
|
|||||||
Reference in New Issue
Block a user