콘텐츠로 이동

11.6. 보안 모델

Backend.AI GO의 컨테이너 실행은 심층 방어(defense-in-depth) 보안 모델로 설계되어, 서로 독립적인 여러 레이어가 함께 작동하면서 컨테이너를 명확히 정해진 경계 안에 묶어 둡니다.

보안 레이어

flowchart TD
    REQ[컨테이너 실행 요청]
    L1["레이어 1: 입력 검증\n(ID/이름/태그 검사)"]
    L2["레이어 2: 마운트 허용 목록\n(경로 정규화 + 허용 목록 확인)"]
    L3["레이어 3: 환경 변수 검사\n(승인된 환경 변수만 통과)"]
    L4["레이어 4: 자격증명 프록시\n(실제 API 키가 컨테이너에 절대 유입되지 않음)"]
    L5["레이어 5: IPC 권한\n(그룹 범위 전송/작업 권한)"]
    L6["레이어 6: 보안 감사\n(실행 중인 컨테이너 주기적 감사)"]
    CONT[컨테이너 실행]

    REQ --> L1 --> L2 --> L3 --> L4 --> L5 --> CONT --> L6

레이어 1: 입력 검증

모든 컨테이너 식별자, 이미지 태그, 빌드 컨텍스트는 인수 주입 공격을 막기 위해 사용 전에 검증을 거칩니다.

컨테이너 ID와 이름

컨테이너 이름은 다음 조건을 만족해야 합니다:

  • 비어 있지 않을 것
  • -로 시작하지 않을 것 (플래그로 해석되기 때문)
  • 영숫자와 _, ., -, :, /만 포함할 것

이미지 태그

이미지 태그는 다음 조건을 만족해야 합니다:

  • 비어 있지 않을 것
  • -로 시작하지 않을 것
  • [registry/]name[:tag][@digest] 형식을 따를 것

추가 인수 차단 목록

다음과 같은 위험한 Docker 플래그는 extra_args를 통해 전달할 수 없습니다:

차단된 플래그 이유
--privileged 호스트에 루트 수준 접근 권한 부여
--cap-add Linux 기능 추가
--security-opt seccomp/AppArmor 프로필 수정
--device 호스트 장치 노출
--pid=host 호스트 PID 네임스페이스 공유
--network=host 호스트 네트워크 스택 공유
--userns=host 사용자 네임스페이스 격리 비활성화
--ipc=host 호스트 IPC 네임스페이스 공유

레이어 2: 마운트 보안

마운트 허용 목록은 파일시스템 접근에 기본 거부(deny-by-default) 정책을 적용합니다.

검증 단계

  1. 심볼릭 링크 해석: 호스트 경로의 심볼릭 링크를 모두 풀어 정규화해, 심볼릭 링크를 통한 경로 우회를 막습니다.

  2. 차단 패턴 확인: 경로의 모든 구성 요소를 차단 패턴 목록과 대조합니다 (대소문자 구분 없음):

    패턴 보호 대상
    .ssh SSH 개인 키
    .gnupg GPG 개인 키
    .env 시크릿이 포함된 환경 파일
    .aws AWS 자격증명
    .azure Azure 서비스 주체 자격증명
    .gcloud Google Cloud 자격증명
    .docker Docker 레지스트리 자격증명
    .kube Kubernetes 클러스터 자격증명
  3. 허용 목록 루트 확인: 정규화된 경로는 명시적으로 허용된 루트 중 적어도 하나의 하위 경로여야 하며, 관리자가 해당 경로를 포함하는 루트를 추가하지 않는 한 어떤 경로도 허용되지 않습니다.

  4. 컨테이너 경로 검사: 컨테이너 쪽 마운트 경로는 /workspace/extra/{name}으로 제한되며, ..이 들어간 경로는 거부됩니다.

  5. 비메인 그룹 강제 읽기 전용: 비메인 그룹의 에이전트는 요청한 모드와 관계없이 항상 읽기 전용 마운트를 받습니다.

실행 전 마운트 검증

curl -X POST http://localhost:55765/api/v1/container/mount/validate \
  -H "Content-Type: application/json" \
  -d '{
    "hostPath": "/Users/alice/projects/myapp",
    "containerPath": "/workspace/extra/myapp",
    "readOnly": true
  }'

검증에 성공한 경우의 응답:

{
  "allowed": true,
  "canonicalHostPath": "/Users/alice/projects/myapp",
  "message": "Mount validated successfully"
}

차단된 경우의 응답:

{
  "allowed": false,
  "canonicalHostPath": "/Users/alice/.ssh",
  "message": "Path contains blocked pattern: .ssh"
}

레이어 3: 환경 변수 검사

컨테이너는 엄격하게 통제된 환경에서 시작됩니다. 호스트의 환경 변수는 모두 제거되고, 승인된 일부만 그대로 전달되거나 값이 재정의됩니다:

변수 동작
ANTHROPIC_BASE_URL 호스트에서 전달 (자격증명 프록시를 가리킴)
ANTHROPIC_API_KEY 항상 플레이스홀더로 재정의 (CREDENTIAL_PROXY_PLACEHOLDER)
TZ 올바른 시간대를 위해 호스트에서 전달

그 밖의 환경 변수는 컨테이너 안에서 보이지 않으므로, 호스트의 시크릿이 실수로든 악의적으로든 새어 나갈 길이 없습니다.

레이어 4: 자격증명 프록시

자격증명 프록시는 API 키 노출을 막는 주된 장치입니다.

동작 방식

sequenceDiagram
    participant A as 컨테이너 에이전트
    participant P as 자격증명 프록시 (localhost:3001)
    participant U as 업스트림 API (api.anthropic.com)

    A->>P: POST /v1/messages\nAuthorization: Bearer CREDENTIAL_PROXY_PLACEHOLDER
    P->>P: 플레이스홀더 검증
    P->>P: 실제 API 키로 대체
    P->>U: POST /v1/messages\nAuthorization: Bearer sk-ant-...
    U-->>P: 응답
    P-->>A: 응답 전달

프록시 모드

모드 대체되는 헤더
API 키 x-api-key: CREDENTIAL_PROXY_PLACEHOLDERx-api-key: {real_key}
OAuth Bearer Authorization: Bearer CREDENTIAL_PROXY_PLACEHOLDERAuthorization: Bearer {real_token}

업스트림 URL 검증

업스트림 URL은 등록 시점에 검증됩니다:

  • HTTPS를 사용해야 합니다 (localhost/127.0.0.1은 개발 용도에 한해 HTTP 허용)
  • 클라우드 메타데이터 엔드포인트(예: 169.254.169.254)는 대상으로 지정할 수 없습니다
  • RFC1918 사설 주소도 대상으로 지정할 수 없습니다 (SSRF 공격 방지)

프록시 상태

curl http://localhost:55765/api/v1/container/credential-proxy/status

레이어 5: IPC 권한

컨테이너는 파일 기반 IPC 채널로 호스트와 통신하는데, IPC 시스템이 그룹 단위 권한을 강제해 그룹 사이의 간섭을 차단합니다.

권한 모델

작업 메인 그룹 일반 그룹
임의의 채팅 JID로 메시지 전송 허용 차단 (자신의 채팅 JID만 가능)
임의의 그룹에 작업 생성 허용 차단 (자신의 그룹만 가능)
후속 메시지 수신 허용 허용
전역 지침 읽기 허용 허용

일반 그룹 컨테이너가 그룹 경계를 넘는 작업을 시도하면 그 시도는 보안 위반으로 기록되고 IPC 명령은 거부됩니다.

보안 이벤트

IPC 권한 검사가 일어날 때마다 보안 이벤트가 생성됩니다:

{
  "id": "evt-abc123",
  "severity": "violation",
  "category": "ipc_permission",
  "message": "Container 'aigo-squad-grp1-session42' attempted cross-group send to 'tg:9876'. Blocked.",
  "containerId": "aigo-squad-grp1-session42",
  "groupName": "grp1",
  "timestamp": "2026-03-15T10:30:00Z"
}

레이어 6: 보안 감사

보안 감사는 120초 간격으로 주기적으로 돌면서, 실행 중인 컨테이너가 예상 구성과 일치하는지 확인합니다.

감사 검사

  • 환경 변수: ANTHROPIC_API_KEY가 실제 키가 아니라 플레이스홀더인지 확인
  • 마운트 포인트: 마운트가 승인된 목록과 일치하는지 확인
  • 새 권한 없음: no-new-privileges 보안 옵션이 설정되어 있는지 확인 (Docker만 해당)

감사 로그

curl "http://localhost:55765/api/v1/container/audit-log?limit=50"

보안 이벤트 로그

보안 이벤트 로그에는 최근 500개의 보안 이벤트가 보관됩니다:

curl http://localhost:55765/api/v1/container/metrics

보안 권장 사항

최소 권한 원칙

  • 에이전트에게 실제로 필요한 최소한의 호스트 디렉터리만 마운트 허용 목록에 추가하세요.
  • 에이전트가 쓸 필요가 없다면 읽기 전용 마운트를 사용하세요.
  • 홈 디렉터리 루트(예: /Users/alice)는 허용 목록에 추가하지 말고, 더 좁은 경로를 구체적으로 지정하세요.

Management API 보호

Management API는 자격증명 매핑을 포함한 모든 컨테이너 구성을 노출합니다. 항상 다음을 지키세요: * 설정 > 고급 > Management API에서 API 키 인증을 활성화하세요. * 원격 접근이 꼭 필요한 경우가 아니라면 Management API를 localhost에만 바인딩하세요 (기본값). * 원격 접근이 필요하다면 TLS를 사용하세요.

감사 로그 보존

보안 이벤트는 인메모리 링 버퍼에 최대 500개까지 보관됩니다. 규정 준수가 필요하다면 감사 로그 엔드포인트로 이벤트를 정기적으로 내보내 영구 로그 관리 시스템에 저장해 두세요.