diff --git a/build.gradle b/build.gradle index 0e5520d..c997f61 100644 --- a/build.gradle +++ b/build.gradle @@ -19,6 +19,7 @@ dependencies { compile 'org.toile-libre.libe:curl:0.0.19' compile 'org.java-websocket:Java-WebSocket:1.3.9' compile group: 'commons-codec', name: 'commons-codec', version: '1.5' + compile group: 'org.json', name: 'json', version: '20180813' } testlogger { diff --git a/src/test/java/SozuContainerTest.java b/src/test/java/SozuContainerTest.java index b1f5fa0..b9aefd8 100644 --- a/src/test/java/SozuContainerTest.java +++ b/src/test/java/SozuContainerTest.java @@ -2,6 +2,7 @@ import org.apache.http.HttpResponse; import org.java_websocket.client.WebSocketClient; import org.java_websocket.handshake.ServerHandshake; +import org.json.JSONObject; import org.junit.*; import org.junit.rules.ErrorCollector; import org.junit.rules.TestWatcher; @@ -21,6 +22,7 @@ import java.util.concurrent.TimeoutException; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.regex.Pattern; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.core.IsNot.not; @@ -493,4 +495,37 @@ public void testchunkedResponse() throws Exception { nodeBackend.stop(); sozuContainer.stop(); } + + @Test + public void testProxyProtocolWithCorrectForwardedHeaders() throws Exception { + SozuContainer sozuContainerSendProxy = SozuContainer.newSozuContainer((Inet4Address) Inet4Address.getByName("172.18.0.3"), (Inet6Address) Inet6Address.getByName("2002:ac14::ff01"), "sozu/config/send-proxy.toml"); + SozuContainer sozuContainerExpectProxy = SozuContainer.newSozuContainer((Inet4Address) Inet4Address.getByName("172.18.0.4"), (Inet6Address) Inet6Address.getByName("2002:ac14::ff02"), "sozu/config/expect-proxy.toml"); + sozuContainerSendProxy.withExposedPorts(SozuContainer.DEFAULT_HTTP_PORT); + + NodeBackendContainer nodeBackend = new NodeBackendContainer("172.18.0.5", Paths.get("node-backends/app-forwarded-headers.js"), 8000); + + nodeBackend.start(); + sozuContainerExpectProxy.start(); + sozuContainerSendProxy.start(); + + sozuContainerExpectProxy.followOutput(toStringSozuConsumer, OutputFrame.OutputType.STDOUT); + sozuContainerSendProxy.followOutput(toStringSozuConsumer, OutputFrame.OutputType.STDOUT); + URL sozuUrl = sozuContainerSendProxy.getBaseUrl("http", SozuContainer.DEFAULT_HTTP_PORT); + + final HttpResponse curlResult = curl("-H 'Host: send.com' " + sozuUrl.toString()); + InputStream in = curlResult.getEntity().getContent(); + String rawBody = IOUtils.toString(in, "UTF-8"); + JSONObject jsonBody = new JSONObject(rawBody); + + collector.checkThat(HttpURLConnection.HTTP_OK, equalTo(curlResult.getStatusLine().getStatusCode())); + // We use + collector.checkThat(true, equalTo(Pattern.matches("proto=http;for=172\\.18\\.0\\.1:[0-9]*;by=172\\.18\\.0\\.3", jsonBody.getString("forwarded")))); + collector.checkThat("http", equalTo(jsonBody.getString("x-forwarded-proto"))); + collector.checkThat("172.18.0.1", equalTo(jsonBody.getString("x-forwarded-for"))); // IP of the docker gateway in the network bridge + collector.checkThat("80", equalTo(jsonBody.getString("x-forwarded-port"))); + + nodeBackend.stop(); + sozuContainerSendProxy.stop(); + sozuContainerExpectProxy.stop(); + } } diff --git a/src/test/resources/node-backends/app-forwarded-headers.js b/src/test/resources/node-backends/app-forwarded-headers.js new file mode 100644 index 0000000..7954209 --- /dev/null +++ b/src/test/resources/node-backends/app-forwarded-headers.js @@ -0,0 +1,23 @@ +// This backend return in the response all forwarded headers +const http = require('http') +const port = process.env.PORT || 8080 + +const requestHandler = (request, response) => { + let res = {} + res.forwarded = request.headers.forwarded + res['x-forwarded-proto'] = request.headers['x-forwarded-proto'] + res['x-forwarded-for'] = request.headers['x-forwarded-for'] + res['x-forwarded-port'] = request.headers['x-forwarded-port'] + + response.end(JSON.stringify(res)) +} + +const server = http.createServer(requestHandler) + +server.listen(port, (err) => { + if (err) { + return console.log('something bad happened', err) + } + + console.log(`server simple is listening on ${port}`) +}) diff --git a/src/test/resources/sozu/config/expect-proxy.toml b/src/test/resources/sozu/config/expect-proxy.toml new file mode 100644 index 0000000..229d6c9 --- /dev/null +++ b/src/test/resources/sozu/config/expect-proxy.toml @@ -0,0 +1,37 @@ +saved_state = "/var/lib/sozu/state.json" +log_level = "info" +log_target = "stdout" + +command_socket = "/var/lib/sozu/" +command_buffer_size = 16384 +max_command_buffer_size = 163840 + +worker_count = 1 +worker_automatic_restart = false + +handle_process_affinity = false + +max_connections = 10 + +max_buffers = 500 +buffer_size = 16384 + +tls_provider = "rustls" + +[[listeners]] +protocol = "http" +address = "172.18.0.4:8001" +expect_proxy = true + +[applications] + +[applications.MyApp] +protocol = "http" + +frontends = [ + { address = "172.18.0.4:8001", hostname="send.com" } +] + +backends = [ + { address = "172.18.0.5:8000", weight = 100 }, +] \ No newline at end of file diff --git a/src/test/resources/sozu/config/send-proxy.toml b/src/test/resources/sozu/config/send-proxy.toml new file mode 100644 index 0000000..23d3b60 --- /dev/null +++ b/src/test/resources/sozu/config/send-proxy.toml @@ -0,0 +1,37 @@ +saved_state = "/var/lib/sozu/state.json" +log_level = "trace" +log_target = "stdout" + +command_socket = "/var/lib/sozu/" +command_buffer_size = 16384 +max_command_buffer_size = 163840 + +worker_count = 1 +worker_automatic_restart = false + +handle_process_affinity = false + +max_connections = 10 + +max_buffers = 500 +buffer_size = 16384 + +tls_provider = "rustls" + +[[listeners]] +protocol = "tcp" +address = "0.0.0.0:80" + +[applications] + +[applications.ExpectProxy] +protocol = "tcp" +send_proxy = true + +frontends = [ + { address = "0.0.0.0:80" } +] + +backends = [ + { address = "172.18.0.4:8001", weight = 100 }, +] \ No newline at end of file