前言
在2014年初,我们将线上使用的 Hadoop 1.0 集群切换到 Hadoop 2.2.0 稳定版, 与此同时部署了 Hadoop 的安全认证。本文主要介绍在 Hadoop 2.2.0 上部署安全认证的方案调研实施以及相应的解决方法。
背景 集群安全措施相对薄弱
最早部署Hadoop集群时并没有考虑安全问题,随着集群的不断扩大, 各部门对集群的使用需求增加,集群安全问题就显得颇为重要。说到安全问题,一般包括如下方面:
用户认证(Authentication)
即是对用户身份进行核对, 确认用户即是其声明的身份, 这里包括用户和服务的认证。
用户授权(Authorization)
即是权限控制,对特定资源, 特定访问用户进行授权或拒绝访问。用户授权是建立再用户认证的基础上, 没有可靠的用户认证谈不上用户授权。
未开启安全认证时,Hadoop 是以客户端提供的用户名作为用户凭证, 一般即是发起任务的Unix 用户。一般线上机器部署服务会采用统一账号,当以统一账号部署集群时,所有执行 Hadoop 任务的用户都是集群的超级管理员,容易发生误操作。即便是以管理员账号部署集群,恶意用户在客户端仍然可以冒充管理员账号执行。
集群整体升级到 hadoop 2.0
2013年10月份 Hadoop 2.2.0 发布,作为 Apache Hadoop 2.X 的 GA 版本。我们考虑将集群整体升级 Hadoop 2.2.0,进入 yarn 时代。与此同时,我们计划在升级过程中一并把集群安全工作做到位,主要基于以下考虑:
与升级工作一样,安全同样是基础工作,把安全搞好会方便我们后续的工作,否则会成为下一个阻碍。
所谓基础工作,就是越往后改动越难的工作,目前不做,将来依赖更多,开展代价更大。
综上,我们的需求是在低版本hadoop升级到Yarn的过程中部署Hadoop安全认证,做好认证之后我们可以在此之上开启适当的权限控制(hdfs, 队列)。
方案调研
在方案调研之前先明确以下安全实践的原则,如下:
做为一个后端服务平台,部署安全的主要目的是防止用户误操作导致的事故(比如误删数据,误操作等)
做安全是为了开放,开放的前提是保证基本的安全,数据安全与平台安全
在保证安全的前提下,尽量简化运维
分析我们遇到的问题,这里我们需要调研:
账号拆分与相应管理方案
开启 Hadoop 安全认证
客户端针对安全认证的相应调整
账号拆分与相应管理方案
集群账号管理
原先我们使用单一账号作为集群管理员,且这一账号为线上统一登录账号, 这存在极大的安全隐患。我们需要使用特殊账号来管理集群。这里涉及的问题是,我们需要几个运维账号呢?
一种简单的做法是使用一个特殊运维账号(比如 hadoop), CDH 和 Apache官方 都推荐按服务划分分账号来启动集群:
User:Group Daemons
hdfs:hadoop NameNode, Secondary NameNode, Checkpoint Node, Backup Node, DataNode
yarn:hadoop ResourceManager, NodeManager
mapred:hadoop MapReduce JobHistory Server
考虑到精细化控制可以有效避免误操作,这里我们遵循官方的建议使用多账号。
在从单一运维账号迁移到多个账号部署时,需要考虑相关文件权限问题,包括本地以及hdfs两部分,这可以在安全部署上线时完成相应改动。
用户账号管理
美团很多小组都有使用 Hadoop 来进行大数据处理需求, 故需要一定程度的多租户环境, 这里主要考虑其中的数据和操作的权限问题。hdfs 本身仅提供类 Unix 的权限系统, 默认的组概念也相对鸡肋。鉴于此,在多用户的管理上可以有简单粗暴的方案:
不同组有各自的根目录,使用不同的账号,对组内文件有全部权限。不同组之间相互不能访问数据(除非手动修改)。
在一个集中的数据仓库环境下,又要生产各个部门的统计数据的话,上述策略不够灵活。目前Cloudera 有一个精细化权限控制的解决方案 sentry, 支持 Role based 的权限管理。由于其定制化较高,不方便使用, 故暂未考虑。
开启 Hadoop 安全认证
Hadoop 的安全认证是基于 Kerberos 实现的。 Kerberos 是一个网络身份验证协议,用户只需输入身份验证信息,验证通过获取票据即可访问多个接入 Kerberos 的服务, 机器的单点登录也可以基于此协议完成的。 Hadoop 本身并不创建用户账号,而是使用 Kerberos 协议来进行用户身份验证,从Kerberos凭证中的用户信息获取用户账号, 这样一来跟实际用户运行的账号也无关。
这里我们从 YARN 上的 MR 任务提交过程简单说明一下:
Yarn 任务提交步骤
用户执行任务前,先通过KDC认证自己,获取TGT(Ticket Granting Ticket)。KDC是 Kerberos 认证的中心服务,存储用户和服务的认证信息。
用户通过 TGT 向 KDC 请求访问服务的Ticket, KDC 生成 session key 后一并发给客户端。
完成身份认证后客户端向服务请求若干token供后续任务执行认证使用(比如 HDFS NameNode Delegation Token, YARN ResourceManager Delegation Token)
客户端连同获取到的 token 一并提交任务,后续任务执行使用 token 进行来自服务的认证
从上可以看出,出于性能的考虑,Hadoop 安全认证体系中仅在用户跟服务通信以及各个服务之间通信适用 Kerberos 认证,在用户认证后任务执行,访问服务,读取/写入数据等均采用特定服务(NameNode, Resource Manager)发起访问token,让需求方凭借 token 访问相应服务和数据。这里 token 的传递,认证以及更新不做深入讨论。
关于开启 Hadoop 安全认证, Cloudera 有详细的文档介绍。由于自身环境以及部署运维的考虑,最终的部署方案有些许出入, 一一说明。
Kerberos 部署
Hadoop 安全认证需要一个 Kerberos 集群, 部署 Kerberos 需要部署KDC。 由于我们的环境中使用 freeIPA 进行主机认证相关的权限控制,已经集成 Kerberos 服务, 故不需要另外部署。
Kerberos 相关的运维操作, 比如添加用户,服务,导出keytab,均可以通过 ipa 相关接口来进行。
Container 的选择
从上图可以看出用户发起的任务是在特定的容器(Container)内执行的, 一开始我们考虑使用DefaultContainer 而不是官方推荐的 LinuxContainer, 缺点是对任务之间的物理隔离以及防范恶意任务方面会有缺陷, 不过方便部署,使用LinuxContainer需要在集群各台机器上部署用户账号。
实际测试发现由于MAPREDUCE-5208的引入,在 hadoop 2.2.0 上开启安全认证后无法使用 DefaultContainer。
这里不希望对代码有过多定制化的修改,我们考虑还是使用 LinuxContainer, 需要解决一下问题:
用户账号创建
我们需要在集群内添加所有可能的任务发起用户账号。借助 freeipa 的统一的用户管理 , 我们只需要在 freeipa 上添加相应用户即可。
container-executor 和 container-executor.cfg 的部署
container-executor 作为Yarn 的 container 执行程序,有一系列的权限要求:
Be owned by root
Be owned by a group that contains only the user running the YARN daemons
Be setuid
Be group readable and executable
配置 container-executor.cfg 不仅需要是owned by root,且其所在目录同样需要 owned by root。这两者都给自动化部署带来不便,鉴于此部分比较独立且基本不会改变,我们可以将其加入集群机器的 puppet 管理当中。
DataNode 启动方式
CDH 推荐的datanode 的启动方式需要使用低端口并且使用jsvc发布, 在运维方面也不太方便。这里我们通过配置ignore.secure.ports.for.testing=true来启动datanode, 规避这些约束。
客户端针对安全认证的相应调整
集群开启安全认证之后, 依赖集群的客户端(脚本, 服务)都需要做相应修改,不过改动基本无异。大部分服务都已包括对 Kerberos 认证的相应处理, 基本不需要修改。
这里首先得说明一下开启安全认证后的认证方式:
使用密码认证
使用用户密码通过kinit认证, 获取到的TGT存在本地凭证缓存当中, 供后续访问服务认证使用。一般在交互式访问中使用。
使用 keytab 认证
用户通过导出的keytab 可以免密码进行用户认证, 后续步骤一致。一般在应用程序中配置使用。
Kerberos 凭证(ticket) 有两个属性, ticket_lifetime 和 renew_lifetime。其中 ticket_lifetime 表明凭证生效的时限,一般为24小时。在凭证失效前部分凭证可以延期失效时间(即Renewable), renew_lifetime 表明凭证最长可以被延期的时限,一般为一个礼拜。当凭证过期之后,对安全认证的服务的后续访问则会失败。这里第一个问题就是如何处理凭证过期