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

Are agenix secrets supported? #351

Open
tbaumann opened this issue Nov 18, 2024 · 6 comments
Open

Are agenix secrets supported? #351

tbaumann opened this issue Nov 18, 2024 · 6 comments

Comments

@tbaumann
Copy link

I started with something simple.

  shb.arr = {
    sonarr.enable = true;
    sonarr.settings.ApiKey = config.age.secrets.arr-api-key.path;
    sonarr.subdomain = "radarr";
    sonarr.domain = "rak.baumann.ma";
  };

My secrets are agenix or ragenix to be precise.

         at /nix/store/9g88fck8ggiah5znz5xn2kxzfr6l7cdq-source/lib/modules.nix:413:36:

          412|       # modules recursively. It returns the final list of unique-by-key modules
          413|       filterModules = modulesPath: { disabled, modules }:
             |                                    ^
          414|         let

       … while calling anonymous lambda

         at /nix/store/9g88fck8ggiah5znz5xn2kxzfr6l7cdq-source/lib/modules.nix:439:31:

          438|           disabledKeys = concatMap ({ file, disabled }: map (moduleKey file) disabled) disabled;
          439|           keyFilter = filter (attrs: ! elem attrs.key disabledKeys);
             |                               ^
          440|         in map (attrs: attrs.module) (builtins.genericClosure {

       … from call site

         at /nix/store/9g88fck8ggiah5znz5xn2kxzfr6l7cdq-source/lib/modules.nix:400:22:

          399|           let
          400|             module = checkModule (loadModule args parentFile "${parentKey}:anon-${toString n}" x);
             |                      ^
          401|             collectedImports = collectStructuredModules module._file module.key module.imports args;

       … while calling anonymous lambda

         at /nix/store/9g88fck8ggiah5znz5xn2kxzfr6l7cdq-source/lib/modules.nix:365:11:

          364|         else
          365|           m: m;
             |           ^
          366|

       … from call site

         at /nix/store/9g88fck8ggiah5znz5xn2kxzfr6l7cdq-source/lib/modules.nix:400:35:

          399|           let
          400|             module = checkModule (loadModule args parentFile "${parentKey}:anon-${toString n}" x);
             |                                   ^
          401|             collectedImports = collectStructuredModules module._file module.key module.imports args;

       … while calling 'loadModule'

         at /nix/store/9g88fck8ggiah5znz5xn2kxzfr6l7cdq-source/lib/modules.nix:336:53:

          335|       # Like unifyModuleSyntax, but also imports paths and calls functions if necessary
          336|       loadModule = args: fallbackFile: fallbackKey: m:
             |                                                     ^
          337|         if isFunction m then

       … from call site

         at /nix/store/9g88fck8ggiah5znz5xn2kxzfr6l7cdq-source/lib/modules.nix:354:14:

          353|           throw "Module imports can't be nested lists. Perhaps you meant to remove one level of lists? Definitions: ${showDefs defs}"
          354|         else unifyModuleSyntax (toString m) (toString m) (applyModuleArgsIfFunction (toString m) (import m) args);
             |              ^
          355|

       … while calling 'unifyModuleSyntax'

         at /nix/store/9g88fck8ggiah5znz5xn2kxzfr6l7cdq-source/lib/modules.nix:454:34:

          453|      of ‘options’, ‘config’ and ‘imports’ attributes. */
          454|   unifyModuleSyntax = file: key: m:
             |                                  ^
          455|     let

       … from call site

         at /nix/store/9g88fck8ggiah5znz5xn2kxzfr6l7cdq-source/lib/modules.nix:354:59:

          353|           throw "Module imports can't be nested lists. Perhaps you meant to remove one level of lists? Definitions: ${showDefs defs}"
          354|         else unifyModuleSyntax (toString m) (toString m) (applyModuleArgsIfFunction (toString m) (import m) args);
             |                                                           ^
          355|

       … while calling 'applyModuleArgsIfFunction'

         at /nix/store/9g88fck8ggiah5znz5xn2kxzfr6l7cdq-source/lib/modules.nix:488:39:

          487|
          488|   applyModuleArgsIfFunction = key: f: args@{ config, options, lib, ... }:
             |                                       ^
          489|     if isFunction f then applyModuleArgs key f args else f;

       … from call site

         at /nix/store/9g88fck8ggiah5znz5xn2kxzfr6l7cdq-source/lib/modules.nix:489:8:

          488|   applyModuleArgsIfFunction = key: f: args@{ config, options, lib, ... }:
          489|     if isFunction f then applyModuleArgs key f args else f;
             |        ^
          490|

       … while calling 'isFunction'

         at /nix/store/9g88fck8ggiah5znz5xn2kxzfr6l7cdq-source/lib/trivial.nix:929:16:

          928|   */
          929|   isFunction = f: builtins.isFunction f ||
             |                ^
          930|     (f ? __functor && isFunction (f.__functor f));

       error: getting status of '/run/agenix/arr-api-key': No such file or directory

Yea, that file doesn't exist at build time.

@ibizaman
Copy link
Owner

ibizaman commented Nov 18, 2024

Hi! Sorry for the lack of documentation on this part. The configuration should be:

sonarr.settings.ApiKey.source = config.age.secrets.arr-api-key.path;

With the additional .source option.

This is needed because the type of the APIKey is secretFileType and that type is defined here, it's actually a submodule with a source field. And if you're curious this type is used because when I generate the config file, the .source field is used to identify what values are coming out of band.

Until more doc is there, you may want to get inspiration from the tests.

Now, TBH I'm not sure why you're getting such a convoluted error. That's definitely something that should be improved.

@tbaumann
Copy link
Author

Ah thanks for the explanation. I had looked at other places how Secrets Files were used (ldap for example) and it was different.

Thanks for the hint about the tests. I was a bit worried about not finding a single example of how to use it anywhere on the net.

I was using nixarr so far. But I love what you did with the ApiKeys and other settings and I plan to use other selfhosted blocks in the future.

Perhaps if I may ask here, what's the right pattern to use the monitoring stack with multiple machines?

I have a bunch of different machines that should collect logs and metrics and the log host should aggregate them. Ideally with full knowledge of which services export metrics.

@tbaumann
Copy link
Author

The media group didn't get created. Huh. In the code it looks like that's not conditional at all.

Also another dumb question, do I need to have a vhost per service? I was using mDNS .local addresses so far. It might be time for real DNS...

@ibizaman
Copy link
Owner

I had looked at other places how Secrets Files were used (ldap for example) and it was different.

I totally understand the confusion. Sorry about that. I'm slowly updating all places to use the secret contract.

nixarr

TIL about nixarr. That's a really nice project!

I love what you did with the ApiKeys and other settings

Thanks :) The method I'm using is quite universal, I use it in all the services in Self Host Blocks where it is relevant. You might be interested to know that I'm (quite slowly) upstreaming this part to nixpkgs NixOS/nixpkgs#328472 so one day it'll be more widespread, hopefully.

I plan to use other selfhosted blocks in the future

That's really pleasing to hear. ❤️ I'm working on mostly documentation right now so you might get better docs when you get to it. Otherwise, feel free to open an issue or join the matrix channel to ask question. I mean instead of guessing the options is they're undocumented. Oh that makes me think this page lists all options, even those that don't have an accompanying example or manual. https://shb.skarabox.com/options.html You might get some inspiration there already.

monitoring stack with multiple machines

That's something I've not yet well investigated. I mean, since Self Host Blocks uses the same machinery as nixpkgs, it won't help you to manage a cluster of machines. I've seen projects manage IPs of servers and other cluster related values but don't remember their names right now.

The best I could tell you right now is to add yourself a scraper like so:

services.prometheus.scrapeConfigs = [
  {
    job_name = "my other server - netdata";
    metrics_path = "/api/v1/allmetrics?format=prometheus&help=yes&source=as-collected";
    static_configs = [
      {
        targets = ["192.168.1.150:19999"];
      }
    ];
  }
];

You'll need to hardcode the IP in some way. It could be done more dynamically by having a DNS server you control.

There is a monitoring block in Self Host Blocks. What I do on my server is enable it and add some scrapers for custom jobs.

The media group didn't get created.

Yeah that's dumb. I made it work for my use case a while ago and didn't go back in that module for a while. I suppose it's failing because it's telling you the media group does not exist?

Actually in my own config there's no mention of creating the media group. Neither in the tests. What error do you get exactly?

do I need to have a vhost per service

Not necessarily. On my DNS provider, I have a two AAAA records. One from the my domain name, say example.com, to my IP address and one for all subdomains, say *.example.com, to my IP address. That's enough for the external config.

Then, that will reach at some point your router and Nginx will pick up the request and redirect it to the correct service using the subdomain.

@tbaumann
Copy link
Author

monitoring stack with multiple machines

That's something I've not yet well investigated. I mean, since Self Host Blocks uses the same machinery as nixpkgs, it won't help you to manage a cluster of machines. I've seen projects manage IPs of servers and other cluster related values but don't remember their names right now.

The best I could tell you right now is to add yourself a scraper like so:

I think it can be done with NixOps. I did once try to get the ball rolling with this question
https://discourse.nixos.org/t/datacenter-abstraction-service-discovery/54802/

The nix-topology module does seem to do a similar task. They even attempt to discover what services are exposed on a machine. (in a very ugly way)

My scrape config is hardcoded as well at the moment. https://github.com/tbaumann/nix-conf/blob/ada81cf338e9cc2d41664d356da9096ef1b750dd/hosts/nas/default.nix#L92

I will use the selfhost blocks to set up the server I guess. That would improve a lot for me already, especially the grafana bootstrap stuff.

The media group didn't get created.

Yeah that's dumb. I made it work for my use case a while ago and didn't go back in that module for a while. I suppose it's failing because it's telling you the media group does not exist?

Actually in my own config there's no mention of creating the media group. Neither in the tests. What error do you get exactly?

Specifically agenix complains that the group media isn't valid for the secrets file.
I can see why

› getent group media

~ 
› getent passwd sonarr
sonarr:x:274:274::/var/lib/sonarr:/run/current-system/sw/bin/nologin

But I also think exraGroups should declare the group. I need to think a bit more...

@tbaumann
Copy link
Author

tbaumann commented Nov 20, 2024

yup, group needs to be declared not just referenced.

users.groups.media = {}; 
› getent group media
media:x:974:sonarr

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

No branches or pull requests

2 participants