Акция Панель управления ispmanager для VDS — первый месяц бесплатно
до 31.07.2026 Подробнее
Выберите продукт

mTLS для API: проверка client certificate в Nginx и HAProxy, отзыв и ротация

Разбираем mutual TLS для защиты API: как выпустить клиентский сертификат в своей PKI, включить и проверить mTLS в Nginx и HAProxy, организовать отзыв через CRL или OCSP и выполнить безопасную ротацию ключей и CA без простоя.
mTLS для API: проверка client certificate в Nginx и HAProxy, отзыв и ротация

mTLS (mutual TLS) — это режим TLS, где сертификат предъявляют обе стороны: сервер доказывает свою подлинность клиенту, а клиент — серверу. Для API это удобная «админская» форма аутентификации: доступ привязан к закрытому ключу и цепочке доверия, а не к строке токена, которую можно случайно утянуть из логов или переменных окружения.

Ниже — практическая настройка mTLS для API на двух самых частых точках терминации: Nginx и HAProxy. Параллельно разберём выпуск тестовой PKI, отзыв сертификатов (CRL/OCSP) и то, как делать ротацию без простоя и сюрпризов.

Когда mTLS для API — действительно хорошая идея

mTLS чаще всего выбирают для сервис‑to‑сервис интеграций и «машинных» клиентов, где вы контролируете окружение выполнения и хранение ключей:

  • внутренние API между микросервисами, особенно через балансировщик или API gateway;
  • доступ партнёров к B2B API, когда у каждого партнёра должен быть свой идентификатор и возможность быстрого отзыва;
  • админские/технические API (deploy hooks, internal tooling), куда не хочется пускать по длинноживущим токенам.

При этом mTLS не отменяет авторизацию уровня приложения. Типичный паттерн: mTLS отвечает за идентификацию и базовый допуск (кто ты), а приложение — за права (что тебе можно). Но в простых сценариях mTLS может быть и единственным барьером.

Самый частый источник проблем с mTLS — не конфиг прокси, а жизненный цикл сертификатов: выпуск, хранение закрытого ключа, отзыв, ротация и наблюдаемость. Если вы не готовы поддерживать эти процессы, токены могут оказаться проще.

PKI для mTLS: что именно нужно выпустить

Чтобы сервер мог проверить client certificate, ему нужен корневой (или промежуточный) удостоверяющий центр (CA), которому он доверяет, и клиентский сертификат, подписанный этим CA.

Минимальный набор:

  • CA сертификат (или цепочка CA), которой будет доверять Nginx/HAProxy при проверке клиента;
  • клиентский сертификат и соответствующий закрытый ключ на стороне клиента;
  • понятные поля идентификации: например, subjectAltName (SAN) с идентификатором клиента/сервиса или хотя бы CN.

Практический совет: для API лучше заранее заложить «смысловую» идентичность в сертификат. Например:

  • SAN DNS: svc.billing.internal для сервисов;
  • SAN URI: spiffe://company/service/billing, если вы стандартизируете идентичности;
  • CN: partner-acme для партнёрских клиентов (как минимум, чтобы удобно читать логи).

Быстрый выпуск тестовой PKI через OpenSSL (для стенда)

Ниже пример для стенда/лаборатории. В продакшене лучше иметь процессы и отдельную инфраструктуру/политику, но принцип тот же.

mkdir -p pki
cd pki
openssl genrsa -out ca.key 4096
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.crt -subj "/CN=Fastfox Demo Root CA"
openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr -subj "/CN=api-client-1"
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365 -sha256

Для реального client certificate желательно задавать расширения, в частности Extended Key Usage: Client Authentication. Многие связки будут работать и без строгой проверки EKU, но если вы включите строгие политики, расширения должны быть корректными.

Если вы используете публично доверенные сертификаты на внешнем API, удобнее централизованно управлять продлением и цепочками — например, через SSL-сертификаты для фронтендов, а mTLS оставить для клиентской стороны вашей PKI.

Схема цепочки доверия PKI для проверки клиентских сертификатов в mTLS

Nginx: включаем проверку клиентского сертификата

