GitLab CI: 告别EC2 Instance Profile,拥抱OIDC

引言

在构建云原生CI/CD流水线时,我们经常遇到的第一个挑战就是"授权"。一个经典的场景是:运行在AWS EC2上的GitLab Runner需要将构建好的Docker镜像推送到AWS ECR(弹性容器镜像服务)。最直接的方案,莫过于为EC2实例配置一个拥有ECR推送权限的IAM Instance Profile。这套方案在纯AWS环境中优雅且高效,因为它免去了管理静态密钥的烦恼。但当我们的Runner需要运行在本地、数据中心或其他云平台时,这套"AWS专属"的授权模式便会立刻失灵。接下来将探讨这一困境,并引入一种更现代化、更具普适性的解决方案------利用OIDC(OpenID Connect)实现GitLab与AWS之间的安全、无缝认证,让CI/CD流水线彻底摆脱底层基础设施的束缚。

症结所在:Instance Profile 的"甜蜜陷阱"

Instance Profile 是一种将IAM角色关联到EC2实例的机制。实例上运行的应用程序(比如GitLab Runner)可以通过EC2的元数据服务,自动获取临时的AWS访问凭证。

它的优点显而易见:

  • 无需硬编码密钥 :代码库和CI/CD变量中都不需要存储敏感的AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY
  • 自动轮换:凭证是临时的,由AWS自动管理和轮换,安全性较高。

然而,这种便利性也带来了它的"陷阱"------基础设施强绑定。一旦GitLab Runner的执行环境离开了AWS EC2,整个授权链路就会中断。这在以下场景中会成为巨大的障碍:

  1. 混合云部署:一部分Runner在AWS,另一部分在本地物理机或VMware环境。
  2. 多云战略:业务可能同时使用AWS、Azure、GCP,Runner需要具备在不同云环境中执行的能力。
  3. 本地开发与调试:开发者在本地机器上无法模拟Instance Profile,导致本地环境与CI环境不一致,增加了调试难度。

因此,我们需要一种与运行环境无关的"身份认证"机制,让GitLab CI的作业(Job)本身成为一个可信的身份,无论它在哪里运行。

破局之道:OIDC,跨平台的信任桥梁

OIDC(OpenID Connect)是一个基于OAuth 2.0协议的简单身份层。它允许客户端(如GitLab CI Job)基于授权服务器(如GitLab本身)的认证,来验证用户的身份,并以一种可交互操作的、类似REST的方式,获取关于最终用户的基本信息。

在我们的场景中,这个流程可以被通俗地理解为:

  1. GitLab作为身份提供商 (IdP):当一个CI/CD作业运行时,GitLab可以为这个作业生成一个唯一的、短期的、包含特定信息(如项目路径、分支、标签等)的身份令牌(JWT - JSON Web Token)。
  2. AWS作为服务提供商 (SP):我们可以配置AWS IAM,使其信任来自特定GitLab实例的JWT。
  3. 建立信任关系:GitLab作业将自己的JWT令牌出示给AWS STS(Security Token Service),AWS验证令牌的签名和内容后,确认"此作业可信",然后颁发一个临时的AWS访问凭证。

整个过程就像GitLab给每个作业发了一张有时效的、防伪的"数字身份证",AWS的安全服务在校验证件无误后,授予其临时访问权限。

下面是这个授权流程的序列图:

实战演练:三步配置 GitLab 与 AWS 的 OIDC 信任

接下来,我们通过三个核心步骤,完成这套优雅的授权体系。

第一步:在 AWS IAM 中配置身份提供程序和角色

  1. 创建OIDC身份提供程序

    • 登录AWS IAM控制台,进入"身份提供商"页面,点击"添加提供商"。
    • 选择"OpenID Connect"。
    • 对于"提供商URL",填入你的GitLab实例地址,例如 https://gitlab.com 或自建实例的域名。
    • 点击"获取指纹"来验证。
    • 对于"受众",填入 https://gitlab.com 或你的实例域名。
    • 完成创建。
  2. 创建IAM角色

    • 创建一个新角色,选择"Web身份"作为可信实体类型。

    • 选择刚刚创建的OIDC提供商。

    • 在"受众"中选择你的GitLab实例。

    • 关键步骤:添加信任策略条件 。为了安全,我们需要精确控制哪个GitLab项目、哪个分支可以代入此角色。点击"编辑信任关系",添加Condition,限制sub(Subject)字段。例如,只允许my-group/my-project项目在main分支上的作业使用此角色:

      json 复制代码
      {
          "Version": "2012-10-17",
          "Statement": [
              {
                  "Effect": "Allow",
                  "Principal": {
                      "Federated": "arn:aws:iam::ACCOUNT_ID:oidc-provider/gitlab.com"
                  },
                  "Action": "sts:AssumeRoleWithWebIdentity",
                  "Condition": {
                      "StringEquals": {
                          "gitlab.com:sub": "project_path:my-group/my-project:ref_type:branch:ref:main"
                      }
                  }
              }
          ]
      }
    • 为该角色附加权限策略,比如AmazonEC2ContainerRegistryPowerUser,以允许它对ECR进行读写操作。

    • 记下这个角色的ARN,例如 arn:aws:iam::123456789012:role/gitlab-ecr-pusher

