nixos/postsrsd: migrate to rfc42 settings
Allow a freeform configuration approach to satisfy different configuration complexities. Remove confinement options and make its hardening options more explicit and removed the deprecated PermissionStartOnly= option.
This commit is contained in:
		
							parent
							
								
									dab3a6e781
								
							
						
					
					
						commit
						83af4a9aed
					
				| @ -7,32 +7,59 @@ | ||||
| let | ||||
| 
 | ||||
|   cfg = config.services.postsrsd; | ||||
|   runtimeDirectoryName = "postsrsd"; | ||||
|   runtimeDirectory = "/run/${runtimeDirectoryName}"; | ||||
|   # TODO: follow RFC 42, but we need a libconfuse format first: | ||||
|   #       https://github.com/NixOS/nixpkgs/issues/401565 | ||||
|   # Arrays in `libconfuse` look like this: {"Life", "Universe", "Everything"} | ||||
|   # See https://www.nongnu.org/confuse/tutorial-html/ar01s03.html. | ||||
|   # | ||||
|   # Note: We're using `builtins.toJSON` to escape strings, but JSON strings | ||||
|   # don't have exactly the same semantics as libconfuse strings. For example, | ||||
|   # "${F}" gets treated as an env var reference, see above issue for details. | ||||
|   libconfuseDomains = "{ " + lib.concatMapStringsSep ", " builtins.toJSON cfg.domains + " }"; | ||||
|   configFile = pkgs.writeText "postsrsd.conf" '' | ||||
|     secrets-file = "''${CREDENTIALS_DIRECTORY}/secrets-file" | ||||
|     domains = ${libconfuseDomains} | ||||
|     separator = "${cfg.separator}" | ||||
|     socketmap = "unix:${cfg.socketPath}" | ||||
| 
 | ||||
|     # Disable postsrsd's jailing in favor of confinement with systemd. | ||||
|     unprivileged-user = "" | ||||
|     chroot-dir = "" | ||||
|   ''; | ||||
|   inherit (lib) | ||||
|     concatMapStringsSep | ||||
|     concatMapAttrsStringSep | ||||
|     isBool | ||||
|     isFloat | ||||
|     isInt | ||||
|     isPath | ||||
|     isString | ||||
|     isList | ||||
|     mkRemovedOptionModule | ||||
|     mkRenamedOptionModule | ||||
|     ; | ||||
| 
 | ||||
|   # This is a implementation of a simple libconfuse config renderer sufficient | ||||
|   # for the postsrsd configuration file complexity. | ||||
|   # TODO: Replace with pkgs.formats.libconfuse, once implemented (https://github.com/NixOS/nixpkgs/issues/401565) | ||||
|   renderValue = | ||||
|     value: | ||||
|     if isBool value then | ||||
|       if value then "true" else "false" | ||||
|     else if isString value || isPath value then | ||||
|       builtins.toJSON value # for escaping | ||||
|     else if isInt value || isFloat value then | ||||
|       toString value | ||||
|     else if isList value then | ||||
|       "{${concatMapStringsSep "," renderValue value}}" | ||||
|     else | ||||
|       throw "postsrsd: unsupported value type in settings option"; | ||||
| 
 | ||||
|   renderAttr = | ||||
|     attrs: concatMapAttrsStringSep "\n" (name: value: "${name} = ${renderValue value}") attrs; | ||||
| 
 | ||||
