Fork me on GitHub

CDH| 安全之Kerberos安全认证

 

 

1 Kerberos概述

 1 什么是Kerberos

Kerberos是一种计算机网络授权协议,用来在非安全网络中,对个人通信以安全的手段进行身份认证。这个词又指麻省理工学院为这个协议开发的一套计算机软件。软件设计上采用客户端/服务器结构,并且能够进行相互认证,即客户端和服务器端均可对对方进行身份认证。可以用于防止窃听、防止重放攻击、保护数据完整性等场合,是一种应用对称密钥体制进行密钥管理的系统。

 2. Kerberos简单了解

Kerberos中有以下一些概念需要了解:

  1KDC:密钥分发中心,负责管理发放票据,记录授权。

  2域:Kerberos管理领域的标识。

  3principal:当每添加一个用户或服务的时候都需要向kdc添加一条principalprincipl的形式为:主名称/实例名@领域名。

  4主名称:主名称可以是用户名或服务名,还可以是单词host,表示是用于提供各种网络服务(如hdfs,yarn,hive)的主体。

  5实例名:实例名简单理解为主机名。

  6领域:Kerberos的域。

3 Kerberos认证原理

1)客户端初始验证

                            

2)获取服务访问

                    

2 安装

server节点安装kerberos相关软件

[root@hadoop101 ~]# yum install -y krb5-server krb5-workstation krb5-libs

[root@hadoop101 ~]# rpm -qa | grep krb5
krb5-server-1.10.3-65.el6.x86_64
krb5-libs-1.10.3-65.el6.x86_64
krb5-workstation-1.10.3-65.el6.x86_64

client节点安装

Hadoop102上的安装:

[root@hadoop102 ~]# yum install -y krb5-workstation krb5-libs
已加载插件:fastestmirror, refresh-packagekit, security...
总下载量:1.6 M
下载软件包:
(1/3): krb5-libs-1.10.3-65.el6.x86_64.rpm                                                                                                           | 675 kB     00:00     
(2/3): krb5-workstation-1.10.3-65.el6.x86_64.rpm                                                                                                    | 814 kB     00:00     
(3/3): libkadm5-1.10.3-65.el6.x86_64.rpm                                                                                                            | 143 kB     00:00     
--------------------------------------------------------------------------
[root@hadoop102 ~]# rpm -qa | grep krb5
  krb5-workstation-1.10.3-65.el6.x86_64
  krb5-libs-1.10.3-65.el6.x86_64

 Hadoop103上的安装:

[root@hadoop103 ~]# yum install -y krb5-workstation krb5-libs
已加载插件:fastestmirror, refresh-packagekit, security
设置安装进程

 

配置kerberos

需要配置的文件有两个为kdc.confkrb5.conf , 配置只是需要Server服务节点配置,即hadoop101.

1) kdc配置

[root@hadoop101 ~]# vim /var/kerberos/krb5kdc/kdc.conf

[kdcdefaults]
 kdc_ports = 88
 kdc_tcp_ports = 88

[realms]
 HADOOP.COM = {
  #master_key_type = aes256-cts
  acl_file = /var/kerberos/krb5kdc/kadm5.acl
  dict_file = /usr/share/dict/words
  admin_keytab = /var/kerberos/krb5kdc/kadm5.keytab
  supported_enctypes = aes128-cts:normal des3-hmac-sha1:normal arcfour-hmac:normal des-hmac-sha1:normal des-cbc-md5:normal des-cbc-crc:normal
 }

说明:

HADOOP.COM:设定的realms,随便起名,Kerberos支持多个realms,一般全用大写

acl_file:admin的用户权限。

admin_keytab:KDC进行校验的keytab

supported_enctypes:支持的校验方式。注意把aes256-cts去掉,JAVA使用aes256-cts验证方式需要安装额外的jar,所有这里不用。

2) krb5文件配置

[root@hadoop101 ~]# vim /etc/krb5.conf

[logging]
 default = FILE:/var/log/krb5libs.log
 kdc = FILE:/var/log/krb5kdc.log
 admin_server = FILE:/var/log/kadmind.log

[libdefaults]
 default_realm = HADOOP.COM
 dns_lookup_realm = false
 dns_lookup_kdc = false
 ticket_lifetime = 24h
 renew_lifetime = 7d
 udp_preference_limit = 1

