sops-nix 비밀을 NixOS와 home-manager로, git-safe 방식으로
Source: Dev.to
NixOS 설정(또는 모든 dotfiles)을 버전 관리하면 전체 시스템이 재현 가능해집니다.
하지만 그 파일들에 포함된 API 키나 비밀번호는 평문으로 저장소에 보관됩니다.
- 단순히
.gitignore로 무시할 수 없습니다 – NixOS는 빌드/활성화 시점에 값이 필요합니다. - 환경 변수로 노출하고 싶지도 않습니다.
해결책: sops‑nix 를 사용하세요 – SOPS 로 비밀을 암호화하고 암호화된 파일을 커밋합니다.
활성화 시 sops‑nix 가 해당 파일을 복호화하는데, 복호화 키는 오직 여러분의 머신에만 존재하고(저장소에는 절대 없음)
dotfiles는 완전히 재현 가능하고, 푸시할 수 있으며, 유출 위험이 없습니다.
개요
- 관리해야 할 첫 번째 비밀:
EXA_API_KEY(Exa.ai용). - 이 가이드는 SSH 키에서 파생된 age 키를 사용한 단일 머신 설정을 보여줍니다 (Michael Stapelberg의 접근 방식을 기반으로 수정함).
- 선택 사항: 빌드 경고를 없애기 위해 자신을 신뢰된 사용자로 추가합니다.
# configuration.nix
nix.settings.trusted-users = [ "@wheel" "noor" ];
한 번 재빌드:
sudo nixos-rebuild switch
Source: …
1️⃣ Age 키 스토어 준비하기
# sops가 age 키를 찾을 디렉터리를 생성합니다
mkdir -p ~/.config/sops/age/
필요한 도구가 포함된 임시 Nix 쉘에 들어갑니다:
nix shell nixpkgs#ssh-to-age nixpkgs#age
1.1 SSH 개인 키로 age 신원 만들기
# SSH 키에 암호가 설정돼 있다면, 입력하라는 프롬프트가 표시됩니다.
ssh-to-age -private-key -i ~/.ssh/id_ed25519 -o ~/.config/sops/age/keys.txt
권한을 잠그세요 (강력히 권장):
chmod 600 ~/.config/sops/age/keys.txt
1.2 개인용 age 수신자 얻기
age-keygen -y ~/.config/sops/age/keys.txt
# → 예시와 같은 값이 출력됩니다: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxx
출력된 값을 복사해 두세요 – 나중에 필요합니다.
1.3 시스템 SSH 호스트 키가 존재하는지 확인하기
ls /etc/ssh/ssh_host_ed25519_key # 존재해야 합니다
존재하지 않는 경우, 모든 호스트 키를 생성합니다:
sudo ssh-keygen -A
1.4 시스템 age 수신자 얻기 (호스트 키에서 파생)
cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age
# → 예시와 같은 값이 출력됩니다: age1yyyyyyyyyyyyyyyyyyyyyyyyyyyy
이 값도 복사해 두세요.
임시 쉘을 종료합니다:
exit
이제 두 개의 수신자가 준비되었습니다:
| 수신자 | 값 (예시) |
|---|---|
admin (개인) | age1xxxxx… |
system (호스트) | age1yyyyy… |
2️⃣ .sops.yaml 설정 만들기
dotfiles 저장소 루트에 파일을 생성합니다 (예: ~/nixos-dotfiles/.sops.yaml):
# .sops.yaml
keys:
- &admin age1xxxxx... # Your personal age recipient
- &system age1yyyyy... # Your system's SSH host key
creation_rules:
- path_regex: secrets/[^/]+\.yaml$
key_groups:
- age:
- *admin
- *system
이 설정이 하는 일
- 개인 키 (
&admin) – 일반 사용자로서 비밀을 복호화/편집할 수 있게 해줍니다 (sudo없이, SSH 암호를 반복 입력할 필요 없음). - 시스템 키 (
&system) – 부팅 시 시스템이 자동으로 비밀을 복호화하고/run/secrets/아래에 노출합니다.
⚠️ 주의: SSH 호스트 키 (
/etc/ssh/ssh_host_ed25519_key)를 회전/재생성하면 새로운 호스트 수신자를 사용해 모든 비밀을 다시 암호화해야 합니다. 그렇지 않으면 부팅 시 복호화에 실패합니다.
3️⃣ 첫 번째 비밀을 만들고 암호화하기
cd ~/nixos-dotfiles
mkdir -p secrets
SOPS로 파일을 엽니다 (nix run 래퍼가 올바른 버전을 가져옵니다):
nix run nixpkgs#sops -- secrets/secrets.yaml
편집기가 열리면 비밀을 추가합니다:
EXA_API_KEY: "your_actual_api_key_here"
저장하고 종료합니다. 이제 파일이 암호화되었습니다.
암호화를 확인합니다:
cat secrets/secrets.yaml
# → ENC[AES256_GCM,...] 로 시작하는 값을 볼 수 있습니다
오직 당신만(~/.config/sops/age/keys.txt 파일을 사용) 이 파일을 복호화할 수 있습니다.
나중에 편집/보기 위해서는:
nix run nixpkgs#sops -- secrets/secrets.yaml
4️⃣ sops‑nix를 NixOS 설정에 연결하기
4.1 sops‑nix를 Flake 입력으로 추가하기
# flake.nix
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
sops-nix = {
url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, home-manager, sops-nix }:
let
system = "x86_64-linux";
in {
nixosConfigurations.nixos = nixpkgs.lib.nixosSystem {
inherit system;
modules = [
sops-nix.nixosModules.sops # **Important:** `sops-nix.homeManagerModules.sops` must be imported so the `sops` options are visible inside `home.nix`.
];
};
};
}
4.2 home.nix에서 비밀 설정하기
# home.nix
{ config, ... }:
{
# Tell sops‑nix where your age private key lives
sops.age.keyFile = "${config.home.homeDirectory}/.config/sops/age/keys.txt";
# Default SOPS file for this user
sops.defaultSopsFile = ./secrets/secrets.yaml;
# Declare the secret we want to expose
sops.secrets.EXA_API_KEY = { };
# Export a session variable that points to the decrypted file
home.sessionVariables = {
EXA_API_KEY = "${config.sops.secrets.EXA_API_KEY.path}";
};
}
얻을 수 있는 것
- 로그인 시
EXA_API_KEY는/run/user/<uid>/secrets/EXA_API_KEY아래의 임시 파일을 가리킵니다. - 해당 파일에는 원시 키 값이 들어 있습니다.
- 쉘에서 읽으려면:
cat "$EXA_API_KEY".
Note:
sops‑nix는 비밀을 파일로 저장하고, 원시 환경 변수로 저장하지 않습니다. 파일 경로에서 비밀을 읽을 수 있는 프로그램은 바로 사용할 수 있습니다.
5️⃣ 비밀 사용하기
# 셸에서
echo "My key is: $(cat "$EXA_API_KEY")"
프로그램이 원시 값을 환경 변수에 직접 넣어야 할 경우, 다음과 같이 래핑할 수 있습니다:
export EXA_API_KEY=$(cat "$EXA_API_KEY")
my-program --api-key "$EXA_API_KEY"
요약
| 단계 | 수행한 작업 |
|---|---|
| 1️⃣ | SSH 키에서 age 신원을 생성하고 개인 및 시스템 수신자를 모두 수집했습니다. |
| 2️⃣ | .sops.yaml 파일을 생성하여 SOPS에 secrets/ 아래 파일을 암호화/복호화할 수 있는 수신자를 지정했습니다. |
| 3️⃣ | EXA_API_KEY를 secrets/secrets.yaml에 추가하고 SOPS로 암호화했습니다. |
| 4️⃣ | sops‑nix를 플레이크에 추가하고, NixOS와 Home Manager 모두에 모듈을 활성화했으며, 키 파일과 비밀 파일을 지정했습니다. |
| 5️⃣ | 복호화된 비밀 파일을 가리키는 세션 변수를 내보내어 사용 준비를 마쳤습니다. |
이제 NixOS 설정(또는 모든 dotfiles 저장소)을 공개 또는 공유 Git 원격에 안전하게 푸시할 수 있으며 비밀이 절대 유출되지 않습니다. 🎉
Source: …
sops‑nix 로 API 키 추가하기
1. 왜 추가 단계가 필요할까?
API_KEY는 파일 경로가 아닙니다. 파일을 기대하는 프로그램을 동작시키려면 두 가지 방법이 있습니다:
- 프로그램을 파일을 읽도록 설정합니다 (
*_FILE환경 변수를 지원하는 프로그램이 많습니다). - 프로그램을 래핑/실행해서 파일 내용을 환경 변수에 넣어줍니다.
재빌드 후에는 기존 터미널 창을 모두 닫으세요 – 열려 있는 쉘은 새로운 변수들을 인식하지 못합니다. 새 터미널을 열어 업데이트된 환경을 확인합니다.
2. 시스템‑레벨 sops 설정
~/nixos-dotfiles/configuration.nix에 다음을 추가합니다 (NixOS 모듈에 필요한 외부 { … }: 래퍼에 주의).
{ ... }:
{
sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
}
무엇을 하는가
- NixOS
sops모듈이 복호화에 사용할 시스템‑레벨 SSH 호스트 키를 선언합니다. configuration.nix(NixOS 레벨)에 있어야 하며,home.nix에서는 안 됩니다.- Home‑Manager
sops모듈은 자체sops.age.keyFile(Step 4에서 설정)을 사용합니다. - 부팅 시 사용자 개입 없이 복호화를 가능하게 하고, 향후 시스템‑레벨 비밀을 사용할 수 있게 합니다.
3. 재빌드 및 적용
sudo nixos-rebuild switch --flake ~/nixos-dotfiles#nixos
재빌드 후 선택 사항
| 옵션 | 명령 | 비고 |
|---|---|---|
| A – 재부팅 (가장 간단) | sudo reboot | 모든 서비스가 새로운 env‑var를 인식하도록 보장합니다. |
| B – 서비스 재시작 | bash\nsystemctl --user daemon-reload\nsystemctl --user restart sops-nix.service\n | 이전에 서비스가 활성화되지 않았다면 동작하지 않을 수 있습니다 (home‑manager 이슈). |
4. 비밀 확인
새 터미널을 열고 실행합니다:
echo $EXA_API_KEY
# → /run/user/1000/secrets/EXA_API_KEY 와 같은 경로가 출력돼야 합니다
cat $EXA_API_KEY
# → 실제 API‑key 값이 출력됩니다
5. 암호화된 비밀 파일 편집
cd ~/nixos-dotfiles
nix run nixpkgs#sops -- secrets/secrets.yaml
수정 후 저장, 커밋, 재빌드합니다.
git add flake.nix configuration.nix home.nix .sops.yaml secrets/secrets.yaml
git commit -m "Add EXA_API_KEY secret with sops-nix"
git push
중요:
- 개인 age 비밀키(
~/.config/sops/age/keys.txt)는 절대 커밋되지 않으며, 레포 외부에 보관됩니다.- 암호화된 파일(
secrets/secrets.yaml)은 비밀키 없이는 사용할 수 없으므로 안전하게 커밋할 수 있습니다.
6. 우리가 한 일 (요약)
- 기존 SSH 호스트 키(
ssh-to-age)를 이용해 age 키를 생성하고~/.config/sops/age/keys.txt에 저장(추적되지 않음). - 두 수신자를 지정한
.sops.yaml을 설정: 편집용 개인 age 키와 부팅 시 복호화를 위한 시스템 SSH 호스트 키. sops를 사용해 암호화된 비밀 파일(secrets/secrets.yaml)을 생성.- 플레이크에
sops‑nix를 연결:configuration.nix의 NixOS 모듈과home.nix의 Home‑Manager 모듈. - 비밀을 환경 변수
$EXA_API_KEY로 노출했으며, 이는/run/user/1000/secrets/EXA_API_KEY에 복호화된 파일을 가리킵니다.
암호화된 파일은 안전하게 푸시할 수 있으며, 비밀키는 절대로 머신을 떠나지 않습니다. (다음 글에서는 YAML 파일을 이용해 Kubernetes 클러스터에서 sops를 활용하는 방법을 다룰 예정입니다.)