本文简要概述了如何使用 YARN 安全容器中的 Linux Container Executor (LCE),以及如何使用 JavaSandboxLinuxContainerRuntime。参考资料除了官网资料 之外,还有YARN-5280 与网络资料。
YARN 安全容器
安全集群中的 YARN 容器使用操作系统工具为容器提供执行隔离。安全容器在作业用户的凭据下执行。操作系统对容器实施访问限制。容器必须作为提交应用程序的用户运行。安全容器仅在安全 YARN 集群的上下文中工作。
容器隔离要求
容器执行器必须访问容器所需的本地文件和目录,例如 jars、配置文件、日志文件、共享对象等。虽然它是由 NodeManager 启动的,但容器不应访问 NodeManager 私有文件和配置。不同用户提交的容器运行应用程序应相互隔离,不能互相访问文件和目录。类似的要求适用于其他系统非文件安全对象,如命名管道、临界区、LPC 队列、共享内存等。
Linux Secure Container Executor
在 Linux 环境中,安全容器执行器是 LinuxContainerExecutor。 它使用一个名为 container-executor
的外部程序来启动容器。 该程序设置了 setuid
访问权限标志,允许它以 YARN 应用程序用户的权限启动容器。
这个 container-executor
的 setuid
二进制文件是 hadoop-yarn
包的一部分。这个容器执行程序仅在 YARN 上使用并仅在 GNU/Linux 上受支持。它要求在启动容器的集群主机上创建所有用户帐户。它使用包含在 Hadoop 发行版中的 setuid
可执行文件,NodeManager 使用这个可执行文件来启动和停止容器。 setuid
可执行文件切换到已提交应用程序并启动或停止容器的用户。
yarn.nodemanager.local-dirs
和 yarn.nodemanager.log-dirs
的配置目录必须由配置的 NodeManager 用户 (yarn
) 和组 (hadoop
) 拥有。对这些目录设置的权限必须是 drwxr-xr-x
。container-executor
程序必须由 root
拥有、被运行 YARN 守护进程的用户组拥有(例如hadoop
)、被 root:hadoop
拥有(如果你的 YARN 用户组为hadoop
)并具有权限集 ---Sr-s---
(6050 权限)。
要将 NodeManager 配置为使用 LinuxContainerExecutor,在 conf/yarn-site.xml
中配置:
yarn-site.xml 1 2 3 4 5 6 7 8 <property > <name > yarn.nodemanager.container-executor.class</name > <value > org.apache.hadoop.yarn.server.nodemanager.LinuxContainerExecutor</value > </property > <property > <name > yarn.nodemanager.linux-container-executor.group</name > <value > hadoop</value > </property >
此外,LCE 需要 container-executor.cfg
文件,该文件由 container-executor
程序读取:
container-executor.cfg 1 2 3 4 5 yarn.nodemanager.linux-container-executor.group=hadoop#configured value of yarn.nodemanager.linux-container-executor.group banned.users=hdfs,mapred#comma separated list of users who can not run applications allowed.system.users=yarn#comma separated list of allowed system users min.user.id=1000#Prevent other super-users feature.terminal.enabled=1
终端功能 (feature.terminal.enabled
) 允许通过 YARN UI2 将受限 shell
放入安全容器中。
除了 $HADOOP_HOME/bin/container-executor
需要 6050 权限(且被 root:hadoop
用户拥有)以外,$HADOOP_HOME
本身需要 755 或更低的权限(不能给与用户组的写权限)。
在实际操作中,官网下载的 Hadoop 其所有者为 1000:1000
,在 yarn
用户的 uid=1000
、hadoop
组的 gid=1000
的前提下,个人使用如下配置:
1 2 3 4 5 6 7 8 sudo chmod +x $HADOOP_HOME /etc/hadoop/*.sh sudo chown root:hadoop $HADOOP_HOME /bin/container-executor sudo chmod 6050 $HADOOP_HOME /bin/container-executor sudo chown root:hadoop $HADOOP_HOME /etc/hadoop/container-executor.cfg sudo chmod 0440 $HADOOP_HOME /etc/hadoop/container-executor.cfg sudo chown root:hadoop $HADOOP_HOME /etc/hadoop sudo chown root:hadoop $HADOOP_HOME /etc sudo chown root:hadoop $HADOOP_HOME
conf/yarn-site.xml
中的可选配置:
yarn-site.xml 1 2 3 4 5 6 7 8 9 10 11 12 <property > <name > yarn.nodemanager.linux-container-executor.resources-handler.class</name > <value > org.apache.hadoop.yarn.server.nodemanager.util.DefaultLCEResourcesHandler</value > </property > <property > <name > yarn.nodemanager.linux-container-executor.nonsecure-mode.limit-users</name > <value > true</value > </property > <property > <name > yarn.nodemanager.linux-container-executor.nonsecure-mode.local-user</name > <value > yarn</value > </property >
使用 Java Sandbox 启动应用程序:JavaSandboxLinuxContainerRuntime
JavaSandboxLinuxContainerRuntime 是 YARN 中 JVM 沙箱的一个实现。用户可以给 JVM 程序配置默认的安全策略,或者使用自带的安全策略。用户也可以配置非 JVM 程序禁止或不在安全模式下运行。这个类扩展了 DefaultLinuxContainerRuntime。它的基本原理就是为container生成相应的Java安全策略文件,修改Java命令以应用这些策略。
要开启JavaSandboxLinuxContainerRuntime,需要进行以下配置:
yarn-site.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <property > <name > yarn.nodemanager.runtime.linux.allowed-runtimes</name > <value > default,docker,javasandbox</value > </property > <property > <name > yarn.nodemanager.runtime.linux.type</name > <value > default</value > </property > <property > <name > yarn.nodemanager.runtime.linux.sandbox-mode</name > <value > enforcing</value > </property > <property > <name > yarn.nodemanager.runtime.linux.sandbox-mode.local-dirs.permissions</name > <value > read</value > </property > <property > <name > yarn.nodemanager.runtime.linux.sandbox-mode.policy.group.hadoop</name > <value > /data/nodemanager/java.policy</value > </property >
下面进行逐条解释。
首先,yarn.nodemanager.runtime.linux.allowed-runtimes
需要添加javasandbox
以允许 JVM 沙箱运行。yarn.nodemanager.runtime.linux.type
可以不做改动,因为 JVM 沙箱的运行不是通过这条配置来进行探测的。
对于yarn.nodemanager.runtime.linux.sandbox-mode
,配置如下:
disabled
: 关闭LinuxContainerRuntime
permissive
: JVM容器会遵循安全策略,非JVM容器正常运行
enforcing
: JVM容器会遵循安全策略,非JVM容器禁止运行,抛出异常
yarn.nodemanager.runtime.linux.sandbox-mode.local-dirs.permissions
默认为read
,可以配置为read,write,execute,delete中的一个或多个。
yarn.nodemanager.runtime.linux.sandbox-mode.policy
默认为空。它接收一个本地文件系统路径(Java策略文件),作为基础策略。如果不指定,则使用默认的策略文件。相应地,yarn.nodemanager.runtime.linux.sandbox-mode.policy.group.$groupName
用于将指定group映射到指定策略。如果一个用户属于多个组,则策略相互叠加。在这里,我们指定hadoop
组的默认策略。
yarn.nodemanager.runtime.linux.sandbox-mode.whitelist-group
为可选配置,可以指定某个队列上的应用不遵循沙箱策略。
在配置时,推荐给mapred-site
增加如下配置,避免找不到JAVA_HOME
:
mapred-site.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <property > <name > yarn.app.mapreduce.am.env</name > <value > JAVA_HOME=/usr/local/openjdk-8</value > </property > <property > <name > yarn.app.mapreduce.am.command-opts</name > <value > -Xmx1024m -Djava.ext.dirs=file:$JAVA_HOME/jre/lib/ext </value > </property > <property > <name > mapred.child.env</name > <value > JAVA_HOME=/usr/local/openjdk-8</value > </property > <property > <name > mapred.child.java.opts</name > <value > -Djava.ext.dirs=file:$JAVA_HOME/jre/lib/ext </value > </property >
其中JAVA_HOME
需要根据你的本地机器指定。在安全策略中,默认的策略可以在 Hadoop 源码中找到,这里给出其中的关键部分:
java.policy 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 grant codeBase "file:${{java.ext.dirs}}/*" { permission java.security.AllPermission; }; grant { permission java.lang.RuntimePermission "accessDeclaredMembers" ; permission java.util.PropertyPermission "java.version" , "read" ; permission java.util.PropertyPermission "java.vendor" , "read" ; permission java.util.PropertyPermission "java.vendor.url" , "read" ; permission java.util.PropertyPermission "java.class.version" , "read" ; permission java.util.PropertyPermission "os.name" , "read" ; permission java.util.PropertyPermission "os.version" , "read" ; permission java.util.PropertyPermission "os.arch" , "read" ; permission java.util.PropertyPermission "file.separator" , "read" ; permission java.util.PropertyPermission "path.separator" , "read" ; permission java.util.PropertyPermission "line.separator" , "read" ; permission java.util.PropertyPermission "java.specification.version" , "read" ; permission java.util.PropertyPermission "java.specification.vendor" , "read" ; permission java.util.PropertyPermission "java.specification.name" , "read" ; permission java.util.PropertyPermission "java.vm.specification.version" , "read" ; permission java.util.PropertyPermission "java.vm.specification.vendor" , "read" ; permission java.util.PropertyPermission "java.vm.specification.name" , "read" ; permission java.util.PropertyPermission "java.vm.version" , "read" ; permission java.util.PropertyPermission "java.vm.vendor" , "read" ; permission java.util.PropertyPermission "java.vm.name" , "read" ; permission java.util.PropertyPermission "awt.Toolkit" , "read" ; permission java.util.PropertyPermission "file.encoding" , "read" ; permission java.util.PropertyPermission "file.encoding.pkg" , "read" ; permission java.util.PropertyPermission "hadoop.metrics.log.level" , "read" ; permission java.util.PropertyPermission "hadoop.root.logger" , "read" ; permission java.util.PropertyPermission "java.awt.graphicsenv" ,"read" ; permission java.util.PropertyPermission "java.awt.printerjob" , "read" ; permission java.util.PropertyPermission "java.class.path" , "read" ; permission java.util.PropertyPermission "yarn.app.container.log.dir" , "read" ; permission java.util.PropertyPermission "yarn.app.container.log.filesize" , "read" ; permission java.lang.RuntimePermission "loadLibrary.gplcompression" ; permission javax.security.auth.AuthPermission "getSubject" ; };
以 MapReduce 程序为例,启动脚本会被修改为如下形式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $JAVA_HOME /bin/java -Djava.security.manager -Djava.security.policy==/tmp/hadoop-yarn/nm-sandbox-policies/container_e18_1653552682428_0001_01_000001-java.policy -Djava.security.debug=all -Djava.io.tmpdir=$PWD /tmp -Dlog4j.configuration=container-log4j.properties -Dyarn.app.container.log.dir=/usr/local/hadoop/logs/userlogs/application_1653552682428_0001/container_e18_1653552682428_0001_01_000001 -Dyarn.app.container.log.filesize=0 -Dhadoop.root.logger=INFO,CLA -Dhadoop.root.logfile=syslog -Xmx1024m -Djava.ext.dirs=file:$JAVA_HOME /jre/lib/ext org.apache.hadoop.mapreduce.v2.app.MRAppMaster 1>/usr/local/hadoop/logs/userlogs/application_1653552682428_0001/container_e18_1653552682428_0001_01_000001/stdout 2>/usr/local/hadoop/logs/userlogs/application_1653552682428_0001/container_e18_1653552682428_0001_01_000001/stderr
使用 Docker 容器启动应用程序:DockerLinuxContainerRuntime
Linux Container Executor (LCE) 允许 YARN NM 启动 YARN 容器以直接在主机上或在 Docker 容器内运行。请求资源的应用程序可以为每个容器指定它应该如何执行。当 LCE 启动 YARN 容器以在 Docker 容器中执行时,应用程序可以指定要使用的 Docker 镜像。Docker 容器提供了一个自定义的执行环境,应用程序的代码在其中运行,与 NM 和其他应用程序的执行环境隔离。Docker 容器甚至可以运行与 NM 上不同风格的 Linux。YARN 的 Docker 提供一致性(所有 YARN 容器将具有相同的软件环境)和隔离(不干扰物理机上安装的任何内容)。
LCE 要求容器执行器二进制文件由 root:hadoop
拥有并具有 6050 权限。为了启动 Docker 容器,Docker 守护进程必须在所有 NM 主机上运行。Docker 客户端还必须安装在所有相关的 NM 主机上。
有关此功能的相关配置,请参见Cluster Configuration 。