Skip to content

Commit aa5ba82

Browse files
committed
use sops file to store secrets
1 parent 02322d0 commit aa5ba82

File tree

8 files changed

+290
-91
lines changed

8 files changed

+290
-91
lines changed

flake.nix

+102
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,108 @@
7373
done
7474
'';
7575

76+
gen-sopsconfig-file = pkgs.writeShellScriptBin "gen-sopsconfig-file" ''
77+
while getopts "hp:s:" o; do
78+
case "''${o}" in
79+
h)
80+
usage
81+
exit 0
82+
;;
83+
p)
84+
pubhostkey=''${OPTARG}
85+
;;
86+
s)
87+
sopskey=''${OPTARG}
88+
;;
89+
*)
90+
usage
91+
exit 1
92+
;;
93+
esac
94+
done
95+
shift $((OPTIND-1))
96+
97+
if [ -z "$sopskey" ]; then
98+
echo "Please pass sops key with -k argument."
99+
exit 1
100+
fi
101+
102+
if [ -z "$pubhostkey" ]; then
103+
echo "Please pass sops key with -o argument."
104+
exit 1
105+
fi
106+
107+
me_age_key="$(${pkgs.age}/bin/age-keygen -y $sopskey)"
108+
host_age_key="$(cat $pubhostkey | ${pkgs.ssh-to-age}/bin/ssh-to-age)"
109+
110+
cat <<SOPS > .sops.yaml
111+
keys:
112+
# To obtain the age key for &me, run:
113+
# nix shell .#age --command age-keygen -y $sopskey
114+
- &me $me_age_key
115+
# To obtain the age key for &server, run:
116+
# nix shell .#age --command age-keygen -y $pubhostkey
117+
- &server $host_age_key
118+
creation_rules:
119+
- path_regex: secrets\.yaml$
120+
key_groups:
121+
- age:
122+
- *me
123+
- *server
124+
SOPS
125+
'';
126+
127+
sops = pkgs.writeShellScriptBin "edit-secrets" ''
128+
while getopts ":hs:" o; do
129+
case "''${o}" in
130+
h)
131+
usage
132+
exit 0
133+
;;
134+
s)
135+
sopskey=''${OPTARG}
136+
;;
137+
*)
138+
;;
139+
esac
140+
done
141+
shift $((OPTIND-1))
142+
143+
export SOPS_AGE_KEY_FILE=$sopskey
144+
${pkgs.sops}/bin/sops $*
145+
'';
146+
147+
sops-yq-edit = pkgs.writeShellScriptBin "edit-secrets" ''
148+
while getopts ":hf:s:t:" o; do
149+
case "''${o}" in
150+
h)
151+
usage
152+
exit 0
153+
;;
154+
f)
155+
file=''${OPTARG}
156+
;;
157+
s)
158+
sopskey=''${OPTARG}
159+
;;
160+
t)
161+
transformation=''${OPTARG}
162+
;;
163+
*)
164+
;;
165+
esac
166+
done
167+
shift $((OPTIND-1))
168+
169+
set -euo pipefail
170+
171+
export SOPS_AGE_KEY_FILE="$sopskey"
172+
${pkgs.sops}/bin/sops encrypt --filename-override "$file" --output "$file.dup" <( \
173+
${pkgs.sops}/bin/sops decrypt "$file" \
174+
| ${pkgs.yq-go}/bin/yq "$transformation"
175+
) \
176+
&& mv "$file.dup" "$file"
177+
'';
76178

77179
# Install a nixosConfigurations instance (<flake>) on a server.
78180
#

modules/configuration.nix

+3-4
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,9 @@ in
1818
description = "Name given to the admin user on the server.";
1919
};
2020

