Loại bỏ Static Secrets: Hướng dẫn Thực tế về GitHub Actions OIDC

Security tutorial - IT technology blog
Security tutorial - IT technology blog

Cú điện thoại thức tỉnh trị giá 5.000 USD

Ít có điều gì phá hỏng buổi sáng của bạn hơn là một thông báo rằng hóa đơn AWS đã tăng vọt thêm 5.000 USD chỉ sau một đêm. Câu chuyện kinh dị này thường bắt đầu với một IAM Access Key duy nhất—bị kẹt trong một repo riêng tư hoặc bị bỏ quên trong GitHub Secrets—rơi vào tay kẻ xấu. Tôi đã từng dành không ít ca trực đêm để chống lại các cuộc tấn công brute-force qua SSH. Những trải nghiệm đó đã dạy tôi một sự thật phũ phàng: bất kỳ thông tin xác thực tĩnh (static credential) nào cũng là một gánh nặng rủi ro. Cho dù đó là mật khẩu hay API key, nếu nó không hết hạn, nó chính là một quả bom hẹn giờ.

Trong nhiều năm, việc triển khai từ GitHub Actions lên AWS hoặc GCP yêu cầu một service account key dài hạn. Bạn sẽ tạo nó, sao chép và dán vào GitHub Repository Secrets. Cảm giác có vẻ an toàn vì nó được “ẩn” đi, nhưng bí mật đó không bao giờ hết hạn. Nếu một kẻ xấu chiếm được khóa đó, chúng sẽ có một tấm vé thông hành vĩnh viễn vào hạ tầng của bạn. Chúng có thể làm tăng chi phí hoặc trích xuất dữ liệu cho đến khi bạn xoay vòng khóa (rotate key) hoặc thu hồi quyền truy cập một cách thủ công.

Tại sao thông tin xác thực dài hạn lại thất bại khi gặp áp lực

Vấn đề với các vụ vi phạm CI/CD thường không phải là do thiếu mã hóa. Đó là do vòng đời của thông tin xác thực. Static secrets có ba khuyết điểm chí tử:

  • Hiệu lực vô hạn: Một AWS Access Key tiêu chuẩn có hiệu lực trong nhiều years nếu bạn không xóa nó. Những dự án cũ, bị bỏ quên sẽ trở thành những lỗ hổng bảo mật lớn nhất của bạn.
  • Khó khăn khi xoay vòng: Các phương pháp bảo mật tốt nhất đề xuất xoay vòng khóa mỗi 90 ngày. Thực tế, hầu hết các nhóm đều thấy việc này rất tẻ nhạt. Việc xoay vòng bị bỏ qua, và các bí mật trở nên lỗi thời nhưng vẫn còn hiệu lực.
  • Phạm vi tiếp cận toàn cầu: Nếu một bí mật bị rò rỉ thông qua một dependency độc hại hoặc một log bị cấu hình sai, kẻ tấn công có thể sử dụng nó từ bất kỳ địa chỉ IP nào vào bất kỳ lúc nào.

Chúng ta cần một hệ thống nơi GitHub Actions chứng minh danh tính của nó mà không cần một “hộ chiếu” vĩnh viễn. Chúng ta cần một thẻ khách tạm thời có khả năng tự hủy.

Sự chuyển dịch: GitHub Secrets và OIDC

Hiểu được sự chuyển đổi này đòi hỏi chúng ta phải xem xét cách xác minh danh tính.

Cách cũ: Chìa khóa vạn năng

Bạn tạo một IAM Key hoặc một tệp JSON và lưu trữ nó trong GitHub. Mỗi khi pipeline của bạn chạy, GitHub sẽ gửi cùng một khóa này qua mạng. Nó không bao giờ thay đổi. Đó là một chìa khóa vạn năng có tác dụng mãi mãi.

Cách hiện đại: OpenID Connect (OIDC)

OIDC cho phép GitHub Actions đổi một token ngắn hạn lấy một thông tin xác thực đám mây tạm thời. Khi một workflow bắt đầu, GitHub đóng vai trò là Nhà cung cấp danh tính (Identity Provider). Nó cấp một JWT (JSON Web Token) duy nhất, được ký riêng cho job đó. Nhà cung cấp đám mây của bạn (AWS hoặc GCP) sẽ xác minh chữ ký và kiểm tra xem token đó có thuộc về repository và branch cụ thể của bạn hay không. Sau khi được xác minh, nó sẽ trả lại một token tạm thời thường hết hạn sau 60 phút. Không có gì được lưu trữ trong GitHub.

Thiết lập OIDC: Từng bước chuyển đổi

Triển khai việc này cho các nhà cung cấp đám mây lớn bao gồm việc dạy cho đám mây của bạn cách “tin tưởng” máy chủ danh tính của GitHub.

1. Cấu hình AWS cho OIDC

Đầu tiên, tạo một OIDC Identity Provider trong AWS IAM Console. Việc này giúp AWS nhận diện các token do GitHub cấp.

# Tạo OIDC provider thông qua AWS CLI
aws iam create-open-id-connect-provider \
    --url "https://token.actions.githubusercontent.com" \
    --client-id-list "sts.amazonaws.com" \
    --thumbprint-list "6938fd4d98bab03faadb97b34396831e3780aea1"

Tiếp theo, xác định một IAM Role cho GitHub Action của bạn. Trust Policy chính là người gác cổng. Nó đảm bảo chỉ có repository cụ thể của bạn mới có thể đảm nhận (assume) role này.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
        },
        "StringLike": {
          "token.actions.githubusercontent.com:sub": "repo:YourUsername/YourRepoName:*"
        }
      }
    }
  ]
}

2. Cấu hình GCP cho OIDC

Google Cloud sử dụng Workload Identity Pools. Đây là một cách tiếp cận trừu tượng hơn nhưng cung cấp khả năng kiểm soát chi tiết.

# 1. Khởi tạo một Workload Identity Pool
gcloud iam workload-identity-pools create "github-pool" \
    --location="global" --display-name="GitHub Actions Pool"

# 2. Liên kết OIDC Provider
gcloud iam workload-identity-pools providers create-oidc "github-provider" \
    --location="global" \
    --workload-identity-pool="github-pool" \
    --issuer-uri="https://token.actions.githubusercontent.com" \
    --attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository"

# 3. Liên kết Service Account với GitHub Repository
gcloud iam service-accounts add-iam-policy-binding "[email protected]" \
    --role="roles/iam.workloadIdentityUser" \
    --member="principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/github-pool/attribute.repository/YourUsername/YourRepoName"

3. Cập nhật Workflow

Tệp YAML của bạn cần một thay đổi cụ thể. Bạn phải cấp quyền id-token: write cho job. Nếu không có quyền này, GitHub không thể tạo JWT cần thiết cho quá trình bắt tay (handshake).

name: Triển khai Đám mây Bảo mật
on: [push]

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Xác thực AWS
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/GitHubActionRole
          aws-region: us-east-1

      - name: Xác minh Danh tính
        run: aws sts get-caller-identity

Tối ưu hóa chiến lược bảo mật của bạn

Chuyển sang OIDC giúp loại bỏ nỗi lo về việc xoay vòng bí mật và thắt chặt bảo mật cho pipeline của bạn. Để tối đa hóa an toàn, hãy tuân theo ba quy tắc sau:

  • Thu hẹp phạm vi: Đừng sử dụng các ký tự đại diện như repo:YourUsername/*. Hãy chỉ định chính xác repository và nếu có thể, cả branch triển khai (ví dụ: ref:refs/heads/main).
  • Áp dụng quyền tối thiểu: Ngay cả với OIDC, role của bạn không nên là Administrator. Nếu job chỉ tải dữ liệu lên S3, chỉ cấp quyền S3 PutObject.
  • Theo dõi Log: Sử dụng AWS CloudTrail hoặc GCP Cloud Audit Logs. Giám sát mọi yêu cầu liên kết danh tính (identity federation) trông có vẻ bất thường.

Bảo mật ngắn hạn (ephemeral security) là tiêu chuẩn vàng. Bằng cách loại bỏ các bí mật vĩnh viễn, bạn đảm bảo rằng một token bị đánh chặn sẽ trở nên vô giá trị vào thời điểm kẻ tấn công cố gắng sử dụng nó. Đó là cách tốt nhất để đảm bảo bạn không bao giờ phải thức dậy với một hóa đơn 5.000 USD bất ngờ nữa.

Share: