亚马逊AWS官方博客

将 GitHub 凭证与 Teleport 结合进行 EKS 身份验证

本博文介绍了如何以 GitHub 作为身份提供商并将 GravitationalTeleport 配置为 Amazon Elastic Kubernetes Service (Amazon EKS) 的身份验证代理,从而验证用户身份。在此例中,Teleport 安装在一个独立的 EC2 实例上,并配置为使用 GitHub 身份验证来验证用户身份。完成身份验证后,用户可以借助分配给 EC2 实例的角色模拟 Kubernetes RBAC 角色,从而可以访问 EKS 集群。

Teleport

Teleport 是 Gravitational 的一种开源解决方案,可以配置为管理 Kubernetes 集群的代理。此开源版包含对 GitHub 身份验证的支持,即您可以将 GitHub 作为身份提供商来验证用户身份。您还可以将 Teleport 的会话记录和审计日志扩展到 Kubernetes。例如,将常规的 kubectl exec 命令记录在审计日志中,并将交互性命令作为可以存储并在以后重放的常规会话记录。商业版的 Teleport 增加支持使用 OAuth/OIDC 或 SAML 的 RBAC 和 SSO,更多信息请参阅 Teleport Enterprise

EKS 身份验证

EKS 目前支持两种类型的身份验证:持有者/服务账户令牌和使用 Webhook 令牌身份验证的 IAM。当用户调用 Kubernetes API 时,一个 Webhook 将会转交一个包含在发送到 IAM 的请求中的身份验证令牌。此令牌是一种 base 64 签名的 URL,由 AWS 命令行界面 (AWS CLI) 生成。在早期版本的 EKS 中,这是使用 aws-iam-authenticator 二进制代码实现的。

虽然 IAM 身份验证足以满足大多数使用案例的需要,但 OIDC 等其他类型的身份验证机制也日益受到关注,尤其是在新 IAM 用户账户的创建遇到政治困难的组织。借助 Teleport 等解决方案,这些组织可以将 GitHub 等替代类型的身份验证机制用于 EKS。

它的工作原理是什么?

Teleport 利用了用户模拟机制,也就是一个用户可以通过模拟标头来扮演另一个用户。当您将 Teleport 与 EKS 集成时,您将向运行 Teleport 的实例分配一个映射到某个 Kubernetes RBAC 角色的 IAM 角色,该角色将赋予模拟其他组的权限。Teleport 完成用户的身份验证后,赋予该实例的角色将允许用户代入 Teleport 配置中指定的 Kubernetes 组权限。

使用案例

替代 Route 53 解析程序终端节点

当您将 EKS 集群终端节点配置为私有时,EKS 集群的名称只能从工作线程 VPC 内部解析。作为创建 Route 53 入站终端节点(成本约为每月 90 USD)的一种替代方式,您可以在 t3.small 实例上运行 Teleport 代理 [在每个工作线程 VPC 中],成本低至每月 14 USD。

动态团队环境

有时,创建 IAM 用户不太实际,例如开发团队的成员频繁变动或者创建 IAM 用户需要的时间过长时。将集群的访问权限委托给团队负责人,再由该负责人通过 Teleport 和 GitHub 来控制访问权限,可以简化新团队成员的入职流程。

先决条件

您需要有一个运行正常的 EKS 集群。如果您不熟悉如何创建 EKS 集群,请参阅 eksctl.io

将 Teleport 与 EKS 集成

创建集群角色和角色绑定

第一步是创建一个集群角色和角色绑定,从而允许 EC2 实例模拟其他用户、组和服务账户。

cat << 'EOF' | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: teleport-impersonation
rules:
- apiGroups:
  - ""
  resources:
  - users
  - groups
  - serviceaccounts
  verbs:
  - impersonate
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: teleport-crb
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: teleport-impersonation
subjects:
- kind: Group
  name: teleport-group
- kind: User
  name: system:anonymous
