ssl-tls-management — скилл для Claude Code: сертификаты и HTTPS
Скилл для работы с SSL/TLS: выпуск сертификатов Let's Encrypt, автопродление, cert-manager в Kubernetes и подбор шифров. Пригодится, когда настраиваешь HTTPS или чинишь истёкший сертификат.
npx skills add BagelHole/DevOps-Security-Agent-Skills --skill ssl-tls-management -a claude-codeЧто делает ssl-tls-management
Скилл закрывает весь путь сертификата: от выпуска через certbot до автоматического продления и разбора шифров через openssl. Внутри — готовые команды для nginx, standalone-режима и DNS-челленджа, включая wildcard-сертификаты. Для Kubernetes описан cert-manager через Helm с примерами ClusterIssuer и Certificate.
Продление настраивается через systemd-таймер, который запускает certbot renew дважды в день, плюс deploy-хуки для перезагрузки nginx и haproxy после обновления. Есть отдельный раздел по openssl: генерация ключей RSA и ECDSA, создание CSR с SAN и самоподписанные сертификаты для разработки.
Заранее понадобится домен с доступом к DNS для публичных сертификатов, root на веб-сервере и установленный certbot. Для внутренних сервисов в Kubernetes нужен кластер с Helm. Если DNS-записи нет, HTTP-челлендж не пройдёт — это повод сначала проверить, куда указывает домен.
Какие задачи решает
- выпустить сертификат Let's Encrypt для нового домена
- настроить автопродление, чтобы HTTPS не падал
- развернуть cert-manager и выдать TLS в Kubernetes
- сгенерировать CSR с SAN для внутреннего центра сертификации
- проверить шифры и версии TLS перед аудитом
Примеры запросов агенту
- Настрой HTTPS для example.com через certbot с автопродлением
- Поставь cert-manager в кластер и выпусти wildcard-сертификат для *.example.com
- Сгенерируй самоподписанный сертификат для локальной разработки
Как установить ssl-tls-management
1Через Skills CLI (Vercel)
npx skills add BagelHole/DevOps-Security-Agent-Skills --skill ssl-tls-management -a claude-code2Вручную: положить файлы
~/.claude/skills/ssl-tls-management/SKILL.mdПапка скилла целиком; для одного проекта — .claude/skills/ssl-tls-management/
1Через Skills CLI (Vercel)
npx skills add BagelHole/DevOps-Security-Agent-Skills --skill ssl-tls-management -a codex2Вручную: положить файлы
.agents/skills/ssl-tls-management/SKILL.mdДля всех проектов — ~/.agents/skills/
1Через Skills CLI (Vercel)
npx skills add BagelHole/DevOps-Security-Agent-Skills --skill ssl-tls-management -a cursor2Вручную: положить файлы
.cursor/skills/ssl-tls-management/SKILL.mdCursor читает и .agents/skills/, и .claude/skills/
1GitHub CLI
gh skill install BagelHole/DevOps-Security-Agent-Skills ssl-tls-management2Вручную: положить файлы
.github/skills/ssl-tls-management/SKILL.md1Через Skills CLI (Vercel)
npx skills add BagelHole/DevOps-Security-Agent-Skills --skill ssl-tls-management -a gemini-cli2Командой платформы
gemini skills install https://github.com/BagelHole/DevOps-Security-Agent-Skills --path security/network/ssl-tls-managementВручную: положить файлы
.devin/skills/ssl-tls-management/SKILL.mdСтарый путь .windsurf/skills/ тоже читается
Через Skills CLI (Vercel)
npx skills add BagelHole/DevOps-Security-Agent-Skills --skill ssl-tls-management -a opencodeClaude.ai
Упакуйте папку в zip и загрузите в Settings → Capabilities → Skills
По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов
Вопросы о ssl-tls-management
Как установить ssl-tls-management в Claude?
Выполни npx skills add BagelHole/DevOps-Security-Agent-Skills --skill ssl-tls-management -a claude-code. Скилл появится в ~/.claude/skills/ssl-tls-management/SKILL.md.
Что делает ssl-tls-management?
Помогает выпускать и продлевать сертификаты Let's Encrypt, настраивать cert-manager в Kubernetes и разбираться с шифрами через openssl.
Что нужно для работы скилла?
Домен с контролем DNS для публичных сертификатов, root-доступ на веб-сервере и установленный certbot. Для Kubernetes — кластер с Helm.
Исходный текст
SKILL.md · MIT
--- name: ssl-tls-management description: Manage SSL/TLS certificates with Let's Encrypt and internal PKI. Configure secure HTTPS, certificate renewal, and cipher suites. Use when implementing secure communications. license: MIT metadata: author: devops-skills version: "1.0" --- # SSL/TLS Management Manage certificates and secure communications across web servers, Kubernetes clusters, and internal services. ## When to Use This Skill Use this skill when: - Setting up HTTPS for a new web application - Automating certificate renewal with Let's Encrypt - Deploying cert-manager in Kubernetes - Configuring TLS for internal service-to-service communication - Auditing cipher suites and TLS versions for compliance - Responding to an expiring or compromised certificate ## Prerequisites - Domain name with DNS control for public certificates - Root/sudo access on web servers - `certbot` installed for Let's Encrypt - `openssl` CLI available (installed by default on most Linux distros) - Kubernetes cluster with Helm for cert-manager deployment - Understanding of X.509 certificate chain of trust ## Let's Encrypt with Certbot ### Installation and Certificate Issuance ```bash # Install certbot (Ubuntu/Debian) apt update && apt install -y certbot python3-certbot-nginx # Obtain certificate for nginx (interactive) certbot --nginx -d example.com -d www.example.com # Non-interactive mode for automation certbot certonly --nginx \ -d example.com \ -d www.example.com \ --non-interactive \ --agree-tos \ --email [email protected] # Standalone mode (when no web server is running) certbot certonly --standalone \ -d example.com \ --preferred-challenges http # DNS challenge (for wildcard certs) certbot certonly --manual \ --preferred-challenges dns \ -d "*.example.com" \ -d example.com # Using DNS plugin for automation (Cloudflare example) pip install certbot-dns-cloudflare cat > /etc/letsencrypt/cloudflare.ini << 'EOF' dns_cloudflare_api_token = YOUR_CLOUDFLARE_API_TOKEN EOF chmod 600 /etc/letsencrypt/cloudflare.ini certbot certonly --dns-cloudflare \ --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \ -d "*.example.com" \ -d example.com ``` ### Renewal Automation ```bash # Test renewal certbot renew --dry-run # Systemd timer (preferred over cron) cat > /etc/systemd/system/certbot-renewal.service << 'EOF' [Unit] Description=Certbot certificate renewal After=network-online.target [Service] Type=oneshot ExecStart=/usr/bin/certbot renew --quiet --deploy-hook "systemctl reload nginx" EOF cat > /etc/systemd/system/certbot-renewal.timer << 'EOF' [Unit] Description=Run certbot renewal twice daily [Timer] OnCalendar=*-*-* 00,12:00:00 RandomizedDelaySec=3600 Persistent=true [Install] WantedBy=timers.target EOF systemctl enable --now certbot-renewal.timer # Verify timer is active systemctl list-timers certbot-renewal.timer # Renewal hooks for post-renewal actions mkdir -p /etc/letsencrypt/renewal-hooks/deploy cat > /etc/letsencrypt/renewal-hooks/deploy/reload-services.sh << 'HOOK' #!/bin/bash systemctl reload nginx # Also reload other services using the cert systemctl reload haproxy 2>/dev/null || true HOOK chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-services.sh ``` ## cert-manager for Kubernetes ### Installation ```bash # Install with Helm helm repo add jetstack https://charts.jetstack.io helm repo update helm install cert-manager jetstack/cert-manager \ --namespace cert-manager \ --create-namespace \ --version v1.14.0 \ --set installCRDs=true \ --set prometheus.enabled=true # Verify installation kubectl get pods -n cert-manager kubectl get crds | grep cert-manager ``` ### ClusterIssuer Configurations ```yaml # letsencrypt-staging (use for testing first) apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt-staging spec: acme: server: https://acme-staging-v02.api.letsencrypt.org/directory email: [email protected] privateKeySecretRef: name: letsencrypt-staging solvers: - http01: ingress: class: nginx --- # letsencrypt-prod apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt-prod spec: acme: server: https://acme-v02.api.letsencrypt.org/directory email: [email protected] privateKeySecretRef: name: letsencrypt-prod solvers: - http01: ingress: class: nginx --- # DNS challenge solver (for wildcard certs with Cloudflare) apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt-prod-dns spec: acme: server: https://acme-v02.api.letsencrypt.org/directory email: [email protected] privateKeySecretRef: name: letsencrypt-prod-dns solvers: - dns01: cloudflare: apiTokenSecretRef: name: cloudflare-api-token key: api-token --- # Self-signed CA issuer for internal services apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: selfsigned-ca spec: selfSigned: {} ``` ### Certificate Resources ```yaml # Public-facing certificate apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: example-cert namespace: default spec: secretName: example-tls issuerRef: name: letsencrypt-prod kind: ClusterIssuer dnsNames: - example.com - www.example.com duration: 2160h # 90 days renewBefore: 720h # 30 days before expiry --- # Wildcard certificate apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: wildcard-cert namespace: default spec: secretName: wildcard-tls issuerRef: name: letsencrypt-prod-dns kind: ClusterIssuer dnsNames: - "*.example.com" - example.com --- # Ingress with automatic TLS apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress annotations: cert-manager.io/cluster-issuer: letsencrypt-prod spec: tls: - hosts: - example.com secretName: example-tls rules: - host: example.com http: paths: - path: / pathType: Prefix backend: service: name: web port: number: 80 ``` ## OpenSSL Commands Reference ```bash # Generate a private key openssl genrsa -out server.key 4096 # Generate an ECDSA key (preferred for performance) openssl ecparam -genkey -name prime256v1 -out server-ec.key # Generate a CSR (Certificate Signing Request) openssl req -new -key server.key -out server.csr \ -subj "/C=US/ST=California/L=San Francisco/O=Acme Corp/CN=example.com" # Generate CSR with SAN (Subject Alternative Names) openssl req -new -key server.key -out server.csr -config <(cat <<EOF [req] default_bits = 4096 distinguished_name = dn req_extensions = san prompt = no [dn] CN = example.com O = Acme Corp C = US [san] subjectAltName = DNS:example.com,DNS:www.example.com,DNS:api.example.com EOF ) # Generate self-signed certificate (development/testing) openssl req -x509 -nodes -days 365 -newkey rsa:4096 \ -keyout selfsigned.key -out selfsigned.crt \ -subj "/CN=localhost" # View certificate details openssl x509 -in cert.pem -noout -text # Check certificate expiration date openssl x509 -in cert.pem -noout -dates # Check remote certificate openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | \ openssl x509 -noout -dates -subject -issuer # Verify certificate chain openssl verify -CAfile ca-bundle.crt server.crt # Check certificate chain from remote server openssl s_client -connect example.com:443 -showcerts 2>/dev/null | \ openssl x509 -noout -text # Convert PEM to PKCS12 openssl pkcs12 -export -out cert.pfx -inkey server.key -in server.crt -certfile ca.crt # Convert PKCS12 to PEM openssl pkcs12 -in cert.pfx -out cert.pem -nodes # Test TLS connection and cipher negotiation openssl s_client -connect example.com:443 -tls1_3 openssl s_client -connect example.com:443 -cipher 'ECDHE-RSA-AES256-GCM-SHA384' ``` ## Strong TLS Configuration ### Nginx ```nginx server { listen 443 ssl http2; server_name example.com; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # Protocol versions ssl_protocols TLSv1.2 TLSv1.3; # Cipher suites (TLS 1.2) ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; # Session settings ssl_session_timeout 1d; ssl_session_cache shared:SSL:10m; ssl_session_tickets off; # OCSP stapling ssl_stapling on; ssl_stapling_verify on; ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem; resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s; # Security headers add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; add_header X-Content-Type-Options nosniff always; add_header X-Frame-Options DENY always; # HTTP to HTTPS redirect (in separate server block) } server { listen 80; server_name example.com www.example.com; return 301 https://$host$request_uri; } ``` ### Apache ```apache <VirtualHost *:443> ServerName example.com SSLEngine on SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384 SSLHonorCipherOrder off SSLUseStapling on SSLStaplingCache shmcb:/tmp/stapling_cache(128000) Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" </VirtualHost> ``` ## Certificate Monitoring ```bash #!/bin/bash # cert-monitor.sh - Monitor certificate expiration across hosts WARN_DAYS=30 CRIT_DAYS=7 HOSTS=( "example.com:443" "api.example.com:443" "admin.example.com:443" ) for host in "${HOSTS[@]}"; do expiry=$(echo | openssl s_client -connect "$host" -servername "${host%%:*}" 2>/dev/null | \ openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2) if [ -z "$expiry" ]; then echo "ERROR: Cannot connect to $host" continue fi expiry_epoch=$(date -d "$expiry" +%s) now_epoch=$(date +%s) days_left=$(( (expiry_epoch - now_epoch) / 86400 )) if [ "$days_left" -le "$CRIT_DAYS" ]; then echo "CRITICAL: $host expires in $days_left days ($expiry)" elif [ "$days_left" -le "$WARN_DAYS" ]; then echo "WARNING: $host expires in $days_left days ($expiry)" else echo "OK: $host expires in $days_left days ($expiry)" fi done ``` ### Prometheus cert-manager Metrics ```yaml # Alert on expiring certificates in Kubernetes groups: - name: cert-manager rules: - alert: CertificateExpiringSoon expr: certmanager_certificate_expiration_timestamp_seconds - time() < 7 * 24 * 3600 for: 1h labels: severity: critical annotations: summary: "Certificate {{ $labels.name }} expires in less than 7 days" - alert: CertificateNotReady expr: certmanager_certificate_ready_status{condition="True"} == 0 for: 15m labels: severity: warning annotations: summary: "Certificate {{ $labels.name }} is not ready" ``` ## Troubleshooting | Problem | Cause | Solution | |---------|-------|----------| | Certbot fails with "connection refused" | Port 80 blocked by firewall | Open port 80 for ACME HTTP-01 challenge | | "Too many certificates already issued" | Let's Encrypt rate limit hit | Use staging endpoint for testing; wait for rate limit reset | | cert-manager challenge stuck pending | Ingress or DNS misconfigured | Check `kubectl describe challenge`; verify DNS records | | Mixed content warnings | HTTP resources on HTTPS page | Update all asset URLs to HTTPS; use CSP headers | | OCSP stapling not working | Resolver not configured | Add `resolver` directive in nginx; verify outbound DNS | | Intermediate cert missing | Incomplete chain served | Use `fullchain.pem` not `cert.pem`; verify with `openssl s_client -showcerts` | | TLS handshake failure | Client doesn't support offered ciphers | Add TLS 1.2 support; check cipher suite compatibility | ## Best Practices - Automate renewal with systemd timers or cert-manager - Monitor expiration dates with alerting (30-day and 7-day warnings) - Use only TLS 1.2 and TLS 1.3 - Enable HSTS with long max-age and includeSubDomains - Enable OCSP stapling to improve handshake performance - Use ECDSA keys for better performance where possible - Test configuration with SSL Labs (ssllabs.com/ssltest) - Keep private keys secure with proper file permissions (0600) - Rotate certificates before expiry, not after - Maintain a certificate inventory across all services ## Related Skills - [hashicorp-vault](../../secrets/hashicorp-vault/) - PKI management - [waf-setup](../waf-setup/) - Web protection - [zero-trust](../zero-trust/) - mTLS and identity-based access
© devops-skills · MIT · BagelHole/DevOps-Security-Agent-Skills