콘텐츠로 이동

보안

인프라 전반의 보안 정책과 구현 현황을 정리합니다.

네트워크 보안

방화벽 정책

인터페이스별 화이트리스트 방식으로 포트를 관리합니다.

인터페이스 용도 열린 포트
eta eth0 (퍼블릭) public edge TCP 80, 443, 10022, 2323; UDP 51820
psi eth0 (퍼블릭) WireGuard/Tailscale transport UDP 51820, 41641; public TCP 없음
rho/tau eth0 (랩 내부 NAT) LAN 및 VPN transport tau TCP 80, 443; UDP 51820, 41641
wg-admin (WireGuard) 신뢰된 호스트 간 관리·서비스 트래픽 모든 포트
tailscale0 (Headscale) 사용자 서비스 서비스별 (주로 80, 443)

기본 정책은 deny all입니다. 단, wg-admin은 networking.firewall.trustedInterfaces에 등록되어 호스트 방화벽이 모든 트래픽을 허용합니다. wg-admin 서비스는 listen address와 WireGuard peer 관리로 노출 범위를 제한합니다. 퍼블릭·Headscale 인터페이스는 각 서비스 모듈이 필요한 포트만 개방합니다.

Bastion 아키텍처

eta가 유일한 인터넷 노출 SSH 호스트입니다. Upterm relay도 eta의 2323/tcp에서만 외부에 노출됩니다. 다른 호스트(psi, rho, tau)는 wg-admin 인터페이스에서만 SSH를 수신합니다.

flowchart LR
  inet["인터넷"] -- "포트 10022" --> eta["eta<br/>jump.sjanglab.org"]
  inet -- "포트 2323" --> eta
  eta -- "wg-admin" --> psi["psi<br/>10.100.0.2"]
  eta -- "wg-admin" --> rho["rho<br/>10.100.0.3"]
  eta -- "wg-admin" --> tau["tau<br/>10.100.0.4"]

SSH 연결 보호

eta는 공개키 인증만 허용합니다. 순차 연결 횟수 기반 방화벽 제한은 정상적인 관리자 접속과 병렬 배포를 차단하므로 사용하지 않습니다.

flowchart TD
  conn["SSH 연결 시도"] --> startup{"sshd MaxStartups<br/>동시 미인증 연결 용량"}
  startup -- "용량 초과" --> shed["확률적 연결 거부"]
  startup -- "통과" --> sshd["공개키 인증"]
  sshd -- "성공" --> ok["접속 허용"]
  sshd -- "실패" --> f2b["fail2ban sshd"]
계층 설정 목적
OpenSSH MaxStartups 64:30:256 병렬 배포를 수용하면서 동시 미인증 연결 폭주 제한
fail2ban sshd 10분 내 인증 실패 3회, 기본 5분 차단 반복 인증 공격 차단

재차 차단되는 IP는 전체 jail 이력을 기준으로 차단 시간이 지수 증가하며 최대 7일까지 늘어납니다. 단순 TCP 연결 종료를 공격으로 간주하는 aggressive jail은 사용하지 않습니다.

WireGuard VPN 분리

네트워크 대역 용도
wg-admin 10.100.0.0/24 인프라 관리 (SSH, DB, 내부 서비스)
Headscale 100.64.0.0/10 사용자 서비스 접근 (웹 서비스)

두 네트워크는 독립적입니다. wg-admin peer key는 인프라 호스트와 승인된 관리자 기기에만 배포합니다. 일반 사용자는 wg-admin peer가 되지 않으며, SSH가 허용된 사용자는 eta bastion을 통해 대상 호스트의 wg-admin 주소로 접속합니다.

계정 및 권한 정책

사용자 계층

역할 NixOS 그룹 sudo Docker SSH 호스트 만료
관리자 wheel, docker, admin, input O O 전체 —
연구원 docker, researcher, input X O 지정 호스트 필수
학생 docker, student, input X O 지정 호스트 필수

Root 접근 정책

접근 경로 허용
SSH (인터넷) 차단 (PermitRootLogin no)
SSH (wg-admin, 10.100.0.0/24) 공개키만 (prohibit-password)
sudo (로컬) wheel 그룹만

root의 authorized_keys에는 관리자 키만 등록됩니다 (modules/users/admins.nix).

시스템 계정

서비스별로 전용 시스템 계정이 분리되어 있습니다.

계정 서비스 유형 특기 사항
nixbot Nixbot CI isSystemUser psi의 nix.settings.extra-allowed-users에 명시
niks3 Nix 캐시 control plane system user R2 credential과 signing key만 읽기 가능
rustfs RustFS S3 저장소 isSystemUser /srv/rustfs/data 전용
acme-sync-* TLS 인증서 동기화 — rsync 전용, 제한된 경로
postgres PostgreSQL isSystemUser DB 전용
nextcloud Nextcloud isSystemUser —

Nix 권한

설정 대상 의미
allowed-users 모든 로컬 계정 (*) Nix daemon 연결, store 조회, 일반 build 허용
extra-allowed-users psi의 nixbot Nixbot service account를 명시적으로 허용하며 현재 wildcard와 중복
trusted-users root (NixOS), @wheel (srvos), seungwon (admin module) 임의 substituter와 서명되지 않은 store path 등 privileged daemon 기능 허용

일반 연구원·학생 계정은 nix shell, nix run, nix develop을 사용할 수 있지만 trusted user 권한은 없습니다. allowed-users는 SSH 로그인이나 호스트 접근 권한을 부여하지 않습니다.

