为内网 Homelab 创建 CA 证书和通配符证书
家里 Homelab 使用了 Let’s Encrypt 的通配符证书提供外网的 HTTPS 访问,内网则是使用 HTTP 或者 IP 直接访问。最近把 Homelab 的外网完全关闭了,通过 VPN 回家以提高安全性。
当全切回内网后,Bitwarden iOS 客户端无法正常使用,在登录时会提示错误。大概原因是 Apple 会禁止使用不安全的连接。
其实应该使用 HTTPS 的,毕竟是一个存储密码的服务,安全性还是很重要的。本文记录了给内网 Homelab 绑定自定与域名以及启用 HTTPS 证书的相关步骤,大致为:
- 创建一个 CA 证书
- 创建一个 通配符证书
- 服务使用通配符证书
- 设备导入 CA 证书
- Enjoy
1. 创建证书
我将创建证书的逻辑整理成了以下脚本,执行根据提示输入即可。
#!/bin/bash
function check_file() {
local file_path="$1"
if [ -e "${file_path}" ]; then
read -p "${file_path} 已存在,是否覆盖?(y/n):" yn
if [[ "${yn}" != "y" ]]; then
echo "操作已取消。"
exit 1
fi
fi
}
# 询问用户 CA 存储的目录
read -p "请输入 CA 存储的目录(例如:/root/my_ca):" ca_dir
mkdir -p "${ca_dir}"
cd "${ca_dir}"
# 询问用户 CA 文件名(默认为 ca.key 和 ca.crt)
read -p "请输入 CA 私钥文件名(默认:ca.key):" ca_key
ca_key=${ca_key:-ca.key}
read -p "请输入 CA 证书文件名(默认:ca.crt):" ca_crt
ca_crt=${ca_crt:-ca.crt}
read -p "请输入 组织名(默认:Homelab.LAB):" org_name
org_name=${org_name:-Homelab.LAB}
check_file "${ca_key}"
check_file "${ca_crt}"
# 询问用户要创建通配符证书的域名(默认为 xxxx.lab)
read -p "请输入要创建通配符证书的域名(默认:xxxx.lab):" domain
domain=${domain:-xxxx.lab}
wildcard_key="wildcard.${domain}.key"
wildcard_csr="wildcard.${domain}.csr"
wildcard_crt="wildcard.${domain}.crt"
wildcard_ext="wildcard.ext"
check_file "${wildcard_key}"
check_file "${wildcard_csr}"
check_file "${wildcard_crt}"
check_file "${wildcard_ext}"
ca_openssl_cnf="ca_openssl.cnf"
check_file "${ca_openssl_cnf}"
cat > "${ca_openssl_cnf}" << EOL
[req]
default_bits = 4096
prompt = no
default_md = sha256
distinguished_name = dn
[ dn ]
C = ZZ
ST = Utopia
L = The Peach Garden
O = ${org_name}
OU = ${org_name}
CN = CA.${domain}
EOL
# 创建 CA
openssl genrsa -out "${ca_key}" 4096
openssl req -x509 -new -nodes -key "${ca_key}" -sha256 -days 3650 -out "${ca_crt}" -config "${ca_openssl_cnf}"
wildcard_openssl_cnf="wildcard_openssl.cnf"
check_file "${wildcard_openssl_cnf}"
cat > "${wildcard_openssl_cnf}" << EOL
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = req_ext
[ dn ]
C = ZZ
ST = Utopia
L = The Peach Garden
O = ${org_name}
OU = ${org_name}
CN = *.${domain}
[ req_ext ]
subjectAltName = @alt_names
[alt_names]
DNS.1 = *.${domain}
DNS.2 = ${domain}
EOL
# 为通配符证书创建私钥
openssl genrsa -out "${wildcard_key}" 2048
# 为通配符证书创建证书签名请求(CSR)
openssl req -new -key "${wildcard_key}" -out "${wildcard_csr}" -config "${wildcard_openssl_cnf}"
# 创建通配符证书的扩展文件
# 其中需要注意参数:extendedKeyUsage = serverAuth 需要添加,
cat > "${wildcard_ext}" << EOL
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
extendedKeyUsage = serverAuth
[alt_names]
DNS.1 = *.${domain}
DNS.2 = ${domain}
EOL
# 使用 CA 对 CSR 文件进行签名,生成通配符证书
openssl x509 -req -in "${wildcard_csr}" -CA "${ca_crt}" -CAkey "${ca_key}" -CAcreateserial -out "${wildcard_crt}" -days 820 -sha256 -extfile "${wildcard_ext}"
echo "创建完成,证书和私钥位于 ${ca_dir} 目录。"
代码执行完成后,会在第一步指定的目录生成几个证书文件:
- 配置相关:
- ca_openssl.cnf
- wildcard.ext
- wildcard_openssl.cnf
- CA 相关:
- helloworld-ca.crt
- helloworld-ca.key
- helloworld-ca.srl
- 通配符 相关:
- wildcard.helloword.lab.crt
- wildcard.helloword.lab.crs
- wildcard.helloword.lab.key
以上代码中,需要注意两个地方(Apple - Requirements for trusted certificates in iOS 13 and macOS 10.15):
- 创建 wildcard_ext 时
extendedKeyUsage = serverAuth参数,不添加的话 Safari 可能会提示:“certificate is not standards compliant” - 创建通配符证书(最后一个 openssl 命令)中
-days 820参数,天数不能大于 825 天,不然 Safari 也会有上面的提示。
2. Kubernates 配置
创建 secret
在 Kubernates 中创建一个名为 helloworld-lab-tls 的通配符证书,需要注意下是使用了通配符的证书文件,而不是 CA 文件。
kubectl create secret tls helloword-lab-tls \
--cert=wildcard.helloword.lab.crt \
--key=wildcard.helloword.lab.key
同步 Secert 到其他命名空间
由于我是区分了 Namespace:
- infra: 证书等跟基础服务相关的
- selfhosts: 自己跑的一些开源服务
所以需要将证书同步到多个命名空间。我使用了 kuberops/config-syncer 来做多命名空间的同步。
安装 kuberops/config-syncer 比较简单,跟着这 #installation 部分操作就行。
使用也比较简单,步骤是:
- 给需要同步的内容打注解
- 给需要使用的命名空间添加 labels
# 1. 给证书打注解,支持同步到其他命名空间
kubectl annotate secret \
helloword-lab-tls \
kubed.appscode.com/sync="tls-certs=helloword.lab" \
-n default
# 2. 给需要同步证书的命名空间添加 label,使能将证书同步到当前命名空间
apiVersion: v1
kind: Namespace
metadata:
name: infra
labels:
tls-certs: helloword.lab
# 3. 查看证书是否已经同步到其他命名空间
~/Workspace/kube [main] → kubectl get secret -A | grep helloword-lab-tls
default helloword-lab-tls kubernetes.io/tls 2 44m
selfhosts helloword-lab-tls kubernetes.io/tls 2 22m
infra helloword-lab-tls kubernetes.io/tls 2 22m