[realms]
 HADOOP.COM = {
  kdc = hadoop101
  admin_server = hadoop101
 }

[domain_realm]
 .example.com = HADOOP.COM
 example.com = HADOOP.COM

说明:l

default_realm:默认的realm,设置 Kerberos 应用程序的默认领域,必须跟要配置的realm的名称一致。如果有多个领域,只需向 [realms] 节添加其他的语句。

ticket_lifetime:表明凭证生效的时限,一般为24小时。

renew_lifetime : 表明凭证最长可以被延期的时限,一般为一个礼拜。当凭证过期之后,对安全认证的服务的后续访问则会失败。

udp_preference_limit= 1:禁止使用 udp,可以防止一个 Hadoop 中的错误

[realms]:列举使用的 realm。

3)同步krb5Client节点

[root@hadoop101 ~]# xsync /etc/krb5.conf
fname=krb5.conf
pdir=/etc
------------------- hadoop102 --------------
sending incremental file list
krb5.conf

sent 506 bytes  received 37 bytes  362.00 bytes/sec
total size is 429  speedup is 0.79
------------------- hadoop103 --------------
sending incremental file list
krb5.conf

sent 506 bytes  received 37 bytes  1086.00 bytes/sec
total size is 429  speedup is 0.79

 

生成Kerberos数据库

server节点执行

[root@hadoop101 ~]# kdb5_util create -s
Loading random data
Initializing database '/var/kerberos/krb5kdc/principal' for realm 'HADOOP.COM',
master key name 'K/M@HADOOP.COM'
You will be prompted for the database Master Password.
It is important that you NOT FORGET this password.
Enter KDC database master key: 123456
Re-enter KDC database master key to verify: 123456

创建完成后/var/kerberos/krb5kdc目录下会生成对应的文件 [root@hadoop101
~]# ls /var/kerberos/krb5kdc/ kadm5.acl kdc.conf principal principal.kadm5 principal.kadm5.lock principal.ok

  这里会在Loading random data卡很久,可以重新开启一个窗口执行cat /dev/sda > /dev/urandom命令,加快消耗CPU,增加随机数采集。

[root@hadoop101 ~]# cat /dev/sda > /dev/urandom

 

创建管理员主体/实例
[root@hadoop101 ~]# kadmin.local -q "addprinc admin/admin"
Authenticating as principal root/admin@HADOOP.COM with password.
WARNING: no policy specified for admin/admin@HADOOP.COM; defaulting to no policy
Enter password for principal "admin/admin@HADOOP.COM": 123456
Re-enter password for principal "admin/admin@HADOOP.COM": 123456
Principal "admin/admin@HADOOP.COM" created.



给管理员实例的所有主体授权 [root@hadoop101 ~]# vim /var/kerberos/krb5kdc/kadm5.acl #修改为以下内容: */admin@HADOOP.COM

说明:

*:全部的主体

/admin:admin实例

@HADOOP.COM:领域

最后*:全部权限。

这个授权的意思:就是授予admin实例的全部主体对应HADOOP.COM领域的全部权限。

也就是创建Kerberos主体的时候如果实例为admin,就具有HADOOP.COM领域的全部权限,比如创建如下的主体user1/admin就拥有全部的HADOOP.COM领域的权限。

启动服务

#启动krb5kdc

[root@hadoop101 ~]# 
[root@hadoop101 ~]# service krb5kdc start
正在启动 Kerberos 5 KDC:                                  [确定]
[root@hadoop101 ~]# service kadmin start
正在启动 Kerberos 5 Admin Server:                         [确定]
[root@hadoop101 ~]# chkconfig krb5kdc on
[root@hadoop101 ~]# chkconfig |grep krb5kdc
krb5kdc            0:关闭    1:关闭    2:启用    3:启用    4:启用    5:启用    6:关闭
[root@hadoop101 ~]# chkconfig kadmin on
[root@hadoop101 ~]# chkconfig |grep kadmin
kadmin             0:关闭    1:关闭    2:启用    3:启用    4:启用    5:启用    6:关闭
[root@hadoop101 ~]# 

注意:启动失败时可以通过/var/log/krb5kdc.log/var/log/kadmind.log来查看。

kinit管理员验证

