#!/bin/bash
set -euo pipefail
DAYS="${TTK_CERT_DAYS:-90}"
die() { echo "error: $*" >&2; exit 1; }
usage() { sed -n '2,24p' "$0"; exit 1; }
[ $# -ge 1 ] || usage
OP="$1"; shift
case "$OP" in
init-ca)
[ $# -ge 1 ] || die "usage: $0 init-ca <CERTDIR>"
D="$1"
mkdir -p "$D"
[ -f "$D/ca.crt" ] && die "$D/ca.crt 已存在 —— 拒绝覆盖既有 CA。换个空目录或先移走它。"
openssl req -x509 -newkey rsa:4096 -keyout "$D/ca.key" -out "$D/ca.crt" \
-days "$DAYS" -nodes -subj "/CN=TTK-CA"
chmod 600 "$D/ca.key"
echo "CA 已建于 $D/"
echo "【ca.key 只留本机、勿删勿拷】ca.key 只能留在这台 CA 机器上(轮换叶子证书要靠它),绝不发往任何 client/server。"
;;
server)
[ $# -ge 2 ] || die "usage: $0 server <CERTDIR> <SAN> [<SAN> ...] (SAN = IP:x.x.x.x 或 DNS:name,必填)"
D="$1"; shift
[ -f "$D/ca.crt" ] && [ -f "$D/ca.key" ] || die "$D 下找不到 CA —— 先跑 '$0 init-ca $D'"
SAN="$(IFS=,; echo "$*")"
[ -n "$SAN" ] || die "至少一个 SAN(IP:x.x.x.x 或 DNS:name)"
printf "subjectAltName=%s\n" "$SAN" > "$D/server.ext"
openssl req -newkey rsa:2048 -keyout "$D/server.key" -out "$D/server.csr" \
-nodes -subj "/CN=xpu-server"
openssl x509 -req -in "$D/server.csr" -CA "$D/ca.crt" -CAkey "$D/ca.key" \
-CAcreateserial -out "$D/server.crt" -days "$DAYS" -extfile "$D/server.ext"
rm -f "$D/server.csr" "$D/server.ext"
chmod 600 "$D/server.key"
echo "Server 证书在 $D/ (server.crt/server.key,SAN=$SAN)"
echo "装到 XPU server:ca.crt + server.crt + server.key(ca.key 留 CA 机,不发)。"
;;
client)
[ $# -ge 2 ] || die "usage: $0 client <CERTDIR> <tenant-name>"
D="$1"; T="$2"
[ -f "$D/ca.crt" ] && [ -f "$D/ca.key" ] || die "$D 下找不到 CA —— 先跑 '$0 init-ca $D'"
openssl req -newkey rsa:2048 -keyout "$D/$T.client.key" -out "$D/$T.client.csr" \
-nodes -subj "/CN=$T"
openssl x509 -req -in "$D/$T.client.csr" -CA "$D/ca.crt" -CAkey "$D/ca.key" \
-CAcreateserial -out "$D/$T.client.crt" -days "$DAYS"
rm -f "$D/$T.client.csr"
chmod 600 "$D/$T.client.key"
echo "租户 '$T' 的 client 证书在 $D/ ($T.client.crt/$T.client.key)"
echo "发给该租户:ca.crt + $T.client.crt + $T.client.key(ca.key 永不发)。server 无需这些(它信 CA)。"
;;
*)
usage
;;
esac