mirror of
https://github.com/nix-community/home-manager.git
synced 2026-06-05 21:02:51 +00:00
`literalExpression` is intended just to signify code that needs to stay a string that gets represented exactly as-is for docs. It has been misused heavily and people get confused repeatedly on when or not to use it because of the rampant misuse.
376 lines
11 KiB
Nix
376 lines
11 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
let
|
|
inherit (lib)
|
|
literalExpression
|
|
mkDefault
|
|
mkIf
|
|
mkOption
|
|
optional
|
|
types
|
|
;
|
|
|
|
cfg = config.programs.gpg;
|
|
|
|
toKeyValue =
|
|
settings:
|
|
lib.generators.toKeyValue {
|
|
mkKeyValue =
|
|
key: value: if lib.isString value then "${key} ${value}" else lib.optionalString value key;
|
|
listsAsDuplicateKeys = true;
|
|
} settings;
|
|
|
|
primitiveType = types.oneOf [
|
|
types.str
|
|
types.bool
|
|
];
|
|
|
|
publicKeyOpts =
|
|
{ config, ... }:
|
|
{
|
|
options = {
|
|
text = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = ''
|
|
Text of an OpenPGP public key.
|
|
'';
|
|
};
|
|
|
|
source = mkOption {
|
|
type = types.path;
|
|
description = ''
|
|
Path of an OpenPGP public key file.
|
|
'';
|
|
};
|
|
|
|
trust = mkOption {
|
|
type = types.nullOr (
|
|
types.enum [
|
|
"unknown"
|
|
1
|
|
"never"
|
|
2
|
|
"marginal"
|
|
3
|
|
"full"
|
|
4
|
|
"ultimate"
|
|
5
|
|
]
|
|
);
|
|
default = null;
|
|
apply =
|
|
v:
|
|
if lib.isString v then
|
|
{
|
|
unknown = 1;
|
|
never = 2;
|
|
marginal = 3;
|
|
full = 4;
|
|
ultimate = 5;
|
|
}
|
|
.${v}
|
|
else
|
|
v;
|
|
description = ''
|
|
The amount of trust you have in the key ownership and the care the
|
|
owner puts into signing other keys. The available levels are
|
|
|
|
`unknown` or `1`
|
|
: I don't know or won't say.
|
|
|
|
`never` or `2`
|
|
: I do **not** trust.
|
|
|
|
`marginal` or `3`
|
|
: I trust marginally.
|
|
|
|
`full` or `4`
|
|
: I trust fully.
|
|
|
|
`ultimate` or `5`
|
|
: I trust ultimately.
|
|
|
|
See the [Key Management chapter](https://www.gnupg.org/gph/en/manual/x334.html)
|
|
of the GNU Privacy Handbook for more.
|
|
'';
|
|
};
|
|
};
|
|
|
|
config = {
|
|
source = mkIf (config.text != null) (pkgs.writeText "gpg-pubkey" config.text);
|
|
};
|
|
};
|
|
|
|
importTrustBashFunctions =
|
|
let
|
|
gpg = "${cfg.package}/bin/gpg";
|
|
in
|
|
''
|
|
function gpgKeyId() {
|
|
${gpg} --show-key --with-colons "$1" \
|
|
| grep ^pub: \
|
|
| cut -d: -f5
|
|
}
|
|
|
|
function importTrust() {
|
|
local keyIds trust
|
|
mapfile -t keyIds <<< "$(gpgKeyId "$1")"
|
|
trust="$2"
|
|
for id in "''${keyIds[@]}" ; do
|
|
{ echo trust; echo "$trust"; (( trust == 5 )) && echo y; echo quit; } \
|
|
| ${gpg} --no-tty --command-fd 0 --edit-key "$id"
|
|
done
|
|
}
|
|
|
|
'';
|
|
|
|
keyringFiles =
|
|
let
|
|
gpg = "${cfg.package}/bin/gpg";
|
|
|
|
importKey =
|
|
{ source, trust, ... }:
|
|
''
|
|
${gpg} --import ${source}
|
|
${lib.optionalString (trust != null) ''importTrust "${source}" ${toString trust}''}
|
|
'';
|
|
|
|
importKeys = lib.concatMapStringsSep "\n" importKey cfg.publicKeys;
|
|
in
|
|
pkgs.runCommand "gpg-pubring" { buildInputs = [ cfg.package ]; } ''
|
|
export GNUPGHOME
|
|
GNUPGHOME=$(mktemp -d)
|
|
|
|
${importTrustBashFunctions}
|
|
${importKeys}
|
|
|
|
mkdir $out
|
|
cp $GNUPGHOME/pubring.kbx $out/pubring.kbx
|
|
if [[ -e $GNUPGHOME/trustdb.gpg ]] ; then
|
|
cp $GNUPGHOME/trustdb.gpg $out/trustdb.gpg
|
|
fi
|
|
'';
|
|
|
|
in
|
|
{
|
|
options.programs.gpg = {
|
|
enable = lib.mkEnableOption "GnuPG";
|
|
|
|
package = lib.mkPackageOption pkgs "gnupg" {
|
|
example = "pkgs.gnupg23";
|
|
extraDescription = "Also used by the gpg-agent service.";
|
|
};
|
|
|
|
settings = mkOption {
|
|
type = types.attrsOf (types.either primitiveType (types.listOf types.str));
|
|
example = {
|
|
no-comments = false;
|
|
s2k-cipher-algo = "AES128";
|
|
};
|
|
description = ''
|
|
GnuPG configuration options. Available options are described
|
|
in
|
|
[
|
|
{manpage}`gpg(1)`
|
|
](https://gnupg.org/documentation/manpage.html).
|
|
|
|
Note that lists are converted to duplicate keys.
|
|
'';
|
|
};
|
|
|
|
scdaemonSettings = mkOption {
|
|
type = types.attrsOf (types.either primitiveType (types.listOf types.str));
|
|
default = { };
|
|
example = {
|
|
disable-ccid = true;
|
|
};
|
|
description = ''
|
|
SCdaemon configuration options. Available options are described
|
|
in
|
|
[
|
|
{manpage}`scdaemon(1)`
|
|
](https://www.gnupg.org/documentation/manuals/gnupg/Scdaemon-Options.html).
|
|
'';
|
|
};
|
|
|
|
dirmngrSettings = mkOption {
|
|
type = types.attrsOf (types.either primitiveType (types.listOf types.str));
|
|
default = { };
|
|
example = literalExpression ''
|
|
{
|
|
allow-version-check = true;
|
|
keyserver = "ldaps://ldap.example.com";
|
|
}
|
|
'';
|
|
description = ''
|
|
Dirmngr configuration options. Available options are described
|
|
in
|
|
[
|
|
{manpage}`dirmngr(1)`
|
|
](https://www.gnupg.org/documentation/manuals/gnupg/Dirmngr-Options.html)
|
|
'';
|
|
};
|
|
|
|
gpgsmSettings = mkOption {
|
|
type = types.attrsOf (types.either primitiveType (types.listOf types.str));
|
|
default = { };
|
|
example = {
|
|
with-key-data = true;
|
|
};
|
|
description = ''
|
|
GPGSM configuration options. Available options are described
|
|
in
|
|
[
|
|
{manpage}`gpgsm(1)`
|
|
](https://www.gnupg.org/documentation/manuals/gnupg/GPGSM-Options.html)
|
|
'';
|
|
};
|
|
|
|
homedir = mkOption {
|
|
type = types.path;
|
|
example = literalExpression ''"''${config.xdg.dataHome}/gnupg"'';
|
|
default = "${config.home.homeDirectory}/.gnupg";
|
|
defaultText = literalExpression ''"''${config.home.homeDirectory}/.gnupg"'';
|
|
description = "Directory to store keychains and configuration.";
|
|
};
|
|
|
|
mutableKeys = mkOption {
|
|
type = types.bool;
|
|
default = true;
|
|
description = ''
|
|
If set to `true`, you may manage your keyring as a user
|
|
using the `gpg` command. Upon activation, the keyring
|
|
will have managed keys added without overwriting unmanaged keys.
|
|
|
|
If set to `false`, the path
|
|
{file}`$GNUPGHOME/pubring.kbx` will become an immutable
|
|
link to the Nix store, denying modifications.
|
|
'';
|
|
};
|
|
|
|
mutableTrust = mkOption {
|
|
type = types.bool;
|
|
default = true;
|
|
description = ''
|
|
If set to `true`, you may manage trust as a user using
|
|
the {command}`gpg` command. Upon activation, trusted keys have
|
|
their trust set without overwriting unmanaged keys.
|
|
|
|
If set to `false`, the path
|
|
{file}`$GNUPGHOME/trustdb.gpg` will be
|
|
*overwritten* on each activation, removing trust for
|
|
any unmanaged keys. Be careful to make a backup of your old
|
|
{file}`trustdb.gpg` before switching to immutable trust!
|
|
'';
|
|
};
|
|
|
|
publicKeys = mkOption {
|
|
type = types.listOf (types.submodule publicKeyOpts);
|
|
example = literalExpression ''
|
|
[ { source = ./pubkeys.txt; } ]
|
|
'';
|
|
default = [ ];
|
|
description = ''
|
|
A list of public keys to be imported into GnuPG. Note, these key files
|
|
will be copied into the world-readable Nix store.
|
|
'';
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
programs.gpg.settings = {
|
|
personal-cipher-preferences = mkDefault "AES256 AES192 AES";
|
|
personal-digest-preferences = mkDefault "SHA512 SHA384 SHA256";
|
|
personal-compress-preferences = mkDefault "ZLIB BZIP2 ZIP Uncompressed";
|
|
default-preference-list = mkDefault "SHA512 SHA384 SHA256 AES256 AES192 AES ZLIB BZIP2 ZIP Uncompressed";
|
|
cert-digest-algo = mkDefault "SHA512";
|
|
s2k-digest-algo = mkDefault "SHA512";
|
|
s2k-cipher-algo = mkDefault "AES256";
|
|
display-charset = mkDefault "utf-8";
|
|
no-comments = mkDefault true;
|
|
no-emit-version = mkDefault true;
|
|
keyid-format = mkDefault "0xlong";
|
|
list-options = mkDefault "show-uid-validity";
|
|
verify-options = mkDefault "show-uid-validity";
|
|
with-fingerprint = mkDefault true;
|
|
require-cross-certification = mkDefault true;
|
|
no-symkey-cache = mkDefault true;
|
|
};
|
|
|
|
home.packages = [ cfg.package ];
|
|
home.sessionVariables = {
|
|
GNUPGHOME = cfg.homedir;
|
|
};
|
|
|
|
home.file."${cfg.homedir}/gpg.conf" = mkIf (cfg.settings != { }) {
|
|
text = toKeyValue cfg.settings;
|
|
};
|
|
|
|
home.file."${cfg.homedir}/scdaemon.conf" = mkIf (cfg.scdaemonSettings != { }) {
|
|
text = toKeyValue cfg.scdaemonSettings;
|
|
};
|
|
|
|
home.file."${cfg.homedir}/dirmngr.conf" = mkIf (cfg.dirmngrSettings != { }) {
|
|
text = toKeyValue cfg.dirmngrSettings;
|
|
};
|
|
|
|
home.file."${cfg.homedir}/gpgsm.conf" = mkIf (cfg.gpgsmSettings != { }) {
|
|
text = toKeyValue cfg.gpgsmSettings;
|
|
};
|
|
|
|
# Link keyring if keys are not mutable
|
|
home.file."${cfg.homedir}/pubring.kbx" = mkIf (!cfg.mutableKeys && cfg.publicKeys != [ ]) {
|
|
source = "${keyringFiles}/pubring.kbx";
|
|
};
|
|
|
|
home.activation = {
|
|
createGpgHomedir = lib.hm.dag.entryBetween [ "linkGeneration" ] [ "writeBoundary" ] ''
|
|
run mkdir -m700 -p $VERBOSE_ARG ${lib.escapeShellArg cfg.homedir}
|
|
'';
|
|
|
|
importGpgKeys =
|
|
let
|
|
gpg = "${cfg.package}/bin/gpg";
|
|
|
|
importKey =
|
|
{ source, trust, ... }:
|
|
# Import mutable keys
|
|
optional cfg.mutableKeys "run ${gpg} $QUIET_ARG --import ${source}"
|
|
|
|
# Import mutable trust
|
|
++ optional (trust != null && cfg.mutableTrust) ''run importTrust "${source}" ${toString trust}'';
|
|
|
|
anyTrust = lib.any (k: k.trust != null) cfg.publicKeys;
|
|
|
|
importKeys = lib.concatStringsSep "\n" (lib.concatMap importKey cfg.publicKeys);
|
|
|
|
# If any key/trust should be imported then create the block. Otherwise
|
|
# leave it empty.
|
|
block = lib.concatStringsSep "\n" (
|
|
optional (importKeys != "") ''
|
|
export GNUPGHOME=${lib.escapeShellArg cfg.homedir}
|
|
if [[ ! -v VERBOSE ]]; then
|
|
QUIET_ARG="--quiet"
|
|
else
|
|
QUIET_ARG=""
|
|
fi
|
|
${importTrustBashFunctions}
|
|
${importKeys}
|
|
unset GNUPGHOME QUIET_ARG keyId importTrust
|
|
''
|
|
++ optional (
|
|
!cfg.mutableTrust && anyTrust
|
|
) ''install -m 0700 ${keyringFiles}/trustdb.gpg "${cfg.homedir}/trustdb.gpg"''
|
|
);
|
|
in
|
|
mkIf (cfg.publicKeys != [ ]) (lib.hm.dag.entryAfter [ "linkGeneration" ] block);
|
|
};
|
|
};
|
|
}
|