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
7 changes: 7 additions & 0 deletions .sops.nix
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,15 @@ let
"modules/nextcloud/secrets.yaml" = [ "tau" ];
"modules/nfs/secrets.yaml" = [ "psi" ];
"modules/users/xrdp-passwords.yaml" = [ "psi" ];
"terraform/authentik/secrets.yaml" = [ ];
"terraform/authentik/oidc-secrets.yaml" = [
"eta"
"tau"
];
"terraform/authentik/users.yaml" = [ ];
"terraform/cloudflare/secrets.yaml" = [ ];
"terraform/github/secrets.yaml" = [ ];
"terraform/headscale/secrets.yaml" = [ ];
"terraform/vultr/secrets.yaml" = [ ];
}
// {
Expand Down
18 changes: 18 additions & 0 deletions .sops.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,20 @@ creation_rules:
- age1zdhqm6ptcnuu3tf2lzcngqmf6eud7jfah7v8falfy5mdksmnfuzq35sq54
- age1730f3cxdyh56zw8xcvlmpa7u2x7353wu4u0e58kyx24rsefgp98sxehm6s
path_regex: modules/vaultwarden/secrets.yaml
- key_groups:
- age:
- age1zdhqm6ptcnuu3tf2lzcngqmf6eud7jfah7v8falfy5mdksmnfuzq35sq54
- age13v0djuhkmnd06zvct0zc6sddykqpk3j9k8ev5sfgd6gtj82s0avs68psvj
- age1730f3cxdyh56zw8xcvlmpa7u2x7353wu4u0e58kyx24rsefgp98sxehm6s
path_regex: terraform/authentik/oidc-secrets.yaml
- key_groups:
- age:
- age1730f3cxdyh56zw8xcvlmpa7u2x7353wu4u0e58kyx24rsefgp98sxehm6s
path_regex: terraform/authentik/secrets.yaml
- key_groups:
- age:
- age1730f3cxdyh56zw8xcvlmpa7u2x7353wu4u0e58kyx24rsefgp98sxehm6s
path_regex: terraform/authentik/users.yaml
- key_groups:
- age:
- age1730f3cxdyh56zw8xcvlmpa7u2x7353wu4u0e58kyx24rsefgp98sxehm6s
Expand All @@ -127,6 +141,10 @@ creation_rules:
- age:
- age1730f3cxdyh56zw8xcvlmpa7u2x7353wu4u0e58kyx24rsefgp98sxehm6s
path_regex: terraform/github/secrets.yaml
- key_groups:
- age:
- age1730f3cxdyh56zw8xcvlmpa7u2x7353wu4u0e58kyx24rsefgp98sxehm6s
path_regex: terraform/headscale/secrets.yaml
- key_groups:
- age:
- age1730f3cxdyh56zw8xcvlmpa7u2x7353wu4u0e58kyx24rsefgp98sxehm6s
Expand Down
6 changes: 5 additions & 1 deletion docs/admin/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
| Nextcloud | OIDC (`user_oidc`) | SSO 로그인 |
| Vaultwarden | OIDC (PKCE) | SSO 로그인 |
| n8n | Forward Auth | nginx에서 인증 후 이메일 헤더 전달 (Headscale ACL + Forward Auth 이중 보호) |
| Grafana | Forward Auth | Tailnet에서만 접근 가능한 관리자 대시보드 |
| Gatus | 없음 | Tailnet에서만 접근 가능한 공개 상태 페이지 (Authentik dashboard tile만 표시) |
| Nixbot | GitHub OAuth | CI/CD 대시보드 접근 |

### RAGFlow UI-only residue cleanup
Expand Down Expand Up @@ -58,4 +60,6 @@ Authentik outpost는 `wg-admin` 인터페이스(포트 9000)에서만 접근 가
| `sjanglab-researchers` | AI + 앱 접근 | `tag:ai`, `tag:apps` |
| `sjanglab-students` | 앱만 접근 | `tag:apps` |

