diff --git a/system/activate-configuration.sh b/system/activate-configuration.sh index fd901c8aca65..b58eb8c15d0b 100644 --- a/system/activate-configuration.sh +++ b/system/activate-configuration.sh @@ -156,6 +156,8 @@ for i in @setuidPrograms@; do chmod 4755 $wrapperDir/$i done +@adjustSetuidOwner@ + PATH="$save_PATH" # Set the host name. Don't clear it if it's not configured in the diff --git a/system/ids.nix b/system/ids.nix index 473f8202e68e..a704ef163ea6 100644 --- a/system/ids.nix +++ b/system/ids.nix @@ -15,6 +15,7 @@ portmap = 11; atd = 12; zabbix = 13; + postfix = 14; nixbld = 30000; # start of range of uids nobody = 65534; @@ -30,6 +31,8 @@ avahi = 10; portmap = 11; atd = 12; + postfix = 13; + postdrop = 14; audio = 17; diff --git a/system/options.nix b/system/options.nix index 07f17cb98e90..e63531f577c5 100644 --- a/system/options.nix +++ b/system/options.nix @@ -339,6 +339,14 @@ "; }; + nativeIPv6 = mkOption { + default = false; + description = " + Whether to use IPv6 even though gw6c is not used. For example, + for Postfix. + "; + }; + extraHosts = mkOption { default = ""; example = "192.168.0.1 lanlocalhost"; @@ -2072,7 +2080,116 @@ "; }; }; - + + postfix = { + enable = mkOption { + default = false; + description =" + Whether to run the Postfix mail server. + "; + }; + user = mkOption { + default = "postfix"; + description = " + How to call postfix user (must be used only for postfix). + "; + }; + group = mkOption { + default = "postfix"; + description = " + How to call postfix group (must be used only for postfix). + "; + }; + setgidGroup = mkOption { + default = "postdrop"; + description = " + How to call postfix setgid group (for postdrop). Should + be uniquely used group. + "; + }; + networks = mkOption { + default = null; + example = ["192.168.0.1/24"]; + description = " + Net masks for trusted - allowed to relay mail to third parties - + hosts. Leave empty to use mynetworks_style configuration or use + default (localhost-only). + "; + }; + networksStyle = mkOption { + default = ""; + description = " + Name of standard way of trusted network specification to use, + leave blank if you specify it explicitly or if you want to use + default (localhost-only). + "; + }; + hostname = mkOption { + default = ""; + description =" + Hostname to use. Leave blank to use just the hostname of machine. + It should be FQDN. + "; + }; + domain = mkOption { + default = ""; + description =" + Domain to use. Leave blank to use hostname minus first component. + "; + }; + origin = mkOption { + default = ""; + description =" + Origin to use in outgoing e-mail. Leave blank to use hostname. + "; + }; + destination = mkOption { + default = null; + example = ["localhost"]; + description = " + Full (!) list of domains we deliver locally. Leave blank for + acceptable Postfix default. + "; + }; + relayDomains = mkOption { + default = null; + example = ["localdomain"]; + description = " + List of domains we agree to relay to. Default is the same as + destination. + "; + }; + relayHost = mkOption { + default = ""; + description = " + Mail relay for outbound mail. + "; + }; + lookupMX = mkOption { + default = false; + description = " + Whether relay specified is just domain whose MX must be used. + "; + }; + postmasterAlias = mkOption { + default = "root"; + description = " + Who should receive postmaster e-mail. + "; + }; + rootAlias = mkOption { + default = ""; + description = " + Who should receive root e-mail. Blank for no redirection. + "; + }; + extraAliases = mkOption { + default = ""; + description = " + Additional entries to put verbatim into aliases file. + "; + }; + }; }; @@ -2206,6 +2323,23 @@ "; }; + setuidOwners = mkOption { + default = []; + example = [{ + program = "sendmail"; + owner = "nodody"; + group = "postdrop"; + setuid = false; + setgid = true; + }]; + description = '' + List of non-trivial setuid programs, like Postfix sendmail. Default + should probably be nobody:nogroup:false:false - if you are bothering + doing anything with a setuid program, "root.root u+s g-s" is not what + you are aiming at.. + ''; + }; + seccureKeys = { public = mkOption { default = /var/elliptic-keys/public; diff --git a/system/system.nix b/system/system.nix index d8c27dd45141..a2001ebf390c 100644 --- a/system/system.nix +++ b/system/system.nix @@ -328,6 +328,21 @@ rec { ]; bash = pkgs.bashInteractive; + + adjustSetuidOwner = pkgs.lib.concatStrings (map + (_entry:let entry = { + owner = "nobody"; + group = "nogroup"; + setuid = false; + setgid = false; + } //_entry; in + '' + chown ${entry.owner}.${entry.group} $wrapperDir/${entry.program} + chmod u${if entry.setuid then "+" else "-"}s $wrapperDir/${entry.program} + chmod g${if entry.setgid then "+" else "-"}s $wrapperDir/${entry.program} + + '') + config.security.setuidOwners); }; diff --git a/upstart-jobs/default.nix b/upstart-jobs/default.nix index 4eed586c2989..08afd46449ad 100644 --- a/upstart-jobs/default.nix +++ b/upstart-jobs/default.nix @@ -389,6 +389,12 @@ let (import ../upstart-jobs/zabbix-server.nix { inherit config pkgs; }) + + # Postfix mail server. + ++ optional config.services.postfix.enable + (import ../upstart-jobs/postfix.nix { + inherit config pkgs; + }) # Handles the reboot/halt events. ++ (map diff --git a/upstart-jobs/postfix.nix b/upstart-jobs/postfix.nix new file mode 100644 index 000000000000..9b85969692df --- /dev/null +++ b/upstart-jobs/postfix.nix @@ -0,0 +1,146 @@ +{config, pkgs} : +let + startingDependency = if config.services.gw6c.enable then "gw6c" else "network-interfaces"; + + cfg = config.services.postfix; + user = cfg.user; + group = cfg.group; + setgidGroup = cfg.setgidGroup; + idList = import ../system/ids.nix; + + optionalString = pkgs.lib.optionalString; + mainCf = + '' + queue_directory = /var/postfix/queue + command_directory = ${pkgs.postfix}/sbin + daemon_directory = ${pkgs.postfix}/libexec/postfix + + mail_owner = ${user} + default_privs = nobody + + '' + + optionalString (config.services.gw6c.enable || config.networking.nativeIPv6) ('' + inet_protocols = all + '') + + + (if cfg.networks!=null then + ('' + mynetworks = ${toString cfg.networks} + '') + else if (cfg.networksStyle != "") then + ('' + mynetworks_style = ${cfg.networksStyle} + '') + else + # Postfix default is subnet, but let's play safe + ('' + mynetworks_style = host + '') + ) + + optionalString (cfg.hostname != "") ('' + myhostname = ${cfg.hostname} + '') + + optionalString (cfg.domain != "") ('' + mydomain = ${cfg.domain} + '') + + optionalString (cfg.origin != "") ('' + myorigin = ${cfg.origin} + '') + + optionalString (cfg.destination != null) ('' + mydestination = ${toString cfg.destination} + '') + + optionalString (cfg.relayDomains != null) ('' + relay_domains = ${toString cfg.relayDomains} + '') + + '' + local_recipient_maps = + '' + + ('' + relayhost = ${if cfg.lookupMX || cfg.relayHost == "" then + cfg.relayHost + else + "[" + cfg.relayHost + "]"} + '') + + ('' + alias_maps = hash:/var/postfix/conf/aliases + + mail_spool_directory = /var/spool/mail/ + + setgid_group = ${setgidGroup} + '') + ; + + aliases = + (optionalString (cfg.postmasterAlias != "") ('' + postmaster: ${cfg.postmasterAlias} + '')) + + + (optionalString (cfg.rootAlias != "") ('' + root: ${cfg.rootAlias} + '')) + + cfg.extraAliases + ; + + aliasesFile = pkgs.writeText "postfix-aliases" aliases; + mainCfFile = pkgs.writeText "postfix-main.cf" mainCf; + +in +{ + name = "postfix"; + users = [ + { name = user; + description = "Postfix mail server user"; + uid = idList.uids.postfix; + group = group; + } + ]; + + groups = [ + { name = group; + gid = idList.gids.postfix; + } + { name = setgidGroup; + gid = idList.gids.postdrop; + } + ]; + + + # I copy _lots_ of shipped configuration filed + # that can be left as is. I am afraid the exact + # will list slightly change in next Postfix + # release, so listing them all one-by-one in an + # accurate way is unlikely to be better. + job = '' + description "Postfix mail server job" + + start on ${startingDependency}/started + stop on never + + script + if ! [ -d /var/spool/postfix ]; then + ${pkgs.coreutils}/bin/mkdir -p /var/spool/mail /var/postfix/conf /var/postfix/queue + fi + + ${pkgs.coreutils}/bin/chown -R ${user}:${group} /var/postfix + ${pkgs.coreutils}/bin/chown -R ${user}:${setgidGroup} /var/postfix/queue + ${pkgs.coreutils}/bin/chmod -R ug+rwX /var/postfix/queue + ${pkgs.coreutils}/bin/chown -R root:root /var/spool/mail + ${pkgs.coreutils}/bin/chmod a+rwxt /var/spool/mail + + ln -sf ${pkgs.postfix}/share/postfix/conf/* /var/postfix/conf + + ln -sf ${aliasesFile} /var/postfix/conf/aliases + ln -sf ${mainCfFile} /var/postfix/conf/main.cf + + ${pkgs.postfix}/sbin/postalias -c /var/postfix/conf /var/postfix/conf/aliases + + ${pkgs.postfix}/sbin/postfix -c /var/postfix/conf start + end script + ''; + + extraEtc = [ + { source = "/var/postfix/conf"; + target = "postfix"; + } + ]; +}