全网统一账户实践

分享下目前我们全网的账号管理体系。

整体的账户管理思路是分而治之。主要分为下面三类账户:
1. 办公网账户,也就是大家熟悉的域账户。对于办公网账户,全网用户一人一账户,在 OpenLDAP 的基础上做了一些开发,这是进入公司内部的大门,所有新入职的员工都会分配一个该账号,不管是在办公室连接 Wi-Fi 还是在家连接 anyconnect VPN,访问 confluence/jira 等基础办公设置,都需要通过此账户进行登录认证。
2. 生产网账户,主要用来访问线上、线下机器资源。工程师访问线上生产、测试机器,登录线下自助机树莓派(raspberry) 均需要通过此账户认证,整个 user/group 的分配、HBAC/sudo 的控制、密码/公钥的管理均在 freeIPA/IDM 上实现,freeIPA 是 RedHat 支持的一整套集成安全信息管理解决方案系统,又称 IDM。
3. 数据库账户,这块比较小众,简单带过。
下面会针对上面两大块分别介绍。

办公网账户

该账号与所有人息息相关,从入职第一天起到你的 lastday,都需要登录该账户才可以访问办公资源,包括常见的 Wi-Fi/Gitlab/Jira/Confluence/Jenkins/Cisco Anyconnect VPN/Zabbix/Grafana/跳板机(intermediate host) 以及自己开发的各种内部系统。

这里面需要先简单描述下目前我们的多网分离的架构,多网是指办公网(包含国际线路)、生产网、测试网、专线网(到医院 HIS/LIS 等信息系统)、OOB(带外管理网),这五张大网是处于「部分」分离的状态的,比如,办公网到生产网/测试网/专线网/OOB 是完全分离的状态,除非通过跳板机(后面全部以 ih 代指)登录之后再访问;生产网跟测试网也是几乎分离的,除了极个别的公共服务。以办公网登录生产、测试网为例,在进入用户验证这步之前,会先通过 IP <-> MAC <-> 用户身份的一一映射绑定,先通过最基本 IP:PORT 的 ACL 方式来控制住大部分的请求。所以在你能通过网页打开 git,通过 ssh 协议 pull/push git 仓库的时候,说明已经通过 TCP/IP 层面的验证机制以及用户级别的验证机制,具体如何实现的会在后面的博客中说明。

默认情况下,OpenLDAP 支持的 schema 非常有限;另外不同应用接入方式都不大一样,需要逐一尝试。

比如 LDAP 跟 Anyconnect 的对接,官方提供的 schema 仅仅基本可用,离生产还有一段距离,默认只支持添加一组 IP/Netmask,以及一条 IP 层面的 ACL,为此需要自行扩展维护一套 cisco.ldif 文件,这个是我们自行维护的一个 schema 文件,根据我们业务的情况,新增了一组 IP/netmask 以及若干的 ACL。下面是一个普通用户的数据文件示例:

从 slapcat 导出的字段数据可以看到,为了实现 Wi-Fi 账号跟 OpenLDAP 的共享,加入了 samba 相关的 schema,具体的可以看这个文件

再比如,对于 anyconnect/ih 的认证,需要增加两步验证机制,这里引入 OTP(privacyIDEA) 以及 Radius 作为跟 OpenLDAP 的桥梁例,具体的交互图可以看这里:

最终实现了用户的 CRUD 在 OpenLDAP 层面控制。用户的两步验证,下图可以看到,privacyIDEA 支持十余种的 token,包括 hOTP/mOTP/sms/email/Yubikey 等等:

privacyIDEA 不仅仅支持我们使用的 ldapresolver,sql/passwd/scim 的 resolver 都有很好的支持。目前我们使用 Google Authenticator 是基于事件的 HOTP(RFC4226):

用来管理 OpenLDAP 的工具有不少,包括我们使用的经典的 phpldapadmin,除此之外,fusiondirectoryweb2ldap 都是不错的选择。同时为了支持用户的自助修改、重置密码,定期修改用户密码,我们引入了 LTB,一个非常神奇的 LDAP 工具箱集合,目前仅支持通过邮件找回密码的链接来重置密码:

