Skip to content
王伟兵 edited this page Sep 7, 2017 · 27 revisions

core-site.xml配置

knox是一种用户代理程序,集群授权给knox后,它才能代理用户请求。通过修改hadoop的核心配置文件core-site.xml中相关参数来授权给knox代理用户请求的权限。
core-site.xml在文件系统中的位置是/etc/hadoop/conf,通过Ambari修改core-site.xml更方便。在Ambari中前往Service -> HDFS -> Configs -> Advanced -> Custom core-site,修改用hadoop.proxyuser.knox.hostshadoop.proxyuser.knox.groups参数:

<property>
    <name>hadoop.proxyuser.knox.groups</name>
    <value>users</value>
</property>
<property>
    <name>hadoop.proxyuser.knox.hosts</name>
    <value>FQDN_OF_KNOX_HOST</value>
</property>

在测试中FQDN_OF_KNOX_HOST被替换为u1401.ambari.apache.org,也就是安装knox网关的虚拟机。把users修改成了*,即允许knox代理(仿冒)所有用户组的用户。

两种认证方式的测试

  • ShiroProvider
    对于LDAP/AD身份验证,使用用户名和密码。没有SPNEGO/Kerberos支持。(这篇apache官方文档对于knox与LDAP的集成讲得很到位)
  • HadoopAuth
    对于SPNEGO/Kerberos身份验证,使用委派令牌。没有LDAP/AD支持。

ShiroProvider(LDAP认证)

用ambari安装的knox,默认安装目录是/usr/hdp/current/knox-server。默认cluster-name是default,对应的拓扑配置文件是:

/usr/hdp/current/knox-server/conf/topologies/default.xml

有两种方式创建LDAP服务器,一是手工安装OpenLDAP;二是使用Knox自带的Demo LDAP:

  • 如果要手工安装OpenLDAP,参考这个LDAP测试的文档。
  • 如果要使用Knox自带的DemoLDAP服务器,则在Ambari中前往 Services -> Knox -> Service Actions -> Start Demo LDAP。

下面的测试使用的u1401上手工部署OpenLDAP,并在LDAP上创建了一个测试用户john(dn: uid=john,ou=People,dc=ambari,dc=apache,dc=org),该用户的密码是johnldap。初学者可以安装一个JXplorer来链接LDAP服务器查看其中的数据。

为了修改Knox的默认集群拓扑文件(default.xml),前往 Services -> Knox -> Configs -> Advanced topology,将第一个xml的第一个provider元素替换为下列内容:

