db88a44435
Refactor the SSH configuration generation to reduce duplication and improve efficiency. - Extract `IdentityFile` line generation into a shared variable in `ssh-inventory.nix` to ensure consistency between per-host and gitea blocks. - Optimize `selectedHosts` resolution in `home-manager/default.nix` by reusing `activeHosts` when the "all" wildcard is used. - Tighten IPv4 regex matching in `ssh-inventory.nix` using anchors. - Update `users-merge.nix` to propagate `extraIdentities` to the rbw outbound configuration. - Set default `authorizedHosts` to "all" for inbound SSH users.
160 lines
6.3 KiB
Nix
160 lines
6.3 KiB
Nix
{ lib, ... }: {
|
|
flake.lib.sshInventory =
|
|
let
|
|
# Hosts: pure network endpoints. No public keys live here anymore —
|
|
# keys are per USER (see `users` below), one key per person.
|
|
hosts = {
|
|
"14900k" = {
|
|
hostName = "192.168.2.25";
|
|
aliases = [ "14900k" "nixdesk" ];
|
|
};
|
|
ideapad = {
|
|
hostName = "192.168.2.229";
|
|
aliases = [ "ideapad" ];
|
|
};
|
|
t2mbp = {
|
|
hostName = "192.168.2.15";
|
|
aliases = [ "t2mbp" ];
|
|
};
|
|
"uConsole" = {
|
|
hostName = "192.168.2.99";
|
|
aliases = [ "uConsole" "uconsole" ];
|
|
};
|
|
# Scratchpad / recovery target — hostName isn't an IP, so `activeHosts`
|
|
# excludes it from navi deployments. Leave it in `hosts` for inventory
|
|
# visibility while recovery is in progress.
|
|
test = {
|
|
hostName = "test";
|
|
aliases = [ "test" ];
|
|
};
|
|
"nix-server" = {
|
|
hostName = "192.168.2.238";
|
|
aliases = [ "nix-server" ];
|
|
};
|
|
r5500 = {
|
|
hostName = "192.168.2.100";
|
|
aliases = [ "r5500" ];
|
|
};
|
|
};
|
|
|
|
# Per-user public keys. One keypair per person, generated once with
|
|
# `ssh-keygen -t ed25519 -C <comment>` (or `rbw gen ssh-key <user>`),
|
|
# private key stored in Bitwarden under the same name. Paste the
|
|
# `ssh-ed25519 AAAA… <comment>` line here.
|
|
users = {
|
|
olivier = {
|
|
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICpORzDw8kEOobjywl7bQaEp6Tez1c/ZP+7VBDsTcM8I olivier";
|
|
};
|
|
server = {
|
|
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICsksvNGOt/ebJtpRi9ocLBjwyl16VSaf+BsL2Hz5PRy server";
|
|
};
|
|
builder = {
|
|
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIILBtfW1cq9mG6uAjAfpZuVa1EWDSTPSCThapDXXlLgV builder";
|
|
};
|
|
};
|
|
|
|
# Hosts SSH is actually safe to deploy/scan to: anything whose hostName
|
|
# parses as an IPv4 dotted-quad. Filters out scratchpad/non-routable
|
|
# entries (e.g. `hostName = "test"`).
|
|
activeHosts = lib.filterAttrs
|
|
(_: h: lib.match "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$" h.hostName != null)
|
|
hosts;
|
|
|
|
# .pub filename convention. OpenSSH 7.3+ accepts a `.pub` file as
|
|
# IdentityFile to filter `IdentityAgent` (rbw) keys without storing the
|
|
# private key on disk — keep this trick.
|
|
mkIdentityFileName = userName: ".ssh/id_ed25519_${lib.strings.toLower userName}.pub";
|
|
|
|
mkSshConfigTemplate =
|
|
{
|
|
selectedHosts ? activeHosts,
|
|
user ? null,
|
|
# Catalog accounts the operator may authenticate AS via
|
|
# `ssh <account>@<host>`. Each entry gets a `Match user <account>`
|
|
# block that overrides IdentityFile so OpenSSH asks the rbw agent
|
|
# for that account's private key. `cfg.user` is already covered
|
|
# by the Host blocks, so don't list it here.
|
|
extraIdentities ? [ ],
|
|
identityAgent ? "SSH_AUTH_SOCK",
|
|
}:
|
|
let
|
|
# Shared `IdentityFile` filter line (when `user` is set). Used by
|
|
# both the per-host and gitea blocks so the two can't drift apart.
|
|
idLine = lib.optionalString (user != null) " IdentityFile ~/${mkIdentityFileName user}\n";
|
|
|
|
# Match blocks for cross-account SSH. Filter out cfg.user since
|
|
# the matching pub is already loaded via the Host block's
|
|
# IdentityFile line — re-emitting it would just duplicate.
|
|
matchBlocks = lib.concatStringsSep "\n" (map
|
|
(n: ''
|
|
Match user ${n}
|
|
IdentityFile ~/${mkIdentityFileName n}
|
|
IdentitiesOnly yes
|
|
'')
|
|
(builtins.filter (n: n != user) extraIdentities));
|
|
|
|
# Per-host block. `IdentityFile` (when `user` is set) is a .pub file
|
|
# pointing at the agent's matching private key. With IdentitiesOnly
|
|
# yes, only agent keys matching that pub are tried — fixes the old
|
|
# "too many auth failures" symptom by design.
|
|
hostBlock = hostName:
|
|
let
|
|
e = selectedHosts.${hostName};
|
|
hostPatterns = lib.concatStringsSep " " (e.aliases ++ [ e.hostName ]);
|
|
in ''
|
|
Host ${hostPatterns}
|
|
HostName ${e.hostName}
|
|
${idLine} IdentityAgent ${identityAgent}
|
|
IdentitiesOnly yes
|
|
'';
|
|
|
|
# Gitea block: always emitted. `User git`, port 222. olivier's
|
|
# public key (passed via `IdentityFile`) is what gitea accepts
|
|
# because it gets pushed into gitea's authorized_keys through
|
|
# gitea's own UI for that account.
|
|
giteaBlock = ''
|
|
Host git.chiasson.cloud gitea
|
|
HostName 192.168.2.238
|
|
Port 222
|
|
User git
|
|
${idLine} IdentityAgent ${identityAgent}
|
|
IdentitiesOnly yes
|
|
'';
|
|
|
|
hostBlocks = lib.concatStringsSep "\n" (map hostBlock (lib.attrNames selectedHosts));
|
|
in
|
|
# Filter empty sections so an absent Match block (no extraIdentities)
|
|
# doesn't add leading whitespace to the rendered config.
|
|
lib.concatStringsSep "\n" (lib.filter (s: s != "") [
|
|
matchBlocks
|
|
giteaBlock
|
|
hostBlocks
|
|
''
|
|
Host *
|
|
IdentitiesOnly yes
|
|
IdentityAgent none
|
|
''
|
|
]);
|
|
|
|
# Per-user line for `users.users.<u>.openssh.authorizedKeys.keys`.
|
|
# `selection` is either "all" or a list of source identifiers for
|
|
# sshd's `from=` constraint (IP, CIDR, or hostname if reverse-DNS works).
|
|
# Returns null if inventory is missing the user's pubkey — caller is
|
|
# expected to assert and fail the build rather than silently lock out.
|
|
mkAuthorizedKeyLine = userName: selection:
|
|
let
|
|
entry = users.${userName} or null;
|
|
in
|
|
if entry == null then null
|
|
else if (entry.publicKey or "") == "" then null
|
|
else if selection == "all" then entry.publicKey
|
|
else ''from="${lib.concatStringsSep "," selection}" ${entry.publicKey}'';
|
|
in
|
|
{
|
|
inherit hosts users activeHosts;
|
|
mkSshConfigTemplate = mkSshConfigTemplate;
|
|
mkAuthorizedKeyLine = mkAuthorizedKeyLine;
|
|
mkIdentityFileName = mkIdentityFileName;
|
|
};
|
|
}
|