[root@hadoop101 ~]# kinit admin/admin
Password for admin/admin@HADOOP.COM: 123456
[root@hadoop101 ~]# klist
Ticket cache: FILE:/tmp/krb5cc_0
Default principal: admin/admin@HADOOP.COM

Valid starting     Expires            Service principal
04/05/20 23:39:44  04/06/20 23:39:44  krbtgt/HADOOP.COM@HADOOP.COM
    renew until 04/05/20 23:39:44

出现以上红色admin/admin@HADOOP.COM说明没问题

在其他机器尝试:

[root@hadoop102 ~]# kinit admin/admin
Password for admin/admin@HADOOP.COM: 123456

[root@hadoop102 ~]# kadmin admin/admin
Authenticating as principal admin/admin@HADOOP.COM with password.
Password for admin/admin@HADOOP.COM: 123456
kadmin:  
如果出现:kadmin: GSS-API (or Kerberos) error while initializing kadmin interface,则重启ntp服务:
[root@hadoop102 ~]# service ntpd restart
关闭 ntpd:                                                [确定]
正在启动 ntpd:                                            [确定]

 

常用命令

1 登录Kerberos

service节点登录:

[root@hadoop101 ~]# kadmin.local 
Authenticating as principal admin/admin@HADOOP.COM with password.
kadmin.local:  

client节点登录:

[root@hadoop102 ~]# kadmin admin/admin
Authenticating as principal admin/admin@HADOOP.COM with password. 123456
Password for admin/admin@HADOOP.COM: 
kadmin:  

退出输入:exit

创建Kerberos主体
[root@hadoop101 ~]# kadmin.local -q "addprinc kris"
Authenticating as principal admin/admin@HADOOP.COM with password.
WARNING: no policy specified for atguigu@HADOOP.COM; defaulting to no policy
Enter password for principal "kris@HADOOP.COM": (输入密码)
Re-enter password for principal "kris@HADOOP.COM": (输入密码)
Principal "kris@HADOOP.COM" created.

修改主体密码 [root@hadoop101 ~]# kadmin.local -q "cpw kris" Authenticating as principal admin/admin@HADOOP.COM with password. Enter password for principal "kris@HADOOP.COM": (输入密码) Re-enter password for principal "kris@HADOOP.COM": (输入密码) Password for "kris@HADOOP.COM" changed.

查看所有主体 [root@hadoop101 ~]# kadmin.local -q "list_principals" Authenticating as principal admin/admin@HADOOP.COM with password. K/M@HADOOP.COM admin/admin@HADOOP.COM kadmin/admin@HADOOP.COM kadmin/changepw@HADOOP.COM kadmin/hadoop102@HADOOP.COM krbtgt/HADOOP.COM@HADOOP.COM
kris@HADOOP.COM

生成keytab密钥文件 生成主体kris的keytab文件到指定目录/root/atguigu.keytab [root@hadoop101 ~]# kadmin.local -q "xst -k /root/kris.keytab kris@HADOOP.COM"


主体认证 使用kinit进行主体认证 [root@hadoop101 ~]# kinit admin/admin Password for admin/admin@HADOOP.COM: 查看认证凭证 [root@hadoop101 ~]# klist Ticket cache: FILE:/tmp/krb5cc_0 Default principal: admin/admin@HADOOP.COM Valid starting Expires Service principal 08/27/19 15:41:28 08/28/19 15:41:28 krbtgt/HADOOP.COM@HADOOP.COM renew until 08/27/19 15:41:28


使用keytab进行认证 [root@hadoop101 ~]# kinit -kt /root/atguigu.keytab atguigu [root@hadoop101 ~]# klist Ticket cache: FILE:/tmp/krb5cc_0 Default principal: atguigu@HADOOP.COM Valid starting Expires Service principal 08/27/19 15:45:09 08/28/19 15:45:09 krbtgt/HADOOP.COM@HADOOP.COM renew until 08/27/19 15:45:09


销毁凭证 [root@hadoop101 ~]# kdestroy [root@hadoop101 ~]# klist klist: No credentials cache found (ticket cache FILE:/tmp/krb5cc_0)

 

