Skip to content

Commit d343942

Browse files
committed
step ca
1 parent 327bedd commit d343942

File tree

1 file changed

+154
-0
lines changed

1 file changed

+154
-0
lines changed
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
---
2+
layout: post
3+
title: "Host own CA for K3S"
4+
date: 2025-10-21 22:51:05 +0800
5+
categories: K3S
6+
---
7+
My K3S is not exposed on Internet. So I never felt the necesity to use HTTPS for all WebUIs. Also because it is hard to use Let's Encrypt in this case.
8+
9+
But while I setup Nextcloud to backup my desktop, the client requires the transmission to be TLS encrypted. This leads this article, host a own CA.
10+
11+
I do not know if there are other options, I use **Step CA** from *smallstep.com*.
12+
13+
1. Install **step-ca**/**step**.
14+
15+
1. Generate step-ca helm values. Be careful with those questions, some are tied with other not-here, not-changable configurations.
16+
17+
```text
18+
✔ Deployment Type: Standalone
19+
20+
What would you like to name your new PKI?
21+
✔ (e.g. Smallstep): Smallstep
22+
23+
What DNS names or IP addresses would you like to add to your new CA?
24+
# This is the only available value in K8S setup, except the namespace part.
25+
✔ (e.g. ca.smallstep.com[,1.1.1.1,etc.]): step-certificates.default.svc.cluster.local
26+
27+
What IP and port will your new CA bind to (it should match service.targetPort)?
28+
# The service is on 9000 by default. Change the value here does not affect the service.
29+
✔ (e.g. :443 or 127.0.0.1:443): :9000
30+
31+
What would you like to name the CA's first provisioner?
32+
# Unlike said in doc, a pre-configured "admin" provisioner, this is the only one in helm installation.
33+
✔ (e.g. you@smallstep.com): me@example.org
34+
35+
Choose a password for your CA keys and first provisioner.
36+
# If chose "generate", the password is shown until press Enter. Copy it and base64 it for used later.
37+
✔ [leave empty and we'll generate one]:
38+
```
39+
40+
2. Install step-ca
41+
42+
`helm upgrade -i step-certificates smallstep/step-certificates -f step-ca-values.yaml --set inject.secrets.ca_password="${BASE64_PASSWORD_FROM_ABOVE}" --set inject.secrets.provisioner_password="${BASE64_PASSWORD_FROM_ABOVE}"/`
43+
44+
2. Install **step-issuer**.
45+
46+
1. `helm install step-issuer smallstep/step-issuer`
47+
48+
2. Setup the issuer.
49+
50+
```Shell
51+
#!/usr/bin/env bash
52+
set -eu -o pipefail
53+
54+
# Same as the one in answer.
55+
CA_URL=https://step-certificates.default.svc.cluster.local
56+
CA_ROOT_B64=$(kubectl get -o jsonpath="{.data['root_ca\.crt']}" configmaps/step-certificates-certs | step base64)
57+
# Same as the one in answer.
58+
CA_PROVISIONER_NAME=magicloud@magicloud.lan
59+
CA_PROVISIONER_KID=$(kubectl get -o jsonpath="{.data['ca\.json']}" configmaps/step-certificates-config | jq -r .authority.provisioners[0].key.kid)
60+
61+
kubectl apply -f - << EOF
62+
---
63+
apiVersion: certmanager.step.sm/v1beta1
64+
kind: StepClusterIssuer
65+
metadata:
66+
name: step-issuer
67+
spec:
68+
# The CA URL:
69+
url: $CA_URL
70+
# The base64 encoded version of the CA root certificate in PEM format:
71+
caBundle: $CA_ROOT_B64
72+
# The provisioner name, kid, and a reference to the provisioner password secret:
73+
provisioner:
74+
name: $CA_PROVISIONER_NAME
75+
kid: $CA_PROVISIONER_KID
76+
passwordRef:
77+
name: step-certificates-provisioner-password
78+
namespace: default
79+
key: password
80+
---
81+
EOF
82+
```
83+
84+
3. Install **cert-manager**.
85+
86+
`kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.19.1/cert-manager.yaml`
87+
88+
4. Use Step CA for Ingress.
89+
90+
In above steps, a `StepClusterIssuer` is created. But be noted, it is not `ClusterIssuer`, since step-ca is out of tree issuer. Its usage is a bit different than in tree ones like ACME.
91+
92+
There are three annotations to be used in Ingress for this case. Per doc, I think two of them is enough.
93+
94+
```YAML
95+
annotations:
96+
# `step-issuer` is the name created above.
97+
cert-manager.io/issuer: step-issuer
98+
# Either of the following should work.
99+
cert-manager.io/issuer-group: certmanager.step.sm
100+
cert-manager.io/issuer-kind: StepClusterIssuer
101+
```
102+
103+
5. Expose Step CA interface to outside of the cluster.
104+
105+
The interface itself is protected by a cert signed by current CA. And its SAN is the service address (apparently). But, one, Traefik does not know about the CA; and two, Traefik requires the target must be in the SAN whilist the target is the pod (not the service) and is its IP. Hence Traefik refuses to do ingressing for Step CA.
106+
107+
The solution is using `IngressRouteTCP` with `spec.tls.passthrough`.
108+
109+
```YAML
110+
apiVersion: traefik.io/v1alpha1
111+
kind: IngressRouteTCP
112+
metadata:
113+
name: step-ca
114+
spec:
115+
routes:
116+
- match: HostSNI(`step-ca.magicloud.lan`)
117+
services:
118+
- name: step-certificates
119+
port: 443
120+
tls:
121+
passthrough: true
122+
```
123+
124+
This leads to another issue, ExternalDNS does not know how to watch this object. So we need its CRD.
125+
126+
First, apply [dnsendpoint CRD](https://raw.githubusercontent.com/kubernetes-sigs/external-dns/master/config/crd/standard/dnsendpoints.externaldns.k8s.io.yaml). Then in ExternalDNS helm values file, add following and upgrade. `service` and `ingress` are the default value, we need `crd`.
127+
128+
```YAML
129+
sources:
130+
- service
131+
- ingress
132+
- crd
133+
```
134+
135+
Now create the DNS record.
136+
137+
```YAML
138+
apiVersion: externaldns.k8s.io/v1alpha1
139+
kind: DNSEndpoint
140+
metadata:
141+
name: step-ca
142+
spec:
143+
endpoints:
144+
- dnsName: step-ca.magicloud.lan
145+
recordType: A
146+
targets:
147+
- 192.168.0.102
148+
```
149+
150+
6. Install the CA.
151+
152+
Theoretically, the CA should be installed everywhere. To other pods in the cluster, `autocert` is the tool. But existing pods is a trouble. Luckily I do not need that. All I need is installing the CA to my desktop so it can communicate with the secured services in cluster.
153+
154+
After install `step` tool in desktop, `step ca bootstrap --ca-url step-ca.magicloud.lan --fingerprint $FINGER_PRINT` to initialize. `$FINGER_PRINT` could be obtained from first few lines of the logs of step-certificates pod. Or, if you are like me, too late to view the log, `step certificate fingerprint $(step path)/certs/root_ca.crt` within the step-certificates pod also shows that. Then `step certificate install $CRT_PATH_SHOWED_IN_LAST_STEP`.

0 commit comments

Comments
 (0)