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

nixos/nextcloud: use LoadCredential to read secrets #367433

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

networkException
Copy link
Member

@networkException networkException commented Dec 22, 2024

This pull request adds support for using systemd's LoadCredential= feature to read various secret files used by nextcloud service units.

Previously credentials had to be readable by the nextcloud user, this is now no longer required.

The nextcloud-occ wrapper script has been adjusted to use systemd-run for loading credentials when being called from outside a service.

See the individual commits for more details.

Depends on #365442
Continuation of #152141

Things done

  • Built on platform(s)
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • For non-Linux: Is sandboxing enabled in nix.conf? (See Nix manual)
    • sandbox = relaxed
    • sandbox = true
  • Tested, as applicable:
  • Tested compilation of all packages that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage
  • Tested basic functionality of all binary files (usually in ./result/bin/)
  • 25.05 Release Notes (or backporting 24.11 and 25.05 Release notes)
    • (Package updates) Added a release notes entry if the change is major or breaking
    • (Module updates) Added a release notes entry if the change is significant
    • (Module addition) Added a release notes entry if adding a new NixOS module
  • Fits CONTRIBUTING.md.

Add a 👍 reaction to pull requests you find important.

@github-actions github-actions bot added 6.topic: nixos Issues or PRs affecting NixOS modules, or package usability issues specific to NixOS 8.has: module (update) This PR changes an existing module in `nixos/` labels Dec 22, 2024
@github-actions github-actions bot added 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin 10.rebuild-linux: 1-10 labels Dec 22, 2024
@networkException
Copy link
Member Author

networkException commented Dec 22, 2024

This should be generally well tested already so reviews are encouraged, undrafting when

Comment on lines +90 to +93
runtimeSystemdCredentials = []
++ (lib.optional (cfg.config.dbpassFile != null) "dbpass:${cfg.config.dbpassFile}")
++ (lib.optional (cfg.config.objectstore.s3.enable) "s3_secret:${cfg.config.objectstore.s3.secretFile}")
++ (lib.optional (cfg.config.objectstore.s3.sseCKeyFile != null) "s3_sse_c_key:${cfg.config.objectstore.s3.sseCKeyFile}");
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
runtimeSystemdCredentials = []
++ (lib.optional (cfg.config.dbpassFile != null) "dbpass:${cfg.config.dbpassFile}")
++ (lib.optional (cfg.config.objectstore.s3.enable) "s3_secret:${cfg.config.objectstore.s3.secretFile}")
++ (lib.optional (cfg.config.objectstore.s3.sseCKeyFile != null) "s3_sse_c_key:${cfg.config.objectstore.s3.sseCKeyFile}");
runtimeSystemdCredentials = lib.optional (cfg.config.dbpassFile != null) "dbpass:${cfg.config.dbpassFile}"
++ lib.optional (cfg.config.objectstore.s3.enable) "s3_secret:${cfg.config.objectstore.s3.secretFile}"
++ lib.optional (cfg.config.objectstore.s3.sseCKeyFile != null) "s3_sse_c_key:${cfg.config.objectstore.s3.sseCKeyFile}";

Copy link
Member Author

Choose a reason for hiding this comment

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

I prefer consistent indentation

Comment on lines +137 to 148
nextcloud-notify_push_setup = {
wantedBy = [ "multi-user.target" ];
requiredBy = [ "nextcloud-notify_push.service" ];
after = [ "nextcloud-notify_push.service" ];
serviceConfig = {
Type = "oneshot";
User = "nextcloud";
Group = "nextcloud";
ExecStart = "${lib.getExe cfgN.occ} notify_push:setup ${cfg.nextcloudUrl}/push";
LoadCredential = config.systemd.services.nextcloud-cron.serviceConfig.LoadCredential;
};
};
Copy link
Member

Choose a reason for hiding this comment

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

This will definitely not work. notify_push:setup verifies if the configuration is correct and checks if the domain and trusted proxy settings are correct. It is not similar to nextcloud-setup and must be run after the notify_push service is up. If it fails the service is none functional.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not too invested in reducing how many credentials the long running process can read. The ordering works just fine like this with after and Type=notify.

In general having nextcloud-notify_push_setup.service compared to postStart just changes which service fails in case something goes wrong

