Tích hợp Infisical API với Python

KiloKilo
13 min read

Trong hệ sinh thái DevOps, quản lý bí mật (secrets) như API keys, mật khẩu cơ sở dữ liệu, hay chứng chỉ là một nhiệm vụ quan trọng nhưng đầy thách thức, đặc biệt trong môi trường Kubernetes. Infisical, một nền tảng quản lý bí mật mã nguồn mở, cung cấp API mạnh mẽ để truy xuất bí mật một cách an toàn và linh hoạt. Thay vì sử dụng External Secrets Operator để đồng bộ bí mật, bạn có thể sử dụng Infisical API trực tiếp trong mã Python để fetch bí mật theo nhu cầu. Trong bài blog này, chúng ta sẽ khám phá cách tích hợp Infisical API với Python trong một ứng dụng Kubernetes, cung cấp code mẫu và các lưu ý bảo mật để triển khai hiệu quả.

Tại sao sử dụng Infisical API với Python?

Việc sử dụng Infisical API để fetch bí mật trực tiếp từ code mang lại một số lợi ích:

  • Kiểm soát chi tiết: Bạn có thể fetch bí mật theo ngữ cảnh cụ thể trong ứng dụng, thay vì phụ thuộc vào các tài nguyên Kubernetes như Secret.

  • Linh hoạt hơn: API cho phép tích hợp với bất kỳ ngôn ngữ lập trình nào, không bị giới hạn bởi các công cụ như External Secrets Operator.

  • Phù hợp với ứng dụng đơn giản: Nếu ứng dụng của bạn không cần đồng bộ tự động hoặc quản lý bí mật phức tạp, gọi API trực tiếp là giải pháp nhẹ và nhanh chóng.

  • Tích hợp DevOps: Dễ dàng tích hợp vào các pipeline CI/CD hoặc các ứng dụng Python chạy trong Kubernetes.

Tuy nhiên, phương pháp này cũng yêu cầu bạn quản lý cẩn thận thông tin xác thực API và xử lý lỗi để đảm bảo bảo mật và độ tin cậy.

Hướng dẫn tích hợp Infisical API với Python

Dưới đây là các bước chi tiết để fetch bí mật từ Infisical API bằng Python trong một ứng dụng Kubernetes.

Bước 1: Thiết lập Infisical và lấy thông tin xác thực

  1. Triển khai Infisical: Bạn có thể sử dụng Infisical Cloud hoặc tự lưu trữ Infisical. Để tự lưu trữ, làm theo các bước sau:

     git clone https://github.com/Infisical/infisical
     cd infisical
     cp .env.example .env
     docker compose -f docker-compose.prod.yml up -d
    

    Sau khi triển khai, Infisical sẽ khả dụng tại http://<your-server-ip>:8080. Đăng ký tài khoản và tạo một dự án mới.

  2. Tạo Machine Identity:

    • Vào Project Settings > Access Control > Machine Identities trong giao diện web Infisical.

    • Chọn Universal Auth và tạo một cặp Client IDClient Secret.

    • Gán quyền truy cập cho Machine Identity vào dự án và môi trường (ví dụ: dev).

  3. Lưu trữ bí mật: Trong dự án Infisical, thêm các bí mật như API_KEY và DB_PASSWORD vào môi trường dev.

Bước 2: Chuẩn bị môi trường Python

Cài đặt thư viện requests để gọi API:

pip install requests

Đảm bảo ứng dụng của bạn chạy trong một Pod Kubernetes và có quyền truy cập vào Infisical API (thông qua Infisical Cloud hoặc endpoint tự lưu trữ).

Bước 3: Viết code Python để fetch bí mật

Dưới đây là code mẫu để fetch bí mật từ Infisical API bằng Python:

import requests
import os
import json

def get_infisical_token(client_id, client_secret, host_api="https://app.infisical.com"):
    """Lấy access token từ Infisical API bằng Universal Auth."""
    url = f"{host_api}/api/v1/auth/universal-auth/login"
    payload = {
        "clientId": client_id,
        "clientSecret": client_secret
    }
    headers = {"Content-Type": "application/json"}

    try:
        response = requests.post(url, json=payload, headers=headers)
        response.raise_for_status()
        return response.json()["accessToken"]
    except requests.RequestException as e:
        raise Exception(f"Failed to get token: {str(e)}")

def fetch_secrets(token, project_id, environment="dev", secrets_path="/", host_api="https://app.infisical.com"):
    """Fetch bí mật từ Infisical API."""
    url = f"{host_api}/api/v3/secrets/raw"
    params = {
        "environment": environment,
        "workspaceId": project_id,
        "secretPath": secrets_path
    }
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }

    try:
        response = requests.get(url, params=params, headers=headers)
        response.raise_for_status()
        secrets = response.json()["secrets"]
        return {secret["secretKey"]: secret["secretValue"] for secret in secrets}
    except requests.RequestException as e:
        raise Exception(f"Failed to fetch secrets: {str(e)}")

def main():
    # Lấy thông tin xác thực từ biến môi trường
    client_id = os.getenv("INFISICAL_CLIENT_ID")
    client_secret = os.getenv("INFISICAL_CLIENT_SECRET")
    project_id = os.getenv("INFISICAL_PROJECT_ID")

    if not all([client_id, client_secret, project_id]):
        raise ValueError("Missing required environment variables")

    # Lấy token và fetch bí mật
    token = get_infisical_token(client_id, client_secret)
    secrets = fetch_secrets(token, project_id)

    # Sử dụng bí mật
    api_key = secrets.get("API_KEY")
    db_password = secrets.get("DB_PASSWORD")
    print(f"API Key: {api_key}")
    print(f"Database Password: {db_password}")

if __name__ == "__main__":
    main()

Lưu code trên vào file fetch_secrets.py.

Bước 4: Cấu hình Kubernetes để chạy ứng dụng

  1. Lưu thông tin xác thực trong Kubernetes Secret: Tạo một Kubernetes Secret để lưu trữ Client ID, Client Secret, và Project ID:

     apiVersion: v1
     kind: Secret
     metadata:
       name: infisical-credentials
       namespace: default
     type: Opaque
     stringData:
       INFISICAL_CLIENT_ID: <your-client-id>
       INFISICAL_CLIENT_SECRET: <your-client-secret>
       INFISICAL_PROJECT_ID: <your-project-id>
    

    Áp dụng tệp YAML:

     kubectl apply -f infisical-credentials.yaml
    
  2. Tạo Pod Kubernetes: Tạo một Pod để chạy ứng dụng Python:

     apiVersion: v1
     kind: Pod
     metadata:
       name: infisical-app
       namespace: default
     spec:
       containers:
       - name: infisical-app
         image: python:3.9
         command: ["python", "/app/fetch_secrets.py"]
         env:
         - name: INFISICAL_CLIENT_ID
           valueFrom:
             secretKeyRef:
               name: infisical-credentials
               key: INFISICAL_CLIENT_ID
         - name: INFISICAL_CLIENT_SECRET
           valueFrom:
             secretKeyRef:
               name: infisical-credentials
               key: INFISICAL_CLIENT_SECRET
         - name: INFISICAL_PROJECT_ID
           valueFrom:
             secretKeyRef:
               name: infisical-credentials
               key: INFISICAL_PROJECT_ID
         volumeMounts:
         - name: app-code
           mountPath: /app
       volumes:
       - name: app-code
         configMap:
           name: infisical-app-code
    
  3. Tạo ConfigMap cho code Python: Lưu code Python vào một ConfigMap để mount vào Pod:

     apiVersion: v1
     kind: ConfigMap
     metadata:
       name: infisical-app-code
       namespace: default
     data:
       fetch_secrets.py: |
         import requests
         import os
         import json
    
         def get_infisical_token(client_id, client_secret, host_api="https://app.infisical.com"):
             url = f"{host_api}/api/v1/auth/universal-auth/login"
             payload = {
                 "clientId": client_id,
                 "clientSecret": client_secret
             }
             headers = {"Content-Type": "application/json"}
             try:
                 response = requests.post(url, json=payload, headers=headers)
                 response.raise_for_status()
                 return response.json()["accessToken"]
             except requests.RequestException as e:
                 raise Exception(f"Failed to get token: {str(e)}")
    
         def fetch_secrets(token, project_id, environment="dev", secrets_path="/", host_api="https://app.infisical.com"):
             url = f"{host_api}/api/v3/secrets/raw"
             params = {
                 "environment": environment,
                 "workspaceId": project_id,
                 "secretPath": secrets_path
             }
             headers = {
                 "Authorization": f"Bearer {token}",
                 "Content-Type": "application/json"
             }
             try:
                 response = requests.get(url, params=params, headers=headers)
                 response.raise_for_status()
                 secrets = response.json()["secrets"]
                 return {secret["secretKey"]: secret["secretValue"] for secret in secrets}
             except requests.RequestException as e:
                 raise Exception(f"Failed to fetch secrets: {str(e)}")
    
         def main():
             client_id = os.getenv("INFISICAL_CLIENT_ID")
             client_secret = os.getenv("INFISICAL_CLIENT_SECRET")
             project_id = os.getenv("INFISICAL_PROJECT_ID")
             if not all([client_id, client_secret, project_id]):
                 raise ValueError("Missing required environment variables")
             token = get_infisical_token(client_id, client_secret)
             secrets = fetch_secrets(token, project_id)
             api_key = secrets.get("API_KEY")
             db_password = secrets.get("DB_PASSWORD")
             print(f"API Key: {api_key}")
             print(f"Database Password: {db_password}")
    
         if __name__ == "__main__":
             main()
    

    Áp dụng ConfigMap và Pod:

     kubectl apply -f infisical-app-code.yaml
     kubectl apply -f infisical-app-pod.yaml
    

Bước 5: Kiểm tra kết quả

Kiểm tra log của Pod để xem bí mật được fetch thành công:

kubectl logs infisical-app

Kết quả mong đợi sẽ hiển thị các bí mật như API_KEY và DB_PASSWORD.

Lưu ý bảo mật

  1. Lưu trữ thông tin xác thực an toàn:

    • Đảm bảo Client ID và Client Secret được lưu trong Kubernetes Secret và không bị mã hóa cứng trong mã nguồn.

    • Sử dụng RBAC để giới hạn quyền truy cập vào Secret trong Kubernetes.

  2. Sử dụng HTTPS:

    • Luôn sử dụng endpoint HTTPS của Infisical (https://app.infisical.com hoặc endpoint tự lưu trữ với SSL) để mã hóa dữ liệu truyền tải.
  3. Xử lý lỗi:

    • Code Python đã bao gồm xử lý lỗi cơ bản, nhưng bạn nên thêm cơ chế retry hoặc fallback để xử lý các trường hợp API không khả dụng.
  4. Quản lý token:

    • Access token từ Infisical có thời hạn. Hãy lưu trữ token tạm thời và làm mới khi cần thiết để tránh gián đoạn.
  5. Hạn chế quyền Machine Identity:

    • Chỉ gán quyền truy cập tối thiểu cần thiết cho Machine Identity (ví dụ: chỉ đọc bí mật trong môi trường dev).

Lợi ích của phương pháp này

  • Đơn giản hóa triển khai: Không cần cài đặt thêm các công cụ như External Secrets Operator, giảm độ phức tạp của cụm Kubernetes.

  • Tùy chỉnh cao: Bạn có thể kiểm soát cách và thời điểm fetch bí mật, phù hợp với các ứng dụng có logic phức tạp.

  • Tích hợp linh hoạt: Dễ dàng tích hợp vào các ứng dụng Python hiện có hoặc các pipeline CI/CD.

  • Phù hợp với ứng dụng nhỏ: Lý tưởng cho các dự án không cần đồng bộ bí mật tự động hoặc không muốn sử dụng Operator.

So sánh với External Secrets Operator

So với việc sử dụng External Secrets Operator, phương pháp sử dụng Infisical API trực tiếp có một số ưu và nhược điểm:

  • Ưu điểm:

    • Không cần cài đặt và cấu hình thêm Operator, giảm tài nguyên cụm Kubernetes.

    • Linh hoạt hơn trong việc fetch bí mật theo logic ứng dụng.

    • Phù hợp với các ứng dụng nhỏ hoặc các dự án không sử dụng GitOps.

  • Nhược điểm:

    • Thiếu tự động hóa đồng bộ bí mật như ESO (bạn phải tự xử lý cập nhật bí mật).

    • Yêu cầu quản lý token và xử lý lỗi trong mã nguồn.

    • Không tận dụng được các tính năng như Secrets Store CSI Driver.

Nếu bạn cần tự động hóa cao hoặc tích hợp GitOps, External Secrets Operator có thể là lựa chọn tốt hơn. Tuy nhiên, với các ứng dụng cần kiểm soát chi tiết hoặc triển khai đơn giản, Infisical API là giải pháp lý tưởng.

Tại sao sử dụng Infisical Python SDK?

Infisical Python SDK mang lại nhiều lợi ích so với việc gọi API trực tiếp:

  • Giao diện đơn giản: SDK cung cấp các phương thức dễ sử dụng, giảm thiểu việc xử lý các yêu cầu HTTP và JSON thủ công.

  • Bảo trì tốt hơn: SDK xử lý các chi tiết như xác thực và làm mới token, giúp mã nguồn gọn gàng và đáng tin cậy.

  • Tích hợp DevOps: Dễ dàng tích hợp vào các ứng dụng Python hoặc pipeline CI/CD trong Kubernetes.

  • Tính linh hoạt: SDK hỗ trợ fetch bí mật từ các môi trường và đường dẫn khác nhau, phù hợp với các ứng dụng có yêu cầu phức tạp.

Phương pháp này đặc biệt phù hợp với các đội ngũ muốn tích hợp quản lý bí mật trực tiếp vào mã nguồn mà không cần các công cụ bổ sung như Operator.

Hướng dẫn sử dụng Infisical Python SDK trong Kubernetes

Dưới đây là các bước chi tiết để sử dụng Infisical Python SDK trong một ứng dụng Kubernetes.

Bước 1: Thiết lập Infisical và lấy thông tin xác thực

  1. Triển khai Infisical: Bạn có thể sử dụng Infisical Cloud hoặc tự lưu trữ Infisical. Để tự lưu trữ, thực hiện các bước sau:

     git clone https://github.com/Infisical/infisical
     cd infisical
     cp .env.example .env
     docker compose -f docker-compose.prod.yml up -d
    

    Sau khi triển khai, Infisical sẽ khả dụng tại http://<your-server-ip>:8080. Đăng ký tài khoản và tạo một dự án mới.

  2. Tạo Machine Identity:

    • Vào Project Settings > Access Control > Machine Identities trong giao diện web Infisical.

    • Chọn Universal Auth và tạo một cặp Client IDClient Secret.

    • Gán quyền truy cập cho Machine Identity vào dự án và môi trường (ví dụ: dev).

  3. Lưu trữ bí mật: Trong dự án Infisical, thêm các bí mật như API_KEY và DB_PASSWORD vào môi trường dev.

Bước 2: Chuẩn bị môi trường Python

Cài đặt Infisical Python SDK:

pip install infisical

Đảm bảo ứng dụng của bạn chạy trong một Pod Kubernetes và có quyền truy cập vào Infisical (thông qua Infisical Cloud hoặc endpoint tự lưu trữ).

Bước 3: Viết code Python sử dụng Infisical SDK

Dưới đây là code mẫu để fetch bí mật từ Infisical bằng Python SDK:

from infisical import InfisicalClient
import os

def fetch_secrets():
    """Fetch bí mật từ Infisical sử dụng Python SDK."""
    try:
        # Khởi tạo Infisical Client
        client = InfisicalClient(
            client_id=os.getenv("INFISICAL_CLIENT_ID"),
            client_secret=os.getenv("INFISICAL_CLIENT_SECRET"),
            url="https://app.infisical.com"  # Thay bằng endpoint tự lưu trữ nếu cần
        )

        # Fetch bí mật từ môi trường 'dev' của dự án
        secrets = client.get_all_secrets(
            environment="dev",
            workspace_id=os.getenv("INFISICAL_PROJECT_ID"),
            path="/"
        )

        # Chuyển đổi danh sách bí mật thành từ điển
        secrets_dict = {secret.secret_name: secret.secret_value for secret in secrets}

        return secrets_dict

    except Exception as e:
        raise Exception(f"Failed to fetch secrets: {str(e)}")

def main():
    # Lấy thông tin xác thực từ biến môi trường
    if not all([os.getenv("INFISICAL_CLIENT_ID"), os.getenv("INFISICAL_CLIENT_SECRET"), os.getenv("INFISICAL_PROJECT_ID")]):
        raise ValueError("Missing required environment variables")

    # Fetch bí mật
    secrets = fetch_secrets()

    # Sử dụng bí mật
    api_key = secrets.get("API_KEY")
    db_password = secrets.get("DB_PASSWORD")
    print(f"API Key: {api_key}")
    print(f"Database Password: {db_password}")

if __name__ == "__main__":
    main()

Lưu code trên vào file fetch_secrets.py.

Bước 4: Cấu hình Kubernetes để chạy ứng dụng

  1. Lưu thông tin xác thực trong Kubernetes Secret: Tạo một Kubernetes Secret để lưu trữ Client ID, Client Secret, và Project ID:

     apiVersion: v1
     kind: Secret
     metadata:
       name: infisical-credentials
       namespace: default
     type: Opaque
     stringData:
       INFISICAL_CLIENT_ID: <your-client-id>
       INFISICAL_CLIENT_SECRET: <your-client-secret>
       INFISICAL_PROJECT_ID: <your-project-id>
    

    Áp dụng tệp YAML:

     kubectl apply -f infisical-credentials.yaml
    
  2. Tạo Pod Kubernetes: Tạo một Pod để chạy ứng dụng Python:

     apiVersion: v1
     kind: Pod
     metadata:
       name: infisical-sdk-app
       namespace: default
     spec:
       containers:
       - name: infisical-sdk-app
         image: python:3.9
         command: ["python", "/app/fetch_secrets.py"]
         env:
         - name: INFISICAL_CLIENT_ID
           valueFrom:
             secretKeyRef:
               name: infisical-credentials
               key: INFISICAL_CLIENT_ID
         - name: INFISICAL_CLIENT_SECRET
           valueFrom:
             secretKeyRef:
               name: infisical-credentials
               key: INFISICAL_CLIENT_SECRET
         - name: INFISICAL_PROJECT_ID
           valueFrom:
             secretKeyRef:
               name: infisical-credentials
               key: INFISICAL_PROJECT_ID
         volumeMounts:
         - name: app-code
           mountPath: /app
       volumes:
       - name: app-code
         configMap:
           name: infisical-sdk-app-code
    
  3. Tạo ConfigMap cho code Python: Lưu code Python vào một ConfigMap để mount vào Pod:

     apiVersion: v1
     kind: ConfigMap
     metadata:
       name: infisical-sdk-app-code
       namespace: default
     data:
       fetch_secrets.py: |
         from infisical import InfisicalClient
         import os
    
         def fetch_secrets():
             try:
                 client = InfisicalClient(
                     client_id=os.getenv("INFISICAL_CLIENT_ID"),
                     client_secret=os.getenv("INFISICAL_CLIENT_SECRET"),
                     url="https://app.infisical.com"
                 )
                 secrets = client.get_all_secrets(
                     environment="dev",
                     workspace_id=os.getenv("INFISICAL_PROJECT_ID"),
                     path="/"
                 )
                 secrets_dict = {secret.secret_name: secret.secret_value for secret in secrets}
                 return secrets_dict
             except Exception as e:
                 raise Exception(f"Failed to fetch secrets: {str(e)}")
    
         def main():
             if not all([os.getenv("INFISICAL_CLIENT_ID"), os.getenv("INFISICAL_PROJECT_ID")]):
                 raise ValueError("Missing required environment variables")
             secrets = fetch_secrets()
             api_key = secrets.get("API_KEY")
             db_password = secrets.get("DB_PASSWORD")
             print(f"API Key: {api_key}")
             print(f"Database Password: {db_password}")
    
         if __name__ == "__main__":
             main()
    

    Áp dụng ConfigMap và Pod:

     kubectl apply -f infisical-sdk-app-code.yaml
     kubectl apply -f infisical-sdk-app-pod.yaml
    

Bước 5: Kiểm tra kết quả

Kiểm tra log của Pod để đảm bảo bí mật được fetch thành công:

kubectl logs infisical-sdk-app

Kết quả mong đợi sẽ hiển thị các bí mật như API_KEY và DB_PASSWORD.

Lưu ý bảo mật

1 MarkDown:1. Lưu trữ thông tin xác thực an toàn:

  • Đảm bảo Client ID, Client Secret, và Project ID được lưu trong Kubernetes Secret và không bị mã hóa cứng trong mã nguồn.

  • Sử dụng RBAC để giới hạn quyền truy cập vào Secret trong Kubernetes.

  1. Sử dụng HTTPS:

    • Luôn sử dụng endpoint HTTPS của Infisical (https://app.infisical.com hoặc endpoint tự lưu trữ với SSL) để mã hóa dữ liệu truyền tải.
  2. Xử lý lỗi:

    • Code mẫu đã bao gồm xử lý lỗi cơ bản. Bạn nên thêm cơ chế retry hoặc logging chi tiết để xử lý các trường hợp SDK không hoạt động.
  3. Quản lý quyền Machine Identity:

    • Chỉ gán quyền tối thiểu cần thiết cho Machine Identity (ví dụ: chỉ đọc bí mật trong môi trường dev).

    • Định kỳ xoay vòng (rotate) Client Secret để tăng cường bảo mật.

  4. Tối ưu hóa hiệu suất:

    • Cache bí mật trong bộ nhớ nếu chúng không thay đổi thường xuyên để giảm số lượng gọi API.

    • Sử dụng cơ chế làm mới token tự động của SDK để tránh hết hạn token.

Lợi ích của việc sử dụng Infisical Python SDK

  • Đơn giản hóa mã nguồn: SDK cung cấp giao diện cấp cao, giảm thiểu việc xử lý các chi tiết như xác thực và định dạng phản hồi API.

  • Tích hợp dễ dàng: Phù hợp với các ứng dụng Python hiện có, đặc biệt trong các dự án không sử dụng Operator hoặc GitOps.

  • Tùy chỉnh cao: Cho phép fetch bí mật theo môi trường, đường dẫn, hoặc logic cụ thể của ứng dụng.

  • Bảo mật mạnh mẽ: Infisical sử dụng mã hóa AES-GCM-256, và SDK đảm bảo truyền tải an toàn qua HTTPS.

So sánh với các phương pháp khác

So với việc sử dụng External Secrets Operator hoặc gọi API trực tiếp:

  • So với External Secrets Operator:

    • Ưu điểm: Không cần cài đặt Operator, giảm độ phức tạp cụm Kubernetes; phù hợp với các ứng dụng nhỏ hoặc không cần đồng bộ tự động.

    • Nhược điểm: Thiếu tự động hóa đồng bộ bí mật; bạn phải tự xử lý cập nhật bí mật trong ứng dụng.

  • So với gọi API trực tiếp:

    • Ưu điểm: SDK xử lý xác thực và làm mới token, giảm lỗi do cấu hình sai; mã nguồn gọn gàng hơn.

    • Nhược điểm: Phụ thuộc vào thư viện SDK, có thể giới hạn tùy chỉnh ở mức thấp hơn so với API thô.

Nếu bạn cần tích hợp GitOps hoặc đồng bộ tự động, External Secrets Operator có thể phù hợp hơn. Tuy nhiên, với các ứng dụng Python cần kiểm soát chi tiết và triển khai đơn giản, Infisical Python SDK là lựa chọn lý tưởng.

0
Subscribe to my newsletter

Read articles from Kilo directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Kilo
Kilo