Skip to content

Commit

Permalink
ddev-router should not accept hostnames it's not configured for, fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
rfay committed Sep 17, 2020
1 parent 13105a1 commit 45ce7d4
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 59 deletions.
2 changes: 1 addition & 1 deletion containers/ddev-router/Dockerfile
Expand Up @@ -8,7 +8,7 @@ ENV DOCKER_HOST unix:///tmp/docker.sock

RUN apt-get -qq update && \
apt-get -qq install --no-install-recommends --no-install-suggests -y \
ca-certificates certbot curl less python-certbot-nginx procps telnet vim wget && \
ca-certificates certbot curl iputils-ping less python-certbot-nginx procps telnet vim wget && \
apt-get autoremove -y && \
apt-get clean -y && \
rm -rf /var/lib/apt/lists/*
Expand Down
2 changes: 1 addition & 1 deletion containers/ddev-router/Procfile
@@ -1,2 +1,2 @@
nginx: nginx
dockergen: docker-gen -watch -only-exposed --notify-output -notify "chmod ugo+x /gen-cert.sh && /gen-cert.sh && sleep 1 && nginx -s reload" /app/gen-cert.sh.tmpl /gen-cert.sh
dockergen: docker-gen -watch -only-exposed --notify-output -notify "bash /gen-cert-and-nginx-config.sh" /app/gen-cert-and-nginx-config.sh.tmpl /gen-cert-and-nginx-config.sh
38 changes: 38 additions & 0 deletions containers/ddev-router/gen-cert-and-nginx-config.sh.tmpl
@@ -0,0 +1,38 @@
#!/bin/bash

# This gets prprocessed by docker-gen into a script which generates needed
# mkcert certs and updates the nginx configs for all projects

set -eu -o pipefail

hostnames='{{ range $host, $containers := groupByMulti $ "Env.VIRTUAL_HOST" "," }}{{ trim $host }} {{ end }}'
echo "Processing certs and nginx for hostnames: $hostnames"

# To redirect invalid hostnames, we need a list of http ports and https ports
httpports='80
{{ range $port, $containers := groupByMulti $ "Env.HTTP_EXPOSE" "," }}{{ trim $port }}
{{ end }}'
echo "${httpports}" >/tmp/httpports.txt
httpsports='443
{{ range $port, $containers := groupByMulti $ "Env.HTTPS_EXPOSE" "," }}{{ trim $port }}
{{ end }}'
echo "${httpsports}" >/tmp/httpsports.txt

# Convert the lists into unique sets of listen directives in /tmp
awk -F: '$0 != "" {printf "\tlisten %s;\n", $1;}' /tmp/httpports.txt | sort -u >/tmp/http_ports.conf
awk -F: '$0 != "" {printf "\tlisten %s ssl http2;\n", $1;}' /tmp/httpsports.txt | sort -u >/tmp/https_ports.conf


if [ ! -z "${USE_LETSENCRYPT:-}" ]; then
for host in ${hostnames}; do
# certbot challenge can fail for many reasons, but don't let it break everything
certbot --nginx certonly -n --domain "${host}" --agree-tos --email "${LETSENCRYPT_EMAIL:-}" || true
done
fi

mkcert -cert-file /etc/nginx/certs/master.crt -key-file /etc/nginx/certs/master.key $hostnames 127.0.0.1 localhost "*.ddev.site"

# This is not recursive, as it executes completely different instructions.
# It's important for the nginx config creation and the nginx reload to take place after all cert
# activities are completed.
docker-gen -only-exposed -notify-output -notify "sleep 1 && nginx -s reload" /app/nginx.tmpl /etc/nginx/conf.d/default.conf
20 changes: 0 additions & 20 deletions containers/ddev-router/gen-cert.sh.tmpl

This file was deleted.

3 changes: 3 additions & 0 deletions containers/ddev-router/healthcheck.sh
Expand Up @@ -20,6 +20,9 @@ connect=false
if nginx -t ; then
config=true
printf "nginx config: OK "
else
printf "nginx configuration invalid: $(nginx -t)"
exit 2
fi

# Check our healthcheck endpoint
Expand Down
75 changes: 40 additions & 35 deletions containers/ddev-router/nginx.tmpl
Expand Up @@ -86,37 +86,12 @@ proxy_set_header Proxy "";
{{ $default_cert := trimSuffix ".crt" $default_cert }}
{{ $default_cert := trimSuffix ".key" $default_cert }}
server {
server_name _; # This is just an invalid value which will never trigger on a real hostname.
listen 80;
{{ if $enable_ipv6 }}
listen [::]:80;
{{ end }}
access_log /var/log/nginx/access.log vhost;

error_page 503 @noupstream;
location @noupstream {
rewrite ^(.*)$ /503.html break;
root /app;
}

location / {
return 503;
}
server_name _; # Catch-all for any server_name that is not matched in the config

## provide a health check endpoint
location = /healthcheck {
access_log off;
return 200;
}
}
# Listen on all configured http ports + 80 (HTTPS_EXPOSE)
# So that we can redirect any server_name that is invalid

{{ if exists (printf "/etc/nginx/certs/%s.crt" $default_cert) }}
server {
server_name _; # This is just an invalid value which will never trigger on a real hostname.
listen 443 ssl http2;
{{ if $enable_ipv6 }}
listen [::]:443 ssl http2;
{{ end }}
include /tmp/http_ports.conf;
access_log /var/log/nginx/access.log vhost;

error_page 503 @noupstream;
Expand All @@ -134,12 +109,42 @@ server {
access_log off;
return 200;
}

ssl_session_tickets off;
ssl_certificate /etc/nginx/certs/{{ (printf "%s.crt" $default_cert) }};
ssl_certificate_key /etc/nginx/certs/{{ (printf "%s.key" $default_cert) }};
}
{{ end }}

{{ if exists (printf "/etc/nginx/certs/%s.crt" $default_cert) }}
server {
server_name _; # Catch-all for any server_name that is not matched in the config

# Listen on all configured https ports + 443 (HTTPS_EXPOSE)
# So that we can redirect any server_name that is invalid
include /tmp/https_ports.conf;

{{ if $enable_ipv6 }}
listen [::]:443 ssl http2;
{{ end }}
access_log /var/log/nginx/access.log vhost;

error_page 503 @noupstream;
location @noupstream {
rewrite ^(.*)$ /503.html break;
root /app;
}

location / {
return 503;
}

## provide a health check endpoint
location = /healthcheck {
access_log off;
return 200;
}

ssl_session_tickets off;
ssl_certificate /etc/nginx/certs/{{ (printf "%s.crt" $default_cert) }};
ssl_certificate_key /etc/nginx/certs/{{ (printf "%s.key" $default_cert) }};
}
{{ end }}

{{ range $host, $containers := groupByMulti $ "Env.VIRTUAL_HOST" "," }}

Expand All @@ -151,7 +156,7 @@ server {
{{/* upstream {{ $upstream_name }} { */}}

