format nix with rfc style
This commit is contained in:
parent
856df6f692
commit
0814fdc0de
13 changed files with 500 additions and 453 deletions
4
.github/workflows/ci.yaml
vendored
4
.github/workflows/ci.yaml
vendored
|
|
@ -14,7 +14,7 @@ jobs:
|
|||
extra-experimental-features = recursive-nix nix-command flakes
|
||||
- run: nix build
|
||||
- run: nix build .#doc
|
||||
- run: nix fmt . -- --check
|
||||
- run: nix fmt . -- --ci
|
||||
- run: nix flake check
|
||||
tests-darwin:
|
||||
runs-on: macos-latest
|
||||
|
|
@ -27,7 +27,7 @@ jobs:
|
|||
extra-experimental-features = recursive-nix nix-command flakes
|
||||
- run: nix build
|
||||
- run: nix build .#doc
|
||||
- run: nix fmt . -- --check
|
||||
- run: nix fmt . -- --ci
|
||||
- run: nix flake check
|
||||
- name: "Install nix-darwin module"
|
||||
run: |
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
{pkgs ? import <nixpkgs> {}}: {
|
||||
agenix = pkgs.callPackage ./pkgs/agenix.nix {};
|
||||
{
|
||||
pkgs ? import <nixpkgs> { },
|
||||
}:
|
||||
{
|
||||
agenix = pkgs.callPackage ./pkgs/agenix.nix { };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,23 @@
|
|||
let
|
||||
user1 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL0idNvgGiucWgup/mP78zyC23uFjYq0evcWdjGQUaBH";
|
||||
system1 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPJDyIr/FSz1cJdcoW69R+NrWzwGK/+3gJpqD1t8L2zE";
|
||||
in {
|
||||
"secret1.age".publicKeys = [user1 system1];
|
||||
"secret2.age".publicKeys = [user1];
|
||||
"passwordfile-user1.age".publicKeys = [user1 system1];
|
||||
"-leading-hyphen-filename.age".publicKeys = [user1 system1];
|
||||
in
|
||||
{
|
||||
"secret1.age".publicKeys = [
|
||||
user1
|
||||
system1
|
||||
];
|
||||
"secret2.age".publicKeys = [ user1 ];
|
||||
"passwordfile-user1.age".publicKeys = [
|
||||
user1
|
||||
system1
|
||||
];
|
||||
"-leading-hyphen-filename.age".publicKeys = [
|
||||
user1
|
||||
system1
|
||||
];
|
||||
"armored-secret.age" = {
|
||||
publicKeys = [user1];
|
||||
publicKeys = [ user1 ];
|
||||
armor = true;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
124
flake.nix
124
flake.nix
|
|
@ -14,76 +14,78 @@
|
|||
systems.url = "github:nix-systems/default";
|
||||
};
|
||||
|
||||
outputs = {
|
||||
self,
|
||||
nixpkgs,
|
||||
darwin,
|
||||
home-manager,
|
||||
systems,
|
||||
}: let
|
||||
eachSystem = nixpkgs.lib.genAttrs (import systems);
|
||||
in {
|
||||
nixosModules.age = ./modules/age.nix;
|
||||
nixosModules.default = self.nixosModules.age;
|
||||
outputs =
|
||||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
darwin,
|
||||
home-manager,
|
||||
systems,
|
||||
}:
|
||||
let
|
||||
eachSystem = nixpkgs.lib.genAttrs (import systems);
|
||||
in
|
||||
{
|
||||
nixosModules.age = ./modules/age.nix;
|
||||
nixosModules.default = self.nixosModules.age;
|
||||
|
||||
darwinModules.age = ./modules/age.nix;
|
||||
darwinModules.default = self.darwinModules.age;
|
||||
darwinModules.age = ./modules/age.nix;
|
||||
darwinModules.default = self.darwinModules.age;
|
||||
|
||||
homeManagerModules.age = ./modules/age-home.nix;
|
||||
homeManagerModules.default = self.homeManagerModules.age;
|
||||
homeManagerModules.age = ./modules/age-home.nix;
|
||||
homeManagerModules.default = self.homeManagerModules.age;
|
||||
|
||||
overlays.default = import ./overlay.nix;
|
||||
overlays.default = import ./overlay.nix;
|
||||
|
||||
formatter = eachSystem (system: nixpkgs.legacyPackages.${system}.alejandra);
|
||||
formatter = eachSystem (system: nixpkgs.legacyPackages.${system}.nixfmt-tree);
|
||||
|
||||
packages = eachSystem (system: {
|
||||
agenix = nixpkgs.legacyPackages.${system}.callPackage ./pkgs/agenix.nix {};
|
||||
doc = nixpkgs.legacyPackages.${system}.callPackage ./pkgs/doc.nix {inherit self;};
|
||||
default = self.packages.${system}.agenix;
|
||||
});
|
||||
packages = eachSystem (system: {
|
||||
agenix = nixpkgs.legacyPackages.${system}.callPackage ./pkgs/agenix.nix { };
|
||||
doc = nixpkgs.legacyPackages.${system}.callPackage ./pkgs/doc.nix { inherit self; };
|
||||
default = self.packages.${system}.agenix;
|
||||
});
|
||||
|
||||
checks =
|
||||
nixpkgs.lib.genAttrs ["aarch64-darwin" "x86_64-darwin"] (system: {
|
||||
integration =
|
||||
(darwin.lib.darwinSystem {
|
||||
inherit system;
|
||||
modules = [
|
||||
./test/integration_darwin.nix
|
||||
checks =
|
||||
nixpkgs.lib.genAttrs [ "aarch64-darwin" "x86_64-darwin" ] (system: {
|
||||
integration =
|
||||
(darwin.lib.darwinSystem {
|
||||
inherit system;
|
||||
modules = [
|
||||
./test/integration_darwin.nix
|
||||
|
||||
# Allow new-style nix commands in CI
|
||||
{nix.extraOptions = "experimental-features = nix-command flakes";}
|
||||
# Allow new-style nix commands in CI
|
||||
{ nix.extraOptions = "experimental-features = nix-command flakes"; }
|
||||
|
||||
home-manager.darwinModules.home-manager
|
||||
{
|
||||
home-manager = {
|
||||
verbose = true;
|
||||
useGlobalPkgs = true;
|
||||
useUserPackages = true;
|
||||
backupFileExtension = "hmbak";
|
||||
users.runner = ./test/integration_hm_darwin.nix;
|
||||
};
|
||||
}
|
||||
];
|
||||
})
|
||||
.system;
|
||||
})
|
||||
// {
|
||||
x86_64-linux.integration = import ./test/integration.nix {
|
||||
inherit nixpkgs home-manager;
|
||||
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
||||
system = "x86_64-linux";
|
||||
home-manager.darwinModules.home-manager
|
||||
{
|
||||
home-manager = {
|
||||
verbose = true;
|
||||
useGlobalPkgs = true;
|
||||
useUserPackages = true;
|
||||
backupFileExtension = "hmbak";
|
||||
users.runner = ./test/integration_hm_darwin.nix;
|
||||
};
|
||||
}
|
||||
];
|
||||
}).system;
|
||||
})
|
||||
// {
|
||||
x86_64-linux.integration = import ./test/integration.nix {
|
||||
inherit nixpkgs home-manager;
|
||||
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
||||
system = "x86_64-linux";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
darwinConfigurations.integration-x86_64.system = self.checks.x86_64-darwin.integration;
|
||||
darwinConfigurations.integration-aarch64.system = self.checks.aarch64-darwin.integration;
|
||||
darwinConfigurations.integration-x86_64.system = self.checks.x86_64-darwin.integration;
|
||||
darwinConfigurations.integration-aarch64.system = self.checks.aarch64-darwin.integration;
|
||||
|
||||
# Work-around for https://github.com/nix-community/home-manager/issues/3075
|
||||
legacyPackages = nixpkgs.lib.genAttrs ["aarch64-darwin" "x86_64-darwin"] (system: {
|
||||
homeConfigurations.integration-darwin = home-manager.lib.homeManagerConfiguration {
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
modules = [./test/integration_hm_darwin.nix];
|
||||
};
|
||||
});
|
||||
};
|
||||
# Work-around for https://github.com/nix-community/home-manager/issues/3075
|
||||
legacyPackages = nixpkgs.lib.genAttrs [ "aarch64-darwin" "x86_64-darwin" ] (system: {
|
||||
homeConfigurations.integration-darwin = home-manager.lib.homeManagerConfiguration {
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
modules = [ ./test/integration_hm_darwin.nix ];
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@
|
|||
pkgs,
|
||||
...
|
||||
}:
|
||||
with lib; let
|
||||
with lib;
|
||||
let
|
||||
cfg = config.age;
|
||||
|
||||
ageBin = lib.getExe config.age.package;
|
||||
|
|
@ -22,13 +23,14 @@ with lib; let
|
|||
|
||||
setTruePath = secretType: ''
|
||||
${
|
||||
if secretType.symlink
|
||||
then ''
|
||||
_truePath="${cfg.secretsMountPoint}/$_agenix_generation/${secretType.name}"
|
||||
''
|
||||
else ''
|
||||
_truePath="${secretType.path}"
|
||||
''
|
||||
if secretType.symlink then
|
||||
''
|
||||
_truePath="${cfg.secretsMountPoint}/$_agenix_generation/${secretType.name}"
|
||||
''
|
||||
else
|
||||
''
|
||||
_truePath="${secretType.path}"
|
||||
''
|
||||
}
|
||||
'';
|
||||
|
||||
|
|
@ -54,7 +56,9 @@ with lib; let
|
|||
umask u=r,g=,o=
|
||||
test -f "${secretType.file}" || echo '[agenix] WARNING: encrypted file ${secretType.file} does not exist!'
|
||||
test -d "$(dirname "$TMP_FILE")" || echo "[agenix] WARNING: $(dirname "$TMP_FILE") does not exist!"
|
||||
LANG=${config.i18n.defaultLocale or "C"} ${ageBin} --decrypt "''${IDENTITIES[@]}" -o "$TMP_FILE" "${secretType.file}"
|
||||
LANG=${
|
||||
config.i18n.defaultLocale or "C"
|
||||
} ${ageBin} --decrypt "''${IDENTITIES[@]}" -o "$TMP_FILE" "${secretType.file}"
|
||||
)
|
||||
chmod ${secretType.mode} "$TMP_FILE"
|
||||
mv -f "$TMP_FILE" "$_truePath"
|
||||
|
|
@ -65,12 +69,9 @@ with lib; let
|
|||
''}
|
||||
'';
|
||||
|
||||
testIdentities =
|
||||
map
|
||||
(path: ''
|
||||
test -f ${path} || echo '[agenix] WARNING: config.age.identityPaths entry ${path} not present!'
|
||||
'')
|
||||
cfg.identityPaths;
|
||||
testIdentities = map (path: ''
|
||||
test -f ${path} || echo '[agenix] WARNING: config.age.identityPaths entry ${path} not present!'
|
||||
'') cfg.identityPaths;
|
||||
|
||||
cleanupAndLink = ''
|
||||
_agenix_generation="$(basename "$(readlink "${cfg.secretsDir}")" || echo 0)"
|
||||
|
|
@ -85,81 +86,89 @@ with lib; let
|
|||
'';
|
||||
|
||||
installSecrets = builtins.concatStringsSep "\n" (
|
||||
["echo '[agenix] decrypting secrets...'"]
|
||||
[ "echo '[agenix] decrypting secrets...'" ]
|
||||
++ testIdentities
|
||||
++ (map installSecret (builtins.attrValues cfg.secrets))
|
||||
++ [cleanupAndLink]
|
||||
++ [ cleanupAndLink ]
|
||||
);
|
||||
|
||||
secretType = types.submodule ({
|
||||
config,
|
||||
name,
|
||||
...
|
||||
}: {
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
default = name;
|
||||
description = ''
|
||||
Name of the file used in ''${cfg.secretsDir}
|
||||
'';
|
||||
secretType = types.submodule (
|
||||
{
|
||||
config,
|
||||
name,
|
||||
...
|
||||
}:
|
||||
{
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
default = name;
|
||||
description = ''
|
||||
Name of the file used in ''${cfg.secretsDir}
|
||||
'';
|
||||
};
|
||||
file = mkOption {
|
||||
type = types.path;
|
||||
description = ''
|
||||
Age file the secret is loaded from.
|
||||
'';
|
||||
};
|
||||
path = mkOption {
|
||||
type = types.str;
|
||||
default = "${cfg.secretsDir}/${config.name}";
|
||||
description = ''
|
||||
Path where the decrypted secret is installed.
|
||||
'';
|
||||
};
|
||||
mode = mkOption {
|
||||
type = types.str;
|
||||
default = "0400";
|
||||
description = ''
|
||||
Permissions mode of the decrypted secret in a format understood by chmod.
|
||||
'';
|
||||
};
|
||||
symlink = mkEnableOption "symlinking secrets to their destination" // {
|
||||
default = true;
|
||||
};
|
||||
};
|
||||
file = mkOption {
|
||||
type = types.path;
|
||||
description = ''
|
||||
Age file the secret is loaded from.
|
||||
'';
|
||||
};
|
||||
path = mkOption {
|
||||
type = types.str;
|
||||
default = "${cfg.secretsDir}/${config.name}";
|
||||
description = ''
|
||||
Path where the decrypted secret is installed.
|
||||
'';
|
||||
};
|
||||
mode = mkOption {
|
||||
type = types.str;
|
||||
default = "0400";
|
||||
description = ''
|
||||
Permissions mode of the decrypted secret in a format understood by chmod.
|
||||
'';
|
||||
};
|
||||
symlink = mkEnableOption "symlinking secrets to their destination" // {default = true;};
|
||||
};
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
mountingScript = let
|
||||
app = pkgs.writeShellApplication {
|
||||
name = "agenix-home-manager-mount-secrets";
|
||||
runtimeInputs = with pkgs; [coreutils];
|
||||
text = ''
|
||||
${newGeneration}
|
||||
${installSecrets}
|
||||
exit 0
|
||||
'';
|
||||
};
|
||||
in
|
||||
mountingScript =
|
||||
let
|
||||
app = pkgs.writeShellApplication {
|
||||
name = "agenix-home-manager-mount-secrets";
|
||||
runtimeInputs = with pkgs; [ coreutils ];
|
||||
text = ''
|
||||
${newGeneration}
|
||||
${installSecrets}
|
||||
exit 0
|
||||
'';
|
||||
};
|
||||
in
|
||||
lib.getExe app;
|
||||
|
||||
userDirectory = dir: let
|
||||
inherit (pkgs.stdenv.hostPlatform) isDarwin;
|
||||
baseDir =
|
||||
if isDarwin
|
||||
then "$(getconf DARWIN_USER_TEMP_DIR)"
|
||||
else "\${XDG_RUNTIME_DIR}";
|
||||
in "${baseDir}/${dir}";
|
||||
userDirectory =
|
||||
dir:
|
||||
let
|
||||
inherit (pkgs.stdenv.hostPlatform) isDarwin;
|
||||
baseDir = if isDarwin then "$(getconf DARWIN_USER_TEMP_DIR)" else "\${XDG_RUNTIME_DIR}";
|
||||
in
|
||||
"${baseDir}/${dir}";
|
||||
|
||||
userDirectoryDescription = dir:
|
||||
userDirectoryDescription =
|
||||
dir:
|
||||
literalExpression ''
|
||||
"''${XDG_RUNTIME_DIR}"/''${dir} on linux or "$(getconf DARWIN_USER_TEMP_DIR)"/''${dir} on darwin.
|
||||
'';
|
||||
in {
|
||||
in
|
||||
{
|
||||
options.age = {
|
||||
package = mkPackageOption pkgs "age" {};
|
||||
package = mkPackageOption pkgs "age" { };
|
||||
|
||||
secrets = mkOption {
|
||||
type = types.attrsOf secretType;
|
||||
default = {};
|
||||
default = { };
|
||||
description = ''
|
||||
Attrset of secrets.
|
||||
'';
|
||||
|
|
@ -200,10 +209,10 @@ in {
|
|||
};
|
||||
};
|
||||
|
||||
config = mkIf (cfg.secrets != {}) {
|
||||
config = mkIf (cfg.secrets != { }) {
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.identityPaths != [];
|
||||
assertion = cfg.identityPaths != [ ];
|
||||
message = "age.identityPaths must be set.";
|
||||
}
|
||||
];
|
||||
|
|
@ -216,13 +225,13 @@ in {
|
|||
Type = "oneshot";
|
||||
ExecStart = mountingScript;
|
||||
};
|
||||
Install.WantedBy = ["default.target"];
|
||||
Install.WantedBy = [ "default.target" ];
|
||||
};
|
||||
|
||||
launchd.agents.activate-agenix = {
|
||||
enable = true;
|
||||
config = {
|
||||
ProgramArguments = [mountingScript];
|
||||
ProgramArguments = [ mountingScript ];
|
||||
KeepAlive = {
|
||||
Crashed = false;
|
||||
SuccessfulExit = false;
|
||||
|
|
|
|||
255
modules/age.nix
255
modules/age.nix
|
|
@ -5,34 +5,37 @@
|
|||
pkgs,
|
||||
...
|
||||
}:
|
||||
with lib; let
|
||||
with lib;
|
||||
let
|
||||
cfg = config.age;
|
||||
|
||||
isDarwin = lib.attrsets.hasAttrByPath ["environment" "darwinConfig"] options;
|
||||
isDarwin = lib.attrsets.hasAttrByPath [ "environment" "darwinConfig" ] options;
|
||||
|
||||
ageBin = config.age.ageBin;
|
||||
|
||||
users = config.users.users;
|
||||
|
||||
sysusersEnabled =
|
||||
if isDarwin
|
||||
then false
|
||||
else options.systemd ? sysusers && (config.systemd.sysusers.enable || config.services.userborn.enable);
|
||||
if isDarwin then
|
||||
false
|
||||
else
|
||||
options.systemd ? sysusers && (config.systemd.sysusers.enable || config.services.userborn.enable);
|
||||
|
||||
mountCommand =
|
||||
if isDarwin
|
||||
then ''
|
||||
if ! diskutil info "${cfg.secretsMountPoint}" &> /dev/null; then
|
||||
num_sectors=1048576
|
||||
dev=$(hdiutil attach -nomount ram://"$num_sectors" | sed 's/[[:space:]]*$//')
|
||||
newfs_hfs -v agenix "$dev"
|
||||
mount -t hfs -o nobrowse,nodev,nosuid,-m=0751 "$dev" "${cfg.secretsMountPoint}"
|
||||
fi
|
||||
''
|
||||
else ''
|
||||
grep -q "${cfg.secretsMountPoint} ramfs" /proc/mounts ||
|
||||
mount -t ramfs none "${cfg.secretsMountPoint}" -o nodev,nosuid,mode=0751
|
||||
'';
|
||||
if isDarwin then
|
||||
''
|
||||
if ! diskutil info "${cfg.secretsMountPoint}" &> /dev/null; then
|
||||
num_sectors=1048576
|
||||
dev=$(hdiutil attach -nomount ram://"$num_sectors" | sed 's/[[:space:]]*$//')
|
||||
newfs_hfs -v agenix "$dev"
|
||||
mount -t hfs -o nobrowse,nodev,nosuid,-m=0751 "$dev" "${cfg.secretsMountPoint}"
|
||||
fi
|
||||
''
|
||||
else
|
||||
''
|
||||
grep -q "${cfg.secretsMountPoint} ramfs" /proc/mounts ||
|
||||
mount -t ramfs none "${cfg.secretsMountPoint}" -o nodev,nosuid,mode=0751
|
||||
'';
|
||||
newGeneration = ''
|
||||
_agenix_generation="$(basename "$(readlink ${cfg.secretsDir})" || echo 0)"
|
||||
(( ++_agenix_generation ))
|
||||
|
|
@ -44,10 +47,7 @@ with lib; let
|
|||
chmod 0751 "${cfg.secretsMountPoint}/$_agenix_generation"
|
||||
'';
|
||||
|
||||
chownGroup =
|
||||
if isDarwin
|
||||
then "admin"
|
||||
else "keys";
|
||||
chownGroup = if isDarwin then "admin" else "keys";
|
||||
# chown the secrets mountpoint and the current generation to the keys group
|
||||
# instead of leaving it root:root.
|
||||
chownMountPoint = ''
|
||||
|
|
@ -56,13 +56,14 @@ with lib; let
|
|||
|
||||
setTruePath = secretType: ''
|
||||
${
|
||||
if secretType.symlink
|
||||
then ''
|
||||
_truePath="${cfg.secretsMountPoint}/$_agenix_generation/${secretType.name}"
|
||||
''
|
||||
else ''
|
||||
_truePath="${secretType.path}"
|
||||
''
|
||||
if secretType.symlink then
|
||||
''
|
||||
_truePath="${cfg.secretsMountPoint}/$_agenix_generation/${secretType.name}"
|
||||
''
|
||||
else
|
||||
''
|
||||
_truePath="${secretType.path}"
|
||||
''
|
||||
}
|
||||
'';
|
||||
|
||||
|
|
@ -87,7 +88,9 @@ with lib; let
|
|||
umask u=r,g=,o=
|
||||
test -f "${secretType.file}" || echo '[agenix] WARNING: encrypted file ${secretType.file} does not exist!'
|
||||
test -d "$(dirname "$TMP_FILE")" || echo "[agenix] WARNING: $(dirname "$TMP_FILE") does not exist!"
|
||||
LANG=${config.i18n.defaultLocale or "C"} ${ageBin} --decrypt "''${IDENTITIES[@]}" -o "$TMP_FILE" "${secretType.file}"
|
||||
LANG=${
|
||||
config.i18n.defaultLocale or "C"
|
||||
} ${ageBin} --decrypt "''${IDENTITIES[@]}" -o "$TMP_FILE" "${secretType.file}"
|
||||
)
|
||||
chmod ${secretType.mode} "$TMP_FILE"
|
||||
mv -f "$TMP_FILE" "$_truePath"
|
||||
|
|
@ -97,12 +100,9 @@ with lib; let
|
|||
''}
|
||||
'';
|
||||
|
||||
testIdentities =
|
||||
map
|
||||
(path: ''
|
||||
test -f ${path} || echo '[agenix] WARNING: config.age.identityPaths entry ${path} not present!'
|
||||
'')
|
||||
cfg.identityPaths;
|
||||
testIdentities = map (path: ''
|
||||
test -f ${path} || echo '[agenix] WARNING: config.age.identityPaths entry ${path} not present!'
|
||||
'') cfg.identityPaths;
|
||||
|
||||
cleanupAndLink = ''
|
||||
_agenix_generation="$(basename "$(readlink ${cfg.secretsDir})" || echo 0)"
|
||||
|
|
@ -117,10 +117,10 @@ with lib; let
|
|||
'';
|
||||
|
||||
installSecrets = builtins.concatStringsSep "\n" (
|
||||
["echo '[agenix] decrypting secrets...'"]
|
||||
[ "echo '[agenix] decrypting secrets...'" ]
|
||||
++ testIdentities
|
||||
++ (map installSecret (builtins.attrValues cfg.secrets))
|
||||
++ [cleanupAndLink]
|
||||
++ [ cleanupAndLink ]
|
||||
);
|
||||
|
||||
chownSecret = secretType: ''
|
||||
|
|
@ -129,67 +129,73 @@ with lib; let
|
|||
'';
|
||||
|
||||
chownSecrets = builtins.concatStringsSep "\n" (
|
||||
["echo '[agenix] chowning...'"]
|
||||
++ [chownMountPoint]
|
||||
[ "echo '[agenix] chowning...'" ]
|
||||
++ [ chownMountPoint ]
|
||||
++ (map chownSecret (builtins.attrValues cfg.secrets))
|
||||
);
|
||||
|
||||
secretType = types.submodule ({config, ...}: {
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
default = config._module.args.name;
|
||||
defaultText = literalExpression "config._module.args.name";
|
||||
description = ''
|
||||
Name of the file used in {option}`age.secretsDir`
|
||||
'';
|
||||
secretType = types.submodule (
|
||||
{ config, ... }:
|
||||
{
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
default = config._module.args.name;
|
||||
defaultText = literalExpression "config._module.args.name";
|
||||
description = ''
|
||||
Name of the file used in {option}`age.secretsDir`
|
||||
'';
|
||||
};
|
||||
file = mkOption {
|
||||
type = types.path;
|
||||
description = ''
|
||||
Age file the secret is loaded from.
|
||||
'';
|
||||
};
|
||||
path = mkOption {
|
||||
type = types.str;
|
||||
default = "${cfg.secretsDir}/${config.name}";
|
||||
defaultText = literalExpression ''
|
||||
"''${cfg.secretsDir}/''${config.name}"
|
||||
'';
|
||||
description = ''
|
||||
Path where the decrypted secret is installed.
|
||||
'';
|
||||
};
|
||||
mode = mkOption {
|
||||
type = types.str;
|
||||
default = "0400";
|
||||
description = ''
|
||||
Permissions mode of the decrypted secret in a format understood by chmod.
|
||||
'';
|
||||
};
|
||||
owner = mkOption {
|
||||
type = types.str;
|
||||
default = "0";
|
||||
description = ''
|
||||
User of the decrypted secret.
|
||||
'';
|
||||
};
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
default = users.${config.owner}.group or "0";
|
||||
defaultText = literalExpression ''
|
||||
users.''${config.owner}.group or "0"
|
||||
'';
|
||||
description = ''
|
||||
Group of the decrypted secret.
|
||||
'';
|
||||
};
|
||||
symlink = mkEnableOption "symlinking secrets to their destination" // {
|
||||
default = true;
|
||||
};
|
||||
};
|
||||
file = mkOption {
|
||||
type = types.path;
|
||||
description = ''
|
||||
Age file the secret is loaded from.
|
||||
'';
|
||||
};
|
||||
path = mkOption {
|
||||
type = types.str;
|
||||
default = "${cfg.secretsDir}/${config.name}";
|
||||
defaultText = literalExpression ''
|
||||
"''${cfg.secretsDir}/''${config.name}"
|
||||
'';
|
||||
description = ''
|
||||
Path where the decrypted secret is installed.
|
||||
'';
|
||||
};
|
||||
mode = mkOption {
|
||||
type = types.str;
|
||||
default = "0400";
|
||||
description = ''
|
||||
Permissions mode of the decrypted secret in a format understood by chmod.
|
||||
'';
|
||||
};
|
||||
owner = mkOption {
|
||||
type = types.str;
|
||||
default = "0";
|
||||
description = ''
|
||||
User of the decrypted secret.
|
||||
'';
|
||||
};
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
default = users.${config.owner}.group or "0";
|
||||
defaultText = literalExpression ''
|
||||
users.''${config.owner}.group or "0"
|
||||
'';
|
||||
description = ''
|
||||
Group of the decrypted secret.
|
||||
'';
|
||||
};
|
||||
symlink = mkEnableOption "symlinking secrets to their destination" // {default = true;};
|
||||
};
|
||||
});
|
||||
in {
|
||||
}
|
||||
);
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(mkRenamedOptionModule ["age" "sshKeyPaths"] ["age" "identityPaths"])
|
||||
(mkRenamedOptionModule [ "age" "sshKeyPaths" ] [ "age" "identityPaths" ])
|
||||
];
|
||||
|
||||
options.age = {
|
||||
|
|
@ -205,7 +211,7 @@ in {
|
|||
};
|
||||
secrets = mkOption {
|
||||
type = types.attrsOf secretType;
|
||||
default = {};
|
||||
default = { };
|
||||
description = ''
|
||||
Attrset of secrets.
|
||||
'';
|
||||
|
|
@ -219,12 +225,14 @@ in {
|
|||
};
|
||||
secretsMountPoint = mkOption {
|
||||
type =
|
||||
types.addCheck types.str
|
||||
(s:
|
||||
(builtins.match "[ \t\n]*" s)
|
||||
== null # non-empty
|
||||
&& (builtins.match ".+/" s) == null) # without trailing slash
|
||||
// {description = "${types.str.description} (with check: non-empty without trailing slash)";};
|
||||
types.addCheck types.str (
|
||||
s:
|
||||
(builtins.match "[ \t\n]*" s) == null # non-empty
|
||||
&& (builtins.match ".+/" s) == null
|
||||
) # without trailing slash
|
||||
// {
|
||||
description = "${types.str.description} (with check: non-empty without trailing slash)";
|
||||
};
|
||||
default = "/run/agenix.d";
|
||||
description = ''
|
||||
Where secrets are created before they are symlinked to {option}`age.secretsDir`
|
||||
|
|
@ -233,14 +241,17 @@ in {
|
|||
identityPaths = mkOption {
|
||||
type = types.listOf types.path;
|
||||
default =
|
||||
if isDarwin
|
||||
then [
|
||||
"/etc/ssh/ssh_host_ed25519_key"
|
||||
"/etc/ssh/ssh_host_rsa_key"
|
||||
]
|
||||
else if (config.services.openssh.enable or false)
|
||||
then map (e: e.path) (lib.filter (e: e.type == "rsa" || e.type == "ed25519") config.services.openssh.hostKeys)
|
||||
else [];
|
||||
if isDarwin then
|
||||
[
|
||||
"/etc/ssh/ssh_host_ed25519_key"
|
||||
"/etc/ssh/ssh_host_rsa_key"
|
||||
]
|
||||
else if (config.services.openssh.enable or false) then
|
||||
map (e: e.path) (
|
||||
lib.filter (e: e.type == "rsa" || e.type == "ed25519") config.services.openssh.hostKeys
|
||||
)
|
||||
else
|
||||
[ ];
|
||||
defaultText = literalExpression ''
|
||||
if isDarwin
|
||||
then [
|
||||
|
|
@ -257,11 +268,11 @@ in {
|
|||
};
|
||||
};
|
||||
|
||||
config = mkIf (cfg.secrets != {}) (mkMerge [
|
||||
config = mkIf (cfg.secrets != { }) (mkMerge [
|
||||
{
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.identityPaths != [];
|
||||
assertion = cfg.identityPaths != [ ];
|
||||
message = "age.identityPaths must be set, for example by enabling openssh.";
|
||||
}
|
||||
];
|
||||
|
|
@ -270,20 +281,18 @@ in {
|
|||
# When using sysusers we no longer be started as an activation script
|
||||
# because those are started in initrd while sysusers is started later.
|
||||
systemd.services.agenix-install-secrets = mkIf sysusersEnabled {
|
||||
wantedBy = ["sysinit.target"];
|
||||
after = ["systemd-sysusers.service"];
|
||||
wantedBy = [ "sysinit.target" ];
|
||||
after = [ "systemd-sysusers.service" ];
|
||||
unitConfig.DefaultDependencies = "no";
|
||||
|
||||
path = [pkgs.mount];
|
||||
path = [ pkgs.mount ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecStart = pkgs.writeShellScript "agenix-install" (
|
||||
concatLines [
|
||||
newGeneration
|
||||
installSecrets
|
||||
chownSecrets
|
||||
]
|
||||
);
|
||||
ExecStart = pkgs.writeShellScript "agenix-install" (concatLines [
|
||||
newGeneration
|
||||
installSecrets
|
||||
chownSecrets
|
||||
]);
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
};
|
||||
|
|
@ -308,7 +317,7 @@ in {
|
|||
};
|
||||
|
||||
# So user passwords can be encrypted.
|
||||
users.deps = ["agenixInstall"];
|
||||
users.deps = [ "agenixInstall" ];
|
||||
|
||||
# Change ownership and group after users and groups are made.
|
||||
agenixChown = {
|
||||
|
|
@ -322,7 +331,7 @@ in {
|
|||
# So other activation scripts can depend on agenix being done.
|
||||
agenix = {
|
||||
text = "";
|
||||
deps = ["agenixChown"];
|
||||
deps = [ "agenixChown" ];
|
||||
};
|
||||
};
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
final: prev: {
|
||||
agenix = prev.callPackage ./pkgs/agenix.nix {};
|
||||
agenix = prev.callPackage ./pkgs/agenix.nix { };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,57 +9,58 @@
|
|||
replaceVars,
|
||||
ageBin ? "${age}/bin/age",
|
||||
shellcheck,
|
||||
}: let
|
||||
}:
|
||||
let
|
||||
bin = "${placeholder "out"}/bin/agenix";
|
||||
in
|
||||
stdenv.mkDerivation rec {
|
||||
pname = "agenix";
|
||||
version = "0.15.0";
|
||||
src = replaceVars ./agenix.sh {
|
||||
inherit ageBin version;
|
||||
jqBin = "${jq}/bin/jq";
|
||||
nixInstantiate = "${nix}/bin/nix-instantiate";
|
||||
mktempBin = "${mktemp}/bin/mktemp";
|
||||
diffBin = "${diffutils}/bin/diff";
|
||||
};
|
||||
dontUnpack = true;
|
||||
doInstallCheck = true;
|
||||
installCheckInputs = [shellcheck];
|
||||
postInstallCheck = ''
|
||||
shellcheck ${bin}
|
||||
${bin} -h | grep ${version}
|
||||
stdenv.mkDerivation rec {
|
||||
pname = "agenix";
|
||||
version = "0.15.0";
|
||||
src = replaceVars ./agenix.sh {
|
||||
inherit ageBin version;
|
||||
jqBin = "${jq}/bin/jq";
|
||||
nixInstantiate = "${nix}/bin/nix-instantiate";
|
||||
mktempBin = "${mktemp}/bin/mktemp";
|
||||
diffBin = "${diffutils}/bin/diff";
|
||||
};
|
||||
dontUnpack = true;
|
||||
doInstallCheck = true;
|
||||
installCheckInputs = [ shellcheck ];
|
||||
postInstallCheck = ''
|
||||
shellcheck ${bin}
|
||||
${bin} -h | grep ${version}
|
||||
|
||||
test_tmp=$(mktemp -d 2>/dev/null || mktemp -d -t 'mytmpdir')
|
||||
export HOME="$test_tmp/home"
|
||||
export NIX_STORE_DIR="$test_tmp/nix/store"
|
||||
export NIX_STATE_DIR="$test_tmp/nix/var"
|
||||
mkdir -p "$HOME" "$NIX_STORE_DIR" "$NIX_STATE_DIR"
|
||||
function cleanup {
|
||||
rm -rf "$test_tmp"
|
||||
}
|
||||
trap "cleanup" 0 2 3 15
|
||||
test_tmp=$(mktemp -d 2>/dev/null || mktemp -d -t 'mytmpdir')
|
||||
export HOME="$test_tmp/home"
|
||||
export NIX_STORE_DIR="$test_tmp/nix/store"
|
||||
export NIX_STATE_DIR="$test_tmp/nix/var"
|
||||
mkdir -p "$HOME" "$NIX_STORE_DIR" "$NIX_STATE_DIR"
|
||||
function cleanup {
|
||||
rm -rf "$test_tmp"
|
||||
}
|
||||
trap "cleanup" 0 2 3 15
|
||||
|
||||
mkdir -p $HOME/.ssh
|
||||
cp -r "${../example}" $HOME/secrets
|
||||
chmod -R u+rw $HOME/secrets
|
||||
(
|
||||
umask u=rw,g=r,o=r
|
||||
cp ${../example_keys/user1.pub} $HOME/.ssh/id_ed25519.pub
|
||||
chown $UID $HOME/.ssh/id_ed25519.pub
|
||||
)
|
||||
(
|
||||
umask u=rw,g=,o=
|
||||
cp ${../example_keys/user1} $HOME/.ssh/id_ed25519
|
||||
chown $UID $HOME/.ssh/id_ed25519
|
||||
)
|
||||
mkdir -p $HOME/.ssh
|
||||
cp -r "${../example}" $HOME/secrets
|
||||
chmod -R u+rw $HOME/secrets
|
||||
(
|
||||
umask u=rw,g=r,o=r
|
||||
cp ${../example_keys/user1.pub} $HOME/.ssh/id_ed25519.pub
|
||||
chown $UID $HOME/.ssh/id_ed25519.pub
|
||||
)
|
||||
(
|
||||
umask u=rw,g=,o=
|
||||
cp ${../example_keys/user1} $HOME/.ssh/id_ed25519
|
||||
chown $UID $HOME/.ssh/id_ed25519
|
||||
)
|
||||
|
||||
cd $HOME/secrets
|
||||
test $(${bin} -d secret1.age) = "hello"
|
||||
'';
|
||||
cd $HOME/secrets
|
||||
test $(${bin} -d secret1.age) = "hello"
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
install -D $src ${bin}
|
||||
'';
|
||||
installPhase = ''
|
||||
install -D $src ${bin}
|
||||
'';
|
||||
|
||||
meta.description = "age-encrypted secrets for NixOS";
|
||||
}
|
||||
meta.description = "age-encrypted secrets for NixOS";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,6 @@
|
|||
stdenvNoCC.mkDerivation rec {
|
||||
name = "agenix-doc";
|
||||
src = ../doc;
|
||||
phases = ["mmdocPhase"];
|
||||
phases = [ "mmdocPhase" ];
|
||||
mmdocPhase = "${mmdoc}/bin/mmdoc agenix $src $out";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# Do not copy this! It is insecure. This is only okay because we are testing.
|
||||
{config, ...}: {
|
||||
system.activationScripts.agenixInstall.deps = ["installSSHHostKeys"];
|
||||
{ config, ... }:
|
||||
{
|
||||
system.activationScripts.agenixInstall.deps = [ "installSSHHostKeys" ];
|
||||
|
||||
system.activationScripts.installSSHHostKeys.text = ''
|
||||
USER1_UID="${toString config.users.users.user1.uid}"
|
||||
|
|
|
|||
|
|
@ -1,137 +1,142 @@
|
|||
{
|
||||
nixpkgs ? <nixpkgs>,
|
||||
pkgs ?
|
||||
import <nixpkgs> {
|
||||
inherit system;
|
||||
config = {};
|
||||
},
|
||||
pkgs ? import <nixpkgs> {
|
||||
inherit system;
|
||||
config = { };
|
||||
},
|
||||
system ? builtins.currentSystem,
|
||||
home-manager ? <home-manager>,
|
||||
}:
|
||||
pkgs.nixosTest {
|
||||
name = "agenix-integration";
|
||||
nodes.system1 = {
|
||||
config,
|
||||
pkgs,
|
||||
options,
|
||||
...
|
||||
}: {
|
||||
imports = [
|
||||
../modules/age.nix
|
||||
./install_ssh_host_keys.nix
|
||||
"${home-manager}/nixos"
|
||||
];
|
||||
|
||||
services.openssh.enable = true;
|
||||
|
||||
age.secrets = {
|
||||
passwordfile-user1.file = ../example/passwordfile-user1.age;
|
||||
leading-hyphen.file = ../example/-leading-hyphen-filename.age;
|
||||
};
|
||||
|
||||
age.identityPaths = options.age.identityPaths.default ++ ["/etc/ssh/this_key_wont_exist"];
|
||||
|
||||
environment.systemPackages = [
|
||||
(pkgs.callPackage ../pkgs/agenix.nix {})
|
||||
];
|
||||
|
||||
users = {
|
||||
mutableUsers = false;
|
||||
|
||||
users = {
|
||||
user1 = {
|
||||
isNormalUser = true;
|
||||
passwordFile = config.age.secrets.passwordfile-user1.path;
|
||||
uid = 1000;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
home-manager.users.user1 = {options, ...}: {
|
||||
nodes.system1 =
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
options,
|
||||
...
|
||||
}:
|
||||
{
|
||||
imports = [
|
||||
../modules/age-home.nix
|
||||
../modules/age.nix
|
||||
./install_ssh_host_keys.nix
|
||||
"${home-manager}/nixos"
|
||||
];
|
||||
|
||||
home.stateVersion = pkgs.lib.trivial.release;
|
||||
services.openssh.enable = true;
|
||||
|
||||
age = {
|
||||
identityPaths = options.age.identityPaths.default ++ ["/home/user1/.ssh/this_key_wont_exist"];
|
||||
secrets.secret2 = {
|
||||
# Only decryptable by user1's key
|
||||
file = ../example/secret2.age;
|
||||
};
|
||||
secrets.secret2Path = {
|
||||
file = ../example/secret2.age;
|
||||
path = "/home/user1/secret2";
|
||||
};
|
||||
secrets.armored-secret = {
|
||||
file = ../example/armored-secret.age;
|
||||
age.secrets = {
|
||||
passwordfile-user1.file = ../example/passwordfile-user1.age;
|
||||
leading-hyphen.file = ../example/-leading-hyphen-filename.age;
|
||||
};
|
||||
|
||||
age.identityPaths = options.age.identityPaths.default ++ [ "/etc/ssh/this_key_wont_exist" ];
|
||||
|
||||
environment.systemPackages = [
|
||||
(pkgs.callPackage ../pkgs/agenix.nix { })
|
||||
];
|
||||
|
||||
users = {
|
||||
mutableUsers = false;
|
||||
|
||||
users = {
|
||||
user1 = {
|
||||
isNormalUser = true;
|
||||
passwordFile = config.age.secrets.passwordfile-user1.path;
|
||||
uid = 1000;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
home-manager.users.user1 =
|
||||
{ options, ... }:
|
||||
{
|
||||
imports = [
|
||||
../modules/age-home.nix
|
||||
];
|
||||
|
||||
home.stateVersion = pkgs.lib.trivial.release;
|
||||
|
||||
age = {
|
||||
identityPaths = options.age.identityPaths.default ++ [ "/home/user1/.ssh/this_key_wont_exist" ];
|
||||
secrets.secret2 = {
|
||||
# Only decryptable by user1's key
|
||||
file = ../example/secret2.age;
|
||||
};
|
||||
secrets.secret2Path = {
|
||||
file = ../example/secret2.age;
|
||||
path = "/home/user1/secret2";
|
||||
};
|
||||
secrets.armored-secret = {
|
||||
file = ../example/armored-secret.age;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = let
|
||||
user = "user1";
|
||||
password = "password1234";
|
||||
secret2 = "world!";
|
||||
hyphen-secret = "filename started with hyphen";
|
||||
armored-secret = "Hello World!";
|
||||
in ''
|
||||
system1.wait_for_unit("multi-user.target")
|
||||
system1.wait_until_succeeds("pgrep -f 'agetty.*tty1'")
|
||||
system1.sleep(2)
|
||||
system1.send_key("alt-f2")
|
||||
system1.wait_until_succeeds("[ $(fgconsole) = 2 ]")
|
||||
system1.wait_for_unit("getty@tty2.service")
|
||||
system1.wait_until_succeeds("pgrep -f 'agetty.*tty2'")
|
||||
system1.wait_until_tty_matches("2", "login: ")
|
||||
system1.send_chars("${user}\n")
|
||||
system1.wait_until_tty_matches("2", "login: ${user}")
|
||||
system1.wait_until_succeeds("pgrep login")
|
||||
system1.sleep(2)
|
||||
system1.send_chars("${password}\n")
|
||||
system1.send_chars("whoami > /tmp/1\n")
|
||||
system1.wait_for_file("/tmp/1")
|
||||
assert "${user}" in system1.succeed("cat /tmp/1")
|
||||
system1.send_chars("cat /run/user/$(id -u)/agenix/secret2 > /tmp/2\n")
|
||||
system1.wait_for_file("/tmp/2")
|
||||
assert "${secret2}" in system1.succeed("cat /tmp/2")
|
||||
system1.send_chars("cat /run/user/$(id -u)/agenix/armored-secret > /tmp/3\n")
|
||||
system1.wait_for_file("/tmp/3")
|
||||
assert "${armored-secret}" in system1.succeed("cat /tmp/3")
|
||||
testScript =
|
||||
let
|
||||
user = "user1";
|
||||
password = "password1234";
|
||||
secret2 = "world!";
|
||||
hyphen-secret = "filename started with hyphen";
|
||||
armored-secret = "Hello World!";
|
||||
in
|
||||
''
|
||||
system1.wait_for_unit("multi-user.target")
|
||||
system1.wait_until_succeeds("pgrep -f 'agetty.*tty1'")
|
||||
system1.sleep(2)
|
||||
system1.send_key("alt-f2")
|
||||
system1.wait_until_succeeds("[ $(fgconsole) = 2 ]")
|
||||
system1.wait_for_unit("getty@tty2.service")
|
||||
system1.wait_until_succeeds("pgrep -f 'agetty.*tty2'")
|
||||
system1.wait_until_tty_matches("2", "login: ")
|
||||
system1.send_chars("${user}\n")
|
||||
system1.wait_until_tty_matches("2", "login: ${user}")
|
||||
system1.wait_until_succeeds("pgrep login")
|
||||
system1.sleep(2)
|
||||
system1.send_chars("${password}\n")
|
||||
system1.send_chars("whoami > /tmp/1\n")
|
||||
system1.wait_for_file("/tmp/1")
|
||||
assert "${user}" in system1.succeed("cat /tmp/1")
|
||||
system1.send_chars("cat /run/user/$(id -u)/agenix/secret2 > /tmp/2\n")
|
||||
system1.wait_for_file("/tmp/2")
|
||||
assert "${secret2}" in system1.succeed("cat /tmp/2")
|
||||
system1.send_chars("cat /run/user/$(id -u)/agenix/armored-secret > /tmp/3\n")
|
||||
system1.wait_for_file("/tmp/3")
|
||||
assert "${armored-secret}" in system1.succeed("cat /tmp/3")
|
||||
|
||||
assert "${hyphen-secret}" in system1.succeed("cat /run/agenix/leading-hyphen")
|
||||
assert "${hyphen-secret}" in system1.succeed("cat /run/agenix/leading-hyphen")
|
||||
|
||||
userDo = lambda input : f"sudo -u user1 -- bash -c 'set -eou pipefail; cd /tmp/secrets; {input}'"
|
||||
userDo = lambda input : f"sudo -u user1 -- bash -c 'set -eou pipefail; cd /tmp/secrets; {input}'"
|
||||
|
||||
before_hash = system1.succeed(userDo('sha256sum passwordfile-user1.age')).split()
|
||||
print(system1.succeed(userDo('agenix -r -i /home/user1/.ssh/id_ed25519')))
|
||||
after_hash = system1.succeed(userDo('sha256sum passwordfile-user1.age')).split()
|
||||
before_hash = system1.succeed(userDo('sha256sum passwordfile-user1.age')).split()
|
||||
print(system1.succeed(userDo('agenix -r -i /home/user1/.ssh/id_ed25519')))
|
||||
after_hash = system1.succeed(userDo('sha256sum passwordfile-user1.age')).split()
|
||||
|
||||
# Ensure we actually have hashes
|
||||
for h in [before_hash, after_hash]:
|
||||
assert len(h) == 2, "hash should be [hash, filename]"
|
||||
assert h[1] == "passwordfile-user1.age", "filename is incorrect"
|
||||
assert len(h[0].strip()) == 64, "hash length is incorrect"
|
||||
assert before_hash[0] != after_hash[0], "hash did not change with rekeying"
|
||||
# Ensure we actually have hashes
|
||||
for h in [before_hash, after_hash]:
|
||||
assert len(h) == 2, "hash should be [hash, filename]"
|
||||
assert h[1] == "passwordfile-user1.age", "filename is incorrect"
|
||||
assert len(h[0].strip()) == 64, "hash length is incorrect"
|
||||
assert before_hash[0] != after_hash[0], "hash did not change with rekeying"
|
||||
|
||||
# user1 can edit passwordfile-user1.age
|
||||
system1.succeed(userDo("EDITOR=cat agenix -e passwordfile-user1.age"))
|
||||
# user1 can edit passwordfile-user1.age
|
||||
system1.succeed(userDo("EDITOR=cat agenix -e passwordfile-user1.age"))
|
||||
|
||||
# user1 can edit even if bogus id_rsa present
|
||||
system1.succeed(userDo("echo bogus > ~/.ssh/id_rsa"))
|
||||
system1.fail(userDo("EDITOR=cat agenix -e passwordfile-user1.age"))
|
||||
system1.succeed(userDo("EDITOR=cat agenix -e passwordfile-user1.age -i /home/user1/.ssh/id_ed25519"))
|
||||
system1.succeed(userDo("rm ~/.ssh/id_rsa"))
|
||||
# user1 can edit even if bogus id_rsa present
|
||||
system1.succeed(userDo("echo bogus > ~/.ssh/id_rsa"))
|
||||
system1.fail(userDo("EDITOR=cat agenix -e passwordfile-user1.age"))
|
||||
system1.succeed(userDo("EDITOR=cat agenix -e passwordfile-user1.age -i /home/user1/.ssh/id_ed25519"))
|
||||
system1.succeed(userDo("rm ~/.ssh/id_rsa"))
|
||||
|
||||
# user1 can edit a secret by piping in contents
|
||||
system1.succeed(userDo("echo 'secret1234' | agenix -e passwordfile-user1.age"))
|
||||
# user1 can edit a secret by piping in contents
|
||||
system1.succeed(userDo("echo 'secret1234' | agenix -e passwordfile-user1.age"))
|
||||
|
||||
# and get it back out via --decrypt
|
||||
assert "secret1234" in system1.succeed(userDo("agenix -d passwordfile-user1.age"))
|
||||
# and get it back out via --decrypt
|
||||
assert "secret1234" in system1.succeed(userDo("agenix -d passwordfile-user1.age"))
|
||||
|
||||
# finally, the plain text should not linger around anywhere in the filesystem.
|
||||
system1.fail("grep -r secret1234 /tmp")
|
||||
'';
|
||||
# finally, the plain text should not linger around anywhere in the filesystem.
|
||||
system1.fail("grep -r secret1234 /tmp")
|
||||
'';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
pkgs,
|
||||
options,
|
||||
...
|
||||
}: let
|
||||
}:
|
||||
let
|
||||
secret = "hello";
|
||||
testScript = pkgs.writeShellApplication {
|
||||
name = "agenix-integration";
|
||||
|
|
@ -11,18 +12,19 @@
|
|||
grep "${secret}" "${config.age.secrets.system-secret.path}"
|
||||
'';
|
||||
};
|
||||
in {
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
./install_ssh_host_keys_darwin.nix
|
||||
../modules/age.nix
|
||||
];
|
||||
|
||||
age = {
|
||||
identityPaths = options.age.identityPaths.default ++ ["/etc/ssh/this_key_wont_exist"];
|
||||
identityPaths = options.age.identityPaths.default ++ [ "/etc/ssh/this_key_wont_exist" ];
|
||||
secrets.system-secret.file = ../example/secret1.age;
|
||||
};
|
||||
|
||||
environment.systemPackages = [testScript];
|
||||
environment.systemPackages = [ testScript ];
|
||||
|
||||
system.stateVersion = 6;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,12 @@
|
|||
options,
|
||||
lib,
|
||||
...
|
||||
}: {
|
||||
imports = [../modules/age-home.nix];
|
||||
}:
|
||||
{
|
||||
imports = [ ../modules/age-home.nix ];
|
||||
|
||||
age = {
|
||||
identityPaths = options.age.identityPaths.default ++ ["/Users/user1/.ssh/this_key_wont_exist"];
|
||||
identityPaths = options.age.identityPaths.default ++ [ "/Users/user1/.ssh/this_key_wont_exist" ];
|
||||
secrets.user-secret.file = ../example/secret2.age;
|
||||
};
|
||||
|
||||
|
|
@ -18,16 +19,20 @@
|
|||
stateVersion = lib.trivial.release;
|
||||
};
|
||||
|
||||
home.file = let
|
||||
name = "agenix-home-integration";
|
||||
in {
|
||||
${name}.source = pkgs.writeShellApplication {
|
||||
inherit name;
|
||||
text = let
|
||||
secret = "world!";
|
||||
in ''
|
||||
diff -q "${config.age.secrets.user-secret.path}" <(printf '${secret}\n')
|
||||
'';
|
||||
home.file =
|
||||
let
|
||||
name = "agenix-home-integration";
|
||||
in
|
||||
{
|
||||
${name}.source = pkgs.writeShellApplication {
|
||||
inherit name;
|
||||
text =
|
||||
let
|
||||
secret = "world!";
|
||||
in
|
||||
''
|
||||
diff -q "${config.age.secrets.user-secret.path}" <(printf '${secret}\n')
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue