Назад | Перейти на главную страницу

Nginx не находит ssl_certificate в докере, хотя он есть

Я запускаю мультидокер на 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. Это каждый раз создавало новую версию приложения, в результате чего хранилище очищалось.