at 25.11-pre 7.5 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8let 9 inherit (lib) 10 mkRemovedOptionModule 11 mkOption 12 mkPackageOption 13 types 14 mkIf 15 optionalString 16 ; 17 18 cfg = config.programs.gnupg; 19 20 agentSettingsFormat = pkgs.formats.keyValue { 21 mkKeyValue = lib.generators.mkKeyValueDefault { } " "; 22 }; 23in 24{ 25 imports = [ 26 (mkRemovedOptionModule [ 27 "programs" 28 "gnupg" 29 "agent" 30 "pinentryFlavor" 31 ] "Use programs.gnupg.agent.pinentryPackage instead") 32 ]; 33 34 options.programs.gnupg = { 35 package = mkPackageOption pkgs "gnupg" { }; 36 37 agent.enable = mkOption { 38 type = types.bool; 39 default = false; 40 description = '' 41 Enables GnuPG agent with socket-activation for every user session. 42 ''; 43 }; 44 45 agent.enableSSHSupport = mkOption { 46 type = types.bool; 47 default = false; 48 description = '' 49 Enable SSH agent support in GnuPG agent. Also sets SSH_AUTH_SOCK 50 environment variable correctly. This will disable socket-activation 51 and thus always start a GnuPG agent per user session. 52 ''; 53 }; 54 55 agent.enableExtraSocket = mkOption { 56 type = types.bool; 57 default = false; 58 description = '' 59 Enable extra socket for GnuPG agent. 60 ''; 61 }; 62 63 agent.enableBrowserSocket = mkOption { 64 type = types.bool; 65 default = false; 66 description = '' 67 Enable browser socket for GnuPG agent. 68 ''; 69 }; 70 71 agent.pinentryPackage = mkOption { 72 type = types.nullOr types.package; 73 example = lib.literalMD "pkgs.pinentry-gnome3"; 74 default = pkgs.pinentry-curses; 75 defaultText = lib.literalMD "matching the configured desktop environment or `pkgs.pinentry-curses`"; 76 description = '' 77 Which pinentry package to use. The path to the mainProgram as defined in 78 the package's meta attributes will be set in /etc/gnupg/gpg-agent.conf. 79 If not set by the user, it'll pick an appropriate flavor depending on the 80 system configuration (qt flavor for lxqt and plasma5, gtk2 for xfce, 81 gnome3 on all other systems with X enabled, curses otherwise). 82 ''; 83 }; 84 85 agent.settings = mkOption { 86 type = agentSettingsFormat.type; 87 default = { }; 88 example = { 89 default-cache-ttl = 600; 90 }; 91 description = '' 92 Configuration for /etc/gnupg/gpg-agent.conf. 93 See {manpage}`gpg-agent(1)` for supported options. 94 ''; 95 }; 96 97 dirmngr.enable = mkOption { 98 type = types.bool; 99 default = false; 100 description = '' 101 Enables GnuPG network certificate management daemon with socket-activation for every user session. 102 ''; 103 }; 104 }; 105 106 config = mkIf cfg.agent.enable { 107 programs.gnupg.agent.settings = mkIf (cfg.agent.pinentryPackage != null) { 108 pinentry-program = lib.getExe cfg.agent.pinentryPackage; 109 }; 110 111 environment.etc."gnupg/gpg-agent.conf".source = 112 agentSettingsFormat.generate "gpg-agent.conf" cfg.agent.settings; 113 114 # This overrides the systemd user unit shipped with the gnupg package 115 systemd.user.services.gpg-agent = { 116 unitConfig = { 117 Description = "GnuPG cryptographic agent and passphrase cache"; 118 Documentation = "man:gpg-agent(1)"; 119 Requires = [ "sockets.target" ]; 120 }; 121 serviceConfig = { 122 ExecStart = "${cfg.package}/bin/gpg-agent --supervised"; 123 ExecReload = "${cfg.package}/bin/gpgconf --reload gpg-agent"; 124 }; 125 }; 126 127 systemd.user.sockets.gpg-agent = { 128 unitConfig = { 129 Description = "GnuPG cryptographic agent and passphrase cache"; 130 Documentation = "man:gpg-agent(1)"; 131 }; 132 socketConfig = { 133 ListenStream = "%t/gnupg/S.gpg-agent"; 134 FileDescriptorName = "std"; 135 SocketMode = "0600"; 136 DirectoryMode = "0700"; 137 }; 138 wantedBy = [ "sockets.target" ]; 139 }; 140 141 systemd.user.sockets.gpg-agent-ssh = mkIf cfg.agent.enableSSHSupport { 142 unitConfig = { 143 Description = "GnuPG cryptographic agent (ssh-agent emulation)"; 144 Documentation = "man:gpg-agent(1) man:ssh-add(1) man:ssh-agent(1) man:ssh(1)"; 145 }; 146 socketConfig = { 147 ListenStream = "%t/gnupg/S.gpg-agent.ssh"; 148 FileDescriptorName = "ssh"; 149 Service = "gpg-agent.service"; 150 SocketMode = "0600"; 151 DirectoryMode = "0700"; 152 }; 153 wantedBy = [ "sockets.target" ]; 154 }; 155 156 systemd.user.sockets.gpg-agent-extra = mkIf cfg.agent.enableExtraSocket { 157 unitConfig = { 158 Description = "GnuPG cryptographic agent and passphrase cache (restricted)"; 159 Documentation = "man:gpg-agent(1)"; 160 }; 161 socketConfig = { 162 ListenStream = "%t/gnupg/S.gpg-agent.extra"; 163 FileDescriptorName = "extra"; 164 Service = "gpg-agent.service"; 165 SocketMode = "0600"; 166 DirectoryMode = "0700"; 167 }; 168 wantedBy = [ "sockets.target" ]; 169 }; 170 171 systemd.user.sockets.gpg-agent-browser = mkIf cfg.agent.enableBrowserSocket { 172 unitConfig = { 173 Description = "GnuPG cryptographic agent and passphrase cache (access for web browsers)"; 174 Documentation = "man:gpg-agent(1)"; 175 }; 176 socketConfig = { 177 ListenStream = "%t/gnupg/S.gpg-agent.browser"; 178 FileDescriptorName = "browser"; 179 Service = "gpg-agent.service"; 180 SocketMode = "0600"; 181 DirectoryMode = "0700"; 182 }; 183 wantedBy = [ "sockets.target" ]; 184 }; 185 186 systemd.user.services.dirmngr = mkIf cfg.dirmngr.enable { 187 unitConfig = { 188 Description = "GnuPG network certificate management daemon"; 189 Documentation = "man:dirmngr(8)"; 190 Requires = "dirmngr.socket"; 191 }; 192 serviceConfig = { 193 ExecStart = "${cfg.package}/bin/dirmngr --supervised"; 194 ExecReload = "${cfg.package}/bin/gpgconf --reload dirmngr"; 195 }; 196 }; 197 198 systemd.user.sockets.dirmngr = mkIf cfg.dirmngr.enable { 199 unitConfig = { 200 Description = "GnuPG network certificate management daemon"; 201 Documentation = "man:dirmngr(8)"; 202 }; 203 socketConfig = { 204 ListenStream = "%t/gnupg/S.dirmngr"; 205 SocketMode = "0600"; 206 DirectoryMode = "0700"; 207 }; 208 wantedBy = [ "sockets.target" ]; 209 }; 210 211 services.dbus.packages = mkIf (lib.elem "gnome3" (cfg.agent.pinentryPackage.flavors or [ ])) [ 212 pkgs.gcr 213 ]; 214 215 environment.systemPackages = [ cfg.package ]; 216 217 environment.interactiveShellInit = '' 218 # Bind gpg-agent to this TTY if gpg commands are used. 219 export GPG_TTY=$(tty) 220 ''; 221 222 programs.ssh.extraConfig = optionalString cfg.agent.enableSSHSupport '' 223 # The SSH agent protocol doesn't have support for changing TTYs; however we 224 # can simulate this with the `exec` feature of openssh (see ssh_config(5)) 225 # that hooks a command to the shell currently running the ssh program. 226 Match host * exec "${pkgs.runtimeShell} -c '${cfg.package}/bin/gpg-connect-agent --quiet updatestartuptty /bye >/dev/null 2>&1'" 227 ''; 228 229 environment.extraInit = mkIf cfg.agent.enableSSHSupport '' 230 if [ -z "$SSH_AUTH_SOCK" ]; then 231 export SSH_AUTH_SOCK=$(${cfg.package}/bin/gpgconf --list-dirs agent-ssh-socket) 232 fi 233 ''; 234 235 assertions = [ 236 { 237 assertion = cfg.agent.enableSSHSupport -> !config.programs.ssh.startAgent; 238 message = "You can't use ssh-agent and GnuPG agent with SSH support enabled at the same time!"; 239 } 240 ]; 241 }; 242}