对于访问kube-apiserver模块的请求来说,如果是使用http协议,则会顺利进入模块内部得到自己想要的;但是如果是用的是https,则能否进入模块内部获得想要的资源,他会首先要进行https自有的tls握手,进而进入kube-apiserver的三大控制,接下来,就让我一起研究下.....
一,对Kubernetes API访问的三大控制
- Authentication:认证,确认“你是不是你",包括多种方式,如 Client Certificates, Password, and Plain Tokens, Bootstrap Tokens, and JWT Tokens等
- Authorization:鉴权,确认“你是不是有权利做这件事”。怎样判定是否有权利,通过配置策略
- Admission Control:一个软件模块,经过配置,它可以访问正在创建或更新的对象的内容,即当已经"放行"的请求进入apiserver后开始真正操作资源的时候,对这份资源的控制。
参考:https://kubernetes.io/docs/reference/access-authn-authz/controlling-access/
API服务器在2个端口上都对外提供服务:
1,Localhost Port:默认8080端口,通过这个端口的请求会绕过认证和鉴权两个控制模块,一般用于集群内部的组件之间的交互。
2,Secure Port:默认6443端口,通过这个端口的请求会经过三大控制模块,用于对外提供服务。
关于认证,ApiServer主要的有三种方式如下:
1)Https双向认证,是双向认证啊,不是单向认证(最安全)。
2)Http Token认证
3)Http Base认证,用户名和密码
以上的认证方式是以模块的形式存在于apiserver中,可以通过配置支持多种认证,请求只要通过一个认证就算成功。
kubectl命令行工具既同时支持CA双向认证也支持简单认证(http base或者token)两种模式与apiserver进行通信,但其他组件只能配置成一种模式。
这里参考了:https://blog.csdn.net/xxb249/article/details/79449434
二,https协议的tls握手
只要你是通过Secure Port来方位api,那么你必然要遵守https协议要求的tls链接,这是一个怎样的交互呢?首先需要了解一个概念:数字证书,让我们细细道来
0,数字证书以及网络传输加密
假设,有两个人,一个叫 小起,一个叫 小终,小起想给小终发送数据,可是怕别人截取,篡改等等一些不安全的行为,于是经历了如下的演变过程,直到最后得到了最完美的加密过程。
stage1: 对称加密
小起 ----------将数据加密,并带着能解密的钥匙,一起交给小终 -----------> 小终
问题:传输的过程中,钥匙可能被盗
stage2:非对称加密
小终:自己有一个秘钥对,公钥:对外公开; 私钥:自己保留; 由公钥加密的数据只能由私钥解密,由私钥加密的内容只能由公钥解密
小起 ----------将数据用小终的公钥加密,交给小终 -----------> 小终(用小终自己的私钥解密)
问题:传输的过程中,有人将数据截获,搞一堆烂七八糟的内容重新用小终的公钥加密,再发给小终。
stage2进阶:签名技术的应用
利用hash算法不可逆的特性,将正文数据进行hash计算得到摘要,当小终收到正文后同样hash计算,如果二者相同说明数据是完整的。
同时,小起还要告诉小终,这个摘要是我计算的,可不是别人计算的的。
小起 -----------------1)就数据用小终的公钥加密; -------------------------------------------------------------------> 小终(用自己的公钥解密数据,用小起的公钥解密签名)
2)将数据用hash算法计算出摘要,并用小起的私钥对摘要进行加密(签名)
问题:有人冒充是小起给小终发数据
stage3:数字证书
前面说了公钥/私钥都是自己家搞出来的东西,就算是被仿造了也不知道,于是将公钥"贴在第三方出具的正式文件上"(我们所说的签发数字证书)。这样就显得有保障,因为这个正式文件上可是有第三方盖章的。但同样还要能够识别这个"章"是真正权威机构的。具体原理是:
小起,小终 <-----------从ca那里获取两样:根证书(“印章”图样),公钥(数字证书)私钥对---------CA中心(证书颁发机构)
小起 (用根证书校验小终的数字证书) <---------交换数字证书 -----------> 小终(用根证书校验小起的数字证书)
整合:对称加密结合非对称加密
考虑到效率问题,真正的业务数据是用对称加密算法加密的。所以综合以后,过程是这样的:
通过stage3,我们现在互相已经有了对方准确的公钥,然后通过stage2的数据交换协商出对称加密秘钥自己保存好,再用stage1的对称加密算法(此时不需要携带解密秘钥)加密数据
小起 --------- 用对称加密算法加密的数据-----------> 小终
1,tls的握手前的准备
所谓的握手,其实就是我们在上一节讲述的“对称加密秘钥”的协商过程,所以再协商之前双方要先去向CA机构发请求,获得数字证书和根证书,而这个CA机构可以是自己,只要在集群范围内大家认可就可以了,所以我们通过cfssl工具生成证书,当然也可以用openssl工具。
以下列出了步骤,有些细节可以参考这里:https://blog.csdn.net/xxb249/article/details/79449434
1)安装cfssl
curl -s -L -o /bin/cfssl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 curl -s -L -o /bin/cfssljson https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 curl -s -L -o /bin/cfssl-certinfo https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 chmod +x /bin/cfssl*
2)写根证书的配置文件
#mkdir -p /etc/kubernetes/apiserver/ #cd /etc/kubernetes/apiserver/ //利用命令先生成一个缺省的样板的请求文件 #cfssl print-defaults config > ca-config.json ---- #cfssl print-defaults csr > ca-csr.json ----修改配置,配置请求文件.csr应该是什么样子的
//根据自己的情况修改配置,ca-config.json中可以同时定义多个配置簇,一个配置簇对应一种类型的数字证书的各项参数
#vi ca-config.json
#vi ca-csr.json
我的配置文件供参考
[root@master test]# cat ca-config.json { "signing": { "default": { "expiry": "43800h" }, "profiles": { "client": { "expiry": "43800h", "usages": [ "signing", "key encipherment", "client auth" ] }, "server": { "expiry": "43800h", "usages": [ "signing", "key encipherment", "server auth", "client auth" ] } } } }
3)生成根正书
# cfssl gencert -initca ca-csr.json | cfssljson -bare ca - 2019/05/22 10:46:34 [INFO] generating a new CA key and certificate from CSR 2019/05/22 10:46:34 [INFO] generate received request 2019/05/22 10:46:34 [INFO] received CSR 2019/05/22 10:46:34 [INFO] generating key: rsa-2048 2019/05/22 10:46:35 [INFO] encoded CSR 2019/05/22 10:46:35 [INFO] signed certificate with serial number 357142409311435315855410100215094728480952302820 [root@master apiserver]# ll total 24 -rw-r--r-- 1 root root 833 May 22 10:39 ca-config.json -rw-r--r-- 1 root root 997 May 22 10:46 ca.csr ---根证书请求文件 -rw-r--r-- 1 root root 262 May 22 10:42 ca-csr.json -rw------- 1 root root 1679 May 22 10:46 ca-key.pem ---根证书对应的私钥 -rw-r--r-- 1 root root 1350 May 22 10:46 ca.pem ----根证书
4)签发数字证书,首先写好配置,然后签发
#cfssl print-defaults csr > server-csr.json # vi server-csr.json ---按情况修改
//-profile参数对应ca-config.json中的一个配置簇
# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=server server-csr.json | cfssljson -bare server
2019/05/30 19:37:53 [INFO] generate received request
2019/05/30 19:37:53 [INFO] received CSR
2019/05/30 19:37:53 [INFO] generating key: rsa-2048
2019/05/30 19:37:53 [INFO] encoded CSR
2019/05/30 19:37:53 [INFO] signed certificate with serial number 605959301321047408407639792005193883834034252126
2019/05/30 19:37:53 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for
websites. For more information see the Baseline Requirements for the Issuance and Management
of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org);
specifically, section 10.2.3 ("Information Requirements").
说明:在部署集群的时候会用到各种证书,有的文档会告诉你有server类型,peer类型,client类型......这对我来说反倒让我一脸懵逼,实际上CA机构才不管你什么类型的证书呢,我只管生成,至于你是用作服务端还是客户端还是什么的,那是你自己去配置,只不过一般情况下我们会根据使用场景生成的证书,会命名成服务端证书和客户端证书之类的。
接下来我们来看看这些证书在tls握手中怎么被使用的。
注:CA是个机构,该机构才能签发证书,根证书可以看做是CA的身份证明,但是我们在接下来的叙述中为了方便,就说成是根证书签发数字证书。
2,tls的握手
关于tlls握手的原理强烈推荐这个博客:https://baijiahao.baidu.com/s?id=1616211978225668389&wfr=spider&for=pc
简述之:
1,客-->服:客户端打招呼,我要访问https:服务器
2,服-->客:服务器把自己的数字证书(公钥)发给客户端
[如果使能了客户端认证]服务器端还会向客户端发请求,告知想要客户的证书
《《《此时客户端拿到了服务端的公钥,如果使能了"跳过证书认证"则就这么着了,否则还要用根证书对该证书进行校验》》》
3,客-->服[如果服务端有请求证书]:客户端同样把自己的数字证书(公钥)发给服务器端
《《《此时服务端拿到了客户端的公钥,然后用根证书对该证书进行校验》》》
小结:在这里面涉及到的证书及关系为:服务器端的数字证书(对)以及签发该证书的根证书;客户端的数字证书(对)以及签发该证书的根证书;
所以当一个app作为服务器的时候,它必须要准备好自己的证书(对)和 客户端使用的根证书
当客户端访问的时候,客户端也必须要准备自己的证书(对) 和 服务器端使用的根证书
好了,讲解到这里,我们去研究下kube-apiserver中是怎么配置这些的,就变得很容易了。
三,api server上有关证书的配置
1, tls协议使用的证书
--tls-cert-file=/run/ssl/server.pem
--tls-private-key-file=/run/ssl/server-key.pem
前面tls握手的时候有说,作为服务器端,必须要要准备好自己的证书(对),所以这两个参数就是指定了证书的路径。但是也可以不指定,缺省情况下会自动生成一对证书,因为这是https协议最基本的要求,你配不配置我都要有的。
自动生成的证书(对)缺省位于: 容器方式安装:位于容器的/run/kubernetes目录下,
主机直接安装,位于主机的/var/run/kubernetes目录下
当然可以用--cert-dir参数变更,以下我的环境,为容器方式安装:
# pwd /run/kubernetes # ls apiserver.crt apiserver.key
2,认证方式一:双向认证
其实说到这里,我们一直没有提是怎么实现api server中三大控制的,接下来我们说的就是认证中的 双向认证:Client Certificates 的使能。
--client-ca-file
这个参数的含义是指定客户端使用的根证书的路径。一旦设置了,那么你在访问api的时候一定得带上使用该根证书签发的公钥/私钥对,例如:
//启动时,指定客户端的根证书位于容器的/run/ssl/client目录下,对应host的/etc/kubernetes/apiserver/client目录下
docker run -d --name=apiserver --net=host \ -v /etc/kubernetes/apiserver:/run/ssl \ k8s.gcr.io/kube-apiserver:v1.13.6-beta.0.39_ddd2add0dd3dbc \ kube-apiserver \ --insecure-bind-address=0.0.0.0 \ --service-cluster-ip-range=11.0.0.0/16 \ --client-ca-file=/run/ssl/client/ca.pem \ --etcd-servers=http://localhost:2379
//那么这里配置的证书和私钥,就要和启动时配置的根证书有关了,他们正是我用cfssl工具生成的一组证书,成套的....另外 -k 表示我跳过对服务端的证书检查,详见"tls的握手"章节
[root@master apiserver]# curl -k https://188.x.x.113:6443 --cert /etc/kubernetes/client/client.pem --key /etc/kubernetes/client/client-key.pem { "paths": [ "/api", "/api/v1" ...
3,认证方式二:Base认证
所谓base认证,很好理解:我先在服务器系统中写个名单,上面列一堆:用户名,密码,客户端访问的时候要带着用户名和密码,服务器一检查,是我名单里的,即通过认证,具体配置方式如下:
--basic-auth-file
用这个参数指定名单的路径
#vi basic_auth_file.csv admin,admin,1 wxy,wxy,2
#docker run -d --name=apiserver --net=host \
-v /etc/kubernetes/apiserver:/run/ssl \
k8s.gcr.io/kube-apiserver:v1.13.6-beta.0.39_ddd2add0dd3dbc \
kube-apiserver \
--insecure-bind-address=0.0.0.0 \
--service-cluster-ip-range=11.0.0.0/16 \ --basic-auth-file=/run/ssl/basic_auth_file.csv \ --etcd-servers=http://localhost:2379
#kubectl --server="https://localhost:6443" --insecure-skip-tls-verify=true --username="admin" --password="admin" get pods
3,认证方式三:token认证
在下一节中有用到
附【坑】k8s的官网上说:
Authentication modules include Client Certificates, Password, and Plain Tokens, Bootstrap Tokens, and JWT Tokens (used for service accounts).
Multiple authentication modules can be specified, in which case each one is tried in sequence, until one of them succeeds.
--basic-auth-file string
If set, the file that will be used to admit requests to the secure port of the API server via http basic authentication.
--client-ca-file string
If set, any request presenting a client certificate signed by one of the authorities in the client-ca-file is authenticated with an identity corresponding to the CommonName of the client certificate.
所以我以为各种认证模块是通过配置开启的,当我没有配置client-ca-file和basic-auth-file时,我就是没有开启认证,所以我用如下命令访问不应该报错的:
[root@master ~]# curl -k https://188.x.x.113:6443
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {
},
"status": "Failure",
"message": "Unauthorized",
"reason": "Unauthorized",
"code": 401
}
但实际的验证的结果是:你在部署你的集群是,至少满足一种认证方式。我使用的kube-apiserver的版本是kubernetes v1.13.5的自己编译。如果这里我理解有误,一定告诉我,谢谢!
三,kube-controller-manager上有关证书的配置&service account
前面说了,kube-apiserver对于不是集群组件,一般暴露的是Secure Port,即使用带有认证的https。那么除了自然人通过curl或者kubectl这种要带着证书或者账号密码或者token外,pod也想访问kube-apiserver怎么办?它怎么携带证书呢?
答:集群为每个namespace指定一个 service account ,叫做服务账号。你可以把它想象成一个名叫"default"的人的账号,pod就是以这个身份去访问api的。具体实现如下:
1,在启动kube-controller-manager时指定如下参数
--service-account-private-key-file
该参数表示的含义是私钥的路径,它的作用是给服务账号产生token,之后pod就可以拿着这个token去访问api server了。
--root-ca-file
该参数会给服务账号一个根证书ca.crt,可选配置,如果配置成给api server签发证书的那个根证书,那就可以拿来用于认证api server。
docker run -d --name=cm --net=host \ -v /etc/kubernetes/apiserver:/run/ssl \ k8s.gcr.io/kube-controller-manager:v1.13.6-beta.0.39_ddd2add0dd3dbc \ kube-controller-manager \ --master=0.0.0.0:8080 \ --service-account-private-key-file=/run/ssl/server-key.pem \
--root-ca-file=/run/ssl/ca.pem
2,在启动kube-apiserver时配置
--service-account-key-file
该参数表示的含义是公钥的路径,它与上面的--service-account-private-key-file是对应关系,因为pod带着token去访问api server,则api server要能解密才行啊,所以同时还需要在api那里配置当然你如果不配置,不影响pod创建,只不过你在pod里访问api的时候就不行了。
docker run -d --name=apiserver --net=host \ -v /etc/kubernetes/apiserver:/run/ssl \ k8s.gcr.io/kube-apiserver:v1.13.6-beta.0.39_ddd2add0dd3dbc \ kube-apiserver \ --insecure-bind-address=0.0.0.0 \ --service-cluster-ip-range=11.0.0.0/16 \ --service-account-key-file=/run/ssl/server.pem \ --etcd-servers=http://localhost:2379
3,启动后,会在各个namespace下生成一个secret
[root@master ~]# kubectl get secret --all-namespaces NAMESPACE NAME TYPE DATA AGE default default-token-x221b kubernetes.io/service-account-token 2 59m kube-public default-token-zx79k kubernetes.io/service-account-token 3 3d3h kube-system default-token-9fbtm kubernetes.io/service-account-token 3 3d3h
4,此时创建pod,可以看到pod把secret的内容挂载到了自己的/var/run/secrets/kubernetes.io/serviceaccount目录下
[root@master ~]# kubectl create -f centos.yaml pod/myapp-centos created [root@master ~]# kubectl get pods NAME READY STATUS RESTARTS AGE myapp-centos 1/1 Running 0 6s [root@master ~]# kubectl get pods myapp-centos -oyaml ... volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount name: default-token-x22lb readOnly: true ... volumes: - name: default-token-x22lb secret: defaultMode: 420 secretName: default-token-x22lb [root@master ~]# kubectl exec -ti myapp-centos /bin/sh sh-4.2# ls /var/run/secrets/kubernetes.io/serviceaccount
sh-4.2# ls
ca.crt namespace token
5,实操验证,带着生成的token可以访问api了
[root@master ~]# kubectl exec -ti myapp-centos /bin/sh sh-4.2# cd /var/run/secrets/kubernetes.io/serviceaccount/ sh-4.2# token=$(cat ./token) sh-4.2# curl https://188.131.210.113:6443 --header "Authorization: Bearer $token" --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt { "paths": [ "/api", "/api/v1"...
结束:
认证这块之前一直不敢碰,感觉好难,但是一路走下来突然发现好像也没什么,总结起来就是:
1,https使用的tls协议,其实就是常说的非对称加密算法的应用,说到加密就得用加密物料,于是要明白数字证书的概念
2,数字证书,根据字面意思他就是一个证书,由第三方大家都认可的机构:CA盖章颁发,证书上面有公钥和CA签名。其存在的价值是保证上面的公钥是官方有效的。
同时CA机构还有一个根证书,也叫CA证书,用来验证数字证书的真伪。每一个人都要去CA那里把根证书提前下载到自己的系统里,以备使用。
3,说到公钥,就不得不提非对称加密算法的原理:
每个人都有一对钥匙:公钥和私钥,公钥加密的数据只能被私钥解密,私钥加密的数据只能被公钥解密;
私钥自己留着,公钥用数字证书的方式可以发给任何人;
于是,这个巧妙的原理就实现了非对称加密算法;
水鬼子:不知道为什么,我就是特别喜欢这个原理,感觉很简单又很有巧思,像按压笔一样,我觉得真是人类伟大的发明。
4,说了这么多都是为访问api server服务,就是说任何请求都不能随随便便请求我,总得带上你的身份证明,是我认可的才行;
所以,api还是manage组件的各项和证书相关的配置就出现了
》》》》》》》》》》》》》》》》》》》》END《《《《《《《《《《《《《《《《《《《《《《《《