安全#

本文档中的信息主要侧重于云部署。对于本地部署,还需要针对您的安装方法进行额外的安全工作。请注意,您特定安装的安全需求可能比我们在此提供的更严格或更宽松。

Brad Geesamen 在 2017 年 Kubecon NA 上发表了一个精彩的演讲,名为 通过示例入侵和加固 Kubernetes 集群 - 我,Brad Geesaman,赛门铁克,您可以 观看演讲。强烈建议您这样做,以了解使用 Kubernetes 运行 JupyterHub 时所面临的安全问题。

报告安全问题#

如果您在 JupyterHub 中发现安全漏洞,无论是代码未能正确实现此处描述的模型,还是模型本身的缺陷,请将其报告给 security@ipython.org.

如果您希望加密您的安全报告,可以使用 此 PGP 公钥.

HTTPS#

本节介绍如何在您的 JupyterHub 上启用 HTTPS。最简单的方法是使用 Let's Encrypt,但我们也会介绍如何设置您自己的 HTTPS 证书。有关 HTTPS 安全的更多信息,请参阅 此博文中的证书部分。

设置您的域名#

  1. 从注册商处购买域名。选择您想要的任何一个。

  2. 从您要使用的域名创建一个 A 记录,指向代理公共服务的 EXTERNAL-IP。具体操作方法取决于您使用的 DNS 提供商。

  3. 等待更改传播。传播可能需要几分钟到几小时。等待直到您能够输入您购买的域名的名称,并且它显示了 JupyterHub 登录页面。

    重要的是您要等待 - 过早地进行下一步可能会导致问题!

设置自动 HTTPS#

JupyterHub 使用 Let's Encrypt 自动为您的部署创建 HTTPS 证书。这将导致您的 HTTPS 证书每隔几个月自动续订。要启用此功能,请对您的 config.yaml 文件进行以下更改

  1. 指定我们需要自动配置 HTTPS 证书的两个信息 - 您的域名和联系电子邮件地址。

    proxy:
      https:
        enabled: true
        hosts:
          - <your-domain-name>
        letsencrypt:
          contactEmail: <your-email-address>
    
  2. 通过运行 helm upgrade ... 应用配置更改。

  3. 等待大约一分钟,现在您的 hub 应该启用了 HTTPS!


注意

如果代理服务的类型为 LoadBalancer(默认情况下为该类型),则可以请求特定的静态 IP 地址(如果可用),而不是动态获取的 IP 地址。虽然对于 HTTPS 来说不是必需的,但使用静态 IP 地址是针对引用固定 IP 的域名的推荐做法。这确保了多个部署使用相同的 IP 地址。IP 可以像这样提供

proxy:
  service:
    loadBalancerIP: xxx.xxx.xxx.xxx

有关此内容的更多信息,请参阅 配置参考页面。


设置手动 HTTPS#

如果您有自己的 HTTPS 证书,并且想使用它们而不是自动配置的 Let’s Encrypt 证书,这也是可能的。请注意,这被认为是高级选项,因此我们建议您只有在有充分理由的情况下才这样做。

有两种方法可以指定您的手动证书,直接在 config.yaml 中或通过创建 Kubernetes secret

在 config.yaml 中指定证书#

  1. 将您的域名和 HTTPS 证书信息添加到您的 config.yaml 中。

    proxy:
      https:
        enabled: true
        type: manual
        manual:
          key: |
            -----BEGIN RSA PRIVATE KEY-----
            ...
            -----END RSA PRIVATE KEY-----
          cert: |
            -----BEGIN CERTIFICATE-----
            ...
            -----END CERTIFICATE-----
    
  2. 通过运行 helm upgrade … 应用配置更改。

  3. 等待大约一分钟,现在您的 hub 应该启用了 HTTPS!

通过 Secret 资源指定证书#

  1. 创建一个类型为 kubernetes.io/tlssecret 资源,其中包含您的证书。

    kubectl create secret tls example-tls --key="tls.key" --cert="tls.crt"

  2. 将您的域名和 secret 的名称添加到您的 config.yaml 中。

    proxy:
      https:
        enabled: true
        hosts:
          - <your-domain-name>
        type: secret
        secret:
          name: example-tls
    
  3. 通过运行 helm upgrade … 应用配置更改。

  4. 等待大约一分钟,现在您的 hub 应该启用了 HTTPS!

将 SSL 卸载到负载均衡器#

在某些具有可信网络的环境中,您可能希望在负载均衡器处终止 SSL。如果启用了 https,并且 proxy.https.type 设置为 offload,则 HTTP 和 HTTPS 前端将针对 JupyterHub 的 HTTP 端口。

