NixOS와 비밀값
3 hours ago
1
- NixOS에서 비밀값을 Nix 설정, 비공개 Git 저장소, git-crypt에 평문으로 두면 Nix store가 누구나 읽을 수 있어 머신 접근자가 비밀값을 읽을 수 있음
- sops-nix는 .sops.yaml 규칙과 sops 편집 흐름으로 비밀값 파일을 암호화하고, 활성화 시 호스트 SSH 키로 복호화해 /run/secrets/<name>의 tmpfs에 평문을 둠
- agenix는 secrets.nix에서 비밀값별 수신자 공개키를 지정하고 .age 파일을 /run/agenix/<name>의 tmpfs에 복호화하며, 새 호스트나 키 교체 때 재키잉이 필요함
- 파일시스템에 비밀값을 직접 두는 방식은 단일 서버나 노트북에는 가능하지만, builtins.readFile "/var/lib/myservice/token"처럼 평가 시점에 읽으면 값이 Nix store에 들어가므로 피해야 함
- 처음 시작하고 독립적인 토큰 몇 개라면 agenix가 절차가 적고, 메일 서버처럼 관련 비밀값이 많은 호스트라면 여러 값을 한 파일에 묶는 sops-nix가 더 잘 맞음
NixOS에서 비밀값을 다룰 때의 기본 위험
- NixOS에서 비밀값 관리는 sops-nix, agenix/ragenix, 파일시스템 활용, 비공개 Git 저장소, git-crypt, Nix 설정에 직접 작성하는 방식으로 나뉨
- 비공개 Git 저장소, git-crypt, Nix 설정 직접 작성 방식은 머신을 공유하거나 설정을 공개할 계획이 있으면 쓰면 안 됨
- 공개된 설정에서 비밀값이 최소 두 번 유출된 적이 있으며, 예시는 1, 2에 남아 있음
sops-nix
- sops-nix는 처음 접할 때 설정이 어렵게 느껴질 수 있지만, 문서가 크게 개선됐고 sops가 SSH 키로 비밀값 암복호화를 기본 지원하게 된 점이 큰 개선임
- 다만 sops-nix는 이 SSH 키 지원에서 아직 뒤처져 있으며 sops-nix#779, sops-nix#922에서 관련 작업이 진행 중
- 사용 흐름은 .sops.yaml에 암복호화 규칙을 만들고, sops 명령으로 비밀값 파일을 편집하는 방식
- 예시는 secrets/*.yaml 경로에 대해 age 수신자로 SSH 공개키를 지정하는 형태
- sops secrets/shush.yaml을 실행하면 선택한 편집기가 열리고, hello: sops 같은 YAML 값을 작성할 수 있음
- 편집기를 종료하면 값은 ENC[AES256_GCM,...] 형태로 암호화되고, 같은 명령으로 다시 편집 가능함
- NixOS 설정에서는 sops-nix 모듈이 대부분의 작업을 처리함
- defaultSopsFile = ./secrets/shush.yaml;로 기본 비밀값 파일을 지정함
- age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];로 호스트 SSH 키를 지정함
- secrets."hello"에 owner, group, mode를 지정해 파일 권한을 설정함
- 활성화 시점에 sops-nix가 호스트의 SSH 키로 파일을 복호화하고 평문을 /run/secrets/<name>에 둠
- 이 경로는 tmpfs이므로 비밀값이 디스크에 닿지 않음
- 값을 필요로 하는 서비스는 해당 경로를 읽으면 됨
- 템플릿 기능은 공유 설정이나 다른 사용자가 참조하는 설정에서 유용함
- 서비스 설정 파일이 평문과 일부 비밀값을 함께 요구할 때 전체 파일을 암호화하지 않아도 됨
- 예를 들어 SMTP_USER=isabel은 평문으로 두고, SMTP_PASSWORD=${config.sops.placeholder."mailserver/smtp_password"}처럼 비밀값 자리표시자를 넣을 수 있음
agenix
- agenix는 sops-nix와 달리 secrets.nix에서 비밀값과 접근 가능한 키를 설정하므로 Nix에 더 가까운 사용감을 줌
- secrets.nix에는 사용자와 호스트의 SSH 공개키를 바인딩하고, 각 .age 파일에 어떤 공개키들이 접근 가능한지 지정함
- 예를 들어 "secret1.age".publicKeys = [ isabel host1 ];, "secret2.age".publicKeys = [ isabel host2 ];처럼 비밀값마다 수신자 목록을 다르게 둘 수 있음
- 비밀값 파일은 agenix CLI로 만들어야 함
- agenix -e secret1.age 명령으로 secret1.age를 생성하거나 나중에 다시 편집할 수 있음
- 호스트 설정에 연결하는 방식은 sops-nix와 비슷하지만, 각 비밀값이 별도 파일이므로 표면적이 더 작음
- age.secrets.secret1.file = ./secrets/secret1.age;로 파일을 지정함
- owner, group, mode로 소유자와 권한을 지정함
- 부팅 시 호스트의 SSH 키로 각 .age 파일을 복호화해 /run/agenix/<name>에 둠
- 가장 자주 걸리는 부분은 재키잉(rekeying) 임
- 새 호스트를 추가하거나 키를 교체하면, secrets.nix에서 publicKeys 목록이 바뀐 모든 비밀값을 다시 암호화해야 함
- agenix --rekey가 이를 처리하지만, 기존 암호문을 읽기 위해 현재 수신자 중 하나의 개인키가 필요함
- 실제로는 새로 올리려는 호스트가 아니라 가장 신뢰하는 머신에서 재키잉이 이뤄짐
파일시스템만 사용하는 방식
- 파일시스템에 비밀값을 직접 두는 방식은 설정이 더 이상 머신을 완전히 설명하지 못한다는 비용이 있음
- 재설치 시 모든 파일을 올바른 위치와 소유권으로 다시 넣어야 함
- 새벽 2시에 서버를 복구하는 상황처럼 복구 작업에서는 큰 재앙이 될 수 있음
- 피해야 할 패턴은 builtins.readFile "/var/lib/myservice/token" 같은 형태
- 이 방식은 평가 시점에 파일을 읽고 내용을 Nix store에 포함함
- Nix store는 누구나 읽을 수 있으므로, 처음 경고한 실패 방식과 정확히 같아짐
- 대신 서비스에는 값 자체가 아니라 경로를 넘겨야 함
- 단일 서버나 노트북에서는 괜찮을 수 있지만, 플릿이거나 설정만으로 처음부터 재구축하고 싶은 환경이라면 sops-nix나 agenix가 더 적합함
- 머신마다 진짜 저장소에 넣으면 안 되는 값이 한두 개 있을 때는 괜찮지만, 나중에 다시 넣어야 할 책임이 미래의 자신에게 생김
sops-nix와 agenix 비교
- sops-nix를 선택하는 주요 이유는 가능한 많은 데이터를 하나의 파일에 묶을 수 있다는 점
- 메일 서버처럼 관련 비밀값이 많은 경우, agenix처럼 5개 안팎의 파일로 나누지 않고 한 파일에 더 많은 비밀값을 둘 수 있음
- 강력한 도구로 계속 쓰기에 적합하며, 메일 서버처럼 비밀값이 매우 많은 호스트에서 먼저 선택할 만함
- agenix는 단순성에서 강점이 있음
- 배워야 할 YAML 스키마가 없음
- 동기화해야 할 .sops.yaml이 없음
- secrets.nix가 그냥 Nix이므로 호스트와 사용자에 쓰던 let ... in 바인딩을 키에도 그대로 쓸 수 있음
- 사고 모델은 “비밀값 하나, 파일 하나, 수신자 목록 하나”이며 접근 제어 방식과 잘 맞음
- 움직이는 부품이 가장 적은 편이면서도 호스트별 키 접근 제어를 제공하므로 NixOS 머신의 비밀값 관리를 처음 묻는 사람에게 추천하기 좋은 선택지임
- 두 도구 모두 문제를 해결하며, 현재 차이는 대부분 사용성에 가까움
- 처음 시작하고 여러 서비스가 관련 비밀값 묶음을 요구한다면 sops-nix가 더 잘 확장됨
- 처음 시작하고 대부분 독립적인 토큰 몇 개만 있다면 agenix가 더 적은 절차로 목적지에 도달함
- 첫 비밀값 도구를 고른다면 agenix로 흐름에 익숙해지고, “비밀값 하나당 파일 하나” 방식의 불편함을 실제로 느낄 때 sops-nix로 넘어가는 편이 좋음
- 양자 내성 관련 내용은 정정됨
- age와 sops는 양자 내성 보안 암호화 키를 지원함
- age#578이 닫히고 v1.3.0이 릴리스됨
- age 키를 만들 때 -pq를 포함하면 되며, 예시는 age-keygen -pq -o key.txt임
-
Homepage
-
Tech blog
- NixOS와 비밀값