Как да настроите уебсайта си за този сладък, сладък HTTPS с Docker, Nginx и letsencrypt

Използвал съм letsencrypt в миналото за безплатни сертификати. Не съм го използвал успешно, откакто преминах към docker / kestrel / nginx. Всичко това се промени днес и имах адски време да разбера какво правя, за да го накарам да работи.

Целият този Unix, docker, nginx, неща са доста нови (за мен), така че може би това е просто нещо, което ми липсваше през цялото време. Въпреки това се надявам, че това ще помогне на някой друг или на мен след няколко месеца, ако реша да го направя отново.

Оригинална настройка

Имам уеб сайт с ядро ​​.net, който се хоства чрез kestrel, работи на докер, с обратен прокси чрез nginx. Досега обратното проксиране от nginx работеше само през http / порт 80. Не знам много за обратните прокси сървъри. От звука му той може да приема заявки и да ги препраща на определено място от името на заявителя. В моя случай контейнерът nginx получава http заявки и nginx препраща тази заявка към моя хост .net основен сайт на kestrel. Това вярно ли е? Да се ​​надяваме!

Както бе споменато по-рано, nginx работи само с http трафик. Имах много проблеми с работата с https, оригиналната конфигурация е както следва:

docker-compose:

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 (в докер мрежата, а не публично) и 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; } }}

В конфигурационния файл настройваме сървър нагоре по веригата със същото име, което наричаме нашата услуга за контейнери от файла за съставяне на докер kritner-website-web:5000.

Имайте предвид, че всичко по-горе може да бъде намерено в тази точка на фиксиране в хранилището на моя уебсайт.

Въведете HTTPS

Letsencrypt е сертифициращ орган, който предлага безплатни сертификати, за да защити вашия уебсайт. Защо е важен HTTPS чрез TLS? Е, има много неща в това и как работи. Основната идея е, че трафикът на потребителя е криптиран от двата края, преди да бъде изпратен на другия край. Това означава, че ако сте на обществен wifi и на https, някой, който „души жицата“, така да се каже, ще види, че трафикът се появява, но не и съдържанието на споменатия трафик. Тъй като двата края криптират / декриптират споменатия трафик с един и същ ключ за криптиране.

Ако сте били на http сайт, този трафик ще се изпраща напред и назад в обикновен текст. Това означава, че данните ви са застрашени от подслушване! Може би ще напиша малко повече за криптирането в даден момент. (* бележка за себе си *) Особено след като това е нещо, което правя като ежедневна работа!

letsencrypt е услуга, която съм използвал преди. Има различни внедрения, за да се опитате да улесните максимално лесното използване. Чрез проучвания за този пост, аз се случи на това.

Въпреки че досега не бях намерил тази страница, тя би била полезна преди началото на моето приключение. Исках да използвам letsencrypt заедно с уебсайта на моя контейнер за докер и nginx, с възможно най-малко поддръжка. сертификатите letsencrypt са добри само за 90 дни.

В проучването ми се случи на докер образ linuxserver / letsencrypt, който обещава да използва nginx, генериране на сертификати letsencrypt И автоматично подновяване. Звучи страхотно! Докато документацията на изображението изглежда предимно адекватна - за някой добре запознат с целия този процес. Установих, че липсва. Целият процес на настройка ми отне известно време, за да разбера. Оттук и този пост, за да се надяваме да помогнем на следващия човек или отново на мен в бъдеще!

Борби

Нещата, с които най-много се борех, когато създавах и работех това изображение на linuxserver / letsencrypt, бяха:

  • Как „работят“ томовете на докер и връзката им с този контейнер
  • Как да настроя обеми, за да използвам моята конфигурация (свързано с горната точка) - Първоначално имах много проблеми да разбера защо променените настройки на контейнера ми се променят при презареждане на споменатия контейнер (защото това е, което те трябва да направи)
  • Как да настроите правилната конфигурация на nginx - къде да я поставите и какво да поставите в нея.

Обеми на Docker

Обеми на докер (doc):

Обемите са предпочитаният механизъм за постоянни данни, генерирани от и използвани от контейнерите на Docker. Докато монтирането на свързване зависи от структурата на директориите на хост машината, томовете се управляват напълно от Docker. Обемите имат няколко предимства пред свързващите монтирания

letsencrypt has a lot of configuration to go along with it. It took a while for me to realize, but I needed a volume that mapped from a directory on the docker host to a specific directory on the letsencrypt image. I eventually accomplished this in the compose file like so:

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

The first item in the array (${DOCKER_KRITNER_NGINX}:/config) takes a new environment variable that maps the host directory (defined in the variable) to the /config within the docker container itself. This means that the docker host (at the env var path) will contain the same config as the docker container at the secondary portion of the volume mapping (/config)

The second item (./nginx.conf:/config/nginx/site-confs/default) maps my local repositories nginx.conf file (the file where I set up the reverse proxy) to override the /config/nginx/site-confs/default file on the docker host and container.

The full list of files that I ended up needing to modify for my particular situation was:

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

The dnsimple.ini configuration was add my api key, and the …/default houses the nginx configuration.

The final default configuration I ended up with is:

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;

There are a few changes from the default that was there, which I’ll try to highlight next.

upstream app_servers { server kritnerwebsite:5000;}

Above is pretty cool, since docker has its own internal DNS (I guess?). You can set up an upstream server by the containers name, in my case “kritnerwebsite”. (Note: I changed it from earlier in the post, which was “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 — I used this for testing out all my various attempts prior to getting it working. letsencrypt has rate limiting when not running in staging mode (or at least in staging it’s harder to run up against).

All the above changes, experimenting, failing, and then finally succeeding can be found in this pull request.

The final result?

and from //www.ssllabs.com/ —

Not an “A+”, but really not bad for using one pre-built docker image for my HTTPs needs!

Related:

  • Going from an “A” to an “A+” on ssllabs.com