<topology>
            <gateway>
                <provider>
                    <role>authentication</role>
                    <name>ShiroProvider</name>
                    <enabled>true</enabled>
                    <param>
                        <name>sessionTimeout</name>
                        <value>30</value>
                    </param>
                    <param>
                        <name>main.ldapRealm</name>
                        <value>org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm</value>
                    </param>
                    <param>
                        <name>main.ldapRealm.userDnTemplate</name>
                        <value>uid={0},ou=people,dc=ambari,dc=apache,dc=org</value>
                    </param>
                    <param>
                        <name>main.ldapRealm.contextFactory.url</name>
                        <value>ldap://{{knox_host_name}}</value>
                    </param>
                    <param>
                        <name>main.ldapRealm.contextFactory.authenticationMechanism</name>
                        <value>simple</value>
                    </param>
                    <param>
                        <name>urls./**</name>
                        <value>authcBasic</value>
                    </param>
                </provider>
(省略一些内容)
        </topology>

相对于默认配置,只修改了两个参数,一个是将main.ldapRealm.userDnTemplate的值定义为uid={0},ou=people,dc=ambari,dc=apache,dc=org;另一个是将main.ldapRealm.contextFactory.url定义为ldap://{{knox_host_name}}。LDAP没有启用TLS,使用默认端口389。

在ambari界面中点击Save按钮保存,并通过橙黄色按钮重启相关服务。之后会发现/usr/hdp/current/knox-server/conf/topologies/目录下的default.xml修改更新了。

测试使用OpenLDAP中的用户john(dn: uid=john,ou=People,dc=ambari,dc=apache,dc=org),该用户的密码是johnldap。使用该用户测试Knox:

$ curl -i -k -u john:johnldap -X GET \
    'https://u1401.ambari.apache.org:8443/gateway/default/webhdfs/v1/tmp/webb?op=LISTSTATUS'
{"FileStatuses":{"FileStatus":[{"accessTime":1493947036270,"blockSize":134217728,"childrenNum":0,"fileId":22980,"group":"hdfs","length":3,"modificationTime":1493947036592,"owner":"webb","pathSuffix":"t1.txt","permission":"644","replication":3,"storagePolicy":0,"type":"FILE"}]}}

可以故意输入错误的密码,如john:1到-u参数,则:

$ curl -i -k -u john:johnldap -X GET \
    'https://localhost:8443/gateway/default/webhdfs/v1/tmp/webb?op=LISTSTATUS'
HTTP/1.1 401 Unauthorized

/tmp/webb是HDFS中的目录,也可以获取很目录的内容:

$ curl -i -k -u john:johnldap -X GET \
    'https://localhost:8443/gateway/default/webhdfs/v1/?op=LISTSTATUS'
(返回值略)

curl -k -i --negotiate -u : https://localhost:8443/gateway/default/webhdfs/v1/tmp?op=LISTSTATUS

Hadoop Auth Provider(SPNEGO/Kerberos认证)

本节参考了这个文章
本节介绍使用Apache Knox配置Hadoop Auth(SPNEGO和基于代理令牌的身份验证)。 Hadoop Auth是一个Java库,可以为HTTP请求启用Kerberos SPNEGO身份验证。它在对受保护的资源进行身份验证后,成功认证后,Hadoop Auth创建一个带有认证令牌,用户名,用户主体,认证类型和到期时间的签名HTTP Cookie。该cookie用于所有后续的HTTP客户端请求,以访问受保护的资源,直到cookie过期。

鉴于Apache Knox的可插拔身份验证提供程序,只需少量配置更改即可轻松地使用Apache Knox设置Hadoop Auth。
假设已经有一个正常工作的Hadoop集群与Apache Knox,而且集群是Kerberized。

配置

要在Apache Knox中使用Hadoop Auth,我们需要更新Knox拓扑。Hadoop Auth配置为提供程序,因此我们需要通过提供程序参数进行配置。Apache Knox使用与Apache Hadoop相同的配置参数,可以预期其类似的行为。要使用Ambari更新Knox拓扑,请转到Knox - > Configs - > Advanced拓扑结构。
以下是Apache Knox拓扑文件中HadoopAuth提供程序代码段的示例(如果通过ambari修改knox的配置文件,在配置文件的Advanced atopology小节中):

<provider>
  <role>authentication</role>
  <name>HadoopAuth</name>
  <enabled>true</enabled>
  <param>
    <name>config.prefix</name>
    <value>hadoop.auth.config</value>
  </param>
  <param>
    <name>hadoop.auth.config.signature.secret</name>
    <value>knox-signature-secret</value>
  </param>
  <param>
    <name>hadoop.auth.config.type</name>
    <value>kerberos</value>
  </param>
  <param>
    <name>hadoop.auth.config.simple.anonymous.allowed</name>
    <value>false</value>
  </param>
  <param>
    <name>hadoop.auth.config.token.validity</name>
    <value>1800</value>
  </param>
  <param>
    <name>hadoop.auth.config.cookie.domain</name>
    <value>ambari.apache.org</value>
  </param>
  <param>
    <name>hadoop.auth.config.cookie.path</name>
    <value>gateway/default</value>
  </param>
  <param>
    <name>hadoop.auth.config.kerberos.principal</name>
    <value>HTTP/[email protected]</value>
  </param>
  <param>
    <name>hadoop.auth.config.kerberos.keytab</name>
    <value>/etc/security/keytabs/spnego.service.keytab</value>
  </param>
  <param>
    <name>hadoop.auth.config.kerberos.name.rules</name>
    <value>DEFAULT</value>
  </param>
</provider>

(需要额外说明的是config.prefix参数(默认是hadoop.auth.config),它指定了多个参数的前缀)
以下是需要最少更新的参数:

  1. hadoop.auth.config.signature.secret - 这是用于在hadoop.auth cookie中签署委托令牌的秘密。需要在给定群集中的Knox网关的所有实例中使用相同的秘密。否则,委托令牌将失败验证,每个请求将重复身份验证。
  2. cookie.domain - 用于存储身份验证令牌的HTTP cookie的域(例如mycompany.com)
  3. hadoop.auth.config.kerberos.principal - Web应用程序Kerberos主体名称。Kerberos主体名称必须以HTTP / ...开头。
  4. hadoop.auth.config.kerberos.keytab - 包含上面指定的kerberos主体的凭据的keytab文件的路径。

如果您使用Ambari,您将不得不重新启动Knox,这是一个Ambari要求,如果拓扑在Ambari之外更新,则不需要重新启动(Apache Knox每次更新拓扑时间戳时重新加载拓扑)。 如果通过ambari更新参数,需要在ambari界面中点击Save按钮保存,并通过橙黄色按钮重启相关服务。之后会发现/usr/hdp/current/knox-server/conf/topologies/目录下的default.xml修改更新了。

测试

  1. 让我们创建一个用户'guest'与组'用户'。请注意,由于属性“hadoop.proxyuser.knox.groups = users”选择了组用户。
$ useradd guest -u 1590 -g users
  1. 用'kadmin.local'命令增加主体:
$ kadmin.local -q "addprinc guest/u1401.ambari.apache.org”
  1. 用kinit登录:
$ kinit guest/[email protected]

用kinit登录后,kerberos会话就可以跨客户端请求使用,如curl。以下curl命令可用于从HDFS请求目录列表,同时通过-negotiate标志与SPNEGO进行身份验证。首先是不通过knox网关,直接访问启用了kerberos的HDFS(取HDFS中/tmp目录下对象清单):

$ curl -k -i --negotiate -u : http://u1401.ambari.apache.org:50070/webhdfs/v1/tmp?op=LISTSTATUS

通过knox网关访问HDFS:

$ curl -k -i --negotiate -u : https://u1401.ambari.apache.org:8443/gateway/default/webhdfs/v1/tmp?op=LISTSTATUS
HTTP/1.1 401 Authentication required
Date: Fri, 24 Feb 2017 14:19:25 GMT
WWW-Authenticate: Negotiate
Set-Cookie: hadoop.auth=; Path=gateway/default; Domain=ambari.apache.org; Secure; HttpOnly
Content-Type: text/html; charset=ISO-8859-1
Cache-Control: must-revalidate,no-cache,no-store
Content-Length: 320
Server: Jetty(9.2.15.v20160210)
 
HTTP/1.1 200 OK
Date: Fri, 24 Feb 2017 14:19:25 GMT
WWW-Authenticate: Negotiate YGwGCSqGSIb3EgECAgIAb10wW6ADAgEFoQMCAQ+iTzBNoAMCARCiRgRE26GeVRA0WkP7eb3csszuxUnSBDFK0NWH2+ai5pFY1onksiVOqjLkY8YS1xF5CshT4IwfrOHz6ivG6218X6oOSb0oCaU=
Set-Cookie: hadoop.auth="u=guest&p=guest/[email protected]&t=kerberos&e=1487947765114&s=fNpq9FYy2DA19Rah7586rgsAieI="; Path=gateway/default; Domain=ambari.apache.org; Secure; HttpOnly
Cache-Control: no-cache
Expires: Fri, 24 Feb 2017 14:19:25 GMT
Date: Fri, 24 Feb 2017 14:19:25 GMT
Pragma: no-cache
Expires: Fri, 24 Feb 2017 14:19:25 GMT
Date: Fri, 24 Feb 2017 14:19:25 GMT
Pragma: no-cache
Content-Type: application/json; charset=UTF-8
X-FRAME-OPTIONS: SAMEORIGIN
Server: Jetty(6.1.26.hwx)
Content-Length: 276
 
{"FileStatuses":{"FileStatus":[{"accessTime":0,"blockSize":0,"childrenNum":1,"fileId":16398,"group":"hdfs","length":0,"modificationTime":1487855904191,"owner":"hdfs","pathSuffix":"entity-file-history","permission":"755","replication":0,"storagePolicy":0,"type":"DIRECTORY"}]}}

服务器首先返回401(Authentication required),并在响应头上放置了WWW-Authenticate: Negotiate标志。这个Negotiate表示要挑战浏览器(收到这个挑战后,在windows的chrome表现为弹出让输入用户名口令的窗口)。而在linux下的curl的则调用了自己的GSS-Negotiate功能来计算出一个请求来再次调用同一个URL,如果在curl中加入--verbose参数可以看到请求增加的内容:

> GET /webhdfs/v1/tmp?op=LISTSTATUS HTTP/1.1
> Authorization: Negotiate YIIC4QYJKoZIhvcSAQICAQBuggLQMIICzKADAgEFoQMCAQ6iBwMFACAAAACjggGcYYIBmDCCAZSgAwIBBaETGxFBTUJBUkkuQVBBQ0hFLk9SR6IqMCigAwIBA6EhMB8bBEhUVFAbF3UxNDAxLmFtYmFyaS5hcGFjaGUub3Jno4IBSjCCAUagAwIBEKEDAgEBooIBOASCATQsvtJkb821p1278/N+uJkQLdSHxwFjikXItktYEUizskJu5l4BkQhs/SPF0Zq0QrSxzMMB9x7WO90w1edyM8lv5oaMNPs7nzTOIYDW52K47NdIY/TwDScNlFubWAg/Aq5b9wxrLrJ7r+G7J9DheYLUTglgztIV0jqFlWFgxr6ZGtkGHx4QdMDeGQDmcdeGEQWlNYbrkO1D5iMCLWySZe3ijBZ77DU22F5ukS6BiCAVbAVouRPCTe22ey19kknygqvsc8TcVS7/5kKcNL1CbZsiJUxwaEeDRNjEFCmRp4and0hO3l2iAc0P+hsCuTBz2oEtQaUV4tCgJ78V7sFRHcT+un/nfLd7P246odeOFOR/e4KOBMH56+WpyabbbH4cZJ3wFcB4dldkj0BKZftIG5pRV2v2g6SCARUwggERoAMCARCiggEIBIIBBFbDBLfvzjDA97j0RqFkxWG7GP97uDOSO3sJ5sgdajePDG/nvP+TBMelraUiJFPe/MZX9aInDyrod4RTTZ4oqoYyyrYte2wuczSuuzcJd1tpzDkSq64EtC9ZU/Ir9Ix0OrFtgmL+Cq4YbrB3FRtzq59xRaiPzDAnwAwG57f6lrBOKpwCLflOiNj7cKiDuDhczSM3p+G0ZE1o9VkDhP5RkXBD8+vSrAZtqM6HrmJ+BukmpNvPMDXGN9eijgfMDC2g1IY2afViC8a2ZiuLVvKnBc8yn+bkwXxejJXMEzz9t0z/qWCF8RPrsRnRHsT87wRuqXrdzT8Awsb6R0qHJhmbfduRyd3C
> User-Agent: curl/7.35.0

上述请求还可以这样发送:

$ curl -c c.txt -k -i --negotiate -u : "https://u1401.ambari.apache.org:8443/gateway/default/webhdfs/v1/tmp?op=LISTSTATUS" 
$  curl -b c.txt -k -i -H 'WWW-Authenticate: Negotiate YGwGCSqGSIb3EgECAgIAb10wW6ADAgEFoQMCAQ+iTzBNoAMCARCiRgREyTPm8S3VhWLJgckAfCtBtaF4ppYYN+LXFDVA4bu9q/zQo1MXAo2A2OaIoaOKVNil3NGXh/oIYcalWb6baxsSeF8giT8=' "https://u1401.ambari.apache.org:8443/gateway/default/webhdfs/v1/tmp?op=LISTSTATUS"

curl -c c.txt参数表示把http响应中的Set-Cookie字段写入了本地的c.txt文件。curl -b c.txt表示带着c.txt文件中的cookie内容发送请求,并利用-H参数设置了令牌。

windows下chrome测试

现在用浏览器(我用的windows下chrome)访问地址https://u1401.ambari.apache.org:8443/gateway/default/webhdfs/v1/tmp?op=LISTSTATUS,会弹出类似基础认证的窗口让输入用户名口令,但无论输入啥都报告401或403错误:

HTTP ERROR 403
Problem accessing /index.html. Reason: 
    GSSException: Defective token detected (Mechanism level: GSSHeader did not find the right tag)

利用chrome插件EditThisCookie,在u1401.ambari.apache.org域中写入之前用curl返回的cookie:

hadoop.auth="u=guest&p=guest/[email protected]&t=kerberos&e=1487947765114&s=fNpq9FYy2DA19Rah7586rgsAieI="

然后浏览器就能显示正确的HDFS下文件清单了。这说明服务器端的配置正确,但浏览器端无法像kinit一样建立kerberos上下文。
windows下可以安装kerberos客户端,但目前还没有找到在windows下利用kinit登录到u1401.ambari.apache.org的办法。可能与windows自带的kerberos功能冲突了。

linux桌面下SPNEGO测试(firefox)

参考
利用vagrant安装了ubuntu16桌面系统,在Vagrantfile中配置的box:

u1408.vm.box = "box-cutter/ubuntu1404-desktop"

利用virtaulbox的菜单"设备->安装增强功能"调大了ubuntu桌面的分辨率(要先为VM添加光驱)。
在ubuntu桌面上组合按键"ctrl+alt+f2"进入命令行状态,按"ctrl+alt+f7"返回图形界面。在命令行状态下安装kerberos客户端并登录:

$ apt install krb5-user

需要注意的是ubuntu桌面系统的当前用户是vagrant而不是root。执行kinit也必须在vagrant用户下,否则图形界面中的firefox将无法与kinit共享会话。按"ctrl+alt+f2",以vagrant用户登录(密码也是vagrant)。

$ kinit guest/[email protected]

启用firefox的negotiate认证

火狐默认不启用negotiate认证。需要在火狐中手工配置启用negotiate的URI。
按"ctrl+alt+f7"返回桌面,点击桌面左侧的火狐图标,在火狐地址栏输入:about:config并回车。然后在search输入框中输入"negotiate",双击配置项network.negotiate-auth.trusted-uris,输入.ambari.apache.org(别忽略最前面的点)。

测试firefox的negotiate认证

在火狐浏览器地址栏中输入:

https://u1401.ambari.apache.org:8443/gateway/default/webhdfs/v1/tmp?op=LISTSTATUS

火狐可能会提示"你的连接是不安全的",点"高级"按钮将这个地址添加到例外。然后就可以看到浏览器以json格式显示了HDFS中/tmp目录下的文件清单。
这说明firefox与kerberos客户端共享了会话。在火狐中按"shift+ctrl+i"进入调试模式,刷新URL请求,可以看到cookie的内容:

hadoop.auth="u=guest&p=guest/[email protected]&t=kerberos&e=1487947765114&s=fNpq9FYy2DA19Rah7586rgsAieI="

由于cookie的存在,浏览器不会每次请求都与kerberos客户端交互,只有cookie失效后,浏览器才会与kerberos客户交互,重新认证。

SPNEGO原理浅析

SPNEGO是基于GSSAPI的,SPENGO认证的发起方是浏览器(或类似客户端),通信协议是HTTP。SPNEGO的由服务器端发起,会在http响应头上放置: WWW-Authenticate:Negotiate,浏览器收到这个响应后,会试图通过GSSAPI调用本地的kerberos客户端。如果本地存在kerberos票据缓存,kerberos客户端会将kerberos票据返回给GSSAPI调用者(猜的,反正是类似的东西)。浏览器收到票据,就构造出下一个http请求发往服务器,http请求头的格式是: Authorization: Negotiate YIIC4QYJKoZIhvcSAQ(后略)。Authorization中应包含了kerberos票据。
如果本地没有kerberos票据缓存或服务器不认可票据,windows的IE/Chrome会弹出内置窗口让用户输入用户名口令(与基础认证类似)。

knox作为认证中心(IdP)

可参考HDP文档apache knox官方文档

knox自带knoxsso.xml这个拓扑文件。该文件利用ShiroProvider这个认证插件连接到LDAP,提供了基于表单的认证和SSO功能。
knoxsso通过一个URL:/gateway/knoxsso/api/v1/websso接受“重定向”请求。用户需要在一个表单中输入用户名和口令,通过验证后会在cookie中写入一个hadoop-jwt令牌,并重定向会原始URL。
[这个knoxssol.xml]是我修改后的拓扑文件。修改的条目是:

main.ldapRealm.userDnTemplate="uid={0},ou=People,dc=ambari,dc=apache,dc=org"
main.ldapRealm.contextFactory.url=ldap://c7301.ambari.apache.org
knoxsso.cookie.secure.only=false
knoxsso.redirect.whitelist.regex="^https?:\/\/(c73\d\d\.ambari\.apache\.org|localhost|127\.0\.0\.1|0:0:0:0:0:0:0:1|::1):[0-9].*$"

这个knoxsso的测试比较麻烦,因为它只接受“重定向”请求。重定向响应头中的location类似:

https://c7301.ambari.apache.org:8443/gateway/knoxsso/api/v1/websso?originalUrl=https://c7301.ambari.apache.org:8443/gateway/tomcat/tomcatui

URL中originaluRL参数的值是原始URL。不知道怎么用简单的办法模拟这个重定向。下面的章节会涉及到ambari与knoxsso的单点登录、tomcat应用与knoxsso的单点登录。

Ambari单点登录到Knox

原文,也可参考HDP的有关文档
Knox提供了基于表单的认证(默认IDP)。利用它,可以实现Ambari与Knox的单点登录。
knox提供的登录页面URL:

https://u1401.ambari.apache.org:8443/gateway/knoxsso/knoxauth/login.html

登录页的样子:
对应用来说,单点登录的端点是:

https://u1401.ambari.apache.org:8443/gateway/knoxsso/api/v1/websso

可以把上面的URL输入到浏览器中测试,发现当没有登录时,会被重定向到登录页面URL。由于登录页面URL是可以配置的,这带来了灵活性,比如可以自己开发个性化登录页。
之前无论是测试Knox与LDAP的集成(ShiroProvider)还是SPNEGO/Kerberos认证(HadoopAuth),都操作的Ambari中Knox配置的Advanced topology,而单点登录需要操作的是Ambari中Knox配置的Advanced knoxsso-topology
在配置中修改了如下内容:

<param>
  <name>knoxsso.redirect.whitelist.regex</name>
  <value>^https?:\/\/(u14\d\d\.ambari\.apache\.org|localhost|127\.0\.0\.1|0:0:0:0:0:0:0:1|::1):[0-9].*$</value>
</param>

这样是为了接受来自u1401-u1410等节点的重定向请求?
还有就是确保参数 knoxsso.cookie.secure.only为false,这是因为ambari并没有启用https。

提取Knox的gateway-identity公钥

$ cd /usr/hdp/current/knox-server
$ keytool -exportcert -keystore data/security/keystores/gateway.jks -alias gateway-identity -rfc -file gateway.pem
Enter keystore password:{master secret}
Certificate stored in file <gateway.pem>
$ cat gateway.pem
–—BEGIN CERTIFICATE–— 
(公钥略)
–—END CERTIFICATE–—

生成了gateway.pem的文件,里面是公钥。过程中用到的master secret保存在文件data/security/master中(密文)。master secret好像是安装knox时定义的,也叫主密码。
重启knox服务。然后设置ambari单点登录:

$ ambari-server setup-sso
Using python  /usr/bin/python
Setting up SSO authentication properties...
Do you want to configure SSO authentication [y/n] (y)?y
Provider URL [URL]: https://u1401.ambari.apache.org:8443/gateway/knoxsso/api/v1/websso
Public Certificate pem (empty) (empty line to finish input):
(输入或粘贴文件gateway.pem中的公钥,并回车两次)
Do you want to configure advanced properties [y/n] (n) ?n
Ambari Server 'setup-sso' completed successfully.
$ ambari-server restart
(要取消SSO,就再次运行ambar-server setup-sso并重启服务)

在浏览器中输入:http://u1401.ambari.apache.org:8080,用户会被重定向到knox的登录界面。在chrome中使用DevTools监测到的重定向请求(UrlEncode后):

https://u1401.ambari.apache.org:8443/gateway/knoxsso/api/v1/websso?originalUrl=http://u1401.ambari.apache.org:8080/#/login?redirected=true

重定向的响应还是个302的重定向,以下是响应中Location值:

Location: https://u1401.ambari.apache.org:8443/gateway/knoxsso/knoxauth/login.html?originalUrl=http://u1401.ambari.apache.org:8080/#/login?redirected=true

上面的websso和login.html两个URL均在前文中提到。
输入用户名john和密码johnldap后点登录,发出的请求:

https://u1401.ambari.apache.org:8443/gateway/knoxsso/api/v1/websso?originalUrl=http://u1401.ambari.apache.org:8080/
(请求头中的信息如下,Basic后面的乱码是john:johnldap的base64编码)
Authorization:Basic am9objpqb2hubGRhcA==

从上面的请求可以看出,KnoxSSO的表单认证(默认IDP)遵循了http基础认证规范。
再看响应,Knox验证john密码通过,返回了307重定向,并在设置cookie:

Set-Cookie: JSESSIONID=1tgu9opzwl60m10c5ogw2yf5da; Path=/gateway/knoxsso
Set-Cookie: rememberMe=deleteMe; Path=/gateway/knoxsso

然后在根路径上写入了hadoop-jwt的cookie:

Set-Cookie: hadoop-jwt=eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJqb2huIiwiaXNzIjoiS05PWFNTTyIsImV4cCI6MTQ5Njg4MjI0NX0.(略);Path=/;Domain=.ambari.apache.org

这个JWT令牌base64解码后:

{"alg":"RS256"}.{"sub":"john","iss":"KNOXSSO","exp":1496882245}.(乱码)

这个JWT令牌有在线工具可以解析和验证,地址:https://jwt.io/#debugger。将前面的eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJqb2huIiwiaXNzIjoiS05PWFNTTyIsImV4cCI6MTQ5Njg4MjI0NX0.(略)复制到工具的Encoded输入框中,然后在VERIFY SIGNATURE框中粘贴前文提到的knox公钥(公钥包括–—BEGIN CERTIFICATE–—和END这两行,但在执行ambari-server setup-sso粘贴时却不能包含这两行),如果令牌验证通过,下面大按钮就显示“Signature Verified”。

http响应的最后是指定了重定向的地址,这个地址是最初通过url参数originalUrl传递给Knox的:

Location: http://u1401.ambari.apache.org:8080/

小结:通过这个请求/响应,KnoxSSO验证了john的密码,为域ambari.apache.org的根路径写入了JWT令牌,并重定向回Ambari的入口页面。

可以用CURL模拟JWT的发放过程:

$  curl -c t.txt -ivk -u john:johnldap https://u1401.ambari.apache.org:8443/gateway/knoxsso/api/v1/websso?originalUrl=http://u1401.ambari.apache.org:8080/
(省略一些,-c t.txt参数表示将cookie内容写入t.txt文件中)
Set-Cookie: hadoop-jwt=eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJqb2huIiwiaXNzIjoiS05PWFNTTyIsImV4cCI6MTQ5NjkwMTE5M30.F9H2EMP-8uDx74AbNJTYlWoLRfm05uFRpvfheoP8fhmKSN_j4UZR1R9Updn0FZsG3OGBM8QsLO2u828yK6f-dPhg_0HntXxGjYE64lnX6_GoLOvBp1CugUGVGH5iOkgWUiKOW3xzxGI2oP3n3pSV_gdt5vKfq2RO8-fAT2r0Xg4;Path=/;Domain=.ambari.apache.org;HttpOnly
Location: http://u1401.ambari.apache.org:8080/
$ curl -b t.txt -iv http://u1401.ambari.apache.org:8080/

下面的curl请求模拟了ambari收到附带JWT的请求,-b t.txt参数表示把t.txt文件中的cookie信息一起发出。如果ambari配置正确则应直跳过登录页面,直接把用户重定向到ambari内部首页。

测试Knox的用户模拟

需要先说明的是,hadoop测试集群的三个节点的操作系统均没有一个john的用户。Knox启用的是ShiroProvider认证方式。现在通过Knox的网关API在HDFS上创建一个/tmp/john的目录,注意这个目录的拥有者:

$ curl -k -i -u john:johnldap -X PUT "https://u1401.ambari.apache.org:8443/gateway/default/webhdfs/v1/tmp/john?op=MKDIRS"
$ curl  -k -u john:johnldap -X GET 'https://u1401.ambari.apache.org:8443/gateway/default/webhdfs/v1/tmp?op=LISTSTATUS'
(省略一些返回值)
      {
        "accessTime": 0,
        "blockSize": 0,
        "childrenNum": 0,
        "fileId": 44214,
        "group": "hdfs",
        "length": 0,
        "modificationTime": 1496819976113,
        "owner": "john",
        "pathSuffix": "john",
        "permission": "755",
        "replication": 0,
        "storagePolicy": 0,
        "type": "DIRECTORY"
      },

这个新建目录的拥有者是john。看上去简单。实际上这里面包含了“用户模拟”。knox服务使用自己的kerberos凭据访问HDFS,但成功模拟了最终用户john在HDFS上创建目录。
关于用户模拟的原理可看考hadoop安全

用户组映射

关于用户组查找的apache官方文档
为了更好地研究用户组映射功能,在新环境(centos7.3)中用ambari重新安装knox和openldap(1),然后用ShiroProvider将openldap配置为knox的用户存储(参照章节"ShiroProvider(LDAP认证)")。

LDAP中增加用户、用户组

部署了LDAP工具ldapscripts,利用ldapscripts添加用户webb和用户组wang

$ sudo ldapaddgroup wang                 (新增用户组wang)
$ sudo ldapadduser webb wang             (添加用户webb,隶属于用户组wang。webb条目多了gidNumber=5002属性)
$ sudo ldapsetpasswd webb                (为webb用户设置密码为"1")
$ sudo ldapaddusertogroup webb wang      (在用户组wang中添加用户webb。wang条目多了memberUid=webb属性)

配置knox的用户模拟

在Ambair面板中通过HDFS-config的Custom core-site小节配置:

hadoop.proxyuser.knox.groups = wang

重启相关服务后测试knox对webhdfs的反向代理功能:

$ curl -k -u webb:1 'https://c7301.ambari.apache.org:8443/gateway/default/webhdfs/v1/tmp?op=LISTSTATUS'
message":"Failed to obtain user group information: org.apache.hadoop.security.authorize.AuthorizationException: User: knox is not allowed to impersonate webb"

提示不能获取用户webb的用户组信息。说明knox的默认LDAP设置不能正确找到用户组。

不控制的用户模拟

将knox的用户模拟用户组设置为"*":

hadoop.proxyuser.knox.groups = *

这表示所有用户组的用户都可以被模拟
再执行上面curl,发现可以正常执行。说明当不控权限时,不需要根据用户查找用户组,所以knox对webhdfs的反向代理可以正常执行。

在本地创建用户和用户组

在knox安装的节点手工创建用户和用户组:

$ sudo groupadd wang                     (增加用户组)
$ useradd webb -g wang                   (增加用户并指定用户组)

将knox的用户模拟用户组设置恢复为:

hadoop.proxyuser.knox.groups = wang

重新测试knox对webhdfs的反向代理功能,执行成功:

$ curl -k -u webb:1 'https://c7301.ambari.apache.org:8443/gateway/default/webhdfs/v1/tmp?op=LISTSTATUS'

说明knox利用了linux本地的“根据用户id查找用户组”功能。

LDAP与knox用户组权限

首先,将users2.ldif导入到LDAP中。
找到knox的GATEWAY_HOME(如/usr/hdp/2.6.1.0-129/knox),新建一个文件<GATEWAY_HOME>/conf/topologies/sample5.xml

<topology>
    <gateway>
        <provider>
            <role>authentication</role>
            <name>ShiroProvider</name>
            <enabled>true</enabled>
            <param name="main.ldapRealm" value="org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm"/>
            <param name="main.ldapContextFactory" value="org.apache.hadoop.gateway.shirorealm.KnoxLdapContextFactory"/>
            <param name="main.ldapRealm.contextFactory" value="$ldapContextFactory"/>

            <param name="main.ldapRealm.contextFactory.url" value="ldap://c7301.ambari.apache.org:389"/>
            <param name="main.ldapRealm.contextFactory.systemUsername" value="cn=admin,dc=ambari,dc=apache,dc=org"/>
            <param name="main.ldapRealm.contextFactory.systemPassword" value="1"/>

            <param name="main.ldapRealm.userSearchBase" value="ou=People,dc=ambari,dc=apache,dc=org"/>
            <param name="main.ldapRealm.userSearchAttributeName" value="uid"/>
            <param name="main.ldapRealm.userObjectClass" value="person"/>

            <param name="main.ldapRealm.authorizationEnabled" value="true"/>
            <param name="main.ldapRealm.groupSearchBase" value="ou=Group,dc=ambari,dc=apache,dc=org"/>
            <param name="main.ldapRealm.groupObjectClass" value="groupofnames"/>
            <param name="main.ldapRealm.groupIdAttribute" value="cn"/>
            <param name="main.ldapRealm.memberAttribute" value="member"/>

            <param name="urls./**" value="authcBasic"/>
        </provider>
               <provider>
                    <role>identity-assertion</role>
                    <name>Default</name>
                    <enabled>true</enabled>
                </provider>

                <provider>
                    <role>authorization</role>
                    <name>AclsAuthz</name>
                    <enabled>true</enabled>
                    <param name="knox.acl" value="*;scientist;*"/>
                </provider>

    </gateway>

    <service>
        <role>KNOX</role>
    </service>
</topology>

用knox自带的测试工具进行测试:

$ /usr/hdp/2.6.1.0-129/knox/bin/knoxcli.sh user-auth-test --cluster default --u sam --p '1' --g
LDAP authentication successful!
sam is a member of: scientist
sam is a member of: analyst
$ /usr/hdp/2.6.1.0-129/knox/bin/knoxcli.sh user-auth-test --cluster default --u tom --p '1' --g
LDAP authentication successful!
tom is a member of: analyst

上面的测试说明sam隶属于scientist组,而tom不是。
<param name="knox.acl" value="*;scientist;*"/>的含义是只有scientist用户组的用户可以通过sample5拓扑文件访问Knox管理API。
用curl测试:

$ curl -ik -u sam:1 'https://c7301.ambari.apache.org:8443/gateway/sample5/api/v1/version'
HTTP/1.1 200 OK
$ curl -ik -u tom:1 'https://c7301.ambari.apache.org:8443/gateway/sample5/api/v1/version'
HTTP/1.1 403 Forbidden

说明knox的用户组权限起作用了。

Knox做tomcat反向代理(表单认证)

原文
同nginx、httpd相比knox自带了基础认证,当用户通过了knox的认证,则对于tomcat的反向代理就可以启用了。这里的tomcat代替了java web应用的位置。
knox默认拓扑文件是default.xml,本文会自建一个tomcat.xml的拓扑文件。通过ambari面板可以修改default.xml,但自定义的拓扑文件只能通过文件系统来修改了。knox拓扑文件的位置是/usr/hdp/current/knox-server/conf/topologies
在上述目录下创建tomcat.xml,内容如下:

<?xml version="1.0" ?>
<topology>
<gateway>
   <provider>
      <role>authentication</role>
      <name>ShiroProvider</name>
      <enabled>true</enabled>
      <param>
         <name>sessionTimeout</name>
         <value>30</value>
      </param>
     <param>
        <name>main.ldapRealm</name>
        <value>org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm</value>
     </param>
      <param>
        <name>main.ldapRealm.userDnTemplate</name>
        <value>uid={0},ou=People,dc=ambari,dc=apache,dc=org</value>
      </param>
      <param>
          <name>main.ldapRealm.contextFactory.url</name>
          <value>ldap://c7301.ambari.apache.org</value>
     </param>
     <param>
       <name>main.ldapRealm.contextFactory.authenticationMechanism</name>
       <value>simple</value>
    </param>
    <param>
       <name>urls./**</name>
       <value>authcBasic</value>
    </param>
</provider>
<provider>
   <role>identity-assertion</role>
   <name>Default</name>
   <enabled>true</enabled>
</provider>
</gateway>

  <service>
     <role>TOMCAT</role>
     <url>http://c7302.ambari.apache.org:8080</url>
 </service>
</topology>

从上述配置文件可以看出,tomcat的部署位置是节点c7302,端口是8080。knox的用户存储在c7301节点的OpenLDAP中。看一下ldap中的用户信息:

$ ldapsearch -x -b uid=sam,ou=People,dc=ambari,dc=apache,dc=org
dn: uid=sam,ou=People,dc=ambari,dc=apache,dc=org
objectClass: account
objectClass: posixAccount
cn: sam
uid: sam
uidNumber: 10004
gidNumber: 5004
homeDirectory: /home/sam
loginShell: /bin/sh
gecos: sam
description: User account

sam用户的密码是1。其实已经通过sssd配置了pam认证,所以可以用su - sam来验证用户和密码(1)。

下面创建TOMCAT这个服务,主要是service.xml和rewrite.xml:

$ cd /usr/hdp/current/knox-server/data/services
$ mkdir -p tomcat/9.0
$ cd tomcat/9.0

在上面的目录下创建service.xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<service role="TOMCAT" name="tomcat" version="9.0">
   <routes>
     <route path="/tomcatui/">
     </route>

     <route path="/tomcatui/**">
     </route>

     <route path="/tomcatui/**?**">
     </route>

   </routes>
</service>

创建rewrite.xml:

<rules>
<rule dir="IN" name="TOMCAT/root/inbound" pattern="*://*:*/**/tomcatui/">
   <rewrite template="{$serviceUrl[TOMCAT]}/"/>