安装Kerberos可能出现的问题
1 Kerberos启动后台日志提示异常:No such file or directory - while initializing database for realm HADOOP.COM /var/log/krb5kdc.log中发现No such file or directory - while initializing database for realm HADOOP.COM。
解决方法:
1)检查kdc.conf和krb5.conf文件是否配置正确,修改配置,注意:配置文件的[kdcdefaults],[logging]、[libdefaults]等的里面不能有空格 2)停止服务 service krb5kdc stop service kadmin stop 3)删除Kerberos数据库重新创建数据库 rm -rf /var/kerberos/krb5kdc/*principal* kdb5_util create -s -r HADOOP.COM 4)创建管理员 kadmin.local -q "addprinc admin/admin" 5)启动服务 service krb5kdc start service kadmin start

2 kinit通过keytab认证出现异常 通过Linux的atguigu主体执行kinit -kt /root/atguigu.keytab atguigu出现下面情况: kinit: ???? while getting initial credentials 或者 kinit: Permission denied while getting initial credentials 解决方式: 1)使用root用户修改kris.keytab的所属用户: chown kris/root/kris.keytab 2) 修改atguigu.keytab的权限为660 chmod 660 /root/kris.keytab

3 kinit认证时密码输入正确却提示密码错误 [root@hadoop101 ~]# kinit kris Password for kris@HADOOP.COM: kinit: Password incorrect while getting initial credentials 这是因为kris已经生成了keytab,所以此时通过这种方式不能认证,需要通过keytab文件来认证,或者修改密码后再认证(修改密码后之前的keytab文件会失效)。 [root@hadoop101 ~]# kinit -kt /root/kris.keytab atguigu [root@hadoop101 ~]# klist Ticket cache: FILE:/tmp/krb5cc_0 Default principal: kris@HADOOP.COM Valid starting Expires Service principal 08/27/19 16:00:39 08/28/19 16:00:39 krbtgt/HADOOP.COM@HADOOP.COM renew until 08/27/19 16:00:39

4 创建数据库异常 Bad krb5 admin server hostname while initializing kadmin interface 原因:在 krb5.conf 文件中为 admin_server 配置了无效的主机名。 解决办法:修改krb5文件,检查admin_server配置是否正确的主机名,修改后,重启执行创建数据库的命令。
5 异常问题:Configuration file does not specify default realm when parsing name admin/admin 这是由于krb5.conf文件被删除或者不可用,重新创建即可

 

CDH添加Kerberos

1 创建超级管理员用户

创建cloudera-scm/admin为CDH的Kerberos管理员主体。

[root@hadoop101 ~]# kadmin.local -q "addprinc cloudera-scm/admin"
Authenticating as principal admin/admin@HADOOP.COM with password.
WARNING: no policy specified for cloudera-scm/admin@HADOOP.COM; defaulting to no policy
Enter password for principal "cloudera-scm/admin@HADOOP.COM": 123456
Re-enter password for principal "cloudera-scm/admin@HADOOP.COM": 123456
Principal "cloudera-scm/admin@HADOOP.COM" created.

停止所有服务

开启Kerberos

 

勾选全部

 

填写配置

Kerberos 加密类型:aes128-ctsdes3-hmac-sha1arcfour-hmac

 

 

 cloudera-scm/admin

123456

 

 

 

 

等待完成

这里因为先停止了所有服务,所以第一二步会报错,忽略。

 

 

查看主体

[root@hadoop101 ~]# kadmin.local -q "list_principals"
Authenticating as principal cloudera-scm/admin@HADOOP.COM with password.
HTTP/hadoop101@HADOOP.COM
HTTP/hadoop102@HADOOP.COM
HTTP/hadoop103@HADOOP.COM
K/M@HADOOP.COM
admin/admin@HADOOP.COM
cloudera-scm/admin@HADOOP.COM
hdfs/hadoop101@HADOOP.COM
hdfs/hadoop102@HADOOP.COM
hdfs/hadoop103@HADOOP.COM
hive/hadoop101@HADOOP.COM
hue/hadoop101@HADOOP.COM
impala/hadoop101@HADOOP.COM
impala/hadoop102@HADOOP.COM
impala/hadoop103@HADOOP.COM
kadmin/admin@HADOOP.COM
kadmin/changepw@HADOOP.COM
kadmin/hadoop101@HADOOP.COM
kafka/hadoop101@HADOOP.COM
kafka/hadoop102@HADOOP.COM
kafka/hadoop103@HADOOP.COM
krbtgt/HADOOP.COM@HADOOP.COM
kris@HADOOP.COM
mapred/hadoop101@HADOOP.COM
oozie/hadoop101@HADOOP.COM
sentry/hadoop101@HADOOP.COM
yarn/hadoop101@HADOOP.COM
yarn/hadoop102@HADOOP.COM
yarn/hadoop103@HADOOP.COM
zookeeper/hadoop101@HADOOP.COM
zookeeper/hadoop102@HADOOP.COM
zookeeper/hadoop103@HADOOP.COM

Sentry配置修改

1 停止所有服务

 

管理员组添加admin用户和组

1)点击sentry

 

2)点击配置

 

3)搜索sentry.service.admin.group

 

4)添加admin并删除其他,保留hiveadmin

 

3 连接用户中添加admin用户和组

搜索sentry.service.allow.connect添加admin,并保存。

 

Sentry 服务高级配置代码段

搜索sentry-site.xml Sentry 服务,添加如下内容:

<property>
<name>sentry.service.processor.factories</name>
<value>org.apache.sentry.provider.db.service.thrift.SentryPolicyStoreProcessorFactory,org.apache.sentry.hdfs.SentryHDFSServiceProcessorFactory</value>
</property>
<property>
<name>sentry.policy.store.plugins</name>
<value>org.apache.sentry.hdfs.SentryPlugin</value>
</property>
<property>
<name>sentry.hdfs.integration.path.prefixes</name>
<value>/user/hive/warehouse</value>
</property>

 

 

 

1 进入hdfs配置页面

 

 

开启Hadoop安全身份验证

搜索hadoop.security.authentication,将其改为kerberos

 

 

开启Hadoop安全授权

搜索hadoop.security.authorization,将其勾选。

 

 

修改Hadoop组映射实现

搜索Hadoop.security.group.mapping修改值为:

org.apache.hadoop.security.ShellBasedUnixGroupsMapping

 

启动访问控制列表

搜索dfs.namenode.acls.enabled,将其勾选

 

启动HDFS权限检查,并启动Sentry同步

1)搜索dfs.permissions,勾选HDFS

 

2搜索 Sentry 同步,勾选HDFS

 

修改管理员用户和授权用户

1修改管理员用户

HDFS的配置页面搜索“授权的管理员用户”,默认是*,把*修改为admin,并添加hdfs

 

2)修改授权用户

HDFS的配置页面搜索“授权的用户”,默认是*表示全部的Kerberos用户,修改为:

Cloudera-scm,admin,hdfs,hbase,hive,spark,zookeeper,yarn,mapred,sqoop,sqoop2,oozie,

hue,kafka, flume,impala,storm,solr等,主要是看自己集群有哪些框架。

备注:这里基本常用的组件超级管理员都授权了,避免组件启动异常。

 

 

 

Yarn配置修改

1 修改运行系统用户

 

2 修改最小用户ID

 

3 RM ACL

Yarn配置页搜索“yarn.acl.enable”,修改其值为true

 

Hive配置修改

1 配置Sentry服务

搜索“Sentry 服务”中选择“Sentry”

 

2 HiveServer2关闭模拟

搜索“Hive Impersonation”,取消勾选。

 

3 启动Hive仓库子目录继承权限

搜索“Hive.warehouse.subdir.inherit.perms”,将其勾选。

 

4 设置sentry.hive.testing.modefalse

搜索“hive-site.xml 的 Hive 服务高级配置代码段(安全阀)”添加如下配置:

名称:sentry.hive.testing.mode

值:false

 

4 设置hive.security.authorization.task.factory

搜索“hive-site.xml 的 HiveServer2 高级配置代码段(安全阀)添加如下配置:

名称:hive.security.authorization.task.factory

值:org.apache.sentry.binding.hive.SentryHiveAuthorizationTaskFactoryImpl

 

5 设置Hive Metastor Serverhive-site.xml

搜索“hive-site.xml 的 Hive Metastore Server 高级配置代码段(安全阀)”添加如下配置:

<property>
<name>hive.metastore.client.impl</name>
<value>org.apache.sentry.binding.metastore.SentryHiveMetaStoreClient</value>
<description>Sets custom Hive Metastore client which Sentry uses to filter out metadata.</description>
</property>
<property>
<name>hive.metastore.pre.event.listeners</name>
<value>org.apache.sentry.binding.metastore.MetastoreAuthzBinding</value>
<description>list of comma separated listeners for metastore events.</description>
</property>
<property>
<name>hive.metastore.event.listeners</name>
<value>org.apache.sentry.binding.metastore.SentryMetastorePostEventListener</value>
<description>list of comma separated listeners for metastore, post events.</description>
</property>
<property>
<name>hive.metastore.filter.hook</name>
<value>org.apache.sentry.binding.metastore.SentryMetaStoreFilterHook</value>
</property>

 

10 Zookeeper配置修改

启动Kerberos身份验证。

在Zookeeper设置界面搜索“enableSecurity”,将其勾选。

 

Kafka配置修改

1 设置Kafka安全身份验证

在Kafka的配置中搜索“kerberos.auth.enable”并将其勾选。

 

 

设置Kafka认证方式

在Kafka设置中搜索“security.inter.broker.protoco”修改其值为:SASL_PLAINTEXT。

 

配置kakfa.properties

l 在Kafka的配置页面中搜索“kafka.properties 的 Kafka Broker 高级配置代码段(安全阀)”其值添加如下内容:

authorizer.class.name=kafka.security.auth.SimpleAclAuthorizer

super.users=User:kafka;User:admin;

 

Kafka支持命令行的kinit操作

1创建jaas文件

[root@hadoop101 ~]# vim /etc/kafka/kafka_client_jaas.conf

fkaClient {
com.sun.security.auth.module.Krb5LoginModule required
useTicketCache=true;
};

 

[root@hadoop101 ~]# xsync /etc/kafka/kafka_client_jaas.conf
fname=kafka_client_jaas.conf
pdir=/etc/kafka
------------------- hadoop102 --------------
sending incremental file list
kafka_client_jaas.conf

sent 180 bytes  received 31 bytes  422.00 bytes/sec
total size is 90  speedup is 0.43
------------------- hadoop103 --------------
sending incremental file list
kafka_client_jaas.conf

sent 180 bytes  received 31 bytes  422.00 bytes/sec
total size is 90  speedup is 0.43

2)修改kafka-run-class.sh文件

JVM performance options参数的KAFKA_JVM_PERFORMANCE_OPTS中加入

-Djava.security.auth.login.config=/etc/kafka/kafka_client_jaas.conf

[root@hadoop101 ~]# vim /opt/cloudera/parcels/KAFKA/lib/kafka/bin/kafka-run-class.sh

 

[root@hadoop101 ~]# xsync /opt/cloudera/parcels/KAFKA/lib/kafka/bin/kafka-run-class.sh[root@hadoop101 ~]# vim /etc/kafka/conf/producer.properties
ecurity.protocol=SASL_PLAINTEXT
sasl.mechanism=GSSAPI
sasl.kerberos.service.name=kafka

分发:

[root@hadoop101 ~]# xsync /etc/kafka/conf/producer.properties

 

4)生成consumer.properties文件
[root@hadoop101 ~]# vim /etc/kafka/conf/consumer.properties

添加
security.protocol=SASL_PLAINTEXT
sasl.mechanism=GSSAPI
sasl.kerberos.service.name=kafka

分发:
[root@hadoop101 ~]# xsync /etc/kafka/conf/consumer.properties

5 设置zookeeper.chroot

kafka的配置页面搜索“zookeeper.chroot”,将值修改为:/kafka,并保存更改。

 

 

6 取消Sentry服务

 

部署客户端并启动服务

 

 

 

 

设置windows浏览器授权

我们设置CDH支持kerberos后会出现下图所示的情况:

可以登录50070,但是不能查看目录及文件,这是由于我们本地环境没有通过认证。

接下来我们设置本地验证。

注意:由于浏览器限制问题,我们这里使用火狐浏览器,其他如:谷歌,ie等均会出现问题。

 

 

 

 

 

 

 

3 安装kfw

1)安装提供的kfw-4.1-amd64.msi。

2)将集群的/etc/krb5.conf文件的内容复制到C:\ProgramData\MIT\Kerberos5\krb.ini中。

3)打开MIT,输入主体名和密码:

   https://www.cnblogs.com/kischn/p/7443343.html

 

 

 

 

 

 

posted @ 2020-04-22 11:54  kris12  阅读(4440)  评论(0编辑  收藏  举报
levels of contents