Compare commits
16 Commits
cdbfeb101d
...
ddc4b4e5a4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ddc4b4e5a4 | ||
|
|
0830a8d0a4 | ||
|
|
e83f6939e8 | ||
|
|
f5ae40c3e7 | ||
|
|
cd13e56e15 | ||
|
|
3339cd6b0c | ||
|
|
6315ac0143 | ||
|
|
b3bcfdcfcb | ||
|
|
2ace9cd2dd | ||
|
|
8735ef24d5 | ||
|
|
85a1127e1d | ||
|
|
9c26c962ff | ||
|
|
da2de12193 | ||
|
|
ff9a817ef8 | ||
|
|
ec501e3029 | ||
|
|
e1b093929c |
@@ -8,20 +8,28 @@ in
|
||||
modules = with inputs.self.modules; [
|
||||
nixos.lxc
|
||||
nixos.sops
|
||||
nixos.step-client
|
||||
nixos.step-ssh-host
|
||||
inputs.home-manager.nixosModules.home-manager
|
||||
nixos."${username}"
|
||||
nixos.zsh
|
||||
nixos.docker
|
||||
nixos.login-text
|
||||
{
|
||||
step-client.hostname = hostname;
|
||||
networking.hostName = hostname;
|
||||
step-ssh-host.hostname = hostname;
|
||||
|
||||
home-manager.users."${username}" = {
|
||||
imports = with inputs.self.modules.homeManager; [
|
||||
sops
|
||||
step-ssh-user
|
||||
];
|
||||
|
||||
shell.program = "zsh";
|
||||
docker.enable = true;
|
||||
step-ssh-user = {
|
||||
enable = true;
|
||||
principals = [ "${hostname}" ];
|
||||
};
|
||||
ssh.matchSets = {
|
||||
certs = true;
|
||||
homelab = true;
|
||||
|
||||
@@ -8,6 +8,11 @@ in
|
||||
flake.modules.homeManager."${hostname}" = { pkgs, config, ... }:
|
||||
let
|
||||
flakeDir = "${config.xdg.configHome}/home-manager/jsl-dendritic";
|
||||
certDir = "${config.home.homeDirectory}/.step/certs";
|
||||
CACert = "${certDir}/root_ca.crt";
|
||||
tlsKey = "${certDir}/key.pem";
|
||||
tlsCert = "${certDir}/cert.pem";
|
||||
mtlsCert = "${certDir}/mtls.pem";
|
||||
in
|
||||
{
|
||||
imports = with inputs.self.modules.homeManager; [
|
||||
@@ -17,6 +22,7 @@ in
|
||||
sops
|
||||
docker
|
||||
desktop
|
||||
step-ssh-user
|
||||
];
|
||||
targets.genericLinux.enable = true;
|
||||
|
||||
@@ -30,6 +36,20 @@ in
|
||||
(writeShellScriptBin "test-push" ''
|
||||
nixos-rebuild switch --flake ${flakeDir}#janus --target-host root@${testTarget}
|
||||
'')
|
||||
(writeShellScriptBin "mtls-generate" ''
|
||||
${lib.getExe pkgs.step-cli} ca certificate \
|
||||
john-pc-ubuntu ${tlsCert} ${tlsKey} \
|
||||
--provisioner admin \
|
||||
--san 192.168.1.85 \
|
||||
--san spiffe://john-stream.com/ubuntu
|
||||
cat ${tlsCert} ${tlsKey} > ${mtlsCert}
|
||||
'')
|
||||
(writeShellScriptBin "mtls-check" ''
|
||||
${lib.getExe pkgs.openssl} x509 \
|
||||
-noout -subject -issuer \
|
||||
-ext subjectAltName,extendedKeyUsage \
|
||||
-enddate -in ${mtlsCert}
|
||||
'')
|
||||
];
|
||||
# TODO: Add host-specific settings here:
|
||||
# - sops secret for `restic_password/john_ubuntu`
|
||||
@@ -42,12 +62,21 @@ in
|
||||
|
||||
homeManagerFlakeDir = flakeDir;
|
||||
docker.enable = true;
|
||||
ssh.matchSets = {
|
||||
|
||||
step-ssh-user = {
|
||||
enable = true;
|
||||
principals = ["root" "${username}" "appdaemon"];
|
||||
provisioner = "admin";
|
||||
};
|
||||
ssh = {
|
||||
certificates.enable = true;
|
||||
matchSets = {
|
||||
certs = true;
|
||||
appdaemon = true;
|
||||
homelab = true;
|
||||
dev = true;
|
||||
};
|
||||
};
|
||||
sops.secrets."restic_password/john_ubuntu" = {
|
||||
path = "${config.xdg.configHome}/resticprofile/password.txt";
|
||||
};
|
||||
@@ -57,8 +86,8 @@ in
|
||||
default = {
|
||||
"inherit" = "base";
|
||||
repository = "rest:https://soteria.john-stream.com/john-ubuntu";
|
||||
# cacert = "${config.home.homeDirectory}/.step/certs/root_ca.crt";
|
||||
# tls-client-cert = "${config.home.homeDirectory}/.step/certs/mtls.pem";
|
||||
cacert = "${CACert}";
|
||||
tls-client-cert = "${mtlsCert}";
|
||||
backup = {
|
||||
source = [
|
||||
"${config.xdg.userDirs.documents}"
|
||||
|
||||
@@ -8,7 +8,7 @@ in
|
||||
modules = with inputs.self.modules; [
|
||||
nixos.lxc
|
||||
nixos.sops
|
||||
nixos.step-client
|
||||
nixos.step-ssh-host
|
||||
inputs.home-manager.nixosModules.home-manager
|
||||
nixos."${username}"
|
||||
nixos.zsh
|
||||
|
||||
45
modules/nixos/login-text.nix
Normal file
45
modules/nixos/login-text.nix
Normal file
@@ -0,0 +1,45 @@
|
||||
{ inputs, ... }: {
|
||||
flake.modules.nixos.login-text = { config, ... }: {
|
||||
programs.rust-motd = {
|
||||
enable = true;
|
||||
refreshInterval = "*:0/5";
|
||||
order = [
|
||||
"global"
|
||||
"last_login"
|
||||
"service_status"
|
||||
"uptime"
|
||||
"memory"
|
||||
"filesystems"
|
||||
];
|
||||
settings = {
|
||||
global = {
|
||||
time_format = "%Y-%m-%d %H:%M:%S %Z";
|
||||
};
|
||||
|
||||
last_login = {
|
||||
john = 5;
|
||||
root = 3;
|
||||
};
|
||||
|
||||
service_status = {
|
||||
SSH = "sshd.socket";
|
||||
"SSH Certs" = "step-ssh-host-renew.timer";
|
||||
Docker = "docker";
|
||||
};
|
||||
|
||||
uptime = {
|
||||
prefix = "Uptime";
|
||||
};
|
||||
|
||||
memory = {
|
||||
swap_pos = "beside";
|
||||
};
|
||||
|
||||
filesystems = {
|
||||
root = "/";
|
||||
nix = "/nix/store";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -7,9 +7,9 @@ in
|
||||
#
|
||||
# NixOS Module
|
||||
#
|
||||
flake.modules.nixos.step-client = { config, pkgs, lib, ... }:
|
||||
flake.modules.nixos.step-ssh-host = { config, pkgs, lib, ... }:
|
||||
let
|
||||
cfg = config.step-client;
|
||||
cfg = config.step-ssh-host;
|
||||
stepBin = lib.getExe pkgs.step-cli;
|
||||
rootCertPath = "/etc/step/certs/root_ca.crt";
|
||||
provisionerPasswordPath = config.sops.secrets."janus/admin_jwk".path;
|
||||
@@ -18,7 +18,10 @@ in
|
||||
in
|
||||
{
|
||||
# NixOS Options
|
||||
options.step-client = {
|
||||
options.step-ssh-host = {
|
||||
hostname = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
};
|
||||
caURL = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "${caURL}";
|
||||
@@ -32,27 +35,21 @@ in
|
||||
type = lib.types.str;
|
||||
default = "admin";
|
||||
};
|
||||
hostname = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
};
|
||||
};
|
||||
|
||||
imports = with inputs.self.modules.nixos; [ ssh ];
|
||||
# NixOS Config
|
||||
config = {
|
||||
ssh.certificates.enable = true;
|
||||
home-manager.sharedModules = with inputs.self.modules; [
|
||||
homeManager.step-client
|
||||
];
|
||||
|
||||
sops.secrets."janus/admin_jwk" = {
|
||||
owner = "root";
|
||||
group = "root";
|
||||
mode = "0400";
|
||||
};
|
||||
networking.nameservers = [ "192.168.1.150" ];
|
||||
networking.dhcpcd.extraConfig = "nohook resolv.conf";
|
||||
|
||||
environment.etc."step/certs/root_ca.crt".source = cfg.rootCertFile;
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
step-cli
|
||||
(writeShellScriptBin "ssh-host-cert-renew" ''
|
||||
@@ -66,25 +63,69 @@ in
|
||||
--principal "${cfg.hostname}.john-stream.com" \
|
||||
"${cfg.hostname}" "${sshKeyPath}.pub"
|
||||
'')
|
||||
(writeShellScriptBin "ssh-host-cert-check" ''
|
||||
ssh-keygen -Lf ${sshCertPath}
|
||||
'')
|
||||
(writeShellScriptBin "ssh-host-cert-check" "${lib.getExe' pkgs.openssh "ssh-keygen"} -Lf ${sshCertPath}")
|
||||
];
|
||||
networking.nameservers = [ "192.168.1.150" ];
|
||||
networking.dhcpcd.extraConfig = "nohook resolv.conf";
|
||||
|
||||
systemd.services.step-ssh-host-renew = {
|
||||
description = "Renew Step SSH host certificate if needed";
|
||||
wantedBy = [ ];
|
||||
after = [ "network-online.target" ];
|
||||
wants = [ "network-online.target" ];
|
||||
path = [ pkgs.step-cli pkgs.openssh pkgs.coreutils pkgs.systemd ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
User = "root";
|
||||
Group = "root";
|
||||
};
|
||||
script = ''
|
||||
set -euo pipefail
|
||||
if ${stepBin} ssh needs-renewal "${sshCertPath}" --expires-in "4h"; then
|
||||
echo "Renewing SSH host certificate"
|
||||
else
|
||||
rc=$?
|
||||
if [ "$rc" -eq 1 ]; then
|
||||
echo "SSH host cert does not need renewal"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$rc" -eq 2 ]; then
|
||||
echo "SSH host cert missing: ${sshCertPath}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "step ssh needs-renewal failed with rc=$rc" >&2
|
||||
exit "$rc"
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
systemd.timers.step-ssh-host-renew = {
|
||||
description = "Periodic Step SSH host certificate renewal";
|
||||
wantedBy = [ "timers.target" ];
|
||||
|
||||
timerConfig = {
|
||||
OnBootSec = "5m";
|
||||
OnUnitActiveSec = "4h";
|
||||
RandomizedDelaySec = "15m";
|
||||
Persistent = true;
|
||||
Unit = "step-ssh-host-renew.service";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
#
|
||||
# Home Manager Module
|
||||
#
|
||||
flake.modules.homeManager.step-client = { config, pkgs, lib, ... }:
|
||||
flake.modules.homeManager.step-ssh-user = { config, pkgs, lib, ... }:
|
||||
let
|
||||
cfg = config.step-client;
|
||||
cfg = config.step-ssh-user;
|
||||
firstPrincipal = lib.head cfg.principals;
|
||||
principalArgs = lib.concatMapStringsSep " "
|
||||
(principal: "--principal \"${principal}\"") cfg.principals;
|
||||
in
|
||||
{
|
||||
options.step-client = {
|
||||
options.step-ssh-user = {
|
||||
enable = lib.mkEnableOption "opionated step client config for SSH certs";
|
||||
caURL = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
@@ -97,7 +138,7 @@ in
|
||||
rootCertFile = {
|
||||
path = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Path to where the root_ca.crt file will be stored for the user";
|
||||
description = "String path to where the root_ca.crt file will be stored for the user";
|
||||
default = "${config.home.homeDirectory}/.step/certs/root_ca.crt";
|
||||
};
|
||||
source = lib.mkOption {
|
||||
@@ -106,14 +147,33 @@ in
|
||||
default = ../../keys/root_ca.crt;
|
||||
};
|
||||
};
|
||||
provisioner = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "admin";
|
||||
};
|
||||
principals = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
# default = [ ];
|
||||
};
|
||||
};
|
||||
config = lib.mkIf cfg.enable {
|
||||
home.file.".step/certs/root_ca.crt".source = cfg.rootCertFile;
|
||||
home.file.".step/certs/root_ca.crt".source = cfg.rootCertFile.source;
|
||||
home.file.".step/config/defaults.json".text = builtins.toJSON {
|
||||
"ca-url" = cfg.caURL;
|
||||
fingerprint = cfg.fingerprint;
|
||||
root = "${cfg.rootCertFile.path}";
|
||||
};
|
||||
sops.secrets."janus/admin_jwk".mode = "0400";
|
||||
home.packages = with pkgs; [
|
||||
(writeShellScriptBin "sign-ssh-cert" ''
|
||||
${lib.getExe pkgs.step-cli} ssh certificate \
|
||||
--sign \
|
||||
${principalArgs} \
|
||||
--provisioner "${cfg.provisioner}" \
|
||||
--provisioner-password-file "${config.sops.secrets."janus/admin_jwk".path}" \
|
||||
"${firstPrincipal}" "${config.ssh.IdentityFile}.pub"
|
||||
'')
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ base:
|
||||
keep-hourly: '8'
|
||||
keep-daily: '14'
|
||||
keep-weekly: '8'
|
||||
keep-monthyl: '6'
|
||||
backup:
|
||||
verbose: true
|
||||
exclude:
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
sudo ${resticprofileBin} --config "${config.xdg.configHome}/resticprofile/profiles.yaml" $@
|
||||
'';
|
||||
rpbackupScript = pkgs.writeShellScriptBin "rp-backup" ''
|
||||
${lib.getExe rpScript} run-schedule backup@default
|
||||
${lib.getExe rpScript} run-schedule backup@default "$@"
|
||||
'';
|
||||
in {
|
||||
programs.resticprofile.package = resticprofilePackage;
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
{inputs, ... }:
|
||||
let
|
||||
userName = "john";
|
||||
sshHostCAPubKey = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNug18oLH0vZxnibXJzMJvTWFPZTnSlhCDDVi+rHhgnIum6ZXQ4SF+VHOOAM5BbzZmMKitNJ5lcrGP15Eur7DzQ=";
|
||||
in
|
||||
{
|
||||
flake.modules.nixos.ssh = { pkgs, config, lib, ... }:
|
||||
let
|
||||
cfg = config.ssh;
|
||||
userCAPath = "ssh/ssh_user_ca.pub";
|
||||
hostKeyFile = "ssh/ssh_host_ed25519_key";
|
||||
in
|
||||
{
|
||||
options.ssh = {
|
||||
certificates = {
|
||||
enable = lib.mkEnableOption "Enable SSH certificates";
|
||||
enable = lib.mkEnableOption "Enable SSH host certificates";
|
||||
userCA = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
default = ../../keys/ssh_user_ca.pub;
|
||||
@@ -26,23 +29,27 @@ in
|
||||
{
|
||||
PasswordAuthentication = false;
|
||||
KbdInteractiveAuthentication = false;
|
||||
HostKey = "/etc/${hostKeyFile}";
|
||||
}
|
||||
(lib.mkIf cfg.certificates.enable {
|
||||
TrustedUserCAKeys = "/etc/ssh/ssh_user_ca.pub";
|
||||
HostKey = "/etc/ssh/ssh_host_ed25519_key";
|
||||
HostCertificate = "/etc/ssh/ssh_host_ed25519_key-cert.pub";
|
||||
TrustedUserCAKeys = "/etc/${userCAPath}";
|
||||
HostCertificate = "/etc/${hostKeyFile}-cert.pub";
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
environment.etc."ssh/ssh_user_ca.pub" = lib.mkIf cfg.certificates.enable {
|
||||
environment.etc."${userCAPath}" = lib.mkIf cfg.certificates.enable {
|
||||
source = cfg.certificates.userCA;
|
||||
};
|
||||
|
||||
programs.ssh.knownHosts = lib.mkIf cfg.certificates.enable {
|
||||
"192.168.1.*" = {
|
||||
certAuthority = true;
|
||||
publicKey = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNug18oLH0vZxnibXJzMJvTWFPZTnSlhCDDVi+rHhgnIum6ZXQ4SF+VHOOAM5BbzZmMKitNJ5lcrGP15Eur7DzQ=";
|
||||
publicKey = sshHostCAPubKey;
|
||||
};
|
||||
"*.john-stream.com" = {
|
||||
certAuthority = true;
|
||||
publicKey = sshHostCAPubKey;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -58,6 +65,19 @@ in
|
||||
description = "Path to the SSH identity file.";
|
||||
};
|
||||
|
||||
certificates = {
|
||||
enable = lib.mkEnableOption "Enable SSH user certificates";
|
||||
# sshCertProvisioner = lib.mkOption {
|
||||
# type = lib.types.str;
|
||||
# default = "admin";
|
||||
# };
|
||||
};
|
||||
|
||||
knownHostsFile = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "${config.home.homeDirectory}/.ssh/known_hosts";
|
||||
};
|
||||
|
||||
matchSets = {
|
||||
appdaemon = lib.mkEnableOption "Enable AppDaemon SSH targets";
|
||||
certs = lib.mkEnableOption "Enable Janus and Soteria SSH targets";
|
||||
@@ -68,23 +88,21 @@ in
|
||||
|
||||
# All this stuff has to be wrapped in a config attribute because of the presence of the options here?
|
||||
config = let
|
||||
identityFile = config.ssh.IdentityFile;
|
||||
cfg = config.ssh;
|
||||
identityFile = cfg.IdentityFile;
|
||||
publicKeyFile = "${identityFile}.pub";
|
||||
certificateFile = "${identityFile}-cert.pub";
|
||||
userKnownHostsFile = "${config.home.homeDirectory}/.ssh/known_hosts";
|
||||
provisionerPasswordPath = config.sops.secrets."janus/admin_jwk".path;
|
||||
in {
|
||||
home.packages = [
|
||||
(pkgs.writeShellScriptBin "sign-ssh-cert" ''
|
||||
echo "Signing ${publicKeyFile}"
|
||||
echo "Copy the Step-CA JWK Provisioner password from 1password"
|
||||
step ssh certificate --sign \
|
||||
--principal root \
|
||||
--principal ${userName} \
|
||||
--principal appdaemon \
|
||||
--provisioner admin \
|
||||
${userName} ${publicKeyFile}
|
||||
'')
|
||||
];
|
||||
home.file.".ssh/known_hosts" = {
|
||||
text = lib.concatStringsSep "\n" (
|
||||
[
|
||||
"fded:fb16:653e:25da:be24:11ff:fea0:753f ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ9ZqiWPrCwHjxFCiu0lT4rlQs7KyMapxKJQQ5PJP1eh"
|
||||
]
|
||||
++ (lib.optional cfg.certificates.enable "@cert-authority 192.168.1.* ${sshHostCAPubKey}")
|
||||
++ (lib.optional cfg.certificates.enable "@cert-authority *.john-stream.com ${sshHostCAPubKey}")
|
||||
);
|
||||
};
|
||||
|
||||
programs.ssh = {
|
||||
enable = true;
|
||||
@@ -107,13 +125,13 @@ in
|
||||
inherit identityFile certificateFile;
|
||||
|
||||
hashKnownHosts = false;
|
||||
userKnownHostsFile = "${userKnownHostsFile}";
|
||||
userKnownHostsFile = cfg.knownHostsFile;
|
||||
|
||||
addKeysToAgent = "yes";
|
||||
forwardAgent = false;
|
||||
};
|
||||
}
|
||||
(lib.mkIf config.ssh.matchSets.appdaemon {
|
||||
(lib.mkIf cfg.matchSets.appdaemon {
|
||||
"appdaemon" = {
|
||||
hostname = "192.168.1.242";
|
||||
user = "appdaemon";
|
||||
@@ -123,7 +141,7 @@ in
|
||||
user = "appdaemon";
|
||||
};
|
||||
})
|
||||
(lib.mkIf config.ssh.matchSets.certs {
|
||||
(lib.mkIf cfg.matchSets.certs {
|
||||
"janus" = {
|
||||
hostname = "janus.john-stream.com";
|
||||
user = "root";
|
||||
@@ -133,7 +151,7 @@ in
|
||||
user = "john";
|
||||
};
|
||||
})
|
||||
(lib.mkIf config.ssh.matchSets.homelab {
|
||||
(lib.mkIf cfg.matchSets.homelab {
|
||||
"docs" = {
|
||||
hostname = "192.168.1.110";
|
||||
user = "root";
|
||||
@@ -151,7 +169,7 @@ in
|
||||
user = "panoptes";
|
||||
};
|
||||
})
|
||||
(lib.mkIf config.ssh.matchSets.dev {
|
||||
(lib.mkIf cfg.matchSets.dev {
|
||||
"test-nix" = {
|
||||
hostname = "fded:fb16:653e:25da:be24:11ff:fea0:753f";
|
||||
user = "john";
|
||||
|
||||
@@ -12,7 +12,7 @@ in
|
||||
keygrip = [
|
||||
];
|
||||
authorizedKeys = [
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIAUa4dcg1TWc4pW++uodyhX4eOqrX/QYIxFWtEP7HFJ john@john-pc-ubuntu"
|
||||
# "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIAUa4dcg1TWc4pW++uodyhX4eOqrX/QYIxFWtEP7HFJ john@john-pc-ubuntu"
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMOkGLo4N/L3RYvaIZ1FmePlxa1HK0fMciZxKtRhN58F root@janus"
|
||||
];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user