1{ lib, pkgs, ... }:
2
3let
4 # Note: For some reason Privoxy can't issue valid
5 # certificates if the CA is generated using gnutls :(
6 certs = pkgs.runCommand "example-certs" { buildInputs = [ pkgs.openssl ]; } ''
7 mkdir $out
8
9 # generate CA keypair
10 openssl req -new -nodes -x509 \
11 -extensions v3_ca -keyout $out/ca.key \
12 -out $out/ca.crt -days 365 \
13 -subj "/O=Privoxy CA/CN=Privoxy CA"
14
15 # generate server key/signing request
16 openssl genrsa -out $out/server.key 3072
17 openssl req -new -key $out/server.key \
18 -out server.csr -sha256 \
19 -subj "/O=An unhappy server./CN=example.com"
20
21 # sign the request/generate the certificate
22 openssl x509 -req -in server.csr -CA $out/ca.crt \
23 -CAkey $out/ca.key -CAcreateserial -out $out/server.crt \
24 -days 500 -sha256
25 '';
26in
27
28{
29 name = "privoxy";
30 meta = with lib.maintainers; {
31 maintainers = [ rnhmjoj ];
32 };
33
34 nodes.machine =
35 { ... }:
36 {
37 services.nginx.enable = true;
38 services.nginx.virtualHosts."example.com" = {
39 addSSL = true;
40 sslCertificate = "${certs}/server.crt";
41 sslCertificateKey = "${certs}/server.key";
42 locations."/".root = pkgs.writeTextFile {
43 name = "bad-day";
44 destination = "/how-are-you/index.html";
45 text = "I've had a bad day!\n";
46 };
47 locations."/ads".extraConfig = ''
48 return 200 "Hot Nixpkgs PRs in your area. Click here!\n";
49 '';
50 };
51
52 services.privoxy = {
53 enable = true;
54 inspectHttps = true;
55 settings = {
56 ca-cert-file = "${certs}/ca.crt";
57 ca-key-file = "${certs}/ca.key";
58 debug = 65536;
59 };
60 userActions = ''
61 {+filter{positive}}
62 example.com
63
64 {+block{Fake ads}}
65 example.com/ads
66 '';
67 userFilters = ''
68 FILTER: positive This is a filter example.
69 s/bad/great/ig
70 '';
71 };
72
73 security.pki.certificateFiles = [ "${certs}/ca.crt" ];
74
75 networking.hosts."::1" = [ "example.com" ];
76 networking.proxy.httpProxy = "http://localhost:8118";
77 networking.proxy.httpsProxy = "http://localhost:8118";
78 };
79
80 nodes.machine_socks4 =
81 { ... }:
82 {
83 services.privoxy = {
84 enable = true;
85 settings.forward-socks4 = "/ 127.0.0.1:9050 .";
86 };
87 };
88 nodes.machine_socks4a =
89 { ... }:
90 {
91 services.privoxy = {
92 enable = true;
93 settings.forward-socks4a = "/ 127.0.0.1:9050 .";
94 };
95 };
96 nodes.machine_socks5 =
97 { ... }:
98 {
99 services.privoxy = {
100 enable = true;
101 settings.forward-socks5 = "/ 127.0.0.1:9050 .";
102 };
103 };
104 nodes.machine_socks5t =
105 { ... }:
106 {
107 services.privoxy = {
108 enable = true;
109 settings.forward-socks5t = "/ 127.0.0.1:9050 .";
110 };
111 };
112
113 testScript = ''
114 with subtest("Privoxy is running"):
115 machine.wait_for_unit("privoxy")
116 machine.wait_for_open_port(8118)
117 machine.succeed("curl -f http://config.privoxy.org")
118
119 with subtest("Privoxy can filter http requests"):
120 machine.wait_for_open_port(80)
121 assert "great day" in machine.succeed(
122 "curl -sfL http://example.com/how-are-you? | tee /dev/stderr"
123 )
124
125 with subtest("Privoxy can filter https requests"):
126 machine.wait_for_open_port(443)
127 assert "great day" in machine.succeed(
128 "curl -sfL https://example.com/how-are-you? | tee /dev/stderr"
129 )
130
131 with subtest("Blocks are working"):
132 machine.wait_for_open_port(443)
133 machine.fail("curl -f https://example.com/ads 1>&2")
134 machine.succeed("curl -f https://example.com/PRIVOXY-FORCE/ads 1>&2")
135
136 with subtest("Temporary certificates are cleaned"):
137 # Count current certificates
138 machine.succeed("test $(ls /run/privoxy/certs | wc -l) -gt 0")
139 # Forward in time 12 days, trigger the timer..
140 machine.succeed("date -s \"$(date --date '12 days')\"")
141 machine.systemctl("start systemd-tmpfiles-clean")
142 # ...and count again
143 machine.succeed("test $(ls /run/privoxy/certs | wc -l) -eq 0")
144
145 with subtest("Privoxy supports socks upstream proxies"):
146 for m in [machine_socks4, machine_socks4a, machine_socks5, machine_socks5t]:
147 m.wait_for_unit("privoxy")
148 m.wait_for_open_port(8118)
149 # We expect a 503 error because the dummy upstream proxy is not reachable.
150 # In issue #265654, instead privoxy segfaulted causing curl to exit with "Empty reply from server".
151 m.succeed("http_proxy=http://localhost:8118 curl -v http://does-not-exist/ 2>&1 | grep 'HTTP/1.1 503'")
152 '';
153}