Interne Services mit selbstsignierten Zertifikaten sind wie Kabelbinder im Rack: funktionieren kurz, werden aber irgendwann peinlich. Spätestens wenn Browser, curl, Java oder irgendein störrischer Agent anfangen zu schreien, brauchst du eine saubere interne PKI.
Wir haben uns dafür eine kleine eigene CA gebaut. Nicht, weil wir Langeweile hatten, sondern weil interne TLS-Zertifikate für APIs, Dashboards, Git, Monitoring und sonstige Bits ordentlich ausgestellt, verteilt und rotiert werden sollen.
Grundregel Nummer eins: Die Root-CA bleibt offline. Immer. Die signiert nur eine Intermediate-CA. Diese Intermediate darf dann Zertifikate für interne Services ausstellen. Wenn die Intermediate kompromittiert wird, tauschst du sie aus. Wenn die Root weg ist, ist PKI-Mordor offen.
Minimal mit OpenSSL sieht das so aus:
```bash
mkdir -p pki/{root,intermediate}
chmod 700 pki/root pki/intermediate
```
Root-Key erzeugen:
```bash
openssl genrsa -aes256 -out pki/root/root-ca.key 4096
```
Root-Zertifikat erstellen:
```bash
openssl req -x509 -new -nodes \
-key pki/root/root-ca.key \
-sha256 -days 3650 \
-out pki/root/root-ca.crt \
-subj "/CN=PLUTEX Internal Root CA/O=PLUTEX"
```
Dann kommt die Intermediate-CA:
```bash
openssl genrsa -out pki/intermediate/intermediate-ca.key 4096
openssl req -new \
-key pki/intermediate/intermediate-ca.key \
-out pki/intermediate/intermediate-ca.csr \
-subj "/CN=PLUTEX Internal Intermediate CA/O=PLUTEX"
```
Die CSR signierst du mit der Root. Wichtig: CA-Extensions setzen, sonst ist das Ding nur ein hübsches Zertifikat ohne Superkräfte.
Für interne Service-Zertifikate ist SAN Pflicht. Common Name ist historisches Deko-Material. Beispiel für `api.intern.example`:
```ini
api.ext
basicConstraints=CA:FALSE
keyUsage=digitalSignature,keyEncipherment
extendedKeyUsage=serverAuth
subjectAltName=DNS:api.intern.example,DNS:api
```
Key und CSR:
```bash
openssl genrsa -out api.key 2048
openssl req -new \
-key api.key \
-out api.csr \
-subj "/CN=api.intern.example"
```
Signieren über die Intermediate:
```bash
openssl x509 -req \
-in api.csr \
-CA pki/intermediate/intermediate-ca.crt \
-CAkey pki/intermediate/intermediate-ca.key \
-CAcreateserial \
-out api.crt \
-days 90 \
-sha256 \
-extfile api.ext
```
Ja, 90 Tage. Nicht 5 Jahre. Lange Laufzeiten fühlen sich bequem an, sind aber Zertifikats-Koma. Kurze Laufzeiten zwingen dich zu Automation. Und Automation ist hier nicht Luxus, sondern Brandschutz.
Verteilung ist der nächste Endgegner. Clients müssen deiner Root-CA vertrauen. Unter Debian/Ubuntu kopierst du das Root-Zertifikat nach:
```bash
cp root-ca.crt /usr/local/share/ca-certificates/plutex-root-ca.crt
update-ca-certificates
```
Bei RHEL-artigen Systemen:
```bash
cp root-ca.crt /etc/pki/ca-trust/source/anchors/
update-ca-trust
```
Java bringt natürlich seinen eigenen Truststore mit, weil Java gerne Java-Dinge tut:
```bash
keytool -importcert \
-alias plutex-root-ca \
-file root-ca.crt \
-keystore cacerts
```
Für Browser, Container-Images, Kubernetes-Nodes und Appliances brauchst du eigene Wege. Dokumentiere sie. Sonst findest du in sechs Monaten irgendwo ein vergilbtes Zertifikat in einem Dockerfile und hörst innerlich Modemgeräusche.
Rotation planen wir so: Zertifikate 90 Tage gültig, Erneuerung ab Tag 60, Alarm ab Tag 75, Eskalation ab Tag 85. Monitoring geht simpel:
```bash
openssl x509 -in api.crt -noout -enddate
```
Besser ist natürlich ein Check in Prometheus, Icinga oder deinem bevorzugten Alarm-Wecker.
Revocation nicht vergessen. CRL oder OCSP klingt nach Enterprise-Kaffee, ist aber sinnvoll, wenn Keys verloren gehen. Für viele interne Setups reicht kurze Laufzeit plus saubere Neu-Ausstellung. Bei kritischen Systemen: Sperrlisten einplanen.
Unser Fazit aus dem Selbstversuch: Eine eigene CA ist kein Hexenwerk, aber auch kein „openssl copy-paste und ab dafür“. Root offline halten, Intermediate nutzen, SAN korrekt setzen, Truststores sauber verteilen und Rotation automatisieren. Dann hast du interne TLS-Zertifikate ohne Browser-Warnungen, ohne Wildwuchs und ohne nächtliche Zertifikats-Geisterbahn.