

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 添加 Jupyter notebook 用户和管理员
<a name="emr-jupyterhub-user-access"></a>

您可以使用两种方法中的一种让用户进行身份验证， JupyterHub 以便他们可以创建笔记本并可以选择进行管理 JupyterHub。最简单的方法是使用 JupyterHub可插拔身份验证模块 (PAM)。此外，在亚马逊 JupyterHub 上，EMR 支持 [LDAP 身份验证器插件， JupyterHub用于](https://github.com/jupyterhub/ldapauthenticator/)从 LDAP 服务器（例如微软 Active Directory 服务器）获取用户身份。此部分提供了通过每种身份验证方法添加用户的说明和示例。

JupyterHub 在 Amazon 上，EMR 有一个具有管理员权限的默认用户。此用户名为 `jovyan`，密码为 `jupyter`。强烈建议将此用户替换为另一个具有管理员权限的用户。您可以在创建集群时使用一个步骤以执行该操作，也可以在集群运行时连接到主节点以执行该操作。

**Topics**
+ [使用 PAM 身份验证](emr-jupyterhub-pam-users.md)
+ [使用 LDAP 身份验证](emr-jupyterhub-ldap-users.md)
+ [用户模拟](emr-jupyterhub-user-impersonation.md)

# 使用 PAM 身份验证
<a name="emr-jupyterhub-pam-users"></a>

在 Amazon EMR JupyterHub 上创建 PAM 用户是一个分为两个步骤的过程。第一步是，将用户添加到在主节点上的 `jupyterhub` 容器中运行的操作系统，以及为每个用户添加一个相应的用户主目录。第二步是将这些操作系统用户添加为 JupyterHub用户，此过程称为白名单。 JupyterHub添加 JupyterHub 用户后，他们可以连接到 JupyterHub URL 并提供其操作系统凭据进行访问。

当用户登录时，会 JupyterHub 打开该用户的笔记本服务器实例，该实例保存在主节点上用户的主目录中，即`/var/lib/jupyter/home/username`。如果笔记本服务器实例不存在，则会在用户的主目录中 JupyterHub 生成一个笔记本实例。以下各节演示如何将用户单独添加到操作系统和操作系统中 JupyterHub，接下来是添加多个用户的基本 bash 脚本。

## 将操作系统用户添加到容器
<a name="emr-jupyterhub-system-user"></a>

以下示例先在容器内使用 [useradd](https://linux.die.net/man/8/useradd) 命令添加单个用户 diego 并为该用户创建一个主目录。第二个命令使用 [chpasswd](https://linux.die.net/man/8/chpasswd) 为此用户设置密码 diego。在使用 SSH 连接时，命令将在主节点命令行上运行。还可以使用步骤运行这些命令，如之前的[通过提交步骤管理](emr-jupyterhub-administer.md#emr-jupyterhub-administer-steps)中所述。

```
sudo docker exec jupyterhub useradd -m -s /bin/bash -N diego
sudo docker exec jupyterhub bash -c "echo diego:diego | chpasswd"
```

## 添加 JupyterHub 用户
<a name="emr-jupyterhub-jupyterhub-user"></a>

您可以使用中的**管理**面板 JupyterHub 或 REST API 来添加用户和管理员，或者只添加用户。

**使用中的管理面板添加用户和管理员 JupyterHub**

1. 使用 SSH 连接到主节点，然后使用*MasterNodeDNS*具有管理员权限的身份登录 https: //: 9443。

1. 选择 **Control Panel (控制面板)**、**Admin (管理员)**。

1. 选择 **User (用户)**、**Add Users (添加用户)**，或选择 **Admin (管理员)**、**Add Admins (添加管理员)**。

**使用 REST API 添加用户**

1. 使用 SSH 连接到主节点并在主节点上使用以下命令，或将此命令作为步骤运行。

1. 获取管理令牌以发出 API 请求，然后*AdminToken*在以下步骤中使用该令牌替换。

1. 使用以下命令，*UserName*替换为在容器中创建的操作系统用户。

   ```
   curl -XPOST -H "Authorization: token AdminToken" "https://$(hostname):9443/hub/api/users/UserName
   ```

**注意**  
首次登录 JupyterHub Web 界面时，系统会自动将您添加为 JupyterHub 非管理员用户。

## 示例：用于添加多个用户的清除脚本
<a name="emr-jupyterhub-script-multuser"></a>

以下示例 bash 脚本结合了本节中前面的步骤，创建了多个 JupyterHub 用户。此脚本可以直接在主节点上运行，也可上载到 Amazon S3 并在之后作为步骤运行。

此脚本先建立一组用户名，并使用 `jupyterhub token` 命令为默认管理员 jovyan 创建一个 API 令牌。然后，它在 `jupyterhub` 容器中为每个用户创建一个操作系统用户，以为每个用户分配一个与其用户名相同的初始密码。最后，它调用 REST API 操作在中创建每个用户 JupyterHub。它在脚本中传递之前生成的令牌并将 REST 响应传输到 `jq` 以方便查看。

```
# Bulk add users to container and JupyterHub with temp password of username
set -x
USERS=(shirley diego ana richard li john mary anaya)
TOKEN=$(sudo docker exec jupyterhub /opt/conda/bin/jupyterhub token jovyan | tail -1)
for i in "${USERS[@]}"; 
do 
   sudo docker exec jupyterhub useradd -m -s /bin/bash -N $i
   sudo docker exec jupyterhub bash -c "echo $i:$i | chpasswd"
   curl -XPOST --silent -k https://$(hostname):9443/hub/api/users/$i \
 -H "Authorization: token $TOKEN" | jq
done
```

将此脚本保存到 Amazon S3 中的位置（如 `s3://amzn-s3-demo-bucket/createjupyterusers.sh`）。然后，可以使用 `script-runner.jar` 将此脚本作为步骤运行。

### 示例：创建集群时运行脚本（AWS CLI）
<a name="emr-jupyterhub-multuser-createcluster"></a>

**注意**  
为了便于读取，包含 Linux 行继续符（\$1）。它们可以通过 Linux 命令删除或使用。对于 Windows，请将它们删除或替换为脱字号（^）。

```
aws emr create-cluster --name="MyJupyterHubCluster" --release-label emr-5.36.2 \
--applications Name=JupyterHub --log-uri s3://amzn-s3-demo-bucket/MyJupyterClusterLogs \
--use-default-roles --instance-type m5.xlarge --instance-count 2 --ec2-attributes KeyName=MyKeyPair \
--steps Type=CUSTOM_JAR,Name=CustomJAR,ActionOnFailure=CONTINUE,\
Jar=s3://region.elasticmapreduce/libs/script-runner/script-runner.jar,Args=["s3://amzn-s3-demo-bucket/createjupyterusers.sh"]
```

### 在现有集群上运行脚本（AWS CLI）
<a name="emr-jupyterhub-multuser-runningcluster"></a>

**注意**  
为了便于读取，包含 Linux 行继续符（\$1）。它们可以通过 Linux 命令删除或使用。对于 Windows，请将它们删除或替换为脱字号（^）。

```
aws emr add-steps --cluster-id j-XXXXXXXX --steps Type=CUSTOM_JAR,\
Name=CustomJAR,ActionOnFailure=CONTINUE,\
Jar=s3://region.elasticmapreduce/libs/script-runner/script-runner.jar,Args=["s3://amzn-s3-demo-bucket/createjupyterusers.sh"]
```

# 使用 LDAP 身份验证
<a name="emr-jupyterhub-ldap-users"></a>

轻型目录访问协议 (LDAP) 是一种应用程序协议，用于查询和修改与 LDAP 兼容目录服务提供程序（如 Active Directory 或 OpenLDAP server）中存储的资源（如用户和计算机）对应的对象。您可以在 Amazon EMR JupyterHub 上使用的 [LDAP 身份验证器插件](https://github.com/jupyterhub/ldapauthenticator/)使用 LDAP 进行用户身份验证。 JupyterHub此插件处理 LDAP 用户的登录会话并为 Jupyter 提供用户信息。这允许用户使用存储在 JupyterHub 兼容 LDAP 的服务器中的身份凭证来连接和笔记本电脑。

本节中的步骤将引导您完成以下步骤，使用的 LDAP 身份验证器插件设置和启用 LDAP。 JupyterHub在连接到主节点命令行时执行这些步骤。有关更多信息，请参阅[连接到主节点和 Notebook 服务器](emr-jupyterhub-connect.md)。

1. 创建一个包含 LDAP 服务器相关信息（如主机 IP 地址、端口、绑定名称等）的 LDAP 配置文件。

1. 修改 `/etc/jupyter/conf/jupyterhub_config.py` 以启用适用于 JupyterHub 的 LDAP Authenticator 插件。

1. 创建并运行在 `jupyterhub` 容器内配置 LDAP 的脚本。

1. 查询用户的 LDAP，然后在容器中为每个用户创建主目录。 JupyterHub 需要主目录来托管笔记本电脑。

1. 运行可重新启动的脚本 JupyterHub

**重要**  
在设置 LDAP 之前，测试网络基础设施以确保 LDAP 服务器和集群主节点可以根据需要进行通信。TLS 一般通过普通 TCP 连接使用端口 389。如果 LDAP 连接使用 SSL，那么显而易见，适用于 SSL 的 TCP 端口为 636。

## 创建 LDAP 配置文件
<a name="emr-jupyterhub-ldap-config"></a>

下方的示例使用以下占位符配置值。将这些值替换为与您的实施匹配的参数。
+ 正在运行的是 LDAP 服务器版本 3，通过端口 389 提供。这是适用于 LDAP 的标准非 SSL 端口。
+ 基本可分辨名称 (DN) 为 `dc=example, dc=org`。

使用文本编辑器创建内容与下类似的 [ldap.conf](http://manpages.ubuntu.com/manpages/bionic/man5/ldap.conf.5.html) 文件。使用适用于 LDAP 实施的值。*host*替换为 LDAP 服务器的 IP 地址或可解析的主机名。

```
base dc=example,dc=org
uri ldap://host
ldap_version 3
binddn cn=admin,dc=example,dc=org
bindpw admin
```

## 启用 LDAP 身份验证器插件 JupyterHub
<a name="emr-jupyterhub-ldap-plugin"></a>

使用文本编辑器修改 `/etc/jupyter/conf/jupyterhub_config.py` 文件并添加与下类似的 [ldapauthenticator](https://github.com/jupyterhub/ldapauthenticator) 属性。*host*替换为 LDAP 服务器的 IP 地址或可解析的主机名。该示例假设用户对象位于名为的组织单位 (ou) 中*people*，并使用您之前使用建立的可分辨名称组件`ldap.conf`。

```
c.JupyterHub.authenticator_class = 'ldapauthenticator.LDAPAuthenticator'
c.LDAPAuthenticator.use_ssl = False
c.LDAPAuthenticator.server_address = 'host' 
c.LDAPAuthenticator.bind_dn_template = 'cn={username},ou=people,dc=example,dc=org'
```

## 在容器内配置 LDAP
<a name="emr-jupyterhub-ldap-container"></a>

使用文本编辑器创建包含以下内容的清除脚本：

```
#!/bin/bash

# Uncomment the following lines to install LDAP client libraries only if
# using Amazon EMR release version 5.14.0. Later versions install libraries by default.
# sudo docker exec jupyterhub bash -c "sudo apt-get update"
# sudo docker exec jupyterhub bash -c "sudo apt-get -y install libnss-ldap libpam-ldap ldap-utils nscd"
 
# Copy ldap.conf
sudo docker cp ldap.conf jupyterhub:/etc/ldap/
sudo docker exec jupyterhub bash -c "cat /etc/ldap/ldap.conf"
 
# configure nss switch
sudo docker exec jupyterhub bash -c "sed -i 's/\(^passwd.*\)/\1 ldap/g' /etc/nsswitch.conf"
sudo docker exec jupyterhub bash -c "sed -i 's/\(^group.*\)/\1 ldap/g' /etc/nsswitch.conf"
sudo docker exec jupyterhub bash -c "sed -i 's/\(^shadow.*\)/\1 ldap/g' /etc/nsswitch.conf"
sudo docker exec jupyterhub bash -c "cat /etc/nsswitch.conf"
 
# configure PAM to create home directories
sudo docker exec jupyterhub bash -c "echo 'session required        pam_mkhomedir.so skel=/etc/skel umask=077' >> /etc/pam.d/common-session"
sudo docker exec jupyterhub bash -c "cat /etc/pam.d/common-session"
 
# restart nscd service
sudo docker exec jupyterhub bash -c "sudo service nscd restart"
 
# Test
sudo docker exec jupyterhub bash -c "getent passwd"

# Install ldap plugin
sudo docker exec jupyterhub bash -c "pip install jupyterhub-ldapauthenticator"
```

将脚本保存到主节点，然后从主节点命令行运行它。例如，对于另存为 `configure_ldap_client.sh` 的脚本，使此文件成为可执行文件：

```
chmod +x configure_ldap_client.sh
```

并运行此脚本：

```
./configure_ldap_client.sh
```

## 将属性添加到 Active Directory
<a name="emr-jupyterhub-ldap-adproperties"></a>

要查找每个用户并在数据库中创建相应的条目， JupyterHub docker 容器需要 Active Directory 中相应用户对象的以下 UNIX 属性。有关更多信息，请参阅 “*既然 Unix 属性插件不再适用于 Active Directory 用户和计算机 MM GID/UID C 管理单元，如何继续编辑 RFC 2307 属性*？ 在《[澄清 Windows Server 2016 技术预览版及以后的 Unix 身份管理 (IDMU) 和 NIS 服务器角色的状态》一文中](https://blogs.technet.microsoft.com/activedirectoryua/2016/02/09/identity-management-for-unix-idmu-is-deprecated-in-windows-server/)。
+ `homeDirectory`

  这是用户主目录的位置，通常是 `/home/username`。
+ `gidNumber`

  这是一个大于 60000 的值，尚未被其它用户使用。检查 `etc/passwd` 文件中是否有正在使用的 GID。
+ `uidNumber`

  这是一个大于 60000 的值，尚未被其它组使用。检查 `etc/group` 文件中是否有正在使用的 UID。
+ `uid`

  这与*username*.

## 创建用户主目录
<a name="emr-jupyterhub-ldap-directories"></a>

JupyterHub 需要容器内的主目录来对 LDAP 用户进行身份验证并存储实例数据。以下示例演示了 LDAP 目录中的两个用户 *shirley* 和 *diego*。

第一步是使用 [ldapsearch 向 LDAP 服务器查询每个用户的用户 ID 和组 ID 信息，如以下示例所示，*host*替换为 LD](http://manpages.ubuntu.com/manpages/xenial/man1/ldapsearch.1.html) AP 服务器的 IP 地址或可解析的主机名：

```
ldapsearch -x -H ldap://host \
 -D "cn=admin,dc=example,dc=org" \
 -w admin \
 -b "ou=people,dc=example,dc=org" \
 -s sub \
 "(objectclass=*)" uidNumber gidNumber
```

此 `ldapsearch` 命令将为 *shirley* 和 *diego* 用户返回看上去与下类似的 LDIF 格式的响应。

```
# extended LDIF

# LDAPv3
# base <ou=people,dc=example,dc=org> with scope subtree
# filter: (objectclass=*)
# requesting: uidNumber gidNumber sn 

# people, example.org
dn: ou=people,dc=example,dc=org

# diego, people, example.org
dn: cn=diego,ou=people,dc=example,dc=org
sn: B
uidNumber: 1001
gidNumber: 100

# shirley, people, example.org
dn: cn=shirley,ou=people,dc=example,dc=org
sn: A
uidNumber: 1002
gidNumber: 100

# search result
search: 2
result: 0 Success

# numResponses: 4
# numEntries: 3
```

通过使用响应中的信息，在容器内运行命令以为每个用户公用名 (`cn`) 创建一个主目录。使用 `uidNumber` 和 `gidNumber` 确定用户对主目录的所有权。以下示例命令可为用户执行此操作*shirley*。

```
sudo docker container exec jupyterhub bash -c "mkdir /home/shirley"
sudo docker container exec jupyterhub bash -c "chown -R $uidNumber /home/shirley"
sudo docker container exec jupyterhub bash -c "sudo chgrp -R $gidNumber /home/shirley"
```

**注意**  
的 LDAP 身份验证器 JupyterHub 不支持创建本地用户。有关更多信息，请参阅[关于本地用户创建的 LDAP 身份验证器配置说明](https://github.com/jupyterhub/ldapauthenticator#configuration-note-on-local-user-creation)。  
要手动创建本地用户，请使用以下命令。  

```
sudo docker exec jupyterhub bash -c "echo 'shirley:x:$uidNumber:$gidNumber::/home/shirley:/bin/bash' >> /etc/passwd"
```

## 重启 JupyterHub 容器
<a name="emr-jupyterhub-ldap-restart"></a>

运行以下命令重新启动 `jupyterhub` 容器：

```
sudo docker stop jupyterhub
sudo docker start jupyterhub
```

# 用户模拟
<a name="emr-jupyterhub-user-impersonation"></a>

在 Amazon EMR 上执行期间，在 Jupyter notebook 中运行的 Spark 作业将访问多个应用程序。例如，Sparkmagic 接收到用户在 Jupyter 中运行的 PySpark 3 个代码，Sparkmagic 使用 HTTP POST 请求将其提交给 Livy，然后使用 YARN 创建一个 Spark 作业在集群上执行。

默认情况下，以这种方式提交的 YARN 作业以 `livy` 用户身份运行，而不管启动该作业的用户如何。通过设置*用户模拟*，您也可以将 Notebook 用户的用户 ID 作为与 YARN 作业关联的用户。每个用户启动的作业分别与 `shirley` 和 `diego` 相关联，而不是由与 `livy` 用户关联的 `shirley` 和 `diego` 同时启动作业。这有助于审核 Jupyter 使用情况以及在组织中管理应用程序。

只有在从 Sparkmagic 到 Livy 的调用未进行身份验证时，才支持该配置。不支持在 Hadoop 应用程序和 Livy 之间提供身份验证或代理层的应用程序（如 Apache Knox Gateway）。本节中配置用户模拟的步骤假设 JupyterHub 和 Livy 在同一个主节点上运行。如果您的应用程序具有单独的集群，则需要修改 [步骤 3：为用户创建 HDFS 主目录](#Step3-UserImpersonation)，以便在 Livy 主节点上创建 HDFS 目录。

**Topics**
+ [步骤 1：配置 Livy](#Step1-UserImpersonation)
+ [步骤 2：添加用户](#Step2-UserImpersonation)
+ [步骤 3：为用户创建 HDFS 主目录](#Step3-UserImpersonation)

## 步骤 1：配置 Livy
<a name="Step1-UserImpersonation"></a>

在创建集群时，您可以使用 `livy-conf` 和 `core-site` 配置分类启用 Livy 用户模拟，如以下示例所示。将配置分类保存为 JSON，然后在创建集群时引用该分类，或者指定内联的配置分类。有关更多信息，请参阅[配置应用程序](emr-configure-apps.md)。

```
[
  {
    "Classification": "livy-conf",
    "Properties": {
      "livy.impersonation.enabled": "true"
    }
  },
  {
    "Classification": "core-site",
    "Properties": {
      "hadoop.proxyuser.livy.groups": "*",
      "hadoop.proxyuser.livy.hosts": "*"
    }
  }
]
```

## 步骤 2：添加用户
<a name="Step2-UserImpersonation"></a>

使用 PAM 或 LDAP 添加 JupyterHub 用户。有关更多信息，请参阅[使用 PAM 身份验证](emr-jupyterhub-pam-users.md)和[使用 LDAP 身份验证](emr-jupyterhub-ldap-users.md)。

## 步骤 3：为用户创建 HDFS 主目录
<a name="Step3-UserImpersonation"></a>

您已连接到主节点以创建用户。在仍连接到主节点时，复制以下内容并将其保存到脚本文件中。该脚本为主节点上的每个 JupyterHub 用户创建 HDFS 主目录。该脚本假设您使用的是默认管理员用户 ID *jovyan*。

```
#!/bin/bash

CURL="curl --silent -k"
HOST=$(curl -s http://169.254.169.254/latest/meta-data/local-hostname)

admin_token() {
    local user=jovyan
    local pwd=jupyter
    local token=$($CURL https://$HOST:9443/hub/api/authorizations/token \
        -d "{\"username\":\"$user\", \"password\":\"$pwd\"}" | jq ".token")
    if [[ $token != null ]]; then
        token=$(echo $token | sed 's/"//g')
    else
        echo "Unable to get Jupyter API Token."
        exit 1
    fi
    echo $token
}

# Get Jupyter Admin token
token=$(admin_token)

# Get list of Jupyter users
users=$(curl -XGET -s -k https://$HOST:9443/hub/api/users \
 -H "Authorization: token $token" | jq '.[].name' | sed 's/"//g')

# Create HDFS home dir 
for user in ${users[@]}; 
do
 echo "Create hdfs home dir for $user"
 hadoop fs -mkdir /user/$user
 hadoop fs -chmod 777 /user/$user
done
```