第二步:改造 .gitlab-ci.yml 文件

现在,我们来修改CI/CD的作业定义,让它去申请JWT并代入角色。

yaml 复制代码
build-and-push:
  stage: build
  image:
    name: amazon/aws-cli:2.13.0
    entrypoint: [""]
  id_tokens:
    GITLAB_OIDC_TOKEN:
      aud: https://gitlab.com # 与AWS OIDC提供商中配置的受众一致
  variables:
    ROLE_ARN: "arn:aws:iam::123456789012:role/gitlab-ecr-pusher"
    AWS_REGION: "us-east-1"
    ECR_REGISTRY: "123456789012.dkr.ecr.us-east-1.amazonaws.com"
    IMAGE_TAG: $CI_COMMIT_SHORT_SHA
  script:
    - echo "Authenticating with AWS using OIDC..."
    # 1. 使用OIDC令牌换取临时AWS凭证
    - |
    aws sts assume-role-with-web-identity \
        --role-arn ${ROLE_ARN} \
        --role-session-name "GitLabRunner-${CI_PIPELINE_ID}" \
        --web-identity-token ${GITLAB_OIDC_TOKEN} \
        --duration-seconds 3600 \
        --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' \
        --output text
      CREDS=$(aws sts assume-role-with-web-identity \
        --role-arn ${ROLE_ARN} \
        --role-session-name "GitLabRunner-${CI_PIPELINE_ID}" \
        --web-identity-token ${GITLAB_OIDC_TOKEN} \
        --duration-seconds 3600 \
        --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' \
        --output text)
    - export AWS_ACCESS_KEY_ID=$(echo "${CREDS}" | awk '{print $1}')
    - export AWS_SECRET_ACCESS_KEY=$(echo "${CREDS}" | awk '{print $2}')
    - export AWS_SESSION_TOKEN=$(echo "${CREDS}" | awk '{print $3}')

    - echo "Logging in to Amazon ECR..."
    # 2. 使用临时凭证登录ECR
    - aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin ${ECR_REGISTRY}

    - echo "Building and pushing Docker image..."
    # 3. 正常执行docker build和push
    # - docker build -t ${ECR_REGISTRY}/my-app:${IMAGE_TAG} .
    # - docker push ${ECR_REGISTRY}/my-app:${IMAGE_TAG}
    - echo "Push complete!"

在这个脚本中:

  • id_tokens关键字指示GitLab为作业生成一个OIDC令牌,并将其存储在GITLAB_OIDC_TOKEN环境变量中。
  • aws sts assume-role-with-web-identity命令是核心,它用JWT换取了临时的AWS凭证。
  • 后续的docker logindocker push命令就和常规操作完全一样了。
结论:拥抱解耦与安全的未来

通过从Instance Profile迁移到OIDC联邦认证,我们不仅解决了跨平台部署的兼容性问题,更在安全性和灵活性上迈出了一大步。这种模式将授权的信任基础从"物理位置"(运行在哪台机器上)转移到了"逻辑身份"(是哪个项目的哪个作业),这正是现代DevSecOps所倡导的核心理念。

现在,无论是运行在AWS EC2、本地数据中心,还是竞争对手的云平台上,我们的GitLab CI流水线都能以统一、安全的方式与AWS ECR进行交互。这不仅提升了系统的可移植性,也为未来的架构演进铺平了道路。对于追求构建健壮、灵活CI/CD体系的团队而言,掌握并实践OIDC无疑是一项高价值的投资。

相关推荐
C-20021 天前
CICD 持续集成与持续交付
运维·ci/cd
Doris_LMS2 天前
Git在idea中的实战使用经验(二)
linux·运维·gitlab·idea
叫我阿柒啊2 天前
从全栈开发到微服务架构:一位Java工程师的实战经验分享
java·ci/cd·kafka·mybatis·vue3·springboot·fullstack
CHENBT_2 天前
mac idea 配置了Gitlab的远程地址,但是每次pull 或者push 都要输入密码,怎么办
gitlab
starvapour2 天前
在windows server 2022搭建gitlab……但是失败了
windows·gitlab
张3蜂2 天前
GitLab Boards 深度解析:选型、竞品、成本与资源消耗
gitlab
烟花的学习笔记2 天前
【科普向-第七篇】Git全家桶介绍:Git/Gitlab/GitHub/TortoiseGit/Sourcetree
git·gitlab·github·tortoisegit·嵌入式软件开发·sourcetree
张3蜂2 天前
Jira vs. GitLab Issues vs. Redmine:终极选型与成本分析
gitlab·jira·redmine
Doris_LMS2 天前
Git在idea中的实战使用经验(一)
java·git·gitlab·idea