feat: support manually managed cert issuers on k3s
The `cert-issuer-name` and `cert-issuer-kind` properties point an app's generated `Certificate` at a cert-manager issuer created outside of Dokku, allowing certificates to be issued through solvers the built-in letsencrypt integration cannot use, such as `dns01` for wildcard certificates. Setting an issuer enables https on its own, as a manually managed issuer has no email for Dokku to configure. An imported certificate still takes precedence, and `letsencrypt-server false` remains the single off switch. Dokku warns before a build starts when the referenced issuer is absent from the cluster, without blocking the deploy. Wildcard domains no longer collide with their apex domain when generating ingress names, and `letsencrypt-server` values are now validated when set rather than at deploy time.
This commit is contained in:
@@ -12,6 +12,7 @@ setup() {
|
||||
}
|
||||
|
||||
teardown() {
|
||||
dokku scheduler-k3s:set --global cert-issuer-name >/dev/null 2>/dev/null || true
|
||||
global_teardown
|
||||
dokku nginx:start
|
||||
uninstall_k3s || true
|
||||
@@ -72,3 +73,135 @@ teardown() {
|
||||
assert_success
|
||||
assert_output "app@dokku.me"
|
||||
}
|
||||
|
||||
@test "(scheduler-k3s:certs) a manually managed cert issuer issues the app certificate" {
|
||||
if [[ -z "$DOCKERHUB_USERNAME" ]] || [[ -z "$DOCKERHUB_TOKEN" ]]; then
|
||||
skip "skipping due to missing docker.io credentials DOCKERHUB_USERNAME:DOCKERHUB_TOKEN"
|
||||
fi
|
||||
|
||||
INGRESS_CLASS=nginx install_k3s
|
||||
|
||||
run /bin/bash -c "dokku apps:create $TEST_APP"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku domains:set $TEST_APP $TEST_APP.dokku.me"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
# no letsencrypt email is ever set, so tls is enabled purely by cert-issuer-name
|
||||
run /bin/bash -c "dokku scheduler-k3s:set $TEST_APP cert-issuer-name dokku-test-selfsigned"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku scheduler-k3s:preview $TEST_APP"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output_contains "ClusterIssuer dokku-test-selfsigned not found"
|
||||
|
||||
create_selfsigned_cluster_issuer
|
||||
|
||||
run deploy_app python "dokku@$DOKKU_DOMAIN:$TEST_APP"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "kubectl get certificate ${TEST_APP}-web -n default -o jsonpath='{.spec.issuerRef.kind}'"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output "ClusterIssuer"
|
||||
|
||||
run /bin/bash -c "kubectl get certificate ${TEST_APP}-web -n default -o jsonpath='{.spec.issuerRef.name}'"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output "dokku-test-selfsigned"
|
||||
|
||||
# a selfSigned issuer issues immediately, so the secret proves end-to-end issuance
|
||||
run /bin/bash -c "kubectl wait --for=condition=Ready certificate/${TEST_APP}-web -n default --timeout=120s"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "kubectl get secret tls-${TEST_APP}-web -n default -o jsonpath='{.type}'"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output "kubernetes.io/tls"
|
||||
|
||||
run /bin/bash -c "kubectl get ingress ${TEST_APP}-web-${TEST_APP}-dokku-me -n default -o jsonpath='{.spec.tls[0].secretName}'"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output "tls-${TEST_APP}-web"
|
||||
}
|
||||
|
||||
@test "(scheduler-k3s:certs) letsencrypt-server false disables a global cert issuer" {
|
||||
if [[ -z "$DOCKERHUB_USERNAME" ]] || [[ -z "$DOCKERHUB_TOKEN" ]]; then
|
||||
skip "skipping due to missing docker.io credentials DOCKERHUB_USERNAME:DOCKERHUB_TOKEN"
|
||||
fi
|
||||
|
||||
INGRESS_CLASS=nginx install_k3s
|
||||
|
||||
create_selfsigned_cluster_issuer
|
||||
|
||||
run /bin/bash -c "dokku scheduler-k3s:set --global cert-issuer-name dokku-test-selfsigned"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku apps:create $TEST_APP"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku scheduler-k3s:set $TEST_APP letsencrypt-server false"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku domains:set $TEST_APP $TEST_APP.dokku.me"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run deploy_app python "dokku@$DOKKU_DOMAIN:$TEST_APP"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "kubectl get certificate ${TEST_APP}-web -n default"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_failure
|
||||
|
||||
run /bin/bash -c "kubectl get ingress ${TEST_APP}-web-${TEST_APP}-dokku-me -n default -o jsonpath='{.spec.tls}'"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output ""
|
||||
}
|
||||
|
||||
create_selfsigned_cluster_issuer() {
|
||||
declare desc="creates a selfSigned ClusterIssuer that issues certificates without acme"
|
||||
|
||||
local manifest="${BATS_TMPDIR:-/tmp}/dokku-test-selfsigned.yaml"
|
||||
cat >"$manifest" <<'EOF'
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: ClusterIssuer
|
||||
metadata:
|
||||
name: dokku-test-selfsigned
|
||||
spec:
|
||||
selfSigned: {}
|
||||
EOF
|
||||
|
||||
run /bin/bash -c "kubectl apply -f $manifest"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ setup() {
|
||||
}
|
||||
|
||||
teardown() {
|
||||
for prop in deploy-timeout image-pull-secrets ingress-class kubeconfig-path kube-context kustomize-root-path namespace network-interface rollback-on-failure shm-size token; do
|
||||
for prop in cert-issuer-kind cert-issuer-name deploy-timeout image-pull-secrets ingress-class kubeconfig-path kube-context kustomize-root-path namespace network-interface rollback-on-failure shm-size token; do
|
||||
dokku scheduler-k3s:set --global "$prop" >/dev/null 2>/dev/null || true
|
||||
done
|
||||
global_teardown
|
||||
@@ -55,10 +55,11 @@ assert_k3s_global_unset_set() {
|
||||
assert_k3s_global_unset_set "ingress-class" "nginx" "traefik"
|
||||
assert_k3s_global_unset_set "network-interface" "eth0" "eth1"
|
||||
assert_k3s_global_unset_set "kubeconfig-path" "/etc/rancher/k3s/k3s.yaml" "/tmp/custom-kubeconfig.yaml"
|
||||
assert_k3s_global_unset_set "cert-issuer-kind" "ClusterIssuer" "Issuer"
|
||||
}
|
||||
|
||||
@test "(scheduler-k3s:report --global) empty-default properties expose computed sibling" {
|
||||
for prop in image-pull-secrets shm-size kube-context; do
|
||||
for prop in image-pull-secrets shm-size kube-context cert-issuer-name; do
|
||||
run /bin/bash -c "dokku scheduler-k3s:set --global $prop"
|
||||
assert_success
|
||||
|
||||
@@ -77,6 +78,16 @@ assert_k3s_global_unset_set() {
|
||||
assert_success
|
||||
run /bin/bash -c "dokku scheduler-k3s:set --global kube-context my-context"
|
||||
assert_success
|
||||
run /bin/bash -c "dokku scheduler-k3s:set --global cert-issuer-name acme-dns"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku scheduler-k3s:report --global --format json | jq -r '.\"scheduler-k3s-global-cert-issuer-name\"'"
|
||||
assert_success
|
||||
assert_output "acme-dns"
|
||||
|
||||
run /bin/bash -c "dokku scheduler-k3s:report --global --format json | jq -r '.\"scheduler-k3s-computed-cert-issuer-name\"'"
|
||||
assert_success
|
||||
assert_output "acme-dns"
|
||||
|
||||
run /bin/bash -c "dokku scheduler-k3s:report --global --format json | jq -r '.\"scheduler-k3s-global-image-pull-secrets\"'"
|
||||
assert_success
|
||||
@@ -123,6 +134,34 @@ assert_k3s_global_unset_set() {
|
||||
assert_output ""
|
||||
}
|
||||
|
||||
@test "(scheduler-k3s:set) rejects invalid cert issuer and letsencrypt server values" {
|
||||
run /bin/bash -c "dokku apps:create $TEST_APP"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku scheduler-k3s:set $TEST_APP cert-issuer-kind Certificate"
|
||||
assert_failure
|
||||
assert_output_contains "Invalid cert-issuer-kind"
|
||||
|
||||
run /bin/bash -c "dokku scheduler-k3s:set $TEST_APP cert-issuer-name my_own_issuer_name"
|
||||
assert_failure
|
||||
assert_output_contains "Invalid cert-issuer-name"
|
||||
|
||||
run /bin/bash -c "dokku scheduler-k3s:set $TEST_APP letsencrypt-server prodd"
|
||||
assert_failure
|
||||
assert_output_contains "Invalid letsencrypt-server"
|
||||
|
||||
run /bin/bash -c "dokku scheduler-k3s:report $TEST_APP --format json | jq -r '.\"scheduler-k3s-cert-issuer-kind\"'"
|
||||
assert_success
|
||||
assert_output ""
|
||||
|
||||
run /bin/bash -c "dokku scheduler-k3s:set $TEST_APP cert-issuer-kind clusterissuer"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku scheduler-k3s:report $TEST_APP --format json | jq -r '.\"scheduler-k3s-cert-issuer-kind\"'"
|
||||
assert_success
|
||||
assert_output "ClusterIssuer"
|
||||
}
|
||||
|
||||
@test "(scheduler-k3s:report) app-level letsencrypt email overrides global and falls back when unset" {
|
||||
run /bin/bash -c "dokku apps:create $TEST_APP"
|
||||
assert_success
|
||||
|
||||
Reference in New Issue
Block a user