node1.cluster -> master节点
node[2-5].cluster -> slave节点
很多Linux系统默认使用AES-256来加密tickets,例如CentOS/Red Hat,这需要在所有的集群节点以及Hadoop使用者的主机上安装 Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy File。
但是在JDK 8.151之后,应经默认使用了Unlimited Strength的Policy文件,所以安装了8.151之后的JDK的话,就可以省略这一步了。
注:本文所有操作均在root用户下执行。
Kerberos是CDH之外的独立基础设施,需要自行安装。
yum -y install krb5-server openldap-clients
yum install -y krb5-workstation krb5-libs
注:以下所有文件在修改前先cp一个bak备份文件
/etc/krb5.conf存在于每一个Kerberos节点上,所以在master节点上编辑好该文件后需要覆盖到集群上的所有节点上。
/etc/krb5.conf在每台机器上已经有一份默认配置,需要修改的是与你的集群相关的特定信息,主要是domain, kdc服务器和admin_server服务器的地址。 以下给出一份配置参考, 其中YOUR-DOMAIN
,YOUR-KDC-SERVER
,YOUR-MASTER-SERVER
需要根据集群的情况确定,然后查找替换即可。
# Configuration snippets may be placed in this directory as well
includedir /etc/krb5.conf.d/[logging]default = FILE:/var/log/krb5libs.logkdc = FILE:/var/log/krb5kdc.logadmin_server = FILE:/var/log/kadmind.log[libdefaults]default_realm = YOUR-DOMAINdns_lookup_realm = falseticket_lifetime = 24hdns_lookup_kdc = truerenew_lifetime = 7dforwardable = truerdns = falseudp_preference_limit = 0
#default_ccache_name = KEYRING:persistent:%{uid}[realms]YOUR-DOMAIN = {kdc = YOUR-KDC-SERVERadmin_server = YOUR-MASTER-SERVER}[domain_realm].YOUR-DOMAIN = YOUR-DOMAIN
YOUR-DOMAIN = YOUR-DOMAIN
这份配置文件中有这些几个重要的配置项需要重点关注一下:
default_realm:Kerberos默认的领域,当整个集群只在一个域里时,通常这个配置项的值就是当前域。如果有多个领域,可以在 [realms] 节添加其他域。
ticket_lifetime: 凭证生效的时限,一般为24小时,即kinit之后,23小时内凭证都是有效的。
renew_lifetime: 凭证最长可以被延期的时限,一般为一个礼拜。当凭证过期之后,对安全认证的服务的后续访问则会失败。
该文件是KDC的配置文件,所以只存在于kdc服务器上,以下是一份配置参考:
[kdcdefaults]kdc_ports = 88kdc_tcp_ports = 88[realms]YOUR-DOMAIN = {master_key_type = aes256-ctsmax_renewable_life = 7d 0h 0m 0sacl_file = /var/kerberos/krb5kdc/kadm5.acldict_file = /usr/share/dict/wordsadmin_keytab = /var/kerberos/krb5kdc/kadm5.keytab
# note that aes256 is ONLY supported in Active Directory in a domain / forrest operating at a 2008 or greater functional level.
# aes256 requires that you download and deploy the JCE Policy files for your JDK release level to provide
# strong java encryption extension levels like AES256. Make sure to match based on the encryption configured within AD for
# cross realm auth, note that RC4 = arcfour when comparing windows and linux enctypes
# 另:在JDK 8.151版本之后,已经默认使用unlimited的policy了,不必再去单独下载安装JCE了supported_enctypes = aes256-cts:normal aes128-cts:normal arcfour-hmac:normaldefault_principal_flags = +renewable, +forwardable}
同样的,YOUR-DOMAIN需要替换成集群实际的domain。 对于这份配置文件,有一个项配置需要特别关注,因于它是CDH要求必须配置的,即max_renewable_life
。在CDH配置Kerberos时,有一项前置条件:
KDC必须配置为允许renewable tickets with non-zerolifetime
当max_renewable_life配置为一个非零值时即满足了这个条件。
一般情况下,我们并不需要编辑这个文件,因为它默认配置的一条规则是可以满足一般需求的,即:
*/admin@YOUR-DOMAIN *
因为这项配置的含义是:只要principal的名字的第二部分是admin,那么该principal就拥有管理员权限。例如后面我们要建立的cloudera-scm/admin@YOUR-DOMAIN
这个principal,就拥有管理员权限,因为它的名字以/admin结尾。
在master节点上,执行:
kdb5_util create -r YOUR-DOMAIN -s
该命令执行完成后,会在/var/kerberos/krb5kdc/ 目录下生成principal数据库文件。
如果遇到数据库已经存在的提示,可以把 /var/kerberos/krb5kdc/ 目录下的 principal 的相关文件都删除掉:
rm -rf /var/kerberos/krb5kdc/principal*
默认的数据库名字都是 principal。可以使用 -d指定数据库名字。
systemctl start krb5kdc
systemctl start kadmin
systemctl enable krb5kdc
systemctl enable kadmin
服务启动之后,我们可以通过一组添加并查看principal的命令来验证kerberos是否已经安装成功:
# 列出Kerberos中的所有认证用户,即principals
kadmin.local -q "list_principals"# 添加认证用户,需要输入密码
kadmin.local -q "addprinc test-user"# 使用该用户登录,获取身份认证,需要输入密码
kinit test-user# 查看当前用户的认证信息ticket
klist# 更新ticket
kinit -R# 销毁当前的ticket
kdestroy# 删除认证用户
kadmin.local -q "delprinc test-user"
当Kerberos安装好之后,我们就可以通过CDH的Cloudera Manager来启用Kerberos了。简单地说,这一过程其实是让Cloudera Manager自动地为所有的CDH上的服务生成相应的服务主体和凭证,同时,将所有服务的身份认证机制改为kerberos(这需要修改所有服务上安全相关的配置项,会由CM自动完成),这样,外围客户端(不管是CLI工具,如hdfs dfs xxxx这样的命令,还是java clent libary等)再连接这些服务时,必须要通过kerberos客户端工具表明身份并认证通过之后(例如kinit xxx这样的CLI操作)才能访问这些服务。
CM上有专门启用Kerberos的向导配置,本文不再赘述,可以参考以下两篇文章中的截图:
https://blog.51cto.com/flyfish225/2096487
https://cloud.tencent.com/developer/article/1077884
CM在enable kerberos的过程中生成的全部都是“服务主体(principal)”,没有一个是“用户主体(principal)”,服务主体(principal)一定都带着主机名以标识这是哪个主机上的哪个服务,例如:
hive/node1.your-domain@your-domain
hdfs/node2.your-domain@your-domain
....
服务主体都是用来标识服务的,并不是用户可以拿来做身份认证的。比如我使用beeline连接hive时就需要明确指明要访问的service principal是哪个,就像下面这样:
beeline -u "jdbc:hive2://node1.your-domain:10000/default;principal=hive/node1.your-domain@your-domain"
如果用户试图以这些service principal的身份进行认证,你也不知道它们的密码的,因为它们的密码是随机生成的。
所以一旦服务被kerberos保护起来以后,就要建立kerberos“用户主体”,使用用户主体来访问服务。以hdfs服务为例,它的默认超级用户是hdfs,一旦启用kerberos,就不会再以本地OS账号的身份进行认证了,而是看kerberos的prinpical里有没有这个用户,一个很简单的验证方法就是:这时即时你使用节点上的linux hdfs账号执行hdfs dfs -ls /
这样的命令一样会因身份认证的问题而执行失败的。所以,我们要做的是就是在kerberos这个层面上建出hdfs的用户主体,任何用户,只要获取了hdfs的凭证,那么它就会被hdfs服务认定为hdfs用户,进而放权进行操作。添加用户凭证在前面也提到过,这里列出hdfs/hive两个主要用户凭证的创建命令:
kadmin.local -q "addprinc hdfs@your-domain"
kadmin.local -q "addprinc hive@your-domain"
我们从建立hdfs和hive的用户主体这个典型的案例可以很清晰地认识到kerberos本质:在一个认证体系里,永远有两个参与方:服务和使用服务的用户,在没有引入kerberos时,以HDFS服务为例,它以当前Linux用户的身份进行身份认证(其实等于没有什么认证动作),然后基于HDFS文件系统的权限体系(即HDFS上的文件owner,group和rwx权限 )进行权限控制。而当引入kerberos时,其实是加了一个透明的身份认证层,即,首先,“用户”的概念已经发生了变化,不再是任何linux账号,而是通过kerberos的addprinc建立的用户。即使绝大多数时候人们总是会在kerberos上建立与linux账号同名的principal,但它们完全是两种体系下的两码事了。但是对于服务端来说,一个用户就是一个确定的,唯一的用户,一旦通过了身份认证,那么确定就是你了,你将获得你本该拥有的一切权限。以HDFS服务的超级账号hdfs为例,它就是一个账号,当我们是使用非kerberos的方式访问HDFS服务时,它检查的是当前linux账号是不是hdfs, 如果是,那这个账号就是hdfs账号, 当我们是使用kerberos的方式访问HDFS服务时,它检查的是:这是不是kerberos principal数据库中存在的名为hdfs的用户主体,如果是,那么它通过的是kerberos认证,不管是哪种方式,一旦通过了身份认证,以hdfs用户的身份访问到了HDFS服务后,那么后面的权限,各种配置是完全一样,因为这是同一个用户(针对于服务的那个用户)!
对于上述的描述,在hadoop文档中有比较官方的解释,但不太易于理解kerberos从中扮演的角色,所以仅供参考:
Hadoop supports two different modes of operation to determine the user’s identity, specified by the hadoop.security.authentication property:- simpleIn this mode of operation, the identity of a client process is determined by the host operating system. On Unix-like systems, the user name is the equivalent of `whoami`.- kerberosIn Kerberized operation, the identity of a client process is determined by its Kerberos credentials. For example, in a Kerberized environment, a user may use the kinit utility to obtain a Kerberos ticket-granting-ticket (TGT) and use klist to determine their current principal. When mapping a Kerberos principal to an HDFS username, all components except for the primary are dropped. For example, a principal todd/foobar@CORP.COMPANY.COM will act as the simple username todd on HDFS.
我们可以再引申地思考一下:kerberos这种认证方式和那种页面上使用用户命名+密码的认证方式的差别是什么呢?kerberos这种认证方式很像是这样运作的:简化所有服务对身份认证的要求(或者说服务本身就不想或不能做身份认证),当一个用户说他是谁的时候,系统就直接认定他是那个人,不需要提供任何密码或凭证,以一个系统的登录界面做比喻的话,就是这个登入对话框上只需要输入用户名即可,没有密码验证。那到底谁来确保这个用户就是他声称的那个人呢?这时我们引入kerberos认证,在用户访问到登入页面之前再拦截一层,这一层是通明的,用户可以主动地通过kinit来表明自己的身份,只要kerberos认证他通过了,后面对接的就是那个登入页面了。
在linux上部署和运行程序会有一些默认的约定或称之为最佳实践,比如:为一个应用创建单独的linux账号,使用这个账号部署和启动应用。如果这个应用要访问大数据集群的资源,比如HDFS或Yarn,那它必须也要纳入kerveros体系里,说白了,就是也要为这个应用在kerberos上建立对应的用户凭证。
其实就具体操作上讲,创建普通用户主体和上面提到的创建hdfs/hive用户主体是一样的,区别只在于: 鉴于应用是基于集群的,Hadoop/CDH明确要求:必须在集群上的每个节点上创建这个账号,同时在HDFS的/user目录下创建同名文件夹,具体的要求是:
1. 确保群集中的所有主机都有一个linux用户帐户并且该帐户的名称与用户的主体名称的第一个组成部分相同。例如,如果用户的主体名称是 joe@HADOOP.COM,则每个框中应存在linux帐户joe。
2. 为每个用户帐户在 HDFS 上的 /user 下创建一个子目录(例如 /user/joe)。将该目录的所有者和组更改为该用户。
上述要求源自CDH官方文档:https://www.cloudera.com/documentation/enterprise/5-3-x/topics/cm_sg_prep_for_users_s17.html
至于为什么必须要在所有节点上创建账号,我还没有找到特别准确的解释,虽然像hdfs/yarn这样的账号都是这样做的,这和他们本来就是集群服务有关,而如果是一个只通过客户端访问集群资源的应用账户来说,为什么必须要这样呢?期待后续能找到答案。