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