В Nginx mTLS настраивается в HTTPS server блоке. Ключевые директивы:

  • ssl_client_certificate — CA/цепочка CA для проверки клиента;
  • ssl_verify_client — режим проверки (обычно on или optional);
  • ssl_verify_depth — глубина цепочки (полезно при промежуточных CA).

Жёсткий режим: mTLS обязателен для всего vhost

server {
    listen 443 ssl;
    server_name api.example.internal;

    ssl_certificate     /etc/nginx/tls/server.fullchain.pem;
    ssl_certificate_key /etc/nginx/tls/server.key;

    ssl_client_certificate /etc/nginx/mtls/clients-ca.crt;
    ssl_verify_client on;
    ssl_verify_depth 2;

    location / {
        proxy_pass http://app_upstream;

        proxy_set_header X-Client-Verify $ssl_client_verify;
        proxy_set_header X-Client-DN $ssl_client_s_dn;
        proxy_set_header X-Client-Fingerprint $ssl_client_fingerprint;
    }
}

Полезные переменные Nginx:

  • $ssl_client_verify: SUCCESS, FAILED, NONE;
  • $ssl_client_s_dn: subject DN клиента;
  • $ssl_client_fingerprint: отпечаток сертификата (удобно как стабильный идентификатор для логов);
  • $ssl_client_escaped_cert: сертификат строкой (часто избыточно и тяжело для логов и заголовков).

Важно: если вы проксируете идентичность дальше в приложение, не принимайте заголовки X-Client-* «на веру», иначе клиент может прислать их сам. Практика: очистить входящие заголовки и выставлять свои только на mTLS-входе.

proxy_set_header X-Client-Verify "";
proxy_set_header X-Client-DN "";
proxy_set_header X-Client-Fingerprint "";

proxy_set_header X-Client-Verify $ssl_client_verify;
proxy_set_header X-Client-DN $ssl_client_s_dn;
proxy_set_header X-Client-Fingerprint $ssl_client_fingerprint;

Мягкий режим: mTLS только для части API

Иногда нужно оставить публичный endpoint (например, /health для внешнего мониторинга) и закрыть только часть маршрутов (/admin, /partner, /private).

Тонкость: ssl_verify_client применяется на уровне server, но можно включить optional, а затем в нужных location проверять результат.

server {
    listen 443 ssl;
    server_name api.example.internal;

    ssl_certificate     /etc/nginx/tls/server.fullchain.pem;
    ssl_certificate_key /etc/nginx/tls/server.key;

    ssl_client_certificate /etc/nginx/mtls/clients-ca.crt;
    ssl_verify_client optional;

    location = /health {
        return 200;
    }

    location /private/ {
        if ($ssl_client_verify != SUCCESS) { return 403; }
        proxy_pass http://app_upstream;
    }

    location / {
        proxy_pass http://app_upstream;
    }
}

Конструкция if в Nginx имеет нюансы, но для простого return обычно безопасна. Если хотите «чище» — делайте через map и переменную с кодом ответа.

Если нужно сравнить подходы Nginx и Apache по клиентским сертификатам, пригодится отдельный разбор: mTLS и проверка client certificate в Apache.

HAProxy: mTLS на фронтенде

В HAProxy проверка клиента задаётся SSL‑параметрами в bind. Ключевые элементы:

  • verify required — требовать клиентский сертификат;
  • ca-file — доверенная цепочка CA;
  • crl-file — CRL, если используете отзыв через CRL;
  • ACL по данным сертификата: DN, fingerprint, issuer.

Пример фронтенда с обязательным mTLS

frontend fe_api_tls
    bind :443 ssl crt /etc/haproxy/tls/server.pem ca-file /etc/haproxy/mtls/clients-ca.crt verify required

    mode http
    option httplog

    http-request set-header X-Client-Verify %[ssl_c_verify]
    http-request set-header X-Client-DN %[ssl_c_s_dn]
    http-request set-header X-Client-Fingerprint %[ssl_c_sha1]

    default_backend be_api

Пара примечаний по выборке:

  • %[ssl_c_verify] — результат проверки (в популярных конфигурациях 0 означает успех);
  • %[ssl_c_s_dn] — subject DN;
  • %[ssl_c_sha1] — отпечаток SHA1: как идентификатор лучше SHA256, но в инфраструктуре часто используют то, что проще собрать в ACL/логи.

