简介
Pomerium 是一种身份感知代理,可实现对内部应用程序的安全访问。Pomerium 提供了一个标准化的接口来为应用程序添加访问控制,无论应用程序本身是否具有授权或身份验证。
Pomerium 可用于:
架构
Pomerium 位于最终用户和需要强身份验证的服务之间。在通过您的身份提供商 (IdP) 验证身份后,Pomerium 使用可配置的策略来决定如何路由用户的请求以及他们是否有权访问该服务。
Pomerium 由 4 个逻辑组件组成:
- 代理服务
- 所有用户流量都流经代理
- 使用身份验证服务验证所有请求
- 将用户定向到身份验证服务以建立会话身份
- 处理策略以确定外部/内部路由映射
- 认证服务
- 根据需要处理您的 IdP 的身份验证流程
- 在初始身份验证后处理身份验证
- 建立用户会话 cookie
- 将用户 OIDC 令牌存储在数据代理服务中
- 授权服务
- 处理策略以确定每个服务的权限
- 处理所有用户会话的授权检查
- 指示代理服务根据需要启动身份验证流程
- 为上游服务提供额外的安全相关标头以供使用
- 数据代理服务
- 检索与身份提供者相关的数据,例如组成员身份
- 存储和刷新身份提供者访问和刷新令牌
- 向授权服务提供流式权威会话和身份数据
- 将会话和身份数据存储在持久存储中
在生产部署中,建议您单独部署每个组件。这允许您限制外部攻击面,以及独立扩展和管理服务。
在测试部署中,所有四个组件都可以从单个二进制文件和配置运行。
认证流程
Pomerium 在来自新用户的完全身份验证期间的内部和外部组件交互如下图所示。
在提供会话令牌的初始身份验证后,仅发生授权检查交互。
使用
日常工作中,我们可能会遇到很多应用没有内置用户模块进行身份验证,比如prometheus、alertmanager、虽然我们可以使用nginx的basic-auth 做登陆身份验证,这种虽然简单但是不利于实际使用中我们针对用户做限制,比如不希望某些人访问,basic-auth 无法实现类似的限制功能,我们尝试使用Pomerium 对我们的网站进行保护。
这里我们还是使用gitlab作为身份提供者,不要问我为啥都是用gitlab作为身份提供者,因为我不喜欢用ldap。
使用docker-compose启动pomerium和一个不具有身份验证的服务verify,verify服务可以显示我们通过身份提供者获取到的用户信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| version: "3" services: pomerium: image: pomerium/pomerium:latest volumes: - ./config.yaml:/pomerium/config.yaml:ro ports: - 80:80 - 443:443 ## https://verify.localhost.pomerium.io --> Pomerium --> http://verify verify: image: pomerium/verify:latest expose: - 80
|
准备配置文件
准备签名密钥
签名密钥是用于签署用户证明 JWT 的私钥,上游应用程序可以使用它来传递识别用户信息,如用户名、id 和组。
如果设置,签名密钥的公钥将可以通过点击 Pomerium 的/.well-known/pomerium/jwks.json
端点来检索,该端点位于身份验证服务上。否则,端点将返回一个空键集。
没加这个访问/.well-known/pomerium/jwks.json会返回 {keys:”null”} ,这里坑了我很久。
1 2 3
| [root@es-01 pomerium] [root@es-01 pomerium] ewogICJ***dWU1MyNWNtb2E5eEhNUW1VQjdQIgp9Cg==
|
这里base64编码后的值为config.yaml文件内signing_key的值
使用gitlab作为身份提供者
身份提供者配置我们需要在gitlab上创建一个应用,记录应用的id和secret,后面config.yaml里会用到。
这里Redirect URI格式为 https://${authenticate_srvice_url}/oauth2/callback
范围勾选openid
, profile
, email
如果我们使用gitlab里的group在Pomerium中进行访问控制,则还需要再gitlab里创建一个访问令牌,用于配置config.yaml中的idp_service_account
创建后记录生成的访问令牌的token值
idp_service_account
是 base64 编码的 JSON 文档,格式如下:
1 2 3
| { "private_token": "GITLAB_TOKEN" }
|
我们生成我们使用的idp_service_account
值
1 2
| [root@es-01 pomerium]# cat json.tmp |base64 -w 0 ewogICJwcml2YXRlX3Rxxx*****Xp0YVBncW1zQl9RIgp9Cg==
|
这里用的config.yaml文件如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| pomerium_debug: true proxy_log_level: debug
authenticate_service_url: https://pomerium.harmay.com
certificate_file: /pomerium/cert.pem certificate_key_file: /pomerium/privkey.pem
idp_provider: "gitlab" idp_client_id: "8cc7c514b88a76a871****88dc4f2ec3bc2dbe6510e0087a" idp_client_secret: "58cd65ff96aea26***b9d5cf1312975ecf41031c06229d11e4d46a" idp_scopes: "openid,profile,email" idp_provider_url: "https://gitlab.lishuai.fun" idp_service_accOUnt: "ewogICJ***dWU1MyNWNtb2E5eEhNUW1VQjdQIgp9Cg=="
jwt_claims_headers: - user - email - name - groups
signing_key: LS0tLS******BLRVktLS0tLQo=
cookie_secret: W9Ir8DThVHcD3sTbCck8+G4O7yx6ubk/C8VqA8zluUs=
routes: - from: http://test.harmay.com to: http://verify:8000 policy: - allow: or: - email: is: 912988434@qq.com - groups: has: "jenkins" - deny: or: - groups: has: "gitlab-ci" pass_identity_headers: true
|
routes包含后端服务的特定访问和控制定义。每个routes至少定义一个from
和to
字段,以及一个policy
定义授权逻辑的key,这里我们定义了访问https://test.xxx.com路由到内部的http://verify:8000这个地址,并且允许邮箱为[email protected]的用户以及属于jenkins这个组的用户访问,拒绝属于gitlab-ci这个组的用户访问
启动服务
1 2 3 4 5 6 7 8
| [root@es-01 pomerium] Creating network "pomerium_default" with the default driver Creating pomerium_pomerium_1 ... done Creating pomerium_verify_1 ... done [root@es-01 pomerium] CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c5dd17fa201f pomerium/verify:latest "/bin/verify" 2 minutes ago Up 2 minutes 80/tcp pomerium_verify_1 345da0092980 pomerium/pomerium:latest "/bin/pomerium -conf…" 2 minutes ago Up About a minute 0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp pomerium_pomerium_1
|
验证
验证允许邮箱登陆
我们先打开https://test.harmay.com访问,首先会跳转到gitlab进行登陆,使用邮箱为[email protected]的用户后通过验证进入如下页面,会显示我们的用户信息。
第一次登陆可能会出现如下的页面,选择Authorize
即可。
我们在准备签名密钥里说的访问/.well-known/pomerium/jwks.json这里也可以验证一下
验证拒绝gitlab-ci
组中用户登录
我们gitlab
中有个用户叫lishuai
,该用户邮箱为[email protected]
我们先将这个用户加入到gitlab-ci
这个组里
此时我们访问https://test.harmay.com 会跳转到gitlab做验证,此时就会被拒绝访问。
我们访问https://pomerium.harmay.com/ 也可以看到用户的信息,此时用户的信息都被拿到但是因为邮箱不是912988434@qq.com或属于jenkins组所以无法登录到https://test.harmay.com
验证允许jenkins组中用户登录
我们再将这个用户从gitlab-ci这个组中移除,加入到jenkins这个组内
此时我们访问https://test.harmay.com 是可以拿到用户信息并正常进入
监控
如果我们想监控Pomerium,我们可以修改config.yaml文件,添加如下配置暴露mtrics指标
1 2 3 4
| ...... metrics_address: ":9090" ......
|
修改docker-compose.yaml,添加端口9090的映射
1 2 3 4 5 6
| ...... ports: - 80:80 - 443:443 - 9090:9090 ........
|
访问指标
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| [root@es-01 pomerium]# curl localhost:9090/metrics|tail -n 20 % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 584k 0 584k 0 0 15.8M 0 --:--:-- --:--:-- --:--:-- 15.8M # TYPE envoy_listener_manager_listener_modified counter envoy_listener_manager_listener_modified{service="pomerium",installation_id="",hostname="df87ae62087c"} 0 # TYPE envoy_vhost_harmay_com_vcluster_other_upstream_rq_timeout counter envoy_vhost_harmay_com_vcluster_other_upstream_rq_timeout{service="pomerium",envoy_virtual_host="pomerium",installation_id="",hostname="df87ae62087c"} 0 envoy_vhost_harmay_com_vcluster_other_upstream_rq_timeout{service="pomerium",envoy_virtual_host="test",installation_id="",hostname="df87ae62087c"} 0 # TYPE envoy_listener_manager_workers_started gauge envoy_listener_manager_workers_started{service="pomerium",installation_id="",hostname="df87ae62087c"} 1 # TYPE envoy_server_memory_physical_size gauge envoy_server_memory_physical_size{service="pomerium",installation_id="",hostname="df87ae62087c"} 1.7430454e+07 # TYPE envoy_cluster_http2_outbound_control_flood counter envoy_cluster_http2_outbound_control_flood{service="pomerium",envoy_cluster_name="pomerium-control-plane-grpc",installation_id="",hostname="df87ae62087c"} 0 envoy_cluster_http2_outbound_control_flood{service="pomerium",envoy_cluster_name="pomerium-databroker",installation_id="",hostname="df87ae62087c"} 0 # TYPE envoy_listener_admin_downstream_cx_total counter envoy_listener_admin_downstream_cx_total{service="pomerium",installation_id="",hostname="df87ae62087c"} 2 # TYPE envoy_listener_manager_lds_init_fetch_timeout counter envoy_listener_manager_lds_init_fetch_timeout{service="pomerium",installation_id="",hostname="df87ae62087c"} 0 # TYPE envoy_vhost_vcluster_upstream_rq_timeout counter envoy_vhost_vcluster_upstream_rq_timeout{service="pomerium",envoy_virtual_cluster="other",envoy_virtual_host="catch-all",installation_id="",hostname="df87ae62087c"} 0 envoy_vhost_vcluster_upstream_rq_timeout{service="pomerium",envoy_virtual_cluster="other",envoy_virtual_host="grpc",installation_id="",hostname="df87ae62087c"} 0 envoy_vhost_vcluster_upstream_rq_timeout{service="pomerium",envoy_virtual_cluster="other",envoy_virtual_host="metrics",installation_id="",hostname="df87ae62087c"} 0
|
总结
实际使用中,我们可以将config.yaml中routes中的地址换成我们实际需要保护的地址,例如prometheus地址或者alertmanager地址等,Pomerium不仅可以保护http协议的服务还可以保护tcp协议的服务,有时间给大家继续分享使用Pomerium如何保护我们的redis和mysql等tcp协议服务。
参考
https://www.pomerium.com/