除了自助机服务,LTB 还支持 LDAP 性能、用户使用数据的监控等若干非常实用的脚本。
这么重要的基础服务稳定性肯定是需要保障的,通过 syncprov 模块实现双 master 的写的高可用,通过多个 slave 同步 master,前端挂 Haproxy 实现读的高可用:

对用户来说,他们看到的是下面这个样子:

PACKT 出版的 Mastering OpenLDAP 是本通俗易懂的书,浏览一遍之后应该能轻松应对上面的内容。 

生产网账户

这里的生产网泛指包含线上生产测试服务器线下自助机树莓派等在内的所有 *nix 系统。所有需要登录如上机器的用户都需要获得该账户的权限之后,才能访问基于 HBAC 机制的主机以及颗粒度更细致的用户执行权限。

整个系统基于 freeIPA/IDM 实现,默认情况下,每台机器有一个专门用来运行进程(我们所有的 JVM 进程都跑在 vm 上,不会出现混跑的状态)的 worker 用户,所有的相关的 bin/, log/, lib/ 等目录文件都通过 Jenkins 统一打包到 ~/worker/ 下面,实现跟系统文件的隔离。/home/worker/ 的权限如下:

worker/ 下面所有的目录文件权限都如上所示。这样,只要用户在 bm_be_sre 这个组下面,就能实现 worker/ 下面也即 JVM 相关服务的读取权限:

下图可以看到,appCenter 这个服务是跑在 worker 下面的:

类似的,如果想实现 worker/ 下面的写操作,比如执行 bin/ 下面的脚本,将用户加到 30002(sre) 这个组即可,这里完全可以各自的需求自定义。所有的 sudo/su 相关权限设置全部在 IPA 的 Policy 操作即可,比如下面这个赋予的是 sre 这个组的用户可以执行 /bin/su – worker、/bin/su worker 这两个单独的比较危险的命令以及 sre_allow 命令行组里面基础命令(/bin/chmod, /bin/chown, /bin/cp 等等):

目前单台 8G/4core 的 IPA 支撑着 1000+ 的线上生产测试的物理虚拟机以及线下 100+ 的树莓派,应付起来绰绰有余,CPU 平均 1% 利用率、disk IO 平均 10%。

对我们的用户来说,在经过办公网/VPN 的 IP ACL 过滤之后,通过办公网账号结合 OTP 登录线上的任意一台跳板机(ih),然后通过 ih 再登录线上的机器,跟办公网账户类似的是,需要定期修改密码。对有权限看到的用户来说,他们看到的是下面这样

以上的两块账户体系算是把当年在阿里的基础核心账户体系复制了一遍,对最终用户来说,体验相对来说会更好些,当然我们没法也暂时没必要实现类似阿里郎那种监控每台入网设备所有操作的系统。

需要注意的是,在 IPA 上新增的用户,或者新增 sudo 权限,由于 sssd 的缓存,不会立即生效,可以通过 sss_cache 或者删除缓存文件可以让规则立即生效,这部分可以以类似 hook 的方式触发,每次修改用户权限的时候自动触发该操作。

freeIPA 结尾的 A 表示 audit,实际情况是目前还不支持 audit 功能,所以这块功能我们通过 snoopy 结合 rsyslog 方式进行收集审计

对于办公网账户,目前通过 python-ldap 实现了多个层次的自助以及服务的打通,freeIPA 还未进行这块的开发工作,最终的效果类比阿里内外,一个 portal 实现不同用户的自助服务。

数据库账户

第三块,或者说非常小众的一块账户是 DB 的账户,这里面主要涉及 MySQL 以及 NoSQL(Redis/ElasticSearch) 的账户划分管理审计。

MySQL 的账户管理我们通过 saltstack 的 mysql_user 进行管理,我们用的saltstack 是 2015.8.7,对 MySQL 5.7 的支持有不少 bug,2016 开头的版本对此都修复了,嫌麻烦的不妨直接把 salt 给升级了或者做 diff 给 2015.8.7 打 patch,主要原因是 5.7 之前存储用户表的密码字段是 password 而 5.7 之后变成了 authentication_string。

