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/transmission: improvements #350085

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

Conversation

diniamo
Copy link
Contributor

@diniamo diniamo commented Oct 20, 2024

As per Transmission's documentation, the umask option should be a string.

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/)
  • 24.11 Release Notes (or backporting 23.11 and 24.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.

@diniamo diniamo changed the title nixos/transmission: fix settings.umask to be of type str nixos/transmission: fix permissions Oct 22, 2024
@diniamo
Copy link
Contributor Author

diniamo commented Oct 22, 2024

@ju1m could you help with this PR?

@ju1m
Copy link
Contributor

ju1m commented Oct 23, 2024

@diniamo thanks for spotting this mistake, type = with types; either str int could be used for backward compatibility, and a warning can be added to issue a deprecation message of int for the next stable version (nixos-25.05) when typeOf cfg.settings.umask == "string".

@diniamo
Copy link
Contributor Author

diniamo commented Oct 23, 2024

Does that mean I should remove the release note entry?

@ju1m
Copy link
Contributor

ju1m commented Oct 23, 2024

@diniamo , sorry I did not notice you had already done all that work, what you did is better, thanks!

@ju1m ju1m self-requested a review October 23, 2024 17:11
Copy link
Contributor

@ju1m ju1m left a comment

Choose a reason for hiding this comment

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

LGTM

@diniamo
Copy link
Contributor Author

diniamo commented Oct 23, 2024

@ju1m just to make sure: I changed serviceConfig.StateDirectoryMode to cfg.downloadDirPermissions as well. It's needed because other things can't access any subdirectories, if they can't access the parent directory. Alternatively, do you have a better solution?

@ju1m
Copy link
Contributor

ju1m commented Oct 23, 2024

@ju1m just to make sure: I changed serviceConfig.StateDirectoryMode to cfg.downloadDirPermissions as well. It's needed because other things can't access any subdirectories, if they can't access the parent directory. Alternatively, do you have a better solution?

Right, no I would not change StateDirectoryMode= because the better way security-wise is to add users to services.transmission.group, or to set cfg.settings.download-dir & co. to some path where intended users have access to them (since they are bind-mounted into the RootDirectory= using BindPaths=, transmission will be able to access them).

Besides, if cfg.downloadDirPermissions is null (the default) then StateDirectoryMode= would default to 755 instead of the current 750 which would open transmission's state folder read-only to every users. Sure ${cfg.home}/${settingsDir}/settings.json is forced to 600, but yet, AFAICS it's not necessary to relax the 750, just use services.transmission.group, which is allowed read-only.

@@ -358,7 +356,7 @@ in
"transmission/${downloadsDir}"
"transmission/${watchDir}"
];
StateDirectoryMode = mkDefault 750;
StateDirectoryMode = cfg.downloadDirPermissions;
Copy link
Contributor

Choose a reason for hiding this comment

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

Please keep 750 and let users add themselves to the transmission group.

@diniamo
Copy link
Contributor Author

diniamo commented Oct 24, 2024

How do I add a user to multiple groups declaratively though?

Edit: seems like removing the downloadDirPermissions option worked. Although what happens if you set it to null is not documented.

@ju1m
Copy link
Contributor

ju1m commented Oct 24, 2024

How do I add a user to multiple groups declaratively though?

users.users."diniamo".extraGroups = [ config.transmission.group ];

Or:

users.groups.${config.transmission.group}.members = [ "diniamo" ];

@diniamo
Copy link
Contributor Author

diniamo commented Oct 24, 2024

And do you know why setting downloadDirPermissions to null makes it 755? I'll add a note about that to the description.

@ju1m
Copy link
Contributor

ju1m commented Oct 24, 2024

And do you know why setting downloadDirPermissions to null makes it 755? I'll add a note about that to the description.

Yes, I explained it in my previous message. But please do not use StateDirectoryMode = cfg.downloadDirPermissions; there's no need to relax 750 since user groups or bind-mounts are a common and more secure way to give access to directories.

@diniamo
Copy link
Contributor Author

diniamo commented Oct 25, 2024

How is this?

