k8s master join 时etcd证书报错问题

最近在给客户部署一套 k8s 集群时,主节点master1安装完后,master2 join时提示 etcd 证书报错,铲掉重装后问题还是稳定复现,客户提供的机器操作系统为Ubuntu 22.04、Kernel:Linux 5.15.x,报错信息节选如下

error execution phase control-plane-prepare/certs: error creating PKI assets: failed to write or validate certificate "etcd-server": cert, not master2 is invalid: x509: certificate is valid for localhost, master2

...

(sudo -E /bin/bash <<EOF

kubeadm join --config=/etc/kubernetes/kubeadm.yaml -v 0 --ignore-preflight-errors=SystemVerification,Port-10250,DirAvailable--etc-kubernetes-manifests

EOF) : error(Process exited with status 1)

使用的安装工具是产研封装的一套k8s外加自研产品的自动化部署流程,相当于个黑盒,看直接报错,问题是出在master2的etcd证书上。首先查看master1和master2的kubeadm.yaml中certSANS和ETD证书配置都没有问题,再去master2上查看etcd  server.crt和peer.crt,发现 CN(Common Name) 和   SAN(Subject Alternative Name) 配置中的主机名都多了一个换行符,但是查看master2的主机中/etc/hostname文件中并没有换行符,然后用master3 join也同样报这个错,但master1就没这个问题,这就很奇葩了,问研发和同事都没遇到过这种问题。

# 检查节点证书 SAN 是否包含所有必要条目
openssl x509 -in /etc/kubernetes/pki/etcd/server.crt -text -noout | egrep -A1 "Subject:|DNS:"

master2节点 etcd-server 证书 SAN 输出示例(签的不是统一证书,每台etcd证书都不一样),多了一个换行符k8s master join 时etcd证书报错问题

Subject:CN = master2\0D

X509v3 Subject Alternative Name:
    DNS:localhost, DNS:master2^M, IP Address:127.0.0.1, IP Address:192.168.1.100

输出字段说明

  1. CN:master2

    • 用于标识证书持有者的名称

  2. DNS:master2

    • 主节点的主机名(必须与节点实际主机名一致)。

  3. DNS:localhost

    • 本地回环地址的 DNS 标识(用于本地通信)。

  4. IP Address:192.168.1.100

    • 主节点的实际 IP 地址(用于跨节点通信)。

  5. IP Address:127.0.0.1

    • 本地回环 IP 地址(用于本机内部通信)。

赶紧恶补下 k8s 中证书使用相关的知识。


在 Kubernetes(K8s)集群中,CN(Common Name) 和 SAN(Subject Alternative Name) 是 X.509 证书中的关键字段,分别用于身份标识和通信合法性验证。

一、CN(Common Name)的作用

1. 标识实体身份

  • 用途:CN 字段用于唯一标识证书持有者(如组件、用户或服务账号)。

  • 典型场景

    • kubelet:证书的 CN 设置为 system:node:<节点名称>,标识节点身份。

CN = system:node:k8s-worker1
    • 用户证书:管理员证书的 CN 可能为 system:admin,标识用户身份。

    • 服务账号:服务账号证书的 CN 通常为 system:serviceaccount:<命名空间>:<服务账号名>

2. 与 RBAC 的关联

  • RBAC 授权:K8s 基于 CN 和 O(Organization) 字段实现基于角色的访问控制(RBAC)。

CN = system:node:k8s-worker1
O = system:nodes
    • 该证书表示属于 system:nodes 组的 k8s-worker1 节点,拥有节点的操作权限。

3. 常见问题

  • 权限不足:若证书的 CN 未正确配置,可能导致 RBAC 拒绝请求。

Error from server (Forbidden): User "system:node:k8s-worker2" cannot list pods

二、SAN(Subject Alternative Name)的作用

1. 验证通信合法性

  • 用途:SAN 指定证书有效的域名或 IP 地址列表,确保通信双方身份合法。

  • 典型场景

    • kube-apiserver 证书必须包含所有可能的访问入口:

