Skip to content
Open
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

!/flake.nix
!/flake.lock
!/.sops.yaml

!shells/
!hosts/
Expand All @@ -17,5 +18,6 @@
!modules/
!docs/
!images/
!secrets/

!/flake/
10 changes: 10 additions & 0 deletions .sops.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Replace the sample age recipient with each host's real public key before encrypting secrets.
# Generate or install the host key with docs/installer/iso.md and inspect the public key with:
# age-keygen -y /var/lib/sops-nix/key.txt
keys:
- &replace_with_host_age_key age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
creation_rules:
- path_regex: secrets/.*\.ya?ml$
key_groups:
- age:
- *replace_with_host_age_key
72 changes: 72 additions & 0 deletions docs/installer/iso.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Installer ISO

この flake は SSH 経由でのインストールを基本にした `installer-iso` NixOS 構成を公開します。直接接続したディスプレイとキーボードもフォールバックとして使えます。

## ビルド

```sh
nix build .#nixosConfigurations.installer-iso.config.system.build.isoImage
```

ISO は `result/iso/` 以下に生成されます。

## 起動とネットワーク

基本は有線 Ethernet + DHCP です。WiFi は NetworkManager でフォールバックとして使えます。

```sh
nmcli radio wifi on
nmcli device wifi list
nmcli device wifi connect '<ssid>' --ask
```

## SSH-first workflow

OpenSSH は ISO 起動時に有効です。`moons` ユーザーが作成され、リポジトリで管理している authorized keys でログインできます。

別マシンから次のように入ります。

```sh
ssh moons@<installer-ip>
```

IP アドレスが分からない場合は、フォールバックコンソールで次を確認します。

```sh
ip addr
```

## 初回用の sops-nix age 鍵を作る

独自 helper は使わず、`age-keygen` と `install` だけでインストール対象に鍵を作ります。インストール対象の root filesystem を `/mnt` にマウントした後に実行してください。

```sh
sudo install -d -m 0700 /mnt/var/lib/sops-nix
sudo age-keygen -o /mnt/var/lib/sops-nix/key.txt
sudo chmod 0600 /mnt/var/lib/sops-nix/key.txt
```

公開 recipient は次で確認します。

```sh
sudo age-keygen -y /mnt/var/lib/sops-nix/key.txt
```

この recipient を `.sops.yaml` に追加してから secret を暗号化・更新してください。

既存の age identity を使い回す場合は、標準の `install` でコピーします。

```sh
sudo install -d -m 0700 /mnt/var/lib/sops-nix
sudo install -m 0600 ./key.txt /mnt/var/lib/sops-nix/key.txt
```

## インストール後も使われる鍵

`/mnt/var/lib/sops-nix/key.txt` に作った age identity は、そのままインストール後の `/var/lib/sops-nix/key.txt` になります。つまり ISO 上で生成した sops-nix 鍵を、インストール後の通常起動でも継続利用できます。

OpenSSH の host key は NixOS の OpenSSH module が `/etc/ssh/` 以下へ自動生成します。`moons` の SSH client key は Home Manager の user systemd service が `~/.ssh/id_ed25519` が無い場合に初回ログイン後へ自動生成します。

## フォールバックローカルコンソール

SSH が使えない場合は、直接接続したディスプレイとキーボードで作業します。ISO には `neovim`、`tmux`、`git`、`sops`、`age`、`ssh-to-age`、`disko`、OpenSSH tools、NetworkManager tools が入っています。
10 changes: 10 additions & 0 deletions docs/installer/recommendations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Installer recommendations

追加で入れる・運用するとよいもの:

- 有線 Ethernet を標準手順にする。WiFi credential や firmware 問題を避けやすいです。
- 再インストール前に `/var/lib/sops-nix/key.txt` のバックアップをオフライン保管する。
- ホストごとの Disko recipe を用意して、パーティション作成も再現可能にする。
- `moons` の SSH client public key はマシンごとに登録する。秘密鍵を他マシンへコピーしない方針にできます。
- リモート地のインストールが必要になったら ISO へ Tailscale を追加する。ローカル作業だけなら authorized keys 付き SSH の方が単純です。
- ホストごとに disk 名、NIC 名、WiFi chipset、Secure Boot 状態、TPM/FIDO availability の確認 checklist を作る。
78 changes: 78 additions & 0 deletions docs/secrets/sops-nix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# sops-nix secrets

このリポジトリでは、`sops-nix` は全 NixOS 構成で常に有効です。復号できるかどうかは Nix の feature ではなく、各 SOPS ファイルがどの age recipient に暗号化されているかで制御します。

## 方針

- sops-nix の NixOS module と `sops.age.generateKey` を使い、独自の鍵生成 shell helper は持ちません。
- 全ホストの sops-nix age identity は `/var/lib/sops-nix/key.txt` に置きます。
- `my.system.secrets.sops.generateKey` のデフォルトは `true` なので、鍵が無い場合は sops-nix 側で生成されます。
- 初回ログインの hashed password など、初回起動時から secret が必要なホストでは、インストーラー ISO 上で先に `/mnt/var/lib/sops-nix/key.txt` を作ってから `nixos-install` します。

## ホスト age 鍵

通常起動後の公開 recipient は次のコマンドで確認します。

```sh
sudo age-keygen -y /var/lib/sops-nix/key.txt
```

インストール対象を `/mnt` にマウントしている間は次のコマンドです。

```sh
sudo age-keygen -y /mnt/var/lib/sops-nix/key.txt
```

## 権限モデル

