mirror of
https://github.com/nix-community/home-manager
synced 2024-11-22 19:19:45 +01:00
gpg-agent: add launchd service agent and sockets
This adds a Darwin Launchd agent along with its sockets to make gpg-agent starts at load or whenever the sockets are needed. Fixes: https://github.com/nix-community/home-manager/issues/3864
This commit is contained in:
parent
c82fc8cf3f
commit
ef50612457
6 changed files with 151 additions and 87 deletions
|
@ -19,3 +19,6 @@ indent_style = tab
|
||||||
|
|
||||||
[*.md]
|
[*.md]
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.plist]
|
||||||
|
insert_final_newline = false
|
||||||
|
|
|
@ -30,7 +30,7 @@ let
|
||||||
$env.SSH_AUTH_SOCK = ($env.SSH_AUTH_SOCK? | default (${gpgPkg}/bin/gpgconf --list-dirs agent-ssh-socket))
|
$env.SSH_AUTH_SOCK = ($env.SSH_AUTH_SOCK? | default (${gpgPkg}/bin/gpgconf --list-dirs agent-ssh-socket))
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# mimic `gpgconf` output for use in `systemd` unit definitions.
|
# mimic `gpgconf` output for use in the service definitions.
|
||||||
# we cannot use `gpgconf` directly because it heavily depends on system
|
# we cannot use `gpgconf` directly because it heavily depends on system
|
||||||
# state, but we need the values at build time. original:
|
# state, but we need the values at build time. original:
|
||||||
# https://github.com/gpg/gnupg/blob/c6702d77d936b3e9d91b34d8fdee9599ab94ee1b/common/homedir.c#L672-L681
|
# https://github.com/gpg/gnupg/blob/c6702d77d936b3e9d91b34d8fdee9599ab94ee1b/common/homedir.c#L672-L681
|
||||||
|
@ -38,10 +38,14 @@ let
|
||||||
let
|
let
|
||||||
hash =
|
hash =
|
||||||
substring 0 24 (hexStringToBase32 (builtins.hashString "sha1" homedir));
|
substring 0 24 (hexStringToBase32 (builtins.hashString "sha1" homedir));
|
||||||
in if homedir == options.programs.gpg.homedir.default then
|
subdir = if homedir == options.programs.gpg.homedir.default then
|
||||||
"%t/gnupg/${dir}"
|
"${dir}"
|
||||||
|
else
|
||||||
|
"d.${hash}/${dir}";
|
||||||
|
in if pkgs.stdenv.isDarwin then
|
||||||
|
"/private/var/run/org.nix-community.home.gpg-agent/${subdir}"
|
||||||
else
|
else
|
||||||
"%t/gnupg/d.${hash}/${dir}";
|
"%t/gnupg/${subdir}";
|
||||||
|
|
||||||
# Act like `xxd -r -p | base32` but with z-base-32 alphabet and no trailing padding.
|
# Act like `xxd -r -p | base32` but with z-base-32 alphabet and no trailing padding.
|
||||||
# Written in Nix for purity.
|
# Written in Nix for purity.
|
||||||
|
@ -77,6 +81,32 @@ let
|
||||||
};
|
};
|
||||||
in hexString: (foldl' go initState (stringToCharacters hexString)).ret;
|
in hexString: (foldl' go initState (stringToCharacters hexString)).ret;
|
||||||
|
|
||||||
|
# Systemd socket unit generator.
|
||||||
|
mkSocket = { desc, docs, stream, fdName }: {
|
||||||
|
Unit = {
|
||||||
|
Description = desc;
|
||||||
|
Documentation = docs;
|
||||||
|
};
|
||||||
|
|
||||||
|
Socket = {
|
||||||
|
ListenStream = gpgconf "${stream}";
|
||||||
|
FileDescriptorName = "${fdName}";
|
||||||
|
Service = "gpg-agent.service";
|
||||||
|
SocketMode = "0600";
|
||||||
|
DirectoryMode = "0700";
|
||||||
|
};
|
||||||
|
|
||||||
|
Install = { WantedBy = [ "sockets.target" ]; };
|
||||||
|
};
|
||||||
|
|
||||||
|
# Launchd agent socket generator.
|
||||||
|
mkAgentSock = name: {
|
||||||
|
SockType = "stream";
|
||||||
|
SockPathName = gpgconf name;
|
||||||
|
SockPathMode =
|
||||||
|
384; # Property lists don't support octal literals (0600 = 384).
|
||||||
|
};
|
||||||
|
|
||||||
in {
|
in {
|
||||||
meta.maintainers = [ maintainers.rycee ];
|
meta.maintainers = [ maintainers.rycee ];
|
||||||
|
|
||||||
|
@ -272,90 +302,74 @@ in {
|
||||||
'') cfg.sshKeys;
|
'') cfg.sshKeys;
|
||||||
})
|
})
|
||||||
|
|
||||||
# The systemd units below are direct translations of the
|
(mkMerge [
|
||||||
# descriptions in the
|
(mkIf pkgs.stdenv.isLinux {
|
||||||
#
|
systemd.user.services.gpg-agent = {
|
||||||
# ${gpgPkg}/share/doc/gnupg/examples/systemd-user
|
Unit = {
|
||||||
#
|
Description = "GnuPG cryptographic agent and passphrase cache";
|
||||||
# directory.
|
Documentation = "man:gpg-agent(1)";
|
||||||
{
|
Requires = "gpg-agent.socket";
|
||||||
assertions = [
|
After = "gpg-agent.socket";
|
||||||
(hm.assertions.assertPlatform "services.gpg-agent" pkgs platforms.linux)
|
# This is a socket-activated service:
|
||||||
];
|
RefuseManualStart = true;
|
||||||
|
};
|
||||||
|
|
||||||
systemd.user.services.gpg-agent = {
|
Service = {
|
||||||
Unit = {
|
ExecStart = "${gpgPkg}/bin/gpg-agent --supervised"
|
||||||
Description = "GnuPG cryptographic agent and passphrase cache";
|
+ optionalString cfg.verbose " --verbose";
|
||||||
Documentation = "man:gpg-agent(1)";
|
ExecReload = "${gpgPkg}/bin/gpgconf --reload gpg-agent";
|
||||||
Requires = "gpg-agent.socket";
|
Environment = [ "GNUPGHOME=${homedir}" ];
|
||||||
After = "gpg-agent.socket";
|
};
|
||||||
# This is a socket-activated service:
|
|
||||||
RefuseManualStart = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Service = {
|
systemd.user.sockets.gpg-agent = mkSocket {
|
||||||
ExecStart = "${gpgPkg}/bin/gpg-agent --supervised"
|
desc = "GnuPG cryptographic agent and passphrase cache";
|
||||||
+ optionalString cfg.verbose " --verbose";
|
docs = "man:gpg-agent(1)";
|
||||||
ExecReload = "${gpgPkg}/bin/gpgconf --reload gpg-agent";
|
stream = "S.gpg-agent";
|
||||||
Environment = [ "GNUPGHOME=${homedir}" ];
|
fdName = "std";
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.user.sockets.gpg-agent = {
|
|
||||||
Unit = {
|
|
||||||
Description = "GnuPG cryptographic agent and passphrase cache";
|
|
||||||
Documentation = "man:gpg-agent(1)";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Socket = {
|
systemd.user.sockets.gpg-agent-ssh = mkIf cfg.enableSshSupport
|
||||||
ListenStream = gpgconf "S.gpg-agent";
|
(mkSocket ({
|
||||||
FileDescriptorName = "std";
|
desc = "GnuPG cryptographic agent (ssh-agent emulation)";
|
||||||
SocketMode = "0600";
|
docs =
|
||||||
DirectoryMode = "0700";
|
"man:gpg-agent(1) man:ssh-add(1) man:ssh-agent(1) man:ssh(1)";
|
||||||
|
stream = "S.gpg-agent.ssh";
|
||||||
|
fdName = "ssh";
|
||||||
|
}));
|
||||||
|
|
||||||
|
systemd.user.sockets.gpg-agent-extra = mkIf cfg.enableExtraSocket
|
||||||
|
(mkSocket {
|
||||||
|
desc =
|
||||||
|
"GnuPG cryptographic agent and passphrase cache (restricted)";
|
||||||
|
docs = "man:gpg-agent(1) man:ssh(1)";
|
||||||
|
stream = "S.gpg-agent.extra";
|
||||||
|
fdName = "extra";
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
(mkIf pkgs.stdenv.isDarwin {
|
||||||
|
launchd.agents.gpg-agent = {
|
||||||
|
enable = true;
|
||||||
|
config = {
|
||||||
|
ProgramArguments = [ "${gpgPkg}/bin/gpg-agent" "--supervised" ]
|
||||||
|
++ optional cfg.verbose "--verbose";
|
||||||
|
EnvironmentVariables = { GNUPGHOME = homedir; };
|
||||||
|
KeepAlive = {
|
||||||
|
Crashed = true;
|
||||||
|
SuccessfulExit = false;
|
||||||
|
};
|
||||||
|
ProcessType = "Background";
|
||||||
|
RunAtLoad = cfg.enableSshSupport;
|
||||||
|
Sockets = {
|
||||||
|
Agent = mkAgentSock "S.gpg-agent";
|
||||||
|
Ssh = mkIf cfg.enableSshSupport (mkAgentSock "S.gpg-agent.ssh");
|
||||||
|
Extra =
|
||||||
|
mkIf cfg.enableExtraSocket (mkAgentSock "S.gpg-agent.extra");
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
})
|
||||||
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" ]; };
|
|
||||||
};
|
|
||||||
})
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,6 +162,7 @@ in import nmtSrc {
|
||||||
./modules/programs/zk
|
./modules/programs/zk
|
||||||
./modules/programs/zplug
|
./modules/programs/zplug
|
||||||
./modules/programs/zsh
|
./modules/programs/zsh
|
||||||
|
./modules/services/gpg-agent
|
||||||
./modules/services/syncthing/common
|
./modules/services/syncthing/common
|
||||||
./modules/xresources
|
./modules/xresources
|
||||||
] ++ lib.optionals isDarwin [
|
] ++ lib.optionals isDarwin [
|
||||||
|
@ -242,7 +243,6 @@ in import nmtSrc {
|
||||||
./modules/services/fusuma
|
./modules/services/fusuma
|
||||||
./modules/services/git-sync
|
./modules/services/git-sync
|
||||||
./modules/services/glance
|
./modules/services/glance
|
||||||
./modules/services/gpg-agent
|
|
||||||
./modules/services/gromit-mpx
|
./modules/services/gromit-mpx
|
||||||
./modules/services/home-manager-auto-upgrade
|
./modules/services/home-manager-auto-upgrade
|
||||||
./modules/services/hypridle
|
./modules/services/hypridle
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
with lib;
|
with lib;
|
||||||
|
|
||||||
{
|
mkIf pkgs.stdenv.isLinux {
|
||||||
config = {
|
config = {
|
||||||
services.gpg-agent.enable = true;
|
services.gpg-agent.enable = true;
|
||||||
services.gpg-agent.pinentryPackage = pkgs.pinentry-gnome3;
|
services.gpg-agent.pinentryPackage = pkgs.pinentry-gnome3;
|
||||||
|
|
41
tests/modules/services/gpg-agent/expected-agent.plist
Normal file
41
tests/modules/services/gpg-agent/expected-agent.plist
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>EnvironmentVariables</key>
|
||||||
|
<dict>
|
||||||
|
<key>GNUPGHOME</key>
|
||||||
|
<string>/path/to/hash</string>
|
||||||
|
</dict>
|
||||||
|
<key>KeepAlive</key>
|
||||||
|
<dict>
|
||||||
|
<key>Crashed</key>
|
||||||
|
<true/>
|
||||||
|
<key>SuccessfulExit</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
<key>Label</key>
|
||||||
|
<string>org.nix-community.home.gpg-agent</string>
|
||||||
|
<key>ProcessType</key>
|
||||||
|
<string>Background</string>
|
||||||
|
<key>ProgramArguments</key>
|
||||||
|
<array>
|
||||||
|
<string>@gpg@/bin/gpg-agent</string>
|
||||||
|
<string>--supervised</string>
|
||||||
|
</array>
|
||||||
|
<key>RunAtLoad</key>
|
||||||
|
<false/>
|
||||||
|
<key>Sockets</key>
|
||||||
|
<dict>
|
||||||
|
<key>Agent</key>
|
||||||
|
<dict>
|
||||||
|
<key>SockPathMode</key>
|
||||||
|
<integer>384</integer>
|
||||||
|
<key>SockPathName</key>
|
||||||
|
<string>/private/var/run/org.nix-community.home.gpg-agent/d.wp4h7ks5zxy4dodqadgpbbpz/S.gpg-agent</string>
|
||||||
|
<key>SockType</key>
|
||||||
|
<string>stream</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -2,19 +2,25 @@
|
||||||
|
|
||||||
with lib;
|
with lib;
|
||||||
|
|
||||||
{
|
let inherit (pkgs.stdenv) isDarwin;
|
||||||
|
in {
|
||||||
config = {
|
config = {
|
||||||
services.gpg-agent.enable = true;
|
services.gpg-agent.enable = true;
|
||||||
services.gpg-agent.pinentryPackage = null; # Don't build pinentry package.
|
services.gpg-agent.pinentryPackage = null; # Don't build pinentry package.
|
||||||
programs.gpg = {
|
programs.gpg = {
|
||||||
enable = true;
|
enable = true;
|
||||||
homedir = "/path/to/hash";
|
homedir = "/path/to/hash";
|
||||||
|
package = config.lib.test.mkStubPackage { outPath = "@gpg@"; };
|
||||||
};
|
};
|
||||||
|
|
||||||
test.stubs.gnupg = { };
|
test.stubs.gnupg = { };
|
||||||
test.stubs.systemd = { }; # depends on gnupg.override
|
test.stubs.systemd = { }; # depends on gnupg.override
|
||||||
|
|
||||||
nmt.script = ''
|
nmt.script = if isDarwin then ''
|
||||||
|
serviceFile=LaunchAgents/org.nix-community.home.gpg-agent.plist
|
||||||
|
assertFileExists "$serviceFile"
|
||||||
|
assertFileContent "$serviceFile" ${./expected-agent.plist}
|
||||||
|
'' else ''
|
||||||
in="${config.systemd.user.sockets.gpg-agent.Socket.ListenStream}"
|
in="${config.systemd.user.sockets.gpg-agent.Socket.ListenStream}"
|
||||||
if [[ $in != "%t/gnupg/d.wp4h7ks5zxy4dodqadgpbbpz/S.gpg-agent" ]]
|
if [[ $in != "%t/gnupg/d.wp4h7ks5zxy4dodqadgpbbpz/S.gpg-agent" ]]
|
||||||
then
|
then
|
||||||
|
|
Loading…
Reference in a new issue