Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add age.secrets.*.{action,service} #87

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open

Conversation

Radvendii
Copy link

@Radvendii Radvendii commented Dec 29, 2021

I don't have the brainspace to test this right now, but I figured I would put it up in case other people were inspired to test before I did.

Things that need testing:

  • Does this even evaluate in current form?
  • Does it correctly restart services when secrets change
  • Does it not restart services when secrets don't change (or when some secrets change but other don't
  • If you change a secret and it's action atomically, which action gets performed? The old one or the new one? (I think this should be the new one)

Implements #84

@winterqt
Copy link

winterqt commented Jan 8, 2022

Does it not restart services when secrets don't change (or when some secrets change but other don't

I believe this is false because of #89.

If you change a secret and it's action atomically, which action gets performed? The old one or the new one? (I think this should be the new one)

As far as I can tell, it would be the old action, since the path condition fires before the units get reloaded.

@Radvendii
Copy link
Author

Radvendii commented Jan 12, 2022

@winterqt thank you! After looking at your comment and thinking about it for a bit, I've completely reworked this PR.

It now hijacks NixOS's mechanism for restarting services. If you specify a service, this is simple. it just adds the information about the secret to the service file so that if any of it changes, the service gets reloaded.

If you specify an action, it creates a dummy service that just executes that action, and similarly causes it to be reloaded when the secret changes.

A few design decisions I'm not sure about:

  • Do we even need action? Do people use this for anything besides restarting services? Someone might want to reload a service. In theory they can set systemd.services.<service>.reloadIfChanged, but maybe they want it to restart if they actually change the service itself?
  • Should we change service to services and make it a list? There are sometimes multiple services that need to be restarted. This would be needed if we remove action, but maybe not if we don't.
  • Is there a better name for the service than agenix-${name}-action?

@winterqt
Copy link

This is really nicely done, congrats. Do you mind if I PR additions to the tests to account for this functionality to your branch, so this all lands in one PR?

modules/age.nix Outdated Show resolved Hide resolved
modules/age.nix Outdated Show resolved Hide resolved
@Radvendii
Copy link
Author

Not at all, go for it.

@Radvendii
Copy link
Author

Well, I thought of yet another rewrite. Advantages of this one include:

  • Simpler (not mucking with nix systemd magic)
  • compares the decrypted secrets, so if you rekey this won't run the action

We could just add something like this to the activation script for each secret:

    if ! [ -e ${secretType.path} ] || \
       ! ${pkgs.diffutils}/bin/diff -q $TMP_FILE ${secretType.path} >/dev/null || \
       [ "$(stat -c '%a' "$TMP_FILE")" -eq "$(stat -c '%a' "${secretType.path}")" ]
    then
      echo "Updated secret ${secretType.name} performing action..."
      ${secretType.action};
    fi

This isn't working on my machine though, because the file at secretType.path doesn't exist while this is running for some reason. Something I've noticed on my machine is that /run/agenix.d doesn't actually store any older generations of secrets, only the current one. Maybe that's the problem? It's getting deleted somewhere? Can't figure out where though.

@winterqt
Copy link

winterqt commented Jan 13, 2022

@ryantm The changes to the integration test to include this functionality changes some reformatting using nixpkgs-fmt, plus some changes to make it work on recent Nixpkgs versions.

Would you prefer that I split the fixes and formatting changes from the additions, or don't format at all, or are you fine with all of this (implementation, fixing the existing test, and testing of this functionality) being in one commit?

@Radvendii
Copy link
Author

Oh, and another advantage of that last rewrite is that it would perform the action the first time the secret is created / the action was added, not just when it gets changed. The disadvantage, of course, is that I still don't know how to get it to work at all.

@ryantm
Copy link
Owner

ryantm commented Jan 18, 2022

The changes to the integration test

Maybe a bunch of things just changed because I don't see any changes to the integration tests now? Generally, I'd prefer to keep formatting changes in a separate PR, so the individual ones are easier to review.

@Radvendii
Copy link
Author

I don't think @winterqt committed the integration test yet.

@Radvendii
Copy link
Author

Oh, and for that you should @winterqt you should know I changed it to have services which takes a list rather than just a single service. I found that it would be helpful in practice.

@Radvendii Radvendii mentioned this pull request Jan 18, 2022
@Radvendii
Copy link
Author

@winterqt any progress on this? I'm happy to work on the integration test but I don't want to duplicate effort in case you just forgot to push.

@winterqt
Copy link

