Skip to content

opensophy-projects/mtls

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 

Repository files navigation

mtls.sh — mTLS Certificate Manager

Opensophy — open-source инструмент для управления mTLS-сертификатами под Traefik.
Лицензия: MIT
Репозиторий: opensophy-projects

image

Скрипт проверен на 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.

Режим new

Создаёт новый роутер и сервис в 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).

Режим patch

Добавляет 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

Порядок действий:

  1. Выбрать сервис из списка
  2. Задать имя сертификата (латиница, без пробелов — пробелы заменяются на -)
  3. Указать срок действия (по умолчанию: значение из настроек, стандарт 365 дней)
  4. Добавить заметку (для кого / чего выдан)
  5. Задать пароль для .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 и запись из БД.

Такой двухшаговый процесс исключает случайное необратимое удаление.


Модуль: Создание CA

Меню → 5

Создаёт корневой CA при первом запуске или пересоздаёт его при необходимости.

Имя CA (CN)            → mTLS-Root-CA
Срок действия (дней)   → 3650

Генерирует:

  • ca.key (4096 бит RSA) — приватный ключ, права 600
  • ca.crt — самоподписанный корневой сертификат
  • index.txt, serial, index.txt.attr — база данных CA для OpenSSL
  • crl.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.


Внутренние механизмы

База данных (db_*)

Все метаданные хранятся в ~/.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') — без внешних файлов.

Bundle (clients-bundle.crt)

Ключевой файл для 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 (per-client)

Для каждого клиентского сертификата создаётся отдельный промежуточный CA (intermediate CA), подписанный корневым CA.

Цепочка доверия:

Root CA  →  Int-CA (alice)  →  client.crt (alice)
Root CA  →  Int-CA (bob)    →  client.crt (bob)

Зачем это нужно:

  1. Гранулярный отзыв: при отзыве сертификата alice из bundle просто исключается int-ca-alice.crt. Bob при этом не затронут.
  2. Без CRL на стороне Traefik: не нужно настраивать проверку списков отзыва — достаточно перестроить bundle.
  3. Мгновенный эффект: Traefik подхватывает обновлённый bundle без перезагрузки (при включённом file provider).

Int-CA параметры:

basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign

pathlen:0 означает, что промежуточный CA не может подписывать другие CA — только конечные сертификаты.


Patch-режим

При добавлении сервиса в режиме patch скрипт вносит изменения в указанный YAML-файл Traefik.

Алгоритм патча (реализован на Python3 через heredoc):

  1. Парсит YAML построчно (без зависимостей — только стандартный Python)
  2. Ищет секцию routers: и в ней роутер с указанным именем
  3. Внутри роутера находит блок tls:
  4. Добавляет строку options: mtls-<service> (или заменяет существующую)

При удалении сервиса строка options: mtls-<service> удаляется из файла.

Возможные результаты патча: patched, already_patched, not_found.


Генерация Traefik-конфига

Функция 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 могут обрабатываться некорректно.
  • Нет уведомлений об истечении: скрипт показывает статус СКОРО в интерфейсе, но не отправляет уведомления автоматически.

About

Create MTLs and manage services quickly and easily

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Languages