Authentik 그룹은 Headscale ACL과 15분마다 자동 동기화됩니다. 상세 매커니즘은 [네트워크 — ACL 동기화](network.md#acl-sync)를 참조하세요.
Authentik 사용자와 그룹 선언은 `terraform/authentik`에서 관리합니다. 사람 계정 목록은 개인정보 보호를 위해 SOPS로 암호화한 `terraform/authentik/users.yaml`에 둡니다. 학생 계정은 `expires_on`을 반드시 지정하고, 만료 후에는 `active: false`로 변경합니다.

Headscale ACL policy는 `terraform/headscale`이 같은 `users.yaml`에서 생성합니다. 비활성 사용자는 Authentik group membership과 Headscale ACL group에서 함께 빠집니다. 상세 매커니즘은 [네트워크 — ACL 정책 관리](network.md#acl-policy)를 참조하세요.
4 changes: 2 additions & 2 deletions docs/admin/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ SBEE Lab 인프라를 운영하기 위한 관리자 가이드입니다.
| `inv` (invoke) | 배포, 운영, 사용자 관리 자동화 | 로컬 `tasks.py` |
| Authentik | SSO/사용자/그룹 관리 | `https://auth.sjanglab.org` |
| sops | 시크릿 암호화/복호화 | `sops hosts/<host>.yaml` |
| Terraform | DNS, GitHub 리소스 관리 | `terraform/` 디렉토리 |
| Terraform | DNS, GitHub, Authentik, Headscale 정책 관리 | `terraform/` 디렉토리 |

## 인증 흐름

Expand All @@ -18,7 +18,7 @@ SBEE Lab 인프라를 운영하기 위한 관리자 가이드입니다.
→ nginx → Authentik Forward Auth → 서비스
```

Authentik 그룹(`sjanglab-admins`, `sjanglab-researchers`, `sjanglab-students`)이 Headscale ACL과 15분마다 자동 동기화되어 네트워크 수준의 접근 제어가 이루어집니다.
Terraform이 Authentik 그룹과 Headscale ACL policy를 같은 사용자 inventory에서 생성하여 네트워크 수준의 접근 제어가 이루어집니다.

## 주요 명령어

Expand Down
28 changes: 12 additions & 16 deletions docs/admin/network.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ NAT 뒤 호스트(rho, tau)는 엔드포인트가 없으며, 공인 IP 호스트

### ACL 그룹 { #acl-groups }

Authentik 그룹과 15분마다 자동 동기화됩니다.
Terraform이 `terraform/authentik/users.yaml`의 그룹 membership으로 생성합니다.

| 그룹 | 접근 태그 |
|------|----------|
Expand All @@ -91,27 +91,23 @@ Authentik 그룹과 15분마다 자동 동기화됩니다.

> †n8n은 네트워크 수준에서 `tag:apps`로 접근 가능하나, Authentik Forward Auth에서 관리자만 허용합니다.

### ACL 동기화 매커니즘 { #acl-sync }
### ACL 정책 관리 { #acl-policy }

Headscale ACL policy는 `terraform/headscale`의 `headscale_policy.tailnet` 리소스가 관리합니다. 사용자 membership은 SOPS로 암호화한 `terraform/authentik/users.yaml`을 source of truth로 사용하고, Authentik 사용자/그룹과 Headscale ACL policy가 같은 inventory에서 생성됩니다.

```mermaid
sequenceDiagram
participant T as systemd 타이머 (15분)
participant S as acl-sync 스크립트
participant A as Authentik API
participant U as users.yaml
participant T as Terraform
participant A as Authentik
participant H as Headscale

T->>S: 실행
S->>A: 그룹 멤버십 조회
A-->>S: 그룹/사용자 목록
S->>S: 정적 ACL + 동적 그룹 병합
S->>H: ACL 파일 갱신
H->>H: inotify 감지 → 리로드
U->>T: 사용자/그룹 inventory
T->>A: Authentik 사용자/그룹 반영
T->>H: Headscale ACL policy 반영
```

1. systemd 타이머가 15분마다 `acl-sync` 스크립트를 실행
1. Authentik API에서 그룹 멤버십을 조회
1. 정적 ACL 규칙과 동적 그룹 정보를 병합하여 새 ACL 파일 생성
1. Headscale이 inotify로 ACL 파일 변경을 감지하여 자동 리로드
Headscale은 `policy.mode = "database"`로 동작합니다. ACL 변경 후 즉시 반영하려면 `terraform/headscale`에서 `terragrunt apply`를 실행합니다.

## 방화벽 정책

Expand All @@ -131,4 +127,4 @@ sequenceDiagram

## ACME 인증서

대부분의 TLS 인증서는 eta에서 Cloudflare DNS 챌린지로 발급됩니다. Nixbot(`buildbot.sjanglab.org`) 공개 인증서는 eta에서 발급되고, psi의 Nixbot nginx도 wg-admin upstream용 인증서를 유지합니다. 다른 호스트(psi, tau)의 나머지 인증서는 `acme-sync` 사용자를 통해 rsync로 동기화됩니다.
대부분의 TLS 인증서는 eta에서 Cloudflare DNS 챌린지로 발급됩니다. Nixbot(`buildbot.sjanglab.org`) 공개 인증서는 eta에서 발급되고, psi의 Nixbot nginx도 wg-admin upstream용 인증서를 유지합니다. 다른 호스트(rho, tau, psi)의 나머지 인증서는 `acme-sync` 사용자를 통해 rsync로 동기화됩니다.
54 changes: 39 additions & 15 deletions docs/admin/terraform.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Terraform

외부 리소스(Cloudflare DNS, GitHub) 코드로 관리합니다.
외부 리소스(Cloudflare DNS, GitHub), Authentik 애플리케이션 정책, Headscale ACL policy를 코드로 관리합니다.

## 백엔드

Expand All @@ -18,20 +18,44 @@ terraform apply

### Cloudflare DNS (`sjanglab.org`)

| 레코드 | 값 | 용도 |
|--------|-----|------|
| `buildbot.sjanglab.org` | 141.164.53.203 | Nixbot CI/CD edge proxy (eta → psi) |
| `logging.sjanglab.org` | 141.164.53.203 | Grafana |
| `hs.sjanglab.org` | 141.164.53.203 | Headscale |
| `auth.sjanglab.org` | 141.164.53.203 | Authentik |
| `vault.sjanglab.org` | 141.164.53.203 | Vaultwarden |
| `gatus.sjanglab.org` | 141.164.53.203 | 상태 페이지 |
| `ntfy.sjanglab.org` | 141.164.53.203 | 알림 |
| `n8n.sjanglab.org` | 141.164.53.203 | 워크플로우 |
| `cache.sjanglab.org` | 141.164.53.203 | Nix 캐시 |
| `upterm.sjanglab.org` | 141.164.53.203 | Upterm relay |

대부분의 웹 서비스는 eta(141.164.53.203)의 nginx를 통해 프록시됩니다. Nixbot 서비스 스택은 psi에 있지만 `buildbot.sjanglab.org` 공개 ingress는 eta가 받아 wg-admin으로 psi에 프록시합니다. Upterm relay는 eta의 `2323/tcp`에 직접 노출됩니다.
공개 ingress가 필요한 레코드만 Cloudflare DNS에 둡니다. Tailnet 전용 서비스 이름은 Headscale split DNS로 관리합니다.

### Authentik

`terraform/authentik`은 사용자, 그룹, nginx forward auth에 필요한 Authentik proxy provider, application, embedded outpost attachment, access policy binding을 관리합니다. Terraform token은 `terraform/authentik/secrets.yaml`의 `AUTHENTIK_TOKEN`으로 전달합니다. 사람 계정 목록은 SOPS로 암호화한 `terraform/authentik/users.yaml`에 둡니다.

기존 UI 객체를 Terraform으로 전환할 때는 먼저 import helper를 실행한 뒤 plan을 확인합니다.

```bash
cd terraform/authentik
terragrunt init
./import-existing.sh
terragrunt plan
```

학생 계정은 `users.yaml`에서 `expires_on`을 설정합니다. 만료된 학생 계정은 `active: false`로 바꿔 Authentik 로그인과 Headscale ACL group membership을 함께 제거합니다.

관리 대상:

| 애플리케이션 | 그룹 |
|--------------|------|
| `n8n.sjanglab.org` | `sjanglab-admins`, `sjanglab-researchers` |
| `status.sjanglab.org` | 인증 없음 (Authentik dashboard tile만 관리) |
| `logging.sjanglab.org` | `sjanglab-admins` |

### Headscale

`terraform/headscale`은 Headscale database ACL policy를 관리합니다. Headscale API key는 `terraform/headscale/secrets.yaml`의 `HEADSCALE_API_KEY`로 전달합니다. 사용자 membership은 `terraform/authentik/users.yaml`을 함께 읽어 Authentik과 같은 source of truth를 사용합니다.

Headscale module은 `services.headscale.settings.policy.mode = "database"` 배포 후 apply합니다. 기존 Headscale users는 먼저 import합니다. `headscale_policy`는 provider가 import를 지원하지 않아 첫 apply가 singleton database policy를 설정하면서 Terraform state를 만듭니다.

```bash
cd terraform/headscale
terragrunt init
./import-existing.sh
terragrunt plan
terragrunt apply
```

### GitHub

Expand Down
30 changes: 15 additions & 15 deletions docs/admin/user-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
| **NixOS** (`modules/users/`) | SSH 계정, 로컬 사용자 | 서버 접속, 파일시스템, Docker |
| **Authentik** (`auth.sjanglab.org`) | SSO 계정, 그룹 | 웹 서비스 인증, Headscale ACL |

두 시스템은 독립적으로 관리되며, Headscale ACL만 Authentik 그룹에서 15분마다 자동 동기화됩니다.
SSO 계정과 Headscale ACL은 `terraform/authentik/users.yaml`을 source of truth로 삼아 Terraform에서 함께 관리합니다.

______________________________________________________________________

Expand Down Expand Up @@ -137,30 +137,30 @@ Authentik은 웹 서비스(Nextcloud, Vaultwarden, n8n)의 SSO 인증과 Headsca

### 사용자 추가

1. `https://auth.sjanglab.org/if/admin/` 접속
1. **Directory → Users** 에서 사용자를 생성하거나 초대합니다
1. **Directory → Groups** 에서 해당 그룹에 사용자를 추가합니다
1. `terraform/authentik/users.yaml`에 사용자를 추가합니다
1. 학생 계정이면 `expires_on`을 설정합니다
1. `terraform/authentik`과 `terraform/headscale` plan을 확인한 뒤 apply합니다

### ACL 동기화
### ACL 정책 반영

Authentik 그룹 변경은 Headscale ACL에 자동 반영됩니다:
Authentik 사용자/그룹과 Headscale ACL은 같은 SOPS inventory에서 생성됩니다:

1. systemd 타이머가 15분마다 `headscale-acl-sync` 서비스를 실행
1. Authentik API에서 `sjanglab-*` 그룹의 멤버십을 조회
1. 정적 ACL 규칙(`acl-rules.nix`)과 동적 그룹 정보를 병합하여 `policy.json` 생성
1. Headscale이 inotify로 파일 변경을 감지하여 자동 리로드
1. `terraform/authentik/users.yaml`이 사용자와 그룹 membership의 source of truth입니다
1. `terraform/authentik`은 Authentik 사용자/그룹을 반영합니다
1. `terraform/headscale`은 같은 membership으로 Headscale database ACL policy를 생성합니다

즉시 동기화가 필요하면:
즉시 반영하려면:

```bash
ssh -p 10022 root@eta systemctl start headscale-acl-sync.service
cd terraform/headscale
terragrunt apply
```

### 사용자 비활성화

1. Authentik 관리 UI에서 사용자를 **비활성화** (삭제가 아닌 비활성화)
1. 그룹에서 제거합니다
1. 다음 ACL 동기화 시 Headscale 접근이 차단됩니다
1. `terraform/authentik/users.yaml`에서 `active: false`로 변경합니다
1. `terraform/authentik`을 apply해 Authentik 로그인을 비활성화합니다
1. `terraform/headscale`을 apply해 Headscale ACL 그룹에서 제거합니다

______________________________________________________________________

Expand Down
59 changes: 0 additions & 59 deletions modules/headscale/acl-rules.nix

This file was deleted.

Loading
Loading