SAN = DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc,
      DNS:kubernetes.default.svc.cluster.local, IP:10.96.0.1, IP:192.168.1.100
    • etcd 证书需包含集群节点 IP 和 DNS:

SAN = DNS:etcd.local, DNS:localhost, IP:192.168.1.100, IP:127.0.0.1

2. 多域名/IP 支持

  • 灵活性:允许一个证书保护多个域名或 IP,适用于复杂网络环境。

  • 示例

SAN = DNS:example.com, DNS:*.example.com, IP:192.168.1.100

3. 常见错误

  • 证书不信任:若 SAN 未包含客户端访问的地址,会触发错误:

x509: certificate is valid for 192.168.1.100, not 192.168.1.101

三、CN 与 SAN 的协作

字段主要用途K8s 应用场景优先级
CN标识实体身份RBAC 用户/节点标识次要(依赖 SAN)
SAN验证通信合法性服务端证书的多域名/IP 支持主要(现代 TLS 标准)

协作示例

  • kube-apiserver 证书

    • CNkube-apiserver(标识服务名称)。

    • SAN:包含所有客户端可能访问的域名和 IP(如集群内 DNS、公网 IP、负载均衡 IP)。

四、配置示例(kubeadm)

在 kubeadm-config.yaml 中显式指定 SAN:

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
apiServer:
  certSANs:
    - "k8s-api.example.com"
    - "192.168.1.100"
    - "10.96.0.1"
etcd:
  local:
    serverCertSANs:
      - "etcd.local"
      - "192.168.1.100"

操作步骤

1.生成证书

kubeadm init phase certs all --config kubeadm-config.yaml

2.验证证书内容

openssl x509 -in /etc/kubernetes/pki/apiserver.crt -text -noout | grep -E "CN|DNS|IP"

五、常见问题排查

1. 节点加入失败

  • 错误

x509: certificate is valid for 192.168.1.100, not 192.168.1.101
  • 解决:更新主节点证书的 SAN,包含新节点的 IP 或主机名。

2. RBAC 权限不足

  • 错误

User "system:node:k8s-worker2" cannot list pods
  • 解决:检查证书的 CN 和 O 字段是否匹配 RBAC 角色绑定。

3. 证书同步问题

  • 现象:待加入节点证书与主节点不一致(master节点证书统一的场景)。

  • 解决:手动复制证书并确保权限正确:

scp /etc/kubernetes/pki/etcd/* user@node:/etc/kubernetes/pki/etcd/
chmod 600 /etc/kubernetes/pki/etcd/*

六、总结

  • CN:用于标识实体身份(如用户、节点),是 RBAC 授权的依据。

  • SAN:确保服务端证书覆盖所有可能的访问方式(域名/IP),是 TLS 验证的核心。

  • 最佳实践

    • 服务端证书:优先配置 SAN,覆盖所有网络访问路径。

    • 客户端证书:明确设置 CN 和 O 字段以匹配 RBAC 策略。

    • 工具支持:使用 kubeadm 配置文件管理 SAN,避免手动生成错误。


定位到了创建的etcd证书有问题,接下来就来查查是啥原因,k8s默认是从 /etc/hostname 中读取主机名,很可能被编辑或者脚本修改后而加上了换行符,不过很遗憾,三台master机器/etc/hostname里都没有换行符。还是先来看看Linux /etc/hostname 里为什么会有换行符。

在 Linux 系统中,/etc/hostname 文件的默认行为确实可能包含末尾的换行符,这是由文本编辑器的默认行为(而非系统本身)导致的。

1. 为何会出现换行符?

  • 文本编辑器的默认行为:大多数编辑器(如 vimnano)在保存文件时,会自动在文件末尾添加换行符(\n)。这是遵循 POSIX 标准 的结果,该标准规定文本文件的每行应以换行符结束。

  • 文件格式问题:如果在 Windows 系统上编辑过 /etc/hostname 文件,可能会自动添加 \r\n(CRLF)换行符,而 Linux 使用 \n(LF),ASCII码为0d

  • 命令写入的默认行为:如使用 echo 命令时,默认会添加换行符若需避免换行符,需添加 -n 选项。

  • 非法字符残留:某些脚本或工具可能意外插入非法字符。

2. 换行符是否会影响主机名?

  • 系统工具会自动处理:Linux 系统工具(如 hostnamectlsystemd-hostnamed)在读取 /etc/hostname 时会自动忽略末尾的换行符,仅提取有效的主机名字符串。

  • 手动操作需注意:若通过脚本或工具直接读取 /etc/hostname,未处理的换行符可能导致意外结果。例如以下几种,在机器上测试都加了换行符。

1. 使用vim编辑/etc/hostname
2. echo xxx > /etc/hostname
3. 获取hostname
hostname | od -c

3. 验证与解决方法

使用 hexdump 或 od 命令检查 /etc/hostname 文件的原始内容:

hexdump -C /etc/hostname
# 或
od -c /etc/hostname

如果输出中包含 \r(回车符,ASCII 码 0d),则会显示类似:

# hexdump -C
00000000  6d 79 68 6f 73 74 0d 0a                           |myhost.|

# od -c
00000000  m y h o s t \n

修复方法

# 清理 /etc/hostname 文件
sed -i 's/\r//g' /etc/hostname
echo -n 'xx' > /etc/hostname

# 重新设置主机名
hostnamectl set-hostname "$(cat /etc/hostname | tr -d '\r')"
systemctl restart systemd-hostnamed.service

4. 最佳实践

  • 避免手动编辑:建议使用 hostnamectl 命令修改主机名,/etc/hostname文件可以使用chattr加锁,避免因为被编辑而自动保存换行符问题。

  • 脚本中处理换行符:若必须直接读取 /etc/hostname,需清理换行符。

# Shell
clean_hostname=$(echo "$raw_hostname" | tr -d '\r\n')

# Python
clean_hostname = raw_hostname.strip()

# GO
hostName = strings.TrimSpace(rawHostName)

写代码时如需获取使用主机名,需显式清理换行符等特殊符号,否则可能会有意外之喜哦!


到目前为止根据现象master2在创建etcd server.crt、peer.crt证书时写入的CN和SAN中主机名带有换行符,导致无法通过主节点的证书验证加入集群,kubeadm join获取主机名使用os.Hostname函数从 /etc/hostname 中获取主机名,且做了特殊字符的处理,只需排查创建证书文件读取主机名没有处理换行符的原因。

查看源码:https://github.com/kubernetes/kubernetes/blob/v1.26.3/pkg/util/node/node.go

k8s master join 时etcd证书报错问题

kubeadm join 命令执行流程:

k8s master join 时etcd证书报错问题

在master2上删除有问题的etcd证书,使用kubeadm join手动执行加入命令,重新创建的证书没有问题,这个问题提单给产研已经三天了,由于他们太忙,也没啥时间认真看,最后给了部署工具的代码仓库权限给我,最后定位到问题就是 GetHostName 方法获取主机后只转换成了字符串,并没有做换行符处理,且生成etcd证书是部署工具做了处理,不是通过kubeadm生成的,主节点master1没问题,是因为处理逻辑不一样,这也解释了为什么master1证书没有问题,而master2和master3只要是加入的master证书都有这个问题。最后拉会找工具研发提了个case,优化 GetHostName 方法添加对特殊字符处理的逻辑编译打包了个新版本,重新部署后问题解决。至于他们说的之前没有碰到这种问题,我也不知道为啥,可能是操作系统原因吧~


参考:

anzhihe 安志合个人博客,版权所有 丨 如未注明,均为原创 丨 转载请注明转自:https://chegva.com/6359.html | ☆★★每天进步一点点,加油!★★☆ | 

您可能还感兴趣的文章!

发表评论

电子邮件地址不会被公开。 必填项已用*标注