SSL
Памир "из коробки" работает с самоподписанным сертификатом, и для обеспечения доверенного доступа к приложению самоподписанный сертификат предпочтительно заменить доверенным. Это возможно сделать несколькими способами:
- Получить сертификат в доверенном УЦ и положить пару открытый/закрытый ключ в директорию data/nginx/certs.
- Настроить ACME-клиент для получения сертификатов от Lets Encrypt, либо внутрикорпоративного доверенного УЦ.
- Настроить интегрированный промежуточный УЦ на базе Step CA с сертификатом, подписанным внутрикорпоративным доверенным УЦ.
Установка подписанного SSL-сертификата
Получив пару сертификат и приватный ключ их необходимо скопировать в директорию data/nginx/certs
.
Имена файлов сертификата и приватного ключа должны совпадать, но при этом иметь разное расширение:
pamir.crt
- сертификатpamir.key
- приватный ключ
После этого в общий override dotenv-файл необходимо установить переменную CERT_NAME
равной имени файла сертификата
без указания расширения, а также для автоматического перенаправления с протокола HTTP на HTTPS необходимо установить
переменную HTTPS_METHOD
значением redirect
:
CERT_NAME=pamir
HTTPS_METHOD=redirect
После этого применить параметры командой:
pamirctl start
Настройка ACME-клиента для получения сертификатов от Lets Encrypt, либо внутрикорпоративного доверенного УЦ
В данном примере будет произведена настройка ACME-клиента с внутрикорпоративным УЦ на базе Vault PKI. Настройка клиента с Lets Encrypt производится аналогично, за исключением некоторых моментов.
Последовательность настройки:
-
В
docker-compose.additional.yml
добавить переопределение переменных сервиса ACME.docker-compose.additional.yml- Внутрикорпоративный УЦ
- Lets Encrypt
docker-compose.additional.ymlservices:
acme:
env_file:
- path: ${ENV_FILE_OVERRIDE:-.override.env}
required: false
environment: !reset []подсказкаВ данном примере переменные внутри сервиса отбрасываются
!reset []
, а будут забираться из общего override dotenv-файла. Это нужно для того, чтобы ACME-клиент всегда доверял корневому сертификату внутрикорпоративного УЦ.docker-compose.additional.ymlservices:
acme:
environment:
ACME_CA_URI: https://acme-v02.api.letsencrypt.org/directory # Адрес сервера ACME
DEFAULT_EMAIL: pamir@example.com # Email администратора для оповещений
DEFAULT_RENEW: 30 # Интервал перевыпуска сертификата -
Если в п. 1 переменные сервиса были отброшены, то они записываются в общий override dotenv-файл:
.override.envLETSENCRYPT_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 -
Если в п. 1 переменные сервиса не отбрасывались, то общий override dotenv-файл будет следующим:
.override.envLETSENCRYPT_HOST=pamir.example.com
CERT_NAME="${LETSENCRYPT_HOST:-default}"
HTTPS_METHOD=redirect
ACME_HTTP_CHALLENGE_LOCATION=legacy -
Применить настройки командой:
pamirctl start
Пример вывода лога 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 для Памир.
Последовательность настройки:
-
Останавливается контейнер Step CA и удаляется том хранения данных для реинициализации УЦ.
docker-compose stop step-ca
docker-compose rm step-ca
docker volume rm pamir_step-data -
В файле
docker-compose.additional.yml
отбросить переменные окружения сервиса Step CA.docker-compose.additional.ymlservices:
step-ca:
environment: !reset [] -
В общий override dotenv-файл установить переменную
EXISTING_ROOT_CA
в значение корневого сертификата внутрикорпоративного УЦ, а также переменныеLETSENCRYPT_HOST
,CERT_NAME
,HTTPS_METHOD
и другие..override.envLETSENCRYPT_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 -
Запустить сервис Step CA и получить лог вывода для копирования запроса сертификата на подпись в корневой УЦ.
pamirctl start step-ca
docker-compose logs --no-log-prefix step-caПример вывода лога с запросом сертификата
docker-compose logs --no-log-prefix step-caGenerating 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 feedback@smallstep.com 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... -
Подписать запрос на промежуточный сертификат в корневом УЦ и полученный сертификат записать в файл, указанный в инструкции в выводе лога.
-
Примонтировать файл с сертификатом в контейнер в файле
docker-compose.additional.yml
с сохранением всех подключенных ранее томов (скопировать их изdocker-compose.deps.yml
).docker-compose.additional.ymlservices:
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 -
Применить настройки командой:
pamirctl start step-ca
Пример вывода лога успешного запуска УЦ Step CA
docker-compose logs --no-log-prefix step-cabadger 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 acmeacme-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