1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 inherit (pkgs) nntp-proxy;
8
9 cfg = config.services.nntp-proxy;
10
11 configBool = b: if b then "TRUE" else "FALSE";
12
13 confFile = pkgs.writeText "nntp-proxy.conf" ''
14 nntp_server:
15 {
16 # NNTP Server host and port address
17 server = "${cfg.upstreamServer}";
18 port = ${toString cfg.upstreamPort};
19 # NNTP username
20 username = "${cfg.upstreamUser}";
21 # NNTP password in clear text
22 password = "${cfg.upstreamPassword}";
23 # Maximum number of connections allowed by the NNTP
24 max_connections = ${toString cfg.upstreamMaxConnections};
25 };
26
27 proxy:
28 {
29 # Local address and port to bind to
30 bind_ip = "${cfg.listenAddress}";
31 bind_port = ${toString cfg.port};
32
33 # SSL key and cert file
34 ssl_key = "${cfg.sslKey}";
35 ssl_cert = "${cfg.sslCert}";
36
37 # prohibit users from posting
38 prohibit_posting = ${configBool cfg.prohibitPosting};
39 # Verbose levels: ERROR, WARNING, NOTICE, INFO, DEBUG
40 verbose = "${toUpper cfg.verbosity}";
41 # Password is made with: 'mkpasswd -m sha-512 <password>'
42 users = (${concatStringsSep ",\n" (mapAttrsToList (username: userConfig:
43 ''
44 {
45 username = "${username}";
46 password = "${userConfig.passwordHash}";
47 max_connections = ${toString userConfig.maxConnections};
48 }
49 '') cfg.users)});
50 };
51 '';
52
53in
54
55{
56
57 ###### interface
58
59 options = {
60
61 services.nntp-proxy = {
62 enable = mkEnableOption (lib.mdDoc "NNTP-Proxy");
63
64 upstreamServer = mkOption {
65 type = types.str;
66 default = "";
67 example = "ssl-eu.astraweb.com";
68 description = lib.mdDoc ''
69 Upstream server address
70 '';
71 };
72
73 upstreamPort = mkOption {
74 type = types.port;
75 default = 563;
76 description = lib.mdDoc ''
77 Upstream server port
78 '';
79 };
80
81 upstreamMaxConnections = mkOption {
82 type = types.int;
83 default = 20;
84 description = lib.mdDoc ''
85 Upstream server maximum allowed concurrent connections
86 '';
87 };
88
89 upstreamUser = mkOption {
90 type = types.str;
91 default = "";
92 description = lib.mdDoc ''
93 Upstream server username
94 '';
95 };
96
97 upstreamPassword = mkOption {
98 type = types.str;
99 default = "";
100 description = lib.mdDoc ''
101 Upstream server password
102 '';
103 };
104
105 listenAddress = mkOption {
106 type = types.str;
107 default = "127.0.0.1";
108 example = "[::]";
109 description = lib.mdDoc ''
110 Proxy listen address (IPv6 literal addresses need to be enclosed in "[" and "]" characters)
111 '';
112 };
113
114 port = mkOption {
115 type = types.port;
116 default = 5555;
117 description = lib.mdDoc ''
118 Proxy listen port
119 '';
120 };
121
122 sslKey = mkOption {
123 type = types.str;
124 default = "key.pem";
125 example = "/path/to/your/key.file";
126 description = lib.mdDoc ''
127 Proxy ssl key path
128 '';
129 };
130
131 sslCert = mkOption {
132 type = types.str;
133 default = "cert.pem";
134 example = "/path/to/your/cert.file";
135 description = lib.mdDoc ''
136 Proxy ssl certificate path
137 '';
138 };
139
140 prohibitPosting = mkOption {
141 type = types.bool;
142 default = true;
143 description = lib.mdDoc ''
144 Whether to prohibit posting to the upstream server
145 '';
146 };
147
148 verbosity = mkOption {
149 type = types.enum [ "error" "warning" "notice" "info" "debug" ];
150 default = "info";
151 example = "error";
152 description = lib.mdDoc ''
153 Verbosity level
154 '';
155 };
156
157 users = mkOption {
158 type = types.attrsOf (types.submodule {
159 options = {
160 username = mkOption {
161 type = types.str;
162 description = lib.mdDoc ''
163 Username
164 '';
165 };
166
167 passwordHash = mkOption {
168 type = types.str;
169 example = "$6$GtzE7FrpE$wwuVgFYU.TZH4Rz.Snjxk9XGua89IeVwPQ/fEUD8eujr40q5Y021yhn0aNcsQ2Ifw.BLclyzvzgegopgKcneL0";
170 description = lib.mdDoc ''
171 SHA-512 password hash (can be generated by
172 `mkpasswd -m sha-512 <password>`)
173 '';
174 };
175
176 maxConnections = mkOption {
177 type = types.int;
178 default = 1;
179 description = lib.mdDoc ''
180 Maximum number of concurrent connections to the proxy for this user
181 '';
182 };
183 };
184 });
185 description = lib.mdDoc ''
186 NNTP-Proxy user configuration
187 '';
188
189 default = {};
190 example = literalExpression ''
191 {
192 "user1" = {
193 passwordHash = "$6$1l0t5Kn2Dk$appzivc./9l/kjq57eg5UCsBKlcfyCr0zNWYNerKoPsI1d7eAwiT0SVsOVx/CTgaBNT/u4fi2vN.iGlPfv1ek0";
194 maxConnections = 5;
195 };
196 "anotheruser" = {
197 passwordHash = "$6$6lwEsWB.TmsS$W7m1riUx4QrA8pKJz8hvff0dnF1NwtZXgdjmGqA1Dx2MDPj07tI9GNcb0SWlMglE.2/hBgynDdAd/XqqtRqVQ0";
198 maxConnections = 7;
199 };
200 }
201 '';
202 };
203 };
204
205 };
206
207 ###### implementation
208
209 config = mkIf cfg.enable {
210
211 users.users.nntp-proxy = {
212 isSystemUser = true;
213 group = "nntp-proxy";
214 description = "NNTP-Proxy daemon user";
215 };
216 users.groups.nntp-proxy = {};
217
218 systemd.services.nntp-proxy = {
219 description = "NNTP proxy";
220 after = [ "network.target" "nss-lookup.target" ];
221 wantedBy = [ "multi-user.target" ];
222 serviceConfig = { User="nntp-proxy"; };
223 serviceConfig.ExecStart = "${nntp-proxy}/bin/nntp-proxy ${confFile}";
224 preStart = ''
225 if [ ! \( -f ${cfg.sslCert} -a -f ${cfg.sslKey} \) ]; then
226 ${pkgs.openssl.bin}/bin/openssl req -subj '/CN=AutoGeneratedCert/O=NixOS Service/C=US' \
227 -new -newkey rsa:2048 -days 365 -nodes -x509 -keyout ${cfg.sslKey} -out ${cfg.sslCert};
228 fi
229 '';
230 };
231
232 };
233
234}