G-1: 메신저 Webhook Relay + _send_to_room 실제 httpx 호출 구현 G-2: POST /api/tasks/bulk SR 대량작업 엔드포인트 (최대 100건) G-3: 라이선스 만료 알림 스케줄러 (매일 09:00 KST) G-4: 체험판 upgrade_banner 필드 + license.py 배너 로직 G-5: core/auto_rca.py + incidents/problem auto-rca 엔드포인트 G-6: core/deploy_impact.py + vibe impact-analysis 엔드포인트 G-7: core/ticket_classifier.py + SR 생성 시 AI 분류 + ai-suggestion API G-8: VulnPatchRecord 모델 + vuln_scan 패치추적 4개 엔드포인트 G-9: core/jira_sync.py + gateway Jira/Confluence 연동 엔드포인트 G-10: core/push_notify.py + routers/push.py + PushSubscription 모델 G-11: approvals 다중승인 (위임/서명/기한초과/마감연장) G-12: alembic.ini + migrations/ + cicd/migrate_to_postgres.sh 하네스: guardia-orchestrator 확장기능 Phase 반영 봇명령어: /sr /status /license /bulk 슬래시 명령어 추가 설치스크립트: setup/ (Ubuntu, CentOS, RHEL, Windows) --test 옵션 포함 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
95 lines
3.8 KiB
Bash
95 lines
3.8 KiB
Bash
#!/bin/bash
|
|
# ==============================================================================
|
|
# GUARDiA ITSM — SSL 인증서 만료일 점검 스크립트
|
|
# 경로: /opt/guardia/scripts/ssl/ssl_expiry_check.sh
|
|
#
|
|
# 사용법:
|
|
# ./ssl_expiry_check.sh <호스트> [포트(기본 443)] [timeout(기본 5초)]
|
|
#
|
|
# 출력 (JSON):
|
|
# 성공: {"host":"...","port":443,"expiry":"...","days_left":N,"level":"OK|WARN|CRITICAL","issuer":"..."}
|
|
# 실패: {"host":"...","port":443,"status":"ERROR","message":"..."}
|
|
#
|
|
# 반환 코드:
|
|
# 0 — 점검 완료 (OK/WARN/CRITICAL 모두)
|
|
# 1 — 인증서 조회 실패
|
|
# ==============================================================================
|
|
|
|
HOST="${1}"
|
|
PORT="${2:-443}"
|
|
TIMEOUT="${3:-5}"
|
|
|
|
if [ -z "${HOST}" ]; then
|
|
echo '{"status":"ERROR","message":"호스트 인수가 필요합니다. 사용법: ssl_expiry_check.sh <host> [port]"}'
|
|
exit 1
|
|
fi
|
|
|
|
# openssl 설치 확인
|
|
if ! command -v openssl &>/dev/null; then
|
|
echo '{"status":"ERROR","message":"openssl 명령어를 찾을 수 없습니다."}'
|
|
exit 1
|
|
fi
|
|
|
|
# ── 인증서 정보 조회 ──────────────────────────────────────────────────────────
|
|
CERT_INFO=$(echo | timeout "${TIMEOUT}" openssl s_client \
|
|
-servername "${HOST}" \
|
|
-connect "${HOST}:${PORT}" \
|
|
2>/dev/null)
|
|
|
|
if [ -z "${CERT_INFO}" ]; then
|
|
echo "{\"host\":\"${HOST}\",\"port\":${PORT},\"status\":\"ERROR\",\"message\":\"연결 실패 또는 타임아웃 (${TIMEOUT}s)\"}"
|
|
exit 1
|
|
fi
|
|
|
|
# 만료일 추출
|
|
EXPIRY=$(echo "${CERT_INFO}" | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
|
|
if [ -z "${EXPIRY}" ]; then
|
|
echo "{\"host\":\"${HOST}\",\"port\":${PORT},\"status\":\"ERROR\",\"message\":\"인증서 만료일을 파싱할 수 없습니다.\"}"
|
|
exit 1
|
|
fi
|
|
|
|
# 발급기관 추출
|
|
ISSUER=$(echo "${CERT_INFO}" | openssl x509 -noout -issuer 2>/dev/null \
|
|
| sed 's/^issuer=//' | tr -d '\n' | sed 's/"/\\"/g')
|
|
|
|
# 주체(CN) 추출
|
|
SUBJECT_CN=$(echo "${CERT_INFO}" | openssl x509 -noout -subject 2>/dev/null \
|
|
| grep -oP '(?<=CN = )[^,]+' | head -1 | tr -d '\n' | sed 's/"/\\"/g')
|
|
|
|
# ── 날짜 계산 ─────────────────────────────────────────────────────────────────
|
|
# Linux date 와 macOS date 모두 지원
|
|
EXPIRY_TS=$(date -d "${EXPIRY}" +%s 2>/dev/null)
|
|
if [ -z "${EXPIRY_TS}" ]; then
|
|
# macOS/BSD 형식 시도
|
|
EXPIRY_TS=$(date -jf "%b %d %T %Y %Z" "${EXPIRY}" +%s 2>/dev/null)
|
|
fi
|
|
if [ -z "${EXPIRY_TS}" ]; then
|
|
# 추가 형식 시도 (Jan 1 ...)
|
|
EXPIRY_TS=$(date -d "$(echo "${EXPIRY}" | tr -s ' ')" +%s 2>/dev/null)
|
|
fi
|
|
|
|
if [ -z "${EXPIRY_TS}" ]; then
|
|
echo "{\"host\":\"${HOST}\",\"port\":${PORT},\"expiry\":\"${EXPIRY}\",\"status\":\"ERROR\",\"message\":\"날짜 파싱 실패\"}"
|
|
exit 1
|
|
fi
|
|
|
|
NOW_TS=$(date +%s)
|
|
DAYS_LEFT=$(( (EXPIRY_TS - NOW_TS) / 86400 ))
|
|
|
|
# ── 알림 등급 ─────────────────────────────────────────────────────────────────
|
|
LEVEL="OK"
|
|
if [ "${DAYS_LEFT}" -le 0 ]; then
|
|
LEVEL="EXPIRED"
|
|
elif [ "${DAYS_LEFT}" -le 7 ]; then
|
|
LEVEL="CRITICAL"
|
|
elif [ "${DAYS_LEFT}" -le 30 ]; then
|
|
LEVEL="WARN"
|
|
fi
|
|
|
|
# ── JSON 출력 ─────────────────────────────────────────────────────────────────
|
|
cat <<EOF
|
|
{"host":"${HOST}","port":${PORT},"expiry":"${EXPIRY}","days_left":${DAYS_LEFT},"level":"${LEVEL}","issuer":"${ISSUER}","subject_cn":"${SUBJECT_CN}"}
|
|
EOF
|
|
|
|
exit 0
|