Merge pull request #8258 from dokku/6423-traefik-challenge-support

Add support for dns-01 challenge mode when using traefik
This commit is contained in:
Jose Diaz-Gonzalez
2026-01-08 12:47:16 -05:00
committed by GitHub
8 changed files with 416 additions and 11 deletions

View File

@@ -199,6 +199,41 @@ dokku traefik:set --global letsencrypt-server https://acme-staging-v02.api.letse
After enabling, the Traefik container will need to be restarted and apps will need to be rebuilt to retrieve certificates from the new server. After enabling, the Traefik container will need to be restarted and apps will need to be rebuilt to retrieve certificates from the new server.
#### Switching to DNS-01 challenge mode
By default, Traefik uses TLS-ALPN-01 challenge for obtaining certificates. To switch to DNS-01 challenge mode (useful for wildcard certificates or when port 443 is not accessible), you need to:
1. Set the challenge mode to `dns`:
```shell
dokku traefik:set --global challenge-mode dns
```
2. Set your DNS provider:
```shell
dokku traefik:set --global dns-provider cloudflare
```
3. Configure the required environment variables for your DNS provider. Each DNS provider requires specific environment variables. The variable names should be prefixed with `dns-provider-`:
```shell
dokku traefik:set --global dns-provider-cf_api_email user@example.com
dokku traefik:set --global dns-provider-cf_api_key your-api-key
```
The `dns-provider-` prefix will be stripped and the variable name will be uppercased when passed to the Traefik container. For example, `dns-provider-cf_api_email` becomes `CF_API_EMAIL`.
After configuring, the Traefik container will need to be restarted and apps will need to be rebuilt.
Refer to the [Traefik DNS Challenge documentation](https://doc.traefik.io/traefik/https/acme/#dnschallenge) for the list of supported DNS providers and their required environment variables.
To switch back to TLS challenge mode:
```shell
dokku traefik:set --global challenge-mode tls
```
### API Access ### API Access
Traefik exposes an API and Dashboard, which Dokku disables by default for security reasons. It can be exposed and customized as described below. Traefik exposes an API and Dashboard, which Dokku disables by default for security reasons. It can be exposed and customized as described below.
@@ -264,7 +299,9 @@ dokku traefik:report
Traefik api vhost: traefik.dokku.me Traefik api vhost: traefik.dokku.me
Traefik basic auth password: password Traefik basic auth password: password
Traefik basic auth username: user Traefik basic auth username: user
Traefik challenge mode: tls
Traefik dashboard enabled: false Traefik dashboard enabled: false
Traefik dns provider:
Traefik image: traefik:v2.8 Traefik image: traefik:v2.8
Traefik letsencrypt email: Traefik letsencrypt email:
Traefik letsencrypt server: Traefik letsencrypt server:
@@ -274,7 +311,9 @@ dokku traefik:report
Traefik api vhost: traefik.dokku.me Traefik api vhost: traefik.dokku.me
Traefik basic auth password: password Traefik basic auth password: password
Traefik basic auth username: user Traefik basic auth username: user
Traefik challenge mode: tls
Traefik dashboard enabled: false Traefik dashboard enabled: false
Traefik dns provider:
Traefik image: traefik:v2.8 Traefik image: traefik:v2.8
Traefik letsencrypt email: Traefik letsencrypt email:
Traefik letsencrypt server: Traefik letsencrypt server:
@@ -284,7 +323,9 @@ dokku traefik:report
Traefik api vhost: traefik.dokku.me Traefik api vhost: traefik.dokku.me
Traefik basic auth password: password Traefik basic auth password: password
Traefik basic auth username: user Traefik basic auth username: user
Traefik challenge mode: tls
Traefik dashboard enabled: false Traefik dashboard enabled: false
Traefik dns provider:
Traefik image: traefik:v2.8 Traefik image: traefik:v2.8
Traefik letsencrypt email: Traefik letsencrypt email:
Traefik letsencrypt server: Traefik letsencrypt server:
@@ -303,7 +344,9 @@ dokku traefik:report node-js-app
Traefik api vhost: traefik.dokku.me Traefik api vhost: traefik.dokku.me
Traefik basic auth password: password Traefik basic auth password: password
Traefik basic auth username: user Traefik basic auth username: user
Traefik challenge mode: tls
Traefik dashboard enabled: false Traefik dashboard enabled: false
Traefik dns provider:
Traefik image: traefik:v2.8 Traefik image: traefik:v2.8
Traefik letsencrypt email: Traefik letsencrypt email:
Traefik letsencrypt server: Traefik letsencrypt server:

View File

@@ -39,6 +39,12 @@ fn-plugin-property-get-all() {
"$PLUGIN_CORE_AVAILABLE_PATH/common/prop" "get-all" "$PLUGIN" "$APP" "$PLUGIN_CORE_AVAILABLE_PATH/common/prop" "get-all" "$PLUGIN" "$APP"
} }
fn-plugin-property-get-all-by-prefix() {
declare desc="returns a map of all properties for a given app with a specified prefix"
declare PLUGIN="$1" APP="$2" PREFIX="$3"
"$PLUGIN_CORE_AVAILABLE_PATH/common/prop" "get-all-by-prefix" "$PLUGIN" "$APP" "$PREFIX"
}
fn-plugin-property-get-default() { fn-plugin-property-get-default() {
declare desc="returns the value for a given property with a specified default value" declare desc="returns the value for a given property with a specified default value"
declare PLUGIN="$1" APP="$2" KEY="$3" DEFAULT="$4" declare PLUGIN="$1" APP="$2" KEY="$3" DEFAULT="$4"

View File

@@ -78,6 +78,19 @@ func main() {
os.Exit(1) os.Exit(1)
} }
for key, value := range values {
fmt.Println(fmt.Sprintf("%s %s", key, strings.TrimSuffix(value, "\n")))
}
case "get-all-by-prefix":
appName := flag.Arg(2)
prefix := flag.Arg(3)
values, err := common.PropertyGetAllByPrefix(pluginName, appName, prefix)
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}
for key, value := range values { for key, value := range values {
fmt.Println(fmt.Sprintf("%s %s", key, strings.TrimSuffix(value, "\n"))) fmt.Println(fmt.Sprintf("%s %s", key, strings.TrimSuffix(value, "\n")))
} }