mTLS только для части путей

Частая схема: один host/порт общий, но для части URL требуем клиентский сертификат через ACL. Для этого на bind включают verify optional, а затем запрещают доступ, если сертификата нет или он невалиден.

frontend fe_api_tls
    bind :443 ssl crt /etc/haproxy/tls/server.pem ca-file /etc/haproxy/mtls/clients-ca.crt verify optional

    mode http

    acl is_private path_beg /private/
    acl mtls_ok ssl_c_verify 0

    http-request deny deny_status 403 if is_private !mtls_ok

    default_backend be_api

Так публичные маршруты будут работать без клиентского сертификата, а приватные — только при успешной проверке.

Когда mTLS завершается на отдельном балансировщике, нередко удобнее держать такую точку терминации на выделенной машине и масштабировать независимо от приложения. Для этого подойдёт VDS с предсказуемой сетью и возможностью быстро добавлять фронтенды.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

CRL и OCSP: как отзывать клиентские сертификаты

Сильная сторона mTLS — адресный отзыв доступа: можно заблокировать конкретного клиента, не трогая остальных. Вопрос — как точка терминации узнаёт, что сертификат отозван.

CRL (Certificate Revocation List)

CRL — это файл со списком серийных номеров отозванных сертификатов. Плюсы: простота и автономность (не нужен сетевой доступ к OCSP). Минусы: файл нужно регулярно обновлять и доставлять на все узлы, а при большом количестве отзывов CRL растёт.

Поддержка на практике:

  • Nginx: отзыв через CRL подключают директивой ssl_crl (зависит от сборки и версии);
  • HAProxy: crl-file на bind.

Пример для HAProxy:

bind :443 ssl crt /etc/haproxy/tls/server.pem ca-file /etc/haproxy/mtls/clients-ca.crt crl-file /etc/haproxy/mtls/clients.crl verify required

Операционный момент: держите CRL рядом с пайплайном отзыва. Как только вы добавили сертификат в список отзыва, убедитесь, что новый CRL доставлен на все точки терминации и сервис перечитал конфигурацию (reload).

OCSP для клиентских сертификатов

OCSP чаще обсуждают для серверных сертификатов и stapling. Теоретически механизм применим и к клиентским, но на практике в mTLS‑сценариях часто выбирают CRL или короткий TTL клиентских сертификатов, потому что:

  • проверка OCSP на фронтенде может требовать сетевого доступа и добавляет вариативность задержек;
  • нужно продумать отказоустойчивость (что делать при недоступности OCSP: fail-open или fail-closed);
  • не все окружения одинаково предсказуемо ведут себя с OCSP именно для клиентской стороны.

Если вы строите PKI «по-взрослому», закладывайте наблюдаемость: логи и метрики по статусам проверок, чтобы отличать отзыв сертификата от инфраструктурной деградации.

Вывод диагностики TLS рукопожатия и проверки клиентского сертификата

Идентификация клиента: CN, SAN или fingerprint

В mTLS важно заранее решить, что будет «логином» клиента. Варианты:

  • Fingerprint — однозначно и удобно для allowlist/denylist, но при перевыпуске сертификата меняется, и правила надо обновлять;
  • CN — просто, но может быть не уникальным и считается устаревающим местом для идентичности;
  • SAN — лучший выбор для идентичности: можно хранить тип (DNS/URI) и структуру.

Практичный подход для API: хранить в SAN «человеческий» идентификатор клиента/сервиса, а fingerprint использовать как дополнительный атрибут для расследований и быстрого блокирования.

Ротация сертификатов без простоя

Ротация — самая частая эксплуатационная задача. Чтобы она не превращалась в аврал:

  • делайте клиентские сертификаты короткими (например, 30–90 дней) и автоматизируйте выпуск;
  • поддерживайте одновременную валидность старого и нового сертификата клиента на время переката;
  • если меняете CA, добавляйте новый CA в trust store заранее и держите оба до завершения миграции;
  • проверяйте, что reload конфигурации не рвёт соединения (у Nginx и HAProxy обычно всё нормально при корректном reload).

