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/postfix: systemd sandbox #93305

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

Conversation

tnias
Copy link
Contributor

@tnias tnias commented Jul 16, 2020

Motivation for this change

Add systemd service sandbox features.

Things done
  • Tested using sandboxing (nix.useSandbox on NixOS, or option sandbox in nix.conf on non-NixOS linux)
  • Built on platform(s)
    • NixOS
    • macOS
    • other Linux distributions
  • Tested via one or more NixOS test(s) if existing and applicable for the change (look inside nixos/tests)
  • Tested compilation of all pkgs that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review wip"
  • Tested execution of all binary files (usually in ./result/bin/)
  • Determined the impact on package closure size (by running nix path-info -S before and after)
  • Ensured that relevant documentation is up to date
  • Fits CONTRIBUTING.md.

cc: postfix maintainers @rickynils @globin

ln -sf /var/spool/mail /var/

#Finally delegate to postfix checking remain directories in /var/lib/postfix and set permissions on them
${pkgs.postfix}/bin/postfix set-permissions config_directory=/var/lib/postfix/conf
Copy link
Member

Choose a reason for hiding this comment

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

The sandbox is incomplete here. This will still run postfix as root, although I don't know whether there is a way around it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I assumed it is acceptable to have no sandbox for the prepare script, because it does not run as a network daemon.
Should put it in a sandbox as well?

To my knowledge the "main daemon" has to be run as root. It drops privileges for it's children, but there is no tight systemd integration. I am open for suggestions. :)

Copy link
Member

@hmenke hmenke Jul 17, 2020

Choose a reason for hiding this comment

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

I see. In that case I'm not sure whether there is any benefit from running postfix set-permission in the sandbox.

PrivateTmp = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
Copy link
Member

@alyssais alyssais Jul 17, 2020

Choose a reason for hiding this comment

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

Won’t this break .forward, or delivering mail to a local user’s home directory?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. Replacing ProtectHome=true by ReadOnlyPaths=/home would allow to read .forward. For delivery to the home directory this has to be disabled entirely.

Should this be behind an option? If yes, how whould an option look like?

Copy link
Member

Choose a reason for hiding this comment

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

I would suggest you don't put this behind an option - don't include it at all. It isn't a reasonable default for a mail server. Add documentation so users who can and wish to will know how to add it.

@hmenke
Copy link
Member

hmenke commented Jul 17, 2020

It's probably a good idea to also discuss this with the people from simple-nixos-mailserver @r-raymond
Same for your opendkim PR #93314, rspamd PR #93293, and redis PR #93277

@r-raymond
Copy link
Contributor

@hmenke thanks for the heads up, I'll ping the current maintainers.

@nlewo
Copy link
Member

nlewo commented Jul 17, 2020

Thanks for the heads up, and thanks for these patches! I really like this idea;)

Did you run the nixos-mailserver tests with your patches? I will do it this WE.

@tnias
Copy link
Contributor Author

tnias commented Jul 17, 2020

Did you run the nixos-mailserver tests with your patches?

I have not yet run any of the official tests. Just tested my usecase for now.

wantedBy = [ "multi-user.target" ];
before = [ "postifx.service" ];
serviceConfig = {
ExecStart = preparePostfixScript;
Copy link
Member

Choose a reason for hiding this comment

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

Can't you run this in the original service as an ExecStartPre with the + prefix?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, the + prefix looks good. I will fix my patches.

Copy link
Member

@peti peti left a comment

Choose a reason for hiding this comment

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

How is this kind of sandboxing going to interact with non-trivial Postfix installations that run, say a mailing list server or reflector scripts or any other mail service that needs resources other than Postfix itself?

Running the daemon in a sandbox feels like a rather intrusive change. This should definitely be opt-in.

@tnias
Copy link
Contributor Author

tnias commented Jul 17, 2020

This should definitely be opt-in.

I will try to do something like services.nginx.enableSandbox.

@aanderse
Copy link
Member

I will try to do something like services.nginx.enableSandbox.

@Mic92 given enableSandbox was meant as a temporary measure and enough time has passed with no negative reports can we please remove it now?

@tnias I'm not in favor of module randomly adding enableSandbox with no consistency at all. Please see my thoughts on the matter here. I think @flokli articulated the issue very well with this comment:

Sandboxing is not a boolean, but something very fuzzy.

@tnias
Copy link
Contributor Author

tnias commented Jul 17, 2020

The fixup changes loosen up rules to allow more general actions.

@peti:

  • mailing lists (mailman3) deliver mail by lmtp (tcp or unix socket). This should not be blocked by the current rules. Are other mailinglists using something not allowed yet?
  • are "reflector scripts" just forwarding mailservers? If yes, I don't see why this should not work.
  • "any other mail service that needs resources other than Postfix itself" can you provide examples? So that I can loosen up the ruleset accordingly

@Izorkin
Copy link
Contributor

Izorkin commented Jul 17, 2020

Rules systemd.tmpfiles can be taken from here - #76604
I running this variant on my server - it worked fine.

@tnias
Copy link
Contributor Author

tnias commented Jul 17, 2020

I used @Izorkin's tmpfiles patch and addded some (hopefully) reasonable sandboxing options.

@tnias
Copy link
Contributor Author

tnias commented Jul 20, 2020

There are some permission/ownership issues in the /var/lib/postfix directory.

@nlewo
Copy link
Member

nlewo commented Jul 21, 2020

@tnias nixos-mailserver tests passed with this patchset.

@tnias
Copy link
Contributor Author

tnias commented Jul 21, 2020

@nlewo I just cleared my /var/lib/postfix directory and now it works just fine. I probably messed up the permissions at some point. ¯_(ツ)_/¯

@tnias
Copy link
Contributor Author

tnias commented Jul 21, 2020

I ran into the fatal: open lock file pid/inet.submission: cannot open file: Permission denied issue again. This widens the sandbox, so it hopefully won't happen again.

ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectSystem = "full";
Copy link
Member

Choose a reason for hiding this comment

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

Does this still allow other milters? I know that our rspamd integration for postfix for example creates a socket in /run/rspamd/rspamd-milter.sock, opendkim might be similar.

Copy link
Member

Choose a reason for hiding this comment

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

IIUC, the full value still allows RW accesses to /run.
Also, I've run the nixos-mailserver tests (rspamd, opendkim, dovecot,...) on top of this patch and tests passed.

@Mic92
Copy link
Member

Mic92 commented Aug 5, 2020

@GrahamcOfBorg test rspamd

@nlewo
Copy link
Member

nlewo commented Aug 23, 2020

Still any blocker on this?
It would be nice to have it in 20.09;)