EOF

未被配置的其他身份验证方法拒绝的请求将视为匿名请求,并赋予用户名 system:anonymous 和组 system:unauthenticated

创建 IAM 信任策略文档

这是将允许 EC2 实例代入角色的信任策略。

cat > [filename] << 'EOF'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF

创建 IAM 角色

ROLE_ARN=$(aws iam create-role --role-name teleport-role --assume-role-policy-document file://[filename] | jq -r '.Role.Arn')

创建授予 list-clusters 和 describe-cluster 权限的 IAM 策略(可选)

使用 aws eks update-kubeconfig 命令创建 kubeconfig 文件时将需要此策略。如果您可通过其他机制在运行 Teleport 的实例上创建 kubeconfig 文件,则无需执行此步骤。

cat > [filename] << 'EOF'
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "eks:DescribeCluster",
                "eks:ListClusters"
            ],
            "Resource": "*"
        }
    ]
}
EOF
POLICY_ARN=$(aws iam create-policy --policy-name teleport-policy --policy-document file://[filename] | jq -r '.Policy.Arn')
aws iam attach-role-policy --role-name teleport-role --policy-arn $POLICY_ARN

更新 aws-auth configmap

这会将 IAM 角色 teleport-role 映射到 Kubernetes 组 teleport-group

如果您使用 eksctl 来创建集群,则可能需要首先将 mapUsers 部分添加到 aws-auth ConfigMap,然后再执行这些命令。

ROLE="    - userarn: $ROLE_ARN\n      username: teleport\n      groups:\n        - teleport-group"
kubectl get -n kube-system configmap/aws-auth -o yaml | awk "/mapUsers: \|/{print;print \" $ROLE \ ";next}1" > /tmp/aws-auth-patch.yml 
kubectl patch configmap/aws-auth -n kube-system --patch "$(cat /tmp/aws-auth-patch.yml)"

安装好补丁后,您的 aws-auth ConfigMap 应会与以下类似:

mapUsers: |
- userarn: arn:aws:iam::123456789012:role/teleport-role
username: teleport
groups:
- teleport-group

安装 Teleport

创建 EC2 实例

使用 Amazon Linux 2 (AL2) AMI 在您的 VPC 中的某个公有子网上创建一个 EC2 实例。修改与该实例关联的安全组以允许端口 22 入站流量,从而可以在实例开始运行后通过 SSH 连接到该实例。您的安全组规则需要允许访问 3080 和 3022-3026 端口,以便用户可以从互联网访问 Teleport 服务器。这也将允许 GitHub 将应答发布回 Teleport 服务器。您还需要打开 80 端口,以允许 Let’s Encrypt 在颁发 SSL 证书时执行 HTTP 验证。

类型 协议 端口范围
自定义 TCP TCP 3022-3026 0.0.0.0/0
自定义 TCP TCP 3080 0.0.0.0/0
HTTP TCP 80 0.0.0.0/0
SSH TCP 22 您的 IP

如果您不修改 EKS 控制层安全组以允许来自 Teleport 安全组的端口 443 入站流量,则您的 Teleport 实例将无法与 Kubernetes API 通信。

将角色分配到实例:

aws iam create-instance-profile --instance-profile-name teleport-role
aws iam add-role-to-instance-profile --instance-profile-name teleport-role --role-name teleport-role
aws ec2 associate-iam-instance-profile --iam-instance-profile Name=teleport-role --instance-id [instance_id]

instance_id 应替换为您计划安装 Teleport 的实例的实例 ID。

实例处于“正在运行”状态后通过 SSH 连接到该实例。

下载 pip:

curl -O https://bootstrap.pypa.io/get-pip.py

安装 pip:

sudo python get-pip.py

升级 AWS CLI:

sudo pip install --upgrade awscli

下载 kubectl:

