Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

### 추가

- **정책 파일 신뢰 기록 (TOFU).** `airlock run`이 정책 파일을 읽을 때마다 정책 다이제스트를 `<감사루트>/trusted-policies.jsonl`의 기록과 대조함. 처음 보거나 바뀐 정책은 `/dev/tty`로 사람에게 확인받고, 터미널이 없으면 종료 코드 78로 거부하며 **`--yes`와 `--observe`로 건너뛸 수 없음**. `airlock policy trust [PATH] [--list]`가 신설되고 `airlock setup`이 만든 파일은 자동으로 기록됨. Linux Landlock이 아직 없는 경로의 생성을 거부할 수 없어 샌드박스 안 에이전트가 `./airlock.toml`을 심을 수 있다는 한계의 로드 시점 방어임. 근거는 `docs/design.md` 9.6
- **`--mediate full`이 rename과 link 계열을 중계함.** `rename`, `renameat`, `renameat2`, `link`, `linkat`, `symlink`, `symlinkat`을 원본 `delete`와 목적지 `create`로 판정하며 하나라도 deny면 거부함. 같은 디렉토리 안 `rename`으로 자기보호 대상 정책 파일을 갈아 끼우는 경로가 이 수준에서 닫힘. 기본 수준 `exec-net`에는 넣지 않음
- **Linux seccomp 필터가 모든 중계 수준(`off` 포함)에서 `ioctl(TIOCSTI)`와 `ioctl(TIOCLINUX)`를 EPERM으로 거부함.** 자식이 상속한 터미널에 입력을 밀어 넣어 ask 승인 프롬프트를 위조하는 경로를 닫음. macOS는 Seatbelt가 이미 거부함. 정상 프로그램은 두 ioctl을 쓰지 않으며 bubblewrap과 flatpak이 같은 조치를 함
- **출력 로케일 (한국어·영문).** 모든 사람용 출력(CLI 도움말, 배너와 한계 목록, 승인 프롬프트, 감사 보고, 마법사, 생성되는 정책 파일의 주석)이 로케일을 따름. 결정 순서는 `AIRLOCK_LANG` -> `~/.config/airlock/config.toml`의 `locale` 키 -> `LC_ALL`/`LC_MESSAGES`/`LANG` 접두 -> 기본 한국어. 기계 판독 필드(JSON 키, `kind`·`status`·`verdict`, 스키마 이름, 규칙 id)와 **정책 다이제스트는 로케일과 무관하게 고정**임. 자세한 것은 `docs/i18n.md`
- **`airlock setup` 첫 질문이 언어 선택.** 답하는 즉시 나머지 질문이 그 언어로 나가고 선택이 구성 파일에 저장됨. 영문 프리셋(`examples/policy/en/*.toml`)이 추가되었으며 한국어판과 주석만 다르고 다이제스트가 같음을 테스트가 강제함
- `airlock audit report --json` 최상위에 `locale` 필드 추가. 이상 `detail` 산문이 로케일을 따르므로 리포트 본문 다이제스트가 생성 로케일에 묶이며, `report`와 `ack`는 같은 로케일에서 돌려야 함 (`docs/limitations.md` 7.18)
Expand Down Expand Up @@ -41,6 +44,12 @@

### 수정

- **macOS 프록시 모드가 유닉스 도메인 소켓을 통째로 열던 문제.** `(allow network-outbound (remote unix))`를 무조건 방출해 샌드박스 안 프로세스가 바깥 유닉스 소켓 리스너로 데이터를 내보내고 상속된 `SSH_AUTH_SOCK`으로 ssh-agent에 서명을 요청할 수 있었으며, 살아 있는 docker.sock이면 컨테이너 데몬에 닿았음. 전부 프록시도 감사 로그도 거치지 않았음. 이제 `/private/var/run/mDNSResponder`와 `/private/var/run/syslog` 리터럴 두 개만 열고 배너와 `gaps()`에 그 목록을 드러냄. 자식 환경에서 `SSH_AUTH_SOCK`과 `DOCKER_HOST`를 벗김
- **앵커 재앵커링으로 종료 뒤 덧붙이기가 탐지되지 않던 문제.** 같은 세션의 두 번째 앵커 줄이 경고로만 남아, 체인과 앵커 파일에 append만 할 수 있는 공격자가 종료된 세션에 엔트리를 덧붙이고 새 앵커 한 줄로 검증을 통과시킬 수 있었음. 세션당 앵커는 정확히 하나이며 초과는 실패임. 앵커 뒤 엔트리(`entries_after_anchor`), `session_end` 뒤 엔트리, 앵커된 세션의 `head.json` 뒤처짐(`anchored_head_lag`, `fsync_per_entry = false`만 예외)이 전부 검증 실패와 증거 이상이 됨. `Session::finish()`를 두 번 부르면 오류
- **상대 심볼릭 링크 대상을 어휘적 부모에 붙여 해소하던 문제.** 심볼릭 링크된 디렉토리 아래의 `x -> ../id_rsa`를 커널은 링크 대상 쪽 파일로 여는데 엔진은 작업 공간 안 경로로 계산해 작업 공간 규칙으로 허용했음. 상대 대상은 해소된 부모에 붙이고, 매달린 링크도 같은 기준으로 계산함
- **macOS `/System/Volumes/Data/...` 표기가 전 티어를 비껴가던 문제.** firmlink 때문에 같은 파일의 두 번째 철자가 자기보호와 `~/.ssh/**` forbid에 매칭하지 않았음 (커널 Seatbelt는 막았지만 정책 판정, `explain`, `--observe`, 감사 귀속이 틀렸음). `/usr/share/firmlinks` 표(못 읽으면 내장 목록)에 있는 대상을 루트 표기로 접으며 요청 경로와 규칙 경로 양쪽에 적용함. 정책 다이제스트는 바뀌지 않음
- **프록시 평문 전달 경로의 bare-LF 요청 스머글링.** 헤드를 `\r\n`으로만 잘라 줄 안의 `\n`이 값에 남았고 그것이 업스트림에 그대로 재조립되어 두 번째 `Host`를 심을 수 있었음. CRLF 쌍이 아닌 CR과 LF, 헤더 값의 제어문자, obs-fold, 중복 `Host`, `HTTP/1.0`과 `HTTP/1.1` 이외의 버전, 비ASCII 호스트를 판정 전에 400으로 거부하고 업스트림에 접속하지 않음
- **중계 층이 `dirfd`를 부호 있는 64비트로 읽던 문제.** 커널은 하위 32비트만 보므로 상위 비트를 세운 값이 `AT_FDCWD`로 해석되어야 하는데 기준을 잃고 브로커 cwd에 붙였음. 커널과 같이 읽고 기준을 못 찾으면 거부함
- **Landlock 순회 예산이 루트 전체에서 공유되어 뒤쪽 루트가 통째로 빠지던 문제.** `/usr` 아래 큰 트리(안드로이드 SDK, CUDA 등)가 예산 20만을 다 쓰면 그 뒤의 `/bin`, `/sbin`, `/lib`, `/etc`, 작업 공간이 규칙을 하나도 못 받아 프로그램이 exec 조차 되지 않았음. 예산을 루트마다 새로 주고, 예산이 끊겨도 그 전에 검사를 마친 항목은 규칙을 받게 함
- **나열할 수 없는 디렉토리가 개별 허용 단계에서 도로 열리던 문제.** 순회는 `Denied`로 판정했지만 매칭되는 규칙이 없어 `blocked`가 false 라, 하위를 검사하지도 못한 채 규칙을 받았음. 순회 결과를 기억해 개별 허용에서 제외함
- **순회 순서가 `read_dir` 반환 순서에 의존하던 문제.** 파일시스템 해시 순서라 같은 정책이 머신마다 다른 강제 범위를 냈음. 이름 순으로 고정함
Expand Down
2 changes: 1 addition & 1 deletion COMPLIANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@

이것이 가장 중요합니다. 별표 7은 **조직이 사용자 단말에 통제를 강제하는** 구도를 전제합니다. 반면 airlock의 신뢰 경계는 "브로커 vs. 에이전트"이고 **사용자는 경계 안쪽**입니다. 구체적으로,

- 정책 파일은 호출자 uid가 소유하고 group·other 쓰기가 없으면 그대로 신뢰됩니다(`airlock-policy/src/path.rs:51`). 서명도 중앙 배포도 요구하지 않기 때문에 사용자가 언제든 다시 쓸 수 있습니다.
- 정책 파일은 호출자 uid가 소유하고 group·other 쓰기가 없으면 읽히고(`airlock-policy/src/path.rs:51`), 처음 보거나 다이제스트가 바뀐 파일은 `/dev/tty`에서 사용자 확인을 한 번 받아 신뢰 기록에 남습니다(`docs/design.md` 9.6). 확인의 주체가 사용자 자신이고 서명도 중앙 배포도 요구하지 않기 때문에 사용자가 언제든 다시 쓰고 다시 신뢰할 수 있습니다.
- 감사 로그 위치는 `AIRLOCK_AUDIT_DIR`로 사용자가 옮길 수 있습니다(`airlock/src/paths.rs:57`).
- 무엇보다 명령 앞에 `airlock run --`을 붙이지 않으면 아무 통제도 걸리지 않습니다.

Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ $ airlock policy check
$ airlock policy explain --file ~/.ssh/id_rsa
$ airlock policy explain --exec rm -rf /

# Run under the broker
# Run under the broker. A policy file seen for the first time gets a [y/N] prompt (files written by setup are already trusted)
$ airlock run -- claude

# Verify and inspect what happened
Expand All @@ -40,8 +40,11 @@ If there is no policy file, only the built-in baseline applies. Airlock looks fo

```bash
$ cp examples/policy/strict.toml airlock.toml
$ airlock policy trust airlock.toml
```

A policy file you placed by hand is shown (path and digest) on the first `airlock run` and must be confirmed with `[y/N]`. Runs without a terminal (CI, headless) are refused instead of prompted, so approve first with `airlock policy trust` as above. A changed policy is asked about again, and `--yes` does not skip this check.

The semantics of the policy system are written up in [policy-dsl.md](docs/policy-dsl.md).

All human-facing output is available in Korean (default) and English. `airlock setup` asks for the language first and saves the choice to `~/.config/airlock/config.toml`; `AIRLOCK_LANG=en` overrides it per run. English preset variants live in `examples/policy/en/`. Details in [i18n.md](docs/i18n.md).
Expand Down
5 changes: 4 additions & 1 deletion README_KR.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ $ airlock policy check
$ airlock policy explain --file ~/.ssh/id_rsa
$ airlock policy explain --exec rm -rf /

# 브로커 아래에서 실행
# 브로커 아래에서 실행. 처음 보는 정책 파일이면 [y/N] 확인을 받음 (setup 이 만든 파일은 이미 신뢰됨)
$ airlock run -- claude

# 무슨 일이 있었는지 검증, 조회
Expand All @@ -40,8 +40,11 @@ $ airlock audit ack --note "일일 점검"

```bash
$ cp examples/policy/strict.toml airlock.toml
$ airlock policy trust airlock.toml
```

손으로 둔 정책 파일은 첫 `airlock run`에서 경로와 다이제스트를 보여 주고 `[y/N]` 확인을 받습니다. 터미널이 없는 실행(CI, 헤드리스)은 확인 없이 거부되므로 위처럼 `airlock policy trust`로 먼저 승인합니다. 정책 내용이 바뀌면 다시 묻고, `--yes`로는 건너뛸 수 없습니다.

정책 시스템에 관한 의미론적 문서를 [policy-dsl.md](docs/policy-dsl.md)에 정리해 두었습니다.

모든 사람용 출력은 한국어(기본)와 영문을 지원합니다. `airlock setup`의 첫 질문이 언어 선택이며 선택은 `~/.config/airlock/config.toml`에 저장됩니다. 일회성 전환은 `AIRLOCK_LANG=en`으로 합니다. 영문 프리셋은 `examples/policy/en/`에 있습니다. 자세한 내용은 [i18n.md](docs/i18n.md)를 참고하세요.
Expand Down
Loading
Loading