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