curl -o kubectl https://amazon-eks.s3-us-west-2.amazonaws.com/1.12.7/2019-03-27/bin/linux/amd64/kubectl
chmod +x ./kubectl
sudo mv ./kubectl /usr/local/bin

下载 Teleport:

curl https://get.gravitational.com/teleport-v4.0.0-linux-amd64-bin.tar.gz -o teleport-v4.0.0-linux-amd64-bin.tar.gz
tar -xzf ./teleport-v4.0.0-linux-amd64-bin.tar.gz

安装 Teleport:

sudo ./teleport/install
PATH=$PATH:/usr/local/bin 
source ~/.bashrc

创建 kubeconfig:

aws eks update-kubeconfig --name [cluster_name] --region [region]

配置 Teleport

创建一个 systemd 单位文件(通过创建单位文件,Teleport 将在实例重启时自动启动):

cat > teleport.service << 'EOF'
[Unit]
Description=Teleport SSH Service
After=network.target

[Service]
Type=simple
Restart=on-failure
ExecStart=/usr/local/bin/teleport start --config=/etc/teleport.yaml --pid-file=/var/run/teleport.pid
ExecReload=/bin/kill -HUP $MAINPID
PIDFile=/var/run/teleport.pid

[Install]
WantedBy=multi-user.target
EOF

复制到 systemd/system 并重新加载:

sudo mv teleport.service /etc/systemd/system/teleport.service
sudo systemctl daemon-reload

创建 HTTPs 的 SSL 证书。在生产环境中使用 Teleport 代理时为 HTTPS 配置 TLS 绝对至关重要。为简单起见,我们使用 Let’s Encrypt 来颁发证书并使用 nip.io 进行简单的 DNS 解析。但对于生产使用案例,使用弹性 IP 和 Route53 域名更为恰当。

从 EPEL 安装 certbot:

sudo wget -r --no-parent -A 'epel-release-*.rpm' http://dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/
sudo rpm -Uvh dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/epel-release-*.rpm
sudo yum-config-manager --enable epel*
sudo yum repolist all

sudo yum install -y certbot python2-certbot-apache

为我们的 nip.io 主机名生成一个证书(用您的电子邮件地址更新下面的代码):

export TELEPORT_PUBLIC_DNS_NAME="$(curl http://169.254.169.254/latest/meta-data/public-hostname | cut -d '.' -f1).nip.io"
echo $TELEPORT_PUBLIC_DNS_NAME
export EMAIL=[yourname@yourdomain.com]

sudo certbot certonly --standalone \
             --preferred-challenges http \
                      -d $TELEPORT_PUBLIC_DNS_NAME \
                      -n \
                      --agree-tos \
                      --email=$EMAIL

Let’s Encrypt 证书仅在很短的时间内有效。cron 作业可以用于定期续订证书(Certbot 开发人员建议每天续订两次):

echo "39 1,13 * * *       root    certbot renew --no-self-upgrade" | sudo tee -a /etc/crontab
sudo systemctl restart crond

添加续订 Hook 以在证书续订时重新加载 Teleport 配置:

echo "renew_hook = systemctl reload teleport" | sudo tee -a /etc/letsencrypt/renewal/${TELEPORT_PUBLIC_DNS_NAME}.conf

配置 config 文件

如果您还没有这样操作,请将 Teleport 实例的公有 DNS 名称作为环境变量导出:

export TELEPORT_PUBLIC_DNS_NAME="$(curl http://169.254.169.254/latest/meta-data/public-hostname | cut -d '.' -f1).nip.io"
  cat > teleport.yaml << EOF 
# By default, this file should be stored in /etc/teleport.yaml

