Existem alguns artigos sobre como aumentar o contêiner Nginx e configurar a renovação automática de certificados LetsEncrypt para ele. Isso descreverá um esquema não padronizado. Luzes:
- O Nginx é implantado como um serviço no Docker Swarm, e não como um contêiner autônomo;
- Para verificação, o esquema DNS-01 é usado, e não o HTTP-01 muito mais popular;
- Para o provedor de DNS GoDaddy, atualmente não há plug-in DNS para certbot, mas há uma API para gerenciar registros de domínio.
O que é o DNS-01 e por que é necessário
Quando um certificado é solicitado no LetsEncrypt, ele precisa garantir que quem o solicita tenha direitos no domínio correspondente. Para isso, LetsEncrypt usa cheques. A verificação mais popular é chamada HTTP-01. Consiste no fato de o cliente receber primeiro um token especial e, em seguida, o servidor LetsEncrypt solicitar o endereço http://<DOMAIN>/.well-known/acme-challenge/<TOKEN>
e verificar se a resposta contém a mesma chave de token + hash do mesmo cliente no qual o token foi gravado. Mas existem 2 pontos:
- A solicitação é sempre executada exatamente como descrito acima. Portanto, você deve ter a porta 80 aberta e o caminho especificado está sempre acessível a partir do exterior, sem qualquer autenticação;
- Certificados curinga não são suportados. Embora você possa escrever um certificado para vários subdomínios de uma só vez (nesse caso, o LetsEncrypt fará solicitações para cada um dos subdomínios).
DNS-01 , -, 80-, -, Wildcard-. , certbot', DNS- _acme-challenge.<YOUR_DOMAIN>
TXT. , . API DNS-, dns-plugin' certbot', API. GoDaddy , API .
Docker Swarm?
Docker Swarm — Docker . Kubernetes , . , Docker Swarm , :
- (stack/service vs container);
- ;
- ;
- secret' ( , Kubernetes).
Swarm.
, example.com,
GoDaddy. -gateway . , , . Swarm' , , , . , gateway, . SSL. , nginx. SSL-.
wildcard- , nginx docker swarm, . nginx' . , gateway.example.com
, .
Docker Swarm
, Docker Engine .
- , Swarm' :
$ docker swarm init
- , :
$ docker node ls
SSL-
, LetsEncrypt GoDaddy.
- API DNS-. GoDaddy, https://developer.godaddy.com/ API Key.
Production
. ACME- certbot'. Certbot :
$ docker run --rm -it --mount type=bind,source=/opt/letsencrypt,target=/etc/letsencrypt certbot/certbot:v1.3.0 --email account@example.com --agree-tos -d *.example.com --manual --preferred-challenges dns certonly
/opt/letsencrypt
— , nginx' ;
account@example.com
— LetsEncrypt;
*.example.com
— , ( , wildcard).
, email ( ), , IP ( ). certbot , TOKEN_STRING
TXT- _acme-challenge.example.com
.
( certbot , DNS). curl' API GoDaddy . , .
payload:
$ cat <<EOF > payload.json
[{
"data": "TOKEN_STRING",
"name": "_acme-challenge",
"type": "TXT"
}]
EOF
TOKEN_STRING — , certbot'.
GoDaddy, 1:
$ export GODADDY_KEY=<KEY>
$ export GODADDY_SECRET=<SECRET>
_acme-challenge ( , URL ):
$ curl -XPUT -d @payload.json -H "Content-Type: application/json" -H "Authorization: sso-key $GODADDY_KEY:$GODADDY_SECRET" https://api.godaddy.com/v1/domains/example.com/records/TXT/_acme-challenge
, DNS ( , . , ):
$ dig -t txt _acme-challenge.example.com
...
;; ANSWER SECTION:
_acme-challenge.example.com. 600 IN TXT "TOKEN_STRING"
...
- certbot' Enter. ,
/opt/letsencrypt/live/example.com
, .
Nginx
, nginx.conf
. , SSL, HTTP 200 "It works!
". , .
nginx.conf /opt/nginx/conf:
nginx.confuser nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
server {
listen 443 ssl default_server;
server_name _;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
return 200 'It works!';
add_header Content-Type text/plain;
}
}
}
- nginx:
$ docker service create --name nginx -p 443:443 --mount type=bind,source=/opt/nginx/conf/nginx.conf,target=/etc/nginx/nginx.conf,ro --mount type=bind,source=/opt/letsencrypt,target=/etc/letsencrypt,ro nginx:1.17.9
https://gateway.example.com
, "It works!".- , :
$ docker service update --force nginx
:) LetsEncrypt 90 . LetsEncrypt 60. 2 , . , :
- . , , , nginx — ;
- Docker Swarm , ( : https://github.com/docker/swarmkit/issues/2852);
- , , Nginx reload, restart. reload HUP, ;
- : , nginx , foreground-
while true; do nginx -s reload; sleep 1d; done
.
, — ( , gateway) cron
. — , cron', - GoDaddy, . docker secrets, Swarm-. , (. . 2 Swarm). JaaS o Alex Ellis (https://github.com/alexellis/jaas).
, - , . , - , root', API GoDaddy . cron, docker run , .
jaas:
$ git clone https://github.com/alexellis/jaas
:
$ cd jaas
$ docker run --rm -v "$PWD":/usr/src/jaas -w /usr/src/jaas golang:1.13 bash -c "go get -d -v github.com/alexellis/jaas && go build -v"
/usr/local/bin ( ):
$ sudo cp jaas /usr/local/bin
jaas:
$ jaas run --image alpine:3.8 --env FOO=bar --command "env"
image' alpine, FOO=bar. env, , . , jaas'.
secret' API GoDaddy:
$ printf $GODADDY_KEY | docker secret create godaddy-key -
$ printf $GODADDY_SECRET | docker secret create godaddy-secret -
DNS certbot' authenticate- cleanup- (https://certbot.eff.org/docs/using.html#hooks). , docker- certbot. curl', wget' PUT-. python 3.8 requests. authenticate.sh
cleanup.sh
/opt/godaddy-hooks
( ).
, certbot
dig
, , DNS ( , ). 60 ( 45, , ). , DNS-, . , GoDaddy - DNS-. Cleanup- API , , .
:
$ chmod +x /opt/godaddy-hooks/authenticate.sh /opt/godaddy-hooks/cleanup.sh
:
$ jaas run --timeout 90s --mount /opt/letsencrypt=/etc/letsencrypt --mount /opt/godaddy-hooks=/opt/hooks -s godaddy-key -s godaddy-secret --image certbot/certbot:v1.3.0 --command "certbot --manual --manual-auth-hook /opt/hooks/authenticate.sh --manual-cleanup-hook /opt/hooks/cleanup.sh renew --dry-run --no-random-sleep-on-renew"
, . cron
, , , anacron
. ( 2 , 3- 4 ). , certbot
renew
, 8 . , , , CA . "" --no-random-sleep-on-renew
, jaas'. :
#!/bin/sh
jaas run --timeout 90s --mount /opt/letsencrypt=/etc/letsencrypt --mount /opt/godaddy-hooks=/opt/hooks -s godaddy-key -s godaddy-secret --image certbot/certbot:v1.3.0 --command "certbot --manual --manual-auth-hook /opt/hooks/authenticate.sh --manual-cleanup-hook /opt/hooks/cleanup.sh renew --no-random-sleep-on-renew"
docker service update --force nginx
GoDaddy
authenticate.sh#!/bin/sh
read key < /run/secrets/godaddy-key
read secret < /run/secrets/godaddy-secret
python - <<EOF
import requests
requests.put(
url = 'https://api.godaddy.com/v1/domains/$CERTBOT_DOMAIN/records/TXT/_acme-challenge',
json = [{'type': 'TXT', 'name': '_acme-challenge', 'data': '$CERTBOT_VALIDATION'}],
headers = {'Authorization': 'sso-key $key:$secret'}
)
EOF
sleep 60
cleanup.sh#!/bin/sh
read key < /run/secrets/godaddy-key
read secret < /run/secrets/godaddy-secret
python - <<EOF
import requests
response = requests.get(
url = 'https://api.godaddy.com/v1/domains/$CERTBOT_DOMAIN/records',
headers = {'Authorization': 'sso-key $key:$secret'}
)
requests.put(
url = 'https://api.godaddy.com/v1/domains/$CERTBOT_DOMAIN/records',
json = [record for record in response.json() if record['name'] != '_acme-challenge'],
headers = {'Authorization': 'sso-key $key:$secret'}
)
EOF