OAuth
是一种授权机制。数据的所有者告诉系统,同意授权第三方应用进入系统,获取这些数据。系统从而产生一个短期的进入令牌token
,用来代替密码,供第三方应用使用。
OAuth 2.0
是用于授权的行业标准协议。OAuth 2.0
致力于简化客户端开发人员的工作,同时为Web
应用程序,桌面应用程序,移动电话和客厅设备提供特定的授权流程。
OAuth 2.0
是目前最流行的授权机制,用来授权第三方应用,获取用户数据。
很多情况下,许多应用程序不提供内置的身份验证或开箱即用的访问控制。由于这些应用程序处理的敏感数据,这可能是一个主要问题,通常有必要提供某种类型的安全性。基于k8s
部署的一些服务,并没有自身的访问认证控制机制。例如我们部署一个用于公司内部使用的web
应用,又不想做基于统一账号SSO
的认证功能的开发,但是又想在用户访问时加上一层认证功能。这类情况的解决思路一般是在访问入口,例如Ingress
上添加一层访问认证,可以借助于basic auth
实现此功能,但basic auth
存在过于简单、账号权限不好控制、需要手动维护等诸多问题。
于是另外一种相对更为成功的解决办法是使Ingress
通过OAuth
对接到能够提供支持oauth
认证的外部服务,例如github
、gitlab
。这种方式没有对应用程序的代码侵入,仅仅在应用入口添加了配置。
除了上述我描述的这个应用场景,还有很多可以利用oauth
轻松实现认证的场景。
oauth2 proxy
是一个反向代理和静态文件服务器,使用提供程序(Google
,GitHub
和其他提供商)提供身份验证,以通过电子邮件,域或组验证帐户。
项目地址:https://github.com/oauth2-proxy/oauth2-proxy
认证过程的流程如下
实验环境:
登录到Gitlab
—>管理中心—>应用,创建一个应用
参数:
GitLab
在用户通过身份验证后应将其发送到的端点,对于oauth2-proxy
应该是https://<应用域名>/oauth2/callback
GitLab
用户配置文件的访问级别。对于大多数应用程序,选择openid
,profile
和email
即可。创建完应用后,会生成一对ID
和密钥,这个在后面会用到。
生成Cookie
密钥。该Cookie密钥作为种子字符串以产生安全的cookie。
参考官方说明,使用base64
编码,可利用以下的python
脚本生成字符串。
import secrets
import base64
print(base64.b64encode(base64.b64encode(secrets.token_bytes(16))))
在k8s
中部署oauth-proxy
,资源清单oauth2-gitlab.yaml
和相关参数说明如下。更多的配置参数,可以参考官方文档
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
k8s-app: oauth2-proxy
name: oauth2-proxy
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
k8s-app: oauth2-proxy
template:
metadata:
labels:
k8s-app: oauth2-proxy
spec:
containers:
- name: oauth2-proxy
image: quay.io/oauth2-proxy/oauth2-proxy:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 4180
protocol: TCP
args:
# OAuth提供者
- --provider=gitlab
# 上游端点的http网址
- --upstream=file:///dev/null
# 对具有指定域的电子邮件进行身份验证,可以多次给出,使用*验证任何电子邮件
- --email-domain=*
# 监听的地址
- --http-address=0.0.0.0:4180
# 设置安全(仅HTTPS)cookie标志
- --cookie-secure=false
# OAuth重定向URL
- --redirect-url=https://nginx-test.ssgeek.com/oauth2/callback
# 跳过登录页面直接进入下一步
- --skip-provider-button=false
# 设置X-Auth-Request-User,X-Auth-Request-Email和X-Auth-Request-Preferred-Username响应头(在Nginx auth_request模式下有用)。与结合使用时--pass-access-token,会将X-Auth-Request-Access-Token添加到响应标头中
- --set-xauthrequest=true
# 跳过OPTIONS请求的身份验证
- --skip-auth-preflight=false
# 绕过OIDC端点发现
- --skip-oidc-discovery
# OpenID Connect发行者url,这里是gitlab的url
- --oidc-issuer-url=https://gitlab.ssgeek.com
# 认证url
- --login-url=https://gitlab.ssgeek.com/oauth/authorize
# token url
- --redeem-url=https://gitlab.ssgeek.com/oauth/token
# 用于令牌验证的url
- --oidc-jwks-url=https://gitlab.ssgeek.com/oauth/discovery/keys
env:
- name: OAUTH2_PROXY_CLIENT_ID
value: '85945b7195ab109377183837b9221bd299bc64b31fe272304a1c777e8e241d83'
- name: OAUTH2_PROXY_CLIENT_SECRET
value: '2f9782928b493686f387d18db9138e92607448cef045c81319967cc3e5ce4ba1'
# 安全cookie的种子字符串,可通过python脚本生成
- name: OAUTH2_PROXY_COOKIE_SECRET
value: 'VGlYNVBVOGw4UFgyRURzbERxVTRiZz09'
---
apiVersion: v1
kind: Service
metadata:
labels:
k8s-app: oauth2-proxy
name: oauth2-proxy
namespace: kube-system
spec:
type: NodePort
ports:
- name: http
port: 4180
protocol: TCP
targetPort: 4180
nodePort: 30020
selector:
k8s-app: oauth2-proxy
应用上面的资源清单,创建deployment
和service
$ kubectl apply -f oauth2-gitlab.yaml
$ kubectl -n kube-system get pods -l k8s-app=oauth2-proxy
NAME READY STATUS RESTARTS AGE
oauth2-proxy-884695869-bkwns 1/1 Running 0 113s
上面通过nodeport
单独暴露了oauth2-proxy
应用,可以访问检查以确保浏览器可以正常打开
资源清单文件nginx.yaml
如下,其中为该nginx
应用配置了https
证书
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:1.15
imagePullPolicy: IfNotPresent
name: nginx
---
apiVersion: v1
kind: Service
metadata:
name: nginx
namespace: kube-system
spec:
selector:
app: nginx
ports:
- name: nginx
port: 80
targetPort: 80
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx
namespace: kube-system
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/rewrite-target: /
# 指定外部认证url
nginx.ingress.kubernetes.io/auth-url: "https://$host/oauth2/auth"
# 指定外部认证重定向的地址
nginx.ingress.kubernetes.io/auth-signin: "https://$host/oauth2/start?rd=$escaped_request_uri"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/secure-backends: "true"
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- nginx-test.ssgeek.com
secretName: nginx-test
rules:
- host: nginx-test.ssgeek.com
http:
paths:
- path: /
backend:
serviceName: nginx
servicePort: 80
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: "nginx"
# 将nginx应用的访问请求跳转到oauth2-proxy组件url
nginx.ingress.kubernetes.io/rewrite-target: "/oauth2"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/secure-backends: "true"
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
name: nginx-oauth2
namespace: kube-system
spec:
tls:
- hosts:
- nginx-test.ssgeek.com
secretName: nginx-test
rules:
- host: nginx-test.ssgeek.com
http:
paths:
- path: /oauth2
backend:
serviceName: oauth2-proxy
servicePort: 4180
应用上面的资源清单,创建相应资源
$ kubectl apply -f other/nginx.yaml
deployment.extensions/nginx unchanged
service/nginx unchanged
ingress.extensions/nginx unchanged
ingress.extensions/nginx-oauth2 unchanged
$ kubectl -n kube-system get po,svc,ing |grep nginx
pod/nginx-5ddcc6cb74-rnjlx 1/1 Running 0 3m
service/nginx ClusterIP 10.68.199.62 <none> 80/TCP 3m
ingress.extensions/nginx nginx-test.ssgeek.com 80, 443 3m
ingress.extensions/nginx-oauth2 nginx-test.ssgeek.com 80, 443 3m
通过访问上面部署的nginx
应用,在浏览器中进行测试,会被重定向到Gitlab
登录页面;
输入账号,正确登录后,会被重定向回nginx
应用。
在请求登录外部认证的过程中查看oauth2-proxy
的日志如下
172.16.1.110:49976 - - [2021/01/23 17:28:23] nginx-test.ssgeek.com GET - "/oauth2/auth" HTTP/1.1 "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.2 Safari/605.1.15" 401 21 0.000
172.16.1.110:9991 - - [2021/01/23 17:28:23] nginx-test.ssgeek.com GET - "/oauth2/start?rd=%2F" HTTP/1.1 "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.2 Safari/605.1.15" 302 341 0.000
172.16.1.110:9991 - admin@example.com [2021/01/23 17:28:32] [AuthSuccess] Authenticated via OAuth2: Session{email:admin@example.com user:root PreferredUsername: token:true id_token:true created:2021-01-23 17:28:32.440915913 +0000 UTC m=+2248.944621207 expires:2021-01-23 17:30:32 +0000 UTC refresh_token:true}
172.16.1.110:9991 - - [2021/01/23 17:28:32] nginx-test.ssgeek.com GET - "/oauth2/callback?code=9b7f5425d48a41f213065a4ff5b3d20d76e6d241ba753c5cd63d1a405f48818e&state=5d868b96b1f539a28da2291404152b7c%3A%2F" HTTP/1.1 "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.2 Safari/605.1.15" 302 24 0.381
172.16.1.110:5610 - admin@example.com [2021/01/23 17:28:32] nginx-test.ssgeek.com GET - "/oauth2/auth" HTTP/1.1 "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.2 Safari/605.1.15" 202 0 0.000
nginx
应用的时候,Ingress nginx controller
会向定义的auth-url
发起认证,该认证由Ingress nginx controller
发起,所以Ingress nginx controller
对应的pod
必须能够访问auth-url
。
Ingress nginx controller
将客户端重定向到auth-signin
。auth-signin
是目标应用的 oauth2
登录页面即oauth2-proxy
。
oauth2
登录页面后,自动进入Gitlab
的登录页面,
用户登录Gitlab
后,Gitlab
再将客户端重定向到在Gitlab
中配置的应用回调地址。
oauth2_proxy
在客户端设置cookie
,并将客户端重定向到最初的访问地址。
cookie
的客户端再次访问目标应用时,通过了auth-url
的认证,成功访问到目标服务即nginx
应用。
本文以基于k8s
部署的nginx
服务为例,记录如何通过ingress
和oauth2 proxy
对接gitlab
实现对应用没有代码侵入的外部认证。
最后,还要提到的一点是,我这里一开始使用的Gitlab
是已有的10.8.4
版本,调试了关于Oauth2-proxy
的很多参数一直不成功,也没有找到解决办法,但是按照官方的配置与github
对接时却没有报任何异常。最终通过提交issue
得到了可能原因,即Gitlab
的API
版本可能不兼容,oauth2-proxy
的开发测试成功版本的Gitlab
在12.x
版本以上。详情可参考我提交的issue。
See you ~
参考文档:
https://oauth.net/2/ https://kubernetes.github.io/ingress-nginx/examples/auth/oauth-external-auth/ https://oauth2-proxy.github.io/oauth2-proxy/docs/