Skip to content
This repository was archived by the owner on Feb 10, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@
};

}) // {
nixosModules.default = import ./modules/op-secrets.nix;
darwinModules.default = import ./modules/darwin.nix;
nixosModules.default = import ./modules/nixos.nix;

# test is a hostname for our machine
nixosConfigurations.test = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules =
[ ./modules/op-secrets.nix ./test-nix-files/configuration.nix ];
[ ./modules/nixos.nix ./test-nix-files/configuration.nix ];
};
};
}
61 changes: 61 additions & 0 deletions modules/common.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{ lib, pkgs, config, ... }:
with lib;
let
inherit (import ./types.nix {
inherit lib;
inherit config;
})
secretFileDeclaration;
in {
options.opnix = {
opBin = mkOption {
type = types.str;
default = "${pkgs._1password-cli}/bin/op";
description = "The 1Password CLI `op` executable to use";
};
environmentFile = mkOption {
type = types.str;
description = ''
Path to a environment file which contains your service account token. Format should be `OP_SERVICE_ACCOUNT_TOKEN="{ your token here }"`. This is used to authorize the 1Password CLI.'';
};
secretsDir = mkOption {
type = types.path;
default = "/run/opnix";
description = ''
Directory where secrets are symlinked to
'';
};
secretsMountPoint = mkOption {
type = types.addCheck types.str (s:
(trim s) != "" # non-empty
&& (builtins.match ".+/" s) == null) # without trailing slash
// {
description =
"${types.str.description} (with check: non-empty without trailing slash)";
};
default = "/run/opnix.d";
};
secrets = mkOption {
type = types.attrsOf secretFileDeclaration;
description = "The secrets you want to use in your NixOS deployment";
default = { };
example = {
my-secret = {
source = "{{ op://VaultName/ItemName/FieldName }}";
mode = "0400";
inherit (config.services.some_service) user;
inherit (config.services.some_service) group;
};
another-secret.source = ''
[SomeTomlHeader]
SomeValue = "{{ op://AnotherVault/AnotherItem/AnotherField }}"
'';
};
};
debug = mkOption {
type = types.bool;
description = "Whether to enable debug logs";
default = false;
};
};
}
65 changes: 65 additions & 0 deletions modules/darwin.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
toplevel @ {
config,
lib,
pkgs,
...
}: let
inherit
(lib)
mkAfter
mkIf
mkMerge
;

cfg = config.opnix;
scripts = import ./scripts.nix toplevel;
in {
imports = [./common.nix];

config = let
opnixScript = ''
${scripts.installSecrets}
${scripts.chownSecrets}
'';
in
mkIf (cfg.secrets != {}) (mkMerge [
{
launchd.daemons.activate-opnix = {
script = ''
set -euo pipefail
export PATH="${pkgs.gnugrep}/bin:${pkgs.coreutils}/bin:@out@/sw/bin:/usr/bin:/bin:/usr/sbin:/sbin"
source ${cfg.environmentFile}
export OP_SERVICE_ACCOUNT_TOKEN
${opnixScript}
'';
serviceConfig = {
RunAtLoad = true;
KeepAlive.SuccessfulExit = false;
};
};
}
{
system.activationScripts = {
# if no generation already exists, rely on the launchd startup job;
# otherwise, if there already is an existing generation, reprovision
# secrets because we did a darwin-rebuild
preActivation.text = ''
${scripts.setOpnixGeneration}
(( _opnix_generation > 1 )) && {
# shellcheck disable=SC1091
source ${cfg.environmentFile}
export OP_SERVICE_ACCOUNT_TOKEN
${scripts.installSecrets}
}
'';

users.text = mkAfter ''
${scripts.setOpnixGeneration}
(( _opnix_generation > 1 )) && {
${scripts.chownSecrets}
}
'';
};
}
]);
}
78 changes: 78 additions & 0 deletions modules/nixos.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
toplevel @ {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you explain what this toplevel @ is for?

config,
lib,
pkgs,
...
}: let
inherit
(lib)
mkIf
mkMerge
mkOption
types
;

cfg = config.opnix;
scripts = import ./scripts.nix toplevel;
in {
imports = [./common.nix];

options.opnix = {
systemdWantedBy = mkOption {
type = types.listOf types.str;
default = [];
description = ''
A list of `systemd` service names that depend on secrets from `opnix`. This option will set `after = [ "opnix.service" ]` and `wants = [ "opnix.service" ]` for each specified `systemd` unit.'';
example = ["homepage-dashboard" "wg-quick-vpn"];
};
};

config = let
opnixScript = ''
${scripts.installSecrets}
${scripts.chownSecrets}
'';
in
mkIf (cfg.secrets != {}) (mkMerge [
{
systemd.services.opnix = {
wants = ["network-online.target"];
after = ["network.target" "network-online.target"];

serviceConfig = {
Type = "oneshot";
EnvironmentFile = cfg.environmentFile;
RemainAfterExit = true;
};

script = opnixScript;
};
}
{
system.activationScripts.opnix-on-rebuild = {
# if no generation already exists, rely on the systemd startup job;
# otherwise, if there already is an existing generation, reprovision
# secrets because we did a nixos-rebuild
text = ''
${scripts.setOpnixGeneration}
(( _opnix_generation > 1 )) && {
source ${cfg.environmentFile}
export OP_SERVICE_ACCOUNT_TOKEN
${opnixScript}
}
'';
deps = ["usrbinenv"];
};
}
{
systemd.services = builtins.listToAttrs (builtins.map (systemdName: {
name = systemdName;
value = {
after = ["opnix.service"];
wants = ["opnix.service"];
};
})
cfg.systemdWantedBy);
}
]);
}
112 changes: 0 additions & 112 deletions modules/op-secrets.nix

This file was deleted.

Loading