# 安全地运行 Apache Storm

## 安全地运行 Apache Storm

尝试保护集群时, Apache Storm 提供了一系列配置选项. 默认情况下, 所有验证和授权都被禁用, 但可以根据需要打开.

## 防火墙/操作系统 级别安全

您仍然可以拥有安全的 storm 集群, 而无需开启正式的身份验证和授权. 但是这样做通常需要配置您的操作系统来限制可以完成的操作. 即使您计划使用 Auth 运行群集, 通常也是一个好主意.

如何设置这些预防措施的具体细节有很大的不同, 超出了本文档的范围.

通常情况下, 启用防火墙并限制传入网络连接仅限于源自群集本身和来自可信主机和服务的网络连接, 一个完整的端口 storm 使用列表如下.

如果您的群集正在处理的数据是敏感的, 则可能最好设置 IPsec 来加密群集中主机之间发送的所有流量.

### Ports

| 默认端口 | Storm 配置 | Client Hosts/进程 | Server |
| --- | --- | --- | --- |
| 2181 | `storm.zookeeper.port` | Nimbus, Supervisors, and Worker 进程 | Zookeeper |
| 6627 | `nimbus.thrift.port` | Storm clients, Supervisors, and UI | Nimbus |
| 8080 | `ui.port` | Client Web 浏览器 | UI |
| 8000 | `logviewer.port` | Client Web 浏览器 | Logviewer |
| 3772 | `drpc.port` | 外部 DRPC Clients | DRPC |
| 3773 | `drpc.invocations.port` | Worker 进程 | DRPC |
| 3774 | `drpc.http.port` | 外部 HTTP DRPC Clients | DRPC |
| 670{0,1,2,3} | `supervisor.slots.ports` | Worker 进程 | Worker 进程 |

### UI / Logviewer

UI 和 logviewer 进程提供了一种方法, 不仅可以看到集群正在做什么, 还可以操纵运行的 topology.通常, 这些进程不应该被暴露, 除了群集的用户.

通常使用某种形式的身份验证, 使用 java servlet 过滤器.

```
ui.filter: "filter.class"
ui.filter.params: "param1":"value1" 
```

或通过限制 UI / Logviewer 端口仅接受来自本地主机的连接, 然后使用另一个可以验证/授权传入连接并代理与 storm 进程的连接的 Web 服务器（如: Apache httpd）将其连接起来. 为了使此工作, ui 进程必须将 logviewer.port 设置为其 storm.yaml 中的代理端口, 而日志查看器必须将其设置为要绑定到的实际端口.

servlet 过滤器是首选, 因为它允许单独的 topology 来详细说明谁是谁, 谁不被允许访问与它们相关联的页面.

Storm UI 可以配置为使用 hadoop-auth 中的 AuthenticationFilter.

```
ui.filter: "org.apache.hadoop.security.authentication.server.AuthenticationFilter"
ui.filter.params:
   "type": "kerberos"
   "kerberos.principal": "HTTP/nimbus.witzend.com"
   "kerberos.keytab": "/vagrant/keytabs/http.keytab"
   "kerberos.name.rules": "RULE:[2:$1@$0]([jt]t@.*EXAMPLE.COM)s/.*/$MAPRED_USER/  RULE:[2:$1@$0]([nd]n@.*EXAMPLE.COM)s/.*/$HDFS_USER/DEFAULT" 
```

确保创建一个主体 'HTTP/{hostname}'（这里的 hostname 应该是运行 UI 守护进程的主机名)

一旦配置用户需要在访问 UI 之前执行 kinit. 例如：

`curl -i --negotiate -u:anyUser -b ~/cookiejar.txt -c ~/cookiejar.txt http://storm-ui-hostname:8080/api/v1/cluster/summary`

