NixOS / nixpkgs

Nix Packages collection & NixOS
MIT License
17.09k stars 13.39k forks source link

authelia: error loading secret permission denied #325374

Open gepbird opened 1 month ago

gepbird commented 1 month ago

Describe the bug

Authelia can't read secrets provided by agenix..

Steps To Reproduce

Steps to reproduce the behavior:

  1. Set up agenix with a dummy secret
  2. Build with this config:
    age.secrets.dummy.file = ../secrets/dummy.age;
    services.authelia.instances.main = {
    enable = true;
    secrets = {
      jwtSecretFile = config.age.secrets.dummy.path;
      storageEncryptionKeyFile = config.age.secrets.dummy.path;
    };
    };
  3. Check logs: journalctl -xeu authelia-main
    authelia[299368]: time="2024-07-08T01:00:48+02:00" level=error msg="Configuration: secrets: error loading secret path /run/agenix/dummy into key 'jwt_secret': open /run/agenix/dummy: permission denied"
    authelia[299368]: time="2024-07-08T01:00:48+02:00" level=error msg="Configuration: secrets: error loading secret path /run/agenix/dummy into key 'storage.encryption_key': open /run/agenix/dummy: permission denied"

Expected behavior

No permission issues.

Screenshots

If applicable, add screenshots to help explain your problem.

Additional context

Decrypted agenix secret files are only readable by root, but authelia (running under non-root) tries to directly read these files, causing the permission error. For example the transmission module uses ExecPreStart (which is running as root) of the service to read the decrypted secret, and after some modification it writes it to a file that is accessible by cfg.user, avoiding permission issues. Should we apply this concept to authelia too?

Notify maintainers

@AndersonTorres @06kellyjac @dit7ya

Metadata

Please run nix-shell -p nix-info --run "nix-info -m" and paste the result.

[user@system:~]$ nix-shell -p nix-info --run "nix-info -m"
 - system: `"x86_64-linux"`
 - host os: `Linux 6.6.36, NixOS, 24.11 (Vicuna), 24.11.20240703.dirty`
 - multi-user?: `yes`
 - sandbox: `yes`
 - version: `nix-env (Nix) 2.18.4`
 - nixpkgs: `/nix/store/8z0nbmykm29kigb9s338y1dm1yr4d7zy-source`

Add a :+1: reaction to issues you find important.

AndersonTorres commented 1 month ago

???

HanEmile commented 1 month ago

I've used the following in the meantime:

    # set the permissions for the secrets...
    age.secrets = {
        # ... passwed via environment vars
        authelia_session_secret.owner = "authelia-main";
        authelia_session_secret.group = "authelia-main";
        authelia_mail_password.owner = "authelia-main";
        authelia_mail_password.group = "authelia-main";

        # ... passed via the services.authelia.instances.main.secrets attribute
        authelia_storage_encryption_key.owner = "authelia-main";
        authelia_storage_encryption_key.group = "authelia-main";
        authelia_jwt_secret.owner = "authelia-main";
        authelia_jwt_secret.group = "authelia-main";
        authelia_oidc_issuer_private_key.owner = "authelia-main";
        authelia_oidc_issuer_private_key.group = "authelia-main";
        authelia_oidc_hmac_secret.owner = "authelia-main";
        authelia_oidc_hmac_secret.group = "authelia-main";
    };
06kellyjac commented 1 month ago

Yes, I wouldn't call this a bug in the module, this is just the reality of using age and linux file permissions.

There are a couple options here:

Set the owner of the secret to match the authelia instance. This is the best (least priviledged) method as only the relevant service can access the file. If multiple services need the same file you could look at putting each user in a group. I would also advise pulling the user from config. rather than hardcoding it to a string. Note the docs that each instance gets a user and is in the format authelia-<name> where name is what you chose for the instance. In this case main so authelia-main.

https://search.nixos.org/options?channel=24.05&show=services.authelia.instances.%3Cname%3E.user&from=0&size=50&sort=relevance&type=packages&query=Authelia

Option two would be to change the authelia instance user to something else. You could have multiple authelia instances with the same user but this is not advised. You could also just set the user to "root" and then it'll have access to the file but that is super not advised and a very bad idea.

Option three you could change the permissions on the secret to be readable by any user. Also a very bad idea and is counter to the idea of secrets but not quite as bad as running authelia as root.


If you did not face this difficulty with other services then they may be running with excessive access e.g. root. Or maybe the difficulty is just due to there being not just 1 authelia user but one per instance?

Either way thank you for raising the issue anyway as I'm sure someone else might run into this.

Maybe at some point we can get around to adding more details to the docs/manual/wiki.

Edit: I didn't spot that mention of transmission but personally I don't like that it uses elevated permissions to make a duplicate copy of your secret. I'd start thinking about what secret I could provide to jq to make it do something else or maybe messing with cfg.user so it escapes the quotes around it and do extra fun things. You could argue most of this requires access to change the nix config anyway and jq has a pretty good track record of very few CVEs :shrug: But there is also the auditability angle of looking at the age secrets and thinking "ok this isn't readable to anyone other than root" but there's a duplicate somewhere else on your machine you might never notice.

gepbird commented 1 month ago

@06kellyjac

Set the owner of the secret to match the authelia instance. This is the best (least priviledged) method as only the relevant service can access the file. If multiple services need the same file you could look at putting each user in a group.

Yes, except instead of altering the original secret that the user provided, we could copy that to a path that only the current authelia instance user can access.

I would also advise pulling the user from config. rather than hardcoding it to a string. Note the docs that each instance gets a user and is in the format authelia- where name is what you chose for the instance. In this case main so authelia-main.

Maybe I don't understand your point, I think the current implementation is good: if user is not specified, then authelia-<name> user will be created, otherwise the end user is responsible for managing the provided user.

If you did not face this difficulty with other services then they may be running with excessive access e.g. root. Or maybe the difficulty is just due to there being not just 1 authelia user but one per instance?

Other services I use work with agenix out of the box, usually only some bootstrap script or systemd has root access, never the whole service. The fact that authelia is using multiple users doesn't really affect it.

Edit: I didn't spot that mention of transmission but personally I don't like that it uses elevated permissions to make a duplicate copy of your secret.

I do agree that running authelia (or any service really) under root is a very bad idea. However I think running some bootstrap script (like copying a secret to an authelia's directory) as root is fine. Just to give you a few more examples how the modules I use solve this:

I'd start thinking about what secret I could provide to jq to make it do something else or maybe messing with cfg.user so it escapes the quotes around it and do extra fun things. You could argue most of this requires access to change the nix config anyway and jq has a pretty good track record of very few CVEs 🤷 But there is also the auditability angle of looking at the age secrets and thinking "ok this isn't readable to anyone other than root" but there's a duplicate somewhere else on your machine you might never notice.

As you said I'd argue with changing and applying a nix config requires elevated privileges, with that power one could do much bigger damage to a system than stealing authelia secrets. In my opinion the duplicated secret files (eg. by transmission) are reasonable to exist due to this permission issue, and are only readable by root and a transmission user. I don't think duplicating a well protected file will cause security issues. For someone who just recently got into agenix might not even know that their encrypted secrets are decrypted and readable by root under /run/agenix.