Metadata-Version: 2.5
Name: chamelion-vault
Version: 0.1.1
Summary: Client for a self-hosted chamelion vault gateway
Requires-Python: >=3.10
Description-Content-Type: text/markdown

<!-- ⚠️ 이 파일은 PyPI 설명란에 그대로 실린다 — 전 세계에 공개된다.
     볼트 주소·DDNS·포트·조직 이름·실제 시크릿 경로를 적지 않는다.
     예시는 언제나 명백한 가짜(`myproject/dev/EXAMPLE_KEY`)로 쓴다.
     저장소의 `tests/test_client_package.py` 가 이 규칙을 검사한다. -->

# chamelion-vault

자체 호스팅 chamelion vault 게이트웨이에서 **시크릿 값을 받아 오는 클라이언트**입니다.

- **의존성이 없습니다.** 표준 라이브러리만 씁니다.
- **값을 디스크에 쓰지 않고 캐시하지 않습니다.** 받은 값은 프로세스 메모리에만 있습니다.
- **로깅을 설정하지 않습니다.** 이 패키지는 여러분의 애플리케이션 안에서 돕니다.

> 이 패키지는 **볼트를 운영하는 조직의 구성원**을 위한 것입니다. 볼트 주소·머신 API 키·
> 터널 접속 정보는 여기 없고 사내 문서에 있습니다. 그것을 모르면 설치까지는 되지만
> 값을 받는 단계에서 막히는 것이 정상입니다.

## 설치

```bash
pip install chamelion-vault
```

## 쓰는 법

```python
from chamelion_vault import get_secret

secret = get_secret("myproject", "dev", "EXAMPLE_KEY")
api_key = secret["value"].get_secret_value()
```

값이 하나뿐인 시크릿은 `["value"]` 에 있습니다. 한 몸인 크리덴셜을 한 경로에 여러
필드로 둔 것을 **번들**이라 부르고, 그때는 필드가 여럿입니다.

```python
secret = get_secret("myproject", "dev", "EXAMPLE_BUNDLE")
sorted(secret)                                  # ['access_key', 'secret_key']
secret["access_key"].get_secret_value()
```

프로젝트·환경이 같고 이름만 여럿이면 한 번에 받을 수 있습니다. **요청은 건별로 나가고**,
하나라도 실패하면 전부 실패로 끝납니다 — 반쪽짜리 설정으로 애플리케이션이 뜨는 것보다
뜨지 않는 편이 낫기 때문입니다.

```python
from chamelion_vault import get_secrets

secrets = get_secrets("myproject", "dev", ["EXAMPLE_KEY", "EXAMPLE_DB_URL"])
secrets["EXAMPLE_KEY"]["value"].get_secret_value()
```

### 값이 상자에 담겨 옵니다

`get_secret_value()` 로 열기 전까지 값은 어디에도 그대로 나타나지 않습니다.

```python
print(secret)             # Secret(myproject/dev/EXAMPLE_KEY, fields=['value'])
print(secret["value"])    # <REDACTED>
```

무심코 남긴 로그 한 줄에 진짜 키가 찍히는 것을 막습니다. **연 뒤에는 막아 주지 않으니**
꺼낸 값은 바로 쓰고 변수에 오래 들고 있지 마세요.

## 설정 — 환경변수 둘

| 환경변수 | 뜻 | 기본값 |
|---|---|---|
| `CHAMELION_VAULT_KEY` | 머신 API 키 | 없음 (필수) |
| `CHAMELION_VAULT_URL` | 게이트웨이 주소 | `http://127.0.0.1:8081` |

**설정 파일은 지원하지 않습니다.** 파일을 읽게 만들면 거기에 키를 적게 되고, 그 파일은
언젠가 저장소에 들어갑니다. 키를 인자로 받는 함수도 두지 않았습니다 — 같은 이유입니다.

머신 API 키는 Slack 에서 받습니다.

```
/vault machine
```

관리자 승인 뒤 **한 번만** 표시됩니다. 잃어버리면 다시 요청하세요.

## 터널