{{ range $container := $containers }}
{{/* This replaces the VIRTUAL_PORT var originally provided by jwilder/nginx-proxy. */}}
{{/* HTTP_EXPOSE replaces the VIRTUAL_PORT var originally provided by jwilder/nginx-proxy. */}}
{{/* HTTP_EXPOSE provides comma-separated ports to serve for container. Ports can be defined as hostPort:containerPort */}}
{{ $ports := coalesce $container.Env.HTTP_EXPOSE "80" }}
{{ $ports := split $ports "," }}
Expand Down
2 changes: 1 addition & 1 deletion pkg/ddevapp/ddevapp_test.go
Expand Up @@ -3192,7 +3192,7 @@ func TestCustomCerts(t *testing.T) {
})
stdout = strings.Trim(stdout, "\n")
// This should be our regular wildcard cert
assert.Contains(stdout, "*.ddev.local\n*.ddev.site")
assert.Contains(stdout, "*.ddev.site")

// Now stop it so we can install new custom cert.
err = app.Stop(true, false)
Expand Down
2 changes: 1 addition & 1 deletion pkg/version/version.go
Expand Up @@ -63,7 +63,7 @@ var DBATag = "5" // Note that this can be overridden by make
var RouterImage = "drud/ddev-router"

// RouterTag defines the tag used for the router.
var RouterTag = "20200817_custom_certs" // Note that this can be overridden by make
var RouterTag = "20200909_ddev_router" // Note that this can be overridden by make

var SSHAuthImage = "drud/ddev-ssh-agent"

Expand Down

0 comments on commit 45ce7d4

Please sign in to comment.