@@ -191,6 +189,10 @@ in
and [](#opt-services.transmission.settings.watch-dir).
Note that you may also want to change
[](#opt-services.transmission.settings.umask).

If `null`, the home and the download directories become
Copy link
Contributor

@ju1m ju1m Oct 25, 2024

Choose a reason for hiding this comment

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

Hmm, I think you've misunderstood what I tried to say previously, when I wrote:

Besides, if cfg.downloadDirPermissions is null (the default) then StateDirectoryMode= would default to 755 instead of the current 750 which would open transmission's state folder read-only to every users.

I was describing what would happen if we were to use StateDirectoryMode = cfg.downloadDirPermissions; but that's no longer the case, so downloadDirPermissions == null remains a no-op.

Indeed, the description only documents the If not null, case, but adding users to config.transmission.group is unrelated to that option being null or not, it's related to StateDirectoryMode == "750".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Then that option is very misleading. If I set it to 755, then I expect other users to be able to read the download directories, which is not the case. This is very unintuitive, and the option descriptions don't mention this anywhere. It took a long while before I figured out what was going on.

It would be nice if we could avoid wasting others' time. What do you suggest?

Copy link
Contributor

@ju1m ju1m Oct 26, 2024

Choose a reason for hiding this comment

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

IMHO, downloadDirPermissions is a quirk that should not even exist in the first place, since adding users to services.transmission.group is both more secure and more idiomatic.

Yet this option lingers and we have to deal with it, I would suggest to improve its description by explaining the two main ways to give access to the directories that I explained previously:

  1. the better way security-wise is to add users to services.transmission.group,
  2. or to set cfg.settings.download-dir & co. to some path where intended users have access to them [and use settings.umask = "002" and downloadDirPermissions = "755"]

@diniamo
Copy link
Contributor Author

diniamo commented Oct 26, 2024

How is this?

I did some minimal testing in a VM, and it seems to be working fine.

"transmission/${downloadsDir}"
"transmission/${watchDir}"
];
StateDirectoryMode = mkDefault 750;
Copy link
Contributor

Choose a reason for hiding this comment

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

Please do not remove StateDirectoryMode nor StateDirectory, otherwise they will not be bound in the RootDirectory.

Copy link
Contributor Author

@diniamo diniamo Oct 26, 2024

Choose a reason for hiding this comment

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

I don't exactly understand the bind mount stuff from the systemd docs. Isn't it enough to only bind the top-level directory (/var/lib/transmission)?

Never mind. I'm not digging into this deeper than I have to.

Copy link
Contributor

Choose a reason for hiding this comment

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

yes, but listing subdirs enforces StateDirectoryMode on them:

Except in case of ConfigurationDirectory=, the innermost specified directories will be owned by the user and group specified in User= and Group=. If the specified directories already exist and their owning user or group do not match the configured ones, all files and directories below the specified directories as well as the directories themselves will have their file ownership recursively changed to match what is configured. As an optimization, if the specified directories are already owned by the right user and group, files and directories below of them are left as-is, even if they do not match what is requested. The innermost specified directories will have their access mode adjusted to the what is specified in RuntimeDirectoryMode=, StateDirectoryMode=, CacheDirectoryMode=, LogsDirectoryMode= and ConfigurationDirectoryMode=.

Note that because "transmission/${downloadsDir}" is necessarily /var/lib/transmission/Downloads, StateDirectoryMode does not adjust modes of cfg.settings.download-dir if it's set to something else than the default "${cfg.home}/${downloadsDir}" (cfg.home being another quirk that lingers).

Copy link
Contributor Author

@diniamo diniamo Oct 26, 2024

Choose a reason for hiding this comment

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

I see. Well IMO this entire module deserves a rewrite, but that's a lot of effort, so the latest commit should be fine.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, if it were not for backward compatibility we should drop cfg.home to always use /var/lib/transmission, and drop downloadDirPermissions to let systemd create directories with "750" (or "770" if members of the transmission group should be allowed to delete dangling torrent files that transmission did not delete).

Except for that, the rest of the module is not that bad (obviously I'm biased),
amongst the things that come to mind that can be improved security-wise:

  1. With recent systemd giving access to LoadCredential=/LoadCredentialEncrypted= in ExecStartPre=, those should be used instead of directly using ${cfg.credentialsFile}, as in
    let keyCred = builtins.split ":" "${cfg.privateKeyFile}"; in
    if lib.length keyCred > 1
    then {
    LoadCredentialEncrypted = [ cfg.privateKeyFile ];
    # Note that neither %d nor ${CREDENTIALS_DIRECTORY} works in BindReadOnlyPaths=
    BindReadOnlyPaths = [ "/run/credentials/radicle-node.service/${lib.head keyCred}:${env.RAD_HOME}/keys/radicle" ];
    }
    else {
    LoadCredential = [ "radicle:${cfg.privateKeyFile}" ];
    BindReadOnlyPaths = [ "/run/credentials/radicle-node.service/radicle:${env.RAD_HOME}/keys/radicle" ];
    };
  2. Instead of using BindReadOnlyPaths = [ builtins.storeDir ], systemd.services.transmission.confinement should be used to only mount transmission's closure dependencies, as in
    confinement = {
    enable = true;
    mode = "full-apivfs";
    packages = [
    pkgs.gitMinimal
    cfg.package
    pkgs.iana-etc
    (lib.getLib pkgs.nss)
    pkgs.tzdata
    ];
    };

@diniamo
Copy link
Contributor Author

diniamo commented Oct 26, 2024

Some more stuff, since we are dragging this out anyway.

@diniamo diniamo changed the title nixos/transmission: fix permissions nixos/transmission: improvements Oct 26, 2024
@diniamo
Copy link
Contributor Author

diniamo commented Oct 27, 2024

@ju1m sorry for this taking so long, but could you take a look?

@Aleksanaa
Copy link
Member

We have to wait until after NixOS 24.11 as this is a breaking change and not a trivial fix.

@diniamo
Copy link
Contributor Author

diniamo commented Oct 28, 2024

I don't get it. Why do we have to wait for 24.11?

@Aleksanaa
Copy link
Member

Because breaking changes are blocked in this period to prepare for the next release. See #339153

@Aleksanaa Aleksanaa added the 2.status: wait for branch‐off Waiting for the next Nixpkgs branch‐off label Oct 28, 2024
@diniamo
Copy link
Contributor Author

diniamo commented Oct 29, 2024

@ju1m it looks like I didn't do enough research: transmission 3 (which is the default for the module) still uses the old way of configuring umask (decimal number), while transmission 4 uses the new way (octal number as a string). Should I update the default package as well, or?

@ambroisie
Copy link
Contributor

ambroisie commented Oct 29, 2024

@diniamo should probably be using type = with lib.types; either str int; (which would make it a non-breaking change) to accommodate both versions.

EDIT: maybe adjusting the default to be 2 or "002" depending of the version of cfg.package would be good too.

@diniamo
Copy link
Contributor Author

diniamo commented Oct 29, 2024

I was thinking of something like that as well. Thnaks for that suggestion, I'll implement it.

@ju1m
Copy link
Contributor

ju1m commented Oct 29, 2024

@ju1m it looks like I didn't do enough research: transmission 3 (which is the default for the module) still uses the old way of configuring umask (decimal number), while transmission 4 uses the new way (octal number as a string). Should I update the default package as well, or?

@diniamo, chances are that nixos-25.05 (first NixOS stable release on which this PR will land) may indeed switch to transmission_4 as default for services.transmission, which is currently blocked to transmission_3 for nixos-24.11:

- `transmission` package has been aliased with a `trace` warning to `transmission_3`. Since [Transmission 4 has been released last year](https://github.com/transmission/transmission/releases/tag/4.0.0), and Transmission 3 will eventually go away, it was decided perform this warning alias to make people aware of the new version. The `services.transmission.package` defaults to `transmission_3` as well because the upgrade can cause data loss in certain specific usage patterns (examples: [#5153](https://github.com/transmission/transmission/issues/5153), [#6796](https://github.com/transmission/transmission/issues/6796)). Please make sure to back up to your data directory per your usage:
- `transmission-gtk`: `~/.config/transmission`
- `transmission-daemon` using NixOS module: `${config.services.transmission.home}/.config/transmission-daemon` (defaults to `/var/lib/transmission/.config/transmission-daemon`)

But that would have to be discussed with people involved in #258058

nixos/transmission: improve code

- Remove `with lib;`
- Use `{ name = { ... }; }` instead of `{ name.foo = ...; name.bar =
...; }`
@diniamo
Copy link
Contributor Author

diniamo commented Oct 29, 2024

In that case, I'll leave updating the default to them, and this should be fine.

@diniamo
Copy link
Contributor Author

diniamo commented Oct 29, 2024

@Aleksanaa this is no longer a breaking change.

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.

5 participants