···
certs = import ./snakeoil-certs.nix;
4
-
import ../make-test-python.nix (
7
-
name = "nginx-proxyprotocol";
6
+
name = "nginx-proxyprotocol";
9
+
maintainers = with pkgs.lib.maintainers; [ raitobezarius ];
10
-
maintainers = with pkgs.lib.maintainers; [ raitobezarius ];
16
+
environment.systemPackages = [ pkgs.netcat ];
17
+
security.pki.certificateFiles = [
17
-
environment.systemPackages = [ pkgs.netcat ];
18
-
security.pki.certificateFiles = [
21
+
networking.extraHosts = ''
22
+
127.0.0.5 proxy.test.nix
23
+
127.0.0.5 noproxy.test.nix
24
+
127.0.0.3 direct-nossl.test.nix
25
+
127.0.0.4 unsecure-nossl.test.nix
26
+
127.0.0.2 direct-noproxy.test.nix
27
+
127.0.0.1 direct-proxy.test.nix
34
+
proxyProtocol = true;
37
+
{ addr = "127.0.0.2"; }
45
+
proxyProtocol = true;
22
-
networking.extraHosts = ''
23
-
127.0.0.5 proxy.test.nix
24
-
127.0.0.5 noproxy.test.nix
25
-
127.0.0.3 direct-nossl.test.nix
26
-
127.0.0.4 unsecure-nossl.test.nix
27
-
127.0.0.2 direct-noproxy.test.nix
28
-
127.0.0.1 direct-proxy.test.nix
48
+
commonHttpConfig = ''
49
+
log_format pcombined '(proxy_protocol=$proxy_protocol_addr) - (remote_addr=$remote_addr) - (realip=$realip_remote_addr) - (upstream=) - (remote_user=$remote_user) [$time_local] '
50
+
'"$request" $status $body_bytes_sent '
51
+
'"$http_referer" "$http_user_agent"';
52
+
access_log /var/log/nginx/access.log pcombined;
53
+
error_log /var/log/nginx/error.log;
35
-
proxyProtocol = true;
38
-
{ addr = "127.0.0.2"; }
46
-
proxyProtocol = true;
49
-
commonHttpConfig = ''
50
-
log_format pcombined '(proxy_protocol=$proxy_protocol_addr) - (remote_addr=$remote_addr) - (realip=$realip_remote_addr) - (upstream=) - (remote_user=$remote_user) [$time_local] '
51
-
'"$request" $status $body_bytes_sent '
52
-
'"$http_referer" "$http_user_agent"';
53
-
access_log /var/log/nginx/access.log pcombined;
54
-
error_log /var/log/nginx/error.log;
59
-
locations."/".return = "200 '$remote_addr'";
61
-
set_real_ip_from 127.0.0.5/32;
62
-
real_ip_header proxy_protocol;
67
-
"*.test.nix" = commonConfig // {
68
-
sslCertificate = certs."*.test.nix".cert;
69
-
sslCertificateKey = certs."*.test.nix".key;
72
-
"direct-nossl.test.nix" = commonConfig;
73
-
"unsecure-nossl.test.nix" = commonConfig // {
75
-
real_ip_header proxy_protocol;
58
+
locations."/".return = "200 '$remote_addr'";
60
+
set_real_ip_from 127.0.0.5/32;
61
+
real_ip_header proxy_protocol;
66
+
"*.test.nix" = commonConfig // {
67
+
sslCertificate = certs."*.test.nix".cert;
68
+
sslCertificateKey = certs."*.test.nix".key;
71
+
"direct-nossl.test.nix" = commonConfig;
72
+
"unsecure-nossl.test.nix" = commonConfig // {
74
+
real_ip_header proxy_protocol;
81
-
services.sniproxy = {
90
-
listener 127.0.0.5:443 {
95
-
^proxy\.test\.nix$ 127.0.0.1 proxy_protocol
96
-
^noproxy\.test\.nix$ 127.0.0.2
80
+
services.sniproxy = {
89
+
listener 127.0.0.5:443 {
94
+
^proxy\.test\.nix$ 127.0.0.1 proxy_protocol
95
+
^noproxy\.test\.nix$ 127.0.0.2
104
-
def check_origin_ip(src_ip: str, dst_url: str, failure: bool = False, proxy_protocol: bool = False, expected_ip: str | None = None):
105
-
check = webserver.fail if failure else webserver.succeed
106
-
if expected_ip is None:
107
-
expected_ip = src_ip
103
+
def check_origin_ip(src_ip: str, dst_url: str, failure: bool = False, proxy_protocol: bool = False, expected_ip: str | None = None):
104
+
check = webserver.fail if failure else webserver.succeed
105
+
if expected_ip is None:
106
+
expected_ip = src_ip
109
-
return check(f"curl {'--haproxy-protocol' if proxy_protocol else '''} --interface {src_ip} --fail -L {dst_url} | grep '{expected_ip}'")
108
+
return check(f"curl {'--haproxy-protocol' if proxy_protocol else '''} --interface {src_ip} --fail -L {dst_url} | grep '{expected_ip}'")
111
-
webserver.wait_for_unit("nginx")
112
-
webserver.wait_for_unit("sniproxy")
113
-
# This should be closed by virtue of ssl = true;
114
-
webserver.wait_for_closed_port(80, "127.0.0.1")
115
-
# This should be open by virtue of no explicit ssl
116
-
webserver.wait_for_open_port(80, "127.0.0.2")
117
-
# This should be open by virtue of ssl = true;
118
-
webserver.wait_for_open_port(443, "127.0.0.1")
119
-
# This should be open by virtue of no explicit ssl
120
-
webserver.wait_for_open_port(443, "127.0.0.2")
121
-
# This should be open by sniproxy
122
-
webserver.wait_for_open_port(443, "127.0.0.5")
123
-
# This should be closed by sniproxy
124
-
webserver.wait_for_closed_port(80, "127.0.0.5")
110
+
webserver.wait_for_unit("nginx")
111
+
webserver.wait_for_unit("sniproxy")
112
+
# This should be closed by virtue of ssl = true;
113
+
webserver.wait_for_closed_port(80, "127.0.0.1")
114
+
# This should be open by virtue of no explicit ssl
115
+
webserver.wait_for_open_port(80, "127.0.0.2")
116
+
# This should be open by virtue of ssl = true;
117
+
webserver.wait_for_open_port(443, "127.0.0.1")
118
+
# This should be open by virtue of no explicit ssl
119
+
webserver.wait_for_open_port(443, "127.0.0.2")
120
+
# This should be open by sniproxy
121
+
webserver.wait_for_open_port(443, "127.0.0.5")
122
+
# This should be closed by sniproxy
123
+
webserver.wait_for_closed_port(80, "127.0.0.5")
126
-
# Sanity checks for the NGINX module
127
-
# direct-HTTP connection to NGINX without TLS, this checks that ssl = false; works well.
128
-
check_origin_ip("127.0.0.10", "http://direct-nossl.test.nix/")
129
-
# webserver.execute("openssl s_client -showcerts -connect direct-noproxy.test.nix:443")
130
-
# direct-HTTP connection to NGINX with TLS
131
-
check_origin_ip("127.0.0.10", "http://direct-noproxy.test.nix/")
132
-
check_origin_ip("127.0.0.10", "https://direct-noproxy.test.nix/")
133
-
# Well, sniproxy is not listening on 80 and cannot redirect
134
-
check_origin_ip("127.0.0.10", "http://proxy.test.nix/", failure=True)
135
-
check_origin_ip("127.0.0.10", "http://noproxy.test.nix/", failure=True)
125
+
# Sanity checks for the NGINX module
126
+
# direct-HTTP connection to NGINX without TLS, this checks that ssl = false; works well.
127
+
check_origin_ip("127.0.0.10", "http://direct-nossl.test.nix/")
128
+
# webserver.execute("openssl s_client -showcerts -connect direct-noproxy.test.nix:443")
129
+
# direct-HTTP connection to NGINX with TLS
130
+
check_origin_ip("127.0.0.10", "http://direct-noproxy.test.nix/")
131
+
check_origin_ip("127.0.0.10", "https://direct-noproxy.test.nix/")
132
+
# Well, sniproxy is not listening on 80 and cannot redirect
133
+
check_origin_ip("127.0.0.10", "http://proxy.test.nix/", failure=True)
134
+
check_origin_ip("127.0.0.10", "http://noproxy.test.nix/", failure=True)
137
-
# Actual PROXY protocol related tests
138
-
# Connecting through sniproxy should passthrough the originating IP address.
139
-
check_origin_ip("127.0.0.10", "https://proxy.test.nix/")
140
-
# Connecting through sniproxy to a non-PROXY protocol enabled listener should not pass the originating IP address.
141
-
check_origin_ip("127.0.0.10", "https://noproxy.test.nix/", expected_ip="127.0.0.5")
136
+
# Actual PROXY protocol related tests
137
+
# Connecting through sniproxy should passthrough the originating IP address.
138
+
check_origin_ip("127.0.0.10", "https://proxy.test.nix/")
139
+
# Connecting through sniproxy to a non-PROXY protocol enabled listener should not pass the originating IP address.
140
+
check_origin_ip("127.0.0.10", "https://noproxy.test.nix/", expected_ip="127.0.0.5")
143
-
# Attack tests against spoofing
144
-
# Let's try to spoof our IP address by connecting direct-y to the PROXY protocol listener.
145
-
# FIXME(RaitoBezarius): rewrite it using Python + (Scapy|something else) as this is too much broken unfortunately.
146
-
# Or wait for upstream curl patch.
147
-
# def generate_attacker_request(original_ip: str, target_ip: str, dst_url: str):
148
-
# return f"""PROXY TCP4 {original_ip} {target_ip} 80 80
142
+
# Attack tests against spoofing
143
+
# Let's try to spoof our IP address by connecting direct-y to the PROXY protocol listener.
144
+
# FIXME(RaitoBezarius): rewrite it using Python + (Scapy|something else) as this is too much broken unfortunately.
145
+
# Or wait for upstream curl patch.
146
+
# def generate_attacker_request(original_ip: str, target_ip: str, dst_url: str):
147
+
# return f"""PROXY TCP4 {original_ip} {target_ip} 80 80
153
-
# def spoof(original_ip: str, target_ip: str, dst_url: str, tls: bool = False, expect_failure: bool = True):
154
-
# method = webserver.fail if expect_failure else webserver.succeed
155
-
# port = 443 if tls else 80
156
-
# print(webserver.execute(f"cat <<EOF | nc {target_ip} {port}\n{generate_attacker_request(original_ip, target_ip, dst_url)}\nEOF"))
157
-
# return method(f"cat <<EOF | nc {target_ip} {port} | grep {original_ip}\n{generate_attacker_request(original_ip, target_ip, dst_url)}\nEOF")
152
+
# def spoof(original_ip: str, target_ip: str, dst_url: str, tls: bool = False, expect_failure: bool = True):
153
+
# method = webserver.fail if expect_failure else webserver.succeed
154
+
# port = 443 if tls else 80
155
+
# print(webserver.execute(f"cat <<EOF | nc {target_ip} {port}\n{generate_attacker_request(original_ip, target_ip, dst_url)}\nEOF"))
156
+
# return method(f"cat <<EOF | nc {target_ip} {port} | grep {original_ip}\n{generate_attacker_request(original_ip, target_ip, dst_url)}\nEOF")
159
-
# check_origin_ip("127.0.0.10", "http://unsecure-nossl.test.nix", proxy_protocol=True)
160
-
# spoof("1.1.1.1", "127.0.0.4", "direct-nossl.test.nix")
161
-
# spoof("1.1.1.1", "127.0.0.4", "unsecure-nossl.test.nix", expect_failure=False)
158
+
# check_origin_ip("127.0.0.10", "http://unsecure-nossl.test.nix", proxy_protocol=True)
159
+
# spoof("1.1.1.1", "127.0.0.4", "direct-nossl.test.nix")
160
+
# spoof("1.1.1.1", "127.0.0.4", "unsecure-nossl.test.nix", expect_failure=False)