Я запускаю мультидокер на AWS EB. Настройка выглядит так:
Dockerrun.aws.json
(xxx в качестве заполнителя для домена. Приложение небезопасно)
{
"AWSEBDockerrunVersion": 2,
"volumes": [
{
"name": "nginx-conf",
"host": {
"sourcePath": "/var/app/current/nginx.conf"
}
},
{
"name": "nginx-cert-conf",
"host": {
"sourcePath": "/var/app/current/nginx.cert.conf"
}
},
{
"name": "cert-bot-lib",
"host": {
"sourcePath": "/var/app/current/django_project/certbot"
}
},
{
"name": "lets-encrypt",
"host": {
"sourcePath": "/var/app/current/django_project/certs"
}
},
{
"name": "cert-challenge",
"host": {
"sourcePath": "/var/app/current/django_project/cert_challenge"
}
}
],
"containerDefinitions": [{
"name": "web-app",
"image": "xxx",
"command": [
"…"
],
"essential": true,
"memory": 1000,
"mountPoints": [
…
]
},
{
"name": "channels-app",
"image": "xxx",
"command": [
"…"
],
"essential": true,
"memory": 1000,
"mountPoints": [
…
]
},
{
"name": "nginx-proxy",
"image": "nginx",
"essential": false,
"memory": 500,
"portMappings": [{
"hostPort": 443,
"containerPort": 443
}],
"links": ["web-app", "channels-app"],
"mountPoints": [{
"sourceVolume": "nginx-conf",
"containerPath": "/etc/nginx/conf.d/default.conf",
"readOnly": true
},
{
"sourceVolume": "lets-encrypt",
"containerPath": "/etc/ssl/certs",
"readOnly": true
}
]
},
{
"name": "nginx-cert-proxy",
"image": "nginx",
"essential": true,
"memory": 250,
"portMappings": [{
"hostPort": 80,
"containerPort": 80
}],
"mountPoints": [{
"sourceVolume": "nginx-cert-conf",
"containerPath": "/etc/nginx/conf.d/default.conf",
"readOnly": true
},
{
"sourceVolume": "cert-challenge",
"containerPath": "/cert_challenge",
"readOnly": true
}
]
},
{
"name": "certbot",
"image": "yspreen/certbot-bash",
"command": [
"-c",
"(([ -d /etc/letsencrypt/live ] && certbot renew --staging --webroot --register-unsafely-without-email --agree-tos --no-eff-email --webroot-path=/cert_challenge) || certbot certonly --staging --webroot --register-unsafely-without-email --agree-tos --no-eff-email --webroot-path=/cert_challenge -d xxx) && cp -Lr /etc/letsencrypt/live /etc/letsencrypt/live_cp"
],
"essential": false,
"memory": 250,
"mountPoints": [{
"sourceVolume": "lets-encrypt",
"containerPath": "/etc/letsencrypt",
"readOnly": false
},
{
"sourceVolume": "cert-challenge",
"containerPath": "/cert_challenge",
"readOnly": false
},
{
"sourceVolume": "cert-bot-lib",
"containerPath": "/var/lib/letsencrypt",
"readOnly": false
}
]
}
]
}
nginx.conf
upstream djangomain {
server web-app:8000;
}
upstream djangochannels {
server channels-app:8001;
}
server {
server_name xxx;
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_tokens off;
disable_symlinks off;
ssl on;
ssl_buffer_size 8k;
ssl_dhparam /etc/ssl/certs/dhparam-2048.pem;
ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
ssl_ecdh_curve secp384r1;
ssl_session_tickets off;
## OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 1.0.0.1;
ssl_certificate /etc/ssl/certs/live_cp/xxx/fullchain.pem;
ssl_certificate_key /etc/ssl/certs/live_cp/xxx/privkey.pem;
location /ws/ {
proxy_pass http://djangochannels/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
}
location /static/ {
alias /static_root/;
autoindex on;
}
location / {
proxy_pass http://djangomain/;
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;
}
}
nginx.cert.conf
server {
listen 80;
listen [::]:80;
server_name localhost;
location ~ /.well-known/acme-challenge {
allow all;
root /cert_challenge;
}
location / {
rewrite ^ https://$host$request_uri? permanent;
}
}
Ключевые файлы должны быть созданы certbot
контейнер. Для этого задача помещается в cert-challenge
объем и выставлен nginx-cert-proxy
контейнер. Это работает.
Что не работает, так это применение сертификатов к nginx-proxy
контейнер. Это происходит из-за следующей ошибки:
nginx: [warn] the "ssl" directive is deprecated, use the "listen ... ssl" directive instead in /etc/nginx/conf.d/default.conf:17
nginx: [emerg] BIO_new_file("/etc/ssl/certs/live_cp/xxx/fullchain.pem") failed (SSL: error:02001002:system library:fopen:No such file or directory:fopen('/etc/ssl/certs/live_cp/xxx/fullchain.pem','r') error:2006D080:BIO routines:BIO_new_file:no such file)
Теперь простое объяснение состоит в том, что есть какая-то опечатка или что nginx не работает из-за символических ссылок от certbot.
Но я изменил nginx.conf
по умолчанию, запускается bash внутри контейнера, и ls показывает:
root@a58f8e87ca:/# ls /etc/ssl/certs/live_cp/xxx/fullchain.pem -lAh
-rw-r--r-- 1 root root 3.8K Oct 25 21:43 /etc/ssl/certs/live_cp/xxx/fullchain.pem
Так файл существует. Что касается символических ссылок, это определенно не ссылка, а настоящий файл. Модифицированный образ certbot позволяет добавить команду копирования, потому что точка входа bash
.
Я совершенно потерялся. Почему nginx не может прочитать файл?
Оказывается, проблема во времени.
Когда все контейнеры запускаются одновременно, сертификаты не генерируются до того, как nginx будет их искать. Когда я вручную вхожу в контейнер для проверки, они тем временем были сгенерированы и работают. Итак, это проблема добавления
until [ -f $cert ]; do sleep 1; done
Перед командой запуска nginx.
Я не думал об этом, потому что забыл, что сертификаты удаляются каждый раз, когда я запускаю установку.
Я тестировал это с помощью команды EB CLI deploy. Это каждый раз создавало новую версию приложения, в результате чего хранилище очищалось.