目前所有的 MySQL 账户用户通过 git 管理而密码通过 salt pillar 管理,审计则使用了 Percona 的 Audit Log Plugin 通过 rsyslog 打到中心服务器收集分析。

对于 Redis/ES 来说,由于上面存储的数据为非敏感数据,所有的都通过 IP 层面的 ACL 进行控制访问。

通过 noVNC 实现数千台自助机的实时可视化

背景很简单,目前我们运营维护着北京市属三甲医院数千台的自助机,这里面包含挂号取号机、检查报告机以及其他若干衍生出来的自助机种类,数量已经超过一千多台,不同的院区通过密密麻麻的专线跟我们的机房源源不断的进行着各种协议的数据传输与交互。

由于各方面技术以及非技术层面的限制,只能使用微软的系统,为了管理这部分的机器,一方面我们通过 saltstack 来进行日常的产品升级以及变更,对于桌面的可视,比如我们需要知道当前这台自助机前患者的操作,就需要 VNC 来帮助了。

一年前自助机刚上线时候,没有很详细的考虑过自助机的大规模运维问题,当时每台自助机都在 Clonezilla 打包阶段打包进了 realVNC,通过一台 Win 跳板机的 realVNC 客户端去连接,realVNC 有书签的功能,所以当时需要查看每台自助机界面的运行情况相对比较方便,随着数量的增加,这种先登录一台 win 的跳板机,在使用 realVNC 书签查看的传统 CS 架构就捉襟见肘了。迁移到 web 上势在必行,本着可小动不大动的原则,调研了 realVNC 的各种 SDK,遗憾的是都不支持,加上 realVNC
有版权问题,放弃。相比之下,TightVNC 则不存在这类问题。

后来我们组的同学调研了几个基于 web 的 VNC 客户端,peer-vncnoVNCguacamole ,peer-vnc 活跃量太低,底层用的是 noVNC,guacamole 还处在孵化期(功能相当强大)并且比较重,放弃。最终选择在 noVNC 的基础上做一些工作。

由于 TightVNC 默认不支持 WebSockets, noVNC 提供了 websockify 这个工具来做 TCP socket 的代理,接受 WebSockets 的握手,转化解析成 TCP socket 流量,然后在 CS 两端传递。由于我们涉及到较多的机器信息需要维护管理,我们将所有的机器信息,比如自助机的编码,机器名,IP/MAC 相关的信息预先存储到 MySQL 里面,接着按照 token: host:port 既定格式生成每家院区的连接信息配置文件,这一步可以在 jenkins/rundeck 上建个 job 方便每次增删改查。接着就可以启动了转发了。

在浏览器中打开本地的 6080 端口即可。下图是集成到我们内部管理平台上的截图,可以快速定位到自助机的健康状况并通过 VNC 进入确诊当前的状态:

noVNC 默认情况下会以交互式的方式连接,在这个过程中会做身份权限校验(账号连接、读写控制),是否是 true color 等,这个对于生产不是很适用,我们后来将授权这块做在 Django 上,结合 LDAP 做登录认证。考虑到专线带宽的限制,默认关闭了 true color 开启了压缩。VNC 对带宽的消耗还是比较厉害的,平均下来,每开一个新链接,会消耗 1Mbps 左右的带宽,所以如果需要做实时的展示大屏,需要考虑这块的瓶颈。

下图是 noVNC 实时图像,由此我们可以看到当前这台自助机的实时状态并对问题的自助机采取一定的操作:

最后将其挂在 haproxy/Nginx 后面,Nginx 1.3 之后支持 websocket,一个快速搭建并可投入使用的自助机可视化平台就算完成了。该平台我们内部命名为 bangkok。

以上思想可以移植到 *nix 平台上,目前不少主流的云 OpenStack/OpenNebula/Digital Ocean 也集成进了 noVNC。