Πώς να ρυθμίσετε τον ιστότοπό σας για αυτό το γλυκό, γλυκό HTTPS με Docker, Nginx και letsencrypt

Έχω χρησιμοποιήσει letsencrypt στο παρελθόν για δωρεάν πιστοποιητικά. Δεν το έχω χρησιμοποιήσει με επιτυχία από τη μετάβαση στο docker / kestrel / nginx. Όλα αυτά άλλαξαν σήμερα, και είχα μια στιγμή να καταλάβω τι έκανα για να λειτουργήσω.

Όλο αυτό το Unix, docker, nginx, πράγματα είναι αρκετά καινούργιο (για μένα), οπότε ίσως είναι κάτι απλό που μου έλειπε όλη την ώρα. Παρ 'όλα αυτά, ελπίζω ότι αυτό θα βοηθήσει κάποιον άλλο, ή εγώ αρκετούς μήνες στο δρόμο, αν αποφασίσω να το κάνω ξανά.

Αρχική ρύθμιση

Έχω έναν κεντρικό ιστότοπο .net, που φιλοξενείται μέσω kestrel, τρέχει στο docker, με αντίστροφο διακομιστή μεσολάβησης μέσω nginx. Μέχρι τώρα, αυτή η αντίστροφη μεσολάβηση από το nginx λειτουργούσε μόνο μέσω http / port 80. Δεν ξέρω πολλά για τους αντίστροφους διακομιστές μεσολάβησης. Από τον ήχο του, μπορεί να λάβει αιτήματα και να τα προωθήσει σε μια συγκεκριμένη τοποθεσία εκ μέρους του αιτούντος. Στην περίπτωσή μου, το κοντέινερ nginx λαμβάνει αιτήματα http και το nginx προωθεί αυτό το αίτημα στον κεντρικό ιστότοπο που φιλοξενείται από το kestrel. Είναι σωστό? Ας ελπίσουμε!

Όπως αναφέρθηκε προηγουμένως, το nginx λειτουργούσε μόνο με την κυκλοφορία http. Αντιμετωπίζω πολλά προβλήματα με το να λειτουργεί με https, η αρχική διαμόρφωση έχει ως εξής:

λιμενεργάτης:

version: '3.6'services: kritner-website-web: image: ${DOCKER_REGISTRY}/kritnerwebsite expose: - "5000" networks: - frontend restart: always container_name: kritnerwebsite_web kritner-website-nginx: image: nginx:latest ports: - "80:80" volumes: - ../src/nginx/nginx.conf:/etc/nginx/nginx.conf depends_on: - kritner-website-web networks: - frontend restart: always container_name: kritnerwebsite_nginx
networks: frontend:

Στο αρχείο docker-compose, χρησιμοποιώ δύο ξεχωριστά κοντέινερ - τον ιστότοπο, ο οποίος εκθέτει τη θύρα 5000 (στο δίκτυο docker, όχι δημόσια) και το nginx που λειτουργεί στη θύρα 80.

nginx.conf

