kubelet的功能复杂,本文记录下它的边角功能--kubelet中的认证和授权
会包括以下几个方面的问题:
-
kubelet作为服务端堆外暴露的10250端口怎么认证和授权的
-
kubelet的只读端口是怎么工作的
-
kubelet作为客户端怎么访问kube-apiserver的
-
kubelet访问kube-apiserver的证书到期后怎么自动更新的
启动参数
kubelet的启动参数很多,通常的启动配置:
/usr/bin/kubelet
--node-ip=1.1.1.1
--hostname-override=1.1.1.1
--address=0.0.0.0
--anonymous-auth=true
--authentication-token-webhook=true
--authorization-mode=Webhook
--bootstrap-kubeconfig=/etc/kubernetes/bootstrap.kubeconfig
--client-ca-file=/etc/kubernetes/ssl/ca.pem
--network-plugin=cni
--cgroup-driver=systemd
--cert-dir=/etc/kubernetes/ssl
--cluster-dns=10.254.0.2
--cluster-domain=cluster.local
--cni-conf-dir=/etc/cni/net.d
--eviction-soft=imagefs.available<12%,memory.available<512Mi,nodefs.available<12%,nodefs.inodesFree<10%
--eviction-soft-grace-period=imagefs.available=3m,memory.available=1m,nodefs.available=3m,nodefs.inodesFree=1m
--eviction-hard=imagefs.available<10%,memory.available<256Mi,nodefs.available<10%,nodefs.inodesFree<5%
--eviction-max-pod-grace-period=30
--image-gc-high-threshold=80
--image-gc-low-threshold=70
--image-pull-progress-deadline=30s
--kube-reserved=cpu=500m,memory=512Mi,ephemeral-storage=1Gi
--kubeconfig=/etc/kubernetes/kubelet.kubeconfig
--resolv-conf=/etc/kubernetes/resolv.conf
--max-pods=200
--minimum-image-ttl-duration=720h0m0s
--node-labels=node.kubernetes.io/k8s-node=true
--pod-infra-container-image=myharbor.com:80/library/pause-amd64:3.1
--port=10250
--read-only-port=0
--root-dir=/data/k8s/kubelet
--rotate-certificates
--rotate-server-certificates
--system-reserved=cpu=500m,memory=1257Mi,ephemeral-storage=1Gi
--fail-swap-on=false
--logtostderr=false
--log-dir=/data/logs/kubernetes/
--v=4
kubelet服务器端证书
在启动参数中可以看到没有常见的HTTPS服务器配置证书的参数
--tls-cert-file=path_to_pem \
--tls-private-key-file=path_to_key \
这是因为kubelet实现了服务端证书的自动刷新。我们先来看下服务端证书是怎么自动刷新的:
func InitializeTLS(kf *options.KubeletFlags, kc *kubeletconfiginternal.KubeletConfiguration) (*server.TLSOptions, error) {
// 是否配置了启动参数 --rotate-server-certificates、--tls-cert-file、--tls-private-key-file
// 我们一般是会配置服务端证书自动刷新的
if !kc.ServerTLSBootstrap && kc.TLSCertFile == "" && kc.TLSPrivateKeyFile == "" {
...
}
...
minTLSVersion, err := cliflag.TLSVersion(kc.TLSMinVersion)
...
tlsOptions := &server.TLSOptions{
Config: &tls.Config{
MinVersion: minTLSVersion,
CipherSuites: tlsCipherSuites,
},
CertFile: kc.TLSCertFile,
KeyFile: kc.TLSPrivateKeyFile,
}
// 是否配置了启动参数 --client-ca-file=/etc/kubernetes/ssl/ca.pem
// 配置一个CA证书是为了收到请求时来验证请求证书
if len(kc.Authentication.X509.ClientCAFile) > 0 {
clientCAs, err := certutil.NewPool(kc.Authentication.X509.ClientCAFile)
...
// Specify allowed CAs for client certificates
tlsOptions.Config.ClientCAs = clientCAs
// Populate PeerCertificates in requests, but don't reject connections without verified certificates
tlsOptions.Config.ClientAuth = tls.RequestClientCert
}
return tlsOptions, nil
}
可见我们如果开启了服务端证书的自动更新,就不需要提供证书对了,那HTTPS服务器的证书是从哪里获取的呢?
// 是否配置了启动参数 --rotate-server-certificates;而且RotateKubeletServerCertificate这个特性再1.12版本就beta了,默认开启了
if kubeCfg.ServerTLSBootstrap && kubeDeps.TLSOptions != nil && utilfeature.DefaultFeatureGate.Enabled(features.RotateKubeletServerCertificate) {
// 先创建一个证书Manager
klet.serverCertificateManager, err = kubeletcertificate.NewKubeletServerCertificateManager(klet.kubeClient, kubeCfg, klet.nodeName, klet.getLastObservedNodeAddresses, certDirectory)
// 由于上面TLSOptions中没有指定证书对的,因此我们需要指定GetCertificate方法,用于启动HTTPS服务器的时候获取证书对
// 可以看到这里返回的正式对就是从 证书Manager 中获取相关证书
kubeDeps.TLSOptions.Config.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
cert := klet.serverCertificateManager.Current()
if cert == nil {
return nil, fmt.Errorf("no serving certificate available for the kubelet")
}
return cert, nil
}
}
证书Maager 的Current()
方法就是简单的校验证书的有效性,证书有效即返回,相当于我们shell命令获取证书有效期openssl x509 -in example.crt -noout -enddate
func (m *manager) Current() *tls.Certificate {
m.certAccessLock.RLock()
defer m.certAccessLock.RUnlock()
if m.cert != nil && m.cert.Leaf != nil && m.now().After(m.cert.Leaf.NotAfter) {
m.logf("%s: Current certificate is expired", m.name)
return nil
}
return m.cert
}
那么证书Maager 中的 cert
是啥时候生成的呢?
在kubelet的启动代码中有如下的代码,启动证书Manager
func (kl *Kubelet) initializeModules() error {
...
// Start the certificate manager if it was enabled.
if kl.serverCertificateManager != nil {
kl.serverCertificateManager.Start()
}
...
return nil
}
那启动证书Manager它会干啥呢,它就是主要负责生成证书的,通过检测证书的有效期,向kube-apiserver发送CSR请求,等待kube-apiserver Approve请求后,将证书写入到文件系统、更新到缓存
func (m *manager) Start() {
...
templateChanged := make(chan struct{})
go wait.Until(func() {
// BackOff方式刷新证书
if err := wait.ExponentialBackoff(backoff, m.rotateCerts); err != nil {
utilruntime.HandleError(fmt.Errorf("%s: Reached backoff limit, still unable to rotate certs: %v", m.name, err))
wait.PollInfinite(32*time.Second, m.rotateCerts)
}
}, time.Second, m.stopCh)
...
}
func (m *manager) rotateCerts() (bool, error) {
// 生成证书请求文件
template, csrPEM, keyPEM, privateKey, err := m.generateCSR()
...
// kube-apiserver的请求客户端
clientSet, err := m.getClientset()
...
// 构建 CertificateSigningRequest资源, 请求kube-apiserver创建资源
reqName, reqUID, err := csr.RequestCertificate(clientSet, csrPEM, "", m.signerName, m.requestedCertificateLifetime, usages, privateKey)
...
// 等待kube-apiserver Approve证书请求
crtPEM, err := csr.WaitForCertificate(ctx, clientSet, reqName, reqUID)
...
// 将证书和KEY 写入到文件系统 /etc/kubernetes/ssl/kubelet-server-2022-03-11-15-01-21.pem
// 创建文件 /etc/kubernetes/ssl/kubelet-server-current.pem 链接到上面的pem文件
// 其中目录 /etc/kubernetes/ssl 是通过 --cert-dir 启动参数指定的
cert, err := m.certStore.Update(crtPEM, keyPEM)
...
// 将证书更新到缓存中
if old := m.updateCached(cert); old != nil && m.certificateRotation != nil {
m.certificateRotation.Observe(m.now().Sub(old.Leaf.NotBefore).Seconds())
}
return true, nil
}
kubelet服务端启动
在启动参数中我们提供了服务端的监听地址为0.0.0.0:10250
;以及是否需要启用只读端口,read-only-port=0
表示不启用
--address=0.0.0.0
--port=10250
--read-only-port=0
然后再kubelet启动的时候会判断我们是否需要启动HTTPS服务器以及是否启动只读端口监听
func startKubelet(k kubelet.Bootstrap, podCfg *config.PodConfig, kubeCfg *kubeletconfiginternal.KubeletConfiguration, kubeDeps *kubelet.Dependencies, enableServer bool) {
...
// start the kubelet server
if enableServer {
go k.ListenAndServe(kubeCfg, kubeDeps.TLSOptions, kubeDeps.Auth, kubeDeps.TracerProvider)
}
if kubeCfg.ReadOnlyPort > 0 {
go k.ListenAndServeReadOnly(netutils.ParseIPSloppy(kubeCfg.Address), uint(kubeCfg.ReadOnlyPort))
}
...
}
启动HTTPS服务器
使用的TLS配置就是之前初始化出来的tlsOptions,虽然没有指定证书和私钥,但是指定了GetCertificate 方法用于获取证书,而获取到的证书就是证书Manager 更新出来的
func ListenAndServeKubeletServer(
host HostInterface,
resourceAnalyzer stats.ResourceAnalyzer,
kubeCfg *kubeletconfiginternal.KubeletConfiguration,
tlsOptions *TLSOptions,
auth AuthInterface,
tp oteltrace.TracerProvider) {
address := netutils.ParseIPSloppy(kubeCfg.Address)
port := uint(kubeCfg.Port)
klog.InfoS("Starting to listen", "address", address, "port", port)
handler := NewServer(host, resourceAnalyzer, auth, tp, kubeCfg)
s := &http.Server{
Addr: net.JoinHostPort(address.String(), strconv.FormatUint(uint64(port), 10)),
Handler: &handler,
IdleTimeout: 90 * time.Second, // matches http.DefaultTransport keep-alive timeout
ReadTimeout: 4 * 60 * time.Minute,
WriteTimeout: 4 * 60 * time.Minute,
MaxHeaderBytes: 1 << 20,
}
if tlsOptions != nil {
s.TLSConfig = tlsOptions.Config
// Passing empty strings as the cert and key files means no
// cert/keys are specified and GetCertificate in the TLSConfig
// should be called instead.
if err := s.ListenAndServeTLS(tlsOptions.CertFile, tlsOptions.KeyFile); err != nil {
klog.ErrorS(err, "Failed to listen and serve")
os.Exit(1)
}
} else if err := s.ListenAndServe(); err != nil {
klog.ErrorS(err, "Failed to listen and serve")
os.Exit(1)
}
}
启动只读服务器
func ListenAndServeKubeletReadOnlyServer(
host HostInterface,
resourceAnalyzer stats.ResourceAnalyzer,
address net.IP,
port uint) {
klog.InfoS("Starting to listen read-only", "address", address, "port", port)
// TODO: https://github.com/kubernetes/kubernetes/issues/109829 tracer should use WithPublicEndpoint
s := NewServer(host, resourceAnalyzer, nil, oteltrace.NewNoopTracerProvider(), nil)
server := &http.Server{
Addr: net.JoinHostPort(address.String(), strconv.FormatUint(uint64(port), 10)),
Handler: &s,
IdleTimeout: 90 * time.Second, // matches http.DefaultTransport keep-alive timeout
ReadTimeout: 4 * 60 * time.Minute,
WriteTimeout: 4 * 60 * time.Minute,
MaxHeaderBytes: 1 << 20,
}
if err := server.ListenAndServe(); err != nil {
os.Exit(1)
}
}
只读服务器和HTTPS服务器的区别在于,提供的是HTTP服务,然后指定了auth=nil
即不启用认证和授权;
HTTPS服务器的认证和授权
一般情况下,由于只读服务器提供的是HTTP服务,并且未开启认证,所以通常情况下处于安全考虑,我们会关闭只读端口;那HTTPS服务器是怎么认证和授权的呢?
func BuildAuth(nodeName types.NodeName, client clientset.Interface, config kubeletconfig.KubeletConfiguration) (server.AuthInterface, func(<-chan struct{}), error) {
// Get clients, if provided
var (
tokenClient authenticationclient.AuthenticationV1Interface
sarClient authorizationclient.AuthorizationV1Interface
)
// 使用的是kube-apiserver的认证和授权API
if client != nil && !reflect.ValueOf(client).IsNil() {
tokenClient = client.AuthenticationV1()
sarClient = client.AuthorizationV1()
}
// 构建认证规则
authenticator, runAuthenticatorCAReload, err := BuildAuthn(tokenClient, config.Authentication)
...
attributes := server.NewNodeAuthorizerAttributesGetter(nodeName)
// 构建授权规则
authorizer, err := BuildAuthz(sarClient, config.Authorization)
...
return server.NewKubeletAuth(authenticator, attributes, authorizer), runAuthenticatorCAReload, nil
}
可以看到认证和授权主要依赖kube-apiserver
的认证和授权API,具体的认证和授权有哪些方式呢?
认证
func BuildAuthn(client authenticationclient.AuthenticationV1Interface, authn kubeletconfig.KubeletAuthentication) (authenticator.Request, func(<-chan struct{}), error) {
var dynamicCAContentFromFile *dynamiccertificates.DynamicFileCAContent
var err error
// 启动参数 --client-ca-file=/etc/kubernetes/ssl/ca.pem;第一种认证方式是通过证书访问,证书是通过这个CA证书签发的
if len(authn.X509.ClientCAFile) > 0 {
dynamicCAContentFromFile, err = dynamiccertificates.NewDynamicCAContentFromFile("client-ca-bundle", authn.X509.ClientCAFile)
...
}
authenticatorConfig := authenticatorfactory.DelegatingAuthenticatorConfig{
// 启动参数--anonymous-auth=true,启用匿名用户,即所有请求都能够通过认证
Anonymous: authn.Anonymous.Enabled,
CacheTTL: authn.Webhook.CacheTTL.Duration,
ClientCertificateCAContentProvider: dynamicCAContentFromFile,
}
// 启动参数--authentication-token-webhook=true,通过kube-apiserver请求TokenAccessReview资源,验证Token是否合法
if authn.Webhook.Enabled {
...
authenticatorConfig.WebhookRetryBackoff = genericoptions.DefaultAuthWebhookRetryBackoff()
authenticatorConfig.TokenAccessReviewClient = client
}
authenticator, _, err := authenticatorConfig.New()
...
return authenticator, ..., err
}
可以看到kubelet支持三种认证方式:
-
携带证书访问,证书是通过
--client-ca-file
签发的,那么证书种的CN 和 O 会作为用户和组信息去授权 -
携带Token访问,kubelet会像kube-apiserver发起TokenAccessReview请求,验证Token的合法性;当然为了避免每次都要去请求kube-apiserver,kubelet会对Token做缓存
-
啥也不带,匿名用户访问
授权
func BuildAuthz(client authorizationclient.AuthorizationV1Interface, authz kubeletconfig.KubeletAuthorization) (authorizer.Authorizer, error) {
// 启动参数 --authorization-mode=Webhook
switch authz.Mode {
...
case kubeletconfig.KubeletAuthorizationModeWebhook:
...
// Webhook 授权模式,请求kube-apiserver的SubjectAccessReview资源,查看用户是否具有权限
authorizerConfig := authorizerfactory.DelegatingAuthorizerConfig{
SubjectAccessReviewClient: client,
AllowCacheTTL: authz.Webhook.CacheAuthorizedTTL.Duration,
DenyCacheTTL: authz.Webhook.CacheUnauthorizedTTL.Duration,
WebhookRetryBackoff: genericoptions.DefaultAuthWebhookRetryBackoff(),
}
return authorizerConfig.New()
...
}
}
可以看到kubelet种授权主要是通过向kube-apiserver创建SubjectAccessReview资源,该资源会把请求方法、请求资源路径、用户信息、组信息打包到一起发送给kube-apiserver,由kube-apiserver判定是否有权限
认证和授权都看完了,这就意味着我们可以向请求kube-apiserver一样的请求kubelet的HTTPS服务端,仅限于证书和Token这两种方式,因为kubelet没有支持其它的kube-apiserver种的认证和授权方式。
kubelet服务端暴露的端点
认证和授权看完后,我们再看下kubelet暴露了那些HTTP端点
func NewServer(
host HostInterface,
resourceAnalyzer stats.ResourceAnalyzer,
auth AuthInterface,
tp oteltrace.TracerProvider,
kubeCfg *kubeletconfiginternal.KubeletConfiguration) Server {
server := Server{
host: host,
resourceAnalyzer: resourceAnalyzer,
auth: auth,
restfulCont: &filteringContainer{Container: restful.NewContainer()},
metricsBuckets: sets.NewString(),
metricsMethodBuckets: sets.NewString("OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT"),
}
// 服务端是否需要启用认证和授权,只读端口由于指定了auth=nil,所以不会认证和授权;但HTTPS服务端会启用
if auth != nil {
server.InstallAuthFilter()
}
...
server.InstallDefaultHandlers()
...
return server
}
func (s *Server) InstallDefaultHandlers() {
s.addMetricsBucketMatcher("healthz")
// 端点 /healthz 检查kubelet健康状况
healthz.InstallHandler(s.restfulCont,
healthz.PingHealthz,
healthz.LogHealthz,
healthz.NamedCheck("syncloop", s.syncLoopHealthCheck),
)
...
// 端点 /pods 获取kubelet所在节点上的所有POD
s.addMetricsBucketMatcher("pods")
ws := new(restful.WebService)
ws.
Path("/pods").
Produces(restful.MIME_JSON)
ws.Route(ws.GET("").
To(s.getPods).
Operation("getPods"))
s.restfulCont.Add(ws)
// 端点 /stats/summary 获取kubelet所在节点CPU、内存、POD信息
s.addMetricsBucketMatcher("stats")
s.restfulCont.Add(stats.CreateHandlers(statsPath, s.host, s.resourceAnalyzer))
// 端点 /metrics 获取指标数据
s.addMetricsBucketMatcher("metrics")
s.addMetricsBucketMatcher("metrics/cadvisor")
s.addMetricsBucketMatcher("metrics/probes")
s.addMetricsBucketMatcher("metrics/resource")
s.restfulCont.Handle(metricsPath, legacyregistry.Handler())
...
...
}
可以看到,kubelet作为HTTPS服务端所提供的端口很少,都是些功能性的API
-
/healthz:检测健康状态,会检查kubelet的日志有没有正常在刷(2min)、kubelet的主功能syncLoop有没有在正常执行(5min)
- /healthz/log: 单独检查日志是否正常在刷的健康状态
- /healthz/syncloop : 也是单独检查syncloop的健康状态
-
/pods :获取节点的POD,通过kube-apiserver指定 spec.nodeName=当前节点获取
-
/stats/summary: 获取kubelet所在节点CPU、内存、POD信息,通过cadvisor获取
-
/metrics: 获取指标数据,通过Prometheus获取
结合认证和和授权,我们调用这些端点试下,这里使用的token就是我们请求kube-apiserver所使用的token:
curl -k -H 'Authorization: Bearer <token>' https://1.1.1.1:10250/healthz
> ok
curl -k -H 'Authorization: Bearer <token>' https://1.1.1.1:10250/pods
> xxxxxx一大串
kubelet访问kube-apiserver
上面介绍的kubelet的这些功能都有个前提条件是,kubelet能够正常访问kube-apiserver,那kubelet是怎么访问kube-apiserver的呢?
func buildKubeletClientConfig(ctx context.Context, s *options.KubeletServer, tp oteltrace.TracerProvider, nodeName types.NodeName) (*restclient.Config, func(), error) {
// 启动参数--rotate-certificates,指定是否需要自动刷新访问kube-apiserver的证书
if s.RotateCertificates {
...
// 启动参数--kubeconfig=/etc/kubernetes/kubelet.kubeconfig,指定了kubelet访问kube-apiserver的kubeconfig文件
// 启动参数--bootstrap-kubeconfig=/etc/kubernetes/bootstrap.kubeconfig, 指定了证书到期后通过这个bootstrap文件去刷新证书
certConfig, clientConfig, err := bootstrap.LoadClientConfig(s.KubeConfig, s.BootstrapKubeconfig, s.CertDirectory)
...
// 也是创建证书Manager,跟服务端的证书刷新是同一个原理
clientCertificateManager, err := buildClientCertificateManager(certConfig, clientConfig, s.CertDirectory, nodeName)
...
clientCertificateManager.Start()
return transportConfig, onHeartbeatFailure, nil
}
...
}
func LoadClientConfig(kubeconfigPath, bootstrapPath, certDir string) (certConfig, userConfig *restclient.Config, err error) {
...
store, err := certificate.NewFileStore("kubelet-client", certDir, certDir, "", "")
...
// --kubeconfig=/etc/kubernetes/kubelet.kubeconfig指向的kubeconfig文件是否存在,已经文件里的证书是否还有效
ok, err := isClientConfigStillValid(kubeconfigPath)
...
// 证书仍然有效,则继续使用这个证书
if ok {
clientConfig, err := loadRESTClientConfig(kubeconfigPath)
...
}
// 证书已经失效了,使用bootstrap去请求新的证书文件
bootstrapClientConfig, err := loadRESTClientConfig(bootstrapPath)
...
...
}
可以看到kubelet访问kube-apiserver也是依赖kubeconfig文件,如果我们指定了一个证书仍然有效的kubeconfig文件,那么将会直接使用这个kubeconfig文件去访问kube-apiserver;
如果我们没有指定或者指定的kubeconfig文件已经到期了,那么在启用了客户端证书自动刷新的前提下,会通过我们指定bootstrap文件去获取有效的证书,然后将证书保存到kubeconfig文件中;
bootstrap获取证书
那么bootstrap是怎么获取证书的呢?一般情况下,我们的bootstrap文件长这样
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: base64_data
server: https://1.1.1.1:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: system:bootstrap:911963
name: default
current-context: default
kind: Config
preferences: {}
users:
- name: system:bootstrap:911963
user:
token: 911963.7c55ce1710186e66
而kube-apiserver也是支持bootstrap token这种认证方式的,在kube-apiserver的代码中可以找到它的踪迹
// kube-apiserver的启动参数 --enable-bootstrap-token-auth
if authenticatorConfig.BootstrapToken {
authenticatorConfig.BootstrapTokenAuthenticator = bootstrap.NewTokenAuthenticator(
versionedInformer.Core().V1().Secrets().Lister().Secrets(metav1.NamespaceSystem),
)
}
// kube-apiserver的启动参数 --enable-bootstrap-token-auth,上述也设置了BootstrapTokenAuthenticator ;因此这里会添加一个认证器
if config.BootstrapToken && config.BootstrapTokenAuthenticator != nil {
tokenAuthenticators = append(tokenAuthenticators, authenticator.WrapAudienceAgnosticToken(config.APIAudiences, config.BootstrapTokenAuthenticator))
}
// 这个认证器只是简单的包装了一下BootstrapTokenAuthenticator ,还得看BootstrapTokenAuthenticator 是怎么认证token的
func (a *audAgnosticTokenAuthenticator) AuthenticateToken(ctx context.Context, tok string) (*Response, bool, error) {
return authenticate(ctx, a.implicit, func() (*Response, bool, error) {
return a.delegate.AuthenticateToken(ctx, tok)
})
}
func (t *TokenAuthenticator) AuthenticateToken(ctx context.Context, token string) (*authenticator.Response, bool, error) {
// 根据token=911963.7c55ce1710186e66,找到用小数点划分的两部分
tokenID, tokenSecret, err := bootstraptokenutil.ParseToken(token)
...
// 在kube-system命名空间下存在对应的Secret,类型必须是 bootstrap.kubernetes.io/token
// bootstrap-token-911963 bootstrap.kubernetes.io/token 6
secretName := "bootstrap-token-" + tokenID
secret, err := t.lister.Get(secretName)
...
...
// 这个Secret中记录了 7c55ce1710186e66 这一串数据,token中的数据需要和这个一致才能通过认证
ts := bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenSecretKey)
if subtle.ConstantTimeCompare([]byte(ts), []byte(tokenSecret)) != 1 {
...
}
// 同样这个Secret中也记录了过期时间,需要在有效期内;过期时间置空表示永不过期
if bootstrapsecretutil.HasExpired(secret, time.Now()) {
...
}
// 认证完成后,找到这个token对应的用户和组信息,默认的组为:system:bootstrappers
groups, err := bootstrapsecretutil.GetGroups(secret)
...
// 这里再后面授权的时候主要就是依靠组信息,用户信息不重要
return &authenticator.Response{
User: &user.DefaultInfo{
Name: bootstrapapi.BootstrapUserPrefix + string(id),
Groups: groups,
},
}, true, nil
}
// 最后通过RBAC授权的时候,集群中一般会存在这么个 clusterrolebinding;它关联了用户组system:bootstrappers的权限
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:node-bootstrapper
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: system:bootstrappers
// 而这个组的权限对应的clusterrole,可以看到就是CSR请求的创建、查询、WATCH,所以kubelet才能够通过bootstrap token申请证书
rules:
- apiGroups:
- certificates.k8s.io
resources:
- certificatesigningrequests
verbs:
- create
- get
- list
- watch
可以看到bootstrap token申请证书主要依赖kube-apiserver的bootstrap token认证,以及所属组system:bootstrappers默认就有CSR证书请求的权限
客户端证书怎么刷新
前面已经了解过了,证书的刷新仍然是由证书Manager完成,刷新过程和服务端证书的刷新是一致的
总结
通过这个文章,大致摸清楚了这些小问题:
-
kubelet作为服务端堆外暴露的10250端口怎么认证和授权的
-
kubelet的只读端口是怎么工作的
-
kubelet作为客户端怎么访问kube-apiserver的
-
kubelet访问kube-apiserver的证书到期后怎么自动更新的
网友评论