</rule>

<rule dir="IN" name="TOMCAT/path/inbound" pattern="*://*:*/**/tomcatui/{**}">
    <rewrite template="{$serviceUrl[TOMCAT]}/{**}"/>
</rule>

<rule dir="IN" name="TOMCAT/full/inbound" pattern="*://*:*/**/tomcatui/{**}?{**}">
        <rewrite template="{$serviceUrl[TOMCAT]}/{**}?{**}"/>
</rule>
</rules>

通过ambari面板重启knox服务。
先用curl测试一下:

$ curl -i -k -u sam:1 'https://c7301.ambari.apache.org:8443/gateway/tomcat/tomcatui'

会显示很多html代码,证明knox反向代理起作用了。在html的代码的前面,还可以看到响应头的中写入的cookie:

Set-Cookie: JSESSIONID=mnjvpmnb72l91bton0ku2e6x5;Path=/gateway/tomcat;Secure;HttpOnly

这是tomcat写的java会话id。
可以去掉基础认证看看:

$ curl -i -k 'https://c7301.ambari.apache.org:8443/gateway/tomcat/tomcatui'
HTTP/1.1 401 Unauthorized

说明knox的基础认证是起作用的。
在浏览器(如chrome)中输入地址https://c7301.ambari.apache.org:8443/gateway/tomcat/tomcatui,根据提示添加例外(表示这是个受信任的网站)。在浏览器弹出的基础认证输入框中输入用户名sam和密码1,点确定就显示了tomcat的首页。