게이트웨이는 볼트 호스트의 **루프백에만** 열려 있습니다. 밖에서 값을 받으려면 그
루프백까지 가는 SSH 터널이 필요하고, **이 패키지는 터널을 대신 띄우지 않습니다.**
프로세스 수명·키 경로·OS 차이가 전부 따라 들어와, 실패했을 때 터널 문제인지 조회
문제인지 구분이 안 되기 때문입니다.

`~/.ssh/config` 에 한 번 적어 두면 그 뒤로는 한 줄입니다.

```
Host vault-tunnel
    HostName <볼트 호스트>
    Port <SSH 포트>
    User <계정>
    IdentityFile ~/.ssh/<터널 키>
    IdentitiesOnly yes
    LocalForward 8081 127.0.0.1:8081
```

```bash
ssh -N vault-tunnel
```

> ⚠️ **왼쪽 포트와 오른쪽 포트는 다른 것입니다.** `LocalForward 8081 127.0.0.1:8081` 에서
> 앞의 `8081` 은 내 컴퓨터에서 고른 포트이고, 뒤의 `127.0.0.1:8081` 은 볼트 호스트의
> 게이트웨이입니다. 내 컴퓨터에서 8081 을 이미 쓰고 있다면 **앞의 값만** 바꾸고
> `CHAMELION_VAULT_URL` 을 거기에 맞추세요. 뒤의 값을 바꾸면 터널이 아예 열리지 않습니다.
>
> ```
>     LocalForward 8082 127.0.0.1:8081
> ```
> ```bash
> export CHAMELION_VAULT_URL=http://127.0.0.1:8082
> ```

> ⚠️ **`IdentitiesOnly yes` 를 빠뜨리지 마세요.** 이것이 없으면 기본 키(`~/.ssh/id_*`)가
> 먼저 인증을 시도해, 터널 키에 걸어 둔 제약이 통째로 비껴갑니다. 실패 원인을 찾기
> 가장 어려운 자리입니다.

`<볼트 호스트>`·`<SSH 포트>`·`<계정>`·`<터널 키>` 는 사내 터널 문서에 있습니다.

## 값을 받지 못했을 때

볼트는 **실패 원인을 알려주지 않습니다.** 키가 틀렸는지·권한이 없는지·그런 시크릿이
없는지를 응답으로 가르면 그것이 곧 남의 볼트를 훑는 도구가 되기 때문입니다. 그래서
이 패키지도 추측하지 않고, 대신 확실히 아는 것만 말합니다.

| 예외 | 뜻 | 할 일 |
|---|---|---|
| `MissingApiKeyError` | 환경변수가 비었다 | `/vault machine` 으로 키를 받는다 |
| `VaultUnreachableError` | 연결 자체가 안 됐다 | 터널이 떠 있는지 본다 |
| `SecretUnavailableError` | 볼트가 값을 주지 않았다 | 아래 셋을 순서대로 확인한다 |
| `UnexpectedResponseError` | 계약에 없는 응답이 왔다 | 주소가 정말 게이트웨이인지 확인한다 |

`SecretUnavailableError` 의 원인은 셋입니다.

1. **접근 그룹** — 그 시크릿에 접근 그룹이 지정돼 있지 않으면 머신은 값을 받지
   못합니다. **가장 흔한 원인**이니 소유자에게 먼저 확인하세요.
2. **머신 키** — 아직 승인 전이거나 폐기됐을 수 있습니다.
3. **경로** — 프로젝트·환경·이름 중 하나가 틀렸을 수 있습니다.

네 예외는 모두 `VaultError` 를 상속하므로 한 번에 잡을 수 있습니다.

```python
from chamelion_vault import VaultError

try:
    secret = get_secret("myproject", "dev", "EXAMPLE_KEY")
except VaultError as error:
    print(error)   # 무엇을 해야 하는지가 메시지에 들어 있습니다
```

## 받을 수 없는 것

- **개인 등급 시크릿.** 소유자 본인만 Slack 으로 받습니다. 머신에게는 열리지 않고,
  관리자도 값을 볼 수 없습니다. 코드에서 써야 하는 값이라면 개인 등급으로 두면 안 됩니다.
- **프로젝트에 딸린 시크릿 목록.** 게이트웨이는 이름을 알고 있는 시크릿 한 건을 줄 뿐,
  "이 프로젝트에 무엇이 있는지" 는 알려주지 않습니다. 목록은 Slack 에서 봅니다.
