Skip to content

Commit 61dc07c

Browse files
authored
Merge pull request #15 from kekru/development
Update Nginx, Configure CA expiration and tests for cert generation
2 parents a18ecb5 + 7500210 commit 61dc07c

File tree

9 files changed

+197
-29
lines changed

9 files changed

+197
-29
lines changed

Dockerfile

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1-
FROM nginx:1.15.12-alpine
1+
# Original Dockerfile: https://github.com/nginxinc/docker-nginx/tree/5488180ebdd45b12b45107694dfa92dc878a2795/stable/alpine
2+
FROM nginx:1.18.0-alpine
23
LABEL MAINTAINER="Kevin Krummenauer <kevin@whiledo.de>"
34
RUN apk add --no-cache openssl
45

5-
COPY resources /script
6+
COPY resources/create-certs.sh /script/create-certs.sh
7+
COPY resources/nginx-cert.conf /etc/nginx/nginx.conf
8+
COPY resources/entrypoint.sh /docker-entrypoint.d/30_entrypoint.sh
69

7-
RUN cp /script/nginx-cert.conf /etc/nginx/nginx.conf \
8-
&& chmod +x /script/create-certs.sh /script/entrypoint.sh
10+
RUN chmod +x /script/create-certs.sh /docker-entrypoint.d/30_entrypoint.sh
911

1012
ENV CREATE_CERTS_WITH_PW="" \
1113
CERTS_DIR=/data/certs \
12-
CERT_HOSTNAME="myserver.example.com"
13-
14-
ENTRYPOINT ["/script/entrypoint.sh"]
15-
CMD ["nginx", "-g", "daemon off;"]
14+
CERT_HOSTNAME="abc.127.0.0.1.nip.io" \
15+
CERT_EXPIRATION_DAYS="365" \
16+
CA_EXPIRATION_DAYS="900"
1617

1718
HEALTHCHECK --start-period=1s \
1819
--interval=5s \

README.md

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Create a docker-compose.yml file:
2727
version: "3.4"
2828
services:
2929
remote-api:
30-
image: kekru/docker-remote-api-tls:v0.2.0
30+
image: kekru/docker-remote-api-tls:v0.3.0
3131
ports:
3232
- 2376:443
3333
volumes:
@@ -47,7 +47,7 @@ Create a docker-compose.yml file, specifying a password and the hostname, on whi
4747
version: "3.4"
4848
services:
4949
remote-api:
50-
image: kekru/docker-remote-api-tls:v0.2.0
50+
image: kekru/docker-remote-api-tls:v0.3.0
5151
ports:
5252
- 2376:443
5353
environment:
@@ -72,11 +72,15 @@ Certificate passphrase will be read from this docker secret. Absolute path of th
7272

7373
If both passphrase and secret file are set, the secret file takes precedence.
7474

75-
#### `CERT_EXPIRATION`
76-
Certificate expiration in days. If not set, the default value 365 is applied.
75+
#### `CERT_EXPIRATION_DAYS`
76+
Certificate expiration for server and client certs in days. If not set, the default value 365 is applied.
77+
78+
#### `CA_EXPIRATION_DAYS`
79+
Certificate expiration for CA in days. If not set, the default value 900 is applied.
7780

7881
#### `CERT_HOSTNAME`
79-
Domain name of the docker server.
82+
Domain name of the docker server.
83+
If you don't have a DNS name, you can use [nip.io](https://nip.io) to get a name for any IP.
8084

8185
## Setup client
8286