knox做tomcat反向代理(knoxsso认证)

knoxsso内置了基于表单的认证,可以充当认证中心(IdP)。
上一章中利用了knox网关的基础认证功能实现了tomcat应用的认证功能,但基础认证功能弱,浏览器弹出的凭据输入窗口也丑。
java web应用与knoxsso集成,常见的做法是java web应用配置过滤器,当检测到knox令牌(hadoop-jwt)不存在或过期时重定向到knoxsso(IdP)端点。还有一种更简单的非侵入式做法,就是直接利用knox网关充当sso agent,让knox网关检测hadoop-jwt令牌并重定向到knoxsso端点。本章主要验证这种认证方式。

根据HDP的文档,knoxsso支持ambari、ranger、atlas的单点登录。实际上,经过测试发现,knox的网关可以经过配置可以充当SSO代理,代替应用实现与knoxsso的单点登录。

准备工作

  1. 按章节knox作为认证中心(IdP)配置好knoxsso.xml。
  2. 按章节Knox做tomcat反向代理(表单认证)准备好tomcat运行环境(c7302节点的8080端口)。

配置拓扑文件

由于LDAP的配置是knoxsso.xml中配置的,新的tomcat.xml变得更简单了:

<?xml version="1.0" ?>
<topology>
<gateway>

    <provider>
        <role>federation</role>
        <name>SSOCookieProvider</name>
        <enabled>true</enabled>
        <param>
            <name>sso.authentication.provider.url</name>
            <value>https://c7301.ambari.apache.org:8443/gateway/knoxsso/api/v1/websso</value>
        </param>
    </provider>