负载均衡器上的 HTTPS 侦听器需要根据提供商进行配置。如果您使用的是 AWS 和其证书管理器提供的证书,您的 config.yml 可能如下所示

proxy:
  https:
    enabled: true
    type: offload
  service:
    annotations:
      # Certificate ARN
      service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "arn:aws:acm:us-east-1:1234567891011:certificate/uuid"
      # The protocol to use on the backend, we use TCP since we're using websockets
      service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "tcp"
      # Which ports should use SSL
      service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https"
      service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "3600"

注释选项将因提供商而异。

确认您的域名正在运行 HTTPS#

有很多方法可以确认域名正在运行可信的 HTTPS 证书。一种选择是使用 Qualys SSL Labs 安全报告生成器。使用以下 URL 结构测试您的域名

https://ssllabs.com/ssltest/analyze.html?d=<YOUR-DOMAIN>

最小化 hub 镜像#

默认的 hub 镜像包含一些有用的调试工具。您可以使用精简版的镜像来最大程度地减少对这些可选工具中漏洞的暴露。

hub:
  image:
    # The slim variant excludes a few non-essential packages that are useful
    # when debugging something from the hub pod. To use it, apply this
    # configuration.
    #
    name: quay.io/jupyterhub/k8s-hub-slim

注意

我们基于 Linux Debian 作为基础镜像。有一些容器扫描器会发现 Debian 中已知的漏洞,而 Debian 安全团队已将其忽略。有关此问题的详细信息,请参阅 此评论

安全访问 Helm#

Helm 3 支持现代 Kubernetes 的安全、身份和授权功能。Helm 的权限使用您的 kubeconfig 文件进行评估。集群管理员可以根据需要限制用户权限。

Kubernetes 文档 中了解有关使用 kubeconfig 文件组织集群访问的更多信息。

删除 Kubernetes 仪表盘#

在许多安装中,Kubernetes 仪表盘 默认情况下会被创建。虽然仪表盘包含有用的信息,但它也存在安全风险。我们**建议**删除它,暂时不要使用它,直到仪表盘变得真正安全为止。

您可以通过从集群中删除 Kubernetes 仪表盘部署来缓解此问题。这很可能可以通过以下方式完成

kubectl --namespace=kube-system delete deployment kubernetes-dashboard

在较旧的集群中,您可能需要执行以下操作

kubectl --namespace=kube-system delete rc kubernetes-dashboard

使用基于角色的访问控制 (RBAC)#

Kubernetes 支持,并且通常需要使用 基于角色的访问控制 (RBAC) 来保护哪些 Pod/用户可以在集群上执行哪些类型的操作。可以设置 RBAC 规则,根据用户的管理需求为其提供最小的必要访问权限。

**至关重要**的是要了解,如果禁用了 RBAC,则所有 Pod 都将在 Kubernetes 集群及其中的所有节点上获得相当于 root 的权限。这会为您的安全带来非常严重的漏洞。

从与 JupyterHub 和 BinderHub 一起使用的 Helm 图表 v0.5 开始,Helm 图表可以与启用了 RBAC 的集群原生配合使用。为了提供合理的默认安全设置,我们为我们使用的各种组件提供了适当的最小 RBAC 规则。我们**强烈建议**使用这些最小或更严格的 RBAC 规则。

如果您出于任何原因想要禁用 RBAC 规则,您可以在 config.yaml 文件中使用以下代码片段来实现。

rbac:
  create: false

我们强烈**建议不要禁用** RBAC 规则,并提醒您此操作会打开安全漏洞。但是,某些云提供商可能不支持 RBAC,在这种情况下,您可以使用此配置将其禁用。

Kubernetes API 访问#

允许用户直接访问 Kubernetes API 可能很危险。它允许用户授予自己更多权限,未经许可访问其他用户的內容,运行(无利可图的)比特币挖矿操作以及各种其他不合法活动。默认情况下,出于此原因,我们不允许访问 服务帐户凭据,这些凭据需要从用户服务器访问 Kubernetes API。

如果您想(谨慎地!)向用户提供对 Kubernetes API 的访问权限,您可以在 config.yaml 文件中使用以下内容。

singleuser:
  serviceAccountName: <service-account-name>

您可以手动创建一个供用户使用的服务帐户,并在此处指定该帐户的名称(推荐),或者使用 default 向他们提供对命名空间的默认服务帐户的访问权限。您还应该理想地(手动)为该服务帐户设置 RBAC 规则,以指定用户将拥有的权限。

这是一个敏感的安全问题(类似于在传统计算环境中编写 sudo 规则),因此请务必小心。

正在进行的工作使这变得更容易!

审核云元数据服务器访问#

大多数云提供商都有一个静态 IP,Pod 可以访问该 IP 以获取有关云的元数据。此元数据可能包含非常敏感的信息,如果落入坏人之手,攻击者可以完全控制您的集群和云资源。因此,**至关重要**的是从可能最终运行恶意代码的用户 Pod 中保护元数据服务,而用户 Pod 可能在不知情的情况下运行恶意代码。

演示文稿,从 27 分钟开始,提供了有关此攻击带来的危险的更多信息。

默认情况下,此 Helm 图表通过两种方式阻止对该元数据的访问,但您只需要一种。

使用 NetworkPolicy 控制器强制执行的 NetworkPolicy 阻止云元数据 API#

如果您在 Kubernetes 集群中拥有NetworkPolicy 控制器(例如 Calico 或 Cilium),它将强制执行此图表创建的 NetworkPolicy 资源(singleuser.networkPolicy.*),该资源默认情况下不允许(因此阻止)用户访问特定 IP 上公开的云元数据 API(169.254.169.254)。

注意

如果您有 NetworkPolicy 控制器,我们建议您依赖它并将 singleuser.cloudMetadata.blockWithIptables 设置为 false

使用运行 iptables 的特权 initContainer 阻止云元数据 API#

如果您不能依赖 NetworkPolicy 方法来阻止对云元数据 API 的访问,我们建议您改为依赖此选项。当 singleuser.cloudMetadata.blockWithIptables 为 true(默认情况下)时,会将一个 initContainer 添加到用户 Pod 中。它将以提升的权限运行,并使用 iptables 命令行工具来阻止对云元数据服务器的所有网络访问。

# default configuration
singleuser:
  cloudMetadata:
    blockWithIptables: true
    ip: 169.254.169.254

在版本 3.0.0 中更改: 此配置不允许与 singleuser.networkPolicy.egressAllowRules.cloudMetadataServer 同时配置为 true,以避免出现模棱两可的配置。

Kubernetes 网络策略#

警告

您的 Kubernetes 集群可能会静默忽略此 Helm 图表可以创建的 NetworkPolicy 资源中描述的网络规则。NetworkPolicy 规则由可选的 NetworkPolicy 控制器强制执行,该控制器通常不会作为设置 Kubernetes 集群的一部分进行设置。

默认情况下,此 Helm 图表创建四个不同的 NetworkPolicy 资源,描述了它们所针对的 Pod 允许的传入/入站和传出/出站连接。

需要理解的关键点是,如果 Pod 的入站或出站连接分别没有被 NetworkPolicy 针对,它们将完全不受 NetworkPolicy 的约束。但是,如果它们被针对,则只有明确允许它们的连接才会被接受。换句话说,定义针对 Pod 的 NetworkPolicy 的行为本身就是对它的约束,但 NetworkPolicy 中的所有规则都是允许规则。

图表中四个网络策略的介绍#

四个网络策略声明了 Helm 图表创建的四种 Pod 的规则。下面是一些表格,在一定程度上描述了四个网络策略的作用。

NetworkPolicy

关联的 Helm 图表配置

受影响的 Pod

Pod 中的显著软件

hub

hub.networkPolicy

hub

jupyterhub, kubespawner, jupyterhub-idle-culler, 身份验证器

proxy

proxy.chp.networkPolicy

proxy

可配置的 HTTP 代理

autohttps

proxy.traefik.networkPolicy

autohttps

traefik, lego

singleuser

singleuser.networkPolicy

jupyter-<用户名>

jupyter_server

NetworkPolicy

始终允许核心功能的出站连接(出站)

hub

proxy Pod 的 REST API 端口 (8001),用户 Pod 的唯一端口 (8888)

proxy

hub Pod 的唯一端口 (8081),用户 Pod 的唯一端口 (8888)

autohttps

proxy Pod 的 HTTP 代理端口 (8000)

singleuser

hub Pod 的唯一端口 (8081),proxy Pod 的代理端口 (8000),autohttps Pod 的 HTTP (8080) 和 HTTPS (8443)

NetworkPolicy

始终允许核心功能的入站连接(入站),入站允许来自具有特定标签的 Pod 的特定端口

hub

来自标记为 hub.jupyter.org/network-access-hub=true 的 Pod

proxy

来自标记为 hub.jupyter.org/network-access-proxy-http=true(HTTP 代理端口)或 hub.jupyter.org/network-access-proxy-api=true(REST API 端口)的相同命名空间中的 Pod

autohttps

来自标记为 hub.jupyter.org/network-access-proxy-http=true(HTTP(S) 代理端口)的 Pod

singleuser

来自标记为 hub.jupyter.org/network-access-singleuser=true(笔记本端口)的 Pod

并非所有功能都在上面概述

记录这些网络策略的完整行为一直很棘手。有关详细信息,目前请参考检查 Helm 图表的模板以及给定配置的渲染结果。

以下是 Helm 图表模板的链接。

以下是您可以用来渲染特定模板的命令。

# These four commands renders the four NetworkPolicy resource templates of the
# latest release of the JupyterHub Helm chart, with default values.
#
# You can pass `--values <your config file>` or `--version <version here>` to
# these commands to inspect the rendered NetworkPolicy resources given your
# specific version and configuration.
#
helm template --repo https://jupyterhub.github.io/helm-chart jupyterhub --show-only templates/hub/netpol.yaml
helm template --repo https://jupyterhub.github.io/helm-chart jupyterhub --show-only templates/proxy/netpol.yaml
helm template --repo https://jupyterhub.github.io/helm-chart jupyterhub --show-only templates/proxy/autohttps/netpol.yaml
helm template --repo https://jupyterhub.github.io/helm-chart jupyterhub --show-only templates/singleuser/netpol.yaml

启用和禁用网络策略#

NetworkPolicy 资源默认情况下会创建,并且随着它们的创建,它们会将入站和出站网络连接限制为 NetworkPolicy 资源中明确允许的连接。要选择不创建 NetworkPolicy 资源,请使用以下配置。

# Example configuration on how to disable the creation of all the Helm chart's
# NetworkPolicy resources.
hub:
  networkPolicy:
    enabled: false
proxy:
  chp:
    networkPolicy:
      enabled: false
  traefik:
    networkPolicy:
      enabled: false
singleuser:
  networkPolicy:
    enabled: false

允许额外的入站网络连接(入站)#

虽然您可以使用 <hub|proxy.chp|proxy.traefik|singleuser>.networkPolicy.ingress 配置添加任意允许规则,除了确保核心功能的规则之外,您还可以标记您希望允许建立与 Helm 图表各种 Pod 连接的 Pod。

例如,要从同一命名空间中的另一个 Pod 访问 Hub Pod,只需将标签 hub.jupyter.org/network-access-hub: "true" 添加到应该能够建立与 Hub Pod 连接的 Pod 上。

可用的访问标签是

  • hub.jupyter.org/network-access-hub: "true",访问 Hub API

  • hub.jupyter.org/network-access-proxy-http: "true",访问代理公共 HTTP 端点

  • hub.jupyter.org/network-access-proxy-api: "true",访问代理 API

  • hub.jupyter.org/network-access-singleuser: "true",直接访问单用户服务器

如果你希望使用这些标签从另一个命名空间访问 Pod,请阅读有关 <hub|proxy.chp|proxy.traefik|singleuser>.networkPolicy.interNamespaceAccessLabels 的内容。

最后,选项 <hub|proxy.chp|proxy.traefik|singleuser>.networkPolicy.allowedIngressPorts 允许你在某些 Pod 上允许传入连接。

允许额外的出站网络连接(出站)#

除了确保核心功能的规则之外,你还可以使用 <hub|proxy.chp|proxy.traefik|singleuser>.networkPolicy.egress 配置添加任意允许规则,你还可以打开或关闭一些预定义的允许规则。它们在配置参考中 <hub|proxy.chp|proxy.traefik|singleuser>.networkPolicy.egressAllowRules 中有说明。

默认情况下,所有出站允许规则都为 hubproxy.chpproxy.traefik 启用,但 singleuser.networkPolicy.egressAllowRules.cloudMetadataServersingleuser.networkPolicy.egressAllowRules.privateIPs 默认设置为 false。实际上,这意味着没有规则允许用户 Pod 与具有 私有 IPv4 地址 的某些 k8s 本地服务通信。

在版本 2.0.0 中更改: 在 JupyterHub Helm 图表 2.0.0 之前,默认配置是允许单用户 Pod 建立到任何地方的出站连接。在 2.0.0 之后,必须显式设置 singleuser.networkPolicy.egressAllowRules.privateIPs=true 才能实现此功能。

限制负载均衡器访问#

默认情况下,任何 IP 地址都可以通过负载均衡器服务访问你的 JupyterHub 部署。如果你想限制哪些 IP 地址可以访问负载均衡器,你可以在你的 config.yaml 中指定一个 IP CIDR 地址列表,如下所示

proxy:
  service:
    loadBalancerSourceRanges:
      - 111.111.111.111/32
      - 222.222.222.222/32

这将限制访问仅限于两个 IP 地址:111.111.111.111222.222.222.222