1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8with lib;
9
10let
11 cfg = config.services.peroxide;
12 settingsFormat = pkgs.formats.yaml { };
13 stateDir = "peroxide";
14in
15{
16 options.services.peroxide = {
17 enable = mkEnableOption "peroxide";
18
19 package = mkPackageOption pkgs "peroxide" {
20 default = [ "peroxide" ];
21 };
22
23 logLevel = mkOption {
24 # https://github.com/sirupsen/logrus#level-logging
25 type = types.enum [
26 "Panic"
27 "Fatal"
28 "Error"
29 "Warning"
30 "Info"
31 "Debug"
32 "Trace"
33 ];
34 default = "Warning";
35 example = "Info";
36 description = "Only log messages of this priority or higher.";
37 };
38
39 settings = mkOption {
40 type = types.submodule {
41 freeformType = settingsFormat.type;
42
43 options = {
44 UserPortImap = mkOption {
45 type = types.port;
46 default = 1143;
47 description = "The port on which to listen for IMAP connections.";
48 };
49
50 UserPortSmtp = mkOption {
51 type = types.port;
52 default = 1025;
53 description = "The port on which to listen for SMTP connections.";
54 };
55
56 ServerAddress = mkOption {
57 type = types.str;
58 default = "[::0]";
59 example = "localhost";
60 description = "The address on which to listen for connections.";
61 };
62 };
63 };
64 default = { };
65 description = ''
66 Configuration for peroxide. See
67 [config.example.yaml](https://github.com/ljanyst/peroxide/blob/master/config.example.yaml)
68 for an example configuration.
69 '';
70 };
71 };
72
73 config = mkIf cfg.enable {
74 services.peroxide.settings = {
75 # peroxide deletes the cache directory on startup, which requires write
76 # permission on the parent directory, so we can't use
77 # /var/cache/peroxide
78 CacheDir = "/var/cache/peroxide/cache";
79 X509Key = mkDefault "/var/lib/${stateDir}/key.pem";
80 X509Cert = mkDefault "/var/lib/${stateDir}/cert.pem";
81 CookieJar = "/var/lib/${stateDir}/cookies.json";
82 CredentialsStore = "/var/lib/${stateDir}/credentials.json";
83 };
84
85 users.users.peroxide = {
86 isSystemUser = true;
87 group = "peroxide";
88 };
89 users.groups.peroxide = { };
90
91 systemd.services.peroxide = {
92 description = "Peroxide ProtonMail bridge";
93 requires = [ "network.target" ];
94 after = [ "network.target" ];
95 wantedBy = [ "multi-user.target" ];
96
97 restartTriggers = [ config.environment.etc."peroxide.conf".source ];
98
99 serviceConfig = {
100 Type = "simple";
101 User = "peroxide";
102 LogsDirectory = "peroxide";
103 LogsDirectoryMode = "0750";
104 # Specify just "peroxide" so that the user has write permission, because
105 # peroxide deletes and recreates the cache directory on startup.
106 CacheDirectory = [
107 "peroxide"
108 "peroxide/cache"
109 ];
110 CacheDirectoryMode = "0700";
111 StateDirectory = stateDir;
112 StateDirectoryMode = "0700";
113 ExecStart = "${cfg.package}/bin/peroxide -log-file=/var/log/peroxide/peroxide.log -log-level ${cfg.logLevel}";
114 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
115 };
116
117 preStart = ''
118 # Create a self-signed certificate if no certificate exists.
119 if [[ ! -e "${cfg.settings.X509Key}" && ! -e "${cfg.settings.X509Cert}" ]]; then
120 ${cfg.package}/bin/peroxide-cfg -action gen-x509 \
121 -x509-org 'N/A' \
122 -x509-cn 'nixos' \
123 -x509-cert "${cfg.settings.X509Cert}" \
124 -x509-key "${cfg.settings.X509Key}"
125 fi
126 '';
127 };
128
129 # https://github.com/ljanyst/peroxide/blob/master/peroxide.logrotate
130 services.logrotate.settings.peroxide = {
131 files = "/var/log/peroxide/peroxide.log";
132 rotate = 31;
133 frequency = "daily";
134 compress = true;
135 delaycompress = true;
136 missingok = true;
137 notifempty = true;
138 su = "peroxide peroxide";
139 postrotate = "systemctl reload peroxide";
140 };
141
142 environment.etc."peroxide.conf".source = settingsFormat.generate "peroxide.conf" cfg.settings;
143 environment.systemPackages = [ cfg.package ];
144 };
145
146 meta.maintainers = with maintainers; [
147 aanderse
148 aidalgol
149 ];
150}