View File

@@ -34,8 +34,16 @@ cmd-traefik-report() {
declare cmd="traefik:report" declare cmd="traefik:report"
[[ "$1" == "$cmd" ]] && shift 1 [[ "$1" == "$cmd" ]] && shift 1
declare APP="$1" INFO_FLAG="$2" declare APP="$1" INFO_FLAG="$2"
local FORMAT="stdout"
if [[ -n "$APP" ]] && [[ "$APP" == --* ]]; then if [[ -n "$APP" ]] && [[ "$APP" == "--format" ]]; then
FORMAT="$INFO_FLAG"
APP=""
INFO_FLAG=""
elif [[ -n "$INFO_FLAG" ]] && [[ "$INFO_FLAG" == "--format" ]]; then
FORMAT="$3"
INFO_FLAG=""
elif [[ -n "$APP" ]] && [[ "$APP" == --* ]]; then
INFO_FLAG="$APP" INFO_FLAG="$APP"
APP="" APP=""
fi fi
@@ -46,15 +54,15 @@ cmd-traefik-report() {
if [[ -z "$APP" ]]; then if [[ -z "$APP" ]]; then
for app in $(dokku_apps); do for app in $(dokku_apps); do
cmd-traefik-report-single "$app" "$INFO_FLAG" | tee || true cmd-traefik-report-single "$app" "$INFO_FLAG" "$FORMAT" | tee || true
done done
else else
cmd-traefik-report-single "$APP" "$INFO_FLAG" cmd-traefik-report-single "$APP" "$INFO_FLAG" "$FORMAT"
fi fi
} }
cmd-traefik-report-single() { cmd-traefik-report-single() {
declare APP="$1" INFO_FLAG="$2" declare APP="$1" INFO_FLAG="$2" FORMAT="${3:-stdout}"
if [[ "$INFO_FLAG" == "true" ]]; then if [[ "$INFO_FLAG" == "true" ]]; then
INFO_FLAG="" INFO_FLAG=""
fi fi
@@ -64,7 +72,9 @@ cmd-traefik-report-single() {
"--traefik-api-vhost: $(fn-traefik-api-vhost)" "--traefik-api-vhost: $(fn-traefik-api-vhost)"
"--traefik-basic-auth-password: $(fn-traefik-basic-auth-password)" "--traefik-basic-auth-password: $(fn-traefik-basic-auth-password)"
"--traefik-basic-auth-username: $(fn-traefik-basic-auth-username)" "--traefik-basic-auth-username: $(fn-traefik-basic-auth-username)"
"--traefik-challenge-mode: $(fn-traefik-challenge-mode)"
"--traefik-dashboard-enabled: $(fn-traefik-dashboard-enabled)" "--traefik-dashboard-enabled: $(fn-traefik-dashboard-enabled)"
"--traefik-dns-provider: $(fn-traefik-dns-provider)"
"--traefik-image: $(fn-traefik-image)" "--traefik-image: $(fn-traefik-image)"
"--traefik-letsencrypt-email: $(fn-traefik-letsencrypt-email)" "--traefik-letsencrypt-email: $(fn-traefik-letsencrypt-email)"
"--traefik-letsencrypt-server: $(fn-traefik-letsencrypt-server)" "--traefik-letsencrypt-server: $(fn-traefik-letsencrypt-server)"
@@ -73,7 +83,34 @@ cmd-traefik-report-single() {
"--traefik-https-entry-point: $(fn-traefik-https-entry-point)" "--traefik-https-entry-point: $(fn-traefik-https-entry-point)"
) )
if [[ -z "$INFO_FLAG" ]]; then # Get dns-provider-* env vars and add them (masked for stdout, unmasked for json)
local dns_provider_env_vars
dns_provider_env_vars="$(fn-traefik-dns-provider-env-vars)"
if [[ -n "$dns_provider_env_vars" ]]; then
while IFS= read -r line; do
local key value
key="$(echo "$line" | cut -d' ' -f1)"
value="$(echo "$line" | cut -d' ' -f2-)"
if [[ -n "$key" ]]; then
if [[ "$FORMAT" == "json" ]]; then
flag_map+=("--traefik-${key}: ${value}")
else
flag_map+=("--traefik-${key}: *******")
fi
fi
done <<<"$dns_provider_env_vars"
fi
if [[ "$FORMAT" == "json" ]]; then
local json_output="{}"
for flag in "${flag_map[@]}"; do
local key value
key="$(echo "$flag" | cut -d':' -f1 | sed 's/^--traefik-//')"
value="$(echo "$flag" | cut -d':' -f2- | sed 's/^ //')"
json_output="$(echo "$json_output" | jq -c --arg key "$key" --arg value "$value" '. + {($key): $value}')"
done
echo "$json_output"
elif [[ -z "$INFO_FLAG" ]]; then
dokku_log_info2_quiet "${APP} traefik information" dokku_log_info2_quiet "${APP} traefik information"
for flag in "${flag_map[@]}"; do for flag in "${flag_map[@]}"; do
key="$(echo "${flag#--}" | cut -f1 -d' ' | tr - ' ')" key="$(echo "${flag#--}" | cut -f1 -d' ' | tr - ' ')"

View File

@@ -42,11 +42,21 @@ fn-traefik-template-compose-file() {
basic_auth="$(htpasswd -nb "$basic_auth_username" "$basic_auth_password" | sed -e s/\\$/\\$\\$/g)" basic_auth="$(htpasswd -nb "$basic_auth_username" "$basic_auth_password" | sed -e s/\\$/\\$\\$/g)"
fi fi
local dns_provider_env_vars=""
local dns_provider
dns_provider="$(fn-traefik-dns-provider)"
if [[ -n "$dns_provider" ]]; then
dns_provider_env_vars="$(fn-traefik-dns-provider-env-vars)"
fi
local SIGIL_PARAMS=(TRAEFIK_API_ENABLED="$(fn-traefik-api-enabled)" local SIGIL_PARAMS=(TRAEFIK_API_ENABLED="$(fn-traefik-api-enabled)"
TRAEFIK_API_VHOST="$(fn-traefik-api-vhost)" TRAEFIK_API_VHOST="$(fn-traefik-api-vhost)"
TRAEFIK_BASIC_AUTH="$basic_auth" TRAEFIK_BASIC_AUTH="$basic_auth"
TRAEFIK_CHALLENGE_MODE="$(fn-traefik-challenge-mode)"
TRAEFIK_DASHBOARD_ENABLED="$(fn-traefik-dashboard-enabled)" TRAEFIK_DASHBOARD_ENABLED="$(fn-traefik-dashboard-enabled)"
TRAEFIK_DATA_DIR="${DOKKU_LIB_ROOT}/data/traefik" TRAEFIK_DATA_DIR="${DOKKU_LIB_ROOT}/data/traefik"
TRAEFIK_DNS_PROVIDER="$dns_provider"
TRAEFIK_DNS_PROVIDER_ENV_VARS="$dns_provider_env_vars"
TRAEFIK_IMAGE="$(fn-traefik-image)" TRAEFIK_IMAGE="$(fn-traefik-image)"
TRAEFIK_LETSENCRYPT_EMAIL="$(fn-traefik-letsencrypt-email)" TRAEFIK_LETSENCRYPT_EMAIL="$(fn-traefik-letsencrypt-email)"
TRAEFIK_LETSENCRYPT_SERVER="$(fn-traefik-letsencrypt-server)" TRAEFIK_LETSENCRYPT_SERVER="$(fn-traefik-letsencrypt-server)"
@@ -100,3 +110,16 @@ fn-traefik-http-entry-point() {
fn-traefik-https-entry-point() { fn-traefik-https-entry-point() {
fn-plugin-property-get-default "traefik" "--global" "https-entry-point" "https" fn-plugin-property-get-default "traefik" "--global" "https-entry-point" "https"
} }
fn-traefik-challenge-mode() {
fn-plugin-property-get-default "traefik" "--global" "challenge-mode" "tls"
}
fn-traefik-dns-provider() {
fn-plugin-property-get-default "traefik" "--global" "dns-provider" ""
}
fn-traefik-dns-provider-env-vars() {
declare desc="returns all dns-provider-* environment variables"
fn-plugin-property-get-all-by-prefix "traefik" "--global" "dns-provider-"
}

View File

@@ -9,22 +9,38 @@ cmd-traefik-set() {
declare cmd="traefik:set" declare cmd="traefik:set"
[[ "$1" == "$cmd" ]] && shift 1 [[ "$1" == "$cmd" ]] && shift 1
declare APP="$1" KEY="$2" VALUE="$3" declare APP="$1" KEY="$2" VALUE="$3"
local VALID_KEYS=("api-enabled" "api-vhost" "dashboard-enabled" "basic-auth-username" "basic-auth-password" "image" "letsencrypt-email" "letsencrypt-server" "log-level" "http-entry-point" "https-entry-point") local VALID_KEYS=("api-enabled" "api-vhost" "challenge-mode" "dashboard-enabled" "basic-auth-username" "basic-auth-password" "dns-provider" "image" "letsencrypt-email" "letsencrypt-server" "log-level" "http-entry-point" "https-entry-point")
local GLOBAL_KEYS=("api-enabled" "api-vhost" "dashboard-enabled" "basic-auth-username" "basic-auth-password" "image" "letsencrypt-email" "letsencrypt-server" "log-level" "http-entry-point" "https-entry-point") local GLOBAL_KEYS=("api-enabled" "api-vhost" "challenge-mode" "dashboard-enabled" "basic-auth-username" "basic-auth-password" "dns-provider" "image" "letsencrypt-email" "letsencrypt-server" "log-level" "http-entry-point" "https-entry-point")
local GLOBAL_ONLY_KEYS=("challenge-mode" "dns-provider")
[[ -z "$KEY" ]] && dokku_log_fail "No key specified" [[ -z "$KEY" ]] && dokku_log_fail "No key specified"
if ! fn-in-array "$KEY" "${VALID_KEYS[@]}"; then # Allow dns-provider-* keys for setting DNS provider environment variables
dokku_log_fail "Invalid key specified, valid keys include: api-enabled api-vhost dashboard-enabled basic-auth-username basic-auth-password image letsencrypt-email letsencrypt-server log-level" local is_dns_provider_env_var=false
if [[ "$KEY" == dns-provider-* ]]; then
is_dns_provider_env_var=true
fi fi
if ! fn-in-array "$KEY" "${GLOBAL_KEYS[@]}"; then if ! fn-in-array "$KEY" "${VALID_KEYS[@]}" && [[ "$is_dns_provider_env_var" != "true" ]]; then
dokku_log_fail "Invalid key specified, valid keys include: api-enabled api-vhost challenge-mode dashboard-enabled basic-auth-username basic-auth-password dns-provider dns-provider-<ENV_VAR> image letsencrypt-email letsencrypt-server log-level http-entry-point https-entry-point"
fi
if ! fn-in-array "$KEY" "${GLOBAL_KEYS[@]}" && [[ "$is_dns_provider_env_var" != "true" ]]; then
if [[ "$APP" == "--global" ]]; then if [[ "$APP" == "--global" ]]; then
dokku_log_fail "The key '$KEY' cannot be set globally" dokku_log_fail "The key '$KEY' cannot be set globally"
fi fi
verify_app_name "$APP" verify_app_name "$APP"
fi fi
# dns-provider-* keys and GLOBAL_ONLY_KEYS can only be set globally
if [[ "$is_dns_provider_env_var" == "true" ]] && [[ "$APP" != "--global" ]]; then
dokku_log_fail "The key '$KEY' can only be set globally"
fi
if fn-in-array "$KEY" "${GLOBAL_ONLY_KEYS[@]}" && [[ "$APP" != "--global" ]]; then
dokku_log_fail "The key '$KEY' can only be set globally"
fi
if [[ -n "$VALUE" ]]; then if [[ -n "$VALUE" ]]; then
dokku_log_info2_quiet "Setting ${KEY} to ${VALUE}" dokku_log_info2_quiet "Setting ${KEY} to ${VALUE}"
fn-plugin-property-write "traefik" "$APP" "$KEY" "$VALUE" fn-plugin-property-write "traefik" "$APP" "$KEY" "$VALUE"

View File

@@ -21,8 +21,25 @@ services:
- --certificatesresolvers.leresolver.acme.caserver={{ $.TRAEFIK_LETSENCRYPT_SERVER }} - --certificatesresolvers.leresolver.acme.caserver={{ $.TRAEFIK_LETSENCRYPT_SERVER }}
- --certificatesresolvers.leresolver.acme.email={{ $.TRAEFIK_LETSENCRYPT_EMAIL }} - --certificatesresolvers.leresolver.acme.email={{ $.TRAEFIK_LETSENCRYPT_EMAIL }}
- --certificatesresolvers.leresolver.acme.storage=/data/acme.json - --certificatesresolvers.leresolver.acme.storage=/data/acme.json
{{ if and (eq $.TRAEFIK_CHALLENGE_MODE "dns") ($.TRAEFIK_DNS_PROVIDER) }}
- --certificatesresolvers.leresolver.acme.dnschallenge=true
- --certificatesresolvers.leresolver.acme.dnschallenge.provider={{ $.TRAEFIK_DNS_PROVIDER }}
{{ else }}
- --certificatesresolvers.leresolver.acme.tlschallenge=true - --certificatesresolvers.leresolver.acme.tlschallenge=true
{{ end }} {{ end }}
{{ end }}
{{ if $.TRAEFIK_DNS_PROVIDER_ENV_VARS }}
environment:
{{ range $env_var := $.TRAEFIK_DNS_PROVIDER_ENV_VARS | split "\n" }}
{{ $env_parts := $env_var | split " " }}
{{ $env_key := index $env_parts 0 }}
{{ $env_value := index $env_parts 1 }}
{{ if $env_key }}
- {{ $env_key | replace "dns-provider-" "" | upper }}={{ $env_value }}
{{ end }}
{{ end }}
{{ end }}
{{ if or (eq $.TRAEFIK_API_ENABLED "true") ($.TRAEFIK_BASIC_AUTH) ($.TRAEFIK_LETSENCRYPT_EMAIL) }} {{ if or (eq $.TRAEFIK_API_ENABLED "true") ($.TRAEFIK_BASIC_AUTH) ($.TRAEFIK_LETSENCRYPT_EMAIL) }}
labels: labels:
@@ -37,7 +54,7 @@ services:
- "traefik.http.routers.api.service=api@internal" - "traefik.http.routers.api.service=api@internal"
- "traefik.http.routers.api.entrypoints={{ if $.TRAEFIK_LETSENCRYPT_EMAIL }}https{{ else }}http{{ end }}" - "traefik.http.routers.api.entrypoints={{ if $.TRAEFIK_LETSENCRYPT_EMAIL }}https{{ else }}http{{ end }}"
{{ end }} {{ end }}
{{ if $.TRAEFIK_BASIC_AUTH }} {{ if $.TRAEFIK_BASIC_AUTH }}
- "traefik.http.routers.api.middlewares=auth" - "traefik.http.routers.api.middlewares=auth"
- "traefik.http.middlewares.auth.basicauth.users={{ $.TRAEFIK_BASIC_AUTH }}" - "traefik.http.middlewares.auth.basicauth.users={{ $.TRAEFIK_BASIC_AUTH }}"

View File

@@ -8,6 +8,8 @@ setup() {
dokku traefik:set --global letsencrypt-server https://acme-staging-v02.api.letsencrypt.org/directory dokku traefik:set --global letsencrypt-server https://acme-staging-v02.api.letsencrypt.org/directory
dokku traefik:set --global letsencrypt-email dokku traefik:set --global letsencrypt-email
dokku traefik:set --global api-enabled dokku traefik:set --global api-enabled
dokku traefik:set --global challenge-mode
dokku traefik:set --global dns-provider
dokku traefik:start dokku traefik:start
create_app create_app
} }
@@ -368,3 +370,251 @@ teardown() {
assert_success assert_success
assert_output_not_exists assert_output_not_exists
} }
@test "(traefik) [dns-01] challenge-mode property" {
run /bin/bash -c "dokku traefik:report $TEST_APP --traefik-challenge-mode"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "tls"
run /bin/bash -c "dokku traefik:set --global challenge-mode dns"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku traefik:report $TEST_APP --traefik-challenge-mode"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "dns"
run /bin/bash -c "dokku traefik:set --global challenge-mode"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku traefik:report $TEST_APP --traefik-challenge-mode"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "tls"
}
@test "(traefik) [dns-01] dns-provider property" {
run /bin/bash -c "dokku traefik:report $TEST_APP --traefik-dns-provider"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_not_exists
run /bin/bash -c "dokku traefik:set --global dns-provider cloudflare"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku traefik:report $TEST_APP --traefik-dns-provider"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "cloudflare"
run /bin/bash -c "dokku traefik:set --global dns-provider"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku traefik:report $TEST_APP --traefik-dns-provider"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_not_exists
}
@test "(traefik) [dns-01] dns-provider-* environment variables" {
run /bin/bash -c "dokku traefik:set --global dns-provider-cf_api_email test@example.com"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku traefik:set --global dns-provider-cf_api_key secret-key"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku traefik:report $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "dns provider cf_api_email"
assert_output_contains "dns provider cf_api_key"
assert_output_contains "*******"
assert_output_not_contains "test@example.com"
assert_output_not_contains "secret-key"
run /bin/bash -c "dokku traefik:report $TEST_APP --traefik-dns-provider-cf_api_email"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "*******"
run /bin/bash -c "dokku traefik:set --global dns-provider-cf_api_email"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku traefik:set --global dns-provider-cf_api_key"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku traefik:report $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_not_contains "dns provider cf_api_email"
assert_output_not_contains "dns provider cf_api_key"
}
@test "(traefik) [dns-01] dns-provider-* can only be set globally" {
run /bin/bash -c "dokku traefik:set $TEST_APP dns-provider-cf_api_email test@example.com"
echo "output: $output"
echo "status: $status"
assert_failure
assert_output_contains "can only be set globally"
}
@test "(traefik) [dns-01] challenge-mode can only be set globally" {
run /bin/bash -c "dokku traefik:set $TEST_APP challenge-mode dns"
echo "output: $output"
echo "status: $status"
assert_failure
assert_output_contains "can only be set globally"
}
@test "(traefik) [dns-01] dns-provider can only be set globally" {
run /bin/bash -c "dokku traefik:set $TEST_APP dns-provider cloudflare"
echo "output: $output"
echo "status: $status"
assert_failure
assert_output_contains "can only be set globally"
}
@test "(traefik) [dns-01] report json format shows unmasked dns-provider-* values" {
run /bin/bash -c "dokku traefik:set --global dns-provider-cf_api_email test@example.com"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku traefik:set --global dns-provider-cf_api_key secret-key"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku traefik:report $TEST_APP --format json | jq -r '.\"dns-provider-cf_api_email\"'"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "test@example.com"
run /bin/bash -c "dokku traefik:report $TEST_APP --format json | jq -r '.\"dns-provider-cf_api_key\"'"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "secret-key"
# Verify stdout format still shows masked values
run /bin/bash -c "dokku traefik:report $TEST_APP"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "*******"
assert_output_not_contains "test@example.com"
assert_output_not_contains "secret-key"
# Cleanup
run /bin/bash -c "dokku traefik:set --global dns-provider-cf_api_email"
run /bin/bash -c "dokku traefik:set --global dns-provider-cf_api_key"
}
@test "(traefik) [dns-01] show-config with tls challenge" {
run /bin/bash -c "dokku traefik:set --global letsencrypt-email test@example.com"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku traefik:show-config | yq -r '.services.traefik.command[] | select(. == \"--certificatesresolvers.leresolver.acme.tlschallenge=true\")'"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "--certificatesresolvers.leresolver.acme.tlschallenge=true"
run /bin/bash -c "dokku traefik:show-config | yq -r '.services.traefik.command[] | select(contains(\"dnschallenge\"))'"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_not_exists
}
@test "(traefik) [dns-01] show-config with dns challenge" {
run /bin/bash -c "dokku traefik:set --global letsencrypt-email test@example.com"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku traefik:set --global challenge-mode dns"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku traefik:set --global dns-provider cloudflare"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku traefik:set --global dns-provider-cf_api_email test@example.com"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku traefik:set --global dns-provider-cf_api_key secret-key"
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku traefik:show-config | yq -r '.services.traefik.command[] | select(. == \"--certificatesresolvers.leresolver.acme.dnschallenge=true\")'"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "--certificatesresolvers.leresolver.acme.dnschallenge=true"
run /bin/bash -c "dokku traefik:show-config | yq -r '.services.traefik.command[] | select(. == \"--certificatesresolvers.leresolver.acme.dnschallenge.provider=cloudflare\")'"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "--certificatesresolvers.leresolver.acme.dnschallenge.provider=cloudflare"
run /bin/bash -c "dokku traefik:show-config | yq -r '.services.traefik.environment[] | select(. == \"CF_API_EMAIL=test@example.com\")'"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "CF_API_EMAIL=test@example.com"
run /bin/bash -c "dokku traefik:show-config | yq -r '.services.traefik.environment[] | select(. == \"CF_API_KEY=secret-key\")'"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "CF_API_KEY=secret-key"
run /bin/bash -c "dokku traefik:show-config | yq -r '.services.traefik.command[] | select(contains(\"tlschallenge\"))'"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_not_exists
# Cleanup
run /bin/bash -c "dokku traefik:set --global challenge-mode"
run /bin/bash -c "dokku traefik:set --global dns-provider"
run /bin/bash -c "dokku traefik:set --global dns-provider-cf_api_email"
run /bin/bash -c "dokku traefik:set --global dns-provider-cf_api_key"
}