9f9e277b60
These (and the `*MD` functions apart from `literalMD`) are now no-ops in nixpkgs and serve no purpose other than to add additional noise and potentially mislead people into thinking unmarked DocBook documentation will still be accepted. Note that if backporting changes including documentation to 23.05, the `mdDoc` calls will need to be re-added. To reproduce this commit, run: $ NIX_PATH=nixpkgs=flake:nixpkgs/e7e69199f0372364a6106a1e735f68604f4c5a25 \ nix shell nixpkgs#coreutils \ -c find . -name '*.nix' \ -exec nix run -- github:emilazy/nix-doc-munge/98dadf1f77351c2ba5dcb709a2a171d655f15099 \ --strip {} + $ ./format
346 lines
10 KiB
Nix
346 lines
10 KiB
Nix
{ config, options, lib, pkgs, ... }:
|
|
|
|
with lib;
|
|
|
|
let
|
|
|
|
cfg = config.services.gpg-agent;
|
|
gpgPkg = config.programs.gpg.package;
|
|
|
|
homedir = config.programs.gpg.homedir;
|
|
|
|
gpgSshSupportStr = ''
|
|
${gpgPkg}/bin/gpg-connect-agent updatestartuptty /bye > /dev/null
|
|
'';
|
|
|
|
gpgInitStr = ''
|
|
GPG_TTY="$(tty)"
|
|
export GPG_TTY
|
|
'' + optionalString cfg.enableSshSupport gpgSshSupportStr;
|
|
|
|
gpgFishInitStr = ''
|
|
set -gx GPG_TTY (tty)
|
|
'' + optionalString cfg.enableSshSupport gpgSshSupportStr;
|
|
|
|
# mimic `gpgconf` output for use in `systemd` unit definitions.
|
|
# we cannot use `gpgconf` directly because it heavily depends on system
|
|
# state, but we need the values at build time. original:
|
|
# https://github.com/gpg/gnupg/blob/c6702d77d936b3e9d91b34d8fdee9599ab94ee1b/common/homedir.c#L672-L681
|
|
gpgconf = dir:
|
|
let
|
|
hash =
|
|
substring 0 24 (hexStringToBase32 (builtins.hashString "sha1" homedir));
|
|
in if homedir == options.programs.gpg.homedir.default then
|
|
"%t/gnupg/${dir}"
|
|
else
|
|
"%t/gnupg/d.${hash}/${dir}";
|
|
|
|
# Act like `xxd -r -p | base32` but with z-base-32 alphabet and no trailing padding.
|
|
# Written in Nix for purity.
|
|
hexStringToBase32 = let
|
|
mod = a: b: a - a / b * b;
|
|
pow2 = elemAt [ 1 2 4 8 16 32 64 128 256 ];
|
|
splitChars = s: init (tail (splitString "" s));
|
|
|
|
base32Alphabet = splitChars "ybndrfg8ejkmcpqxot1uwisza345h769";
|
|
hexToIntTable = listToAttrs (genList (x: {
|
|
name = toLower (toHexString x);
|
|
value = x;
|
|
}) 16);
|
|
|
|
initState = {
|
|
ret = "";
|
|
buf = 0;
|
|
bufBits = 0;
|
|
};
|
|
go = { ret, buf, bufBits }:
|
|
hex:
|
|
let
|
|
buf' = buf * pow2 4 + hexToIntTable.${hex};
|
|
bufBits' = bufBits + 4;
|
|
extraBits = bufBits' - 5;
|
|
in if bufBits >= 5 then {
|
|
ret = ret + elemAt base32Alphabet (buf' / pow2 extraBits);
|
|
buf = mod buf' (pow2 extraBits);
|
|
bufBits = bufBits' - 5;
|
|
} else {
|
|
ret = ret;
|
|
buf = buf';
|
|
bufBits = bufBits';
|
|
};
|
|
in hexString: (foldl' go initState (splitChars hexString)).ret;
|
|
|
|
in {
|
|
meta.maintainers = [ maintainers.rycee ];
|
|
|
|
options = {
|
|
services.gpg-agent = {
|
|
enable = mkEnableOption "GnuPG private key agent";
|
|
|
|
defaultCacheTtl = mkOption {
|
|
type = types.nullOr types.int;
|
|
default = null;
|
|
description = ''
|
|
Set the time a cache entry is valid to the given number of
|
|
seconds.
|
|
'';
|
|
};
|
|
|
|
defaultCacheTtlSsh = mkOption {
|
|
type = types.nullOr types.int;
|
|
default = null;
|
|
description = ''
|
|
Set the time a cache entry used for SSH keys is valid to the
|
|
given number of seconds.
|
|
'';
|
|
};
|
|
|
|
maxCacheTtl = mkOption {
|
|
type = types.nullOr types.int;
|
|
default = null;
|
|
description = ''
|
|
Set the maximum time a cache entry is valid to n seconds. After this
|
|
time a cache entry will be expired even if it has been accessed
|
|
recently or has been set using gpg-preset-passphrase. The default is
|
|
2 hours (7200 seconds).
|
|
'';
|
|
};
|
|
|
|
maxCacheTtlSsh = mkOption {
|
|
type = types.nullOr types.int;
|
|
default = null;
|
|
description = ''
|
|
Set the maximum time a cache entry used for SSH keys is valid to n
|
|
seconds. After this time a cache entry will be expired even if it has
|
|
been accessed recently or has been set using gpg-preset-passphrase.
|
|
The default is 2 hours (7200 seconds).
|
|
'';
|
|
};
|
|
|
|
enableSshSupport = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = ''
|
|
Whether to use the GnuPG key agent for SSH keys.
|
|
'';
|
|
};
|
|
|
|
sshKeys = mkOption {
|
|
type = types.nullOr (types.listOf types.str);
|
|
default = null;
|
|
description = ''
|
|
Which GPG keys (by keygrip) to expose as SSH keys.
|
|
'';
|
|
};
|
|
|
|
enableExtraSocket = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = ''
|
|
Whether to enable extra socket of the GnuPG key agent (useful for GPG
|
|
Agent forwarding).
|
|
'';
|
|
};
|
|
|
|
verbose = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = ''
|
|
Whether to produce verbose output.
|
|
'';
|
|
};
|
|
|
|
grabKeyboardAndMouse = mkOption {
|
|
type = types.bool;
|
|
default = true;
|
|
description = ''
|
|
Tell the pinentry to grab the keyboard and mouse. This
|
|
option should in general be used to avoid X-sniffing
|
|
attacks. When disabled, this option passes
|
|
{option}`no-grab` setting to gpg-agent.
|
|
'';
|
|
};
|
|
|
|
enableScDaemon = mkOption {
|
|
type = types.bool;
|
|
default = true;
|
|
description = ''
|
|
Make use of the scdaemon tool. This option has the effect of
|
|
enabling the ability to do smartcard operations. When
|
|
disabled, this option passes
|
|
{option}`disable-scdaemon` setting to gpg-agent.
|
|
'';
|
|
};
|
|
|
|
extraConfig = mkOption {
|
|
type = types.lines;
|
|
default = "";
|
|
example = ''
|
|
allow-emacs-pinentry
|
|
allow-loopback-pinentry
|
|
'';
|
|
description = ''
|
|
Extra configuration lines to append to the gpg-agent
|
|
configuration file.
|
|
'';
|
|
};
|
|
|
|
pinentryFlavor = mkOption {
|
|
type = types.nullOr (types.enum pkgs.pinentry.flavors);
|
|
example = "gnome3";
|
|
default = "gtk2";
|
|
description = ''
|
|
Which pinentry interface to use. If not
|
|
`null`, it sets
|
|
{option}`pinentry-program` in
|
|
{file}`gpg-agent.conf`. Beware that
|
|
`pinentry-gnome3` may not work on non-Gnome
|
|
systems. You can fix it by adding the following to your
|
|
system configuration:
|
|
```nix
|
|
services.dbus.packages = [ pkgs.gcr ];
|
|
```
|
|
For this reason, the default is `gtk2` for
|
|
now.
|
|
'';
|
|
};
|
|
|
|
enableBashIntegration = mkEnableOption "Bash integration" // {
|
|
default = true;
|
|
};
|
|
|
|
enableZshIntegration = mkEnableOption "Zsh integration" // {
|
|
default = true;
|
|
};
|
|
|
|
enableFishIntegration = mkEnableOption "Fish integration" // {
|
|
default = true;
|
|
};
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable (mkMerge [
|
|
{
|
|
home.file."${homedir}/gpg-agent.conf".text = concatStringsSep "\n"
|
|
(optional (cfg.enableSshSupport) "enable-ssh-support"
|
|
++ optional cfg.grabKeyboardAndMouse "grab"
|
|
++ optional (!cfg.enableScDaemon) "disable-scdaemon"
|
|
++ optional (cfg.defaultCacheTtl != null)
|
|
"default-cache-ttl ${toString cfg.defaultCacheTtl}"
|
|
++ optional (cfg.defaultCacheTtlSsh != null)
|
|
"default-cache-ttl-ssh ${toString cfg.defaultCacheTtlSsh}"
|
|
++ optional (cfg.maxCacheTtl != null)
|
|
"max-cache-ttl ${toString cfg.maxCacheTtl}"
|
|
++ optional (cfg.maxCacheTtlSsh != null)
|
|
"max-cache-ttl-ssh ${toString cfg.maxCacheTtlSsh}"
|
|
++ optional (cfg.pinentryFlavor != null)
|
|
"pinentry-program ${pkgs.pinentry.${cfg.pinentryFlavor}}/bin/pinentry"
|
|
++ [ cfg.extraConfig ]);
|
|
|
|
home.sessionVariablesExtra = optionalString cfg.enableSshSupport ''
|
|
if [[ -z "$SSH_AUTH_SOCK" ]]; then
|
|
export SSH_AUTH_SOCK="$(${gpgPkg}/bin/gpgconf --list-dirs agent-ssh-socket)"
|
|
fi
|
|
'';
|
|
|
|
programs.bash.initExtra = mkIf cfg.enableBashIntegration gpgInitStr;
|
|
programs.zsh.initExtra = mkIf cfg.enableZshIntegration gpgInitStr;
|
|
programs.fish.interactiveShellInit =
|
|
mkIf cfg.enableFishIntegration gpgFishInitStr;
|
|
}
|
|
|
|
(mkIf (cfg.sshKeys != null) {
|
|
# Trailing newlines are important
|
|
home.file."${homedir}/sshcontrol".text = concatMapStrings (s: ''
|
|
${s}
|
|
'') cfg.sshKeys;
|
|
})
|
|
|
|
# The systemd units below are direct translations of the
|
|
# descriptions in the
|
|
#
|
|
# ${gpgPkg}/share/doc/gnupg/examples/systemd-user
|
|
#
|
|
# directory.
|
|
{
|
|
assertions = [
|
|
(hm.assertions.assertPlatform "services.gpg-agent" pkgs platforms.linux)
|
|
];
|
|
|
|
systemd.user.services.gpg-agent = {
|
|
Unit = {
|
|
Description = "GnuPG cryptographic agent and passphrase cache";
|
|
Documentation = "man:gpg-agent(1)";
|
|
Requires = "gpg-agent.socket";
|
|
After = "gpg-agent.socket";
|
|
# This is a socket-activated service:
|
|
RefuseManualStart = true;
|
|
};
|
|
|
|
Service = {
|
|
ExecStart = "${gpgPkg}/bin/gpg-agent --supervised"
|
|
+ optionalString cfg.verbose " --verbose";
|
|
ExecReload = "${gpgPkg}/bin/gpgconf --reload gpg-agent";
|
|
Environment = [ "GNUPGHOME=${homedir}" ];
|
|
};
|
|
};
|
|
|
|
systemd.user.sockets.gpg-agent = {
|
|
Unit = {
|
|
Description = "GnuPG cryptographic agent and passphrase cache";
|
|
Documentation = "man:gpg-agent(1)";
|
|
};
|
|
|
|
Socket = {
|
|
ListenStream = gpgconf "S.gpg-agent";
|
|
FileDescriptorName = "std";
|
|
SocketMode = "0600";
|
|
DirectoryMode = "0700";
|
|
};
|
|
|
|
Install = { WantedBy = [ "sockets.target" ]; };
|
|
};
|
|
}
|
|
|
|
(mkIf cfg.enableSshSupport {
|
|
systemd.user.sockets.gpg-agent-ssh = {
|
|
Unit = {
|
|
Description = "GnuPG cryptographic agent (ssh-agent emulation)";
|
|
Documentation =
|
|
"man:gpg-agent(1) man:ssh-add(1) man:ssh-agent(1) man:ssh(1)";
|
|
};
|
|
|
|
Socket = {
|
|
ListenStream = gpgconf "S.gpg-agent.ssh";
|
|
FileDescriptorName = "ssh";
|
|
Service = "gpg-agent.service";
|
|
SocketMode = "0600";
|
|
DirectoryMode = "0700";
|
|
};
|
|
|
|
Install = { WantedBy = [ "sockets.target" ]; };
|
|
};
|
|
})
|
|
|
|
(mkIf cfg.enableExtraSocket {
|
|
systemd.user.sockets.gpg-agent-extra = {
|
|
Unit = {
|
|
Description =
|
|
"GnuPG cryptographic agent and passphrase cache (restricted)";
|
|
Documentation = "man:gpg-agent(1) man:ssh(1)";
|
|
};
|
|
|
|
Socket = {
|
|
ListenStream = gpgconf "S.gpg-agent.extra";
|
|
FileDescriptorName = "extra";
|
|
Service = "gpg-agent.service";
|
|
SocketMode = "0600";
|
|
DirectoryMode = "0700";
|
|
};
|
|
|
|
Install = { WantedBy = [ "sockets.target" ]; };
|
|
};
|
|
})
|
|
]);
|
|
}
|