mirror of https://github.com/shred/acme4j
Move test servers into a separate Docker container
- Enables the dns-01 test - Fixes integration test on MacOSpull/55/head
parent
c5f5a6d3f5
commit
06985c4404
|
@ -74,7 +74,7 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>io.fabric8</groupId>
|
<groupId>io.fabric8</groupId>
|
||||||
<artifactId>docker-maven-plugin</artifactId>
|
<artifactId>docker-maven-plugin</artifactId>
|
||||||
<version>0.20.1</version>
|
<version>0.21.0</version>
|
||||||
|
|
||||||
<configuration>
|
<configuration>
|
||||||
<logStdout>true</logStdout>
|
<logStdout>true</logStdout>
|
||||||
|
@ -87,7 +87,7 @@
|
||||||
<name>acme4j/pebble:${project.version}</name>
|
<name>acme4j/pebble:${project.version}</name>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<from>golang:1.7</from>
|
<from>golang:latest</from>
|
||||||
<optimise>true</optimise>
|
<optimise>true</optimise>
|
||||||
<runCmds>
|
<runCmds>
|
||||||
<run>go get -u -v -d gopkg.in/square/go-jose.v2</run>
|
<run>go get -u -v -d gopkg.in/square/go-jose.v2</run>
|
||||||
|
@ -100,7 +100,10 @@
|
||||||
<port>14000</port>
|
<port>14000</port>
|
||||||
</ports>
|
</ports>
|
||||||
<cmd>
|
<cmd>
|
||||||
<shell>pebble -config /etc/pebble/pebble-config.json</shell>
|
<shell>
|
||||||
|
echo "nameserver $(grep 'bammbamm' /etc/hosts|cut -f1)">/etc/resolv.conf; \
|
||||||
|
pebble -config /etc/pebble/pebble-config.json
|
||||||
|
</shell>
|
||||||
</cmd>
|
</cmd>
|
||||||
<assembly>
|
<assembly>
|
||||||
<mode>dir</mode>
|
<mode>dir</mode>
|
||||||
|
@ -115,12 +118,58 @@
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
<run>
|
<run>
|
||||||
<network>
|
<ports>
|
||||||
<mode>host</mode>
|
<port>14000:14000</port>
|
||||||
</network>
|
</ports>
|
||||||
<extraHosts>
|
<links>
|
||||||
<host>example.com:127.0.0.1</host>
|
<link>bammbamm</link>
|
||||||
</extraHosts>
|
</links>
|
||||||
|
<wait>
|
||||||
|
<log>Pebble running</log>
|
||||||
|
</wait>
|
||||||
|
<!-- DNS must point to bammbamm, but we need the IP here...
|
||||||
|
See the "echo" line in the build/cmd above.
|
||||||
|
<dns>
|
||||||
|
<host>bammbamm</host>
|
||||||
|
</dns>
|
||||||
|
-->
|
||||||
|
<!-- See https://github.com/letsencrypt/pebble/issues/36
|
||||||
|
<env>
|
||||||
|
<PEBBLE_VA_NOSLEEP>1</PEBBLE_VA_NOSLEEP>
|
||||||
|
</env>
|
||||||
|
-->
|
||||||
|
</run>
|
||||||
|
</image>
|
||||||
|
<image>
|
||||||
|
<alias>bammbamm</alias>
|
||||||
|
<name>acme4j/bammbamm:${project.version}</name>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<from>openjdk:8-jre</from>
|
||||||
|
<ports>
|
||||||
|
<port>53/udp</port>
|
||||||
|
<port>5001</port>
|
||||||
|
<port>5002</port>
|
||||||
|
<port>14001</port>
|
||||||
|
</ports>
|
||||||
|
<cmd>
|
||||||
|
<shell>
|
||||||
|
java -cp "/maven/*" org.shredzone.acme4j.it.BammBamm
|
||||||
|
</shell>
|
||||||
|
</cmd>
|
||||||
|
<assembly>
|
||||||
|
<descriptorRef>artifact-with-dependencies</descriptorRef>
|
||||||
|
</assembly>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<run>
|
||||||
|
<hostname>bammbamm</hostname>
|
||||||
|
<ports>
|
||||||
|
<port>14001:14001</port>
|
||||||
|
</ports>
|
||||||
|
<wait>
|
||||||
|
<log>Bammbamm running</log>
|
||||||
|
</wait>
|
||||||
</run>
|
</run>
|
||||||
</image>
|
</image>
|
||||||
</images>
|
</images>
|
||||||
|
@ -159,11 +208,21 @@
|
||||||
<artifactId>nanohttpd</artifactId>
|
<artifactId>nanohttpd</artifactId>
|
||||||
<version>${nanohttpd.version}</version>
|
<version>${nanohttpd.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.nanohttpd</groupId>
|
||||||
|
<artifactId>nanohttpd-nanolets</artifactId>
|
||||||
|
<version>${nanohttpd.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>dnsjava</groupId>
|
<groupId>dnsjava</groupId>
|
||||||
<artifactId>dnsjava</artifactId>
|
<artifactId>dnsjava</artifactId>
|
||||||
<version>${dnsjava.version}</version>
|
<version>${dnsjava.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
|
<artifactId>httpclient</artifactId>
|
||||||
|
<version>${httpclient.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* acme4j - Java ACME client
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Richard "Shred" Körber
|
||||||
|
* http://acme4j.shredzone.org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
*/
|
||||||
|
package org.shredzone.acme4j.it;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import fi.iki.elonen.NanoHTTPD;
|
||||||
|
import fi.iki.elonen.NanoHTTPD.IHTTPSession;
|
||||||
|
import fi.iki.elonen.NanoHTTPD.Response;
|
||||||
|
import fi.iki.elonen.router.RouterNanoHTTPD.UriResource;
|
||||||
|
import fi.iki.elonen.router.RouterNanoHTTPD.UriResponder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A generic responder class for requests.
|
||||||
|
*/
|
||||||
|
public abstract class AbstractResponder implements UriResponder {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(AbstractResponder.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the request.
|
||||||
|
*
|
||||||
|
* @param urlParams
|
||||||
|
* Map of decoded URL parameters
|
||||||
|
* @param session
|
||||||
|
* {@link IHTTPSession} containing the decoding body parameters
|
||||||
|
*/
|
||||||
|
public abstract void handle(Map<String, String> urlParams, IHTTPSession session) throws Exception;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response post(UriResource uriResource, Map<String, String> urlParams, IHTTPSession session) {
|
||||||
|
LOG.info("POST " + uriResource);
|
||||||
|
try {
|
||||||
|
session.parseBody(new HashMap<>());
|
||||||
|
handle(urlParams, session);
|
||||||
|
return NanoHTTPD.newFixedLengthResponse(Response.Status.OK, NanoHTTPD.MIME_PLAINTEXT, "OK");
|
||||||
|
} catch (Exception ex) {
|
||||||
|
LOG.error("Request failed", ex);
|
||||||
|
return NanoHTTPD.newFixedLengthResponse(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, ex.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response get(UriResource uriResource, Map<String, String> urlParams, IHTTPSession session) {
|
||||||
|
LOG.warn("Unsupported " + session.getMethod() + " " + uriResource);
|
||||||
|
return NanoHTTPD.newFixedLengthResponse(Response.Status.FORBIDDEN, NanoHTTPD.MIME_PLAINTEXT, "use POST");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response put(UriResource uriResource, Map<String, String> urlParams, IHTTPSession session) {
|
||||||
|
return get(uriResource, urlParams, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response delete(UriResource uriResource, Map<String, String> urlParams, IHTTPSession session) {
|
||||||
|
return get(uriResource, urlParams, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response other(String method, UriResource uriResource, Map<String, String> urlParams, IHTTPSession session) {
|
||||||
|
return get(uriResource, urlParams, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
* acme4j - Java ACME client
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Richard "Shred" Körber
|
||||||
|
* http://acme4j.shredzone.org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
*/
|
||||||
|
package org.shredzone.acme4j.it;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
import org.shredzone.acme4j.it.server.DnsServer;
|
||||||
|
import org.shredzone.acme4j.it.server.HttpServer;
|
||||||
|
import org.shredzone.acme4j.it.server.TlsSniServer;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import fi.iki.elonen.NanoHTTPD;
|
||||||
|
import fi.iki.elonen.router.RouterNanoHTTPD;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mock server to test Pebble. It provides a HTTP server, TLS-SNI server, and DNS
|
||||||
|
* server. The servers can be configured remotely via simple HTTP POST requests.
|
||||||
|
* <p>
|
||||||
|
* <em>WARNING:</em> This is a very simple server that is only meant to be used for
|
||||||
|
* integration tests. Do not use in the outside world!
|
||||||
|
*/
|
||||||
|
public class BammBamm {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(BammBamm.class);
|
||||||
|
|
||||||
|
private static final BammBamm INSTANCE = new BammBamm();
|
||||||
|
|
||||||
|
private final int appPort;
|
||||||
|
private final int httpPort;
|
||||||
|
private final int dnsPort;
|
||||||
|
private final int tlsSniPort;
|
||||||
|
private final AppServer appServer;
|
||||||
|
private final DnsServer dnsServer;
|
||||||
|
private final HttpServer httpServer;
|
||||||
|
private final TlsSniServer tlsSniServer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the singleton instance of {@link BammBamm}.
|
||||||
|
*/
|
||||||
|
public static BammBamm instance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BammBamm() {
|
||||||
|
ResourceBundle bundle = ResourceBundle.getBundle("bammbamm");
|
||||||
|
appPort = Integer.parseInt(bundle.getString("app.port"));
|
||||||
|
dnsPort = Integer.parseInt(bundle.getString("dns.port"));
|
||||||
|
httpPort = Integer.parseInt(bundle.getString("http.port"));
|
||||||
|
tlsSniPort = Integer.parseInt(bundle.getString("tlsSni.port"));
|
||||||
|
|
||||||
|
dnsServer = new DnsServer();
|
||||||
|
httpServer = new HttpServer();
|
||||||
|
tlsSniServer = new TlsSniServer();
|
||||||
|
appServer = new AppServer(appPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link DnsServer} instance.
|
||||||
|
*/
|
||||||
|
public DnsServer getDnsServer() {
|
||||||
|
return dnsServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link HttpServer} instance.
|
||||||
|
*/
|
||||||
|
public HttpServer getHttpServer() {
|
||||||
|
return httpServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link TlsSniServer} instance.
|
||||||
|
*/
|
||||||
|
public TlsSniServer getTlsSniServer() {
|
||||||
|
return tlsSniServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the servers.
|
||||||
|
*/
|
||||||
|
public void start() {
|
||||||
|
dnsServer.start(dnsPort);
|
||||||
|
httpServer.start(httpPort);
|
||||||
|
tlsSniServer.start(tlsSniPort);
|
||||||
|
|
||||||
|
try {
|
||||||
|
appServer.start(NanoHTTPD.SOCKET_READ_TIMEOUT, true);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new UncheckedIOException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.info("Bammbamm running, listening on port {}", appServer.getListeningPort());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the servers.
|
||||||
|
*/
|
||||||
|
public void stop() {
|
||||||
|
appServer.stop();
|
||||||
|
tlsSniServer.stop();
|
||||||
|
httpServer.stop();
|
||||||
|
dnsServer.stop();
|
||||||
|
|
||||||
|
LOG.info("Bammbamm was stopped.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* App server with all predefined routes.
|
||||||
|
*/
|
||||||
|
private static class AppServer extends RouterNanoHTTPD {
|
||||||
|
public AppServer(int port) {
|
||||||
|
super(port);
|
||||||
|
super.addMappings();
|
||||||
|
|
||||||
|
addRoute(DnsHandler.ADD_A_RECORD, DnsHandler.AddARecord.class);
|
||||||
|
addRoute(DnsHandler.REMOVE_A_RECORD, DnsHandler.RemoveARecord.class);
|
||||||
|
addRoute(DnsHandler.ADD_TXT_RECORD, DnsHandler.AddTxtRecord.class);
|
||||||
|
addRoute(DnsHandler.REMOVE_TXT_RECORD, DnsHandler.RemoveTxtRecord.class);
|
||||||
|
|
||||||
|
addRoute(HttpHandler.ADD, HttpHandler.Add.class);
|
||||||
|
addRoute(HttpHandler.REMOVE, HttpHandler.Remove.class);
|
||||||
|
|
||||||
|
addRoute(TlsSniHandler.ADD, TlsSniHandler.Add.class);
|
||||||
|
addRoute(TlsSniHandler.REMOVE, TlsSniHandler.Remove.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start bammbamm. It runs until the Java process is stopped.
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
BammBamm.instance().start();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,277 @@
|
||||||
|
/*
|
||||||
|
* acme4j - Java ACME client
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Richard "Shred" Körber
|
||||||
|
* http://acme4j.shredzone.org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
*/
|
||||||
|
package org.shredzone.acme4j.it;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.cert.CertificateEncodingException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Base64.Encoder;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.HttpStatus;
|
||||||
|
import org.apache.http.NameValuePair;
|
||||||
|
import org.apache.http.client.ClientProtocolException;
|
||||||
|
import org.apache.http.client.HttpClient;
|
||||||
|
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.impl.client.HttpClients;
|
||||||
|
import org.apache.http.message.BasicNameValuePair;
|
||||||
|
import org.apache.http.util.EntityUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A BammBamm client.
|
||||||
|
*/
|
||||||
|
public class BammBammClient {
|
||||||
|
private final String baseUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new BammBamm client.
|
||||||
|
*
|
||||||
|
* @param baseUrl
|
||||||
|
* Base URL of the BammBamm server to connect to.
|
||||||
|
*/
|
||||||
|
public BammBammClient(String baseUrl) {
|
||||||
|
this.baseUrl = baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a HTTP token.
|
||||||
|
*
|
||||||
|
* @param token
|
||||||
|
* Token to add
|
||||||
|
* @param challenge
|
||||||
|
* Challenge to respond with
|
||||||
|
*/
|
||||||
|
public void httpAddToken(String token, String challenge) throws IOException {
|
||||||
|
createRequest(HttpHandler.ADD)
|
||||||
|
.arg(":token", token)
|
||||||
|
.param("challenge", challenge)
|
||||||
|
.submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a HTTP token.
|
||||||
|
*
|
||||||
|
* @param token
|
||||||
|
* Token to remove
|
||||||
|
*/
|
||||||
|
public void httpRemoveToken(String token) throws IOException {
|
||||||
|
createRequest(HttpHandler.REMOVE)
|
||||||
|
.arg(":token", token)
|
||||||
|
.submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an A Record to the DNS. Only one A Record is supported per domain. If another
|
||||||
|
* A Record is set, it will replace the existing one.
|
||||||
|
*
|
||||||
|
* @param domain
|
||||||
|
* Domain of the A Record
|
||||||
|
* @param ip
|
||||||
|
* IP address or domain name. If a domain name is used, it will be resolved
|
||||||
|
* and the IP will be used.
|
||||||
|
*/
|
||||||
|
public void dnsAddARecord(String domain, String ip) throws IOException {
|
||||||
|
createRequest(DnsHandler.ADD_A_RECORD)
|
||||||
|
.arg(":domain", domain)
|
||||||
|
.param("ip", ip)
|
||||||
|
.submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an A Record from the DNS.
|
||||||
|
*
|
||||||
|
* @param domain
|
||||||
|
* Domain to remove the A Record from
|
||||||
|
*/
|
||||||
|
public void dnsRemoveARecord(String domain) throws IOException {
|
||||||
|
createRequest(DnsHandler.REMOVE_A_RECORD)
|
||||||
|
.arg(":domain", domain)
|
||||||
|
.submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a TXT Record to the DNS. Only one TXT Record is supported per domain. If
|
||||||
|
* another TXT Record is set, it will replace the existing one.
|
||||||
|
*
|
||||||
|
* @param domain
|
||||||
|
* Domain to add the TXT Record to
|
||||||
|
* @param txt
|
||||||
|
* TXT record to add
|
||||||
|
*/
|
||||||
|
public void dnsAddTxtRecord(String domain, String txt) throws IOException {
|
||||||
|
createRequest(DnsHandler.ADD_TXT_RECORD)
|
||||||
|
.arg(":domain", domain)
|
||||||
|
.param("txt", txt)
|
||||||
|
.submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a TXT Record from the DNS.
|
||||||
|
*
|
||||||
|
* @param domain
|
||||||
|
* Domain to remove the TXT Record from
|
||||||
|
*/
|
||||||
|
public void dnsRemoveTxtRecord(String domain) throws IOException {
|
||||||
|
createRequest(DnsHandler.REMOVE_TXT_RECORD)
|
||||||
|
.arg(":domain", domain)
|
||||||
|
.submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a certificate for TLS-SNI tests.
|
||||||
|
*
|
||||||
|
* @param alias
|
||||||
|
* An alias to be used for removal, for example the domain name being
|
||||||
|
* validated.
|
||||||
|
* @param privateKey
|
||||||
|
* {@link PrivateKey} of the certificate
|
||||||
|
* @param cert
|
||||||
|
* {@link X509Certificate} containing the domain names to respond to
|
||||||
|
*/
|
||||||
|
public void tlsSniAddCertificate(String alias, PrivateKey privateKey, X509Certificate cert) throws IOException {
|
||||||
|
try {
|
||||||
|
createRequest(TlsSniHandler.ADD)
|
||||||
|
.arg(":alias", alias)
|
||||||
|
.param("privateKey", privateKey.getEncoded())
|
||||||
|
.param("cert", cert.getEncoded())
|
||||||
|
.submit();
|
||||||
|
} catch (CertificateEncodingException ex) {
|
||||||
|
throw new IOException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a certificate.
|
||||||
|
*
|
||||||
|
* @param alias
|
||||||
|
* Certificate alias to remove
|
||||||
|
*/
|
||||||
|
public void tlsSniRemoveCertificate(String alias) throws IOException {
|
||||||
|
createRequest(TlsSniHandler.REMOVE)
|
||||||
|
.arg(":alias", alias)
|
||||||
|
.submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link Request} object.
|
||||||
|
*
|
||||||
|
* @param call
|
||||||
|
* Path to be called
|
||||||
|
* @return Created {@link Request} object
|
||||||
|
*/
|
||||||
|
private Request createRequest(String call) {
|
||||||
|
return new Request(baseUrl, call);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class helps to assemble and invoke a HTTP POST request.
|
||||||
|
*/
|
||||||
|
private static class Request {
|
||||||
|
private static final HttpClient CLIENT = HttpClients.createDefault();
|
||||||
|
private static final Encoder BASE64 = Base64.getEncoder();
|
||||||
|
private static final Charset UTF8 = Charset.forName("utf-8");
|
||||||
|
|
||||||
|
private final List<NameValuePair> params = new ArrayList<>();
|
||||||
|
private final String baseUrl;
|
||||||
|
private String call;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link Request}.
|
||||||
|
*
|
||||||
|
* @param baseUrl
|
||||||
|
* Base URL of the server to invoke
|
||||||
|
* @param call
|
||||||
|
* Path to invoke. It may contain placeholders.
|
||||||
|
*/
|
||||||
|
public Request(String baseUrl, String call) {
|
||||||
|
this.baseUrl = baseUrl;
|
||||||
|
this.call = call;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a path parameter.
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* Placeholder to change, leading ':' inclusive!
|
||||||
|
* @param value
|
||||||
|
* Value of the parameter
|
||||||
|
* @return itself
|
||||||
|
*/
|
||||||
|
public Request arg(String key, String value) {
|
||||||
|
try {
|
||||||
|
call = call.replace(key, URLEncoder.encode(value, UTF8.name()));
|
||||||
|
} catch (UnsupportedEncodingException ex) {
|
||||||
|
throw new InternalError("utf-8 missing", ex);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a form parameter. It will be sent in the request body.
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* Parameter name
|
||||||
|
* @param value
|
||||||
|
* Parameter value
|
||||||
|
* @return itself
|
||||||
|
*/
|
||||||
|
public Request param(String key, String value) {
|
||||||
|
params.add(new BasicNameValuePair(key, value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a binary form parameter. It will be sent in the request body.
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* Parameter name
|
||||||
|
* @param value
|
||||||
|
* Parameter value. It will be Base64 encoded.
|
||||||
|
* @return itself
|
||||||
|
*/
|
||||||
|
public Request param(String key, byte[] value) {
|
||||||
|
return param(key, BASE64.encodeToString(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submits the POST request.
|
||||||
|
*/
|
||||||
|
public void submit() throws IOException {
|
||||||
|
try {
|
||||||
|
HttpPost httppost = new HttpPost(baseUrl + call);
|
||||||
|
if (!params.isEmpty()) {
|
||||||
|
httppost.setEntity(new UrlEncodedFormEntity(params, UTF8));
|
||||||
|
}
|
||||||
|
HttpResponse response = CLIENT.execute(httppost);
|
||||||
|
|
||||||
|
EntityUtils.consume(response.getEntity());
|
||||||
|
|
||||||
|
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
|
||||||
|
throw new IOException(response.getStatusLine().getReasonPhrase());
|
||||||
|
}
|
||||||
|
} catch (ClientProtocolException ex) {
|
||||||
|
throw new IOException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* acme4j - Java ACME client
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Richard "Shred" Körber
|
||||||
|
* http://acme4j.shredzone.org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
*/
|
||||||
|
package org.shredzone.acme4j.it;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.shredzone.acme4j.it.server.DnsServer;
|
||||||
|
|
||||||
|
import fi.iki.elonen.NanoHTTPD.IHTTPSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request handler for all {@code dns-01} related requests.
|
||||||
|
*/
|
||||||
|
public class DnsHandler {
|
||||||
|
|
||||||
|
public static final String ADD_A_RECORD = "/dns/add/a/:domain";
|
||||||
|
public static final String REMOVE_A_RECORD = "/dns/remove/a/:domain";
|
||||||
|
public static final String ADD_TXT_RECORD = "/dns/add/txt/:domain";
|
||||||
|
public static final String REMOVE_TXT_RECORD = "/dns/remove/txt/:domain";
|
||||||
|
|
||||||
|
public static class AddARecord extends AbstractResponder {
|
||||||
|
@Override
|
||||||
|
public void handle(Map<String, String> urlParams, IHTTPSession session) throws Exception {
|
||||||
|
String domain = urlParams.get("domain");
|
||||||
|
String ip = session.getParameters().get("ip").get(0);
|
||||||
|
|
||||||
|
DnsServer server = BammBamm.instance().getDnsServer();
|
||||||
|
server.addARecord(domain, InetAddress.getByName(ip));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class RemoveARecord extends AbstractResponder {
|
||||||
|
@Override
|
||||||
|
public void handle(Map<String, String> urlParams, IHTTPSession session) throws Exception {
|
||||||
|
String domain = urlParams.get("domain");
|
||||||
|
|
||||||
|
DnsServer server = BammBamm.instance().getDnsServer();
|
||||||
|
server.removeARecord(domain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AddTxtRecord extends AbstractResponder {
|
||||||
|
@Override
|
||||||
|
public void handle(Map<String, String> urlParams, IHTTPSession session) throws Exception {
|
||||||
|
String domain = urlParams.get("domain");
|
||||||
|
String txt = session.getParameters().get("txt").get(0);
|
||||||
|
|
||||||
|
DnsServer server = BammBamm.instance().getDnsServer();
|
||||||
|
server.addTxtRecord(domain, txt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class RemoveTxtRecord extends AbstractResponder {
|
||||||
|
@Override
|
||||||
|
public void handle(Map<String, String> urlParams, IHTTPSession session) throws Exception {
|
||||||
|
String domain = urlParams.get("domain");
|
||||||
|
|
||||||
|
DnsServer server = BammBamm.instance().getDnsServer();
|
||||||
|
server.removeTxtRecord(domain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* acme4j - Java ACME client
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Richard "Shred" Körber
|
||||||
|
* http://acme4j.shredzone.org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
*/
|
||||||
|
package org.shredzone.acme4j.it;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.shredzone.acme4j.it.server.HttpServer;
|
||||||
|
|
||||||
|
import fi.iki.elonen.NanoHTTPD.IHTTPSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request handler for all {@code http-01} related requests.
|
||||||
|
*/
|
||||||
|
public class HttpHandler {
|
||||||
|
|
||||||
|
public static final String ADD = "/http/add/:token";
|
||||||
|
public static final String REMOVE = "/http/remove/:token";
|
||||||
|
|
||||||
|
public static class Add extends AbstractResponder {
|
||||||
|
@Override
|
||||||
|
public void handle(Map<String, String> urlParams, IHTTPSession session) throws Exception {
|
||||||
|
String token = urlParams.get("token");
|
||||||
|
String challenge = session.getParameters().get("challenge").get(0);
|
||||||
|
|
||||||
|
HttpServer server = BammBamm.instance().getHttpServer();
|
||||||
|
server.addToken(token, challenge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Remove extends AbstractResponder {
|
||||||
|
@Override
|
||||||
|
public void handle(Map<String, String> urlParams, IHTTPSession session) throws Exception {
|
||||||
|
String token = urlParams.get("token");
|
||||||
|
|
||||||
|
HttpServer server = BammBamm.instance().getHttpServer();
|
||||||
|
server.removeToken(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* acme4j - Java ACME client
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Richard "Shred" Körber
|
||||||
|
* http://acme4j.shredzone.org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
*/
|
||||||
|
package org.shredzone.acme4j.it;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Base64.Decoder;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.shredzone.acme4j.it.server.TlsSniServer;
|
||||||
|
|
||||||
|
import fi.iki.elonen.NanoHTTPD.IHTTPSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request handler for all {@code tls-sni-02} related requests.
|
||||||
|
*/
|
||||||
|
public class TlsSniHandler {
|
||||||
|
|
||||||
|
public static final String ADD = "/tlssni/add/:alias";
|
||||||
|
public static final String REMOVE = "/tlssni/remove/:alias";
|
||||||
|
|
||||||
|
public static class Add extends AbstractResponder {
|
||||||
|
@Override
|
||||||
|
public void handle(Map<String, String> urlParams, IHTTPSession session) throws Exception {
|
||||||
|
String alias = urlParams.get("alias");
|
||||||
|
String privateKeyEncoded = session.getParameters().get("privateKey").get(0);
|
||||||
|
String certEncoded = session.getParameters().get("cert").get(0);
|
||||||
|
|
||||||
|
Decoder base64 = Base64.getDecoder();
|
||||||
|
|
||||||
|
KeyFactory kf = KeyFactory.getInstance("RSA");
|
||||||
|
PrivateKey privateKey = kf.generatePrivate(new PKCS8EncodedKeySpec(
|
||||||
|
base64.decode(privateKeyEncoded)));
|
||||||
|
|
||||||
|
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||||
|
X509Certificate cert = (X509Certificate) cf.generateCertificate(
|
||||||
|
new ByteArrayInputStream(base64.decode(certEncoded)));
|
||||||
|
|
||||||
|
TlsSniServer server = BammBamm.instance().getTlsSniServer();
|
||||||
|
server.addCertificate(alias, privateKey, cert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Remove extends AbstractResponder {
|
||||||
|
@Override
|
||||||
|
public void handle(Map<String, String> urlParams, IHTTPSession session) throws Exception {
|
||||||
|
String alias = urlParams.get("alias");
|
||||||
|
|
||||||
|
TlsSniServer server = BammBamm.instance().getTlsSniServer();
|
||||||
|
server.removeCertificate(alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
# Port where BammBamm is listening for new connections
|
||||||
|
app.port = 14001
|
||||||
|
|
||||||
|
# DNS server port
|
||||||
|
dns.port = 53
|
||||||
|
|
||||||
|
# HTTP server port
|
||||||
|
http.port = 5002
|
||||||
|
|
||||||
|
# TLS-SNI server port
|
||||||
|
tlsSni.port = 5001
|
|
@ -23,15 +23,12 @@ import java.security.cert.X509Certificate;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
import org.junit.AfterClass;
|
|
||||||
import org.junit.BeforeClass;
|
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.shredzone.acme4j.Account;
|
||||||
|
import org.shredzone.acme4j.AccountBuilder;
|
||||||
import org.shredzone.acme4j.Authorization;
|
import org.shredzone.acme4j.Authorization;
|
||||||
import org.shredzone.acme4j.Certificate;
|
import org.shredzone.acme4j.Certificate;
|
||||||
import org.shredzone.acme4j.Order;
|
import org.shredzone.acme4j.Order;
|
||||||
import org.shredzone.acme4j.Account;
|
|
||||||
import org.shredzone.acme4j.AccountBuilder;
|
|
||||||
import org.shredzone.acme4j.Session;
|
import org.shredzone.acme4j.Session;
|
||||||
import org.shredzone.acme4j.Status;
|
import org.shredzone.acme4j.Status;
|
||||||
import org.shredzone.acme4j.challenge.Challenge;
|
import org.shredzone.acme4j.challenge.Challenge;
|
||||||
|
@ -40,9 +37,6 @@ import org.shredzone.acme4j.challenge.Http01Challenge;
|
||||||
import org.shredzone.acme4j.challenge.TlsSni02Challenge;
|
import org.shredzone.acme4j.challenge.TlsSni02Challenge;
|
||||||
import org.shredzone.acme4j.exception.AcmeException;
|
import org.shredzone.acme4j.exception.AcmeException;
|
||||||
import org.shredzone.acme4j.exception.AcmeLazyLoadingException;
|
import org.shredzone.acme4j.exception.AcmeLazyLoadingException;
|
||||||
import org.shredzone.acme4j.it.server.DnsServer;
|
|
||||||
import org.shredzone.acme4j.it.server.HttpServer;
|
|
||||||
import org.shredzone.acme4j.it.server.TlsSniServer;
|
|
||||||
import org.shredzone.acme4j.util.CSRBuilder;
|
import org.shredzone.acme4j.util.CSRBuilder;
|
||||||
import org.shredzone.acme4j.util.CertificateUtils;
|
import org.shredzone.acme4j.util.CertificateUtils;
|
||||||
|
|
||||||
|
@ -51,38 +45,12 @@ import org.shredzone.acme4j.util.CertificateUtils;
|
||||||
*/
|
*/
|
||||||
public class OrderIT extends PebbleITBase {
|
public class OrderIT extends PebbleITBase {
|
||||||
|
|
||||||
private static final int TLS_SNI_PORT = 5001;
|
|
||||||
private static final int HTTP_PORT = 5002;
|
|
||||||
private static final int DNS_PORT = 5003;
|
|
||||||
|
|
||||||
private static final String TEST_DOMAIN = "example.com";
|
private static final String TEST_DOMAIN = "example.com";
|
||||||
|
|
||||||
private static TlsSniServer tlsSniServer;
|
private final String bammbammUrl = System.getProperty("bammbammUrl", "http://localhost:14001");
|
||||||
private static HttpServer httpServer;
|
private final String bammbammHostname = System.getProperty("bammbammHostname", "bammbamm");
|
||||||
private static DnsServer dnsServer;
|
|
||||||
|
|
||||||
@BeforeClass
|
private BammBammClient client = new BammBammClient(bammbammUrl);
|
||||||
public static void setup() {
|
|
||||||
tlsSniServer = new TlsSniServer();
|
|
||||||
tlsSniServer.start(TLS_SNI_PORT);
|
|
||||||
|
|
||||||
httpServer = new HttpServer();
|
|
||||||
httpServer.start(HTTP_PORT);
|
|
||||||
|
|
||||||
dnsServer = new DnsServer();
|
|
||||||
dnsServer.start(DNS_PORT);
|
|
||||||
|
|
||||||
await().until(() -> tlsSniServer.isListening()
|
|
||||||
&& httpServer.isListening()
|
|
||||||
&& dnsServer.isListening());
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterClass
|
|
||||||
public static void shutdown() {
|
|
||||||
tlsSniServer.stop();
|
|
||||||
httpServer.stop();
|
|
||||||
dnsServer.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test if a certificate can be ordered via tns-sni-02 challenge.
|
* Test if a certificate can be ordered via tns-sni-02 challenge.
|
||||||
|
@ -98,7 +66,12 @@ public class OrderIT extends PebbleITBase {
|
||||||
X509Certificate cert = CertificateUtils.createTlsSni02Certificate(
|
X509Certificate cert = CertificateUtils.createTlsSni02Certificate(
|
||||||
challengeKey, challenge.getSubject(), challenge.getSanB());
|
challengeKey, challenge.getSubject(), challenge.getSanB());
|
||||||
|
|
||||||
tlsSniServer.addCertificate(challenge.getSubject(), challengeKey.getPrivate(), cert);
|
client.dnsAddARecord(TEST_DOMAIN, bammbammHostname);
|
||||||
|
client.tlsSniAddCertificate(challenge.getSubject(), challengeKey.getPrivate(), cert);
|
||||||
|
|
||||||
|
cleanup(() -> client.dnsRemoveARecord(TEST_DOMAIN));
|
||||||
|
cleanup(() -> client.tlsSniRemoveCertificate(challenge.getSubject()));
|
||||||
|
|
||||||
return challenge;
|
return challenge;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -112,7 +85,12 @@ public class OrderIT extends PebbleITBase {
|
||||||
Http01Challenge challenge = auth.findChallenge(Http01Challenge.TYPE);
|
Http01Challenge challenge = auth.findChallenge(Http01Challenge.TYPE);
|
||||||
assertThat(challenge, is(notNullValue()));
|
assertThat(challenge, is(notNullValue()));
|
||||||
|
|
||||||
httpServer.addToken(challenge.getToken(), challenge.getAuthorization());
|
client.dnsAddARecord(TEST_DOMAIN, bammbammHostname);
|
||||||
|
client.httpAddToken(challenge.getToken(), challenge.getAuthorization());
|
||||||
|
|
||||||
|
cleanup(() -> client.dnsRemoveARecord(TEST_DOMAIN));
|
||||||
|
cleanup(() -> client.httpRemoveToken(challenge.getToken()));
|
||||||
|
|
||||||
return challenge;
|
return challenge;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -121,13 +99,19 @@ public class OrderIT extends PebbleITBase {
|
||||||
* Test if a certificate can be ordered via dns-01 challenge.
|
* Test if a certificate can be ordered via dns-01 challenge.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@Ignore // TODO PEBBLE: cannot query our dnsServer yet...
|
|
||||||
public void testDnsValidation() throws Exception {
|
public void testDnsValidation() throws Exception {
|
||||||
orderCertificate(TEST_DOMAIN, auth -> {
|
orderCertificate(TEST_DOMAIN, auth -> {
|
||||||
Dns01Challenge challenge = auth.findChallenge(Dns01Challenge.TYPE);
|
Dns01Challenge challenge = auth.findChallenge(Dns01Challenge.TYPE);
|
||||||
assertThat(challenge, is(notNullValue()));
|
assertThat(challenge, is(notNullValue()));
|
||||||
|
|
||||||
dnsServer.addTxtRecord("_acme-challenge." + TEST_DOMAIN, challenge.getDigest());
|
String challengeDomainName = "_acme-challenge." + TEST_DOMAIN;
|
||||||
|
|
||||||
|
client.dnsAddARecord(TEST_DOMAIN, bammbammHostname);
|
||||||
|
client.dnsAddTxtRecord(challengeDomainName, challenge.getDigest());
|
||||||
|
|
||||||
|
cleanup(() -> client.dnsRemoveARecord(TEST_DOMAIN));
|
||||||
|
cleanup(() -> client.dnsRemoveTxtRecord(challengeDomainName));
|
||||||
|
|
||||||
return challenge;
|
return challenge;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -173,7 +157,7 @@ public class OrderIT extends PebbleITBase {
|
||||||
challenge.trigger();
|
challenge.trigger();
|
||||||
|
|
||||||
await()
|
await()
|
||||||
.pollInterval(3, SECONDS)
|
.pollInterval(1, SECONDS)
|
||||||
.timeout(30, SECONDS)
|
.timeout(30, SECONDS)
|
||||||
.conditionEvaluationListener(cond -> updateAuth(auth))
|
.conditionEvaluationListener(cond -> updateAuth(auth))
|
||||||
.until(auth::getStatus, not(Status.PENDING));
|
.until(auth::getStatus, not(Status.PENDING));
|
||||||
|
|
|
@ -19,7 +19,10 @@ import static org.junit.Assert.assertThat;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
import org.shredzone.acme4j.util.KeyPairUtils;
|
import org.shredzone.acme4j.util.KeyPairUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,10 +34,23 @@ import org.shredzone.acme4j.util.KeyPairUtils;
|
||||||
* {@code pebbleHost} and {@code pebblePort} respectively.
|
* {@code pebbleHost} and {@code pebblePort} respectively.
|
||||||
*/
|
*/
|
||||||
public abstract class PebbleITBase {
|
public abstract class PebbleITBase {
|
||||||
|
|
||||||
private final String pebbleHost = System.getProperty("pebbleHost", "localhost");
|
private final String pebbleHost = System.getProperty("pebbleHost", "localhost");
|
||||||
private final int pebblePort = Integer.parseInt(System.getProperty("pebblePort", "14000"));
|
private final int pebblePort = Integer.parseInt(System.getProperty("pebblePort", "14000"));
|
||||||
|
|
||||||
|
private final List<CleanupCallback> cleanup = new ArrayList<>();
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void performCleanup() throws Exception {
|
||||||
|
for (CleanupCallback callback : cleanup) {
|
||||||
|
callback.cleanup();
|
||||||
|
}
|
||||||
|
cleanup.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void cleanup(CleanupCallback callback) {
|
||||||
|
cleanup.add(callback);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The {@link URI} of the pebble server to test against.
|
* @return The {@link URI} of the pebble server to test against.
|
||||||
*/
|
*/
|
||||||
|
@ -66,4 +82,9 @@ public abstract class PebbleITBase {
|
||||||
assertThat(url.getPath(), not(isEmptyOrNullString()));
|
assertThat(url.getPath(), not(isEmptyOrNullString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public static interface CleanupCallback {
|
||||||
|
void cleanup() throws Exception;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
1
pom.xml
1
pom.xml
|
@ -53,6 +53,7 @@
|
||||||
<properties>
|
<properties>
|
||||||
<bouncycastle.version>1.57</bouncycastle.version>
|
<bouncycastle.version>1.57</bouncycastle.version>
|
||||||
<dnsjava.version>2.1.7</dnsjava.version>
|
<dnsjava.version>2.1.7</dnsjava.version>
|
||||||
|
<httpclient.version>4.5.3</httpclient.version>
|
||||||
<jose4j.version>0.6.0</jose4j.version>
|
<jose4j.version>0.6.0</jose4j.version>
|
||||||
<nanohttpd.version>2.3.1</nanohttpd.version>
|
<nanohttpd.version>2.3.1</nanohttpd.version>
|
||||||
<slf4j.version>1.7.25</slf4j.version>
|
<slf4j.version>1.7.25</slf4j.version>
|
||||||
|
|
|
@ -39,8 +39,10 @@ Now just have a look at [this source code](https://github.com/shred/acme4j/blob/
|
||||||
Running Integration Tests
|
Running Integration Tests
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
_acme4j-client_ contains a number of integration tests. These tests are _not_ executed by maven by default, as they require a [Pebble ACME test server](https://github.com/letsencrypt/pebble).
|
_acme4j_ provides a number of integration tests. These tests are _not_ executed by maven by default, as they require Docker on the build machine.
|
||||||
|
|
||||||
To run them, install and run a Pebble server instance on your build machine. Then invoke maven with the `-DskipITs=false` option set, to enable the integration tests at `verify` phase.
|
To run them, install Docker and make it available to your user. Then invoke `mvn -Pci verify` to run the integration tests. The tests build images of the current [Pebble ACME test server](https://github.com/letsencrypt/pebble), and an internal test server that provides a configurable HTTP and DNS server for Pebble.
|
||||||
|
|
||||||
If you are running a Pebble server on a different host and/or port, invoke maven with the `pebbleHost` and `pebblePort` system properties set to the appropriate host and port, respectively. For example, to connect to `acme.example.com` port 12345, use `-DpebbleHost=acme.example.com -DpebblePort=12345`.
|
If you change into the `acme-it` project directory, you can also build, start and stop the test servers with `mvn docker:build`, `mvn docker:start` and `mvn docker:stop`, respectively. While the test servers are running, you can execute the integration tests in your IDE.
|
||||||
|
|
||||||
|
The Pebble server requires a few workarounds in _acme4j_ at the moment. To enable them, pass `-Dpebble=true` as VM parameter when running the integration tests.
|
||||||
|
|
Loading…
Reference in New Issue