Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 04bbf00c3d | |||
| e0abbd6b90 |
@@ -61,6 +61,9 @@ in
|
|||||||
};
|
};
|
||||||
ssh = {
|
ssh = {
|
||||||
certificates.enable = true;
|
certificates.enable = true;
|
||||||
|
knownHosts = [
|
||||||
|
"fded:fb16:653e:25da:be24:11ff:fea0:753f ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ9ZqiWPrCwHjxFCiu0lT4rlQs7KyMapxKJQQ5PJP1eh"
|
||||||
|
];
|
||||||
matchSets = {
|
matchSets = {
|
||||||
certs = true;
|
certs = true;
|
||||||
appdaemon = true;
|
appdaemon = true;
|
||||||
|
|||||||
@@ -1,16 +1,10 @@
|
|||||||
{ self, inputs, ... }: {
|
{ self, inputs, ... }: {
|
||||||
flake.modules.homeManager.onepassword = { config, pkgs, lib, ... }: {
|
flake.modules.homeManager.onepassword = { config, ... }: {
|
||||||
home.file.".config/1Password/ssh/agent.toml".text = ''
|
home.file.".config/1Password/ssh/agent.toml".text = ''
|
||||||
# https://developer.1password.com/docs/ssh/agent/config
|
# https://developer.1password.com/docs/ssh/agent/config
|
||||||
[[ssh-keys]]
|
[[ssh-keys]]
|
||||||
vault = "Private"
|
vault = "Private"
|
||||||
'';
|
'';
|
||||||
programs.ssh = {
|
programs.ssh.matchBlocks."*".identityAgent = "${config.home.homeDirectory}/.1password/agent.sock";
|
||||||
enable = true;
|
|
||||||
extraConfig = ''
|
|
||||||
Host *
|
|
||||||
IdentityAgent ${config.home.homeDirectory}/.1password/agent.sock
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,8 +64,8 @@ in
|
|||||||
(writeShellScriptBin "gen-age-key" ''
|
(writeShellScriptBin "gen-age-key" ''
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
if [ ! -f "${config.ssh.IdentityFile}" ]; then
|
if [ ! -f "${config.ssh.identityFile}" ]; then
|
||||||
${echo} "SSH identity file not found: ${config.ssh.IdentityFile}" >&2
|
${echo} "SSH identity file not found: ${config.ssh.identityFile}" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ in
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
${mkdir} -p "$(${dirname} "${cfg.ageKeyFile}")"
|
${mkdir} -p "$(${dirname} "${cfg.ageKeyFile}")"
|
||||||
${lib.getExe pkgs.ssh-to-age} -i ${config.ssh.IdentityFile} -private-key > ${cfg.ageKeyFile}
|
${lib.getExe pkgs.ssh-to-age} -i ${config.ssh.identityFile} -private-key > ${cfg.ageKeyFile}
|
||||||
${echo} -n "Created ${cfg.ageKeyFile}: "
|
${echo} -n "Created ${cfg.ageKeyFile}: "
|
||||||
${echo} $(${lib.getExe show-age-key})
|
${echo} $(${lib.getExe show-age-key})
|
||||||
'')
|
'')
|
||||||
@@ -90,7 +90,7 @@ in
|
|||||||
sops = {
|
sops = {
|
||||||
defaultSopsFile = sopsSecretsPath;
|
defaultSopsFile = sopsSecretsPath;
|
||||||
defaultSopsFormat = "yaml";
|
defaultSopsFormat = "yaml";
|
||||||
age.sshKeyPaths = [ "${config.ssh.IdentityFile}" ];
|
age.sshKeyPaths = [ "${config.ssh.identityFile}" ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
${principalArgs} \
|
${principalArgs} \
|
||||||
--provisioner "${cfg.provisioner}" \
|
--provisioner "${cfg.provisioner}" \
|
||||||
--provisioner-password-file "${config.sops.secrets."janus/admin_jwk".path}" \
|
--provisioner-password-file "${config.sops.secrets."janus/admin_jwk".path}" \
|
||||||
"${firstPrincipal}" "${config.ssh.IdentityFile}.pub"
|
"${firstPrincipal}" "${config.ssh.identityFile}.pub"
|
||||||
'')
|
'')
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|||||||
+47
-43
@@ -4,37 +4,35 @@ let
|
|||||||
sshHostCAPubKey = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNug18oLH0vZxnibXJzMJvTWFPZTnSlhCDDVi+rHhgnIum6ZXQ4SF+VHOOAM5BbzZmMKitNJ5lcrGP15Eur7DzQ=";
|
sshHostCAPubKey = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNug18oLH0vZxnibXJzMJvTWFPZTnSlhCDDVi+rHhgnIum6ZXQ4SF+VHOOAM5BbzZmMKitNJ5lcrGP15Eur7DzQ=";
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
flake.modules.nixos.ssh = { pkgs, config, lib, ... }:
|
flake.modules.nixos.ssh = { config, pkgs, lib, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.ssh;
|
cfg = config.ssh;
|
||||||
userCAPath = "ssh/ssh_user_ca.pub";
|
configDir = "/etc/ssh";
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.ssh = {
|
options.ssh = {
|
||||||
configDir = lib.mkOption {
|
|
||||||
description = "String path to the host SSH config directory";
|
|
||||||
type = lib.types.str;
|
|
||||||
default = "/etc/ssh";
|
|
||||||
};
|
|
||||||
hostKey = lib.mkOption {
|
hostKey = lib.mkOption {
|
||||||
description = "String path to the host private key file";
|
description = "String path to the host private key file";
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
default = "${cfg.configDir}/ssh_host_ed25519_key";
|
default = "ssh_host_ed25519_key";
|
||||||
};
|
};
|
||||||
certificates = {
|
certificates = {
|
||||||
enable = lib.mkEnableOption "Enable SSH host certificates";
|
enable = lib.mkEnableOption "Enable SSH host certificates";
|
||||||
userCA = lib.mkOption {
|
userCA = lib.mkOption {
|
||||||
|
description = "Content for the SSH user CA file (public key)";
|
||||||
type = lib.types.path;
|
type = lib.types.path;
|
||||||
default = ../../keys/ssh_user_ca.pub;
|
default = ../hosts/janus/ssh_user_ca.pub;
|
||||||
};
|
};
|
||||||
userCAPath = lib.mkOption {
|
userCAFile = lib.mkOption {
|
||||||
|
description = "String path to the SSh user CA";
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
default = "${cfg.configDir}/ssh_user_ca.pub";
|
default = "ssh_user_ca.pub";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
|
cfg.certificates = lib.mkDefault true;
|
||||||
services.openssh = {
|
services.openssh = {
|
||||||
enable = true;
|
enable = true;
|
||||||
# require public key authentication for better security
|
# require public key authentication for better security
|
||||||
@@ -42,16 +40,16 @@ in
|
|||||||
{
|
{
|
||||||
PasswordAuthentication = false;
|
PasswordAuthentication = false;
|
||||||
KbdInteractiveAuthentication = false;
|
KbdInteractiveAuthentication = false;
|
||||||
HostKey = cfg.hostKey;
|
HostKey = "${configDir}/${cfg.hostKey}";
|
||||||
}
|
}
|
||||||
(lib.mkIf cfg.certificates.enable {
|
(lib.mkIf cfg.certificates.enable {
|
||||||
TrustedUserCAKeys = cfg.certificates.userCAPath;
|
TrustedUserCAKeys = "${configDir}/${cfg.certificates.userCAFile}";
|
||||||
HostCertificate = "${cfg.hostKey}-cert.pub";
|
HostCertificate = "${configDir}/${cfg.hostKey}-cert.pub";
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.etc."${userCAPath}" = lib.mkIf cfg.certificates.enable {
|
environment.etc."ssh/${cfg.certificates.userCAFile}" = lib.mkIf cfg.certificates.enable {
|
||||||
source = cfg.certificates.userCA;
|
source = cfg.certificates.userCA;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -68,52 +66,56 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
flake.modules.homeManager.ssh = { pkgs, config, lib, ... }:
|
flake.modules.homeManager.ssh = { config, pkgs, lib, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.ssh;
|
||||||
|
configDir = "${config.home.homeDirectory}/.ssh";
|
||||||
|
identityFile = cfg.identityFile;
|
||||||
|
publicKeyFile = "${identityFile}.pub";
|
||||||
|
certificateFile = "${identityFile}-cert.pub";
|
||||||
|
in
|
||||||
{
|
{
|
||||||
options.ssh = {
|
options.ssh = with lib; {
|
||||||
IdentityFile = lib.mkOption {
|
identityFile = mkOption {
|
||||||
# Intentionally not using a path type here because that will end up with the private key getting copied into the store
|
# Intentionally not using a path type here because that will end up with the private key getting copied into the store
|
||||||
type = lib.types.str;
|
type = types.str;
|
||||||
default = "${config.home.homeDirectory}/.ssh/id_ed25519";
|
default = "${config.home.homeDirectory}/.ssh/id_ed25519";
|
||||||
description = "Path to the SSH identity file.";
|
description = "Path to the SSH identity file.";
|
||||||
};
|
};
|
||||||
|
|
||||||
certificates = {
|
certificates = {
|
||||||
enable = lib.mkEnableOption "Enable SSH user certificates";
|
enable = mkEnableOption "Enable SSH client certificates";
|
||||||
# sshCertProvisioner = lib.mkOption {
|
|
||||||
# type = lib.types.str;
|
|
||||||
# default = "admin";
|
|
||||||
# };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
knownHostsFile = lib.mkOption {
|
knownHostsFile = mkOption {
|
||||||
type = lib.types.str;
|
type = types.str;
|
||||||
default = "${config.home.homeDirectory}/.ssh/known_hosts";
|
default = "${configDir}/known_hosts";
|
||||||
|
};
|
||||||
|
|
||||||
|
knownHosts = mkOption {
|
||||||
|
description = "";
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [ ];
|
||||||
};
|
};
|
||||||
|
|
||||||
matchSets = {
|
matchSets = {
|
||||||
appdaemon = lib.mkEnableOption "Enable AppDaemon SSH targets";
|
appdaemon = mkEnableOption "Enable AppDaemon SSH targets";
|
||||||
certs = lib.mkEnableOption "Enable Janus and Soteria SSH targets";
|
certs = mkEnableOption "Enable Janus and Soteria SSH targets";
|
||||||
homelab = lib.mkEnableOption "Enable various Homelab targets";
|
homelab = mkEnableOption "Enable various Homelab targets";
|
||||||
dev = lib.mkEnableOption "Enable development targets";
|
dev = mkEnableOption "Enable development targets";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# All this stuff has to be wrapped in a config attribute because of the presence of the options here?
|
# All this stuff has to be wrapped in a config attribute because of the presence of the options here?
|
||||||
config = let
|
config = let
|
||||||
cfg = config.ssh;
|
|
||||||
identityFile = cfg.IdentityFile;
|
|
||||||
publicKeyFile = "${identityFile}.pub";
|
|
||||||
certificateFile = "${identityFile}-cert.pub";
|
|
||||||
provisionerPasswordPath = config.sops.secrets."janus/admin_jwk".path;
|
provisionerPasswordPath = config.sops.secrets."janus/admin_jwk".path;
|
||||||
in {
|
in {
|
||||||
home.file.".ssh/known_hosts" = {
|
home.file.".ssh/known_hosts" = {
|
||||||
text = lib.concatStringsSep "\n" (
|
text = lib.concatStringsSep "\n" (
|
||||||
[
|
cfg.knownHosts ++ lib.optionals cfg.certificates.enable [
|
||||||
"fded:fb16:653e:25da:be24:11ff:fea0:753f ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ9ZqiWPrCwHjxFCiu0lT4rlQs7KyMapxKJQQ5PJP1eh"
|
"@cert-authority 192.168.1.* ${sshHostCAPubKey}"
|
||||||
|
"@cert-authority *.john-stream.com ${sshHostCAPubKey}"
|
||||||
]
|
]
|
||||||
++ (lib.optional cfg.certificates.enable "@cert-authority 192.168.1.* ${sshHostCAPubKey}")
|
|
||||||
++ (lib.optional cfg.certificates.enable "@cert-authority *.john-stream.com ${sshHostCAPubKey}")
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -122,12 +124,12 @@ in
|
|||||||
enableDefaultConfig = false;
|
enableDefaultConfig = false;
|
||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
SetEnv TERM="xterm-256color"
|
SetEnv TERM="xterm-256color"
|
||||||
IdentityAgent ~/.1password/agent.sock
|
|
||||||
'';
|
'';
|
||||||
|
|
||||||
matchBlocks = lib.mkMerge [
|
matchBlocks = lib.mkMerge [
|
||||||
{
|
{
|
||||||
"*" = {
|
"*" = lib.mkMerge [
|
||||||
|
{
|
||||||
user = "john";
|
user = "john";
|
||||||
|
|
||||||
compression = false;
|
compression = false;
|
||||||
@@ -135,14 +137,16 @@ in
|
|||||||
serverAliveCountMax = 3;
|
serverAliveCountMax = 3;
|
||||||
|
|
||||||
identitiesOnly = true;
|
identitiesOnly = true;
|
||||||
inherit identityFile certificateFile;
|
inherit identityFile;
|
||||||
|
|
||||||
hashKnownHosts = false;
|
hashKnownHosts = false;
|
||||||
userKnownHostsFile = cfg.knownHostsFile;
|
userKnownHostsFile = cfg.knownHostsFile;
|
||||||
|
|
||||||
addKeysToAgent = "yes";
|
addKeysToAgent = "yes";
|
||||||
forwardAgent = false;
|
forwardAgent = false;
|
||||||
};
|
}
|
||||||
|
(lib.mkIf cfg.certificates.enable { inherit certificateFile; })
|
||||||
|
];
|
||||||
}
|
}
|
||||||
(lib.mkIf cfg.matchSets.appdaemon {
|
(lib.mkIf cfg.matchSets.appdaemon {
|
||||||
"appdaemon" = {
|
"appdaemon" = {
|
||||||
|
|||||||
Reference in New Issue
Block a user