mirror of
https://github.com/Mic92/sops-nix.git
synced 2026-02-22 21:05:44 +08:00
Merge 56365fb9a8 into 8b89f44c2c
This commit is contained in:
commit
ed14334b6e
5 changed files with 262 additions and 3 deletions
|
|
@ -274,6 +274,34 @@ in
|
|||
Paths to ssh keys added as age keys during sops description.
|
||||
'';
|
||||
};
|
||||
|
||||
# Options for hardware key support (YubiKey, FIDO2, etc.)
|
||||
systemdDeps = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
example = [ "pcscd.socket" ];
|
||||
description = ''
|
||||
Additional systemd units that the sops-nix user service should depend on.
|
||||
This is useful when using age plugins that require external services like pcscd.
|
||||
'';
|
||||
};
|
||||
|
||||
requirePcscd = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether pcscd (PC/SC Smart Card Daemon) is required for age decryption.
|
||||
Enable this when using hardware key plugins like age-plugin-yubikey
|
||||
or age-plugin-fido2-hmac.
|
||||
|
||||
This adds a pre-start check to wait for pcscd to be available before
|
||||
attempting decryption.
|
||||
|
||||
Note: You must also enable `services.pcscd.enable = true` in your
|
||||
NixOS configuration. The pcscd service runs at the system level and
|
||||
will be socket-activated when the YubiKey is accessed.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
gnupg = {
|
||||
|
|
@ -377,9 +405,15 @@ in
|
|||
);
|
||||
};
|
||||
|
||||
# Note: pcscd.socket is a system service, not a user service, so we cannot
|
||||
# add it as a direct dependency for requirePcscd. Instead, we add a pre-start
|
||||
# script that waits for pcscd to be available.
|
||||
systemd.user.services.sops-nix = lib.mkIf pkgs.stdenv.hostPlatform.isLinux {
|
||||
Unit = {
|
||||
Description = "sops-nix activation";
|
||||
After = cfg.age.systemdDeps
|
||||
++ lib.optional cfg.age.requirePcscd "yubikey-touch-detector.service";
|
||||
Wants = cfg.age.systemdDeps;
|
||||
};
|
||||
Service = {
|
||||
Type = "oneshot";
|
||||
|
|
@ -387,9 +421,35 @@ in
|
|||
lib.mapAttrsToList (name: value: "'${name}=${value}'") cfg.environment
|
||||
);
|
||||
ExecStart = script;
|
||||
ExecStartPre = lib.mkIf cfg.age.requirePcscd [
|
||||
"${pkgs.writeShellScript "wait-for-pcscd" ''
|
||||
# Ensure pcscd is available for YubiKey communication.
|
||||
# When pcscd.socket is enabled, systemd creates /run/pcscd/pcscd.comm
|
||||
# and starts pcscd.service on-demand when the socket is accessed.
|
||||
|
||||
i=0
|
||||
while [ $i -lt 30 ]; do
|
||||
# Check if the pcscd socket file exists - this is the most reliable check
|
||||
# and doesn't require D-Bus access
|
||||
if [ -e /run/pcscd/pcscd.comm ]; then
|
||||
exit 0
|
||||
fi
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
echo "Warning: pcscd socket not found at /run/pcscd/pcscd.comm" >&2
|
||||
echo "YubiKey decryption may fail. Ensure services.pcscd.enable = true" >&2
|
||||
''}"
|
||||
];
|
||||
};
|
||||
Install.WantedBy =
|
||||
if cfg.gnupg.home != null then [ "graphical-session-pre.target" ] else [ "default.target" ];
|
||||
# When pcscd is required, we need to wait for the graphical session to be active
|
||||
# so that polkit recognizes it as an active session and allows pcscd access.
|
||||
# Otherwise, we run at default.target for faster boot times.
|
||||
if cfg.gnupg.home != null || cfg.age.requirePcscd
|
||||
then [ "graphical-session-pre.target" ]
|
||||
else [ "default.target" ];
|
||||
};
|
||||
|
||||
# Darwin: load secrets once on login
|
||||
|
|
|
|||
|
|
@ -316,6 +316,22 @@ in
|
|||
List of plugins to use for sops decryption.
|
||||
'';
|
||||
};
|
||||
|
||||
# Options for hardware key support (YubiKey, FIDO2, etc.)
|
||||
requirePcscd = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether pcscd (PC/SC Smart Card Daemon) is required for age decryption.
|
||||
Enable this when using hardware key plugins like age-plugin-yubikey
|
||||
or age-plugin-fido2-hmac.
|
||||
|
||||
On macOS, the system's built-in smart card services (CryptoTokenKit)
|
||||
typically handle YubiKey communication automatically. This option
|
||||
is provided for consistency with Linux but may not require additional
|
||||
configuration on macOS.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
gnupg = {
|
||||
|
|
|
|||
|
|
@ -369,6 +369,43 @@ in
|
|||
Paths to ssh keys added as age keys during sops description.
|
||||
'';
|
||||
};
|
||||
|
||||
# Options for hardware key support (YubiKey, FIDO2, etc.)
|
||||
activationScriptDeps = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
example = [ "setupPcscdForSops" ];
|
||||
description = ''
|
||||
Additional activation script names that must complete before
|
||||
setupSecrets and setupSecretsForUsers run. This is useful when
|
||||
using age plugins that require external services like pcscd.
|
||||
'';
|
||||
};
|
||||
|
||||
systemdDeps = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
example = [ "pcscd.socket" ];
|
||||
description = ''
|
||||
Additional systemd units that sops-install-secrets should depend on
|
||||
when using systemd activation mode. This is useful when using age
|
||||
plugins that require external services like pcscd.
|
||||
'';
|
||||
};
|
||||
|
||||
requirePcscd = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether pcscd (PC/SC Smart Card Daemon) is required for age decryption.
|
||||
Enable this when using hardware key plugins like age-plugin-yubikey
|
||||
or age-plugin-fido2-hmac. This automatically configures the necessary
|
||||
dependencies to ensure pcscd is running before secrets are decrypted.
|
||||
|
||||
Note: You must also enable `services.pcscd.enable = true` in your
|
||||
NixOS configuration for this to work.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
gnupg = {
|
||||
|
|
@ -467,7 +504,8 @@ in
|
|||
# When using sysusers we no longer are started as an activation script because those are started in initrd while sysusers is started later.
|
||||
systemd.services.sops-install-secrets = lib.mkIf (regularSecrets != { } && cfg.useSystemdActivation) {
|
||||
wantedBy = [ "sysinit.target" ];
|
||||
after = [ "systemd-sysusers.service" "userborn.service" ];
|
||||
after = [ "systemd-sysusers.service" "userborn.service" ] ++ cfg.age.systemdDeps;
|
||||
wants = cfg.age.systemdDeps;
|
||||
requiredBy = [ "sysinit-reactivation.target" ];
|
||||
before = [ "sysinit-reactivation.target" ];
|
||||
environment = cfg.environment;
|
||||
|
|
@ -491,6 +529,7 @@ in
|
|||
"groups"
|
||||
]
|
||||
++ lib.optional cfg.age.generateKey "generate-age-key"
|
||||
++ cfg.age.activationScriptDeps
|
||||
)
|
||||
''
|
||||
[ -e /run/current-system ] || echo setting up secrets...
|
||||
|
|
@ -520,5 +559,40 @@ in
|
|||
{
|
||||
system.build.sops-nix-manifest = manifest;
|
||||
}
|
||||
|
||||
# Automatic pcscd configuration for hardware key plugins
|
||||
(lib.mkIf cfg.age.requirePcscd {
|
||||
assertions = [
|
||||
{
|
||||
assertion = config.services.pcscd.enable or false;
|
||||
message = ''
|
||||
sops.age.requirePcscd is enabled but services.pcscd.enable is not set.
|
||||
Please add `services.pcscd.enable = true;` to your configuration.
|
||||
'';
|
||||
}
|
||||
];
|
||||
|
||||
# Add pcscd.socket as a systemd dependency
|
||||
sops.age.systemdDeps = [ "pcscd.socket" ];
|
||||
|
||||
# For activation script mode, ensure pcscd is started before secrets
|
||||
system.activationScripts.setupPcscdForSops = lib.mkIf (!cfg.useSystemdActivation) (
|
||||
lib.stringAfter [ "specialfs" ] ''
|
||||
# Ensure pcscd drivers are available
|
||||
mkdir -p /var/lib/pcsc
|
||||
ln -sfn ${pkgs.ccid}/pcsc/drivers /var/lib/pcsc/drivers
|
||||
|
||||
# Try to start pcscd via socket activation, or directly if needed
|
||||
if ! ${pkgs.systemd}/bin/systemctl is-active --quiet pcscd.socket 2>/dev/null; then
|
||||
if ! ${pkgs.systemd}/bin/systemctl is-active --quiet pcscd.service 2>/dev/null; then
|
||||
# Start pcscd directly with auto-exit for activation script context
|
||||
${pkgs.pcsclite}/bin/pcscd --auto-exit 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
''
|
||||
);
|
||||
|
||||
sops.age.activationScriptDeps = lib.mkIf (!cfg.useSystemdActivation) [ "setupPcscdForSops" ];
|
||||
})
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ in
|
|||
{
|
||||
wantedBy = [ "systemd-sysusers.service" ];
|
||||
before = [ "systemd-sysusers.service" ];
|
||||
after = cfg.age.systemdDeps;
|
||||
wants = cfg.age.systemdDeps;
|
||||
environment = cfg.environment;
|
||||
unitConfig.DefaultDependencies = "no";
|
||||
path = cfg.age.plugins;
|
||||
|
|
@ -48,7 +50,11 @@ in
|
|||
|
||||
system.activationScripts = lib.mkIf (secretsForUsers != { } && !useSystemdActivation) {
|
||||
setupSecretsForUsers =
|
||||
lib.stringAfter ([ "specialfs" ] ++ lib.optional cfg.age.generateKey "generate-age-key") ''
|
||||
lib.stringAfter (
|
||||
[ "specialfs" ]
|
||||
++ lib.optional cfg.age.generateKey "generate-age-key"
|
||||
++ cfg.age.activationScriptDeps
|
||||
) ''
|
||||
[ -e /run/current-system ] || echo setting up secrets for users...
|
||||
${withEnvironment "${cfg.package}/bin/sops-install-secrets -ignore-passwd ${manifestForUsers}"}
|
||||
''
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue