为内网 Homelab 创建 CA 证书和通配符证书

2023-06-20 [基础设施] #Homelab #CA-certificate #OpenWrt #Network

家里 Homelab 使用了 Let’s Encrypt 的通配符证书提供外网的 HTTPS 访问,内网则是使用 HTTP 或者 IP 直接访问。最近把 Homelab 的外网完全关闭了,通过 VPN 回家以提高安全性。

当全切回内网后,Bitwarden iOS 客户端无法正常使用,在登录时会提示错误。大概原因是 Apple 会禁止使用不安全的连接

其实应该使用 HTTPS 的,毕竟是一个存储密码的服务,安全性还是很重要的。本文记录了给内网 Homelab 绑定自定与域名以及启用 HTTPS 证书的相关步骤,大致为:

  1. 创建一个 CA 证书
  2. 创建一个 通配符证书
  3. 服务使用通配符证书
  4. 设备导入 CA 证书
  5. 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} 目录。"

代码执行完成后,会在第一步指定的目录生成几个证书文件:

以上代码中,需要注意两个地方(Apple - Requirements for trusted certificates in iOS 13 and macOS 10.15):

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:

所以需要将证书同步到多个命名空间。我使用了 kuberops/config-syncer 来做多命名空间的同步。

安装 kuberops/config-syncer 比较简单,跟着这 #installation 部分操作就行。

使用也比较简单,步骤是:

  1. 给需要同步的内容打注解
  2. 给需要使用的命名空间添加 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

tls secrets in kubernetes

服务使用通配符证书

部署的相关服务需要使用证书也可以直接在 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

  1. MacOS - Keychain Access - 左侧选择系统 / System keychain_access_import 导入过程中请选择信任证书 keychain_access_import_trust

导入并信任之后为: keychain_access_imported

Linux (Debian)

sudo cp wildcard.helloword.lab.crt /usr/local/share/ca-certificates
sudo update-ca-certificates

然后 curl 看一下是否有证书错误即可。

浏览器证书导入

Firefox 访问依旧提示证书错误,Firefox 没有使用系统的证书存储,需要自己单独导入一次。 firefox_warning.png firefox_importing_1.png firefox_importing.png

之后再访问就正常了。

手机导入证书

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 用命令行修改:

  1. 编辑 /etc/config/dhcp 文件
  2. config dnsmasq 部分的底部添加:list address '/git.helloworld.lab/10.0.0.2'
  3. 重启 /etc/init.d/dnsmasq restart

给各个设备、路由器都配置好后,就可以完全无障碍访问自签名的 HTTPS 域名了。

文章作者:eightpigs
创作时间:2023-06-20
更新时间:2023-06-20
许可协议:CC by-nc-nd 4.0