作为业内首个全托管Istio兼容的阿里云服务网格产品ASM,一开始从架构上就保持了与社区、业界趋势的一致性,控制平面的组件托管在阿里云侧,与数据面侧的用户集群独立。ASM产品是基于社区Istio定制实现的,在托管的控制面侧提供了用于支撑精细化的流量管理和安全管理的组件能力。通过托管模式,解耦了Istio组件与所管理的K8s集群的生命周期管理,使得架构更加灵活,提升了系统的可伸缩性。从2022年4月1日起,阿里云服务网格ASM正式推出商业化版本, 提供了更丰富的能力、更大的规模支持及更完善的技术保障,更好地满足客户的不同需求场景, 详情可见产品介绍:https://www.aliyun.com/product/servicemesh。
背景
单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。通常在将应用接入到账号管理系统时,需要应用额外维护与认证/授权系统的认证/授权流程,而借助服务网格的外部授权能力,可以通过配置轻松地接入支持OIDC协议的认证/授权系统实现单点登录。本文选用广泛应用的开源项目keycloak作为认证授权系统进行演示,将服务网格内应用接入到Keycloak进行单点登录。
准备工作
-
创建一个ASM实例及ACK集群,并且将ACK集群加入至ASM
-
创建一个ASM网关,用于随后将服务暴露至公网
-
为default命名空间启用Sidecar自动注入
部署和配置
一、部署演示应用和keycloak
使用下面的yaml,将httpbin应用部署至ACK集群。
apiVersion: v1
kind: ServiceAccount
metadata:
name: httpbin
---
apiVersion: v1
kind: Service
metadata:
name: httpbin
labels:
app: httpbin
service: httpbin
spec:
ports:
- name: http
port: 8000
targetPort: 80
selector:
app: httpbin
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin
spec:
replicas: 1
selector:
matchLabels:
app: httpbin
version: v1
template:
metadata:
labels:
app: httpbin
version: v1
spec:
serviceAccountName: httpbin
containers:
- image: docker.io/kennethreitz/httpbin
imagePullPolicy: IfNotPresent
name: httpbin
ports:
- containerPort: 80
使用下面的yaml,将keycloak部署至ACK集群。
apiVersion: v1
kind: Service
metadata:
name: keycloak
labels:
app: keycloak
spec:
ports:
- name: http
port: 8080
targetPort: 8080
selector:
app: keycloak
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: keycloak
labels:
app: keycloak
spec:
replicas: 1
selector:
matchLabels:
app: keycloak
template:
metadata:
labels:
app: keycloak
spec:
containers:
- name: keycloak
image: quay.io/keycloak/keycloak:latest
args: ["start-dev"]
env:
- name: KEYCLOAK_ADMIN
value: "admin"
- name: KEYCLOAK_ADMIN_PASSWORD
value: "admin"
- name: KC_PROXY
value: "edge"
ports:
- name: http
containerPort: 8080
readinessProbe:
httpGet:
path: /realms/master
port: 8080
二、将演示应用和keycloak通过ASM网关暴露至公网
本例使用https(443端口)访问演示应用,使用http(80端口)访问keycloak控制台。请参考https://help.aliyun.com/document_detail/164895.html?spm=5176.13895322.help.dexternal.23f55fcfolpSUG为https服务创建证书,并命名为:myexample-credential。证书就绪后,使用下面的yaml在ASM集群创建网关规则和虚拟服务,将keycloak暴露于公网
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: ingressgateway
namespace: istio-system
spec:
selector:
app: istio-ingressgateway # 需要确认该selector和当前已存在的网关是匹配的
servers:
- hosts:
- '*'
port:
name: http-httpbin # 使用HTTPS(443端口)访问示例应用
number: 443
protocol: HTTPS
tls:
credentialName: myexample-credential
mode: SIMPLE
- hosts:
- '*'
port:
name: keycloak # 使用HTTP(80端口)访问keycloak控制台
number: 80
protocol: HTTP
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
creationTimestamp: '2022-06-09T11:44:20Z'
generation: 18
name: ingressgateway-vs
namespace: istio-system
resourceVersion: '7643220'
uid: bcff89f3-6842-4049-945b-76cb9f9d9961
spec:
gateways:
- ingressgateway
hosts:
- '*'
http:
- match:
- port: 80
name: keycloak
route:
- destination:
host: keycloak.default.svc.cluster.local
port:
number: 8080
- name: httpbin
route:
- destination:
host: httpbin.default.svc.cluster.local
port:
number: 8000
配置完成后,通过http://${入口网关地址}即可访问keycloak应用
三、配置keycloak
使用管理员账号登录KeyCloak控制台,选择您希望使用的Realm。点击导航栏中的Configure,再点击展开的子菜单中的Clients
点击右侧Create,创建用于oauth2proxy的client配置,按照下列值填写,并点击Save保存
-
Client ID: oauth2proxy
-
Client Protocol: openid-connect
创建完成后,回到Client列表,点击刚才创建的client,进入配置页面
在配置页面Settings选项卡中,找到AccessType,下拉选择Confidential:
然后点击上方Mappers选项卡,点击Create,创建如下2个mapper
最后,我们为keycloak添加一个测试账号用于演示登录认证流程,导航栏Manage -> Users -> Add user,为了简化演示流程,我们直接将Email Verified设置为On,表明该用户已经验证过邮件了。
至此,Keycloak的配置已经基本完毕。
四、部署oauth2proxy
本文提供了部署oauth2proxy的yaml模板,您需要先获得如下值,并替换至模板内,再进行部署
-
入口网关地址:ASM控制台 -> 网格实例 -> ASM网关 -> Kubernetes服务列可得到入口网关IP地址
-
ClientID: 第三步创建的ClientID
-
ClientSecret:keycloak控制台 -> Configure -> Clients -> 在列表中点击第三步创建的Client -> Credentials选项卡 -> Secret
apiVersion: apps/v1
kind: Deployment
metadata:
name: oauth2-proxy
namespace: istio-system
spec:
selector:
matchLabels:
app: oauth2-proxy
template:
metadata:
labels:
app: oauth2-proxy
spec:
containers:
- name: oauth2-proxy
image: quay.io/oauth2-proxy/oauth2-proxy:v7.2.0
args:
- --provider=keycloak-oidc
- --cookie-secure=true # 本例中需要使用https访问应用,所以设置为true
- --redirect-url=https://${入口网关地址}/oauth2/callback
- --cookie-samesite=lax
- --cookie-refresh=1h
- --cookie-expire=4h
- --cookie-name=_oauth2_proxy_istio_ingressgateway
- --set-authorization-header=true
- --email-domain=*
- --http-address=0.0.0.0:4180
- --upstream=static://200
- --skip-provider-button=false
- --whitelist-domain=*
- --oidc-issuer-url=http://${入口网关地址}/realms/master
- --skip-provider-button=false
- --scope=openid profile email
env:
- name: OAUTH2_PROXY_CLIENT_ID
value: "${ClientID}"
- name: OAUTH2_PROXY_CLIENT_SECRET
value: "${ClientSecret}"
- name: OAUTH2_PROXY_COOKIE_SECRET
value: "${CookieSecret}"
resources:
requests:
cpu: 10m
memory: 100Mi
ports:
- containerPort: 4180
protocol: TCP
readinessProbe:
periodSeconds: 3
httpGet:
path: /ping
port: 4180
五、创建外部授权服务及授权策略
打开ASM控制台,进入网格实例,点击导航栏中“零信任安全”,展开菜单后点击“外部授权服务”,点击“创建”
协议选择HTTP、服务地址为第三步部署的oauth2proxy的集群内域名,即:oauth2-proxy.istio-system.svc.cluster.local,服务端口即为oauth2-proxy服务的4180端口,超时时间设置为10秒。为了使得授权流程及认证成功后的身份信息能够被正确传递,还需设置以下几项:
-
启用“在鉴权请求中携带header”,并添加下列值
-
cookie
-
x-forward-access-token
-
启用“鉴权通过时覆盖header”,并添加下列值
-
authorization
-
cookie
-
path
-
x-auth-request-access-token
-
x-forwarded-access-token
-
启用“鉴权失败时覆盖header”,并添加下列值
-
content-type
-
set-cookie
-
启用“在鉴权请求中携带请求body”,并设置最大长度为10240(10K)。
点击确定创建外部授权服务。
接下来还需要创建授权策略,将请求指向外部授权服务进行授权流程。在当前服务网格实例左侧导航栏点击“授权策略”,点击使用yaml创建,将下面yaml拷贝粘贴,点击确定创建。
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: oidc
namespace: istio-system
spec:
action: CUSTOM
provider:
name: httpextauth-oidc
rules:
- {}
selector:
matchLabels:
istio: ingressgateway
验证
经过前面5个步骤的配置,我们现在可以访问应用以验证单点登录是否已经生效。在浏览器输入https://入口网关地址,访问应用,返回的页面为oauth2-proxy的登录页面。
点击“Sign in with Keycloak OIDC”,将跳转到keycloak登录页面,
输入账号密码,点击登录,成功跳转到应用页面:
点击Httpbin应用页面上的Request inspection,展开后点击/headers,点击右侧Try-it-out,再点击Execute,即可查看请求的Headers,可以看到,请求中携带了Authorization和Cookie两个Header,Authorization为一个JwtToken,可通过工具进行Decode,查看JwtToken中携带的身份信息。