zioinfo-mail/skills/guardia-agent/SKILL.md
DESKTOP-TKLFCPR\ython e228faabf5 feat(itsm): G-1~G-12 확장 기능 + 하네스/봇/설치스크립트 구현
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>
2026-05-29 18:18:52 +09:00

159 lines
5.2 KiB
Markdown

---
name: guardia-agent
description: >
GUARDiA 프로젝트에서 Python 역방향 에이전트(Reverse Agent) 코드를 작성하거나
수정할 때 사용하는 스킬. 다음 경우에 반드시 먼저 읽어라:
- 내부망 중계 PC 에이전트 코드 작성
- asyncio / websockets 기반 역방향 연결 구현
- PostgreSQL psycopg2 DB 조회 코드
- SM 운영 쉘 스크립트 원격 실행 파이프라인
- 에이전트 자동 재연결(Backoff) 로직
---
# GUARDiA 역방향 에이전트 스킬
## 개념: 역방향 연결 (Reverse Connection)
공공기관 방화벽은 **인바운드 차단** → 에이전트가 먼저 **아웃바운드로 연결**
외부 서버로 전화를 걸어두면 서버가 그 터널을 통해 명령 전달 가능
```
[내부망 에이전트] ──(Outbound WS)──► [외부 중계 WAS]
◄──(명령 역전달)────
```
## 핵심 구현 패턴
### 무한 재연결 루프 (Backoff)
```python
async def agent_main_loop():
while True:
try:
async with websockets.connect(EXTERNAL_WS_URL) as ws:
print("연결 성공")
async for message in ws:
await handle_command(ws, json.loads(message))
except (websockets.ConnectionClosed, OSError):
print("연결 끊김 — 5초 후 재연결")
await asyncio.sleep(5)
except Exception as e:
print(f"예외: {e}")
await asyncio.sleep(5)
```
### 화이트리스트 기반 명령 분기 (필수)
```python
async def handle_command(ws, packet):
action = packet.get("action")
params = packet.get("params", {})
task_id = packet.get("task_id")
room_id = packet.get("room_id")
# 절대로 임의 셸 명령 직접 실행 금지
# 반드시 정의된 함수만 호출
if action == "FETCH_MES_QC":
data = fetch_mes_qc(params.get("date"))
status = "SUCCESS"
elif action == "CHECK_INTERNAL_WAS_STATUS":
data = check_was_health()
status = "SUCCESS"
elif action == "CHECK_DISK_SPACE":
data = check_disk_space()
status = "SUCCESS"
else:
data = {"error": f"허용되지 않은 action: {action}"}
status = "FAIL"
await ws.send(json.dumps({
"event": "TASK_FINISHED",
"room_id": room_id,
"task_id": task_id,
"payload": {"status": status, "data": data}
}))
```
## DB 조회 표준 (psycopg2)
```python
from psycopg2.extras import RealDictCursor
import datetime
class InfrastructureJsonEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, (datetime.datetime, datetime.date)):
return obj.isoformat()
return super().default(obj)
def fetch_mes_qc(work_date: str) -> list:
"""고정 쿼리 + 파라미터 바인딩 (SQL Injection 방지)"""
conn = psycopg2.connect(**DB_CONFIG)
try:
with conn.cursor(cursor_factory=RealDictCursor) as cur:
sql = """
SELECT work_date, prc_code, err_msg, reg_date
FROM tb_mes_wrk_prc_i
WHERE work_date = %s
ORDER BY reg_date DESC LIMIT 10
"""
cur.execute(sql, (work_date,))
return json.loads(json.dumps(cur.fetchall(), cls=InfrastructureJsonEncoder))
finally:
conn.close()
```
## WAS 헬스체크
```python
def check_was_health() -> dict:
try:
r = requests.get("http://10.100.10.10:8080/actuator/health", timeout=3)
return r.json()
except requests.exceptions.ConnectionError:
return {"status": "DOWN", "error": "연결 거부"}
except requests.exceptions.Timeout:
return {"status": "DOWN", "error": "타임아웃"}
```
## 금지 사항
- `subprocess.run(user_input)` 절대 금지
- 패스워드/API 키 하드코딩 금지 → 환경변수 또는 암호화 설정 파일
- 예외 발생 시 에이전트 프로세스 다운(exit) 금지 → try-except로 감싸고 계속 실행
- 메신저 응답에 내부망 IP 그대로 노출 금지
## 환경변수 참조 패턴
```python
import os
DB_CONFIG = {
"host": os.environ["DB_HOST"],
"port": int(os.environ.get("DB_PORT", 5432)),
"user": os.environ["DB_USER"],
"password": os.environ["DB_PASSWORD"],
"database": os.environ["DB_NAME"],
}
EXTERNAL_WS_URL = os.environ["EXTERNAL_WS_URL"]
```
## Windows 서비스 등록 (운영 배포용)
```
# NSSM 사용 (Non-Sucking Service Manager)
nssm install GUARDiA-Agent "python" "C:\GUARDiA\src\agent\main.py"
nssm set GUARDiA-Agent AppDirectory "C:\GUARDiA"
nssm start GUARDiA-Agent
```
## 라이선스 주의사항
역방향 에이전트 자체는 라이선스 검증 없이 동작하지만, 에이전트가 연결되는 GUARDiA ITSM 서버에 유효한 라이선스가 있어야 기관·서버 등록이 가능하다.
| 에디션 | 연결 가능 기관 수 | 등록 가능 서버 수 |
|--------|-------------|-------------|
| COMMUNITY | 1 | 20 |
| STANDARD | 50 | 500 |
| ENTERPRISE | 무제한 | 무제한 |
기관 수 초과 시 에이전트 등록 요청(`POST /api/institutions`)이 HTTP 403으로 거부된다.
갱신 전까지 기존 연결된 에이전트는 계속 동작한다.