Перейти к основному содержимому

SSL

Памир "из коробки" работает с самоподписанным сертификатом, и для обеспечения доверенного доступа к приложению самоподписанный сертификат предпочтительно заменить доверенным. Это возможно сделать несколькими способами:

  1. Получить сертификат в доверенном УЦ и положить пару открытый/закрытый ключ в директорию data/nginx/certs.
  2. Настроить ACME-клиент для получения сертификатов от Lets Encrypt, либо внутрикорпоративного доверенного УЦ.
  3. Настроить интегрированный промежуточный УЦ на базе Step CA с сертификатом, подписанным внутрикорпоративным доверенным УЦ.

Установка подписанного SSL-сертификата

Получив пару сертификат и приватный ключ их необходимо скопировать в директорию data/nginx/certs.

Имена файлов сертификата и приватного ключа должны совпадать, но при этом иметь разное расширение:

  • pamir.crt - сертификат
  • pamir.key - приватный ключ

После этого в общий override dotenv-файл необходимо установить переменную CERT_NAME равной имени файла сертификата без указания расширения, а также для автоматического перенаправления с протокола HTTP на HTTPS необходимо установить переменную HTTPS_METHOD значением redirect:

.override.env
CERT_NAME=pamir
HTTPS_METHOD=redirect

После этого применить параметры командой:

pamirctl start

Настройка ACME-клиента для получения сертификатов от Lets Encrypt, либо внутрикорпоративного доверенного УЦ

В данном примере будет произведена настройка ACME-клиента с внутрикорпоративным УЦ на базе Vault PKI. Настройка клиента с Lets Encrypt производится аналогично, за исключением некоторых моментов.

Последовательность настройки:

  1. В docker-compose.additional.yml добавить переопределение переменных сервиса ACME.

    docker-compose.additional.yml
    docker-compose.additional.yml
    services:
    acme:
    env_file:
    - path: ${ENV_FILE_OVERRIDE:-.override.env}
    required: false
    environment: !reset []
    подсказка

    В данном примере переменные внутри сервиса отбрасываются !reset [], а будут забираться из общего override dotenv-файла. Это нужно для того, чтобы ACME-клиент всегда доверял корневому сертификату внутрикорпоративного УЦ.

  2. Если в п. 1 переменные сервиса были отброшены, то они записываются в общий override dotenv-файл:

    .override.env
    LETSENCRYPT_HOST=pamir.example.com                                          # Общедоступный URL сервера {vars.name}
    CERT_NAME="${LETSENCRYPT_HOST:-default}" # Имя сертификата, равное URL
    HTTPS_METHOD=redirect # Автоматическое перенаправление на HTTPS
    ACME_CA_URI=https://vault.example.com:8200/v1/pamir_ssl_pki/acme/directory # Адрес сервера ACME
    ACME_HTTP_CHALLENGE_LOCATION=legacy # Параметр получения HTTP ответа для ACME
    DEFAULT_EMAIL=pamir@example.com # Email администратора для оповещений
    DEFAULT_RENEW=1 # Интервал перевыпуска сертификата
    EXISTING_ROOT_CA="-----BEGIN CERTIFICATE-----
    ...
    -----END CERTIFICATE-----" # Корневой сертификат УЦ в формате PEM
  3. Если в п. 1 переменные сервиса не отбрасывались, то общий override dotenv-файл будет следующим:

    .override.env
    LETSENCRYPT_HOST=pamir.example.com
    CERT_NAME="${LETSENCRYPT_HOST:-default}"
    HTTPS_METHOD=redirect
    ACME_HTTP_CHALLENGE_LOCATION=legacy
  4. Применить настройки командой:

    pamirctl start