<provider>
   <role>identity-assertion</role>
   <name>Default</name>
   <enabled>true</enabled>
</provider>
</gateway>

  <service>
     <role>TOMCAT</role>
     <url>http://c7302.ambari.apache.org:8080</url>
 </service>
</topology>

新的拓扑文件,把provider由authentication改成了federation。其它的不变。上述配置的原理可参考apache的这个knox官方文档。文档中的解释摘录如下:

  • SSOCookieProvider检查hadoop-jwt cookie,并在其缺少时重定向到配置的SSO提供程序URL(knoxsso端点)
  • 配置的KnoxSSO端点提供者程序以特定的方式(呈现表单,重定向到SAML IdP等)挑战用户
  • KnoxSSO上的认证提供商通过凭证/令牌验证用户的身份
  • WebSSO服务将规范化的Java Subject转换为JWT令牌,并将其作为响应设置为名为hadoop-jwt的cookie
  • WebSSO服务然后将用户代理重定向到原始请求的URL - 所请求的Knox服务后续调用将在传入请求中找到cookie,而不需要再次使用WebSSO服务,直到它过期。

修改好tomcat.xml后通过ambari面板重启knox服务。

测试

在浏览器中输入:https://c7301.ambari.apache.org:8443/gateway/tomcat/tomcatui。发现被重定向到地址:https://c7301.ambari.apache.org:8443/gateway/knoxsso/knoxauth/login.html?originalUrl=https://c7301.ambari.apache.org:8443/gateway/tomcat/tomcatui
这是一个输入用户名口令的表单,输入sam:1,点“Sign In”按钮。sam用户定义在LDAP中。用户被重定向回原始URL:https://c7301.ambari.apache.org:8443/gateway/tomcat/tomcatui,并且cookie中写入了:

hadoop-jwt=eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJzYW0iLCJpc3MiOiJLTk9YU1NPIiwiZXhwIjoxNTA0NzUwMzgyfQ.pWLkoNrtx5-93yRT7Lt3XCHSZJ5rqIDpSI4hD0BHtL6HeWPv_GanoC2ZSeD0vTMkX9fv3tG7zz_2WsAWwgijix9wegAZl-58dMdD-6sZI0VIyCsyYZxFhk-9PY7foi9jtE7GpUy5WbFnF9px1eYOnX_lfawo_ZKvUoL0xJbukUA

由于cookie hadoop-jwt的存在,knox网关不再拦截请求,请求被反向代理到tomcat,tomcat的首页显示出来了。

备忘

knox安装目录: /usr/hdp/current/knox-server
ambari的knox目录:/var/lib/ambari-server/resources/stacks/HDP/2.5/services/KNOX
Support HUE interacting with a Hadoop cluster via the Gateway
How to configure and troubleshoot a Knox topology

Clone this wiki locally