- `sops-nix` 自体は全環境で有効です。
- 復号権限は `.sops.yaml` と各暗号化ファイルの age recipient で管理します。
- あるホストに読ませたい secret は、そのホストの public age recipient を入れて `sops updatekeys` します。
- 読ませたくないホストの recipient は入れません。

## moons のログインパスワードを SOPS から使う

`moons` ユーザーの hashed password を SOPS から使う場合は、対象ホストや profile で次のように設定します。

```nix
my.features.sops = {
defaultSopsFile = ../../secrets/users/moons.yaml;
userPassword.enable = true;
};
```

期待する secret key は次の形です。

```yaml
users:
moons:
hashedPassword: "$y$j9T$..."
```

ハッシュは次のコマンドで作ります。

```sh
mkpasswd -m yescrypt
```

暗号化ファイルの編集は次のコマンドです。

```sh
sops secrets/users/moons.yaml
```

## SSH 鍵との関係

- OpenSSH server の host key は、NixOS の `services.openssh.hostKeys` の標準挙動で `/etc/ssh/` 以下へ自動生成されます。
- `moons` の通常の SSH client key は、`my.features.identity.sshDefaultKey.enable` が有効な profile で user systemd service が初回ログイン後に `~/.ssh/id_ed25519` として自動生成します。
- sops-nix の age 鍵は SSH 鍵とは別に `/var/lib/sops-nix/key.txt` へ sops-nix の `generateKey` で自動生成します。

## SSH host key を sops-nix recipient に使う場合

sops-nix の README では、SSH host key から age recipient を作る方法も紹介されています。必要になった場合は次のように public key を age recipient に変換できます。

```sh
ssh-to-age < /etc/ssh/ssh_host_ed25519_key.pub
```

ただし、初回起動のユーザーパスワード復号に使う場合は、復号前に host key が存在している必要があります。このリポジトリでは初回インストール手順を単純にするため、sops-nix 専用の age key file を標準にしています。
21 changes: 21 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@
inputs.nixpkgs.follows = "nixpkgs";
};

# Secrets
sops-nix = {
url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs";
};

# Boot
lanzaboote = {
url = "github:nix-community/lanzaboote";
Expand Down
57 changes: 45 additions & 12 deletions hosts/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,35 @@ let
};
};

mkNixos =
{
host,
system,
modules ? [ ],
}:
assert lib.assertMsg (lib.elem system config.systems)
"mkNixos: system '${system}' not in valid systems: ${lib.generators.toPretty { } config.systems}";
nixosSystem {
inherit system modules;
specialArgs = {
inherit
inputs
username
unstable
host
;
};
};

mkSystem =
{
host,
system,
profiles ? [ ],
extraModules ? [ ],
}:
assert lib.assertMsg (lib.elem system config.systems)
"mkSystem: system '${system}' not in valid systems: ${lib.generators.toPretty { } config.systems}";
nixosSystem {
inherit system;
mkNixos {
inherit host system;
modules = [
{
nixpkgs.config.allowUnfree = true;
Expand All @@ -37,14 +55,25 @@ let
]
++ map (p: ../profiles/${p}.nix) profiles
++ extraModules;
specialArgs = {
inherit
inputs
username
unstable
host
;
};
};

mkInstallerIso =
{
system,
extraModules ? [ ],
}:
mkNixos {
host = "installer-iso";
inherit system;
modules = [
{
nixpkgs.config.allowUnfree = true;
}
"${inputs.nixpkgs}/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix"
../modules
./installer-iso/default.nix
]
++ extraModules;
};
in
{
Expand All @@ -59,6 +88,10 @@ in
"workloads/remote"
];
};
installer-iso = mkInstallerIso {
system = "x86_64-linux";
};

x1g13 = mkSystem {
host = "x1g13";
system = "x86_64-linux";
Expand Down
65 changes: 65 additions & 0 deletions hosts/installer-iso/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
lib,
pkgs,
username,
...
}:
{
isoImage = {
isoName = lib.mkDefault "moons-nixos-installer.iso";
appendToMenuLabel = " moons installer";
};

networking = {
hostName = "nixos-installer";
useDHCP = lib.mkDefault true;
networkmanager = {
enable = true;
wifi.backend = "wpa_supplicant";
wifi.powersave = false;
};
wireless.iwd.enable = false;
};

services.openssh = {
enable = true;
openFirewall = true;
settings = {
PermitRootLogin = "no";
PasswordAuthentication = false;
KbdInteractiveAuthentication = false;
PubkeyAuthentication = "yes";
};
};

users.users.${username} = {
isNormalUser = true;
extraGroups = [
"networkmanager"
"wheel"
];
};

security.sudo.wheelNeedsPassword = false;

environment.systemPackages = with pkgs; [
age # File encryption tool used with sops-nix age keys
disko # Declarative disk partitioning helper
git # Version control client for fetching dotfiles
neovim # Text editor for fallback console installation
networkmanager # Network management CLI and daemon for Ethernet and WiFi
openssh # Standard SSH client, server, and ssh-keygen tools
sops # Editor and CLI for SOPS encrypted secrets
ssh-to-age # Convert SSH public keys to age recipients
tmux # Terminal multiplexer for resilient SSH sessions
];

programs.zsh.enable = true;

documentation = {
enable = true;
nixos.enable = true;
};

system.stateVersion = "26.05";
}
1 change: 1 addition & 0 deletions modules/features/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
./gui
./identity
./network
./sops
./services
./storage
];
Expand Down
Loading