服务使用通配符证书
部署的相关服务需要使用证书也可以直接在 Ignress 配置上配置,如下:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: memos-ingress
namespace: selfhosts
annotations:
kubernetes.io/ingress.class: "traefik"
spec:
tls:
- hosts:
- memo.helloword.lab
secretName: helloword-lab-tls
rules:
- host: memo.helloword.lab
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: memos-svc
port:
name: memos-web
添加好配置记得应到 pod,完成以上步骤后,可以在浏览器访问下服务,应该会提示证书错误,接下来就需要给设备添加自签名的通配符证书。
3. 导入证书
需要注意下,上面在绑定证书时是使用的通配符证书,而在设备上使用时是需要导入 CA 证书。
MacOS
- MacOS - Keychain Access - 左侧选择系统 / System
导入过程中请选择信任证书

导入并信任之后为:

Linux (Debian)
sudo cp wildcard.helloword.lab.crt /usr/local/share/ca-certificates
sudo update-ca-certificates
然后 curl 看一下是否有证书错误即可。
浏览器证书导入
Firefox 访问依旧提示证书错误,Firefox 没有使用系统的证书存储,需要自己单独导入一次。

之后再访问就正常了。
手机导入证书
iPhone
- 下载 或者 AirDrop 传到手机后,系统设置中会有提示,按步骤导
- 打开 iPhone 的 “设置” 应用。
- 滚动到 “通用”,然后点击进入。
- 向下滚动,找到 “关于本机”,点击进入。
- 在 “关于本机” 页面中,找到 “证书信任设置”,点击进入。
- 在 “证书信任设置” 页面中,找到您刚刚导入的根证书(应该是您的
ca.crt文件)。它应该在 “针对根证书启用完全信任” 部分。 - 打开根证书旁边的开关以启用信任。
- 系统会提示您确认此操作。点击 “继续” 以信任该证书。
Android(以下来源为 Add & remove certificates - Google)
- Open your device’s Settings app.
- Tap Security & privacy And then More security settings and then Encryption & credentials.
- Tap Install a certificate And then Wi-Fi certificate.
- Tap Menu Icon.
- Tap where you saved the certificate.
- Tap the file.
- If needed, enter the key store password. Tap OK.
- Enter a name for the certificate.
- Tap OK.
4. 内网 DNS 配置
我家庭网络的核心是 OpenWrt,所以这里贴出 OpenWrt 配置 DNS 的部分。
思路是将指定域名指定到 K3s 上(我的 Kubernates 是 K3s 版本),K3s 的 IP 为: 10.0.0.2。
OpenWrt - Network - DHCP and DNS - Addresses 配置,格式如下:
/git.helloworld.lab/10.0.0.2
也可以直接通过登录到 OpenWrt 用命令行修改:
- 编辑
/etc/config/dhcp文件 - 在
config dnsmasq部分的底部添加:list address '/git.helloworld.lab/10.0.0.2' - 重启
/etc/init.d/dnsmasq restart
给各个设备、路由器都配置好后,就可以完全无障碍访问自签名的 HTTPS 域名了。