English · 한국어
기대 결과(샘플)¶
데모(../scripts/up.sh → demo.sh)를 실행하면 얻는 대표 결과입니다. 실행하기 전에 무엇이 보일지 확인할 수 있습니다. (완전히 관측된 웜 실행에서 캡처했습니다.) 데모는 일곱 단계(0/6부터 6/6까지)입니다. 접근 준비 → SSH 확인 → 관측 → 보기 → 토폴로지 → 인벤토리 → 전환물 생성입니다.
아래 샘플은 관측 보기(3/6)를 캡처한 것입니다.
| 파일 | 내용 |
|---|---|
| discover-view.txt | 콘솔 출력: 발견한 자산(OpenSSL · JCA/BouncyCastle)과 관측한 통신 연결 간선별 등급 |
| topology.svg | 관측한 토폴로지(색 = 등급: 🟢 양자내성 / 🔴 고전 / ⚪ 등급 미정, 실선 = 관측함) |
핵심 이야기: 최신 스택과 레거시 스택이 TLS와 SSH 모두에서 서로 다릅니다.
| 연결 간선 | 등급 | 까닭 |
|---|---|---|
web-gw→pay-app TLS |
🟢 X25519MLKEM768 | Go crypto/tls 하이브리드 |
web-gw→pay-app SSH |
🟢 sntrup761 | 양쪽 모두 OpenSSH 9 이상 |
web-gw→pay-db TLS |
🔴 x25519 | OpenSSL 1.1.1에는 양자내성 그룹이 없음 |
web-gw→pay-db SSH |
🔴 curve25519 | 레거시 OS의 OpenSSH 8.2에는 양자내성 KEX가 없음 |
pay-app에서는 BouncyCastle을 포함한 JCA provider 체인(실행 중에 addProvider로 추가되므로 정적 스캔으로는 보이지 않음)도
attach로 관측합니다. 구성은 topology/topology.yaml이 정의하며, 그 파일을 고치면 이 결과도 바뀝니다.
등급은 설정이 아니라 관측입니다. SSH 등급은 양쪽 KEXINIT의 교집합(RFC 4253)으로 계산합니다. 클라이언트가 sntrup761을 제시해도 서버가 지원하지 않으면 그 연결 간선은 🔴입니다. 한쪽만 관측했을 때는 협상을 지어내지 않고 연결 간선을 ⚪ 등급 미정으로 둡니다.
뒤 단계에서 추가로 보이는 것:
- 접근 준비(0): pqcota-hosts가 hosts.csv를 Ansible 인벤토리(접속 키, 실행 중에만 존재)로 바꾸고 엔드포인트를 upsert합니다. 인벤토리에 비밀은 0개입니다.
- 중앙 인벤토리(5): ▸ Payments DB (ip:22) │ Payments DB · production · db · owner=DBA team과 같은 엔드포인트와 프로파일 머리말, 그리고 @app 라벨입니다. pay-db가 공유하는 libssl.so.1.1은 두 앱 모두에 귀속됩니다. @/opt/apps/api-gw,/opt/apps/payment-gw
- 연결 간선의 앱(5): 관측한 연결 간선 줄 끝의 @app입니다. 관측이 잡았으면 @payment.service, 실행 파일 경로로 잡았으면 @/usr/bin/openssl(exe-path), 잡지 못했으면 @?입니다. 이 데모에서는 네 연결 간선 가운데 셋이 @?이고
까닭이 함께 출력됩니다. socket closed between capture and lookup — short-lived connections are missed(3). 데모의 트래픽이 모두
짧게 끝나는 연결이어서 생깁니다. 그중 하나를 pqcota-declare-attribution으로 지정하면
@batch-runner.service(declared)가 되고, 관측이 이미 잡은 칸은 그대로 남습니다.
- 전환물 생성(6): 확정된 계획 → L2·L3 플레이북과 되돌림 기록입니다. affected apps: /opt/apps/api-gw, /opt/apps/payment-gw · before : libssl.so.1.1@1.1.1f
실제로 실행하면 달라질 수 있는 것과 까닭¶
결정성을 위해 두 장치를 더했으므로 처음 실행도 위 결과와 같습니다.
- 연결 간선 캡처를 완전해질 때까지 재시도: demo.sh는 목표 연결 간선 수(토폴로지의 연결 간선 수)에 닿을 때까지 최대 4번 다시 수집합니다.
netcap의 관측 창과 트래픽 시점 사이의 경합을 흡수하므로, 콜드 스타트인 첫 실행도 완전한 연결 간선을 얻습니다.
(DEMO_TARGET_EDGES / DEMO_MAX_ATTEMPTS로 조절합니다. 최대 시도 안에 목표에 닿지 못하는 극도로 제한된 환경에서만 연결 간선이 모자랍니다.)
- 베이스 이미지를 고정했고, BouncyCastle 1.85도 고정했습니다(해시 확인). 그래서 OpenSSL/Go/JDK 버전과 1.1.1f/3.0.13/3.5.5 같은 문자열은
태그가 갱신되어도 대체로 같습니다(배포판이 보안 업데이트를 내면 마이너 버전이 오를 수 있습니다).
그래도 달라질 수 있는 것: - 컨테이너 IP(172.18.0.x)는 실행할 때마다 동적으로 바뀝니다. 이야기에는 상관없습니다(노드는 이름으로 이어집니다). - 토폴로지를 고치면 노드, 연결 간선, 등급이 그에 맞게 바뀝니다. 이 샘플은 기본 구성을 기준으로 합니다.
결정성¶
같은 입력은 같은 출력을 냅니다. 등급 분류(🟢/🔴/⚪), 발견한 자산, 세 가지 대조 상태(별도 확장에서)는 모두 결정적인 로직입니다. 위의 「차이」는 무엇이 관측되는가(캡처 시점)와 베이스 이미지 버전의 문제이지, 분류나 판정 로직의 비결정성이 아닙니다.