@aanderse
Copy link
Member

And here I was thinking it would be really nice to not have this in 20.09 so it has more time to bake in unstable giving more people a chance to see if this breaks anything in various configurations... but systemd sandboxing on software that wasn't designed in the age of systemd dominance (and no support from upstream) always makes me nervous 🤷‍♂️

@Mic92
Copy link
Member

Mic92 commented Aug 23, 2020

I would also say to have it tested in unstable for a bit longer and merge it after the branch-off. Postfix itself already does quite a bit of privilege separation so these sandboxes would not improve too much in terms of security.

@symphorien symphorien mentioned this pull request Aug 26, 2020
11 tasks
@ju1m
Copy link
Contributor

ju1m commented Sep 18, 2020

Due to the use of ExecStartPre= (as a str) this breaks configs using it as a str, or using preStart=. The error is:

The option `systemd.services.postfix.serviceConfig.ExecStartPre' has conflicting definitions, in `/nix/store/pb48ks5whk0sk5nn5602m6xw307vjbaj-nixpkgs-patched/nixos/modules/system/boot/systemd.nix' and `/nix/store/pb48ks5whk0sk5nn5602m6xw307vjbaj-nixpkgs-patched/nixos/modules/services/mail/postfix.nix'.

Such option merging conflict happens regularly to me and others #97371
So this does not concern only this PR (though at least it may be more user friendly to set services.postfix's ExecStartPre= as a listOf str). One general solution could be to modify the merging of systemd unit options to merge str and listOf str into listOf str. Or at least also make preStart= generate an ExecStartPre= which is a listOf str.

@Mic92
Copy link
Member

Mic92 commented Jan 8, 2021

Was this closed on purpose?

@tnias
Copy link
Contributor Author

tnias commented Jan 8, 2021

@Mic92 There seemed to be little action around it and in the spirit of reducing the number of open PRs I closed it. We can also keep it open if you prefer.

@Mic92
Copy link
Member

Mic92 commented Jan 8, 2021

@tnias I think it just take some time to make sure that postfix does not break.

@Mic92 Mic92 reopened this Jan 8, 2021
@Mic92
Copy link
Member

Mic92 commented Jan 8, 2021

/marvin opt-in
/status needs_reviewer

@aanderse
Copy link
Member

We're roughly 4-5 months away from a new release. This change presents high enough risk that I would love to see it merged very soon. 4-5 months sounds like a long enough time to break and fix enough machines that we don't have to worry when this goes stable.

Just a thought...


ReadWritePaths = [ "/var/lib/postfix" "/var/spool/mail" ];

CapabilityBoundingSet = [ "CAP_DAC_OVERRIDE" "CAP_NET_BIND_SERVICE" "CAP_SETGID" "CAP_SETUID" ];
Copy link
Member

Choose a reason for hiding this comment

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

@tnias Have you checked if postfix drops these capabilities for its less-privileged childs?
Otherwise it might make postfix less secure than it was before (those capabilities allow to read/write arbitrary files and change to different users/groups).

Copy link
Contributor

Choose a reason for hiding this comment

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

Before this update
Master process:

CapInh: 0000000000000000
CapPrm: 000001ffffffffff
CapEff: 000001ffffffffff
CapBnd: 000001ffffffffff
CapAmb: 0000000000000000

Slave process:

CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 000001ffffffffff
CapAmb: 0000000000000000
0x000001ffffffffff=cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read,cap_perfmon,cap_bpf,cap_checkpoint_restore

After this update:
Master process:

CapInh: 0000000000000000
CapPrm: 00000000000004c2
CapEff: 00000000000004c2
CapBnd: 00000000000004c2
CapAmb: 0000000000000000

Slave process:

CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 00000000000004c2
CapAmb: 0000000000000000
0x00000000000004c2=cap_dac_override,cap_setgid,cap_setuid,cap_net_bind_service

@stale
Copy link

stale bot commented Jul 21, 2021

I marked this as stale due to inactivity. → More info

@stale stale bot added the 2.status: stale https://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md label Jul 21, 2021
@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/hardening-systemd-services/17147/7

@stale stale bot removed the 2.status: stale https://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md label Jan 16, 2022
@stale stale bot added the 2.status: stale https://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md label Jul 31, 2022
@tomfitzhenry
Copy link
Contributor

FWIW, I've proposed a smaller set of hardening directives in #255227, inspired by Gentoo's.

@stale stale bot removed the 2.status: stale https://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md label Sep 15, 2023
@wegank wegank added 2.status: stale https://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md 2.status: merge conflict labels Mar 19, 2024
@stale stale bot removed the 2.status: stale https://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md label Mar 20, 2024
@wegank wegank marked this pull request as draft March 25, 2024 16:17
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.

None yet