21-
initialHashedPassword = mkOption {
21+
hashedPasswordFile = mkOption {
2222
type = types.str;
23-
default = "$y$j9T$7EZvmryvlpTHSRG7dC5IU1$lBc/nePnkvqZ//jNpx/UpFKze/p6P7AIhJubK/Ghj68";
24-
description = "Initial password for the admin user. Can be changed later. Default is 'skarabox123'.";
23+
description = "Contains password for the admin user.";
2524
};
2625

2726
facter-config = lib.mkOption {
@@ -110,7 +109,7 @@ in
110109
users.users.${cfg.username} = {
111110
isNormalUser = true;
112111
extraGroups = [ "wheel" ];
113-
inherit (cfg) initialHashedPassword;
112+
inherit (cfg) hashedPasswordFile;
114113
openssh.authorizedKeys.keyFiles = [ cfg.sshAuthorizedKeyFile ];
115114
};
116115

modules/initialgen.nix

+18-11
Original file line numberDiff line numberDiff line change
@@ -93,25 +93,32 @@ USAGE
9393
e "Generating server host key in ./host_key and ./host_key.pub..."
9494
rm host_key && ${nix} shell ${../.}#openssh --command ssh-keygen -t ed25519 -N "" -f host_key && chmod 600 host_key
9595
96+
e "Generating sops key ./sops.key..."
97+
rm sops.key && ${nix} shell ${../.}#age --command age-keygen -o sops.key
98+
e "Generating sops config ./.sops.yaml..."
99+
${nix} run ${../.}#gen-sopsconfig-file -- -s sops.key -p host_key.pub
100+
101+
e "Generating sops secrets file ./secrets.yaml..."
102+
touch secrets.yaml
103+
${nix} run ${../.}#sops -- -s sops.key encrypt -i secrets.yaml
104+
96105
e "Generating ssh key in ./ssh_skarabox and ./ssh_skarabox.pub..."
97106
rm ssh_skarabox && ${nix} shell ${../.}#openssh --command ssh-keygen -t ed25519 -N "" -f ssh_skarabox && chmod 600 ssh_skarabox
98107
99-
e "Generating initial password for user in ./initialHashedPassword..."
100-
${nix} run ${../.}#mkpasswd -- $mkpasswdargs > initialHashedPassword
108+
e "Generating initial password for user in secrets.yaml under user/hashedPassword"
109+
${nix} run ${../.}#sops-yq-edit -- -s sops.key -f secrets.yaml \
110+
-t ".user.hashedPassword = \"$(${nix} run ${../.}#mkpasswd -- $mkpasswdargs)\""
101111
102112
e "Generating hostid in ./hostid..."
103113
${nix} shell ${../.}#util-linux --command uuidgen | head -c 8 > hostid
104114
105-
e "Generating root pool passphrase in ./root_passphrase..."
106-
chmod 600 root_passphrase
107-
${nix} run ${../.}#openssl -- rand -hex 64 > root_passphrase
115+
e "Generating root pool passphrase in secrets.yaml under skarabox/disks/rootPassphrase"
116+
${nix} run ${../.}#sops-yq-edit -- -s sops.key -f secrets.yaml \
117+
-t ".skarabox.disks.rootPassphrase = \"$(${nix} run ${../.}#openssl -- rand -hex 64)\""
108118
109-
e "Generating data pool passphrase in ./data_passphrase..."
110-
chmod 600 data_passphrase
111-
${nix} run ${../.}#openssl -- rand -hex 64 > data_passphrase
112-
113-
e "Generating sops key ./sops.key..."
114-
rm sops.key && ${nix} shell ${../.}#age --command age-keygen -o sops.key
119+
e "Generating data pool passphrase in secrets.yaml under skarabox/disks/dataPassphrase"
120+
${nix} run ${../.}#sops-yq-edit -- -s sops.key -f secrets.yaml \
121+
-t ".skarabox.disks.dataPassphrase = \"$(${nix} run ${../.}#openssl -- rand -hex 64)\""
115122
116123
e "You will need to fill out the ./ip, ./known_hosts and ./system file"
117124
e "and adjust the ssh_port and ssh_boot_port if you want to."

modules/installonbeacon.nix

+132-59
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,133 @@
11
{ pkgs, nixos-anywhere }:
2-
pkgs.writeShellScriptBin "install-on-beacon" ''
3-
usage () {
4-
cat <<USAGE
5-
Usage: $0 -i IP -p PORT -f FLAKE -k HOST_KEY -r ROOT_PASSPHRASE_FILE -d DATA_PASSPHRASE_FILE [-a EXTRA_OPTS]
6-
7-
-h: Shows this usage
8-
-i IP: IP of the target host running the beacon.
9-
-p PORT: Port of the target host running the beacon.
10-
-f FLAKE: Flake to install on the target host.
11-
-k HOST_KEY_FILE: SSH key to use as the host identification key.
12-
-r ROOT_PASSPHRASE_FILE: File containing the root passphrase used to encrypt the root ZFS pool.
13-
-d DATA_PASSPHRASE_FILE: File containing the data passphrase used to encrypt the data ZFS pool.
14-
-a EXTRA_OPTS: Extra options to pass verbatim to nixos-anywhere.
15-
USAGE
16-
}
17-
while getopts "hi:p:f:k:r:d:a:" o; do
18-
case "''${o}" in
19-
h)
20-
usage
21-
exit 0
22-
;;
23-
i)
24-
ip=''${OPTARG}
25-
;;
26-
p)
27-
port=''${OPTARG}
28-
;;
29-
f)
30-
flake=''${OPTARG}
31-
;;
32-
k)
33-
host_key_file=''${OPTARG}
34-
;;
35-
r)
36-
root_passphrase_file=''${OPTARG}
37-
;;
38-
d)
39-
data_passphrase_file=''${OPTARG}
40-
;;
41-
a)
42-
extra_opts=''${OPTARG}
43-
;;
44-
*)
45-
usage
46-
exit 1
47-
;;
48-
esac
49-
done
50-
shift $((OPTIND-1))
51-
52-
${nixos-anywhere}/bin/nixos-anywhere \
53-
--flake $flake \
54-
--disk-encryption-keys /tmp/host_key $host_key_file \
55-
--disk-encryption-keys /tmp/root_passphrase $root_passphrase_file \
56-
--disk-encryption-keys /tmp/data_passphrase $data_passphrase_file \
57-
--ssh-port $port \
58-
skarabox@$ip \
59-
$extra_opts
60-
''
2+
let
3+
# We use a separate script here because sops run script
4+
# with /bin/sh and this way we can override the shell easily.
5+
#
6+
# Other methods of doing that exist but this way also helps with
7+
# making escaping variables easier. And it runs shellcheck.
8+
innerScript = pkgs.writeShellApplication {
9+
name = "install-on-beacon";
10+
11+
runtimeInputs = [
12+
pkgs.yq
13+
];
14+
15+
text = ''
16+
fd="$1"
17+
ip="$2"
18+
port="$3"
19+
flake="$4"
20+
host_key_file="$5"
21+
root_passphrase_path="$6"
22+
data_passphrase_path="$7"
23+
extra_opts="$8"
24+
25+
# We read the fd once and store it because
26+
# we can't read it twice.
27+
secrets="$(cat "$fd")"
28+
root_passphrase=$(echo "$secrets" | yq -r "$root_passphrase_path")
29+
data_passphrase=$(echo "$secrets" | yq -r "$data_passphrase_path")
30+
31+
nixos-anywhere \
32+
--flake "$flake" \
33+
--disk-encryption-keys /tmp/host_key "$host_key_file" \
34+
--disk-encryption-keys /tmp/root_passphrase <(echo "$root_passphrase") \
35+
--disk-encryption-keys /tmp/data_passphrase <(echo "$data_passphrase") \
36+
--ssh-port "$port" \
37+
skarabox@"$ip" \
38+
"$extra_opts"
39+
'';
40+
};
41+
in
42+
pkgs.writeShellApplication {
43+
name = "install-on-beacon";
44+
45+
runtimeInputs = [
46+
nixos-anywhere
47+
pkgs.bash
48+
pkgs.sops
49+
pkgs.yq
50+
];
51+
52+
text = ''
53+
usage () {
54+
cat <<USAGE
55+
Usage: $0 -i IP -p PORT -f FLAKE -k HOST_KEY -r ROOT_PASSPHRASE_FILE -d DATA_PASSPHRASE_FILE [-a EXTRA_OPTS]
56+
57+
-h: Shows this usage
58+
-i IP: IP of the target host running the beacon.
59+
-p PORT: Port of the target host running the beacon.
60+
-f FLAKE: Flake to install on the target host.
61+
-k HOST_KEY_FILE: SSH key to use as the host identification key.
62+
-r ROOT_PASSPHRASE_PATH: Path in the yaml secrets file for the root passphrase used to encrypt the root ZFS pool.
63+
-d DATA_PASSPHRASE_PATH: Path in the yaml secrets file for the data passphrase used to encrypt the root ZFS pool.
64+
-s SOPS_KEY: File containing a sops key capable of decrypting the secrets.
65+
-a EXTRA_OPTS: Extra options to pass verbatim to nixos-anywhere.
66+
USAGE
67+
}
68+
69+
check_empty () {
70+
if [ -z "$1" ]; then
71+
echo "$3 must not be empty, pass with flag $2"
72+
fi
73+
}
74+
75+
while getopts "hi:p:f:k:r:d:s:a:" o; do
76+
case "''${o}" in
77+
h)
78+
usage
79+
exit 0
80+
;;
81+
i)
82+
ip=''${OPTARG}
83+
;;
84+
p)
85+
port=''${OPTARG}
86+
;;
87+
f)
88+
flake=''${OPTARG}
89+
;;
90+
k)
91+
host_key_file=''${OPTARG}
92+
;;
93+
r)
94+
root_passphrase_path=''${OPTARG}
95+
;;
96+
d)
97+
data_passphrase_path=''${OPTARG}
98+
;;
99+
s)
100+
sopskey=''${OPTARG}
101+
;;
102+
a)
103+
extra_opts=''${OPTARG}
104+
;;
105+
*)
106+
usage
107+
exit 1
108+
;;
109+
esac
110+
done
111+
shift $((OPTIND-1))
112+
113+
check_empty "$ip" -i ip
114+
check_empty "$port" -p port
115+
check_empty "$flake" -f flake
116+
check_empty "$host_key_file" -k host_key_file
117+
check_empty "$root_passphrase_path" -r root_passphrase_path
118+
check_empty "$data_passphrase_path" -d data_passphrase_path
119+
check_empty "$sopskey" -s sopskey
120+
121+
SOPS_AGE_KEY_FILE=$sopskey sops exec-file secrets.yaml "
122+
bash \
123+
${innerScript}/bin/install-on-beacon {} \
124+
$ip \
125+
$port \
126+
$flake \
127+
$host_key_file \
128+
$root_passphrase_path \
129+
$data_passphrase_path \
130+
$extra_opts
131+
"
132+
'';
133+
}

