mirror of
https://github.com/ryan4yin/nix-config.git
synced 2026-11-13 12:13:46 +01:00
Hosts now accept only the two CanoKeys' resident SSH keys for ryan, plus the romantic recovery key for root; per-host user keys stop working. Darwin builds work again: the shared base no longer sets nixpkgs.config next to nix-darwin's nixpkgs.pkgs. Assisted-by: DeepSeek Harness (claude-opus-5-5) Assisted-by: DeepSeek Harness (deepseek-flash)
103 lines
3.5 KiB
Nix
103 lines
3.5 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
mysecrets,
|
|
myvars,
|
|
...
|
|
}:
|
|
let
|
|
# WA-035: settings for the home LAN, which the desktop reaches over a Mesh
|
|
# WiFi backhaul.
|
|
lanHost = {
|
|
# A lost first SYN must not fail the connection; a LAN handshake is ~2 ms,
|
|
# so the connect is cut short and retried instead of hanging.
|
|
ConnectTimeout = 1;
|
|
ConnectionAttempts = 3;
|
|
# One persistent master per host keeps a session (and with it the mesh's
|
|
# forwarding entry for that peer) open, so later connections reuse it
|
|
# instead of resolving and handshaking again. Three missed keepalives reap a
|
|
# master whose path died.
|
|
ControlMaster = "auto";
|
|
ControlPersist = "30m";
|
|
ServerAliveInterval = 15;
|
|
};
|
|
in
|
|
{
|
|
home.file.".ssh/romantic.pub".source = "${mysecrets}/public/romantic.pub";
|
|
|
|
# Each CanoKey holds a resident FIDO2 SSH key for GitHub and every host, its
|
|
# handle exported on each desktop to ~/.ssh/canokey-<name>; every use needs a
|
|
# touch, and ssh skips the handle of an unplugged key. Setup:
|
|
# docs/security-keys.md.
|
|
programs.ssh = {
|
|
enable = true;
|
|
# macOS's ssh has no FIDO provider ("Cannot download keys without provider").
|
|
package = lib.mkIf pkgs.stdenv.hostPlatform.isDarwin pkgs.openssh;
|
|
|
|
enableDefaultConfig = false;
|
|
# Keep this one definition: Nix merges a `settings."<pattern>"` attribute
|
|
# path into `settings = ...` only while that value stays an attrset literal,
|
|
# so the generated LAN patterns are merged inside it with `//` instead.
|
|
settings = {
|
|
"*" = {
|
|
# Main key first, then the backup.
|
|
IdentityFile = map (name: "${config.home.homeDirectory}/.ssh/${name}") [
|
|
"canokey-tea"
|
|
"canokey-moe"
|
|
];
|
|
ForwardAgent = false;
|
|
AddKeysToAgent = "yes";
|
|
Compression = true;
|
|
ServerAliveInterval = 0;
|
|
ServerAliveCountMax = 3;
|
|
HashKnownHosts = false;
|
|
UserKnownHostsFile = "~/.ssh/known_hosts";
|
|
ControlMaster = "no";
|
|
ControlPath = "~/.ssh/master-%r@%n:%p";
|
|
ControlPersist = "no";
|
|
};
|
|
|
|
"github.com" = {
|
|
# WA-028: pinned to IPv4 until the mihomo v6 fake-ip pool is proven here
|
|
AddressFamily = "inet";
|
|
HostName = "ssh.github.com";
|
|
Port = 443;
|
|
User = "git";
|
|
IdentitiesOnly = true;
|
|
};
|
|
|
|
"192.168.*" = {
|
|
ForwardAgent = true;
|
|
}
|
|
// lanHost;
|
|
}
|
|
# Aliases forward the agent like their addresses do; servers ask it for sudo
|
|
# (modules/nixos/server/ssh-agent-sudo.nix).
|
|
// lib.genAttrs (builtins.attrNames myvars.networking.hostsAddr) (
|
|
_:
|
|
{
|
|
ForwardAgent = true;
|
|
}
|
|
// lanHost
|
|
);
|
|
};
|
|
|
|
# Keep ~/.ssh/config a real, user-owned file rather than a /nix/store symlink.
|
|
#
|
|
# OpenSSH accepts a config owned by root or the invoking user, but
|
|
# bubblewrap-based sandboxes (`--unshare-user`) expose root-owned store files
|
|
# as `nobody`, which trips the check (`Bad owner or permissions`). Disabling
|
|
# the home.file entry keeps Home Manager's linker out of the way; `install`
|
|
# writes a fresh 0600 copy on each activation.
|
|
#
|
|
# Linux only: macOS' Seatbelt sandbox keeps store ownership intact, so the
|
|
# normal symlink is fine there.
|
|
home.file.".ssh/config".enable = lib.mkIf pkgs.stdenv.hostPlatform.isLinux false;
|
|
home.activation.sshConfigRegularFile = lib.mkIf pkgs.stdenv.hostPlatform.isLinux (
|
|
lib.hm.dag.entryAfter [ "linkGeneration" ] ''
|
|
run install -D -m 600 ${config.home.file.".ssh/config".source} "$HOME/.ssh/config"
|
|
''
|
|
);
|
|
}
|