Compare commits

...

8 Commits

15 changed files with 191 additions and 110 deletions

View File

@@ -1,4 +1,3 @@
include ./taskrc.d/$HOSTNAME
include ./taskrc.d/aliases
include ./taskrc.d/urgency
include ./taskrc.d/reports
@@ -12,4 +11,3 @@ rc.json.array=on
rc.verbose=nothing
news.version=3.1.0
recurrence=off

36
flake.lock generated
View File

@@ -121,11 +121,11 @@
},
"locked": {
"dir": "pkgs/firefox-addons",
"lastModified": 1778040175,
"narHash": "sha256-SSXJp3BMjO2LrW/VLjNdGGcjd3RFEyV4FemYA6OGrYw=",
"lastModified": 1778904158,
"narHash": "sha256-xZ/zdpu9X8OI1jGqd4z6W1hKa+DQh8A9k3TH6w9tNbo=",
"owner": "rycee",
"repo": "nur-expressions",
"rev": "3bd76b0f41e65661866bddcac57ebe83aeadb581",
"rev": "c31bafebd7e30517ead27149ad790802b998e98f",
"type": "gitlab"
},
"original": {
@@ -284,11 +284,11 @@
]
},
"locked": {
"lastModified": 1776796298,
"narHash": "sha256-PcRvlWayisPSjd0UcRQbhG8Oqw78AcPE6x872cPRHN8=",
"lastModified": 1778507602,
"narHash": "sha256-kTwur1wV+01SdqskVMSo6JMEpg71ps3HpbFY2GsflKs=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "3cfd774b0a530725a077e17354fbdb87ea1c4aad",
"rev": "61ab0e80d9c7ab14c256b5b453d8b3fb0189ba0a",
"type": "github"
},
"original": {
@@ -342,11 +342,11 @@
]
},
"locked": {
"lastModified": 1778009629,
"narHash": "sha256-nUoQtf4Zq7DRYJrfv904hjrxjAlWVP6a1pNNFKx3FCg=",
"lastModified": 1778921576,
"narHash": "sha256-jgIbBcGgKTtkp+lBas29hNFUU1t5O/2+GFvUjbiG0vM=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "00ed86e58bb6979a7921859fd1615d19382eac5c",
"rev": "32b42d71b4b22c4465963285bb6e462d7c93d45e",
"type": "github"
},
"original": {
@@ -451,11 +451,11 @@
},
"nixos-hardware": {
"locked": {
"lastModified": 1777917524,
"narHash": "sha256-k+LVe9YaO2BEPB9AaCtTtOMCeGi4dxDo6gt4Un3qoPY=",
"lastModified": 1778924461,
"narHash": "sha256-pDGhSCz+QxjHzP9gqYpK59k4Z1ZpVyYjzBgoXLFXh9s=",
"owner": "NixOS",
"repo": "nixos-hardware",
"rev": "df7783100babf59001340a7a874ba3824e441ecb",
"rev": "65a5c8f9c19d5f9d1b06d8942b04d790414ccfdd",
"type": "github"
},
"original": {
@@ -467,11 +467,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1777954456,
"narHash": "sha256-hGdgeU2Nk87RAuZyYjyDjFL6LK7dAZN5RE9+hrDTkDU=",
"lastModified": 1778443072,
"narHash": "sha256-zi7/fsqM/kFdNuED//4WOCUtezGtKKqRNORjMvfwjnA=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "549bd84d6279f9852cae6225e372cc67fb91a4c1",
"rev": "da5ad661ba4e5ef59ba743f0d112cbc30e474f32",
"type": "github"
},
"original": {
@@ -714,11 +714,11 @@
"tinted-zed": "tinted-zed"
},
"locked": {
"lastModified": 1777835090,
"narHash": "sha256-VLH8zPweblCOvpnQXp4fVs7f6Q79YhXF5XFKlOrvIFk=",
"lastModified": 1778776709,
"narHash": "sha256-YhnEcpiY6+l3RFA+cPmdTaeODGvNRuqE8B7VBjPVIxo=",
"owner": "danth",
"repo": "stylix",
"rev": "7989a1054b01153212dede6005abfd1576b8328c",
"rev": "e8ea85b4f7dddda9603e0f1ac86cd92cee3b2819",
"type": "github"
},
"original": {

View File

@@ -30,8 +30,9 @@
../../modules/ssh
../../modules/taskwarrior
../../modules/terminal
../../modules/zk
../../modules/torrenting
../../modules/yubikey
../../modules/zk
];
home = {
@@ -58,6 +59,7 @@
shell.bash.aliases.lang-js = true;
shell.bash.addBinToPath = true;
torrenting.enable = true;
my.yubikey.enable = true;
zk.enable = true;
programs = {

View File

@@ -29,6 +29,7 @@
../../modules/ssh
../../modules/taskwarrior
../../modules/terminal
../../modules/yubikey
];
home = {
@@ -53,6 +54,7 @@
git.github.enable = true;
shell.bash.aliases.lang-js = true;
shell.bash.addBinToPath = true;
my.yubikey.enable = true;
programs = {
home-manager.enable = true;

View File

@@ -0,0 +1,61 @@
# SSH keys
* primary keys (host-specific, non-resident)
* backup key (shared, resident)
## generate keys
### YubiKey 01 - `host_01`
```sh
ssh-keygen -t ed25519-sk \
-O verify-required \
-f ~/.ssh/id_ed25519_sk \
-C "h@host_01"
```
### YubiKey 01 — `host_02`
```sh
ssh-keygen -t ed25519-sk \
-O verify-required \
-f ~/.ssh/id_ed25519_sk \
-C "h@host_02"
```
### YubiKey 02 - `host_*`
```sh
ssh-keygen -t ed25519-sk \
-O resident \
-O verify-required \
-f ~/.ssh/id_ed25519_sk_bak \
-C "backup"
```
## register keys
when you the primary key (`id_ed25519_sk.pub`), make sure to also register the
backup key (`id_ed25519_sk_bak.pub`) if needed.
## recovery scenarios
| scenario | recovery |
|---|---|
| primary key file lost | generate new primary key on that device, re-register (use backup key) |
| primary YubiKey lost | generate new primary keys on all devices using new YubiKey (use backup key) |
| backup key file lost | regenerate from backup YubiKey resident key (use `ssh-keygen -K`) |
| backup YubiKey lost | generate resident backup key, distribute across hosts, re-register (use primary key) |
## notes / to do
TODO: automate distributing `id_ed25519_sk_bak`, `id_ed25519_sk_bak.pub` to all devices
TODO: declare setup scripts (use e.g. `$HOSTNAME`)
TODO: register backup key with hosts (add to authorized hosts for each host)
TODO: register backup key with services (e.g. Gitea)
TODO: make sure to fall back to backup key when host-specific primary key is not present
TODO: see if / how `-O application=ssh:<name>` could be used
## references
* <https://developers.yubico.com/SSH/Securing_SSH_with_FIDO2.html>

View File

@@ -35,6 +35,7 @@ in
// {
"*" = {
addKeysToAgent = "yes";
forwardAgent = false;
};
};
};

View File

@@ -0,0 +1,19 @@
{
lib,
config,
pkgs,
...
}:
{
options.my.yubikey = {
enable = lib.mkEnableOption "yubikey";
};
config = lib.mkIf config.my.yubikey.enable {
home.packages = with pkgs; [
yubikey-manager
yubikey-personalization
];
};
}

View File

@@ -91,21 +91,8 @@ in
];
my.yubikey = {
enable = false;
keys = [
{
handle = "<KeyHandle1>";
userKey = "<UserKey1>";
coseType = "<CoseType1>";
options = "<Options1>";
}
{
handle = "<KeyHandle2>";
userKey = "<UserKey2>";
coseType = "<CoseType2>";
options = "<Options2>";
}
];
enable = true;
pam.enable = false;
};
services.locate = {

View File

@@ -1 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOOXPEhdKOVnb6mkeLLUcFGt+mnUR5pMie17JtjrxwgO h@andromache
sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIIj+isTFg+W3v68HL28a9p9nX7ldAxu+ht9uUfKLvqcwAAAABHNzaDo= h@andromache

View File

@@ -99,21 +99,7 @@ in
my.yubikey = {
enable = true;
# inherit (config.host) username;
# keys = [
# {
# handle = "<KeyHandle1>";
# userKey = "<UserKey1>";
# coseType = "<CoseType1>";
# options = "<Options1>";
# }
# {
# handle = "<KeyHandle2>";
# userKey = "<UserKey2>";
# coseType = "<CoseType2>";
# options = "<Options2>";
# }
# ];
pam.enable = false;
};
services = {

View File

@@ -26,6 +26,13 @@ in
useNautilus = false;
};
programs.xwayland.enable = true;
environment = {
systemPackages = [ pkgs.xwayland-satellite ];
sessionVariables.NIXOS_OZONE_WL = "1";
};
xdg.portal = {
enable = true;
extraPortals = [

View File

@@ -17,7 +17,8 @@ in
};
# auto generate authorized_keys from `authorizedHosts`
config.users.users.${username}.openssh.authorizedKeys.keys = lib.flatten (
config.users.users.${username}.openssh.authorizedKeys.keys =
lib.flatten (
map (
hostname:
let
@@ -25,5 +26,6 @@ in
in
lib.optionals (builtins.pathExists keyFile) (lib.splitString "\n" (builtins.readFile keyFile))
) ((builtins.filter (h: h != config.host.name) adminHosts) ++ config.ssh.authorizedHosts)
);
)
++ lib.splitString "\n" (builtins.readFile ./ssh_bak.pub);
}

View File

@@ -14,8 +14,16 @@ in
chmod 644 "$HOST_DIR/ssh_host.pub"
fi
USER_KEY="/home/${username}/.ssh/id_ed25519.pub"
if [ -f "$USER_KEY" ] && [ -d "$HOST_DIR" ]; then
USER_KEY=""
for candidate in \
"/home/${username}/.ssh/id_ed25519_sk.pub" \
"/home/${username}/.ssh/id_ed25519.pub"; do
if [ -f "$candidate" ]; then
USER_KEY="$candidate"
break
fi
done
if [ -n "$USER_KEY" ] && [ -d "$HOST_DIR" ]; then
cp "$USER_KEY" "$HOST_DIR/ssh_user.pub"
chown ${username}:users "$HOST_DIR/ssh_user.pub"
chmod 644 "$HOST_DIR/ssh_user.pub"

1
modules/ssh/ssh_bak.pub Normal file
View File

@@ -0,0 +1 @@
sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAINg9dwrE10NQ7WMhL/hFJHRNFlDedvloCm9E4XEfnpStAAAABHNzaDo= backup

View File

@@ -15,13 +15,13 @@ let
in
{
options.my.yubikey = {
enable = mkEnableOption "yubiKey U2F authentication";
enable = mkEnableOption "YubiKey";
pam = {
enable = mkEnableOption "YubiKey PAM U2F";
origin = mkOption {
type = types.str;
default = "pam://yubi";
};
keys = mkOption {
type = types.listOf (
types.submodule {
@@ -48,16 +48,24 @@ in
default = [ ];
};
};
};
config = mkIf cfg.enable {
config = mkMerge [
(mkIf cfg.enable {
services.udev.packages = with pkgs; [
yubikey-manager
yubikey-personalization
];
})
(mkIf (cfg.enable && cfg.pam.enable) {
security.pam = {
u2f = {
enable = true;
settings = {
interactive = true;
cue = true;
inherit (cfg) origin;
authfile = pkgs.writeText "u2f-mappings" (authfileContent username cfg.keys);
origin = cfg.pam.origin;
authfile = pkgs.writeText "u2f-mappings" (authfileContent username cfg.pam.keys);
};
};
services = {
@@ -65,7 +73,6 @@ in
sudo.u2fAuth = true;
};
};
services.udev.packages = with pkgs; [ yubikey-personalization ];
};
})
];
}