Scenario
I am currently setting up a server with Postfix and need to configure several Postfix lookup tables which are defined in configuration files such as
user = db_user
password = my-secret-db-user-password
hosts = 127.0.0.1
dbname = mail_server
query = SELECT * FROM domains WHERE domain='%s'
As you can see, the files contain a secret password and thus shouldn’t be stored in the nix store. Thus, normal NixOS methods can not be used here.
I know of (and use) agenix and replace-secret but there seems to be no reference solution on how to exactly use them together idiomatically.
From how both tools work, it is clear that
- agenix will provide the password in a file to be read
- the configuration file will have be stored somewhere with the password replaced by placeholder such as
@secret-db-password@
- since most of NixOS paths are read-only, the configuration file will have be copied someplace where it can be modified
- replace-secret will take this stored configuration and replace the placeholder with the real password
- the final file will need to end up in a location where the service Postfix will read it
Open Questions
-
How are configuration files stored and written?
a. an actual template file next to configuration.nix which gets copied to the final destination?
b.pkgs.writeText
?
c.etc.environment.text
? (probably not, because the template file is useless by itself) -
What mechanisms to use to get this file into its final destination?
a.cp
?
b.install
?
c. something built-in into NixOS? -
Where to store the final configuration file?
a. in /etc/postfix/myfile.cf?
b. in /var/lib/postfix/conf/myfile.cf?
Current Solution (excerpt)
I came up with the following solution so far:
# mail_server.nix
{ config, pkgs, lib, ... }:
let
postfix-map1-file = pkgs.writeText "postfix-map1-file" ''
user = db_user
password = @DB_USER_PASSWORD@
hosts = 127.0.0.1
dbname = mail_server
query = SELECT * FROM domains WHERE domain='%s'
'';
postfix-map2-file = pkgs.writeText "postfix-map2-file" ''
user = db_user
password = @DB_USER_PASSWORD@
hosts = 127.0.0.1
dbname = mail_server
query = ANOTHER SQL QUERY HERE'
'';
in
# ...
# Other services' configuration
# ...
# POSTFIX
services.postfix =
{
enable = true;
# main.cf
config = {
map1 = "pgsql:/var/lib/postfix/conf/postfix-map1-file.cf";
map2 = "pgsql:/var/lib/postfix/conf/postfix-map1-file.cf";
};
};
systemd.services.postfix = {
preStart = ''
install --owner postfix --mode 400 ${postfix-map1-file} /var/lib/postfix/conf/postfix-map1-file.cf
install --owner postfix --mode 400 ${postfix-map2-file} /var/lib/postfix/conf/postfix-map1-file.cf
${pkgs.replace-secret}/bin/replace-secret @MAIL_USER_PASSWORD@ ${config.age.secrets.db-user-password.path} /var/lib/postfix/conf/postfix-map1-file.cf
${pkgs.replace-secret}/bin/replace-secret @MAIL_USER_PASSWORD@ ${config.age.secrets.db-user-password.path} /var/lib/postfix/conf/postfix-map2-file.cf
'';
path = with pkgs; [ replace-secret ];
};
Can something be improved here? Is this the intended/idiomatic use? I can only think of making a loop for the map files to not have write install
and replace-sercret
twice.
Related thread where I did something similar for setting users password in PostgreSQL.