Docker рдЭреБрдВрдб рдореЗрдВ рдПрдХ Nginx / LetsEncrypt рдмрдВрдбрд▓ рдХреЛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░рдирд╛

Nginx рдХрдВрдЯреЗрдирд░ рдХреЛ рдХреИрд╕реЗ рдмрдврд╝рд╛рдПрдВ рдФрд░ рдЗрд╕рдХреЗ рд▓рд┐рдП LetsEncrypt рдкреНрд░рдорд╛рдгрдкрддреНрд░ рдСрдЯреЛ-рдирд╡реАрдиреАрдХрд░рдг рдХреЛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░рдиреЗ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдХрд╛рдлреА рдХреБрдЫ рд▓реЗрдЦ рд╣реИрдВред рдпрд╣ рдПрдХ рдЧреИрд░-рдорд╛рдирдХ рдпреЛрдЬрдирд╛ рдХрд╛ рд╡рд░реНрдгрди рдХрд░реЗрдЧрд╛ред рдореБрдЦреНрдп рд╡рд┐рд╢реЗрд╖рддрд╛рдПрдВ:


  1. рдирдЧреНрдиреЗрдХреНрд╕ рдХреЛ рдбреЛрдХрд░ рдЭреБрдВрдб рдореЗрдВ рдПрдХ рд╕реЗрд╡рд╛ рдХреЗ рд░реВрдк рдореЗрдВ рддреИрдирд╛рдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рди рдХрд┐ рдПрдХ рд╕реНрдЯреИрдВрдбрдЕрд▓реЛрди рдХрдВрдЯреЗрдирд░ рдХреЗ рд░реВрдк рдореЗрдВ;
  2. рд╕рддреНрдпрд╛рдкрди рдХреЗ рд▓рд┐рдП, DNS-01 рдпреЛрдЬрдирд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдФрд░ рдЕрдзрд┐рдХ рд▓реЛрдХрдкреНрд░рд┐рдп HTTP-01 рдирд╣реАрдВ;
  3. DNS рдкреНрд░рджрд╛рддрд╛ GoDaddy рдХреЗ рд▓рд┐рдП, рд╕рд░реНрдЯрд┐рдлрд┐рдХреЗрдЯ рдХреЗ рд▓рд┐рдП рд╡рд░реНрддрдорд╛рди рдореЗрдВ рдХреЛрдИ DNS рдкреНрд▓рдЧрдЗрди рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдбреЛрдореЗрди рд░рд┐рдХреЙрд░реНрдб рдкреНрд░рдмрдВрдзрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдПрдкреАрдЖрдИ рд╣реИред

DNS-01 рдХреНрдпрд╛ рд╣реИ рдФрд░ рдЗрд╕рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдХреНрдпреЛрдВ рд╣реИ


рдЬрдм LetsEncrypt рд╕реЗ рдПрдХ рдкреНрд░рдорд╛рдг рдкрддреНрд░ рдХрд╛ рдЕрдиреБрд░реЛрдз рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рддреЛ рдпрд╣ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рдХрд┐ рдЬреЛ рдЕрдиреБрд░реЛрдз рдХрд░рддрд╛ рд╣реИ рдЙрд╕рдХреЗ рдкрд╛рд╕ рд╕рдВрдмрдВрдзрд┐рдд рдбреЛрдореЗрди рдХреЗ рдЕрдзрд┐рдХрд╛рд░ рд╣реИрдВред рдЗрд╕рдХреЗ рд▓рд┐рдП, LetsEncrypt рдЪреЗрдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИред рд╕рдмрд╕реЗ рд▓реЛрдХрдкреНрд░рд┐рдп рдЪреЗрдХ рдХреЛ HTTP-01 рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИред рдпрд╣ рдЗрд╕ рддрдереНрдп рдореЗрдВ рд╕рдорд╛рд╣рд┐рдд рд╣реИ рдХрд┐ рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЛ рдкрд╣рд▓реЗ рдПрдХ рд╡рд┐рд╢реЗрд╖ рдЯреЛрдХрди рдЬрд╛рд░реА рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдФрд░ рдлрд┐рд░ LetsEncrypt рд╕рд░реНрд╡рд░ рдкрддреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдЕрдиреБрд░реЛрдз http://<DOMAIN>/.well-known/acme-challenge/<TOKEN>рдХрд░рддрд╛ рд╣реИ рдФрд░ рдЬрд╛рдВрдЪрддрд╛ рд╣реИ рдХрд┐ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдореЗрдВ рдЙрд╕реА рдХреНрд▓рд╛рдЗрдВрдЯ рдХреА рдЯреЛрдХрди + рд╣реИрд╢реЗрдб рдХреБрдВрдЬреА рд╣реИ рдЬрд┐рд╕рдореЗрдВ рдЯреЛрдХрди рд▓рд┐рдЦрд╛ рдЧрдпрд╛ рдерд╛ред рд▓реЗрдХрд┐рди 2 рдмрд┐рдВрджреБ рд╣реИрдВ:


  • рдЕрдиреБрд░реЛрдз рд╣рдореЗрд╢рд╛ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рд╣реЛрддрд╛ рд╣реИ рдЬреИрд╕рд╛ рдХрд┐ рдКрдкрд░ рд╡рд░реНрдгрд┐рдд рд╣реИред рдЗрд╕рд▓рд┐рдП, рдЖрдкрдХреЗ рдкрд╛рд╕ рдкреЛрд░реНрдЯ 80 рдЦреБрд▓рд╛ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП, рдФрд░ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдкрде рдмрд┐рдирд╛ рдХрд┐рд╕реА рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдХреЗ рд╣рдореЗрд╢рд╛ рдмрд╛рд╣рд░ рд╕реЗ рд╕реБрд▓рдн рд╣реИ;
  • рд╡рд╛рдЗрд▓реНрдбрдХрд╛рд░реНрдб рдкреНрд░рдорд╛рдгрдкрддреНрд░ рд╕рдорд░реНрдерд┐рдд рдирд╣реАрдВ рд╣реИрдВред рдпрджреНрдпрдкрд┐ рдЖрдк рдПрдХ рдмрд╛рд░ рдореЗрдВ рдХрдИ рдЙрдк-рдбреЛрдореЗрди рдХреЗ рд▓рд┐рдП рдПрдХ рдкреНрд░рдорд╛рдгрдкрддреНрд░ рд▓рд┐рдЦ рд╕рдХрддреЗ рд╣реИрдВ (рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, LetsEncrypt рдкреНрд░рддреНрдпреЗрдХ рдЙрдк-рдбреЛрдореЗрди рдХреЗ рд▓рд┐рдП рдЕрдиреБрд░реЛрдз рдХрд░реЗрдЧрд╛)ред

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 .


  1. , Swarm' :

    $ docker swarm init
  2. , :

    $ docker node ls

SSL-


, LetsEncrypt GoDaddy.


  1. API DNS-. GoDaddy, https://developer.godaddy.com/ API Key. Production.
  2. 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.


  3. ( certbot , DNS). curl' API GoDaddy . , .


    1. payload:


      $ cat <<EOF > payload.json
      [{
        "data": "TOKEN_STRING",
        "name": "_acme-challenge",
        "type": "TXT"
      }]
      EOF

      TOKEN_STRING тАФ , certbot'.


    2. GoDaddy, 1:


      $ export GODADDY_KEY=<KEY>
      $ export GODADDY_SECRET=<SECRET>

    3. _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

    4. , DNS ( , . , ):


      $ dig -t txt _acme-challenge.example.com
      ...
      ;; ANSWER SECTION:
      _acme-challenge.example.com. 600 IN TXT "TOKEN_STRING"
      ...


  4. certbot' Enter. , /opt/letsencrypt/live/example.com , .

Nginx


, nginx.conf. , SSL, HTTP 200 "It works!". , .


  1. nginx.conf /opt/nginx/conf:


    nginx.conf
    user 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;
        }
      }
    }



  1. 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
  2. https://gateway.example.com , "It works!".
  3. , :

    $ docker service update --force nginx


:) LetsEncrypt 90 . LetsEncrypt 60. 2 , . , :



, тАФ ( , gateway) cron. тАФ , cron', - GoDaddy, . docker secrets, Swarm-. , (. . 2 Swarm). JaaS o Alex Ellis (https://github.com/alexellis/jaas).


, - , . , - , root', API GoDaddy . cron, docker run , .


  1. jaas:


    $ git clone https://github.com/alexellis/jaas

  2. :


    $ 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"

  3. /usr/local/bin ( ):


    $ sudo cp jaas /usr/local/bin

  4. jaas:


    $ jaas run --image alpine:3.8 --env FOO=bar --command "env"

    image' alpine, FOO=bar. env, , . , jaas'.


  5. secret' API GoDaddy:


    $ printf $GODADDY_KEY | docker secret create godaddy-key -
    $ printf $GODADDY_SECRET | docker secret create godaddy-secret -

  6. 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 , , .


  7. :


    $ chmod +x /opt/godaddy-hooks/authenticate.sh /opt/godaddy-hooks/cleanup.sh

  8. :


    $ 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"

  9. , . 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

All Articles