개요 — 무엇을 하나
**Inventory("주머니")**는 SE·시스템·보안 담당자가 장애나 침해 상황에서 지체 없이 대응하도록, 필요한 자료를 미리 담아 두는 단일 콘솔입니다. 네 가지 자료를 탭으로 나눠 관리합니다.
| 탭 | 무엇 | 저장 테이블 | 대표 용도 |
|---|---|---|---|
| Tools(툴) | 수집·설치·진단 스크립트 (CodeMirror 편집) | infr_snippet (kind=tool) |
run_report·eve_ingest 같은 수집기, 프로브 스크립트 |
| Snippets(스니펫) | 짧은 CLI 명령/한 줄 조각 | infr_snippet (kind=snippet) |
자주 쓰는 journalctl·ss·iptables 한 줄 |
| Docs(가이드 문서) | 플레이북·기준·네트워크 맵·마크다운 | infr_doc |
비상 대응 절차, 컴플라이언스 룰, 아키텍처 다이어그램 |
| Keys(키정보) | 팀 공유 비밀번호·API 키·전역 변수 | infr_vault_key |
DB 계정, API 키, 매시업 폴백 토큰 |
핵심 특징:
{{PLACEHOLDER}}매시업 — Tools/Snippets 본문의{{TOKEN}}자리를 실제 인스턴스 값·수집기 설정·전역 키로 채워 바로 실행 가능한 코드로 결합 복사합니다.- 다국어 제목/설명(i18n JSON) — 제목·설명·본문을 언어별 맵(
{ko, en, …})으로 저장해 세션 언어로 표시하고, 빈 언어는 en 폴백합니다. - 키 열람(reveal) + 감사 — 비밀은 Laravel
Crypt(APP_KEY)로 암호화 저장하고 목록에는 절대 실리지 않습니다. 눈(eye) 토글로 복호화 열람할 때마다infr_audit_log에 감사 기록이 남습니다. - AI 초안 어시스트 — 가이드 문서를 준비된 AI provider(없으면 Mock)로 초안 생성합니다.
Inventory는 크리덴셜을 코드에 하드코딩하지 않기 위한 장치이기도 합니다. 스크립트 본문에는
{{DB_PASSWORD}}같은 토큰만 두고, 실제 값은 Keys(키정보)·인스턴스 변수·수집기 설정에서 매시업 시점에만 주입합니다.
언제 쓰나
- 장애 대응 중 필요한 진단 스크립트를 즉시 찾아 대상 서버 값으로 채워 복사할 때.
- 반복 CLI(로그 조회, 소켓 상태, 방화벽 규칙 확인)를 스니펫으로 저장해 두고 재사용할 때.
- 비상 대응 플레이북 / 컴플라이언스 기준 / 네트워크 아키텍처 맵을 팀 지식으로 문서화할 때.
- 팀 공유 비밀(공용 DB 계정, 외부 API 키)을 안전하게 보관하고 감사 추적 하에 열람할 때.
- 수집기·프로브 스크립트를 인스턴스별로 결합 생성해 배포·에뮬레이트할 때(참고: 원격 에뮬레이트).
화면 구성
상단에 툴 · 스니펫 · 가이드 문서 · 키정보 4개 탭과 각 탭의 개수 배지가 있습니다. 탭 목록은 활성화 시 개별 엔드포인트로 지연 로드됩니다(초기 화면은 옵션·카운트·권한·AI 준비여부·인스턴스 목록만 부트스트랩).
공통 컨트롤:
- 검색창 — 제목·내용·태그·본문·출처를 부분일치(
LIKE %q%)로 검색. - 필터 — 탭에 따라 분류(category)·언어(lang)·상태(status)·키 분류로 좁힘.
- 정렬·페이지 —
seq·제목·수정일 등으로 정렬(기본seq오름차순), 페이지당 5~100건(기본 24). - 카드 그리드 — 항목마다 카드로 표시. 카드에서 열기/편집/복제/삭제/복사 액션.
Tools · Snippets 카드
- 코드 미리보기 +
{{변수}}배지(비밀 토큰은 별도 표시). - 인스턴스 선택 후 결합 복사 — 대상 인스턴스를 고르면 토큰이 채워진 코드가 클립보드로 복사됩니다.
- 편집 모달은 CodeMirror(언어별 하이라이트)로 본문을 편집하고, 저장 시 서버가
{{PLACEHOLDER}}를 재추출합니다.
Docs 카드
카테고리별로 편집기 유형이 달라집니다.
| 카테고리 | 편집기 | 저장 위치 |
|---|---|---|
playbook (운영·비상 대응) |
케이스+대응 리스트 (증상/대응/심각도) | payload JSON |
compliance (보안 지침·기준) |
규칙+근거+참조 리스트 | payload JSON |
network_map (아키텍처·네트워크) |
Konva 네트워크 맵(노드·연결선·그룹·PNG 저장) | payload JSON |
access (계정·권한) |
마크다운 | body_md |
misc (그 외) |
마크다운 | body_md |
문서 상태는 draft(초안) / published(게시)이며, AI 어시스트 버튼으로 주제를 입력해 초안을 생성할 수 있습니다.
Keys 카드
- 항상 마스킹 표시. 보기(eye) 토글로만 실제 값을 복호화해 노출합니다.
- 키 종류: 외부키(external) = 팀 공유 비밀 / 내부키(internal) = 전역 변수(토큰명 지정, 매시업 최후순위 폴백).
사용법
1. Tool/Snippet 추가·편집
- 툴 또는 스니펫 탭에서 **[추가]**를 누릅니다(쓰기 권한 level≥2 필요).
- 필수: 종류(kind), 분류(category), 언어(lang), 제목(1개 이상 언어), 본문.
- 언어(lang) 값:
bash·javascript(Node.js) ·sql·php·ini·other. - 제목·설명·본문은 언어 토글로 언어별 입력이 가능하며, 비운 언어는 다른 언어로 폴백 표시됩니다.
- 언어(lang) 값:
- 본문에 크리덴셜·호스트 같은 가변값은
{{TOKEN}}으로 둡니다. 저장하면 서버가 정규식{{ ... }}으로 토큰을 추출해placeholders메타에 반영합니다. - 저장 시 감사 로그(
snippet.create/snippet.update)가 남습니다. **[복제]**는(copy)접미의 동일 사본을 만듭니다.
토큰 이름에
PASS·SECRET·TOKEN·KEY·CRED문자열이 들어가면 비밀 토큰으로 자동 분류되어, 매시업 시 자동 주입 대상에서 빠지고 "수동으로 채우세요" 안내가 붙습니다.
2. {{PLACEHOLDER}} 매시업 (결합 복사)
Tools 코드 모달에서 대상 인스턴스를 고르면, 토큰이 다음 우선순위로 채워집니다.
- 인스턴스 내장 토큰 — HOSTNAME·IP_PRIVATE·IP_PUBLIC·SSH_HOST·SSH_USER 등(비밀 아님).
- 인스턴스 변수 — 해당 인스턴스의 [env] 비밀 아닌 변수(인스턴스 변수).
- 소스별 채우기 — 수집기 설정(collector)·규칙 임계(rules)·인스턴스(instance) 값.
- 전역 비밀 폴백 — 위에서 못 채운 토큰을 내부키(Keys) 의 전역 변수로 최후 주입.
내장 토큰·인스턴스 변수로 안 채워진 비밀 토큰은 GET /inventory/keys/vars(전역 내부키 → 비밀 맵)로 폴백 주입됩니다. 이 요청은 레벨≥1 + 매 요청 감사(key.vars) 로 처리됩니다(Option B — 의도된 예외).
# 예: 저장된 본문
mysqldump -h {{DB_HOST}} -u {{DB_USER}} -p{{DB_PASSWORD}} {{DB_NAME}} > /backup/dump.sql
# 인스턴스 "blue" 선택 후 결합 복사 →
# {{DB_HOST}}=10.0.0.12(인스턴스), {{DB_USER}}=app(env 변수),
# {{DB_PASSWORD}}=(내부키 전역 폴백·감사), {{DB_NAME}}=(수동 입력)
3. 가이드 문서 작성 + AI 어시스트
- 문서 탭에서 [문서 추가] → 카테고리를 선택하면 편집기 유형이 바뀝니다.
network_map은 노드 추가·드래그·그룹핑·연결(기부자→채택자)·PNG 저장을 지원하는 Konva 캔버스입니다.- [AI 어시스트] — 주제(예: "랜섬웨어 대응")를 입력하면 준비된 AI provider가 스키마에 맞는 초안(playbook은 case/answer, compliance는 rule/rationale, 나머지는 markdown)을 생성합니다.
- AI 키가 없으면 Mock 초안이 생성됩니다(설정: AI 어댑터).
4. 키정보(Vault Key) 관리·열람
- [키 추가](level≥2) → 분류(db/api/ssh/service/other), 키 종류(외부/내부), 제목, 사용자/URL/메모, 비밀값을 입력.
- 내부키는 토큰명(대문자, 정규식
^[A-Z][A-Z0-9_]*$, 예TLX_TOKEN)을 지정해야 매시업 폴백으로 쓰입니다. 외부키는 토큰이 강제로 비워집니다. - 편집 시 비밀값을 비워 두면 기존 값이 유지됩니다(신규는 필수).
- 내부키는 토큰명(대문자, 정규식
- 목록·카드에는 비밀이 절대 실리지 않고
has_secret여부만 표시됩니다. - 보기(eye) 토글 →
GET /inventory/keys/{id}/reveal이 복호화 값을 반환하고 감사 로그(key.reveal) 를 남깁니다. 복사 후 다시 숨기면 화면에서 사라집니다.
키 열람과 전역 비밀 주입(
key.vars)은 전부 감사됩니다(누가·언제·어떤 키/토큰). 감사 기록은 감사 로그에서 확인할 수 있습니다. 비밀은 로그·URL·클립보드 히스토리에 남지 않도록 주의하세요.
데이터 소스
테이블
| 테이블 | 내용 | 비고 |
|---|---|---|
infr_snippet |
Tools(kind=tool) + Snippets(kind=snippet) |
제목·설명·본문 다국어 JSON, placeholders 메타 |
infr_doc |
가이드 문서 | payload(json) 또는 body_md(markdown) |
infr_vault_key |
팀 공유 키 | secret_enc = 암호화 저장 |
infr_audit_log |
저장·삭제·열람 감사 | append-only |
엔드포인트 (경로 접두 /asset/inventory)
| 메서드·경로 | 동작 | 권한 |
|---|---|---|
GET /inventory |
셸 부트스트랩(옵션·카운트·권한·인스턴스·i18n) | 로그인(level≥1) |
GET /inventory/snippets/list |
툴/스니펫 목록(검색·필터·정렬·페이지) | 로그인 |
POST /inventory/snippets/save |
저장(생성/수정) | level≥2 |
POST /inventory/snippets/duplicate |
복제 | level≥2 |
POST /inventory/snippets/delete |
삭제 | level≥2 |
GET /inventory/docs/list |
문서 목록 | 로그인 |
POST /inventory/docs/save |
문서 저장 | level≥2 |
POST /inventory/docs/delete |
문서 삭제 | level≥2 |
POST /inventory/docs/ai-assist |
AI 초안 생성 | level≥2 |
GET /inventory/keys/list |
키 목록(마스킹) | 로그인 |
GET /inventory/keys/vars |
전역 내부키 → 비밀 맵(매시업 폴백) | level≥1 + 감사 |
GET /inventory/keys/{id}/reveal |
비밀 복호화 열람 | level≥1 + 감사 |
POST /inventory/keys/save |
키 저장 | level≥2 |
POST /inventory/keys/delete |
키 삭제 | level≥2 |
부트스트랩 페이로드 주요 필드
{
"counts": { "tool": 12, "snippet": 30, "doc": 8, "key": 15 },
"can_write": true, // level >= 2
"can_reveal": true, // level >= 1
"ai_ready": true, // 준비된 실제 AI provider 존재
"instances": [ { "id": 1, "name": "blue", "alias": "blue",
"ip_private": "10.0.0.12", "reachable": true } ],
"lang": "ko"
}
권한
| 작업 | 필요 레벨 | 감사 |
|---|---|---|
| Inventory 열람(모든 탭 목록·검색) | 로그인(demo=1 이상) | — |
| 키 열람(reveal) · 전역 비밀 주입(vars) | level≥1 | ✅ key.reveal / key.vars |
| Tool/Snippet/Doc/Key 저장·삭제·복제 | level≥2(staff) | ✅ *.create/update/delete/duplicate |
| AI 어시스트 | level≥2 | — |
- 표준 레벨: demo=1 · staff=5 · admin=10.
/asset라우트 그룹은infr.auth(로그인)만 걸려 있고, 쓰기 게이팅은 컨트롤러 내부(user_level >= 2)에서 이뤄집니다. - 쓰기 권한이 없으면 저장 시
403 {"ok":false,"message":"권한이 없습니다(레벨 2 이상 필요)."}가 반환됩니다. - 관련 권한 개념은 RBAC 레벨 참고.
문제해결 · FAQ
Q. {{TOKEN}} 이 안 채워집니다.
비밀 토큰(PASS/SECRET/TOKEN/KEY/CRED 포함)은 자동 주입 대상이 아닙니다. 인스턴스 변수([env])나 내부키(전역 변수)로 등록하거나 수동으로 입력하세요. 인스턴스가 매시업 소스에 없으면 저장된 인스턴스가 없거나 필드가 비어 있는지 확인합니다.
Q. 키 [보기]를 눌러도 값이 안 나옵니다.
복호화 실패(복호화에 실패했습니다.)는 대개 APP_KEY가 저장 이후 바뀐 경우입니다. Crypt는 APP_KEY로 복호화하므로, 키를 회전했다면 기존 비밀은 다시 저장해야 합니다.
Q. AI 어시스트가 "AI Mock"으로만 나옵니다.
준비된 실제 AI provider가 없어서(ai_ready=false) Mock 초안이 생성된 상태입니다. AI 어댑터에서 provider 키를 등록하세요.
Q. 저장이 422로 거부됩니다.
제목을 최소 한 언어로 입력했는지, 본문(정본=ko 우선)이 비어 있지 않은지 확인하세요(제목을 하나 이상의 언어로 입력하세요. / 본문을 입력하세요.).
Q. 내부키 토큰명을 저장할 수 없습니다.
토큰명은 대문자로 시작하고 대문자·숫자·밑줄만 허용됩니다(^[A-Z][A-Z0-9_]*$). 예: DB_PASSWORD, TLX_TOKEN. 외부키는 토큰명을 가질 수 없습니다.
Q. 비밀이 목록/카드에 노출되나요?
아니요. 목록 응답에는 has_secret(존재 여부)만 포함되고 실제 값은 reveal 엔드포인트로만 반환됩니다.
관련 항목
- 인스턴스 변수(env) — 매시업 토큰의 인스턴스 값 소스
- 원격 에뮬레이트 — 인벤토리 프로브를 인스턴스에 동기화·실행
- AI 어댑터 — 문서 AI 초안 provider 설정
- 수집기 — 수집기 설정 매시업 소스
- 규칙 임계 — srct/kts 상수 매시업 소스
- 감사 로그 — 저장·삭제·키 열람 기록
