导读

本文描述了两种场景下,申请Let’s Encrypt证书的途径:

  1. 云服务器中,通过K8S集群中部署cert-manager来申请,这种方式下,您需要具备如下条件:
  • 具备独立公网IP资源的K8S集群(比如公有云服务器上运行的K8S集群)
  • K8S集群已经安装了Ingress控制器
  • 服务器已经安装了helm
  • 一个域名,且指向该公网IP
  • 对于国内服务器,域名已经备案(如果没有备案,将不能通过cert-manager申请证书)
  1. 在云服务器中,通过安装certbot来申请证书,这种方式下,您需要具备如下条件:
  • 具备独立公网IP资源的云服务器(本文的脚本为centos 7.x适用的脚本)
  • 一个域名,且指向该公网IP
  • 对于国内服务器,域名已经备案

安装的详细步骤建议参考官网的文档。本文中的具体配置供参考。

方式一:通过在K8S集群中部署cert-manager申请

通过helm安装

执行以下命令安装(详情参见官网

helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install \
  cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --version v1.8.0 \
  --set installCRDs=true

安装完成后,通过部署一个测试的Issuer,并签发证书进行测试,也可以通过安装cmctl测试(详情参见官网

cat <<EOF > test-resources.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: cert-manager-test
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: test-selfsigned
  namespace: cert-manager-test
spec:
  selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: selfsigned-cert
  namespace: cert-manager-test
spec:
  dnsNames:
    - example.com
  secretName: selfsigned-cert-tls
  issuerRef:
    name: test-selfsigned
EOF

kubectl apply -f test-resources.yaml
kubectl describe certificate -n cert-manager-test

创建ACME方式的ClusterIssuer

通过如下命令创建ClusterIssuer(集群级的证书签发机构):

cat << __EOF__ > clusterissuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    preferredChain: "ISRG Root X1"
    email: <your email>
    privateKeySecretRef:
      name: <Cluster Issuer Secret Name>
    solvers:    
    - http01:
        ingress:
          name: <Exist Ingress Name>
__EOF__

kubectl apply -f clusterissuer.yaml

在上述例子中,solver表示可以完成Challenge(Challenge即证书签发机构要求你完成的一项任务,即证明你对域名拥有控制权)的资源。ACME的Challenge分为两种:

  • http01:提供一个可以通过域名访问的web服务,证明你拥有该域名
  • dns01: 查询域名信息时,能够获得指定的DNS TXT记录,证明你拥有该域名

我们使用http01这种方式。
配置http01 solver时,我们通过下列配置指定现有的ingress:

- http01:
        ingress:
          name: <Exist Ingress Name>

此时,cert-manager会编辑该Ingress,以使证书签发机构的查询流量走到我们集群内部的solver pod中。

签发证书

安装完后,部署下面的资源签发证书:

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: my-certificate
spec:
  secretName: my-certificate-tls
  issuerRef:
    name: letsencrypt-prod # Same to the name of the cluster issuer
    kind: ClusterIssuer
  dnsNames:
  - <your domain>

通过以下命令查看签发的证书是否成功:

kubectl get certificate

如果签发证书不成功,可以根据官网FAQ进行排查。
如果在cert-manager的pod中遇到下面类似的错误,可能是因为域名没有备案:

sync.go:386 cert-manager/challenges/acceptChallenge "msg"="error waiting for authorization" "error"="acme: authorization error for t.sharework.cn: 403 urn:ietf:params:acme:error:unauthorized: : Invalid response from https://dnspod.qcloud.com/static/webblock.html?d=<your domain>: \"

Let’s Encrypt现委托qcloud.com进行域名查询,如果您的域名尚未备案,将得到上述错误。

方式二:通过安装certbot申请

您也可以直接通过安装certbot来进行申请,假设您拥有云服务器,并且在服务器创建时执行了以下脚本(按需要修改前三项参数):

#!/bin/sh
CERT_DOMAIN="your.domain"
CERT_EMAIL="your email"
ROOT_PASSWORD="<root password>"

# Ensure centos-release installed

if test -z $(rpm -qa centos-release); then
    yum install -y centos-release
fi
basearch=$(arch)
releasever=$(rpm -q --qf %{version} centos-release)

# Setup nginx

if test -z "$(rpm -qa nginx)"; then
    cat << __EOF__ > /etc/yum.repos.d/nginx.repo
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=0
enabled=1
module_hotfixes=true
__EOF__
    REFRESH_INSTALL="y"
    yum install -y nginx
    systemctl start nginx.service
    systemctl enable nginx.service
else
    REFRESH_INSTALL="n"
    systemctl restart nginx.service
    systemctl enable nginx.service
fi

# Setup snapd

yum install -y snapd
systemctl restart snapd.service
systemctl enable snapd
snap install core
snap refresh core
if test ! -d /snap; then
    ln -s -d /var/lib/snapd/snap /snap
fi

# Setup certbot

snap install --classic certbot
if test ! -f /usr/bin/certbot; then
    ln -s /snap/bin/certbot /usr/bin/certbot
fi

# Enable port 80 and 443

if test ! -z "$(firewall-cmd --state | grep -e "^running")"; then
    FIREWALL_POLICY_ADDED=""
    if test -z "$(firewall-cmd --list-ports | tr ' ' '\n'|sed 's/[ ]//g' | grep "80/tcp")"; then
        firewall-cmd --permanent --add-port=80/tcp
        FIREWALL_POLICY_ADDED="80"
    fi
    if test -z "$(firewall-cmd --list-ports | tr ' ' '\n'|sed 's/[ ]//g' | grep "443/tcp")"; then
        firewall-cmd --permanent --add-port=443/tcp
        FIREWALL_POLICY_ADDED=$FIREWALL_POLICY_ADDED"443"
    fi
    if test ! -z "$FIREWALL_POLICY_ADDED"; then
        firewall-cmd --reload
    fi    
fi

cat << __EOF__ > /etc/nginx/conf.d/default.conf
server {
    listen 80;
    server_name  localhost $CERT_DOMAIN;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}
__EOF__

# Restart Nginx to accept the new domain

systemctl restart nginx.service

# change root password
echo $ROOT_PASSWORD | passwd --stdin root

cat << __EOF__ >> /etc/profile
echo "Certbot Server"
echo "=============="
echo "You can request certificate by following steps(<IP> is the IP address of THIS VPS) : "
echo ""
echo "1. Setup DNS A record, bind the domain to the <IP>"
echo "2. certbot run --nginx -n --email $CERT_EMAIL -d $CERT_DOMAIN --agree-tos"
echo "3. cd to a proper local directory"
echo "4. scp -r root@<IP>:{/etc/letsencrypt/live/$CERT_DOMAIN,/etc/letsencrypt/options-ssl-nginx.conf,/etc/letsencrypt/ssl-dhparams.pem,/etc/nginx/conf.d/default.conf} ."
echo ""
echo "You can find certificates in sub folder $CERT_DOMAIN, besides with nginx configuration files."
__EOF__

服务器启动后,您可以登录服务器并按照上面的提示进行证书申请。