mirror of
https://github.com/nix-community/home-manager
synced 2024-11-21 20:23:08 +00: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]
|
||||
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))
|
||||
'';
|
||||
|
||||
# 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
|
||||
# state, but we need the values at build time. original:
|
||||
# https://github.com/gpg/gnupg/blob/c6702d77d936b3e9d91b34d8fdee9599ab94ee1b/common/homedir.c#L672-L681
|
||||
|
@ -38,10 +38,14 @@ let
|
|||
let
|
||||
hash =
|
||||
substring 0 24 (hexStringToBase32 (builtins.hashString "sha1" homedir));
|
||||
in if homedir == options.programs.gpg.homedir.default then
|
||||
"%t/gnupg/${dir}"
|
||||
subdir = if homedir == options.programs.gpg.homedir.default then
|
||||
"${dir}"
|
||||
else
|
||||
"d.${hash}/${dir}";
|
||||
in if pkgs.stdenv.isDarwin then
|
||||
"/private/var/run/org.nix-community.home.gpg-agent/${subdir}"
|
||||
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.
|
||||
# Written in Nix for purity.
|
||||
|
@ -77,6 +81,32 @@ let
|
|||
};
|
||||
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 {
|
||||
meta.maintainers = [ maintainers.rycee ];
|
||||
|
||||
|
@ -272,90 +302,74 @@ in {
|
|||
'') 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)
|
||||
];
|
||||
(mkMerge [
|
||||
(mkIf pkgs.stdenv.isLinux {
|
||||
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;
|
||||
};
|
||||
|
||||
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}" ];
|
||||
};
|
||||
};
|
||||
|
||||
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)";
|
||||
systemd.user.sockets.gpg-agent = mkSocket {
|
||||
desc = "GnuPG cryptographic agent and passphrase cache";
|
||||
docs = "man:gpg-agent(1)";
|
||||
stream = "S.gpg-agent";
|
||||
fdName = "std";
|
||||
};
|
||||
|
||||
Socket = {
|
||||
ListenStream = gpgconf "S.gpg-agent";
|
||||
FileDescriptorName = "std";
|
||||
SocketMode = "0600";
|
||||
DirectoryMode = "0700";
|
||||
systemd.user.sockets.gpg-agent-ssh = mkIf cfg.enableSshSupport
|
||||
(mkSocket ({
|
||||
desc = "GnuPG cryptographic agent (ssh-agent emulation)";
|
||||
docs =
|
||||
"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/zplug
|
||||
./modules/programs/zsh
|
||||
./modules/services/gpg-agent
|
||||
./modules/services/syncthing/common
|
||||
./modules/xresources
|
||||
] ++ lib.optionals isDarwin [
|
||||
|
@ -242,7 +243,6 @@ in import nmtSrc {
|
|||
./modules/services/fusuma
|
||||
./modules/services/git-sync
|
||||
./modules/services/glance
|
||||
./modules/services/gpg-agent
|
||||
./modules/services/gromit-mpx
|
||||
./modules/services/home-manager-auto-upgrade
|
||||
./modules/services/hypridle
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
with lib;
|
||||
|
||||
{
|
||||
mkIf pkgs.stdenv.isLinux {
|
||||
config = {
|
||||
services.gpg-agent.enable = true;
|
||||
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;
|
||||
|
||||
{
|
||||
let inherit (pkgs.stdenv) isDarwin;
|
||||
in {
|
||||
config = {
|
||||
services.gpg-agent.enable = true;
|
||||
services.gpg-agent.pinentryPackage = null; # Don't build pinentry package.
|
||||
programs.gpg = {
|
||||
enable = true;
|
||||
homedir = "/path/to/hash";
|
||||
package = config.lib.test.mkStubPackage { outPath = "@gpg@"; };
|
||||
};
|
||||
|
||||
test.stubs.gnupg = { };
|
||||
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}"
|
||||
if [[ $in != "%t/gnupg/d.wp4h7ks5zxy4dodqadgpbbpz/S.gpg-agent" ]]
|
||||
then
|
||||
|
|
Loading…
Reference in a new issue