@@ -95,3 +99,19 @@ docker-compose up -d
9599
# Run ps over remote api (use GitBash when you are on Windows)
96100
./dockerRemote ps
97101
```
102+
103+
## Changelog
104+
105+
#### v0.2.0
106+
107+
First stable release
108+
Thanks [@smiller171](https://github.com/smiller171) for contributing!
109+
110+
#### v0.3.0
111+
112+
+ update nginx version
113+
+ add configuration for cert expiration
114+
+ add configuration to use swarm secret as password for cert generation
115+
+ add automatic tests
116+
117+
Thanks [@benkorichard](https://github.com/benkorichard) for contributing!

resources/entrypoint.sh

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
#!/bin/sh
22

3-
CERT_EXPIRATION_DAYS=${CERT_EXPIRATION:-365}
4-
53
if [ -n "$CERTS_PASSWORD_FILE" ]; then
64
echo "Using cert password from $CERTS_PASSWORD_FILE"
75
CREATE_CERTS_WITH_PW="$(cat $CERTS_PASSWORD_FILE)"
@@ -11,7 +9,7 @@ if [ -n $CREATE_CERTS_WITH_PW ]; then
119
if [ -z "$(ls -A $CERTS_DIR)" ]; then
1210

1311
echo "Create CA cert"
14-
/script/create-certs.sh -m ca -pw $CREATE_CERTS_WITH_PW -t $CERTS_DIR -e 900
12+
/script/create-certs.sh -m ca -pw $CREATE_CERTS_WITH_PW -t $CERTS_DIR -e $CA_EXPIRATION_DAYS
1513
echo "Create server cert"
1614
/script/create-certs.sh -m server -h $CERT_HOSTNAME -pw $CREATE_CERTS_WITH_PW -t $CERTS_DIR -e $CERT_EXPIRATION_DAYS
1715
echo "Create client cert"
@@ -29,5 +27,3 @@ if [ -n $CREATE_CERTS_WITH_PW ]; then
2927
echo "$CERTS_DIR is not empty. Not creating certs."
3028
fi
3129
fi
32-
33-
exec "$@"

test/docker-compose.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ services:
88
- 30129:443
99
env_file:
1010
- ./target/integr-test/remote-api.env
11-
environment:
12-
- CERT_HOSTNAME=abc.127.0.0.1.nip.io
1311
volumes:
1412
- "/var/run/docker.sock:/var/run/docker.sock:ro"
1513

test/src/test/java/de/kekru/dockerremoteapitls/test/BasicConnectionTest.java

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,20 @@
44
import static org.junit.Assert.assertThrows;
55

66
import de.kekru.dockerremoteapitls.test.utils.AbstractIntegrationTest;
7+
import de.kekru.dockerremoteapitls.test.utils.CertUtils;
8+
import java.io.File;
9+
import java.time.LocalDate;
710
import org.junit.BeforeClass;
811
import org.junit.Test;
912

1013
public class BasicConnectionTest extends AbstractIntegrationTest {
1114

1215
@BeforeClass
1316
public static void init() {
14-
startRemoteApiContainer("CREATE_CERTS_WITH_PW=supersecret");
17+
startRemoteApiContainer(
18+
"CERT_HOSTNAME=abc.127.0.0.1.nip.io",
19+
"CREATE_CERTS_WITH_PW=supersecret"
20+
);
1521
}
1622

1723
@Test
@@ -54,4 +60,40 @@ public void failsOnNoTls() {
5460
.hasMessageContaining("error during connect: Get http://abc.127.0.0.1.nip.io:30129/v1.40/containers/json: EOF");
5561
}
5662

63+
@Test
64+
public void caCertHasCorrectDefaultValues() {
65+
66+
CertUtils caCert = new CertUtils(new File(certsDir + "/ca-cert.pem"));
67+
68+
assertThat(caCert.getCert().getSubjectDN().getName())
69+
.isEqualTo("EMAILADDRESS=test@example.com, CN=example.com, OU=IT, O=ExampleCompany, L=London, ST=London, C=GB");
70+
71+
assertThat(caCert.getExpiresAt())
72+
.isEqualTo(LocalDate.now().plusDays(900));
73+
}
74+
75+
@Test
76+
public void serverCertHasCorrectDefaultValues() {
77+
78+
CertUtils caCert = new CertUtils(new File(certsDir + "/server-cert.pem"));
79+
80+
assertThat(caCert.getCert().getSubjectDN().getName())
81+
.isEqualTo("CN=abc.127.0.0.1.nip.io");
82+
83+
assertThat(caCert.getExpiresAt())
84+
.isEqualTo(LocalDate.now().plusDays(365));
85+
}
86+
87+
@Test
88+
public void clientCertHasCorrectDefaultValues() {
89+
90+
CertUtils caCert = new CertUtils(new File(certsDirClient + "/cert.pem"));
91+
92+
assertThat(caCert.getCert().getSubjectDN().getName())
93+
.isEqualTo("CN=testClient");
94+
95+
assertThat(caCert.getExpiresAt())
96+
.isEqualTo(LocalDate.now().plusDays(365));
97+
}
98+
5799
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package de.kekru.dockerremoteapitls.test;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import de.kekru.dockerremoteapitls.test.utils.AbstractIntegrationTest;
6+
import de.kekru.dockerremoteapitls.test.utils.CertUtils;
7+
import java.io.File;
8+
import java.io.IOException;
9+
import java.time.LocalDate;
10+
import org.apache.commons.io.FileUtils;
11+
import org.junit.BeforeClass;
12+
import org.junit.Test;
13+
14+
public class CertGenerationTest extends AbstractIntegrationTest {
15+
16+
@BeforeClass
17+
public static void init() {
18+
startRemoteApiContainer(
19+
"CERT_HOSTNAME=something-else.127.0.0.1.nip.io",
20+
"CREATE_CERTS_WITH_PW=supersecret123",
21+
"CERT_EXPIRATION_DAYS=17",
22+
"CA_EXPIRATION_DAYS=1273"
23+
);
24+
}
25+
26+
@Test
27+
public void caCertHasCorrectDefaultValues() {
28+
29+
CertUtils caCert = new CertUtils(new File(certsDir + "/ca-cert.pem"));
30+
31+
assertThat(caCert.getCert().getSubjectDN().getName())
32+
.isEqualTo("EMAILADDRESS=test@example.com, CN=example.com, OU=IT, O=ExampleCompany, L=London, ST=London, C=GB");
33+
34+
assertThat(caCert.getExpiresAt())
35+
.isEqualTo(LocalDate.now().plusDays(1273));
36+
}
37+
38+
@Test
39+
public void serverCertHasCorrectDefaultValues() {
40+
41+
CertUtils caCert = new CertUtils(new File(certsDir + "/server-cert.pem"));
42+
43+
assertThat(caCert.getCert().getSubjectDN().getName())
44+
.isEqualTo("CN=something-else.127.0.0.1.nip.io");
45+
46+
assertThat(caCert.getExpiresAt())
47+
.isEqualTo(LocalDate.now().plusDays(17));
48+
}
49+
50+
@Test
51+
public void clientCertHasCorrectDefaultValues() {
52+
53+
CertUtils caCert = new CertUtils(new File(certsDirClient + "/cert.pem"));
54+
55+
assertThat(caCert.getCert().getSubjectDN().getName())
56+
.isEqualTo("CN=testClient");
57+
58+
assertThat(caCert.getExpiresAt())
59+
.isEqualTo(LocalDate.now().plusDays(17));
60+
}
61+
62+
@Test
63+
public void caCertInClientDirIsSameAsInServerDir() throws IOException {
64+
65+
String ca = FileUtils.readFileToString(new File(certsDir + "/ca-cert.pem"), "UTF-8");
66+
String caInClientDir = FileUtils.readFileToString(new File(certsDirClient + "/ca.pem"), "UTF-8");
67+
68+
assertThat(ca).isEqualTo(caInClientDir);
69+
}
70+
71+
}

test/src/test/java/de/kekru/dockerremoteapitls/test/WrongCertTest.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ public class WrongCertTest extends AbstractIntegrationTest {
1818

1919
@BeforeClass
2020
public static void init() {
21-
startRemoteApiContainer("CREATE_CERTS_WITH_PW=supersecret");
21+
startRemoteApiContainer(
22+
"CERT_HOSTNAME=abc.127.0.0.1.nip.io",
23+
"CREATE_CERTS_WITH_PW=supersecret"
24+
);
2225
}
2326

2427
@Test

test/src/test/java/de/kekru/dockerremoteapitls/test/utils/AbstractIntegrationTest.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,14 @@ public class AbstractIntegrationTest {
2727

2828
@ClassRule
2929
public static TemporaryFolder folder = new TemporaryFolder();
30-
protected static ShellExecutor shellExecutor = new ShellExecutor();
31-
protected static File certsDir;
32-
protected static File certsDirClient;
33-
protected static File remoteApiEnvFile;
30+
protected static final ShellExecutor shellExecutor = new ShellExecutor();
31+
protected static final File certsDir = new File("target/integr-test/certs");
32+
protected static final File certsDirClient = new File(certsDir + "/client");
33+
protected static final File remoteApiEnvFile = new File("target/integr-test/remote-api.env");
3434

3535

3636
@BeforeClass
3737
public static void initTests() throws IOException {
38-
remoteApiEnvFile = new File("target/integr-test/remote-api.env");
39-
certsDir = new File("target/integr-test/certs");
40-
certsDirClient = new File(certsDir + "/client");
4138
if (certsDir.exists()) {
4239
FileUtils.cleanDirectory(certsDir);
4340
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package de.kekru.dockerremoteapitls.test.utils;
2+
3+
import java.io.File;
4+
import java.io.FileInputStream;
5+
import java.io.InputStream;
6+
import java.time.LocalDate;
7+
import java.time.ZoneId;
8+
import java.util.Date;
9+
import javax.security.cert.X509Certificate;
10+
11+
public class CertUtils {
12+
13+
private final X509Certificate cert;
14+
15+
public CertUtils(File file) {
16+
cert = getCert(file);
17+
}
18+
19+
private X509Certificate getCert(File file) {
20+
try (InputStream in = new FileInputStream(file)) {
21+
return X509Certificate.getInstance(in);
22+
} catch (Exception e) {
23+
throw new RuntimeException(e);
24+
}
25+
}
26+
27+
public LocalDate getExpiresAt() {
28+
return toLocalDate(cert.getNotAfter());
29+
}
30+
31+
public LocalDate toLocalDate(Date dateToConvert) {
32+
return dateToConvert.toInstant()
33+
.atZone(ZoneId.systemDefault())
34+
.toLocalDate();
35+
}
36+
37+
public X509Certificate getCert() {
38+
return cert;
39+
}
40+
}

0 commit comments

Comments
 (0)