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

OpenSSL: проверка и разбор TLS-сертификатов в CLI

Уже забыли, когда последний раз сертификат на проде протухал неожиданно? Знакомо. OpenSSL умеет отвечать на вопросы о TLS-сертификатах быстрее, чем любой чекер из маркетплейса. Разбираем ключевые сценарии без воды.

Базовый разбор сертификата

Первая команда, с которой начинается любая диагностика — текстовый дамп сертификата.

openssl x509 -text -noout -in cert.pem

Вывод показывает Subject, Issuer, сроки валидности, алгоритм подписи и публичный ключ. Для быстрой справки без простыни:

# Только subject
openssl x509 -noout -subject -in cert.pem

# Только issuer
openssl x509 -noout -issuer -in cert.pem

# Только fingerprint (SHA-256)
openssl x509 -noout -fingerprint -sha256 -in cert.pem

Флаг -in принимает путь к файлу. Если сертификат скачан через браузер — обычно в формате PEM или DER. OpenSSL понимает оба, но для DER нужен дополнительный флаг:

openssl x509 -inform DER -in cert.der -text -noout
Примечание

-noout убирает base64-блок из вывода. Полезно, когда нужен только структурированный результат, а не копия сертификата.

Срок действия: dates и проверка на просрочку

Для мониторинга удобнее получить только даты:

openssl x509 -noout -dates -in cert.pem

Типичный вывод:

notBefore=Jan 15 00:00:00 2024 GMT
notAfter=Jan 14 23:59:59 2025 GMT

Для автоматизации удобнее получить timestamp и считать разницу:

# Оставшиеся дни до экспайра
not_after=$(openssl x509 -noout -enddate -in cert.pem | cut -d= -f2)
days_left=$(( ($(date -d "$not_after" +%s) - $(date +%s)) / 86400 ))
echo "$days_left days left"

Если days_left отрицательный — сертификат уже просрочен.

Подсказка

Для проверки сразу нескольких хостов из inventory удобен однострочник:

for host in api.example.com admin.example.com; do
  echo -n "$host: "
  echo | openssl s_client -servername "$host" -connect "$host":443 2>/dev/null \
    | openssl x509 -noout -enddate
done

-servername передаёт SNI — без него некоторые хосты отдают дефолтный сертификат.

Проверка цепочки: s_client и verify

Подключение с выводом сертификатов:

openssl s_client -connect example.com:443 -showcerts </dev/null

В выводе будет цепочка от leaf-сертификата до root CA. Для фильтрации только сертификатов:

openssl s_client -connect example.com:443 -showcerts </dev/null \
  | awk '/-----BEGIN/,/-----END/{if(/-----BEGIN/)a=1;a;if(/-----END/)a=0}' > chain.pem

Проверка цепочки через системный store:

openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt chain.pem

Если проверка падает с error 20 at 0 depth lookup, значит, промежуточный CA не найден. Обычная причина — на сервере некорректно настроен chain.

Предупреждение

openssl verify по умолчанию использует системный store. В Ubuntu это /etc/ssl/certs/ca-certificates.crt, в Alpine — отдельный пакет ca-certificates. Если проверка не работает — проверьте, что пакет установлен.

Короткая проверка без сохранения в файл:

echo | openssl s_client -connect example.com:443 2>/dev/null \
  | openssl verify

Вывод Verify return code: 0 (ok) означает успех.

Чтобы не залипать в интерактивном режиме и завершать процесс с ошибкой при плохой цепочке:

openssl s_client -connect example.com:443 -servername example.com \
  -quiet -verify_return_error </dev/null

Принудительная версия протокола или cipher — когда нужно убедиться, что сервер ещё принимает конкретный handshake:

openssl s_client -connect example.com:443 -servername example.com \
  -tls1_2 -cipher ECDHE-RSA-AES256-GCM-SHA384 -quiet </dev/null

Для внутреннего CA передайте бандл явно. Без -CAfile частный PKI обычно отвечает Verify return code: 21 (unable to get local issuer certificate):

openssl s_client -connect example.com:443 -servername example.com \
  -CAfile /etc/ssl/certs/ca-bundle.crt -verify_return_error -quiet </dev/null

Извлечение CN и SAN

Common Name извлекается напрямую:

openssl x509 -noout -subject -in cert.pem | grep -oP '(?<=CN = )[^,]+'

Но CN давно недостаточно — современные сертификаты используют Subject Alternative Names (SAN). OpenSSL 1.1.1+ умеет вытащить их чисто:

openssl x509 -noout -ext subjectAltName -in cert.pem

Вывод:

X509v3 Subject Alternative Name:
    DNS:example.com, DNS:www.example.com, DNS:api.example.com, IP:192.0.2.1

Для получения только списка DNS-имен:

openssl x509 -noout -ext subjectAltName -in cert.pem \
  | grep -oP '(?<=DNS:)[^,]+'
Примечание

Если SAN отсутствует (старый сертификат), браузеры падают в fallback на CN. При проверке API-ендпоинтов это объясняет, почему curl ругается, а браузер открывает.

Сравнение сроков экспайри нескольких хостов

Скрипт для чека по списку хостов — практическая основа мониторинга:

#!/bin/bash
# check-certs.sh — проверка сроков действия сертификатов

check_host() {
  local host=$1
  local port=${2:-443}
  
  echo | timeout 5 openssl s_client -servername "$host" -connect "$host:$port" 2>/dev/null \
    | openssl x509 -noout -enddate 2>/dev/null \
    | cut -d= -f2 \
    | while read date; do
        ts=$(date -d "$date" +%s)
        now=$(date +%s)
        days=$(( (ts - now) / 86400 ))
        printf "%-30s %3d days  %s\n" "$host" "$days" "$date"
      done
}

# Пример использования
for h in api.example.com admin.example.com legacy.internal; do
  check_host "$h"
done

Типичный вывод:

api.example.com                   45 days  Jan 14 23:59:59 2025 GMT
admin.example.com               -12 days  Dec  1 23:59:59 2024 GMT
legacy.internal                -120 days  Aug  5 23:59:59 2024 GMT

Отрицательные значения — просроченные сертификаты. В продакшене удобно завернуть в cron с уведомлением в чат при пороге меньше 30 дней.

Таблица ключевых флагов

КомандаФлагНазначение
x509-textПолный дамп в текст
x509-nooutНе выводить base64-блок
x509-datesNotBefore, NotAfter
x509-subjectSubject (CN, O, OU)
x509-issuerIssuer (выдавщий CA)
x509-fingerprint -sha256Отпечаток сертификата
x509-enddateТолько срок окончания
x509-ext san / subjectAltNameАльтернативные имена
s_client-connect host:portСоединение с TLS
s_client-servername nameSNI (обязателен для vhost)
s_client-showcertsВывести всю цепочку
s_client-quietНе заходить в интерактивный режим
s_client-tls1_2 / -tls1_3Принудительная версия TLS
s_client-verify_return_errorНенулевой код при ошибке валидации
verify-CAfile pathФайл доверенных CA
verify-partial_chainПринять неполную цепочку
Подсказка

openssl s_client умеет не только читать сертификаты. С флагом -starttls smtp или -starttls pop3 проверяет почтовые серверы. -http извлекает HTTP-заголовки через TLS-соединение. Это полезно для диагностики miTM-фильтров.

Все команды работают из коробки в любом дистрибутиве Linux. Никаких зависимостей, кроме самого OpenSSL — утилита есть на каждом сервере. Если нет — ставьтся за секунду: apt install openssl или apk add openssl.