本文为家庭宽带环境下,k3s中traefik配置Let’s Encrypt TLS证书的方法。
之前设置了 morningserver.local 的本地域名,但一些问题。
- DNS 设置比较麻烦
- mDNS不支持泛域名广播
- 本地 hosts 也不支持泛域名
- 只能通过设置路由器上的DNSmasq,达到内网泛域名解析的目的
- 本地域名后期无法对外服务
因此考虑直接通过一个公网域名进行服务访问。由于本地域名的特殊性,浏览器会做特殊处理,忽略“http不安全”问题。对于公网域名,通过http访问,浏览器就会报错,同时自签证书也会需要设备信任及ca存储一系列问题。最后考虑通过traefik配置Let’s Encrypt自动获取 TLS 证书。
ACME
Automatic Certificate Management Environment (ACME)是自动证书管理环境,可以通过客户端申请 / 续期证书。 免费的证书颁发机构 Let’s Encrypt申请证书都是通过ACME完成的。
ACME有好几种验证方式,在traefik中也都支持验证方式:
- HTTP-01: 通过服务器的80端口进行认证;
- TLS-ALPN-01: 通过服务器443端口进行认证;
- DNS-01: 通过在域名DNS中TXT记录中放置特定字符串来认证对域名的归属权;
所有家庭宽带都不开放443及80端口,因此对于家宽用户只能使用DNS-01认证。
DNS-01 认证
DNS-01 认证通过在域名DNS中TXT记录中放置特定字符串来认证对域名的归属权,因此需要DNS支持API。 traefik支持的DNS API列表可以查看 Let’s Encrypt providers。需要将对应的密钥或token写到环境变量。
打两个比方:
- DNSPod:需要获取到DNSPod的token(非腾讯云API密钥),dnspod需使用
ID,Token
才是完整版token格式- DNSPOD_API_KEY
- 阿里云DNS:获取API key, 建议在阿里云上建立DNS专用帐号分权分域
- ALICLOUD_ACCESS_KEY, ALICLOUD_SECRET_KEY, ALICLOUD_REGION_ID
traefik配置
K3S的traefik是通过chart包进行安装的,此时可以通过 HelmChartConfig 来覆盖默认配置的一些字段(参考网络 | K3s及 使用 HelmChartConfig 自定义打包组件)。
可以参考Traefik Let’s Encrypt Documentation了解一些概念。但实际配置HelmChartConfig需参考 traefik-helm-chart/values.yaml at v20.3.0 · traefik/traefik-helm-chart 进行配置。此时要关注到 k3s实用的 traefik chart包版本,此处使用 v20.3.0
tag。
在宿主机上创建文件 /var/lib/rancher/k3s/server/manifests/traefik-config.yaml
并写入
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
certResolvers:
dnspod:
# for challenge options cf. https://doc.traefik.io/traefik/https/acme/
email: [email protected]
dnsChallenge:
# also add the provider's required configuration under env
# or expand then from secrets/configmaps with envfrom
# cf. https://doc.traefik.io/traefik/https/acme/#providers
provider: dnspod
# add futher options for the dns challenge as needed
# cf. https://doc.traefik.io/traefik/https/acme/#dnschallenge
delayBeforeCheck: 30
# match the path to persistence
storage: /data/acme.json
env:
- name: LEGO_DISABLE_CNAME_SUPPORT
value: 'true'
- name: DNSPOD_API_KEY
value: tokenid,token
此时的resolver名称叫 dnspod
,后面会使用到。同时通过打开 LEGO_DISABLE_CNAME_SUPPORT
参数来适配cname的域名,不打开会产生混乱。
IngressRoute配置
创建一个IngressRoute资源,此时需注意apiVersion,老版本的traefik域名为traefik.containo.us,不要使用traefik.io。这是一个指向portainer service 9000端口的IngressRoute,同时指明了使用 dnspod这个resolver资源。
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: portainer
namespace: portainer
spec:
entryPoints:
- websecure
routes:
# 多个域名只要在同一个namespace下都能往下加
- kind: Rule
match: Host(`portainer.yourdomain.com`) && PathPrefix(`/`)
services:
- name: portainer
port: 9000
tls:
certResolver: dnspod
DNS配置
我配置了公网域名 in.yourdomain.com 解析到家庭服务器局域网地址,同时将 * CNAME到 _in.yourdomain.com _
此时通过https访问域名已经可以成功了
http跳转https
使用 traefik 中间件功能,可以做到http跳转https。这边考虑到仍然会有http的需求,就不做全局配置,仅针对各个域名进行设置。
Middleware
配置中间件。永久跳转到 websecure
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: redirect
namespace: default
spec:
redirectScheme:
permanent: true
scheme: https
http IngressRoute
复制上面的IngressRoute, 将entryPoints改为 web,删除tls那一节,并在每一项需要的route里增加中间件
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: portainer-http
namespace: portainer
spec:
entryPoints:
- web
routes:
# 多个域名只要在同一个namespace下都能往下加
- kind: Rule
match: Host(`portainer.yourdomain.com`) && PathPrefix(`/`)
services:
- name: portainer
port: 9000
middlewares:
- name: redirect
打开浏览器跳转成功
Debug
当然是查看 traefik pod日志啦,证书获取失败会有很详细的原因介绍,就是得多等一会儿。应该会受 delayBeforeCheck: 30 的时间影响。
另外灵活使用curl命令查看返回结果。
总结
这样内网就可以直接使用公网域名访问内部服务了。后续有对外服务的需求只要直接暴露服务,并修改DNS即可。