아키텍처

아키텍처와 설계

왜 Rust인가

SSH-Frontière는 세 가지 이유로 Rust로 작성되었습니다:

  1. 메모리 안전성: 버퍼 오버플로 없음, use-after-free 없음, 널 포인터 없음. 로그인 셸로 실행되는 보안 컴포넌트에게 이것은 결정적입니다.

  2. 정적 바이너리: x86_64-unknown-linux-musl 타겟으로 컴파일하면(다른 타겟도 가능하나 동작 보장 없음) 바이너리는 약 1 Mo이며 시스템 의존성이 없습니다. 서버에 복사하면 바로 사용 가능합니다.

  3. 성능: 프로그램이 시작, 검증, 실행, 종료를 밀리초 단위로 완료합니다. 런타임 없음, 가비지 컬렉터 없음, JIT 없음.

동기식이며 일회성

SSH-Frontière는 동기식 원샷 프로그램입니다. 데몬 없음, async 없음, Tokio 없음.

생명주기는 단순합니다:

  1. sshd가 키로 SSH 연결을 인증
  2. sshd가 fork하고 ssh-frontiere를 로그인 셸로 실행
  3. ssh-frontiere가 명령을 검증하고 실행
  4. 프로세스 종료

각 SSH 연결은 새로운 프로세스를 생성합니다. 연결 간 공유 상태 없음, 동시성 문제 없음.

코드 구조

코드는 명확한 책임을 가진 모듈로 구성되어 있습니다:

모듈책임
main.rs진입점, 인자 평탄화, 오케스트레이터 호출
orchestrator.rs메인 플로우: 배너, 헤더, 명령, 응답, 세션 루프
config.rsTOML 구성 구조체, fail-fast 검증
protocol.rs헤더 프로토콜: 파서, 배너, 인증, 세션, body
crypto.rsSHA-256 (FIPS 180-4 구현), base64, 논스, 챌린지-응답
dispatch.rs명령 파싱 (따옴표, key=value), 해석, RBAC
chain_parser.rs명령 체인 파서 (연산자 ;, &, |)
chain_exec.rs체인 실행: 엄격 순차(;), 허용 순차(&), 복구(|)
discovery.rshelplist 명령: 도메인과 액션 탐색
logging.rs구조화된 JSON 로깅, 민감한 인자 마스킹
output.rsJSON 응답, 종료 코드
lib.rsproof 바이너리 및 fuzz 헬퍼를 위한 crypto 노출

각 모듈은 동일 디렉토리에 테스트 파일(*_tests.rs)이 있습니다.

보조 바이너리 proof (src/bin/proof.rs)는 E2E 테스트 및 클라이언트 통합을 위한 인증 proof를 계산합니다.

헤더 프로토콜

SSH-Frontière는 stdin/stdout 상의 텍스트 프로토콜을 사용합니다. 접두사는 방향에 따라 다릅니다:

클라이언트에서 서버로 (stdin):

접두사역할
+ 구성: 디렉티브 (auth, session, body)
# 주석: 서버에서 무시됨
(일반 텍스트)명령: 도메인 액션 [인자]
. (한 줄에 단독)블록 끝: 명령 블록 종료

서버에서 클라이언트로 (stdout):

접두사역할
#> 주석: 배너, 안내 메시지
+> 구성: capabilities, 챌린지 논스
>>> 응답: 최종 JSON 응답
>> Stdout: 스트리밍 표준 출력 (ADR 0011)
>>! Stderr: 스트리밍 오류 출력

연결 흐름

클라이언트                                서버
  |                                        |
  |  <-- 배너 + capabilities -------------  |   #> ssh-frontiere 0.1.0
  |                                        |   +> capabilities rbac, session, help, body
  |                                        |   +> challenge nonce=a1b2c3...
  |                                        |   #> type "help" for available commands
  |                                        |
  |  --- +auth (선택사항) --------------->   |   + auth token=runner-ci proof=deadbeef...
  |  --- +session (선택사항) ------------>   |   + session keepalive
  |                                        |
  |  --- 명령 (일반 텍스트) ------------->   |   forgejo backup-config
  |  --- 블록 끝 ----------------------->   |   .
  |  <-- 스트리밍 stdout ----------------   |   >> Backup completed
  |  <-- 최종 JSON 응답 ----------------   |   >>> {"status_code":0,"status_message":"executed",...}
  |                                        |
  |  (세션 keepalive인 경우)               |
  |  --- 명령 2 ----------------------->   |   infra healthcheck
  |  --- 블록 끝 ----------------------->   |   .
  |  <-- JSON 응답 2 ------------------   |   >>> {"status_code":0,...}
  |  --- 세션 종료 (빈 블록) ----------->   |   .
  |  <-- 세션 종료 --------------------   |   #> session closed

JSON 응답

각 명령은 >>>로 시작하는 한 줄의 최종 JSON 응답을 생성합니다. 표준 출력과 오류 출력은 >>>>!를 통해 스트리밍으로 전송됩니다:

>> Backup completed
>>> {"command":"forgejo backup-config","status_code":0,"status_message":"executed","stdout":null,"stderr":null}

body 프로토콜

+body 헤더를 사용하면 stdin을 통해 자식 프로세스에 여러 줄의 콘텐츠를 전송할 수 있습니다. 네 가지 구분 모드:

TOML 구성

구성 형식은 선언적 TOML입니다. ADR 0001에 문서화된 선택:

구성은 로드 시 검증(fail-fast): TOML 문법, 필드 완전성, 플레이스홀더 일관성, 최소 하나의 도메인, 도메인당 최소 하나의 액션, 비어있지 않은 enum 값.

의존성 정책

SSH-Frontière는 필수적이지 않은 의존성 제로 정책을 가지고 있습니다. 각 외부 크레이트는 실질적인 필요로 정당화되어야 합니다.

현재 의존성

직접 의존성 3개, 전이 의존성 약 20개:

크레이트용도
serde + serde_jsonJSON 직렬화 (로깅, 응답)
tomlTOML 구성 로드

평가 매트릭스

의존성을 추가하기 전에 8개의 가중 기준(5점 만점)으로 평가합니다: 라이선스(탈락 기준), 거버넌스(x3), 커뮤니티(x2), 업데이트 빈도(x2), 크기(x3), 전이 의존성(x3), 기능(x2), 비종속성(x1). 최소 점수: 3.5/5.

감사

프로젝트 개발 과정

SSH-Frontière는 체계적인 TDD 방법론과 함께 Claude Code 에이전트에 의해 주도된 순차적 단계(1~9, 중간 단계 2.5 및 5.5 포함)로 개발되었습니다:

단계내용
1기능적 디스패처, TOML 구성, 3단계 RBAC
2프로덕션 구성, 운영 스크립트
2.5SHA-256 FIPS 180-4, BTreeMap, 우아한 타임아웃
3통합 헤더 프로토콜, 챌린지-응답 인증, 세션
4E2E SSH Docker 테스트, 코드 정리, 포지 통합
5가시성 태그, 토큰별 수평 필터링
5.5선택적 논스, 명명된 인자, proof 바이너리 (6단계 포함, 병합)
7구성 가이드, dry-run --check-config, 접두사 없는 help
8구조화된 오류 타입, clippy pedantic, cargo-fuzz, proptest
9body 프로토콜, 자유 인자, max_body_size, 종료 코드 133

프로젝트 참여자:

사람과 기계가 함께 일하며, 더 좋고, 더 빠르고, 더 안전하게.