Skip to content

Commit 929ac3b

Browse files
committed
Adds env var to set certbot acme server
this is required for test suite to use dns certbot request without talking to live or staging letsencrypt servers or production level dns providers. This is a backwards port from the v3 branch and opens the door for a full certificate cypress test
1 parent f48e1b4 commit 929ac3b

File tree

9 files changed

+180
-11
lines changed

9 files changed

+180
-11
lines changed

backend/internal/certificate.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const internalHost = require('./host');
2020

2121

2222
const letsencryptStaging = config.useLetsencryptStaging();
23+
const letsencryptServer = config.useLetsencryptServer();
2324
const letsencryptConfig = '/etc/letsencrypt.ini';
2425
const certbotCommand = 'certbot';
2526

@@ -838,7 +839,8 @@ const internalCertificate = {
838839
'--email "' + certificate.meta.letsencrypt_email + '" ' +
839840
'--preferred-challenges "dns,http" ' +
840841
'--domains "' + certificate.domain_names.join(',') + '" ' +
841-
(letsencryptStaging ? '--staging' : '');
842+
(letsencryptStaging ? '--staging' : '') +
843+
(letsencryptServer !== null ? `--server '${letsencryptServer}'` : '');
842844

843845
logger.info('Command:', cmd);
844846

backend/lib/config.js

+10
Original file line numberDiff line numberDiff line change
@@ -180,5 +180,15 @@ module.exports = {
180180
*/
181181
useLetsencryptStaging: function () {
182182
return !!process.env.LE_STAGING;
183+
},
184+
185+
/**
186+
* @returns {string|null}
187+
*/
188+
useLetsencryptServer: function () {
189+
if (process.env.LE_SERVER) {
190+
return process.env.LE_SERVER;
191+
}
192+
return null;
183193
}
184194
};

docker/Dockerfile

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
# This file assumes that the frontend has been built using ./scripts/frontend-build
55

6+
FROM nginxproxymanager/testca AS testca
7+
FROM letsencrypt/pebble AS pebbleca
68
FROM nginxproxymanager/nginx-full:certbot-node
79

810
ARG TARGETPLATFORM
@@ -45,6 +47,8 @@ RUN yarn install \
4547

4648
# add late to limit cache-busting by modifications
4749
COPY docker/rootfs /
50+
COPY --from=pebbleca /test/certs/pebble.minica.pem /etc/ssl/certs/pebble.minica.pem
51+
COPY --from=testca /home/step/certs/root_ca.crt /etc/ssl/certs/NginxProxyManager.crt
4852

4953
# Remove frontend service not required for prod, dev nginx config as well
5054
RUN rm -rf /etc/s6-overlay/s6-rc.d/user/contents.d/frontend /etc/nginx/conf.d/dev.conf \

docker/dev/Dockerfile

+15-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
FROM nginxproxymanager/testca AS testca
2+
FROM letsencrypt/pebble AS pebbleca
13
FROM nginxproxymanager/nginx-full:certbot-node
24
LABEL maintainer="Jamie Curnow <[email protected]>"
35

4-
# See: https://github.com/just-containers/s6-overlay/blob/master/README.md
6+
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
7+
58
ENV SUPPRESS_NO_CONFIG_WARNING=1 \
69
S6_BEHAVIOUR_IF_STAGE2_FAILS=1 \
710
S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 \
@@ -17,17 +20,20 @@ RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
1720
&& rm -rf /var/lib/apt/lists/*
1821

1922
# Task
20-
RUN cd /usr \
21-
&& curl -sL https://taskfile.dev/install.sh | sh \
22-
&& cd /root
23+
WORKDIR /usr
24+
RUN curl -sL https://taskfile.dev/install.sh | sh
25+
WORKDIR /root
2326

2427
COPY rootfs /
25-
RUN rm -f /etc/nginx/conf.d/production.conf
26-
RUN chmod 644 /etc/logrotate.d/nginx-proxy-manager
27-
28-
# s6 overlay
2928
COPY scripts/install-s6 /tmp/install-s6
30-
RUN /tmp/install-s6 "${TARGETPLATFORM}" && rm -f /tmp/install-s6
29+
RUN rm -f /etc/nginx/conf.d/production.conf \
30+
&& chmod 644 /etc/logrotate.d/nginx-proxy-manager \
31+
&& /tmp/install-s6 "${TARGETPLATFORM}" \
32+
&& rm -f /tmp/install-s6
33+
34+
# Certs for testing purposes
35+
COPY --from=pebbleca /test/certs/pebble.minica.pem /etc/ssl/certs/pebble.minica.pem
36+
COPY --from=testca /home/step/certs/root_ca.crt /etc/ssl/certs/NginxProxyManager.crt
3137

3238
EXPOSE 80 81 443
3339
ENTRYPOINT [ "/init" ]

docker/docker-compose.ci.yml

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ services:
99
environment:
1010
DEBUG: 'true'
1111
FORCE_COLOR: 1
12+
# Required for DNS Certificate provisioning in CI
13+
LE_SERVER: 'https://ca.internal/acme/acme/directory'
14+
REQUESTS_CA_BUNDLE: '/etc/ssl/certs/NginxProxyManager.crt'
1215
volumes:
1316
- 'npm_data_ci:/data'
1417
- 'npm_le_ci:/etc/letsencrypt'

docker/docker-compose.dev.yml

+86
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,20 @@ services:
3333
DB_MYSQL_NAME: 'npm'
3434
# DB_SQLITE_FILE: "/data/database.sqlite"
3535
# DISABLE_IPV6: "true"
36+
# Required for DNS Certificate provisioning testing:
37+
LE_SERVER: 'https://ca.internal/acme/acme/directory'
38+
REQUESTS_CA_BUNDLE: '/etc/ssl/certs/NginxProxyManager.crt'
3639
volumes:
3740
- npm_data:/data
3841
- le_data:/etc/letsencrypt
42+
- './dev/resolv.conf:/etc/resolv.conf:ro'
3943
- ../backend:/app
4044
- ../frontend:/app/frontend
4145
- ../global:/app/global
46+
healthcheck:
47+
test: ["CMD", "/usr/bin/check-health"]
48+
interval: 10s
49+
timeout: 3s
4250
depends_on:
4351
- db
4452
working_dir: /app
@@ -58,6 +66,23 @@ services:
5866
volumes:
5967
- db_data:/var/lib/mysql
6068

69+
stepca:
70+
image: jc21/testca
71+
volumes:
72+
- './dev/resolv.conf:/etc/resolv.conf:ro'
73+
- '/etc/localtime:/etc/localtime:ro'
74+
networks:
75+
nginx_proxy_manager:
76+
aliases:
77+
- ca.internal
78+
79+
dnsrouter:
80+
image: jc21/dnsrouter
81+
volumes:
82+
- ./dev/dnsrouter-config.json.tmp:/dnsrouter-config.json:ro
83+
networks:
84+
- nginx_proxy_manager
85+
6186
swagger:
6287
image: swaggerapi/swagger-ui:latest
6388
container_name: npm_swagger
@@ -74,19 +99,80 @@ services:
7499
container_name: npm_squid
75100
volumes:
76101
- './dev/squid.conf:/etc/squid/squid.conf:ro'
102+
- './dev/resolv.conf:/etc/resolv.conf:ro'
77103
- '/etc/localtime:/etc/localtime:ro'
78104
networks:
79105
- nginx_proxy_manager
80106
ports:
81107
- 8128:3128
82108

109+
pdns:
110+
image: pschiffe/pdns-mysql
111+
volumes:
112+
- '/etc/localtime:/etc/localtime:ro'
113+
environment:
114+
PDNS_master: 'yes'
115+
PDNS_api: 'yes'
116+
PDNS_api_key: 'npm'
117+
PDNS_webserver: 'yes'
118+
PDNS_webserver_address: '0.0.0.0'
119+
PDNS_webserver_password: 'npm'
120+
PDNS_webserver-allow-from: '127.0.0.0/8,192.0.0.0/8,10.0.0.0/8,172.0.0.0/8'
121+
PDNS_version_string: 'anonymous'
122+
PDNS_default_ttl: 1500
123+
PDNS_allow_axfr_ips: '127.0.0.0/8,192.0.0.0/8,10.0.0.0/8,172.0.0.0/8'
124+
PDNS_gmysql_host: pdns-db
125+
PDNS_gmysql_port: 3306
126+
PDNS_gmysql_user: pdns
127+
PDNS_gmysql_password: pdns
128+
PDNS_gmysql_dbname: pdns
129+
depends_on:
130+
- pdns-db
131+
networks:
132+
nginx_proxy_manager:
133+
aliases:
134+
- ns1.pdns
135+
- ns2.pdns
136+
137+
pdns-db:
138+
image: mariadb
139+
environment:
140+
MYSQL_ROOT_PASSWORD: 'pdns'
141+
MYSQL_DATABASE: 'pdns'
142+
MYSQL_USER: 'pdns'
143+
MYSQL_PASSWORD: 'pdns'
144+
volumes:
145+
- 'pdns_mysql:/var/lib/mysql'
146+
- '/etc/localtime:/etc/localtime:ro'
147+
- './dev/pdns-db.sql:/docker-entrypoint-initdb.d/01_init.sql:ro'
148+
networks:
149+
- nginx_proxy_manager
150+
151+
cypress:
152+
image: "npm_dev_cypress"
153+
build:
154+
context: ../
155+
dockerfile: test/cypress/Dockerfile
156+
environment:
157+
HTTP_PROXY: 'squid:3128'
158+
HTTPS_PROXY: 'squid:3128'
159+
volumes:
160+
- '../test/results:/results'
161+
- './dev/resolv.conf:/etc/resolv.conf:ro'
162+
- '/etc/localtime:/etc/localtime:ro'
163+
command: cypress run --browser chrome --config-file=cypress/config/ci.js
164+
networks:
165+
- nginx_proxy_manager
166+
83167
volumes:
84168
npm_data:
85169
name: npm_core_data
86170
le_data:
87171
name: npm_le_data
88172
db_data:
89173
name: npm_db_data
174+
pdns_mysql:
175+
name: npm_pdns_mysql
90176

91177
networks:
92178
nginx_proxy_manager:

scripts/.common.sh

+10
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,13 @@ COMPOSE_PROJECT_NAME="npmdev"
1515
COMPOSE_FILE="docker/docker-compose.dev.yml"
1616

1717
export COMPOSE_FILE COMPOSE_PROJECT_NAME
18+
19+
# $1: container_name
20+
get_container_ip () {
21+
local container_name=$1
22+
local container
23+
local ip
24+
container=$(docker-compose ps --all -q "${container_name}" | tail -n1)
25+
ip=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$container")
26+
echo "$ip"
27+
}

scripts/cypress-dev

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/bin/bash -e
2+
3+
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
4+
. "$DIR/.common.sh"
5+
6+
# Ensure docker-compose exists
7+
if hash docker-compose 2>/dev/null; then
8+
cd "${DIR}/.."
9+
rm -rf "$DIR/../test/results"
10+
docker-compose up --build cypress
11+
else
12+
echo -e "${RED}❯ docker-compose command is not available${RESET}"
13+
fi

scripts/start-dev

+36-1
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,43 @@ DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
77
if hash docker-compose 2>/dev/null; then
88
cd "${DIR}/.."
99
echo -e "${BLUE}${CYAN}Starting Dev Stack ...${RESET}"
10+
echo -e "${BLUE}$(docker-compose config)${RESET}"
1011

11-
docker-compose up -d --remove-orphans --force-recreate --build
12+
# Bring up a stack, in steps so we can inject IPs everywhere
13+
docker-compose up -d pdns pdns-db
14+
PDNS_IP=$(get_container_ip "pdns")
15+
echo -e "${BLUE}${YELLOW}PDNS IP is ${PDNS_IP}${RESET}"
16+
17+
# adjust the dnsrouter config
18+
LOCAL_DNSROUTER_CONFIG="$DIR/../docker/dev/dnsrouter-config.json"
19+
rm -rf "$LOCAL_DNSROUTER_CONFIG.tmp"
20+
# IMPORTANT: changes to dnsrouter-config.json will affect this line:
21+
jq --arg a "$PDNS_IP" '.servers[0].upstreams[1].upstream = $a' "$LOCAL_DNSROUTER_CONFIG" > "$LOCAL_DNSROUTER_CONFIG.tmp"
22+
23+
docker-compose up -d dnsrouter
24+
DNSROUTER_IP=$(get_container_ip "dnsrouter")
25+
echo -e "${BLUE}${YELLOW}DNS Router IP is ${DNSROUTER_IP}"
26+
27+
if [ "${DNSROUTER_IP:-}" = "" ]; then
28+
echo -e "${RED}❯ ERROR: DNS Router IP is not set${RESET}"
29+
exit 1
30+
fi
31+
32+
# mount the resolver
33+
LOCAL_RESOLVE="$DIR/../docker/dev/resolv.conf"
34+
rm -rf "${LOCAL_RESOLVE}"
35+
printf "nameserver %s\noptions ndots:0" "${DNSROUTER_IP}" > "${LOCAL_RESOLVE}"
36+
37+
# bring up all remaining containers, except cypress!
38+
docker-compose up -d --remove-orphans stepca squid
39+
docker-compose pull db
40+
docker-compose up -d --remove-orphans --pull=never fullstack
41+
docker-compose up -d --remove-orphans swagger
42+
43+
# docker-compose up -d --remove-orphans --force-recreate --build
44+
45+
# wait for main container to be healthy
46+
bash "$DIR/wait-healthy" "$(docker-compose ps --all -q fullstack)" 120
1247

1348
echo ""
1449
echo -e "${CYAN}Admin UI: http://127.0.0.1:3081${RESET}"

0 commit comments

Comments
 (0)