1.  Firefox: 转到 配置并搜索 network.negotiate-auth.trusted-uris 双击以添加值 "[http://storm-ui-hostname:8080](http://storm-ui-hostname:8080)"
2.  Google-chrome: 从命令行开始 google-chrome --auth-server-whitelist="*storm-ui-hostname" --auth-negotiate-delegate-whitelist="*storm-ui-hostname"
3.  IE: 配置受信任的网站以包含 "storm-ui-hostname" 并允许该网站的协商

**注意**: 为了通过 `logviewer` 安全模式查看任何日志, 运行的所有主机 `logviewer` 也应该添加到上述白名单中.对于大集群, 您可以列出主机的域（例如: 设置 `network.negotiate-auth.trusted-uris` 为 `.yourdomain.com`）.

**警告**: 在 AD MIT Keberos 设置中, 密钥大小大于默认的 UI jetty 服务器请求头大小. 确保在 storm.yaml 中将 ui.header.buffer.bytes 设置为 65536 . 更多详细信息, 请参见 [STORM-633](https://issues.apache.org/jira/browse/STORM-633)

## UI / DRPC SSL

UI 和 DRPC 都允许用户配置 ssl.

### UI

对于 UI 用户需要在 storm.yaml 中设置以下配置. 在此步骤之前, 用户必须注意使用合适的密钥和证书生成密钥库.

1.  ui.https.port
2.  ui.https.keystore.type（示例: "jks"）
3.  ui.https.keystore.path（示例: "/etc/ssl/storm_keystore.jks"）
4.  ui.https.keystore.password（密钥库密码）
5.  ui.https.key.password（私钥密码）

可选配置

1.  ui.https.truststore.path（示例: "/etc/ssl/storm_truststore.jks"）
2.  ui.https.truststore.password（信任密码）
3.  ui.https.truststore.type（示例: "jks"）

如果用户想要设置双向认证

1.  ui.https.want.client.auth（如果这设置为客户端认证身份验证的真实服务器请求, 但如果没有提供身份验证, 则保持连接）
2.  ui.https.need.client.auth（如果设置为 true 服务器需要客户端提供身份验证）

### DRPC

与 UI 类似, 用户需要为 DRPC 配置以下内容

1.  drpc.https.port
2.  drpc.https.keystore.type（示例: "jks"）
3.  drpc.https.keystore.path（示例: "/etc/ssl/storm_keystore.jks"）
4.  drpc.https.keystore.password（密钥库密码）
5.  drpc.https.key.password（私钥密码）

可选配置

1.  drpc.https.truststore.path（示例: "/etc/ssl/storm_truststore.jks"）
2.  drpc.https.truststore.password（信任密码）
3.  drpc.https.truststore.type（示例: "jks"）

如果用户想要设置双向认证

1.  drpc.https.want.client.auth（如果这设置为客户端证书认证的真实服务器请求, 但如果没有提供认证, 则保持连接）
2.  drpc.https.need.client.auth（如果设置为 true 服务器需要客户端提供身份验证）

## 认证 (Kerberos)

Storm 通过 thrift 和 SASL 提供可插拔的身份验证支持. 此示例仅适用于 Kerberos , 因为它是大多数大型数据项目的常见设置.

在每个节点上设置 KDC 并配置 kerberos 超出了本文档的范围, 并假设您已经完成了.

### 创建 Headless Principals and keytabs

每个 Zookeeper 服务器, Nimbus 和 DRPC 服务器将需要一个服务的 principal, 按照惯例, 它将包含将运行的主机的 FQDN. 请注意, zookeeper 用户必须是 zookeeper. supervisor 和 UI 还需要一个 principal 来运行, 但由于它们是外向连接, 所以他们不需要是服务的 principal. 以下是如何设置 kerberos principal 的示例, 但细节可能会因您的 KDC 和操作系统而异.

```
# Zookeeper (Will need one of these for each box in teh Zk ensamble)
sudo kadmin.local -q 'addprinc zookeeper/zk1.example.com@STORM.EXAMPLE.COM'
sudo kadmin.local -q "ktadd -k /tmp/zk.keytab  zookeeper/zk1.example.com@STORM.EXAMPLE.COM"
# Nimbus and DRPC
sudo kadmin.local -q 'addprinc storm/storm.example.com@STORM.EXAMPLE.COM'
sudo kadmin.local -q "ktadd -k /tmp/storm.keytab storm/storm.example.com@STORM.EXAMPLE.COM"
# All UI logviewer and Supervisors
sudo kadmin.local -q 'addprinc storm@STORM.EXAMPLE.COM'
sudo kadmin.local -q "ktadd -k /tmp/storm.keytab storm@STORM.EXAMPLE.COM" 
```

确保将keytab分发到相应的框, 并设置 FS 权限, 以便只有运行 ZK 或 storm 的 headless 用户才能访问它们.

#### Storm Kerberos 配置

Storm 和 Zookeeper 都使用 jaas 配置文件来记录用户. 每个 jaas 文件可能会有不同的界面被使用.

要在 storm 中启用 Kerberos 身份验证, 您需要设置以下 storm.yaml 配置:

```
storm.thrift.transport: "org.apache.storm.security.auth.kerberos.KerberosSaslTransportPlugin"
java.security.auth.login.config: "/path/to/jaas.conf" 
```

Nimbus 和 supervisor 进程也将连接到 ZooKeeper(ZK), 我们希望将其配置为使用 Kerberos 进行身份验证. 做这个附加:

```
-Djava.security.auth.login.config=/path/to/jaas.conf 
```

对 nimbus , ui 和 supervisor 的 childopts.给出了写入时, 默认的 childopts 设置的一个例子：

```
nimbus.childopts: "-Xmx1024m  -Djava.security.auth.login.config=/path/to/jaas.conf"
ui.childopts: "-Xmx768m  -Djava.security.auth.login.config=/path/to/jaas.conf"
supervisor.childopts: "-Xmx256m  -Djava.security.auth.login.config=/path/to/jaas.conf" 
```

对于 storm 节点, jaas.conf 文件应如下所示. StormServer 部分由 nimbus 和 DRPC 节点使用.它不需要包括在主管节点上. StormClient 部分被所有想要与 nimbus 通讯的 storm client 使用, 包括 ui, logviewer 和 supervisor. 我们将在网关上使用这一部分, 但其结构将会有所不同. Client 部分被想要与 zookeeper 通讯的进程使用, 并且只需要包含在 nimbus 和 supervisor 中. 服务器部分由 zookeeper 服务器使用. 在 jaas 中没有使用的部分不是问题.

```
StormServer {
   com.sun.security.auth.module.Krb5LoginModule required
   useKeyTab=true
   keyTab="$keytab"
   storeKey=true
   useTicketCache=false
   principal="$principal";
};
StormClient {
   com.sun.security.auth.module.Krb5LoginModule required
   useKeyTab=true
   keyTab="$keytab"
   storeKey=true
   useTicketCache=false
   serviceName="$nimbus_user"
   principal="$principal";
};
Client {
   com.sun.security.auth.module.Krb5LoginModule required
   useKeyTab=true
   keyTab="$keytab"
   storeKey=true
   useTicketCache=false
   serviceName="zookeeper"
   principal="$principal";
};
Server {
   com.sun.security.auth.module.Krb5LoginModule required
   useKeyTab=true
   keyTab="$keytab"
   storeKey=true
   useTicketCache=false
   principal="$principal";
}; 
```

以下是基于 keytab 生成的示例:

```
StormServer {
   com.sun.security.auth.module.Krb5LoginModule required
   useKeyTab=true
   keyTab="/keytabs/storm.keytab"
   storeKey=true
   useTicketCache=false
   principal="storm/storm.example.com@STORM.EXAMPLE.COM";
};
StormClient {
   com.sun.security.auth.module.Krb5LoginModule required
   useKeyTab=true
   keyTab="/keytabs/storm.keytab"
   storeKey=true
   useTicketCache=false
   serviceName="storm"
   principal="storm@STORM.EXAMPLE.COM";
};
Client {
   com.sun.security.auth.module.Krb5LoginModule required
   useKeyTab=true
   keyTab="/keytabs/storm.keytab"
   storeKey=true
   useTicketCache=false
   serviceName="zookeeper"
   principal="storm@STORM.EXAMPLE.COM";
};
Server {
   com.sun.security.auth.module.Krb5LoginModule required
   useKeyTab=true
   keyTab="/keytabs/zk.keytab"
   storeKey=true
   useTicketCache=false
   serviceName="zookeeper"
   principal="zookeeper/zk1.example.com@STORM.EXAMPLE.COM";
}; 
```

Nimbus 还会将 principal 转换成 本地用户名, 以便其他服务可以使用此名称.配置 Kerberos 身份验证集

```
storm.principal.tolocal: "org.apache.storm.security.auth.KerberosPrincipalToLocal" 
```

这只需要在 nimbus 上完成, 但不会对任何节点造成伤害. 我们还需要从 ZooKeeper 的角度通知 topology 谁是 supervisor 守护程序和 nimbus 守护程序正在运行.

```
storm.zookeeper.superACL: "sasl:${nimbus-user}" 
```

这里 _nimbus-user_ 是 nimbus 用于使用 ZooKeeper 进行身份验证的 Kerberos 用户. 如果 ZooKeeeper 正在剥离主机和领域, 那么这需要主机和领域也被剥离.

#### ZooKeeper 集成

关于如何设置安全的 ZK 的完整细节超出了本文档的范围. 但是, 一般来说, 您要在每个服务器上启用 SASL 身份验证, 并可选择剥离主机和领域.

```
authProvider.1 = org.apache.zookeeper.server.auth.SASLAuthenticationProvider
kerberos.removeHostFromPrincipal = true
kerberos.removeRealmFromPrincipal = true 
```

并且您希望在启动服务器时在命令行中包含 jaas.conf, 以便可以使用它来查找 keytab.

```
-Djava.security.auth.login.config=/jaas/zk_jaas.conf 
```

#### 网关

理想情况下, 最终用户只需要在与 storm 交互之前运行 kinit.为了无缝地实现这一点, 我们需要在网关上使用默认的 jaas.conf.

```
StormClient {
   com.sun.security.auth.module.Krb5LoginModule required
   doNotPrompt=false
   useTicketCache=true
   serviceName="$nimbus_user";
}; 
```

如果最终用户具有无头键的用户, 则可以覆盖此 keytab.

### 授权设置

_身份验证_ 完成了验证用户是谁的工作, 但是我们也需要 _授权_ 来执行每个用户可以执行的任务.

nimbus 的首选授权插件是 SimpleACLAuthorizer.要使用 _SimpleACLAuthorizer_, 请设置以下内容：

```
nimbus.authorizer: "org.apache.storm.security.auth.authorizer.SimpleACLAuthorizer" 
```

DRPC 有一个独立的授权器配置. 不要对 DRPC 使用 SimpleACLAuthorizer.

该 _SimpleACLAuthorizer_ 插件需要知道谁主管用户, 它需要知道所有的管理员用户, 包括运行UI守护进程在用户的.

这些通过 _nimbus.supervisor.users_ 和 _nimbus.admins_ 分别设置. 每个可以是一个完整的 Kerberos principal 名称, 也可以是主机和领域的用户名.

日志服务器有自己的授权配置.这些都是通过 _logs.users_ 和 _logs.groups_ 设置的. 应将这些设置为集群中所有节点的管理员用户或组.

提交 topology 时, 提交用户也可以在此列表中指定用户. 指定的用户和组（除了群集范围内的用户）将被授予对日志查看器中提交的 topology 的 worker 日志的访问权限.

### Supervisors 匿名用户 和 组设置

为了确保 multi-tenancy 用户的隔离, 需要在 supervisor 节点上运行 supervisors, 匿名用户 和 唯一组 来执行. 要启用以下步骤.

1.  向所有 supervisor 主机添加 headlessuser.
2.  创建唯一的组, 并使其成为 supervisor 节点上 匿名用户的主组.
3.  对于这些 supervisor 节点, 设置以下 storm 属性.

### Multi-tenant 调度

为了更好地支持 multi-tenancy, 我们已经编写了一个新的调度程序. 启用此调度程序集.

```
storm.scheduler: "org.apache.storm.scheduler.multitenant.MultitenantScheduler" 
```

请注意, 此调度程序的许多功能都依赖于 storm 身份验证. 没有他们, 调度程序将不知道用户是什么, 也不会正确地隔离 topology.

multi-tenant 调度程序的目标是提供一种将 topology 彼此隔离的方法, 但也限制了个人用户可以在群集中拥有的资源.

调度器当前有一个配置, 可以通过 =storm.yaml= 或通过一个名为 =multitenant-scheduler.yaml= 的单独配置文件来设置, 该配置文件应该放在与 =storm.yaml= 相同的目录中. 最好使用 =multitenant-scheduler.yaml=, 因为它可以更新而不需要重新启动 nimbus.

当前只有一个配置 =multitenant-scheduler.yaml=, =multitenant.scheduler.user.pools= 是从用户名的映射到用户保证能够用于其 topology 结构的最大节点数.

例如：

```
multitenant.scheduler.user.pools: 
    "evans": 10
    "derek": 10 
```

### 以提交 topology 的用户身份运行 worker 进程

默认情况下, storm 运行作为运行 supervisor 的用户的 worker. 这不是安全的理想选择. 使 storm 作为启动它们的用户进行 topology 运行.

```
supervisor.run.worker.as.user: true 
```

有几个与此相关的文件需要正确配置以使 storm 安全.

worker-launcher可执行文件是一个特殊程序, 允许 supervisor 以不同的用户身份启动 worker. 为了这个工作, 它需要由 root 拥有, 但是该组被设置为只有 supervisor 匿名用户是其中的一部分的组. 它还需要拥有6550个权限. 还有一个 worker-launcher.cfg 文件, 通常位于 /etc/ 下, 应该如下所示:

```
storm.worker-launcher.group=$(worker_launcher_group)
min.user.id=$(min_user_id) 
```

其中 worker_launcher_group 是 supervisor 其中一部分的同一组, 并且 min.user.id 设置为系统上的第一个真实用户标识. 此配置文件也需要由 root 拥有, 不具有世界或组写权限.

### 冒充一个用户

storm client 可以代表另一个用户提交请求. 例如, 如果 `userX` 提交 oozie 工作流程, 并且作为工作流执行的一部分；如果用户 `oozie` 想要代表 `userX` 它提交 topology, 可以通过利用模拟功能来实现. 为了提交 topology 作为其他用户, 您可以使用 `StormSubmitter.submitTopologyAs` API.或者, 您可以使用 `NimbusClient.getConfiguredClientAs` nimbus client 作为其他用户, 并使用此 client 执行任何 nimbus 操作（即: kill/rebalance/activate/deactivate）.

默认情况下禁用模拟授权, 这意味着任何用户都可以执行模拟. 为了确保只有授权用户可以执行模拟, 您应该启动 nimbus `nimbus.impersonation.authorizer` 设置 `org.apache.storm.security.auth.authorizer.ImpersonationAuthorizer`. 该 `ImpersonationAuthorizer` 用于 `nimbus.impersonation.acl` 为 ACL 对用户进行授权. 以下是用于支持模拟的nimbus配置示例:

```
nimbus.impersonation.authorizer: org.apache.storm.security.auth.authorizer.ImpersonationAuthorizer
nimbus.impersonation.acl:
    impersonating_user1:
        hosts:
            [comma separated list of hosts from which impersonating_user1 is allowed to impersonate other users]
        groups:
            [comma separated list of groups whose users impersonating_user1 is allowed to impersonate]
    impersonating_user2:
        hosts:
            [comma separated list of hosts from which impersonating_user2 is allowed to impersonate other users]
        groups:
            [comma separated list of groups whose users impersonating_user2 is allowed to impersonate] 
```

为了支持 oozie 用例, 可以提供以下配置:

```
nimbus.impersonation.acl:
    oozie:
        hosts:
            [oozie-host1, oozie-host2, 127.0.0.1]
        groups:
            [some-group-that-userX-is-part-of] 
```

### 自动凭证的推送和更新

个人 topology 能够向 worker 推送凭证（票证和令牌）, 以便他们可以访问安全服务. 将它暴露给所有的用户可能会对他们造成痛苦. 要在常见情况下隐藏这些插件, 可以使用插件来填充凭据, 将另一方解压缩到 java 主题中, 并且还允许 Nimbus 在需要时更新凭据. 这些由以下配置控制. topology.auto-credentials 是一个 java 插件的列表, 所有这些插件都必须实现 IAutoCredentials 接口, 该接口填充网关上的凭据, 并在 worker 端解包它们. 在 kerberos 安全集群上, 默认情况下应设置为指向 org.apache.storm.security.auth.kerberos.AutoTGT. nimbus.credential.renewers.classes 也应设置为此值, 以便 nimbus 可以代表用户定期更新 TGT.

nimbus.credential.renewers.freq.secs 控制更新者轮询多长时间查看是否需要更新, 但默认值应该是正常的.

此外, Nimbus 本身可以用于代表用户提交 topology 来获取凭据. 这可以使用 nimbus.autocredential.plugins.classes 进行配置, 这是完全限定类名的列表, 所有这些都必须实现 INimbusCredentialPlugin. Nimbus 将调用所有配置的实现的 populateCredentials 方法作为 topology 提交的一部分. 您应该使用此配置与 topology.auto-credentials 和 nimbus.credential.renewers.classes, 以便凭据可以在 worker 端填充, 并且 nimbus 可以自动更新它们. 目前有两个使用此配置的示例, AutoHDFS 和 AutoHBase, 它们自动填充 topology 提交程序的 hdfs 和 hbase 委托令牌, 以便他们不必在所有可能的工作主机上分发密钥表.

### 范围

默认情况下, storm 允许提交任何大小的 topology. 但 ZK 等人对 topology 结构实际上有多大的限制. 以下配置允许您限制 topology 的最大大小.

| YAML 设置 | 描述 |
| --- | --- |
| nimbus.slots.perTopology | topology 可以使用的 slots/workers 的最大数量. |
| nimbus.executors.perTopology | topology 可以使用的最大 executors/threads. |

### 日志清理

LogViewer 守护进程现在也负责清理旧的日志文件, 以防止 dead topologies.

| YAML 设置 | 描述 |
| --- | --- |
| logviewer.cleanup.age.mins | worker 的日志必须在该日志被考虑进行清理之前（最后修改时间）多大.（生活 worker 的日志永远不会被 logviewer 清理：他们的日志通过回拨滚动.） |
| logviewer.cleanup.interval.secs | 日志记录器清理工作日志的时间间隔（秒）. |

### 允许特定用户或组访问 storm

使用 SimpleACLAuthorizer, 任何具有有效 Kerberos 票证的用户都可以部署 topology 或进行其他操作, 例如 激活, 停用, 访问集群信息. 可以通过指定 nimbus.users 或 nimbus.groups 来限制此访问. 如果 nimbus.users 仅配置列表中的用户可以部署 topology 或访问集群. 类似地, nimbus.groups 限制对属于这些组的用户的 storm 集群访问.

要配置, 请在 storm.yaml 中指定以下配置:

```
nimbus.users: 
   - "testuser" 
```

或者

```
nimbus.groups: 
   - "storm" 
```

### DRPC

希望更多在这个很快