适用于Kubernetes服务的强大,简单的SSL

嗨,我是埃文·布朗 (@evandbrown),我在Google Cloud Platform解决方案架构团队中工作。我最近写了 文章教程 about using Jenkins on Kubernetes to automate the Docker 和 GCE image build process. Today I’m going to discuss how I used Kubernetes services 和 secrets to add SSL to the Jenkins web UI. After reading this, you’ll be able to add SSL termination (and HTTP->HTTPS redirects + basic auth) to your public HTTP Kubernetes services.

在一开始的时候

本着最低限度的精神,我构建的Jenkins-on-Kubernetes的第一个版本非常基础,但功能丰富:

  • Jenkins领导者只是一个容器中的一个容器,但是它是由复制控制器管理的,因此,如果失败,它将自动重生。
  • Jenkins领导者公开了两个端口-Web UI的TCP 8080和供构建代理注册的TCP 50000-这些端口可作为带有公共负载平衡器的Kubernetes服务使用。

这是第一个版本的视觉效果:

这可行,但是我有一些问题。首先,默认的Jenkins安装中未配置身份验证。领导者坐在任何人都可以访问的公共Internet上,直到您连接并配置身份验证。而且由于没有加密,所以配置身份验证是一种象征性手势。我们需要SSL,现在就需要它!

做你所知道的

我考虑了几毫秒,试图让SSL直接在Jenkins上运行。我以前从未做过,我想知道它是否像在SSL上使用SSL一样简单 Nginx的, something I do have experience with. I’m all for learning new things, but this seemed like a great place to not invent a new wheel: SSL on Nginx的 is straightforward 和 well documented (as are its reverse-proxy capabilities), 和 Kubernetes is all about building functionality by orchestrating 和 composing containers. Let’s use Nginx的, 和 add a few bonus features that Nginx的 makes simple: HTTP->HTTPS redirection, 和 basic access authentication.

SSL终止代理作为Nginx服务

我首先将一个 Docker文件 它从标准nginx映像继承而来,复制了一些Nginx配置文件,并添加了自定义入口点(start.sh)。入口点脚本检查环境变量(ENABLE_SSL)并相应地激活正确的Nginx配置(这意味着可以使用未加密的HTTP反向代理,但这会破坏目的)。该脚本还配置了基本访问身份验证(如果已启用)(ENABLE_BASIC_AUTH env var)。

最后,start.sh评估SERVICE_HOST_ENV_NAME和SERVICE_PORT_ENV_NAME环境变量。这些变量应设置为您要代理的Kubernetes服务的环境变量的名称。在此示例中,为我们的Jenkins领导者提供的服务巧妙地命名为jenkins,这意味着集群中的Pod将看到一个名为JENKINS_SERVICE_HOST和JENKINS_SERVICE_PORT_UI的环境变量(Jenkins领导者上8080映射到的端口)。 SERVICE_HOST_ENV_NAME和SERVICE_PORT_ENV_NAME只是简单地引用了用于特定方案的正确服务,从而允许在部署中通用使用该映像。

定义控制器和服务

像本示例中的其他所有Pod一样,我们将使用复制控制器部署Nginx,从而使我们能够进行横向或纵向扩展,并从容器故障中自动恢复。摘录自示例应用程序中的完整描述符 显示了pod规范的一些相关信息:

  spec:

    containers:

      -

        name: "nginx-ssl-proxy"

        image: "gcr.io/cloud-solutions-images/nginx-ssl-proxy:latest"

        env:

          -

            name: "SERVICE\_HOST\_ENV\_NAME"

            value: "JENKINS\_SERVICE\_HOST"

          -

            name: "SERVICE\_PORT\_ENV\_NAME"

            value: "JENKINS\_SERVICE\_PORT\_UI"

          -

            name: "ENABLE\_SSL"

            value: "true"

          -

            name: "ENABLE\_BASIC\_AUTH"

            value: "true"

        ports:

          -

            name: "nginx-ssl-proxy-http"

            containerPort: 80

          -

            name: "nginx-ssl-proxy-https"

            containerPort: 443

吊舱将提供将TCP 80和443公开给公共负载平衡器的服务。这是服务描述符 (也可在示例应用中找到):

  kind: "Service"

  apiVersion: "v1"

  metadata:

    name: "nginx-ssl-proxy"

    labels:

      name: "nginx"

      role: "ssl-proxy"

  spec:

    ports:

      -

        name: "https"

        port: 443

        targetPort: "nginx-ssl-proxy-https"

        protocol: "TCP"

      -

        name: "http"

        port: 80

        targetPort: "nginx-ssl-proxy-http"

        protocol: "TCP"

    selector:

      name: "nginx"

      role: "ssl-proxy"

    type: "LoadBalancer"

这里是使用SSL终止代理的概述。请注意,詹金斯不再直接暴露于公共互联网中:

现在,Nginx容器如何获得超级秘密的SSL密钥/证书和htpasswd文件(用于基本访问身份验证)?

保密,安全

Kubernetes有一个 秘密的API和资源。 “机密”旨在保存敏感信息,例如密码,OAuth令牌和ssh密钥。与逐字记录在pod定义或docker映像中相比,将这些信息保密是更加安全和灵活的。”

您可以通过3个简单的步骤在集群中创建机密:

Base64编码您的秘密数据(即SSL密钥对或htpasswd文件)

$ cat ssl.key | base64  
   LS0tLS1CRUdJTiBDRVJUS...

创建一个描述您的秘密的json文档,并添加base64编码的值:

  apiVersion: "v1"

  kind: "Secret"

  metadata:

    name: "ssl-proxy-secret"

    namespace: "default"

  data:

    proxycert: "LS0tLS1CRUd..."

    proxykey: "LS0tLS1CR..."

    htpasswd: "ZXZhb..."

创建秘密资源:

$ kubectl create -f secrets.json

要从容器访问机密,请在您的pod规范中将其指定为卷安装。这是相关摘录 Nginx的代理模板 我们之前看过:

  spec:

    containers:

      -

        name: "nginx-ssl-proxy"

        image: "gcr.io/cloud-solutions-images/nginx-ssl-proxy:latest"

        env: [...]

        ports: ...[]

        volumeMounts:

          -

            name: "secrets"

            mountPath: "/etc/secrets"

            readOnly: true

    volumes:

      -

        name: "secrets"

        secret:

          secretName: "ssl-proxy-secret"

定义了一个指向ssl-proxy-secret秘密资源的secret类型的卷,然后将其装入容器中的/ etc / secrets中。较早示例中的secrets规范定义了data.proxycert,data.proxykey和data.htpasswd,因此我们将看到这些文件出现(以base64解码)在/ etc / secrets / proxycert,/ etc / secrets / proxykey和/ etc / secrets / htpasswd供Nginx进程访问。

现在都在一起了

我有“容器和Kubernetes既有趣又酷!”时时刻刻,就像每天一样。我开始拥有“容器和Kubernetes极为有用和强大的功能,并通过帮助我轻松地完成重要的事情来增加我的工作的价值”。这个带有Nginx示例的SSL终止代理肯定是后者之一。我不必浪费时间学习使用SSL的新方法。我能够以一种可重用的方式,使用可重复使用的工具,快速地解决我的问题(从构思到工作大约需要2个小时)。

签出完整 使用Jenkins,Packer和Kubernetes进行自动映像构建 回购以查看SSL终止代理在实际集群中的使用方式,或在 Nginx的的SSL代理回购 (带有Dockerfile和Packer模板,因此您可以自己构建映像)。