template/configuration.nix

+6-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# More info at:
44
# - https://wiki.nixos.org/wiki/NixOS_modules
55
# - https://nixos.org/manual/nixos/stable/#sec-writing-modules
6-
{ lib, ... }:
6+
{ lib, config, ... }:
77
let
88
inherit (lib) mkMerge;
99
in
@@ -19,7 +19,7 @@ in
1919
{
2020
skarabox.hostname = "skarabox";
2121
skarabox.username = "skarabox";
22-
skarabox.initialHashedPassword = lib.trim (builtins.readFile ./initialHashedPassword);
22+
skarabox.hashedPasswordFile = config.sops.secrets."user/hashedPassword".path;
2323
skarabox.facter-config = ./facter.json;
2424
skarabox.disks.rootDisk = "/dev/nvme0n1"; # Update with result of running `fdisk -l` on the USB stick.
2525
skarabox.disks.rootDisk2 = null; # Set a value only if you have a second disk for the root partition.
@@ -52,6 +52,10 @@ in
5252
sops.age = {
5353
sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
5454
};
55+
56+
sops.secrets."user/hashedPassword" = {};
57+
sops.secrets."skarabox/disks/rootPassphrase" = {};
58+
sops.secrets."skarabox/disks/dataPassphrase" = {};
5559
}
5660
# Your config
5761
{

0 commit comments

Comments
 (0)