Пример вывода лога ACME-клиента
docker-compose logs acme
acme-1  | Info: running acme-companion version 2.2.9
acme-1 | Info: 4096 bits RFC7919 Diffie-Hellman group found, generation skipped.
acme-1 | Reloading nginx docker-gen (using separate container 4ecfe411aa30092476a6f1a5a374eb7f2cea66b3032978b1eaea9a75ddcd4cc0)...
acme-1 | Reloading nginx (using separate container 4ecfe411aa30092476a6f1a5a374eb7f2cea66b3032978b1eaea9a75ddcd4cc0)...
acme-1 | 2025/04/28 10:23:37 Generated '/app/letsencrypt_service_data' from 12 containers
acme-1 | 2025/04/28 10:23:37 Running '/app/signal_le_service'
acme-1 | 2025/04/28 10:23:37 Watching docker events
acme-1 | 2025/04/28 10:23:37 Contents of /app/letsencrypt_service_data did not change. Skipping notification '/app/signal_le_service'
acme-1 | Reloading nginx docker-gen (using separate container 4ecfe411aa30092476a6f1a5a374eb7f2cea66b3032978b1eaea9a75ddcd4cc0)...
acme-1 | Reloading nginx (using separate container 4ecfe411aa30092476a6f1a5a374eb7f2cea66b3032978b1eaea9a75ddcd4cc0)...
acme-1 | Creating/renewal pamir.example.com certificates... (pamir.example.com)
acme-1 | [Пн апр 28 10:23:38 UTC 2025] Using CA: https://vault.example.com:8200/v1/pamir_ssl_pki/acme/directory
acme-1 | [Пн апр 28 10:23:38 UTC 2025] Creating domain key
acme-1 | [Пн апр 28 10:23:39 UTC 2025] The domain key is here: /etc/acme.sh/pamir@example.com/pamir.example.com/pamir.example.com.key
acme-1 | [Пн апр 28 10:23:39 UTC 2025] Single domain='pamir.example.com'
acme-1 | [Пн апр 28 10:23:39 UTC 2025] Getting domain auth token for each domain
acme-1 | [Пн апр 28 10:23:39 UTC 2025] Getting webroot for domain='pamir.example.com'
acme-1 | [Пн апр 28 10:23:39 UTC 2025] Verifying: pamir.example.com
acme-1 | [Пн апр 28 10:23:41 UTC 2025] Processing
acme-1 | [Пн апр 28 10:23:44 UTC 2025] Processing
acme-1 | [Пн апр 28 10:23:46 UTC 2025] Processing
acme-1 | [Пн апр 28 10:23:48 UTC 2025] Processing
acme-1 | 2025/04/28 10:23:49 Received event health_status: healthy for container 720c5627186e
acme-1 | 2025/04/28 10:23:49 Received event health_status: healthy for container ce1d5a602d34
acme-1 | 2025/04/28 10:23:49 Received event health_status: healthy for container a9001630550c
acme-1 | 2025/04/28 10:23:50 Received event health_status: healthy for container c3c75f1f0a27
acme-1 | [Пн апр 28 10:23:50 UTC 2025] Processing
acme-1 | 2025/04/28 10:23:50 Received event health_status: healthy for container 9ee102cb74d7
acme-1 | 2025/04/28 10:23:51 Received event health_status: healthy for container b5808d045f99
acme-1 | 2025/04/28 10:23:52 Received event start for container e55fb48a9f47
acme-1 | [Пн апр 28 10:23:52 UTC 2025] Processing
acme-1 | 2025/04/28 10:23:53 Received event start for container 673d10d1c6b0
acme-1 | 2025/04/28 10:23:53 Received event start for container b01cca9abae3
acme-1 | 2025/04/28 10:23:53 Received event start for container b3468d517ecf
acme-1 | 2025/04/28 10:23:54 Received event start for container a20428e6e40f
acme-1 | 2025/04/28 10:23:54 Received event start for container 387add980864
acme-1 | 2025/04/28 10:23:54 Received event start for container 46365889b2ba
acme-1 | 2025/04/28 10:23:55 Received event start for container fabe018d987c
acme-1 | [Пн апр 28 10:23:55 UTC 2025] Success
acme-1 | [Пн апр 28 10:23:55 UTC 2025] Verify finished, start to sign.
acme-1 | [Пн апр 28 10:23:55 UTC 2025] Lets finalize the order.
acme-1 | [Пн апр 28 10:23:55 UTC 2025] Le_OrderFinalize='https://vault.example.com:8200/v1/pamir_ssl_pki/acme/order/cf162c9f-5fc5-086e-d4aa-6bfa66118535/finalize'
acme-1 | [Пн апр 28 10:23:55 UTC 2025] Downloading cert.
acme-1 | [Пн апр 28 10:23:55 UTC 2025] Le_LinkCert='https://vault.example.com:8200/v1/pamir_ssl_pki/acme/order/cf162c9f-5fc5-086e-d4aa-6bfa66118535/cert'
acme-1 | [Пн апр 28 10:23:56 UTC 2025] Cert success.
acme-1 | -----BEGIN CERTIFICATE-----
acme-1 | ...
acme-1 | -----END CERTIFICATE-----
acme-1 | [Пн апр 28 10:23:56 UTC 2025] Your cert is in /etc/acme.sh/pamir@example.com/pamir.example.com/pamir.example.com.cer
acme-1 | [Пн апр 28 10:23:56 UTC 2025] Your cert key is in /etc/acme.sh/pamir@example.com/pamir.example.com/pamir.example.com.key
acme-1 | [Пн апр 28 10:23:56 UTC 2025] The intermediate CA cert is in /etc/acme.sh/pamir@example.com/pamir.example.com/ca.cer
acme-1 | [Пн апр 28 10:23:56 UTC 2025] And the full chain certs is there: /etc/acme.sh/pamir@example.com/pamir.example.com/fullchain.cer
acme-1 | [Пн апр 28 10:23:56 UTC 2025] Installing cert to:/etc/nginx/certs/pamir.example.com/cert.pem
acme-1 | [Пн апр 28 10:23:56 UTC 2025] Installing CA to:/etc/nginx/certs/pamir.example.com/chain.pem
acme-1 | [Пн апр 28 10:23:56 UTC 2025] Installing key to:/etc/nginx/certs/pamir.example.com/key.pem
acme-1 | [Пн апр 28 10:23:56 UTC 2025] Installing full chain to:/etc/nginx/certs/pamir.example.com/fullchain.pem
acme-1 | Creating/renewal pamir.example.com certificates... (pamir.example.com)
acme-1 | [Пн апр 28 10:23:57 UTC 2025] Domains not changed.
acme-1 | [Пн апр 28 10:23:57 UTC 2025] Skip, Next renewal time is: Ср мая 28 10:23:56 UTC 2025
acme-1 | [Пн апр 28 10:23:57 UTC 2025] Add '--force' to force to renew.