|   configFile = pkgs.writeText "postsrsd.conf" ( | ||||
|     renderAttr (lib.filterAttrsRecursive (_: v: v != null) cfg.settings) | ||||
|   ); | ||||
| in | ||||
| { | ||||
|   imports = | ||||
|     map | ||||
|     [ | ||||
|       (mkRemovedOptionModule [ "services" "postsrsd" "socketPath" ] '' | ||||
|         Configure/reference `services.postsrsd.settings.socketmap` instead. Note that its now required to start with the `inet:` or `unix:` prefix. | ||||
|       '') | ||||
|       (mkRenamedOptionModule | ||||
|         [ "services" "postsrsd" "domains" ] | ||||
|         [ "services" "postsrsd" "settings" "domains" ] | ||||
|       ) | ||||
|       (mkRenamedOptionModule | ||||
|         [ "services" "postsrsd" "separator" ] | ||||
|         [ "services" "postsrsd" "settings" "separator" ] | ||||
|       ) | ||||
|     ] | ||||
|     ++ map | ||||
|       ( | ||||
|         name: | ||||
|         lib.mkRemovedOptionModule [ "services" "postsrsd" name ] '' | ||||
| @ -62,24 +89,125 @@ in | ||||
|       secretsFile = lib.mkOption { | ||||
|         type = lib.types.path; | ||||
|         default = "/var/lib/postsrsd/postsrsd.secret"; | ||||
|         description = "Secret keys used for signing and verification"; | ||||
|         description = '' | ||||
|           Secret keys used for signing and verification. | ||||
| 
 | ||||
|           ::: {.note} | ||||
|           The secret will be generated, if it does not exist at the given path. | ||||
|           ::: | ||||
|         ''; | ||||
|       }; | ||||
| 
 | ||||
|       domains = lib.mkOption { | ||||
|         type = lib.types.listOf lib.types.str; | ||||
|         description = "Domain names for rewrite"; | ||||
|         default = [ config.networking.hostName ]; | ||||
|         defaultText = lib.literalExpression "[ config.networking.hostName ]"; | ||||
|       }; | ||||
|       settings = lib.mkOption { | ||||
|         type = lib.types.submodule { | ||||
|           freeformType = | ||||
|             with lib.types; | ||||
|             attrsOf (oneOf [ | ||||
|               bool | ||||
|               float | ||||
|               int | ||||
|               path | ||||
|               str | ||||
|               (listOf str) | ||||
|             ]); | ||||
| 
 | ||||
|       separator = lib.mkOption { | ||||
|         type = lib.types.enum [ | ||||
|           "-" | ||||
|           "=" | ||||
|           "+" | ||||
|         ]; | ||||
|         default = "="; | ||||
|         description = "First separator character in generated addresses"; | ||||
|           options = { | ||||
|             domains = lib.mkOption { | ||||
|               type = with lib.types; listOf str; | ||||
|               default = [ ]; | ||||
|               example = [ "example.com" ]; | ||||
|               description = '' | ||||
|                 List of local domains, that do not require rewriting. | ||||
|               ''; | ||||
|             }; | ||||
| 
 | ||||
|             secrets-file = lib.mkOption { | ||||
|               type = lib.types.str; | ||||
|               default = "\${CREDENTIALS_DIRECTORY}/secrets-file"; | ||||
|               readOnly = true; | ||||
|               description = '' | ||||
|                 Path to the file containing the secret keys. | ||||
| 
 | ||||
|                 ::: {.note} | ||||
|                 Secrets are passed using `LoadCredential=` on the systemd unit, | ||||
|                 so this options is read-only. | ||||
| 
 | ||||
|                 Configure {option}`services.postsrsd.secretsFile` instead. | ||||
|               ''; | ||||
|             }; | ||||
| 
 | ||||
|             separator = lib.mkOption { | ||||
|               type = lib.types.enum [ | ||||
|                 "-" | ||||
|                 "=" | ||||
|                 "+" | ||||
|               ]; | ||||
|               default = "="; | ||||
|               description = '' | ||||
|                 SRS tag separator used in generated sender addresses. | ||||
| 
 | ||||
|                 Unless you have a very good reason, you should leave this | ||||
|                 setting at its default. | ||||
|               ''; | ||||
|             }; | ||||
| 
 | ||||
|             srs-domain = lib.mkOption { | ||||
|               type = with lib.types; nullOr str; | ||||
|               default = null; | ||||
|               example = "srs.example.com"; | ||||
|               description = '' | ||||
|                 Dedicated mail domain used for ephemeral SRS envelope addresses. | ||||
| 
 | ||||
|                 Recommended to configure, when hosting multiple unrelated mail | ||||
|                 domains (e.g. for different customers), to prevent privacy | ||||
|                 issues. | ||||
| 
 | ||||
|                 Set to `null` to not configure any `srs-domain`. | ||||
|               ''; | ||||
|             }; | ||||
| 
 | ||||
|             socketmap = lib.mkOption { | ||||
|               type = lib.types.strMatching "^(unix|inet):.+"; | ||||
|               default = "unix:/run/postsrsd/socket"; | ||||
|               example = "inet:localhost:10003"; | ||||
|               description = '' | ||||
|                 Listener configuration in socket map format native to Postfix configuration. | ||||
|               ''; | ||||
|             }; | ||||
| 
 | ||||
|             chroot-dir = lib.mkOption { | ||||
|               type = lib.types.str; | ||||
|               default = ""; | ||||
|               readOnly = true; | ||||
|               description = '' | ||||
|                 Path to chroot into at runtime as an additional layer of protection. | ||||
| 
 | ||||
|                 ::: {.note} | ||||
|                 We confine the runtime environment through systemd hardening instead, so this option is read-only. | ||||
|                 ::: | ||||
|               ''; | ||||
|             }; | ||||
| 
 | ||||
|             unprivileged-user = lib.mkOption { | ||||
|               type = lib.types.str; | ||||
|               default = ""; | ||||
|               readOnly = true; | ||||
|               description = '' | ||||
|                 Unprivileged user to drop privileges to. | ||||
| 
 | ||||
|                 ::: {.note} | ||||
|                 Our systemd unit never runs postsrsd as a privileged process, so this option is read-only. | ||||
|                 ::: | ||||
|               ''; | ||||
|             }; | ||||
|           }; | ||||
|         }; | ||||
|         default = { }; | ||||
|         description = '' | ||||
|           Configuration options for the postsrsd.conf file. | ||||
| 
 | ||||
|           See the [example configuration](https://github.com/roehling/postsrsd/blob/main/doc/postsrsd.conf) for possible values. | ||||
|         ''; | ||||
|       }; | ||||
| 
 | ||||
|       user = lib.mkOption { | ||||
| @ -93,15 +221,6 @@ in | ||||
|         default = "postsrsd"; | ||||
|         description = "Group for the daemon"; | ||||
|       }; | ||||
| 
 | ||||
|       socketPath = lib.mkOption { | ||||
|         type = lib.types.path; | ||||
|         default = "${runtimeDirectory}/socket"; | ||||
|         readOnly = true; | ||||
|         description = '' | ||||
|           Path to the Unix socket for connecting to postsrsd. | ||||
|           Read-only, intended for usage when integrating postsrsd into other NixOS config.''; | ||||
|       }; | ||||
|     }; | ||||
|   }; | ||||
| 
 | ||||
| @ -143,15 +262,21 @@ in | ||||
|       before = [ "postfix.service" ]; | ||||
|       wantedBy = [ "multi-user.target" ]; | ||||
|       requires = [ "postsrsd-generate-secrets.service" ]; | ||||
|       confinement.enable = true; | ||||
| 
 | ||||
|       serviceConfig = { | ||||
|         ExecStart = "${lib.getExe pkgs.postsrsd} -C ${configFile}"; | ||||
|         User = cfg.user; | ||||
|         Group = cfg.group; | ||||
|         PermissionsStartOnly = true; | ||||
|         RuntimeDirectory = runtimeDirectoryName; | ||||
|         RuntimeDirectory = "postsrsd"; | ||||
|         LoadCredential = "secrets-file:${cfg.secretsFile}"; | ||||
| 
 | ||||
|         PrivateDevices = true; | ||||
|         PrivateMounts = true; | ||||
|         PrivateTmp = true; | ||||
|         PrivateUsers = true; | ||||
|         ProtectControlGroups = true; | ||||
|         ProtectKernelModules = true; | ||||
|         ProtectKernelTunables = true; | ||||
|       }; | ||||
|     }; | ||||
|   }; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Martin Weinelt
						Martin Weinelt