Типовая схема смены CA:

  1. Добавить новый CA в ssl_client_certificate/ca-file (цепочкой), оставить старый.
  2. Выпустить клиентам новые сертификаты от нового CA и развернуть.
  3. Проверить, что все клиенты перешли (по логам/метрикам и списку активных fingerprint).
  4. Убрать старый CA, обновить CRL/политику.

Проверка и диагностика: как быстро понять, что не так

Для первичной проверки удобно использовать openssl s_client: можно явно указать клиентский сертификат и ключ и увидеть, прошла ли взаимная проверка.

openssl s_client -connect api.example.internal:443 -servername api.example.internal -cert client.crt -key client.key -CAfile ca.crt

Что смотреть в выводе:

  • успешность TLS‑рукопожатия;
  • ошибки валидации цепочки (не тот CA, неполная цепочка, неверный depth);
  • совпадает ли SNI (-servername) с сертификатом сервера.

Для API поверх HTTP дополнительно проверьте запрос через curl с клиентским сертификатом:

curl -v --cert client.crt --key client.key --cacert ca.crt https://api.example.internal/private/ping

Частые ошибки в mTLS и как их избегать

Сервер «не видит» промежуточный CA

Симптом: клиентский сертификат вроде подписан вашим CA, но прокси не принимает. Частая причина — в trust store лежит не та цепочка (доверили только root, а у вас intermediate, или наоборот). Решение: в ssl_client_certificate/ca-file держать правильную цепочку CA, которая именно валидирует клиентские сертификаты.

Слишком широкое доверие

Если в trust store положить «корпоративный» CA, которым подписывают вообще всё, вы рискуете тем, что любой сертификат из этого контура сможет пройти как клиентский. Для API лучше заводить отдельный CA или отдельный intermediate под клиентов API.

Нет политики отзыва

mTLS без отзыва — это почти как вечные токены. Минимум: короткий срок жизни client cert + процесс мгновенной блокировки (CRL или denylist по fingerprint). И обязательно тестируйте отзыв, а не только выпуск.

Потеря идентичности на уровне приложения

Если у вас несколько прокси, можно потерять контекст «кто клиент». Либо завершайте mTLS на самом приложении, либо стандартизируйте передачу идентичности (DN/SAN/fingerprint) и защищайте эти заголовки от подмены на границе.

Итоговый чек-лист для mTLS в API

  • Определили модель идентичности (SAN/CN/fingerprint) и формат логирования.
  • Выпустили отдельный CA (или intermediate) под API‑клиентов.
  • Настроили Nginx (ssl_client_certificate + ssl_verify_client) или HAProxy (verify required/verify optional).
  • Продумали отзыв: CRL и доставка на точки, либо альтернативная стратегия (короткий TTL + denylist/allowlist).
  • Сделали ротацию: параллельная валидность, миграция CA без простоя.
  • Добавили диагностику: openssl s_client, curl, логи по результатам $ssl_client_verify/ssl_c_verify.

Если вы один раз выстроите PKI‑процесс и дисциплину ротации, mTLS становится предсказуемым способом защищать API — с понятным аудитом и быстрым отзывом доступа без «перевыпуска всех токенов».

Поделиться статьей

Вам будет интересно

Debian/Ubuntu: mount: wrong fs type, bad option, bad superblock — как быстро найти и исправить причину OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: mount: wrong fs type, bad option, bad superblock — как быстро найти и исправить причину

Ошибка mount: wrong fs type, bad option, bad superblock в Debian/Ubuntu может означать и простую опечатку в имени раздела, и пробл ...
Debian/Ubuntu: XFS metadata corruption и emergency read-only — пошаговое восстановление OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: XFS metadata corruption и emergency read-only — пошаговое восстановление

Если XFS-раздел внезапно стал доступен только для чтения, а сервер ушёл в emergency mode, главное — не спешить. Разберём безопасны ...
Debian/Ubuntu: как исправить Failed to fetch при apt update OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: как исправить Failed to fetch при apt update

Ошибка Failed to fetch при apt update в Debian и Ubuntu обычно связана не с самим APT, а с DNS, сетью, зеркалом, прокси, временем ...