Copy link
Member

Choose a reason for hiding this comment

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

This will definitely not work. notify_push:setup verifies if the configuration is correct and checks if the domain and trusted proxy settings are correct

@SuperSandro2000 I think @networkException is right here and I fail to understand what'll break exactly either.
Please elaborate.

@Ma27
Copy link
Member

Ma27 commented Dec 22, 2024

Thanks!
Will try to review in the upcoming days.

…-and-secrets

This patch adds a subtest and corresponding configuration to
with-declarative-redis-and-secrets to test for nextcloud notify_push
to be working, just as in with-postgresql-and-redis.

As notify_push needs to connect to the database, including it
in this test checks that it can read the dbpassFile properly.
This patch replaces the use of writeScriptBin for the nextcloud-occ
script with writeShellApplication, enabling shell checking.

This patch also updates various invocations of the script to
use lib.getExe.
@networkException networkException force-pushed the nextcloud-loadcredential branch 2 times, most recently from 6c60763 to 6fece8a Compare December 22, 2024 22:12
@networkException networkException marked this pull request as ready for review December 23, 2024 16:27
nixos/modules/services/web-apps/nextcloud.nix Outdated Show resolved Hide resolved
# On Nextcloud ≥ 26, it is not necessary to patch the database files to prevent
# an automatic creation of the database user.
environment.NC_setup_create_db_user = lib.mkIf (nextcloudGreaterOrEqualThan "26") "false";
};
nextcloud-cron = {
after = [ "nextcloud-setup.service" ];
# FIXME: In contrast to the occ wrapper script running phpCli directly will not
# set NEXTCLOUD_CONFIG_DIR correctly currently.
Copy link
Member

Choose a reason for hiding this comment

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

Can you explain why you added a fixme for that?

Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe a note is better, I added this after removing the explicit (somewhat duplicate) variable from the nextcloud-update-db service

umask 0077
# NOTE: The phpfpm runtime directory is currently preserved
# between restarts.
rm -rf /run/phpfpm/nextcloud-credentials/
Copy link
Member

Choose a reason for hiding this comment

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

I think it would be nicer to use $RUNTIME_DIRECTORY/nextcloud-credentials/ here.
It's rather unlikely that you ever change serviceConfig.RuntimeDirectory in a phpfpm unit, but with that change we'd be safe here.

Copy link
Member

Choose a reason for hiding this comment

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

Also... doesn't this mean that other phpfpm instances can see these values (even though only readable as root, so not from PHP code)?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it would be nicer to use $RUNTIME_DIRECTORY/nextcloud-credentials/ here.

I don't think this currently applies here but $RUNTIME_DIRECTORY has the pesky detail that you can technically specify multiple directories which will end up as /run/a:/run/b in the variable

Copy link
Member Author

Choose a reason for hiding this comment

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

Also... doesn't this mean that other phpfpm instances can see these values (even though only readable as root, so not from PHP code)?

Yes sadly thats the case, briefly as root and then as nextcloud. I don't know phpfpm good enough to know what would happen if the service were rootless, but that would be much cleaner here

Copy link
Member

Choose a reason for hiding this comment

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

I don't think this currently applies here but $RUNTIME_DIRECTORY has the pesky detail that you can technically specify multiple directories which will end up as /run/a:/run/b in the variable

Meh, makes perfect sense for a variable name in signular :/
but good point, I'll retract that.

Yes sadly thats the case, briefly as root and then as nextcloud. I don't know phpfpm good enough to know what would happen if the service were rootless, but that would be much cleaner here

My main problem is that the runtime directory is used for each phpfpm service, i.e. each PHP application on the same host will have this in its runtime directory (sure, different user then, but this smells like a disaster waiting to happen).

But given that we can specify multiple runtime directories, wouldn't it be an option to add another directory to the phpfpm-nextcloud service and place the secrets into that dir?

Copy link
Member Author

Choose a reason for hiding this comment

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

But given that we can specify multiple runtime directories, wouldn't it be an option to add another directory to the phpfpm-nextcloud service and place the secrets into that dir?

Sure, good point. I'll work on this in a bit

Copy link
Member Author

Choose a reason for hiding this comment

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

I've added a separate runtime directory to the service (https://github.com/NixOS/nixpkgs/compare/dedb0ac10499c7406ce55399db5ac7fdf3568907..4d4d3672a52c630e4a3f7970b5c3aa1bb2beda00). Note that sadly RuntimeDirectoryPreserve=true as well as the mode also applies. I also don't know a way to avoid the lib.mkForce here.

Comment on lines +137 to 148
nextcloud-notify_push_setup = {
wantedBy = [ "multi-user.target" ];
requiredBy = [ "nextcloud-notify_push.service" ];
after = [ "nextcloud-notify_push.service" ];
serviceConfig = {
Type = "oneshot";
User = "nextcloud";
Group = "nextcloud";
ExecStart = "${lib.getExe cfgN.occ} notify_push:setup ${cfg.nextcloudUrl}/push";
LoadCredential = config.systemd.services.nextcloud-cron.serviceConfig.LoadCredential;
};
};
Copy link
Member

Choose a reason for hiding this comment

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

This will definitely not work. notify_push:setup verifies if the configuration is correct and checks if the domain and trusted proxy settings are correct

@SuperSandro2000 I think @networkException is right here and I fail to understand what'll break exactly either.
Please elaborate.

# NOTE: This early returns the script when nextcloud is in maintenance mode
# or needs `occ upgrade`. Using ExecCondition= is not possible here
# because it doesn't work with systemd credentials.
${lib.getExe occ} status -e; [ $? -ne 0 ] && exit 0
Copy link
Member

Choose a reason for hiding this comment

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

Hmm, doesn't this fail the service since script adds a set -e?

Copy link
Member Author

Choose a reason for hiding this comment

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

I've changed the implementation to use status --output=json, feels nicer compared to -e anyways: https://github.com/NixOS/nixpkgs/compare/4d4d3672a52c630e4a3f7970b5c3aa1bb2beda00..3d64615e8a60490360283fad2ed7b8bca6f29372

script = ''
# NOTE: This early returns the script when nextcloud is in maintenance mode
# or needs `occ upgrade`. Using ExecCondition= is not possible here
# because it doesn't work with systemd credentials.
Copy link
Member

Choose a reason for hiding this comment

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

fwiw opened systemd/systemd#35788 to discuss whether we could change this upstream.

@networkException networkException force-pushed the nextcloud-loadcredential branch from dedb0ac to 4d4d367 Compare January 1, 2025 00:08
networkException and others added 3 commits January 1, 2025 02:14
This patch adds support for using systemd's LoadCredential
feature to read various secret files used by nextcloud service
units.

Previously credentials had to be readable by the nextcloud user,
this is now no longer required.

The nextcloud-occ wrapper script has been adjusted to use
systemd-run for loading credentials when being called from
outside a service.

In detail this change touches various details of the module:

- The nix_read_secret() php function now takes the name of a
  file relative to the path specified in the CREDENTIALS_DIRECTORY
  environment variable.
- The nix_read_secret() now exits with error code 1 instead of
  throwing a RuntimeException as this will properly error out
  the nextcloud-occ script
- Only the nextcloud-setup service unit has the adminpass credential
  added in addition to the other credentials
- Uses of ExecCondition= in nextcloud-cron and nextcloud-update-db
  have been replaced by a shell conditional as ExecCondition currently
  doesn't support credentials
- The phpfpm-nextcloud service now runs a preStart script to make
  the credentials it gets readable by the nextcloud user as the
  unit runs as root but the php process itself as nextcloud.
- To invoke occ notify_push:setup when using nextcloud notify_push
  a new service has been added that replaces the preStart script
  in nextcloud-notify_push.service. This has been done as the
  main executable only needs the database password credential.

Co-authored-by: lassulus <[email protected]>
This patch changes the implementation of the subtests to
check for redis' cache being non empty to only run redis-cli
and jq in a shell and assert the returned length in python.

This fixes jq "len" simply not compiling and makes sure
regressions get noticed.
@networkException networkException force-pushed the nextcloud-loadcredential branch from 4d4d367 to 3d64615 Compare January 1, 2025 01:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
6.topic: nixos Issues or PRs affecting NixOS modules, or package usability issues specific to NixOS 8.has: module (update) This PR changes an existing module in `nixos/` 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin 10.rebuild-linux: 1-10
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants