Giaosucan's blog - Chia sẻ kiến thức theo cách bá đạo

Ticker

20/recent/ticker-posts

Project Zeus 07 - External Secrets

 


Tiếp tục bài 06, sau khi Jenkins đã được deploy trên K8s cluster, các team đều được cung cấp một namespace riêng để sử dụng. Mỗi namespace có đầy đủ các resource cần thiết như pod, volume, secrets.


K8s secret truyền thống

Theo cách truyền thống, các credentials như user/pass, certificate, token, key được lưu ở k8s secrets. Sử dụng k8s secrets, developer không cần phải hardcode những data này trong code, đảm bảo security , sensittive data không bị expose


How to K8s: Kubernetes Secrets Made Simple | MacStadium Blog

Secrets hoàn toàn độc lập so với k8s pod và được mount vào pod giống như 1 volume hoặc dưới dang biến môi trường

env:
      - name: SECRET_USERNAME
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: username
 - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true

Cách này ban đầu chạy khá ổn. Tuy nhiên khi hệ thống hoạt động một thời gian, team size growing, thì gặp một số vấn đề sau

Bản thân GCP hay AWS, Azure đều có service chuyên để lưu trữ sensitive data như GCP Secret Manager, Hashicorp Vault . Các service này ưu việt hơn so với k8s secret built-in. Hỗ trợ backup, auto-rotation , versioning . Điều này dẫn tới có 2 nơi cùng lưu secrets, làm mất đồng bộ dữ liệu, cần có một single source of truth cho sensite data

Sensitive data phải rotate liên tục để đảm bảo security . Chức năng này Secret Manager của GCP hay AWS đã có sẵn, chỉ việc setting trên console hoặc run cli as schedule

aws secretsmanager rotate-secret
    --secret-id MySecret 
    --rotation-lambda-arn arn:aws:lambda:us-east-2:123456789012:function:SecretsManagerMyLambdaFunction-alt-users
    --rotation-rules "{\"ScheduleExpression\": \"cron(0 16 1,15 * ? *)\", \"Duration\": \"2h\"}"

Còn trên K8s, team phải viết from scratch một bộ script python chạy cronjob execute rotation . Danh sách các secrets được đánh ID và lưu trong file config json. Script cronjobs đọc ID và thực hiện rotate. Mỗi khi secret được thêm bớt thì update file config này. Các này tuy chạy dc nhưng bất tiện khi số lượng secrets ngày một tăng , phải update file liên tục

External Secrets

Do đó cần phải có giải pháp khác đó là External Secrets

high-level

Idea là sync sensititve data từ Secret Manager của AWS/GCP hay Hashicorp vào K8s secrets. Developer chỉ cần update data trên secret manager thì k8s secret tự động đồng bộ theo. Toàn bộ code application trên k8s pod ko thay đổi, vẫn đọc data từ k8s secret như bình thường

architecture.png

Như vậy thay vì viết manifest tạo các secret như thông thường, developer sẽ thêm một  ExternalSecrets object dùng Custom Resource Definition

Install external secrets dùng Helm

helm repo add external-secrets https://charts.external-secrets.io

helm install external-secrets \
   external-secrets/external-secrets \
    -n external-secrets \
    --create-namespace \
  # --set installCRDs=true

Tạo Secret Store dùng provider AWS secret manager. Provider có thể là GCP, Azure hay Hashicorp tùy hệ thống

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: secretstore-sample
spec:
  provider:
    aws:
      service: SecretsManager
      region: us-east-1
      auth:
        secretRef:
          accessKeyIDSecretRef:
            name: awssm-secret
            key: access-key
          secretAccessKeySecretRef:
            name: awssm-secret
            key: secret-access-key

Sau đó tạo external secrets như k8s secrets thông thường

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: example
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: secretstore-sample
    kind: SecretStore
  target:
    name: secret-to-be-created
    creationPolicy: Owner
  data:
  - secretKey: secret-key-to-be-managed
    remoteRef:
      key: provider-key
      version: provider-key-version
      property: provider-key-property
  dataFrom:
  - extract:
      key: remote-key-in-the-provider

Nếu describe secrets bạn sẽ thấy trạng thái synced. Tuy nhiên thỉnh thoảng sẽ có lúc bị outofsynced , thường phải xóa đi deploy lại

Như vậy thì secret key secret-key-to-be-managed đã tự động sync với SM. Developer chỉ việc thay đổi thông tin trên SM là xong.

Vì Secret là sensitive data nên cơ chế phân quyền quy định user nào có quyền đọc. edit được define .

Việc deploy secrets cũng được thực hiện qua ArgoCD pipeline hoàn toàn tự động.

Như vậy k8s secret đã được Sync với SM. Còn một vấn đề nữa đó là Jenkins credentials, những credentials này trước kia được tạo/update manual nên rất khó quản lý và cũng chạy độc lập. Do đó cần phải sync credentials này vào k8s secrets, giúp cho mọi credentials được quy về một single source duy nhất là SM

Giải pháp là Kubernetes Credentials Provider, một plugin của Jenkins. Bằng plugin này thay vì add Jenkins credentials manually của GUI console, developer chỉ cần tạo k8s secrets là có credentials , tuy nhiên có 1 điểm khác so với k8s secrets thông thường là phải thêm label và annotation “jenkins.io/credentials-type” , “jenkins.io/credentials-description” vào tất cả secrets , để plugin này có thể nhận biết được

apiVersion: v1
kind: Secret
metadata:
# this is the jenkins id.
  name: "another-test-usernamepass"
  labels:
# so we know what type it is.
    "jenkins.io/credentials-type": "usernamePassword"
  annotations:
# description - can not be a label as spaces are not allowed
    "jenkins.io/credentials-description" : "credentials from Kubernetes"
type: Opaque
stringData:
  username: myUsername
  password: 'Pa$$word'

Nhờ đó việc tạo Jenkins credentials có thể thực hiện bằng code và chạy tự động qua argocd pipeline

Đăng nhận xét

0 Nhận xét