From 3cb7df6fdda5d4a55f3ce498e9ad95d72028ed7b Mon Sep 17 00:00:00 2001
From: Nicolas Duchon <nicolas.duchon@gmail.com>
Date: Sat, 12 Oct 2024 19:07:26 +0200
Subject: [PATCH] feat: disable default certificate creation by default (#1157)

---
 app/cleanup_test_artifacts                   |   8 --
 app/entrypoint.sh                            |   6 +-
 docs/Let's-Encrypt-and-ACME.md               |   4 +
 docs/Persistent-data.md                      |   4 -
 test/config.sh                               |   1 -
 test/tests/certs_single_domain/run.sh        |   1 -
 test/tests/default_cert/expected-std-out.txt |   1 -
 test/tests/default_cert/run.sh               | 105 -------------------
 test/tests/permissions_custom/run.sh         |   8 +-
 test/tests/permissions_default/run.sh        |   8 +-
 test/tests/symlinks/run.sh                   |   3 +-
 test/tests/test-functions.sh                 |   6 --
 12 files changed, 15 insertions(+), 140 deletions(-)
 delete mode 100644 test/tests/default_cert/expected-std-out.txt
 delete mode 100755 test/tests/default_cert/run.sh

diff --git a/app/cleanup_test_artifacts b/app/cleanup_test_artifacts
index 4e76d3f0..a7dbec1a 100755
--- a/app/cleanup_test_artifacts
+++ b/app/cleanup_test_artifacts
@@ -7,14 +7,6 @@ while [[ $# -gt 0 ]]; do
     flag="$1"
 
     case $flag in
-        --default-cert)
-        for filename in default.crt default.key; do
-            filepath="/etc/nginx/certs/$filename"
-            [[ -f "$filepath" ]] && rm -rf "$filepath"
-        done
-        shift
-        ;;
-
         --location-config)
         for domain in 'le1.wtf' '*.example.com' 'test.*' 'le3.pizza' 'subdomain.example.com' 'test.domain.tld'; do
             [[ -f "/etc/nginx/vhost.d/$domain" ]] && rm -f "/etc/nginx/vhost.d/$domain"
diff --git a/app/entrypoint.sh b/app/entrypoint.sh
index 616715ec..00f1ef2b 100755
--- a/app/entrypoint.sh
+++ b/app/entrypoint.sh
@@ -110,7 +110,9 @@ function check_dh_group {
 }
 
 function check_default_cert_key {
-    local cn='letsencrypt-nginx-proxy-companion'
+    local cn='acme-companion'
+
+    echo "Warning: there is no future support planned for the self signed default certificate creation feature and it might be removed in a future release."
 
     if [[ -e /etc/nginx/certs/default.crt && -e /etc/nginx/certs/default.key ]]; then
         default_cert_cn="$(openssl x509 -noout -subject -in /etc/nginx/certs/default.crt)"
@@ -179,7 +181,7 @@ if [[ "$*" == "/bin/bash /app/start.sh" ]]; then
         check_writable_directory '/etc/nginx/vhost.d'
         check_writable_directory '/etc/nginx/conf.d'
     fi
-    check_default_cert_key
+    parse_true "${CREATE_DEFAULT_CERTIFICATE:=false}" && check_default_cert_key
     check_dh_group
     reload_nginx
     check_default_account
diff --git a/docs/Let's-Encrypt-and-ACME.md b/docs/Let's-Encrypt-and-ACME.md
index 42fd7457..a04d0c2c 100644
--- a/docs/Let's-Encrypt-and-ACME.md
+++ b/docs/Let's-Encrypt-and-ACME.md
@@ -153,3 +153,7 @@ Reusing private keys can help if you intend to use [HPKP](https://developer.mozi
     1. The container will use the special purpose `staging` configuration directory.
     1. The directory URI is forced to The Let's Encrypt v2 staging one (`ACME_CA_URI` is ignored)
     2. The account email address is forced empty (`DEFAULT_EMAIL` and `LETSENCRYPT_EMAIL` are ignored)
+
+#### Self signed default certificate
+
+If you want **acme-companio** to create a self signed certificate as default certificate for **nginx-proxy**, you can set the `CREATE_DEFAULT_CERTIFICATE` environment variable to `true`. This will generate a self signed cert / key pair to `/etc/nginx/certs/default.crt` and `/etc/nginx/certs/default.key`, with `acme-companion` as Common Name. Please note that no future support is planned for this feature and it might be removed in a future release.
diff --git a/docs/Persistent-data.md b/docs/Persistent-data.md
index d2c16f32..ceb87e5c 100644
--- a/docs/Persistent-data.md
+++ b/docs/Persistent-data.md
@@ -65,8 +65,6 @@ By default, the **acme-companion** container will enforce the following ownershi
 ```
 [drwxr-xr-x]  /etc/nginx/certs
 ├── [-rw-r--r-- root root]  dhparam.pem
-├── [-rw-r--r-- root root]  default.crt
-├── [-rw------- root root]  default.key
 ├── [drwxr-xr-x root root]  domain.tld
 │   ├── [-rw-r--r-- root root]  cert.pem
 │   ├── [-rw-r--r-- root root]  chain.pem
@@ -90,8 +88,6 @@ For example, `FILES_UID=1000`, `FILES_PERMS=644` and `FOLDERS_PERMS=700` will re
 ```
 [drwxr-xr-x]  /etc/nginx/certs
 ├── [-rw-r--r-- 1000 1000]  dhparam.pem
-├── [-rw-r--r-- 1000 1000]  default.crt
-├── [-rw-r--r-- 1000 1000]  default.key
 ├── [drwx------ 1000 1000]  domain.tld
 │   ├── [-rw-r--r-- 1000 1000]  cert.pem
 │   ├── [-rw-r--r-- 1000 1000]  chain.pem
diff --git a/test/config.sh b/test/config.sh
index 474b4f43..9b72e040 100755
--- a/test/config.sh
+++ b/test/config.sh
@@ -5,7 +5,6 @@ globalTests+=(
 	docker_api
 	docker_api_legacy
 	location_config
-	default_cert
 	certs_single
 	certs_san
 	certs_single_domain
diff --git a/test/tests/certs_single_domain/run.sh b/test/tests/certs_single_domain/run.sh
index 7a068a01..7ea0d7df 100755
--- a/test/tests/certs_single_domain/run.sh
+++ b/test/tests/certs_single_domain/run.sh
@@ -96,7 +96,6 @@ for hosts in "${letsencrypt_hosts[@]}"; do
   done
 
   docker stop "$container" &> /dev/null
-  docker exec "$le_container_name" /app/cleanup_test_artifacts --default-cert
   i=$(( i + 1 ))
 
 done
diff --git a/test/tests/default_cert/expected-std-out.txt b/test/tests/default_cert/expected-std-out.txt
deleted file mode 100644
index 8b137891..00000000
--- a/test/tests/default_cert/expected-std-out.txt
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/test/tests/default_cert/run.sh b/test/tests/default_cert/run.sh
deleted file mode 100755
index 54b2a70d..00000000
--- a/test/tests/default_cert/run.sh
+++ /dev/null
@@ -1,105 +0,0 @@
-#!/bin/bash
-
-## Test for default certificate creation.
-
-if [[ -z $GITHUB_ACTIONS ]]; then
-  le_container_name="$(basename "${0%/*}")_$(date "+%Y-%m-%d_%H.%M.%S")"
-else
-  le_container_name="$(basename "${0%/*}")"
-fi
-run_le_container "${1:?}" "$le_container_name"
-
-# Create the $domains array from comma separated domains in TEST_DOMAINS.
-IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
-
-# Cleanup function with EXIT trap
-function cleanup {
-  # Cleanup the files created by this run of the test to avoid foiling following test(s).
-  docker exec "$le_container_name" /app/cleanup_test_artifacts --default-cert
-  docker stop "$le_container_name" > /dev/null
-}
-trap cleanup EXIT
-
-function check_default_cert_existence {
-  docker exec "$le_container_name" [[ -f "/etc/nginx/certs/default.crt" ]]
-}
-
-function default_cert_fingerprint {
-  if check_default_cert_existence; then
-    docker exec "$le_container_name" openssl x509 -in "/etc/nginx/certs/default.crt" -fingerprint -noout
-  fi
-}
-
-function default_cert_subject {
-  if check_default_cert_existence; then
-    docker exec "$le_container_name" openssl x509 -in "/etc/nginx/certs/default.crt" -subject -noout
-  fi
-}
-
-user_cn="user-provided"
-
-timeout="$(date +%s)"
-timeout="$((timeout + 120))"
-until docker exec "$le_container_name" [[ -f /etc/nginx/certs/default.crt ]]; do
-  if [[ "$(date +%s)" -gt "$timeout" ]]; then
-    echo "Default cert wasn't created under one minute at container first launch."
-    break
-  fi
-  sleep 0.1
-done
-
-# Connection test to unconfigured domains
-for domain in "${domains[@]}"; do
-  wait_for_conn --domain "$domain" --default-cert
-done
-
-# Test if the default certificate get re-created when
-# the certificate or private key file are deleted
-for file in 'default.key' 'default.crt'; do
-  old_default_cert_fingerprint="$(default_cert_fingerprint)"
-  docker exec "$le_container_name" /app/cleanup_test_artifacts --default-cert
-  docker restart "$le_container_name" > /dev/null
-  timeout="$(date +%s)"
-  timeout="$((timeout + 120))"
-  while [[ "$(default_cert_fingerprint)" == "$old_default_cert_fingerprint" ]]; do
-    if [[ "$(date +%s)" -gt "$timeout" ]]; then
-      echo "Default cert wasn't re-created under one minute after $file deletion."
-      break
-    fi
-    sleep 0.1
-  done
-done
-
-# Test if the default certificate get re-created when
-# the certificate expire in less than three months
-docker exec "$le_container_name" bash -c 'rm -rf /etc/nginx/certs/default.*'
-docker exec "$le_container_name" openssl req -x509 \
-  -newkey rsa:4096 -sha256 -nodes -days 60 \
-  -subj "/CN=letsencrypt-nginx-proxy-companion" \
-  -keyout /etc/nginx/certs/default.key \
-  -out /etc/nginx/certs/default.crt &> /dev/null
-old_default_cert_fingerprint="$(default_cert_fingerprint)"
-docker restart "$le_container_name" > /dev/null && sleep 10
-timeout="$(date +%s)"
-timeout="$((timeout + 110))"
-while [[ "$(default_cert_fingerprint)" == "$old_default_cert_fingerprint" ]]; do
-  if [[ "$(date +%s)" -gt "$timeout" ]]; then
-    echo "Default cert wasn't re-created under one minute when the certificate expire in less than three months."
-    break
-  fi
-  sleep 0.1
-done
-
-# Test that a user provided default certificate isn't overwrited
-docker exec "$le_container_name" bash -c 'rm -rf /etc/nginx/certs/default.*'
-docker exec "$le_container_name" openssl req -x509 \
-  -newkey rsa:4096 -sha256 -nodes -days 60 \
-  -subj "/CN=$user_cn" \
-  -keyout /etc/nginx/certs/default.key \
-  -out /etc/nginx/certs/default.crt &> /dev/null
-docker restart "$le_container_name" > /dev/null
-
-# Connection test to unconfigured domains
-for domain in "${domains[@]}"; do
-  wait_for_conn --domain "$domain" --subject-match "$user_cn"
-done
diff --git a/test/tests/permissions_custom/run.sh b/test/tests/permissions_custom/run.sh
index 01a5f56f..5f69f081 100755
--- a/test/tests/permissions_custom/run.sh
+++ b/test/tests/permissions_custom/run.sh
@@ -66,9 +66,8 @@ done
 
 # Array of private file paths to test
 private_files=( \
-  [0]="/etc/nginx/certs/default.key" \
-  [1]="/etc/nginx/certs/${domains[0]}/key.pem" \
-  [2]="/etc/acme.sh/default/${domains[0]}/${domains[0]}.key" \
+  [0]="/etc/nginx/certs/${domains[0]}/key.pem" \
+  [1]="/etc/acme.sh/default/${domains[0]}/${domains[0]}.key" \
   )
 
 # Test private file paths
@@ -85,8 +84,7 @@ public_files=( \
   [1]="/etc/nginx/certs/${domains[0]}/cert.pem" \
   [2]="/etc/nginx/certs/${domains[0]}/chain.pem" \
   [3]="/etc/nginx/certs/${domains[0]}/fullchain.pem" \
-  [4]="/etc/nginx/certs/default.crt" \
-  [5]="/etc/nginx/certs/dhparam.pem" \
+  [4]="/etc/nginx/certs/dhparam.pem" \
   )
 
 # Test public file paths
diff --git a/test/tests/permissions_default/run.sh b/test/tests/permissions_default/run.sh
index 21bc1381..7e5f880a 100755
--- a/test/tests/permissions_default/run.sh
+++ b/test/tests/permissions_default/run.sh
@@ -60,9 +60,8 @@ done
 
 # Array of private file paths to test
 private_files=( \
-  [0]="/etc/nginx/certs/default.key" \
-  [1]="/etc/nginx/certs/${domains[0]}/key.pem" \
-  [2]="/etc/acme.sh/default/${domains[0]}/${domains[0]}.key" \
+  [0]="/etc/nginx/certs/${domains[0]}/key.pem" \
+  [1]="/etc/acme.sh/default/${domains[0]}/${domains[0]}.key" \
   )
 
 # Test private file paths
@@ -79,8 +78,7 @@ public_files=( \
   [1]="/etc/nginx/certs/${domains[0]}/cert.pem" \
   [2]="/etc/nginx/certs/${domains[0]}/chain.pem" \
   [3]="/etc/nginx/certs/${domains[0]}/fullchain.pem" \
-  [4]="/etc/nginx/certs/default.crt" \
-  [5]="/etc/nginx/certs/dhparam.pem" \
+  [4]="/etc/nginx/certs/dhparam.pem" \
   )
 
 # Test public file paths
diff --git a/test/tests/symlinks/run.sh b/test/tests/symlinks/run.sh
index 4ad20c7d..2889ba10 100755
--- a/test/tests/symlinks/run.sh
+++ b/test/tests/symlinks/run.sh
@@ -47,8 +47,7 @@ docker exec "$le_container_name" cp /etc/nginx/certs/le1.wtf/key.pem /etc/nginx/
 docker exec "$le_container_name" bash -c 'cd /etc/nginx/certs; ln -s ./le4.wtf/fullchain.pem ./le4.wtf.crt'
 docker exec "$le_container_name" bash -c 'cd /etc/nginx/certs; ln -s ./le4.wtf/key.pem ./le4.wtf.key'
 
-# symlink default certificate to le1.wtf certificate
-docker exec "$le_container_name" rm -f /etc/nginx/certs/default.crt /etc/nginx/certs/default.key
+# Symlink default certificate to le1.wtf certificate
 docker exec "$le_container_name" bash -c 'cd /etc/nginx/certs; ln -s ./le1.wtf/fullchain.pem ./default.crt'
 docker exec "$le_container_name" bash -c 'cd /etc/nginx/certs; ln -s ./le1.wtf/key.pem ./default.key'
 
diff --git a/test/tests/test-functions.sh b/test/tests/test-functions.sh
index 6d067bca..40adde39 100755
--- a/test/tests/test-functions.sh
+++ b/test/tests/test-functions.sh
@@ -263,7 +263,6 @@ export -f check_cert_subj
 
 # Wait for a successful https connection to domain passed with -d/--domain then wait
 #   - until the served certificate isn't the default one (default behavior)
-#   - until the served certificate is the default one (--default-cert)
 #   - until the served certificate subject match a string (--subject-match)
 function wait_for_conn {
   local action
@@ -280,11 +279,6 @@ function wait_for_conn {
       shift
       ;;
 
-      --default-cert)
-      action='--match'
-      shift
-      ;;
-
       --subject-match)
       action='--match'
       string="$2"