Настройка интегрированного УЦ Step CA и ACME-клиента с импортом доверенного промежуточного сертификата

В данном примере будет произведена настройка интегрированного в Памир удостоверяющего центра на базе Step CA, который будет выступать промежуточным УЦ для выпуска сертификатов SSL для Памир.

Последовательность настройки:

  1. Останавливается контейнер Step CA и удаляется том хранения данных для реинициализации УЦ.

    docker-compose stop step-ca
    docker-compose rm step-ca
    docker volume rm pamir_step-data
  2. В файле docker-compose.additional.yml отбросить переменные окружения сервиса Step CA.

    docker-compose.additional.yml
    services:
    step-ca:
    environment: !reset []
  3. В общий override dotenv-файл установить переменную EXISTING_ROOT_CA в значение корневого сертификата внутрикорпоративного УЦ, а также переменные LETSENCRYPT_HOST, CERT_NAME, HTTPS_METHOD и другие.

    .override.env
    LETSENCRYPT_HOST=pamir.example.com                                          # Общедоступный URL сервера {vars.name}
    CERT_NAME="${LETSENCRYPT_HOST:-default}" # Имя сертификата, равное URL
    HTTPS_METHOD=redirect # Автоматическое перенаправление на HTTPS
    ACME_HTTP_CHALLENGE_LOCATION=legacy # Параметр получения HTTP ответа для ACME
    EXISTING_ROOT_CA="-----BEGIN CERTIFICATE-----
    ...
    -----END CERTIFICATE-----" # Корневой сертификат УЦ в формате PEM
    STEPCA_INIT_WITH_EXISTING_CA=${EXISTING_ROOT_CA:-} # Корневой сертификат УЦ для Step CA
  4. Запустить сервис Step CA и получить лог вывода для копирования запроса сертификата на подпись в корневой УЦ.

    pamirctl start step-ca
    docker-compose logs --no-log-prefix step-ca
    Пример вывода лога с запросом сертификата
    docker-compose logs --no-log-prefix step-ca
    Generating root certificate... done!
    Generating intermediate certificate... done!

    ✔ Root certificate: /home/step/.step/certs/root_ca.crt
    ✔ Root private key: /home/step/.step/secrets/root_ca_key
    ✔ Root fingerprint: 8b84db596c7d1df57784404ad7494fad23030cecea0a0aeb6d3e86eee72681a5
    ✔ Intermediate certificate: /home/step/.step/certs/intermediate_ca.crt
    ✔ Intermediate private key: /home/step/.step/secrets/intermediate_ca_key
    badger 2025/04/28 15:37:04 INFO: All 0 tables opened in 0s
    ✔ Database folder: /home/step/.step/db
    ✔ Default configuration: /home/step/.step/config/defaults.json
    ✔ Certificate Authority configuration: /home/step/.step/config/ca.json
    ✔ Admin provisioner: admin (JWK)
    ✔ Super admin subject: step

    Your PKI is ready to go. To generate certificates for individual services see 'step help ca'.

    FEEDBACK 😍 🍻
    The step utility is not instrumented for usage statistics. It does not phone
    home. But your feedback is extremely valuable. Any information you can provide
    regarding how you’re using `step` helps. Please send us a sentence or two,
    good or bad at or join GitHub Discussions
    https://github.com/smallstep/certificates/discussions and our Discord
    https://u.step.sm/discord.

    👉 Your CA administrative username is: step
    👉 Your CA administrative password is: coXYYgHtFSzMeHYyyqmMLU0ANPDBNbKnvVOzgUu6
    🤫 This will only be displayed once.
    Your certificate signing request has been saved in /home/step/.step/certs/intermediate_ca.csr.
    Your private key has been saved in /home/step/.step/secrets/intermediate_ca_key.
    👉 Your Intermediate CA certificate request:

    -----BEGIN CERTIFICATE REQUEST-----
    ...
    -----END CERTIFICATE REQUEST-----

    Please sign certificate with your root CA and
    place signed Intermediate certificate to this path:

    ./data/step/intermediate_ca.crt

    and bind certificate file to this path in container:

    /home/step/.step/certs/intermediate_ca.crt

    🤫 Waiting 3600s...
  5. Подписать запрос на промежуточный сертификат в корневом УЦ и полученный сертификат записать в файл, указанный в инструкции в выводе лога.

  6. Примонтировать файл с сертификатом в контейнер в файле docker-compose.additional.yml с сохранением всех подключенных ранее томов (скопировать их из docker-compose.deps.yml).

    docker-compose.additional.yml
    services:
    step-ca:
    environment: !reset []
    volumes:
    - type: volume
    source: step-data
    target: /home/step
    - type: bind
    source: ./data/step
    target: /home/step/data
    read_only: true
    - type: bind
    source: ./data/step/intermediate_ca.crt
    target: /home/step/.step/certs/intermediate_ca.crt
    read_only: true
  7. Применить настройки командой:

    pamirctl start step-ca
    Пример вывода лога успешного запуска УЦ Step CA
    docker-compose logs --no-log-prefix step-ca
    badger 2025/04/28 15:48:20 INFO: All 0 tables opened in 3ms
    badger 2025/04/28 15:48:20 INFO: Replaying file id: 0 at offset: 0
    badger 2025/04/28 15:48:20 INFO: Replay took: 117.33µs
    2025/04/28 15:48:20 Starting Smallstep CA/0.24.1 (linux/amd64)
    2025/04/28 15:48:20 Documentation: https://u.step.sm/docs/ca
    2025/04/28 15:48:20 Community Discord: https://u.step.sm/discord
    2025/04/28 15:48:20 Config file: /home/step/.step/config/ca.json
    2025/04/28 15:48:20 The primary server URL is https://localhost:9000
    2025/04/28 15:48:20 Root certificates are available at https://localhost:9000/roots.pem
    2025/04/28 15:48:20 Additional configured hostnames: step-ca, pamir.example.com
    2025/04/28 15:48:20 X.509 Root Fingerprint: 8fbe5b86e3559d292c8c23061d6b5a787391b4e19f03b4941253e1efb26186bc
    2025/04/28 15:48:20 Serving HTTPS on :9000 ...
    подсказка

    Настройку ACME-клиента дополнительно осуществлять не нужно, клиент изначально настроен на работу с интегрированным УЦ Step CA.

    Пример вывода лога ACME-клиента
    docker-compose logs acme
    acme-1  | Info: running acme-companion version 2.2.9
    acme-1 | Info: 4096 bits RFC7919 Diffie-Hellman group found, generation skipped.
    acme-1 | Reloading nginx docker-gen (using separate container 89388683678e701029e772adca9fb8ea555161bcb053370399caf59a533063ba)...
    acme-1 | Reloading nginx (using separate container 89388683678e701029e772adca9fb8ea555161bcb053370399caf59a533063ba)...
    acme-1 | 2025/04/28 12:53:03 Generated '/app/letsencrypt_service_data' from 12 containers
    acme-1 | 2025/04/28 12:53:03 Running '/app/signal_le_service'
    acme-1 | 2025/04/28 12:53:03 Watching docker events
    acme-1 | 2025/04/28 12:53:03 Contents of /app/letsencrypt_service_data did not change. Skipping notification '/app/signal_le_service'
    acme-1 | Reloading nginx docker-gen (using separate container 89388683678e701029e772adca9fb8ea555161bcb053370399caf59a533063ba)...
    acme-1 | Reloading nginx (using separate container 89388683678e701029e772adca9fb8ea555161bcb053370399caf59a533063ba)...
    acme-1 | Creating/renewal pamir.example.com certificates... (pamir.example.com)
    acme-1 | [Пн апр 28 12:53:04 UTC 2025] Using CA: https://step-ca:9000/acme/acme/directory
    acme-1 | [Пн апр 28 12:53:04 UTC 2025] Creating domain key
    acme-1 | [Пн апр 28 12:53:04 UTC 2025] The domain key is here: /etc/acme.sh/pamir@example.com/pamir.example.com/pamir.example.com.key
    acme-1 | [Пн апр 28 12:53:04 UTC 2025] Single domain='pamir.example.com'
    acme-1 | [Пн апр 28 12:53:04 UTC 2025] Getting domain auth token for each domain
    acme-1 | [Пн апр 28 12:53:04 UTC 2025] Getting webroot for domain='pamir.example.com'
    acme-1 | [Пн апр 28 12:53:04 UTC 2025] Verifying: pamir.example.com
    acme-1 | [Пн апр 28 12:53:07 UTC 2025] Pending
    acme-1 | [Пн апр 28 12:53:09 UTC 2025] Pending
    acme-1 | [Пн апр 28 12:53:11 UTC 2025] Pending
    acme-1 | [Пн апр 28 12:53:13 UTC 2025] Pending
    acme-1 | 2025/04/28 12:53:15 Received event health_status: healthy for container 18821ba8ad9a
    acme-1 | 2025/04/28 12:53:15 Received event health_status: healthy for container be79938d3127
    acme-1 | [Пн апр 28 12:53:15 UTC 2025] Pending
    acme-1 | 2025/04/28 12:53:15 Received event health_status: healthy for container c9a72ac70a51
    acme-1 | 2025/04/28 12:53:16 Received event health_status: healthy for container 25270c964010
    acme-1 | 2025/04/28 12:53:16 Received event health_status: healthy for container 18a6a51c3ebb
    acme-1 | [Пн апр 28 12:53:17 UTC 2025] Success
    acme-1 | [Пн апр 28 12:53:17 UTC 2025] Verify finished, start to sign.
    acme-1 | [Пн апр 28 12:53:17 UTC 2025] Lets finalize the order.
    acme-1 | [Пн апр 28 12:53:17 UTC 2025] Le_OrderFinalize='https://step-ca:9000/acme/acme/order/QjhglH8EB0zoW4FXfD09jpoYRobr1L8M/finalize'
    acme-1 | [Пн апр 28 12:53:17 UTC 2025] Downloading cert.
    acme-1 | [Пн апр 28 12:53:17 UTC 2025] Le_LinkCert='https://step-ca:9000/acme/acme/certificate/kiRHVhDcuLgdjARRszhGPnpjZCyEOWl6'
    acme-1 | [Пн апр 28 12:53:18 UTC 2025] Cert success.
    acme-1 | -----BEGIN CERTIFICATE-----
    acme-1 | ...
    acme-1 | -----END CERTIFICATE-----
    acme-1 | [Пн апр 28 12:53:18 UTC 2025] Your cert is in /etc/acme.sh/pamir@example.com/pamir.example.com/pamir.example.com.cer
    acme-1 | [Пн апр 28 12:53:18 UTC 2025] Your cert key is in /etc/acme.sh/pamir@example.com/pamir.example.com/pamir.example.com.key
    acme-1 | [Пн апр 28 12:53:18 UTC 2025] The intermediate CA cert is in /etc/acme.sh/pamir@example.com/pamir.example.com/ca.cer
    acme-1 | [Пн апр 28 12:53:18 UTC 2025] And the full chain certs is there: /etc/acme.sh/pamir@example.com/pamir.example.com/fullchain.cer
    acme-1 | [Пн апр 28 12:53:18 UTC 2025] Installing cert to:/etc/nginx/certs/pamir.example.com/cert.pem
    acme-1 | [Пн апр 28 12:53:18 UTC 2025] Installing CA to:/etc/nginx/certs/pamir.example.com/chain.pem
    acme-1 | [Пн апр 28 12:53:18 UTC 2025] Installing key to:/etc/nginx/certs/pamir.example.com/key.pem
    acme-1 | [Пн апр 28 12:53:18 UTC 2025] Installing full chain to:/etc/nginx/certs/pamir.example.com/fullchain.pem