Modify proxy to pass full certificate before login (#896)

* Modify proxy to pass full certificate until partner is logged in

* refactor tests

* revert package-lock.json

* add sample cert string to tests
This commit is contained in:
sarahcaseybot 2020-12-15 16:36:39 -05:00 committed by GitHub
parent cb16a7649f
commit 9975bc2195
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 163 additions and 20 deletions

View file

@ -16,6 +16,7 @@ package google.registry.proxy;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.networking.handler.SslServerInitializer.CLIENT_CERTIFICATE_PROMISE_KEY;
import static google.registry.proxy.TestUtils.SAMPLE_CERT;
import static google.registry.proxy.handler.ProxyProtocolHandler.REMOTE_ADDRESS_KEY;
import static google.registry.util.ResourceUtils.readResourceBytes;
import static google.registry.util.X509Utils.getCertificateHash;
@ -25,7 +26,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.base.Throwables;
import google.registry.proxy.handler.HttpsRelayServiceHandler.NonOkHttpResponseException;
import google.registry.testing.FakeClock;
import google.registry.util.SelfSignedCaCertificate;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedChannel;
@ -36,6 +36,8 @@ import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.cookie.Cookie;
import io.netty.handler.codec.http.cookie.DefaultCookie;
import io.netty.util.concurrent.Promise;
import java.io.ByteArrayInputStream;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -96,8 +98,8 @@ class EppProtocolModuleTest extends ProtocolModuleTest {
return buffer;
}
private FullHttpRequest makeEppHttpRequest(byte[] content, Cookie... cookies) {
return TestUtils.makeEppHttpRequest(
private FullHttpRequest makeEppHttpRequestWithCertificate(byte[] content, Cookie... cookies) {
return TestUtils.makeEppHttpRequestWithCertificate(
new String(content, UTF_8),
PROXY_CONFIG.epp.relayHost,
PROXY_CONFIG.epp.relayPath,
@ -120,7 +122,10 @@ class EppProtocolModuleTest extends ProtocolModuleTest {
@Override
void beforeEach() throws Exception {
testComponent = makeTestComponent(new FakeClock());
certificate = SelfSignedCaCertificate.create().cert();
CertificateFactory cf = CertificateFactory.getInstance("X.509");
certificate =
(X509Certificate)
cf.generateCertificate(new ByteArrayInputStream(SAMPLE_CERT.getBytes(UTF_8)));
initializeChannel(
ch -> {
ch.attr(REMOTE_ADDRESS_KEY).set(CLIENT_ADDRESS);
@ -134,13 +139,15 @@ class EppProtocolModuleTest extends ProtocolModuleTest {
@Test
void testSuccess_singleFrameInboundMessage() throws Exception {
// First inbound message is hello.
assertThat((FullHttpRequest) channel.readInbound()).isEqualTo(makeEppHttpRequest(HELLO_BYTES));
assertThat((FullHttpRequest) channel.readInbound())
.isEqualTo(makeEppHttpRequestWithCertificate(HELLO_BYTES));
byte[] inputBytes = readResourceBytes(getClass(), "login.xml").read();
// Verify inbound message is as expected.
assertThat(channel.writeInbound(getByteBufFromContent(inputBytes))).isTrue();
assertThat((FullHttpRequest) channel.readInbound()).isEqualTo(makeEppHttpRequest(inputBytes));
assertThat((FullHttpRequest) channel.readInbound())
.isEqualTo(makeEppHttpRequestWithCertificate(inputBytes));
// Nothing more to read.
assertThat((Object) channel.readInbound()).isNull();
@ -161,8 +168,10 @@ class EppProtocolModuleTest extends ProtocolModuleTest {
Unpooled.wrappedBuffer(
getByteBufFromContent(inputBytes1), getByteBufFromContent(inputBytes2))))
.isTrue();
assertThat((FullHttpRequest) channel.readInbound()).isEqualTo(makeEppHttpRequest(inputBytes1));
assertThat((FullHttpRequest) channel.readInbound()).isEqualTo(makeEppHttpRequest(inputBytes2));
assertThat((FullHttpRequest) channel.readInbound())
.isEqualTo(makeEppHttpRequestWithCertificate(inputBytes1));
assertThat((FullHttpRequest) channel.readInbound())
.isEqualTo(makeEppHttpRequestWithCertificate(inputBytes2));
// Nothing more to read.
assertThat((Object) channel.readInbound()).isNull();
@ -186,11 +195,13 @@ class EppProtocolModuleTest extends ProtocolModuleTest {
// The second frame contains the first message, and part of the second message.
assertThat(channel.writeInbound(inputBuffer.readBytes(inputBytes2.length))).isTrue();
assertThat((FullHttpRequest) channel.readInbound()).isEqualTo(makeEppHttpRequest(inputBytes1));
assertThat((FullHttpRequest) channel.readInbound())
.isEqualTo(makeEppHttpRequestWithCertificate(inputBytes1));
// The third frame contains the rest of the second message.
assertThat(channel.writeInbound(inputBuffer)).isTrue();
assertThat((FullHttpRequest) channel.readInbound()).isEqualTo(makeEppHttpRequest(inputBytes2));
assertThat((FullHttpRequest) channel.readInbound())
.isEqualTo(makeEppHttpRequestWithCertificate(inputBytes2));
// Nothing more to read.
assertThat((Object) channel.readInbound()).isNull();
@ -252,7 +263,7 @@ class EppProtocolModuleTest extends ProtocolModuleTest {
byte[] inputBytes1 = readResourceBytes(getClass(), "logout.xml").read();
assertThat(channel.writeInbound(getByteBufFromContent(inputBytes1))).isTrue();
assertThat((FullHttpRequest) channel.readInbound())
.isEqualTo(makeEppHttpRequest(inputBytes1, cookie1, cookie2));
.isEqualTo(makeEppHttpRequestWithCertificate(inputBytes1, cookie1, cookie2));
// Second outbound message change cookies.
byte[] outputBytes2 = readResourceBytes(getClass(), "logout_response.xml").read();
@ -267,7 +278,7 @@ class EppProtocolModuleTest extends ProtocolModuleTest {
byte[] inputBytes2 = readResourceBytes(getClass(), "login.xml").read();
assertThat(channel.writeInbound(getByteBufFromContent(inputBytes2))).isTrue();
assertThat((FullHttpRequest) channel.readInbound())
.isEqualTo(makeEppHttpRequest(inputBytes2, cookie1, cookie2, cookie3));
.isEqualTo(makeEppHttpRequestWithCertificate(inputBytes2, cookie1, cookie2, cookie3));
// Nothing more to write or read.
assertThat((Object) channel.readOutbound()).isNull();

View file

@ -36,6 +36,54 @@ import io.netty.handler.codec.http.cookie.ServerCookieEncoder;
/** Utility class for various helper methods used in testing. */
public class TestUtils {
public static final String SAMPLE_CERT =
"-----BEGIN CERTIFICATE-----\n"
+ "MIIDvTCCAqWgAwIBAgIJAK/PgPT0jTwRMA0GCSqGSIb3DQEBCwUAMHUxCzAJBgNV\n"
+ "BAYTAlVTMREwDwYDVQQIDAhOZXcgWW9yazERMA8GA1UEBwwITmV3IFlvcmsxDzAN\n"
+ "BgNVBAoMBkdvb2dsZTEdMBsGA1UECwwUZG9tYWluLXJlZ2lzdHJ5LXRlc3QxEDAO\n"
+ "BgNVBAMMB2NsaWVudDEwHhcNMTUwODI2MTkxODA4WhcNNDMwMTExMTkxODA4WjB1\n"
+ "MQswCQYDVQQGEwJVUzERMA8GA1UECAwITmV3IFlvcmsxETAPBgNVBAcMCE5ldyBZ\n"
+ "b3JrMQ8wDQYDVQQKDAZHb29nbGUxHTAbBgNVBAsMFGRvbWFpbi1yZWdpc3RyeS10\n"
+ "ZXN0MRAwDgYDVQQDDAdjbGllbnQxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n"
+ "CgKCAQEAvoE/IoFJyzb0dU4NFhL8FYgy+B/GnUd5aA66CMx5xKRMbEAtIgxU8TTO\n"
+ "W+9jdTsE00Grk3Ct4KdY73CYW+6IFXL4O0K/m5S+uajh+I2UMVZJV38RAIqNxue0\n"
+ "Egv9M4haSsCVIPcX9b+6McywfYSF1bzPb2Gb2FAQO7Jb0BjlPhPMIROCrbG40qPg\n"
+ "LWrl33dz+O52kO+DyZEzHqI55xH6au77sMITsJe+X23lzQcMFUUm8moiOw0EKrj/\n"
+ "GaMTZLHP46BCRoJDAPTNx55seIwgAHbKA2VVtqrvmA2XYJQA6ipdhfKRoJFy8Z8H\n"
+ "DYsorGtazQL2HhF/5uJD25z1m5eQHQIDAQABo1AwTjAdBgNVHQ4EFgQUParEmiSR\n"
+ "U/Oqy8hr7k+MBKhZwVkwHwYDVR0jBBgwFoAUParEmiSRU/Oqy8hr7k+MBKhZwVkw\n"
+ "DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAojsUhF6PtZrStnHBFWNR\n"
+ "ryzvANB8krZlYeX9Hkqn8zIVfAkpbVmL8aZQ7yj17jSpw47PQh3x5gwA9yc/SS0G\n"
+ "E1rGuxYH02UGbua8G0+vviSQfLtskPQzK7EIR63WNhHEo/Q9umLJkZ0LguWEBf3L\n"
+ "q8CoXv2i/RNvqVPcTNp/zCKXJZAa8wAjNRJs834AZj4k5xwyYZ3F8D5PGz+YMOmV\n"
+ "M9Qd+NdXSC/Qn7HQzFhE8p5elBV35P8oX5dXEfn0S7zOXDenp5JvvLoggOWOcKsq\n"
+ "KiWDQrsT+TMKmHL94/h4t7FghtQLMzY5SGYJsYTv/LG8tewrz6KRb/Wj3JNojyEw\n"
+ "Ug==\n"
+ "-----END CERTIFICATE-----\n";
public static final String SAMPLE_CERT_ENCODED =
"MIIDvTCCAqWgAwIBAgIJAK/PgPT0jTwRMA0GCSqGSIb3DQEBCwUAMHUxCzAJBgNV"
+ "BAYTAlVTMREwDwYDVQQIDAhOZXcgWW9yazERMA8GA1UEBwwITmV3IFlvcmsxDzAN"
+ "BgNVBAoMBkdvb2dsZTEdMBsGA1UECwwUZG9tYWluLXJlZ2lzdHJ5LXRlc3QxEDAO"
+ "BgNVBAMMB2NsaWVudDEwHhcNMTUwODI2MTkxODA4WhcNNDMwMTExMTkxODA4WjB1"
+ "MQswCQYDVQQGEwJVUzERMA8GA1UECAwITmV3IFlvcmsxETAPBgNVBAcMCE5ldyBZ"
+ "b3JrMQ8wDQYDVQQKDAZHb29nbGUxHTAbBgNVBAsMFGRvbWFpbi1yZWdpc3RyeS10"
+ "ZXN0MRAwDgYDVQQDDAdjbGllbnQxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB"
+ "CgKCAQEAvoE/IoFJyzb0dU4NFhL8FYgy+B/GnUd5aA66CMx5xKRMbEAtIgxU8TTO"
+ "W+9jdTsE00Grk3Ct4KdY73CYW+6IFXL4O0K/m5S+uajh+I2UMVZJV38RAIqNxue0"
+ "Egv9M4haSsCVIPcX9b+6McywfYSF1bzPb2Gb2FAQO7Jb0BjlPhPMIROCrbG40qPg"
+ "LWrl33dz+O52kO+DyZEzHqI55xH6au77sMITsJe+X23lzQcMFUUm8moiOw0EKrj/"
+ "GaMTZLHP46BCRoJDAPTNx55seIwgAHbKA2VVtqrvmA2XYJQA6ipdhfKRoJFy8Z8H"
+ "DYsorGtazQL2HhF/5uJD25z1m5eQHQIDAQABo1AwTjAdBgNVHQ4EFgQUParEmiSR"
+ "U/Oqy8hr7k+MBKhZwVkwHwYDVR0jBBgwFoAUParEmiSRU/Oqy8hr7k+MBKhZwVkw"
+ "DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAojsUhF6PtZrStnHBFWNR"
+ "ryzvANB8krZlYeX9Hkqn8zIVfAkpbVmL8aZQ7yj17jSpw47PQh3x5gwA9yc/SS0G"
+ "E1rGuxYH02UGbua8G0+vviSQfLtskPQzK7EIR63WNhHEo/Q9umLJkZ0LguWEBf3L"
+ "q8CoXv2i/RNvqVPcTNp/zCKXJZAa8wAjNRJs834AZj4k5xwyYZ3F8D5PGz+YMOmV"
+ "M9Qd+NdXSC/Qn7HQzFhE8p5elBV35P8oX5dXEfn0S7zOXDenp5JvvLoggOWOcKsq"
+ "KiWDQrsT+TMKmHL94/h4t7FghtQLMzY5SGYJsYTv/LG8tewrz6KRb/Wj3JNojyEw"
+ "Ug==";
public static FullHttpRequest makeHttpPostRequest(String content, String host, String path) {
ByteBuf buf = Unpooled.wrappedBuffer(content.getBytes(US_ASCII));
FullHttpRequest request =
@ -101,6 +149,21 @@ public class TestUtils {
return request;
}
public static FullHttpRequest makeEppHttpRequestWithCertificate(
String content,
String host,
String path,
String accessToken,
String sslClientCertificateHash,
String clientAddress,
Cookie... cookies) {
FullHttpRequest request =
makeEppHttpRequest(
content, host, path, accessToken, sslClientCertificateHash, clientAddress, cookies);
request.headers().set("X-SSL-Full-Certificate", SAMPLE_CERT_ENCODED);
return request;
}
public static FullHttpResponse makeWhoisHttpResponse(String content, HttpResponseStatus status) {
FullHttpResponse response = makeHttpResponse(content, status);
response.headers().set("content-type", "text/plain");

View file

@ -16,6 +16,7 @@ package google.registry.proxy.handler;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.networking.handler.SslServerInitializer.CLIENT_CERTIFICATE_PROMISE_KEY;
import static google.registry.proxy.TestUtils.SAMPLE_CERT;
import static google.registry.proxy.TestUtils.assertHttpRequestEquivalent;
import static google.registry.proxy.TestUtils.makeEppHttpResponse;
import static google.registry.proxy.handler.ProxyProtocolHandler.REMOTE_ADDRESS_KEY;
@ -43,6 +44,8 @@ import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.cookie.Cookie;
import io.netty.handler.codec.http.cookie.DefaultCookie;
import io.netty.util.concurrent.Promise;
import java.io.ByteArrayInputStream;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -109,9 +112,23 @@ class EppServiceHandlerTest {
cookies);
}
private FullHttpRequest makeEppHttpRequestWithCertificate(String content, Cookie... cookies) {
return TestUtils.makeEppHttpRequestWithCertificate(
content,
RELAY_HOST,
RELAY_PATH,
ACCESS_TOKEN,
getCertificateHash(clientCertificate),
CLIENT_ADDRESS,
cookies);
}
@BeforeEach
void beforeEach() throws Exception {
clientCertificate = SelfSignedCaCertificate.create().cert();
CertificateFactory cf = CertificateFactory.getInstance("X.509");
clientCertificate =
(X509Certificate)
cf.generateCertificate(new ByteArrayInputStream(SAMPLE_CERT.getBytes(UTF_8)));
channel = setUpNewChannel(eppServiceHandler);
}
@ -194,7 +211,7 @@ class EppServiceHandlerTest {
setHandshakeSuccess();
// hello bytes should be passed to the next handler.
FullHttpRequest helloRequest = channel.readInbound();
assertThat(helloRequest).isEqualTo(makeEppHttpRequest(HELLO));
assertThat(helloRequest).isEqualTo(makeEppHttpRequestWithCertificate(HELLO));
// Nothing further to pass to the next handler.
assertThat((Object) channel.readInbound()).isNull();
assertThat(channel.isActive()).isTrue();
@ -216,12 +233,34 @@ class EppServiceHandlerTest {
String content = "<epp>stuff</epp>";
channel.writeInbound(Unpooled.wrappedBuffer(content.getBytes(UTF_8)));
FullHttpRequest request = channel.readInbound();
assertThat(request).isEqualTo(makeEppHttpRequest(content));
assertThat(request).isEqualTo(makeEppHttpRequestWithCertificate(content));
// Nothing further to pass to the next handler.
assertThat((Object) channel.readInbound()).isNull();
assertThat(channel.isActive()).isTrue();
}
@Test
void testSuccess_sendCertificateOnlyBeforeLogin() throws Exception {
setHandshakeSuccess();
// First inbound message is hello.
channel.readInbound();
String content = "<epp>stuff</epp>";
channel.writeInbound(Unpooled.wrappedBuffer(content.getBytes(UTF_8)));
FullHttpRequest request = channel.readInbound();
assertThat(request).isEqualTo(makeEppHttpRequestWithCertificate(content));
// Receive response indicating session is logged in
HttpResponse response = makeEppHttpResponse(content, HttpResponseStatus.OK);
response.headers().set("Logged-In", "true");
// Send another inbound message after login
channel.writeOutbound(response);
channel.writeInbound(Unpooled.wrappedBuffer(content.getBytes(UTF_8)));
request = channel.readInbound();
// Second request should not have full certificate
assertThat(request).isEqualTo(makeEppHttpRequest(content));
assertThat((Object) channel.readInbound()).isNull();
assertThat(channel.isActive()).isTrue();
}
@Test
void testSuccess_sendResponseToNextHandler() throws Exception {
setHandshakeSuccess();
@ -294,7 +333,8 @@ class EppServiceHandlerTest {
String requestContent = "<epp>request</epp>";
channel.writeInbound(Unpooled.wrappedBuffer(requestContent.getBytes(UTF_8)));
FullHttpRequest request = channel.readInbound();
assertHttpRequestEquivalent(request, makeEppHttpRequest(requestContent, cookie1, cookie2));
assertHttpRequestEquivalent(
request, makeEppHttpRequestWithCertificate(requestContent, cookie1, cookie2));
// Nothing further to pass to the next handler.
assertThat((Object) channel.readInbound()).isNull();
assertThat((Object) channel.readOutbound()).isNull();
@ -317,13 +357,16 @@ class EppServiceHandlerTest {
// First request written.
channel.writeInbound(Unpooled.wrappedBuffer(requestContent1.getBytes(UTF_8)));
FullHttpRequest request1 = channel.readInbound();
assertHttpRequestEquivalent(request1, makeEppHttpRequest(requestContent1, cookie1, cookie2));
assertHttpRequestEquivalent(
request1, makeEppHttpRequestWithCertificate(requestContent1, cookie1, cookie2));
String responseContent2 = "<epp>response2</epp>";
Cookie cookie3 = new DefaultCookie("name3", "value3");
Cookie newCookie2 = new DefaultCookie("name2", "newValue");
// Second response written.
channel.writeOutbound(
makeEppHttpResponse(responseContent2, HttpResponseStatus.OK, cookie3, newCookie2));
HttpResponse response =
makeEppHttpResponse(responseContent2, HttpResponseStatus.OK, cookie3, newCookie2);
response.headers().set("Logged-In", "true");
channel.writeOutbound(response);
channel.readOutbound();
String requestContent2 = "<epp>request2</epp>";
// Second request written.