Opensophy — open-source инструмент для управления mTLS-сертификатами под Traefik.
Лицензия: MIT
Репозиторий: opensophy-projects
Скрипт проверен на Ubuntu, платформа Dokploy - в качестве управления докер контейнерами итп.
mtls.sh — интерактивный bash-менеджер mTLS-сертификатов для reverse-proxy Traefik. Он позволяет:
- создавать и управлять корневым CA (Certificate Authority)
- выпускать клиентские сертификаты (
.crt,.key,.p12) - автоматически генерировать конфигурацию Traefik с настройками mTLS
- отзывать и удалять сертификаты
- работать в двух режимах интеграции с Traefik: создание нового роутера или патч существующего
Скрипт не требует сторонних инструментов кроме openssl и python3 — оба присутствуют в любом современном Linux-окружении.
Запускать от лица root пользователся
| Зависимость | Где используется | Обязательна |
|---|---|---|
openssl |
Генерация CA, CSR, подпись сертификатов, CRL, PKCS#12 | ✅ |
python3 |
JSON-база данных, YAML-патчинг конфигов Traefik | ✅ |
bash ≥ 4 |
Сам скрипт | ✅ |
ip (iproute2) |
Определение IP-адреса хоста для Traefik target | Нет (fallback: 172.17.0.1) |
При запуске скрипт автоматически проверяет наличие openssl и python3. Если они отсутствуют, предлагает установить их через apt-get, yum, apk или brew.
mtls.sh
├── CONFIG — загрузка/сохранение настроек (~/.mtls-manager.conf)
├── DB — JSON-база сертификатов (~/.mtls-manager.db)
├── SERVICES — JSON-список сервисов (~/.mtls-manager.services)
├── CA — создание корневого CA, CRL
├── INT_CA — промежуточный CA на каждого клиента
├── BUNDLE — сборка clients-bundle.crt из всех активных int-CA
├── PATCH — патчинг существующих Traefik YAML-конфигов
├── TRAEFIK — генерация mtls-manager.yml
└── UI — интерактивное меню (header, hr, ask, menu_choice...)
Данные хранятся в трёх файлах в домашней директории пользователя:
| Файл | Формат | Содержимое |
|---|---|---|
~/.mtls-manager.conf |
KEY="value" | Пути, срок по умолчанию |
~/.mtls-manager.db |
JSON | Метаданные сертификатов (имя, сервис, даты, статус, пути) |
~/.mtls-manager.services |
JSON array | Список зарегистрированных сервисов |
chmod +x mtls.sh
sudo ./mtls.sh
sudoнужен, если пути CA и Traefik находятся в/etc/. Для локального тестирования (пресетp3)sudoне нужен.
🔐 mTLS Certificate Manager
/etc/traefik/dynamic
CA ✔ сервисов: 2 сертификатов: 5
1) Создать сертификат
2) Список сертификатов
3) Отозвать / удалить сертификат
4) Управление сервисами
5) Создать / пересоздать CA
6) Настройка путей
7) Обновить Traefik конфиг
0) Выход
Статус CA и счётчики обновляются при каждом открытии главного меню.
Меню → 4
Сервис — это логическая единица, которой выдаются сертификаты. Каждый сервис соответствует одному защищённому ресурсу в Traefik.
Создаёт новый роутер и сервис в Traefik-конфиге:
Имя сервиса → myapp
Домен → myapp.example.com
Target URL → http://localhost:3000
В сгенерированном mtls-manager.yml появятся:
http:
routers:
myapp-mtls:
rule: "Host(`myapp.example.com`)"
entryPoints:
- websecure
service: myapp-mtls
tls:
options: mtls-myapp
services:
myapp-mtls:
loadBalancer:
servers:
- url: "http://172.17.0.1:3000"
localhostи127.0.0.1в target автоматически заменяются на IP шлюза хоста (определяется черезip route).
Добавляет mTLS-опцию к уже существующему роутеру в другом YAML-файле Traefik (например, в конфиге Dokploy).
Файл конфига → /etc/dokploy/traefik/dynamic/dokploy.yml
Роутер → my-existing-router
При создании первого сертификата для этого сервиса скрипт патчит указанный файл:
# До патча:
my-existing-router:
tls: {}
# После патча:
my-existing-router:
tls:
options: mtls-myappПри удалении сервиса patch снимается автоматически.
Меню → 1
Порядок действий:
- Выбрать сервис из списка
- Задать имя сертификата (латиница, без пробелов — пробелы заменяются на
-) - Указать срок действия (по умолчанию: значение из настроек, стандарт 365 дней)
- Добавить заметку (для кого / чего выдан)
- Задать пароль для
.p12-файла (можно оставить пустым)
Скрипт последовательно выполняет:
openssl genrsa → client.key (2048 бит)
openssl req -new → client.csr
create_int_ca() → промежуточный CA для этого клиента
sign_client_with_int_ca → client.crt (подписан промежуточным CA)
openssl pkcs12 → client.p12 (ключ + сертификат + корневой CA)
rebuild_bundle() → обновление clients-bundle.crt
do_gen_traefik() → обновление mtls-manager.yml
Готовый файл .p12 импортируется в браузер, мобильное устройство или curl.
/etc/traefik/certs/mtls/clients/
└── <service>/
└── <cert-name>/
├── client.key — приватный ключ клиента
├── client.crt — сертификат клиента
└── client.p12 — bundle для импорта
client.csrудаляется после подписания — в хранении не нуждается.
Меню → 2
Выводит таблицу всех сертификатов с колонками:
# Имя Сервис Создан Истекает Статус Заметка
1 alice myapp 2025-01-15 2026-01-15 АКТИВЕН iPhone Alice
2 bob-laptop myapp 2025-03-01 2025-04-01 СКОРО (5д) ...
3 old-cert api 2024-01-01 2024-12-31 ИСТЁК ...
4 revoked api 2024-06-01 2025-06-01 ОТОЗВАН ...
Статусы:
| Статус | Условие | Цвет |
|---|---|---|
| АКТИВЕН | Действует, > 30 дней до истечения | Зелёный |
| СКОРО (Nд) | Истекает менее чем через 30 дней | Жёлтый |
| ИСТЁК | Дата истечения в прошлом | Красный |
| ОТОЗВАН | Поле revoked=1 в БД |
Красный |
Меню → 3
Удаление происходит в два шага (защита от случайного удаления):
Шаг 1 — Отзыв:
Устанавливает флаг revoked=1 в БД, промежуточный CA этого клиента исключается из clients-bundle.crt, Traefik-конфиг обновляется. Сертификат перестаёт работать немедленно — без перезагрузки Traefik.
Шаг 2 — Удаление файлов (при повторном входе):
Удаляет директорию с client.key, client.crt, client.p12, директорию промежуточного CA и запись из БД.
Такой двухшаговый процесс исключает случайное необратимое удаление.
Меню → 5
Создаёт корневой CA при первом запуске или пересоздаёт его при необходимости.
Имя CA (CN) → mTLS-Root-CA
Срок действия (дней) → 3650
Генерирует:
ca.key(4096 бит RSA) — приватный ключ, права 600ca.crt— самоподписанный корневой сертификатindex.txt,serial,index.txt.attr— база данных CA для OpenSSLcrl.pem— список отзыва (изначально пустой)openssl-ca.cnf— конфигурационный файл OpenSSL
⚠️ Пересоздание CA аннулирует все ранее выпущенные сертификаты.
Меню → 6
| Параметр | По умолчанию |
|---|---|
| Путь к dynamic-конфигам Traefik | /etc/traefik/dynamic |
| Путь к CA | /etc/traefik/certs/mtls |
| Путь к клиентским сертификатам | /etc/traefik/certs/mtls/clients |
| Имя выходного файла | mtls-manager.yml |
| Срок действия сертификата (дней) | 365 |
Настройки сохраняются в ~/.mtls-manager.conf с правами 600.
Все метаданные хранятся в ~/.mtls-manager.db — JSON-объект, где ключ это <service>__<certname>.
{
"myapp__alice": {
"name": "alice",
"service": "myapp",
"days": "365",
"note": "iPhone Alice",
"created": "2025-01-15",
"expires": "2026-01-15",
"revoked": "0",
"path": "/etc/traefik/certs/mtls/clients/myapp/alice",
"serial": "01",
"int_ca_path": "/etc/traefik/certs/mtls/intermediates/myapp__alice"
},
"__ca__": {
"cn": "mTLS-Root-CA",
"days": "3650",
"created": "2025-01-01 10:00:00"
}
}Запись __ca__ хранит метаданные корневого CA и исключается из списков клиентских сертификатов (префикс __).
Все операции с БД реализованы через встроенные Python3-скрипты (heredoc << 'PYEOF') — без внешних файлов.
Ключевой файл для Traefik. Содержит цепочку промежуточных CA всех активных (не отозванных) клиентов. Traefik использует его для верификации входящих клиентских сертификатов.
rebuild_bundle():
для каждого uid в БД:
если revoked != 1:
дописать int-ca.crt в bundle
если bundle пуст → использовать ca.crt
Обновляется автоматически при каждом изменении (выпуск/отзыв).
/etc/traefik/
├── certs/mtls/
│ ├── ca.key ← приватный ключ CA (600)
│ ├── ca.crt ← корневой сертификат CA
│ ├── crl.pem ← список отзыва
│ ├── openssl-ca.cnf ← конфиг OpenSSL
│ ├── index.txt ← база CA
│ ├── serial ← серийный счётчик
│ ├── clients-bundle.crt ← bundle активных int-CA
│ ├── intermediates/
│ │ └── <service>__<name>/
│ │ ├── int-ca.key ← ключ промежуточного CA
│ │ └── int-ca.crt ← сертификат промежуточного CA
│ └── clients/
│ └── <service>/
│ └── <name>/
│ ├── client.key
│ ├── client.crt
│ └── client.p12
└── dynamic/
└── mtls-manager.yml ← генерируемый конфиг Traefik
Для каждого клиентского сертификата создаётся отдельный промежуточный CA (intermediate CA), подписанный корневым CA.
Цепочка доверия:
Root CA → Int-CA (alice) → client.crt (alice)
Root CA → Int-CA (bob) → client.crt (bob)
Зачем это нужно:
- Гранулярный отзыв: при отзыве сертификата alice из bundle просто исключается
int-ca-alice.crt. Bob при этом не затронут. - Без CRL на стороне Traefik: не нужно настраивать проверку списков отзыва — достаточно перестроить bundle.
- Мгновенный эффект: Traefik подхватывает обновлённый bundle без перезагрузки (при включённом file provider).
Int-CA параметры:
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
pathlen:0 означает, что промежуточный CA не может подписывать другие CA — только конечные сертификаты.
При добавлении сервиса в режиме patch скрипт вносит изменения в указанный YAML-файл Traefik.
Алгоритм патча (реализован на Python3 через heredoc):
- Парсит YAML построчно (без зависимостей — только стандартный Python)
- Ищет секцию
routers:и в ней роутер с указанным именем - Внутри роутера находит блок
tls: - Добавляет строку
options: mtls-<service>(или заменяет существующую)
При удалении сервиса строка options: mtls-<service> удаляется из файла.
Возможные результаты патча: patched, already_patched, not_found.
Функция do_gen_traefik() создаёт файл <TRAEFIK_DYNAMIC_PATH>/<OUTPUT_FILE> со следующей структурой:
# Generated by mtls-manager — 2025-01-15 10:00:00
# DO NOT EDIT MANUALLY
tls:
options:
mtls-myapp:
clientAuth:
caFiles:
- "/etc/traefik/certs/mtls/clients-bundle.crt"
clientAuthType: RequireAndVerifyClientCert
minVersion: VersionTLS12
http:
routers:
myapp-mtls:
rule: "Host(`myapp.example.com`)"
entryPoints:
- websecure
service: myapp-mtls
tls:
options: mtls-myapp
services:
myapp-mtls:
loadBalancer:
servers:
- url: "http://172.17.0.1:3000"Секции http.routers и http.services генерируются только для сервисов в режиме new. Для patch-сервисов создаётся только блок tls.options.
При каждом выпуске нового .p12 браузер сохраняет в своё хранилище не только клиентский сертификат, но и промежуточный CA. Это может привести к накоплению устаревших записей.
Подробная инструкция по очистке для Linux, Windows, Android и iOS приведена в документе Очистка промежуточных.md.
Краткая инструкция:
Linux (Chrome):
apt install libnss3-tools
certutil -L -d /home/<user>/.local/share/pki/nssdb/
certutil -D -d /home/<user>/.local/share/pki/nssdb/ -n "opensophy - mTLS-Manager"Windows (Chrome/Edge):
Get-ChildItem -Path Cert:\CurrentUser\CA | Where-Object { $_.Subject -like "*opensophy*" } | Remove-Item| Пресет | Dynamic path | CA path |
|---|---|---|
p1 Dokploy |
/etc/dokploy/traefik/dynamic |
/etc/dokploy/traefik/dynamic/certificates/ca |
p2 Traefik |
/etc/traefik/dynamic |
/etc/traefik/certs/ca |
p3 Локально |
./traefik-local/dynamic |
./traefik-local/certs/ca |
- Нет поддержки ECDSA: скрипт использует RSA (2048 бит для клиентов, 4096 для CA). ECDSA не поддерживается.
- OCSP/CRL не настраивается: отзыв реализован через bundle, не через стандартные механизмы CRL/OCSP.
- Один bundle на все сервисы: все сервисы используют один
clients-bundle.crt. Изолировать bundle по сервисам невозможно без модификации скрипта. - Python3-парсинг YAML: патчинг конфигов Traefik реализован построчным разбором, а не через yaml-библиотеку. Нестандартные форматы YAML могут обрабатываться некорректно.
- Нет уведомлений об истечении: скрипт показывает статус СКОРО в интерфейсе, но не отправляет уведомления автоматически.