@Radvendii I opened Radvendii#1 a few days ago. I assumed you would be notified, but I just noticed that GitHub doesn't enable notifications on forks for their creators by default, like they do for normal repos.

@Radvendii
Copy link
Author

Whoops! Also my notification settings are a bit scuffed right now, which might be why.

@Radvendii Radvendii changed the title WIP: add age.secrets.*.{action,service} add age.secrets.*.{action,service} Jan 25, 2022
@Radvendii
Copy link
Author

Okay, this is no longer WIP. I think it's good to go

@Radvendii
Copy link
Author

Bump? @ryantm

modules/age.nix Outdated
Comment on lines 98 to 127
services = mkOption {
type = types.listOf types.str;
default = [];
description = "The systemd services that uses this secret. Will be restarted when the secret changes.";
example = "[ wireguard-wg0 ]";
};

Choose a reason for hiding this comment

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

Some feedback regarding this:

  • What about wanting to only reload a unit instead of restarting it? There's reloadTriggers as well. Maybe there should be a way to make it reload instead of restart.
  • I'd think this option should be named units or dependentUnits instead (or with above proposal maybe dependentUnitsToRestart), and the strings it should accept are of the form "wireguard-wg0.service", but also "my.target". restart/reloadTriggers exist for all unit types, not just services. This then also matches the wantedBy and co. NixOS options. Note that you can probably use utils.systemdUtils.unitNameType for the type of this option then.
  • The example should be declared like example = literalExpression ''[ "wireguard-wg0" ]'' (or adding the .service suffix with the above suggestion)

Copy link
Author

Choose a reason for hiding this comment

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

I'm glad you weighed in. I don't have strong opinions about any of this, and the only reason I didn't do it the way you're describing is because I'm not that familiar with systemd units. Feel free to make these changes.

Copy link
Author

Choose a reason for hiding this comment

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

Oh. You don't have access to my repository.* I guess make a PR against my fork and I'll accept it? I would just make the changes myself but I don't think I understand them well enough to do it right on my first try.

*I stop using git for a week and totally forget how anything works XD

modules/age.nix Outdated
Comment on lines 229 to 233
# We execute the action on reload so that it doesn't happen at
# startup. The only disadvantage is that it won't trigger the
# first time the service is created.
reload = action;
reloadIfChanged = true;

Choose a reason for hiding this comment

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

I think it makes more sense to also trigger at startup. This service is then essentially a secret post-processing-after-loading script.

Copy link
Author

Choose a reason for hiding this comment

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

I'm not sure what you mean by "post-processing-after-loading script". This script doesn't do any post-processing, it just restarts the service. If I understand correctly, having it trigger at startup would mean that the dependent service (e.g. wireguard) would start up twice every time you booted, without any good reason.

@infinisil
Copy link

Check out #84 (comment) for a potential alternative

@Radvendii
Copy link
Author

Radvendii commented Jun 1, 2022

I've overhauled this to use /run/activation-{restart,reload}-list and to check whether the actual decrypted secrets have changed.

h/t @EHfive

Radvendii and others added 5 commits June 1, 2022 13:45
represents an action to perform or systemd service to restart when the
secret changes
Co-authored-by: Winter <[email protected]>
winterqt and others added 3 commits June 1, 2022 13:49
compare the decrypted secrets, and make use of /run/nixos/activation-{restart,reload}-list
@Radvendii
Copy link
Author

ping @ryantm I think this PR is in an even better state and is ready to merge again (from my end)

Copy link
Owner

@ryantm ryantm left a comment

Choose a reason for hiding this comment

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

Thank you. This is looking pretty good overall to me. I left some comments/questions.

modules/age.nix Outdated Show resolved Hide resolved
test/integration.nix Outdated Show resolved Hide resolved
modules/age.nix Outdated Show resolved Hide resolved
@Radvendii
Copy link
Author

This is very bizzare. nix flake check works on my machine and it's on the same commit. TFW flakes not reproducible.

@Radvendii
Copy link
Author

I fixed the behaviour in some edge cases (including the one we discussed), and added a bunch more tests to make sure it's behaving right in those edge cases. There are even more cases that could be added (e.g. changing more than one thing at the same time), but that becomes very big very fast. let me know if you'd like me to add those tests.

@Radvendii
Copy link
Author

ping @ryantm hopefully it's ready now.

@Radvendii
Copy link
Author

@ryantm

@winterqt
Copy link

winterqt commented Aug 7, 2023

ping @ryantm

@winterqt
Copy link

winterqt commented Aug 7, 2023

also ping @cole-h :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants