-
-
Notifications
You must be signed in to change notification settings - Fork 14.6k
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/radicale: add settings option #120440
Conversation
c56d28c
to
0fbed8c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a few comments to think on. Also, are the old versions of radicale
still supported upstream?
@@ -43,7 +54,7 @@ in | |||
''; | |||
}; | |||
|
|||
services.radicale.config = mkOption { | |||
config = mkOption { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this still needed? Presumably for the older versions of radicale
? If so, can set add a comment about when you would use which option and how they are mutually exclusive. Maybe an assertion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
settings
should also work with older versions, but I kept config
for backwards compatibility with existing configuration.nix
s
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem with this is that third-party modules can't rely on settings
. Every module that wants to read or write settings
options has the chance to not work in case the user doesn't use settings
. And it's even worse: A third-party module writing to settings
makes the settings == {}
check fail, meaning the users config
option gets ignored! Because of this I think it might be better to actually remove this option.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And it's even worse: A third-party module writing to
settings
makes thesettings == {}
check fail, meaning the usersconfig
option gets ignored! Because of this I think it might be better to actually remove this option.
That's not true: If both config
and settings
are set an assertion is triggerred.
Nevertheless, I'm all in favor of removing config
but it might make sense to first mark it as deprecated using lib.warn
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed
@@ -84,9 +150,25 @@ in | |||
)); | |||
User = "radicale"; | |||
Group = "radicale"; | |||
StateDirectory = "radicale/collections"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Link to upstream documentation with comment so we can track changes would be useful. Additionally, will these hardening options break older versions of radicale
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They will only break configurations where data is stored at a non-standard location.
cfg = config.services.radicale; | ||
|
||
confFile = pkgs.writeText "radicale.conf" cfg.config; | ||
format = let |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't the ini
format work here? That would be much easier to read...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, that's why I had to create my own. The IP addresses Radicale listens on have to be given as a comma-separated list: https://radicale.org/3.0.html#tutorials/basic-configuration/addresses. I thought requiring users to specify e.g. hosts = "0.0.0.0:5232, [::]:5232"
is a little unnatural.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have a (unit) test to ensure our formatter is doing what we expect it to do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, there is nixos/tests/radicale3.nix
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No no I mean: can we actually format the IPs as we claim? The nixos (integration) test just covers one case as far as I can see. I would love to have a few tests. One case for each of the permissible input values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And essentially, I'm not doing anything except for replacing lists of strings with comma-separated strings and then I use lib.generators.toINI
, which is tested in lib/tests/misc.nix
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No no I mean: can we actually format the IPs as we claim? The nixos (integration) test just covers one case as far as I can see. I would love to have a few tests. One case for each of the permissible input values.
Wouldn't we have to put the generator in lib/
for that? I honestly don't think unit tests are needed for such a simple thing as concatStringsSep ", "
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I extended formats.ini
to support this with #121613
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot!
@SuperSandro2000 I did not ping @andir without a reason: I know he uses Radicale: https://github.com/andir/infra/tree/master/config/servers/mail/radicale |
Thats not my fault. He blocked me and when I edit the reviewer list he gets removed. |
No, they are not. But using a newer version requires changes to the configuration (and the data format is different between version 1 and the others), so we keep them for old |
80cebea
to
8d3ed4a
Compare
cfg = config.services.radicale; | ||
|
||
confFile = pkgs.writeText "radicale.conf" cfg.config; | ||
format = let |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have a (unit) test to ensure our formatter is doing what we expect it to do?
type = "from_file"; | ||
file = toString rightsFile; | ||
}; | ||
|
||
environment.systemPackages = [ cfg.package ]; | ||
|
||
users.users.radicale = | ||
{ uid = config.ids.uids.radicale; | ||
description = "radicale user"; | ||
home = "/var/lib/radicale"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this requires a changelog entry so an admin can put special attention towards this change. It is subtile as the service might start but the data might no longer be "there" if we forgot some case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, see the list of todos at the very top. I will add a changelog entry once we decided on what the changes will be.
e4798a9
to
aba0248
Compare
I applied some more hardening options to the systemd unit. Maybe @mweinelt who's been doing that a lot lately wants to have a look? |
I suggest the following: We remove the old package versions from Nixpkgs since keeping them gives a sense of them still being supported. In the NixOS service, we add an assertion that checks whether |
I usually add systemd-analyze to the nixos test, so I can revisit the results quickly. diff --git a/nixos/tests/radicale3.nix b/nixos/tests/radicale3.nix
index cda43b6c06c..0fedaeea705 100644
--- a/nixos/tests/radicale3.nix
+++ b/nixos/tests/radicale3.nix
@@ -74,5 +74,7 @@ in {
with subtest("Test web interface"):
machine.succeed("curl --fail http://${user}:${password}@localhost:${port}/.web/")
+
+ machine.log(machine.succeed("systemd-analyze security radicale.service"))
'';
}) Other than that hardening means you better know what interfaces radicale needs, which I do not.
Can't lock down network things further, but |
Doesn't work. Radicale definitely needs |
Then you can add that to |
1807f71
to
f6d0cba
Compare
That's what I did. Turns out is uses syscalls so no |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The hardening is even more restrictive now, but I think NixOS modules are not supposed to be usable for everyone, they just provide an opinionated default.
@@ -84,9 +161,43 @@ in | |||
)); | |||
User = "radicale"; | |||
Group = "radicale"; | |||
StateDirectory = "radicale/collections"; | |||
# Hardening | |||
CapabilityBoundingSet = ""; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs CAP_NET_BIND_SERVICE
if you want to use a port < 1024. How should we handle that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Best case: Socket activation, if supported.
Second best: Only pass it in if port < 1024
.
I don't think python actively requests capabilities, so you'd need to put it into AmbientCapabilities
as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Best case: Socket activation, if supported.
not yet: Kozea/Radicale#1141
Second best: Only pass it in if
port < 1024
.
Then we need a new service option because we shouldn't parse settings.server.hosts
.
We can make it dependent on bindLocalhost
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, the current implementation doesn't have the permissions to bind to ports < 1024, so they should also be given by hand in the future.
IPAddressAllow = mkIf bindLocalhost "localhost"; | ||
IPAddressDeny = mkIf bindLocalhost "any"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this problematic? One scenario I can think of is a hook accessing the (non-local) network. Does anyone do that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't feel comfortable putting a hostname in there, but maybe that's just the networker in me speaking. But making it contingent on bindLocalhost
sounds like a sensible approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not actually a hostname, it's a special name interpreted as 127.0.0.0/8 ::1/128
by systemd: https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#IPAddressAllow=ADDRESS%5B/PREFIXLENGTH%5D%E2%80%A6
They should create a good basis to use the package and should be applicable to a or the most common use case(s). It is okay if they don't work for every possible use case but they should supply things like extraConfig to make it easier to adapt them to my use case. |
I would really appreciate some feedback on this. |
f6d0cba
to
b529613
Compare
Instead of an assertion, we might just want to use |
You can use the |
b529613
to
f8d41d5
Compare
f8d41d5
to
6ffacb3
Compare
6ffacb3
to
0ad341e
Compare
0ad341e
to
fd8cc3d
Compare
The radicale version is no longer chosen automatically based on system.stateVersion because that gave the impression that old versions are still supported.
fd8cc3d
to
ec10438
Compare
From my point of view, this is ready now. Please test whether the hardening breaks your deployments (I documented the most common case in the release notes). Let me know if you think anything needs more documentation. |
ec10438
to
762be5c
Compare
I will merge this soon unless someone complains or prefers that we call the new option |
Motivation for this change
NixOS/rfcs#42
Things done
sandbox
innix.conf
on non-NixOS linux)nix-shell -p nixpkgs-review --run "nixpkgs-review wip"
./result/bin/
)nix path-info -S
before and after)I'm looking for comments on my approach.
The following points need to be solved:
nixosTests.radicale
is broken cause it uses/tmp
filesystem_folder
. Is documenting that change sufficient? Should we add an option to add paths toReadWritePaths
?