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

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

Conversation

networkException
Copy link
Member

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 (currently included in this branch)
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 +1107 to +1126
# NOTE: The phpfpm runtime directory is currently preserved
# between restarts.
rm -rf /run/phpfpm/nextcloud-credentials/
mkdir -p /run/phpfpm/nextcloud-credentials/
cp $CREDENTIALS_DIRECTORY/* /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.

Why not symlink them?

Copy link
Member Author

Choose a reason for hiding this comment

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

As per my comment above the files are not readable by the nextcloud user.

$ pwd
/run/credentials/phpfpm-nextcloud.service
$ ls -lah
total 4.0K
dr-x------  2 root root  60 Dec 22 20:58 .
drwxr-xr-x 11 root root 220 Dec 22 20:58 ..
-r--------  1 root root   7 Dec 22 20:58 dbpass

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}";

Comment on lines +119 to +120
# NOTE: If there are no credentials that are required at runtime then there's no need
# to load any credentials.
Copy link
Member

Choose a reason for hiding this comment

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

IMO we should always execute the script the same way to avoid edge cases and to make testing changes easier.

Copy link
Member Author

Choose a reason for hiding this comment

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

If there are no credentials the script would try to use systemd-run from within other services, which fails.

It needs some way of knowing if its running in a service or that it shouldn't expect credentials

Copy link
Member

Choose a reason for hiding this comment

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

On a first glance I'd be fine with that, but it would be nice to have a test which runs occ from the "outside" to make sure we immediately notice if we're about to break it (that means, a command that fails if secrets are unreadable).

Copy link
Member Author

@networkException networkException Dec 22, 2024

Choose a reason for hiding this comment

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

We have very basic checks for nextcloud-occ everywhere:

with subtest("Ensure nextcloud-occ is working"):
nextcloud.succeed("nextcloud-occ status")

If you have ideas to expand this let me know

Copy link
Member Author

Choose a reason for hiding this comment

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

This is the output of nextcloud-occ status with logging enabled for whenever a file path is being read

       function nix_read_secret($file) {
         $credential_file = getenv("CREDENTIALS_DIRECTORY") . "/" . $file;
+        error_log($credential_file);
         if (!is_readable($credential_file)) {
$ nextcloud-occ status
/run/credentials/run-u13.service/dbpass
  - installed: true
  - version: 30.0.4.1
  - versionstring: 30.0.4
  - edition: 
  - maintenance: false
  - needsDbUpgrade: false
  - productname: Nextcloud
  - extendedSupport: false

I've just pushed additional changes that make the script exit with error code 1, the previously thrown RuntimeException would be caught somewhere:

subtest: Ensure nextcloud-occ is working
nextcloud: must succeed: nextcloud-occ status
nextcloud # [   23.857755] systemd[1]: Starting /nix/store/b1wvkjx96i3s7wblz38ya0zr8i93zbc5-coreutils-9.5/bin/env NEXTCLOUD_CONFIG_DIR=/var/lib/nextcloud/config /nix/store/c6i258wgrwgg8aladc0v97k39cs2sgp6-php-with-extensions-8.3.15/bin/php occ status...
nextcloud # [   23.897224] systemd[1]: Started /nix/store/b1wvkjx96i3s7wblz38ya0zr8i93zbc5-coreutils-9.5/bin/env NEXTCLOUD_CONFIG_DIR=/var/lib/nextcloud/config /nix/store/c6i258wgrwgg8aladc0v97k39cs2sgp6-php-with-extensions-8.3.15/bin/php occ status.
nextcloud # [   23.984784] nextcloud-update-db-start[1148]: oc_filecache table updated successfully.
nextcloud # [   23.985753] nextcloud-update-db-start[1148]: Adding additional systag_by_objectid index to the oc_systemtag_object_mapping table, this can take some time...
nextcloud # [   24.110756] systemd[1]: run-u4.service: Main process exited, code=exited, status=1/FAILURE
nextcloud # [   24.112105] systemd[1]: run-u4.service: Failed with result 'exit-code'.
nextcloud: output: Cannot read credential 'dbpass' passed by NixOS, $CREDENTIALS_DIRECTORY is not set!

Test "Ensure nextcloud-occ is working" failed with error: "command `nextcloud-occ status` failed (exit code 1)"

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

@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 and others added 2 commits December 22, 2024 23:10
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.
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