## IMPORTANT ##
#When editing YAML configuration, please pay attention to how your editor handles white space.YAML requires consistent handling of tab characters
# This section of the configuration file applies to all teleport
# services.
teleport:
    # nodename allows to assign an alternative name this node can be reached by.
    # by default it's equal to hostname
    nodename: $TELEPORT_PUBLIC_DNS_NAME
    
    # Data directory where Teleport keeps its data, like keys/users for 
    # authentication (if using the default BoltDB back-end)
    data_dir: /var/lib/teleport

    # Teleport throttles all connections to avoid abuse.These settings allow
    # you to adjust the default limits
    connection_limits:
        max_connections: 1000
        max_users: 250

    # Logging configuration.Possible output values are 'stdout', 'stderr' and 
    # 'syslog'.Possible severity values are INFO, WARN and ERROR (default).
    log:
        output: stderr
        severity: ERROR

    # Type of storage used for keys.You need to configure this to use etcd
    # backend if you want to run Teleport in HA configuration.
    storage:
        type: dir

# This section configures the 'auth service':
auth_service:
    authentication: 
       type: github
    enabled: yes
    # IP and the port to bind to.Other Teleport nodes will be connecting to
    # this port (AKA "Auth API" or "Cluster API") to validate client 
    # certificates 
    listen_addr: 0.0.0.0:3025

# This section configures the 'node service':
ssh_service:
    enabled: yes
    # IP and the port for SSH service to bind to.
    listen_addr: 0.0.0.0:3022
    # See explanation of labels in "Labeling Nodes" section below
    public_addr: $TELEPORT_PUBLIC_DNS_NAME:3022 
    labels:
        role: master
        type: postgres
    # List (YAML array) of commands to periodically execute and use
    # their output as labels.
    # See explanation of how this works in "Labeling Nodes" section below
    commands:
    - name: arch
      command: [/usr/bin/uname, -p]
      period: 1h0m0s

# This section configures the 'proxy servie'
proxy_service:
    enabled: yes
    # SSH forwarding/proxy address.Command line (CLI) clients always begin their
    # SSH sessions by connecting to this port
    listen_addr: 0.0.0.0:3023

    # Reverse tunnel listening address.An auth server (CA) can establish an 
    # outbound (from behind the firewall) connection to this address.
    # This will allow users of the outside CA to connect to behind-the-firewall 
    # nodes.
    tunnel_listen_addr: 0.0.0.0:3024

    # List (array) of other clusters this CA trusts.
    # trusted_clusters:
      # - key_file: /path/to/main-cluster.ca
        # Comma-separated list of OS logins allowed to users of this 
        # trusted cluster
        # allow_logins: john,root
        # Establishes a reverse SSH tunnel from this cluster to the trusted
        # cluster, allowing the trusted cluster users to access nodes of this 
        # cluster
        #tunnel_addr: 80.10.0.12:3024

    # The HTTPS listen address to serve the Web UI and also to authenticate the 
    # command line (CLI) users via password+HOTP
    web_listen_addr: 0.0.0.0:3080

    # TLS certificate for the HTTPS connection.Configuring these properly is 
    # critical for Teleport security.
    https_key_file: /etc/letsencrypt/live/$TELEPORT_PUBLIC_DNS_NAME/privkey.pem
    https_cert_file: /etc/letsencrypt/live/$TELEPORT_PUBLIC_DNS_NAME/fullchain.pem
    kubernetes: 
      enabled: yes
      public_addr: $TELEPORT_PUBLIC_DNS_NAME:3026 
      listen_addr: 0.0.0.0:3026
      kubeconfig_file: /home/ec2-user/.kube/config
EOF

将此文件复制到 etc 目录:

sudo cp ./teleport.yaml /etc/teleport.yaml

启动 Teleport:

sudo systemctl start teleport.service 
systemctl status teleport.service 
journalctl -u teleport.service

退出 EC2 实例。

配置 GitHub

遵循全新创建新组织的说明操作。

创建 OAuth 应用程序

首页 URL 字段中,键入 Teleport 实例的公有 nip.io DNS 名称,再加上端口 3080:

https://ec2-12-34-56-78.nip.io:3080

授权回调 URL 字段中,键入 Teleport 实例的公有 DNS 名称,再加上 /v1/webapi/github/callback

https://ec2-12-34-56-78.nip.io:3080/v1/webapi/github/callback

完成创建 OAuth 应用程序后,复制您的组织设置下“OAuth 应用程序”页面中的客户端 ID 和密钥。在配置 GitHub 身份验证时您需要使用这些值。

完成 Teleport 配置

通过 SSH 连接到 Teleport 实例。

创建 github.yaml:

export TELEPORT_PUBLIC_DNS_NAME="$(curl http://169.254.169.254/latest/meta-data/public-hostname | cut -d '.' -f1).nip.io"

cat > github.yaml << EOF
kind: github
version: v3
metadata:
  # connector name that will be used with 'tsh --auth=github login'
  name: github
spec:
  # client ID of Github OAuth app
  client_id: [your client id]
  # client secret of Github OAuth app
  client_secret: [your client secret]  # connector display name that will be shown on web UI login screen display: Github # callback URL that will be called after successful authentication redirect_url: https://$TELEPORT_PUBLIC_DNS_NAME:3080/v1/webapi/github/callback # mapping of org/team memberships onto allowed logins and roles teams_to_logins: - organization: [your github org name] # Github organization name team: [your github team name] # Github team name within that organization # allowed UNIX logins for team octocats/admins: logins: - ec2-user # list of Kubernetes groups this Github team is allowed to connect to kubernetes_groups: ["system:masters"] EOF

应用 github.yaml:

sudo /usr/local/bin/tctl create -f ./github.yaml

退出 EC2 实例。

配置 Teleport 客户端

下载 Teleport (MAC):

curl -O https://get.gravitational.com/teleport-v4.0.2-darwin-amd64-bin.tar.gz 
tar -xzf teleport-v4.0.2-darwin-amd64-bin.tar.gz

适用于其他操作系统的客户端二进制代码可在 Teleport 社区版找到。

将 TSH 二进制代码复制到路径中:

cp ./teleport/tsh /usr/local/bin/tsh

访问 Kubernetes 集群

在运行以下命令之前,请备份您的 kubeconfig 文件,因为在您登录到 Teleport 后该文件将被覆盖。如果您已将 KUBECONFIG 导出至其他文件,则运行“unset KUBECONFIG”。

tsh login 命令中,键入 Teleport 实例的公有 nip.io DNS 名称,再加上端口 3080:

tsh login --proxy=ec2-12-34-56-78.nip.io:3080

这将自动打开一个新的浏览器选项卡,以便您登录到 GitHub。如果您已经登录到 GitHub,此页面将显示为“登录成功”。

您应会在终端中看到与以下类似的输出:

If browser window does not open automatically, open it by clicking on the link:
http://127.0.0.1:55811/7c22cd2d-c93b-41f4-ad70-a829e247fff9
> Profile URL: https://ec2-12-34-56-78.nip.io:3080

Logged in as: johnsmith
Cluster: ec2-12-23-56-78.nip.io
Roles: admin*
Logins: ec2-user
Valid until: 2019-07-09 05:07:13 +0800 CST [valid for 12h0m0s]
Extensions: permit-agent-forwarding, permit-port-forwarding, permit-pty

现在您可以使用 kubectl 访问 Kubernetes API 服务器。运行 kubectl config get-contexts,验证您的 kubeconfig 已被替换并且您已经通过代理连接。

总结

通过将 Teleport 作为身份验证代理,除 IAM 角色和用户外,您还可以使用 GitHub 身份来验证需要访问 EKS 集群的用户身份。这对于取得 IAM 委托人较为困难或者耗时较长的环境尤其有用。此外,它对于团队组成的流动性较大的环境也非常有用。

与以往一样,我们欢迎大家就本博文发表反馈意见。如果您有任何建议或问题,请单击下方的查看评论按钮。