SSH 보안

알고리즘 정책

항목 허용 알고리즘
Ciphers chacha20-poly1305, aes256-gcm, aes128-gcm
KexAlgorithms curve25519-sha256, diffie-hellman-group16-sha512, diffie-hellman-group18-sha512
MACs hmac-sha2-512-etm, hmac-sha2-256-etm

세션 정책

설정 값
인증 방식 공개키만 (비밀번호 비활성화)
키 알고리즘 Ed25519 권장
MaxAuthTries 3
LoginGraceTime 30초
ClientAliveInterval 1200초 (20분)
X11Forwarding 비활성화
Compression 비활성화

SSH CA 인증서

호스트 키는 CA로 서명되어 있어 known_hosts에 CA 공개키만 등록하면 모든 호스트를 신뢰할 수 있습니다.

@cert-authority *.sjanglab.org ssh-ed25519 AAAAC3...

설정 위치: modules/sshd/certs/

비밀 관리

sops-nix + age

모든 비밀은 age(Curve25519) 암호화로 보호됩니다. 호스트별 age 키와 관리자 키가 각 비밀 파일을 복호화할 수 있습니다.

비밀 파일 접근 가능 키 내용
hosts/<host>.yaml 해당 호스트 + admin root 비밀번호 해시, WireGuard 키
hosts/rho.yaml, hosts/tau.yaml 해당 호스트 RustFS root credential, host-local backup passwords, PostgreSQL 사용자/복제 암호
modules/acme/secrets.yaml eta, psi, tau Cloudflare API 인증
modules/buildbot/secrets.yaml psi Nixbot GitHub App/OAuth 시크릿
modules/gitea-mq/secrets.yaml eta merge queue GitHub App private key와 webhook secret
modules/authentik/secrets.yaml eta OIDC 클라이언트 시크릿

비밀 편집

# 비밀 편집 (age 키 자동 사용)
sops hosts/psi.yaml

# 키 교체 후 재암호화
sops updatekeys hosts/psi.yaml

자세한 내용은 비밀 관리를 참조합니다.

TLS 인증서

대부분의 도메인은 eta에서 Let's Encrypt ACME + Cloudflare DNS 챌린지로 인증서를 발급합니다. Nixbot 공개 ingress는 eta에서 인증서를 발급하고, psi의 Nixbot 스택도 내부 nginx용 인증서를 유지합니다. 다른 호스트에서 사용하는 인증서는 acme-sync 서비스가 rsync로 동기화하고, 대상 호스트의 systemd path unit이 파일 변경을 감지하여 nginx를 자동 리로드합니다.

도메인 발급 호스트 사용 호스트
auth.sjanglab.org eta eta
vault.sjanglab.org eta eta
hs.sjanglab.org eta eta
cloud.sjanglab.org eta tau (동기화)
n8n.sjanglab.org eta tau (동기화)
docling.sjanglab.org eta psi (동기화)
nixbot.sjanglab.org eta, psi eta (public edge), psi (service stack)
mq.sjanglab.org eta eta
upterm.sjanglab.org eta eta

인증서 동기화: eta에서 발급 → acme-sync 서비스가 rsync로 대상 호스트에 전송 → systemd path unit이 변경 감지 → nginx 자동 리로드. Nixbot은 eta 공개 edge와 psi 서비스 스택 양쪽에서 인증서를 발급합니다.

데이터 보안

PostgreSQL

설정 값
바인드 주소 wg-admin IP만
인증 방식 SCRAM-SHA-256 (원격), Peer (로컬)
복제 WAL 스트리밍 (rho → tau)
암호 관리 sops 암호화

RustFS S3 backup store

설정 값
API 바인드 wg-admin IP만 (:9100)
console 바인드 localhost only (127.0.0.1:9101)
저장소 /srv/rustfs/data
daemon 모듈 nixpkgs upstream services.rustfs
secret 전달 sops template EnvironmentFile
bucket state rustfs-bootstrap.service가 S3 API로 생성/검증
client 인증 access key / secret key

RustFS root credential은 daemon 시작, bootstrap, break-glass 용도로만 사용합니다. Backup job은 repo별 writer/pruner/reader/mirror credential과 IAM policy를 사용해야 합니다.

감사 및 모니터링

auditd

Linux 감사 데몬으로 다음을 추적합니다:

추적 대상 파일
PAM 세션 /var/log/wtmp, /var/log/btmp, /var/run/utmp
SSH 설정 변경 /etc/ssh/sshd_config

로그: /var/log/audit/audit.log (최대 8MB × 2)

로그 파이프라인

flowchart LR
  src["auditd / journald"] --> vec["Vector"]
  vec --> loki["Loki (rho)"]
  loki --> graf["Grafana"]

시스템 안정성

자동 업그레이드

설정 값
소스 github:SBEE-lab/infra
업그레이드 system.autoUpgrade가 매일 04:40 KST에 최신 설정 적용
재부팅 체크 매월 마지막 토요일 (auto-reboot 서비스)
재부팅 조건 커널 변경 시에만 24시간 후 자동 재부팅
재부팅 지터 최대 20분 지연 (호스트별 분산)

서비스 복구

systemd가 서비스 실패 시 자동 재시작합니다. 모니터링은 Gatus(status.sjanglab.org)에서 헬스체크를 수행합니다.