worker_processes 4; events { worker_connections 1024; } http { sendfile on; upstream app_servers { server kritner-website-web:5000; } server { listen 80; location / { proxy_pass //app_servers; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; } }}

Στο αρχείο config, δημιουργούμε έναν ανάντη διακομιστή με το ίδιο όνομα που καλούμε την υπηρεσία κοντέινερ από το αρχείο σύνθεσης σύνδεσης kritner-website-web:5000.

Σημείωση, όλα τα παραπάνω βρίσκονται σε αυτό το σημείο δέσμευσης στο αποθετήριο του ιστότοπού μου.

Εισαγάγετε HTTPS

Το Letsencrypt είναι μια αρχή έκδοσης πιστοποιητικών που προσφέρει δωρεάν πιστοποιητικά για την προστασία του ιστότοπού σας. Γιατί είναι σημαντικό το HTTPS μέσω TLS; Λοιπόν, υπάρχουν πολλά, και πώς λειτουργεί Η βασική ιδέα είναι ότι η κίνηση του χρήστη είναι κρυπτογραφημένη και στα δύο άκρα πριν από την αποστολή στο άλλο άκρο. Αυτό σημαίνει ότι αν βρίσκεστε σε δημόσιο Wi-Fi και σε https, κάποιος που "εισπνεύσει το καλώδιο" για να μιλήσει, θα δει ότι υπάρχει κίνηση, αλλά όχι το περιεχόμενο της εν λόγω επισκεψιμότητας. Δεδομένου ότι και τα δύο άκρα κρυπτογραφούν / αποκρυπτογραφούν την εν λόγω κίνηση με το ίδιο κλειδί κρυπτογράφησης.

Εάν βρίσκεστε σε έναν ιστότοπο http, αυτή η επισκεψιμότητα θα αποστέλλεται μπρος-πίσω σε απλό κείμενο. Σημαίνει ότι τα δεδομένα σας κινδυνεύουν να παραβλεφθούν! Ίσως θα γράψω λίγο περισσότερο για την κρυπτογράφηση σε κάποιο σημείο. (* σημείωση για τον εαυτό μου *) Ειδικά επειδή είναι κάτι που κάνω ως δουλειά της ημέρας μου!

letsencrypt είναι μια υπηρεσία που έχω χρησιμοποιήσει στο παρελθόν. Υπάρχουν διάφορες εφαρμογές που προσπαθούν να το κάνουν όσο το δυνατόν πιο εύκολο στη χρήση. Μέσα από έρευνα για αυτήν την ανάρτηση, συνέβη αυτό.

Αν και δεν βρήκα αυτήν τη σελίδα μέχρι τώρα, θα ήταν χρήσιμο πριν ξεκινήσω την περιπέτειά μου. Ήθελα να χρησιμοποιήσω το letencrypt μαζί με τον ιστότοπο κοντέινερ μου και το nginx, με όσο το δυνατόν λιγότερη συντήρηση. Το πιστοποιητικό letencrypt είναι καλό μόνο για 90 ημέρες.

Κατά την έρευνά μου, συνάντησα έναν linuxserver / letsencrypt image docker που υπόσχεται να χρησιμοποιήσει το nginx, τη δημιουργία πιστοποιητικών letencrypt και την αυτόματη ανανέωση. Ακούγεται υπέροχο! Ενώ η τεκμηρίωση της εικόνας φαίνεται ως επί το πλείστον επαρκής - για κάποιον πολύ έμπειρο σε όλη αυτή τη διαδικασία. Το βρήκα ότι λείπει. Η όλη διαδικασία εγκατάστασης με πήρε λίγο χρόνο για να το καταλάβω. Ως εκ τούτου, αυτή η ανάρτηση, ελπίζω να βοηθήσω το επόμενο άτομο, ή και πάλι στο μέλλον!

Αγώνες

Τα πράγματα με τα οποία δυσκολεύτηκα περισσότερο όταν ανέβαζα και λειτουργούσα αυτήν την εικόνα linuxserver / letsencrypt ήταν:

  • Πώς δουλεύει ο όμιλος λιμενεργάτης και η σχέση τους με αυτό το κοντέινερ
  • Πώς να ρυθμίσω τόμους για να χρησιμοποιήσω τη διαμόρφωσή μου (που σχετίζεται με το παραπάνω σημείο) - Αρχικά είχα πολλά προβλήματα να καταλάβω γιατί οι ρυθμίσεις μου άλλαξαν στο κοντέινερ αλλάζονταν ξανά κατά την επαναφόρτωση του εν λόγω κοντέινερ (επειδή αυτό είναι υποτίθεται ότι κάνει)
  • Πώς να ρυθμίσετε τη σωστή διαμόρφωση nginx - πού να το βάλετε και τι να το βάλετε.

Όγκοι Docker

Όγκοι Docker (έγγραφο):

Οι τόμοι είναι ο προτιμώμενος μηχανισμός για διαρκή δεδομένα που δημιουργούνται από και χρησιμοποιούνται από κοντέινερ Docker. Ενώ οι προσάρτηση σύνδεσης εξαρτώνται από τη δομή καταλόγου του κεντρικού υπολογιστή, οι τόμοι διαχειρίζονται πλήρως από το Docker. Οι τόμοι έχουν πολλά πλεονεκτήματα σε σχέση με τις βάσεις σύνδεσης

Το letsencrypt έχει πολλές ρυθμίσεις για να ταιριάζει. Χρειάστηκε λίγος χρόνος για να το καταλάβω, αλλά χρειαζόμουν έναν τόμο που αντιστοιχίστηκε από έναν κατάλογο στον κεντρικό υπολογιστή του docker σε έναν συγκεκριμένο κατάλογο στην εικόνα letencrypt. Τελικά το κατάφερα στο αρχείο σύνθεσης έτσι:

volumes: - ${DOCKER_KRITNER_NGINX}:/config - ./nginx.conf:/config/nginx/site-confs/default

Το πρώτο στοιχείο στον πίνακα ( ${DOCKER_KRITNER_NGINX}:/config) παίρνει μια νέα μεταβλητή περιβάλλοντος που αντιστοιχίζει τον κατάλογο κεντρικού υπολογιστή (ορίζεται στη μεταβλητή) στο /configεσωτερικό του κοντέινερ. Αυτό σημαίνει ότι ο κεντρικός υπολογιστής σύνδεσης (στη διαδρομή env var) θα περιέχει την ίδια διαμόρφωση με το κοντέινερ σύνδεσης στο δευτερεύον τμήμα της χαρτογράφησης τόμου ( /config)

Το δεύτερο στοιχείο ( ./nginx.conf:/config/nginx/site-confs/default) χαρτογραφεί το αρχείο nginx.conf των τοπικών αποθετηρίων μου (το αρχείο όπου έχω ρυθμίσει τον αντίστροφο διακομιστή μεσολάβησης) για να παρακάμψουμε το /config/nginx/site-confs/defaultαρχείο στον κεντρικό υπολογιστή και το κοντέινερ.

Ο πλήρης κατάλογος των αρχείων που κατέληξα να χρειαστεί να τροποποιήσω για τη συγκεκριμένη κατάστασή μου ήταν:

  • /config/dns-conf/dnsimple.ini
  • /config/nginx/site-confs/default

Η dnsimple.iniδιαμόρφωση ήταν να προσθέσω το κλειδί api μου και να …/defaultπεριλαμβάνει τη διαμόρφωση nginx.

Η τελική defaultδιαμόρφωση που κατέληξα είναι:

upstream app_servers { server kritnerwebsite:5000;}
## Version 2018/09/12 - Changelog: //github.com/linuxserver/docker-letsencrypt/commits/master/root/defaults/default
# listening on port 80 disabled by default, remove the "#" signs to enable# redirect all traffic to httpsserver { listen 80; server_name kritnerwebsite; return 301 //$host$request_uri;}
# main server blockserver { listen 443 ssl;
# enable subfolder method reverse proxy confs include /config/nginx/proxy-confs/*.subfolder.conf;
# all ssl related config moved to ssl.conf include /config/nginx/ssl.conf; # enable for ldap auth #include /config/nginx/ldap.conf;
client_max_body_size 0;
location / { proxy_pass //app_servers; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; }
}
# enable subdomain method reverse proxy confsinclude /config/nginx/proxy-confs/*.subdomain.conf;# enable proxy cache for authproxy_cache_path cache/ keys_zone=auth_cache:10m;

Υπάρχουν μερικές αλλαγές από την προεπιλογή που ήταν εκεί, τις οποίες θα προσπαθήσω να τονίσω στη συνέχεια.

upstream app_servers { server kritnerwebsite:5000;}

Πάνω είναι πολύ ωραίο, αφού το docker έχει το δικό του εσωτερικό DNS (υποθέτω;) Μπορείτε να ρυθμίσετε έναν ανάντη διακομιστή με το όνομα κοντέινερ, στην περίπτωσή μου "kritnerwebsite". (Σημείωση: Το άλλαξα από νωρίτερα στην ανάρτηση, η οποία ήταν "kritner-website-web".)

# listening on port 80 disabled by default, remove the "#" signs to enable# redirect all traffic to httpsserver { listen 80; server_name kritnerwebsite; return 301 //$host$request_uri;}

Uncommented out this section from the default, applied my server_name of “kritnerwebsite”

# main server blockserver { listen 443 ssl;
# enable subfolder method reverse proxy confs include /config/nginx/proxy-confs/*.subfolder.conf;
# all ssl related config moved to ssl.conf include /config/nginx/ssl.conf; # enable for ldap auth #include /config/nginx/ldap.conf;
client_max_body_size 0;
location / { proxy_pass //app_servers; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; }
}

In the above, it’s mostly from the “default” save for “location” and everything within that object. Here, we’re setting up the reverse proxy to forward requests to “/” (anything) to our //app_servers (kritnerwebsite as per our upstream).

docker-compose.yml

Our docker compose file didn’t change a *whole* lot from the initial. There were a few notable changes, which I’ll also get into describing:

version: '3.6'services: nginx: image: linuxserver/letsencrypt ports: - "80:80" - "443:443" volumes: - ${DOCKER_KRITNER_NGINX}:/config - ./nginx.conf:/config/nginx/site-confs/default depends_on: - kritnerwebsite networks: - frontend container_name: nginx environment: - PUID=1001 # get on dockerhost through command "id "" - PGID=1001 - [email protected] - URL=kritner.com - SUBDOMAINS=www - TZ=America/NewYork - VALIDATION=dns # using dns validation - DNSPLUGIN=dnsimple # via dnsimple, note there is additional configuration require separate from this file # - STAGING=true # this should be uncommented when testing for initial success, to avoid some rate limiting
kritnerwebsite: image: ${DOCKER_REGISTRY}/kritnerwebsite networks: - frontend expose: - "5000" restart: always container_name: kritnerwebsite networks: frontend:

for the new parts:

nginx: image: linuxserver/letsencrypt

Using a different image — linuxserver/letsencrypt instead of nginx. This image has nginx included, but also certbot, along with a cronjob to run certbot at application start.

ports: - "80:80" - "443:443"

Now we’re using both http and https ports (though note, we’re redirecting http calls to https via the nginx config).

volumes: - ${DOCKER_KRITNER_NGINX}:/config - ./nginx.conf:/config/nginx/site-confs/default

Already discussed earlier in the post, we’re using these volumes to properly set up the nginx configuration, with our dnsimple api key, as well as our reverse proxying to the kritnerwebsite.

environment: - PUID=1001 # get on dockerhost through command "id " - PGID=1001 - [email protected] - URL=kritner.com - SUBDOMAINS=www - TZ=America/NewYork - VALIDATION=dns # using dns validation - DNSPLUGIN=dnsimple # via dnsimple, note there is additional configuration require separate from this file # - STAGING=true # this should be uncommented when testing for initial success, to avoid some rate limiting

Environment variables needed as per the letsencrypt documentation can be found here.

  • PUID/PGID — get on dockerhost through command “id ”
  • Email — well, your email (used for cert expiration emails apparently)
  • URL — the main domain URL
  • subdomains — any subdomains to the URL to be certified
  • TZ — timezone
  • Validation — the type of validation to do — I’m using DNSimple, so i needed DNS in this field. Other options are html, tls-sni
  • dnsplugin — dnsimple — other options are cloudflare, cloudxns, digitalocean, dnsmadeeasy, google, luadns, nsone, rfc2136 and route53 as per the letsencrypt documentation
  • Staging = true - Το χρησιμοποίησα για να δοκιμάσω όλες τις διάφορες προσπάθειές μου πριν το λειτουργήσω. Το letsencrypt έχει περιορισμό ρυθμού όταν δεν εκτελείται σε λειτουργία σταδιοποίησης (ή τουλάχιστον στη σκηνή είναι πιο δύσκολο να το αντιμετωπίσετε).

Όλες αυτές οι αλλαγές, πειραματισμοί, αποτυχίες και τελικά επιτυχείς μπορούν να βρεθούν σε αυτό το αίτημα έλξης.

Το τελικό αποτέλεσμα;

και από //www.ssllabs.com/ -

Δεν είναι "A +", αλλά πραγματικά δεν είναι κακό για τη χρήση μιας προ-ενσωματωμένης εικόνας σύνδεσης για τις ανάγκες HTTPs μου!

Σχετιζομαι με:

  • Μετάβαση από "A" σε "A +" στο ssllabs.com