Bỏ qua

Environment & Secrets Management

Secrets Overview

Types of Secrets

┌─────────────────────────────────────────────────────────────┐
│                    SECRETS IN LIGHT STACK                    │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  Supabase:                                                   │
│  ├── SUPABASE_URL           (public - can expose)          │
│  ├── SUPABASE_ANON_KEY      (public - can expose)          │
│  └── SUPABASE_SERVICE_KEY   (SECRET - never expose!)       │
│                                                              │
│  Cloudflare:                                                 │
│  ├── CLOUDFLARE_ACCOUNT_ID  (semi-secret)                  │
│  └── CLOUDFLARE_API_TOKEN   (SECRET)                       │
│                                                              │
│  Third-party:                                                │
│  ├── STRIPE_SECRET_KEY      (SECRET)                       │
│  ├── SENDGRID_API_KEY       (SECRET)                       │
│  └── Other API keys         (SECRET)                       │
│                                                              │
└─────────────────────────────────────────────────────────────┘

GitHub Secrets

Repository Secrets

Repository → Settings → Secrets and variables → Actions

Types:
├── Repository secrets (all workflows)
├── Environment secrets (specific environment)
└── Organization secrets (shared across repos)

Add Secret

1. Go to Settings → Secrets → Actions
2. Click "New repository secret"
3. Name: SUPABASE_SERVICE_KEY
4. Value: (paste secret value)
5. Add secret

Note: Once added, value cannot be viewed again

GitHub Variables

Repository Variables

Repository → Settings → Secrets and variables → Actions → Variables

Use for non-sensitive configuration:
├── SUPABASE_PROJECT_REF
├── CLOUDFLARE_ACCOUNT_ID
├── NODE_VERSION
└── Other config values

Secrets vs Variables

Type Use For Access
Secrets API keys, passwords ${{ secrets.NAME }}
Variables Config, IDs ${{ vars.NAME }}

Environment-specific Secrets

Setup

# Workflow with environment secrets
jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    environment: staging  # Uses staging secrets
    steps:
      - name: Deploy
        env:
          SUPABASE_URL: ${{ vars.SUPABASE_URL }}
          SUPABASE_KEY: ${{ secrets.SUPABASE_SERVICE_KEY }}

  deploy-production:
    runs-on: ubuntu-latest
    environment: production  # Uses production secrets
    steps:
      - name: Deploy
        env:
          SUPABASE_URL: ${{ vars.SUPABASE_URL }}
          SUPABASE_KEY: ${{ secrets.SUPABASE_SERVICE_KEY }}

Environment Configuration

staging environment:
├── Secrets:
│   ├── SUPABASE_SERVICE_KEY=staging-key
│   └── CLOUDFLARE_API_TOKEN=token
├── Variables:
│   ├── SUPABASE_URL=https://staging.supabase.co
│   └── SUPABASE_PROJECT_REF=staging-ref
└── Protection: None (auto-deploy)

production environment:
├── Secrets:
│   ├── SUPABASE_SERVICE_KEY=production-key
│   └── CLOUDFLARE_API_TOKEN=token
├── Variables:
│   ├── SUPABASE_URL=https://production.supabase.co
│   └── SUPABASE_PROJECT_REF=production-ref
└── Protection:
    ├── Required reviewers: 1
    └── Deployment branches: main only

Cloudflare Secrets

Worker Secrets

# Set secret via CLI
wrangler secret put MY_SECRET
# Enter value when prompted

# Set for specific environment
wrangler secret put MY_SECRET --env staging

# List secrets (names only)
wrangler secret list

# Delete secret
wrangler secret delete MY_SECRET

In CI/CD

- name: Set Cloudflare Secrets
  run: |
    echo "${{ secrets.STRIPE_KEY }}" | wrangler secret put STRIPE_KEY
    echo "${{ secrets.SENDGRID_KEY }}" | wrangler secret put SENDGRID_KEY
  env:
    CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}

Environment Variables in Next.js

Build-time vs Runtime

// NEXT_PUBLIC_ = Available in browser (build-time embedded)
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=public-key

// Without NEXT_PUBLIC_ = Server-side only (never exposed to browser)
SUPABASE_SERVICE_KEY=secret-key
STRIPE_SECRET_KEY=sk_live_xxx

Accessing Variables

// Client-side (browser)
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;

// Server-side only
const serviceKey = process.env.SUPABASE_SERVICE_KEY;
// This is undefined in browser

Build-time Injection

GitHub Actions

- name: Build Next.js
  run: npm run build
  env:
    # Public variables (embedded in build)
    NEXT_PUBLIC_SUPABASE_URL: ${{ vars.SUPABASE_URL }}
    NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }}
    NEXT_PUBLIC_APP_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }}

Cloudflare Runtime

# wrangler.toml - Runtime variables
[vars]
SUPABASE_URL = "https://xxx.supabase.co"
APP_ENV = "production"

# Secrets set via CLI (not in toml)
# SUPABASE_SERVICE_KEY
# STRIPE_SECRET_KEY

Security Best Practices

Do's and Don'ts

✅ DO:
   - Use environment-specific secrets
   - Rotate secrets regularly
   - Use minimal permissions for tokens
   - Audit secret access
   - Use NEXT_PUBLIC_ prefix correctly

❌ DON'T:
   - Commit secrets to git
   - Log secret values
   - Use production secrets in staging
   - Share secrets in chat/email
   - Use same secret across all environments

Secret Rotation

# Rotate Supabase service key
# Dashboard → Project Settings → API → Regenerate service key

# Update in GitHub
# Settings → Secrets → Update SUPABASE_SERVICE_KEY

# Update in Cloudflare
wrangler secret put SUPABASE_SERVICE_KEY

Local Development

.env.local

# .env.local (gitignored)
NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321
NEXT_PUBLIC_SUPABASE_ANON_KEY=local-anon-key
SUPABASE_SERVICE_KEY=local-service-key

.gitignore

# Environment files
.env
.env.local
.env.*.local

# Cloudflare
.dev.vars

# Never commit
*.pem
*.key
credentials.json

Tổng kết

Secrets Checklist

  • [ ] Separate secrets per environment
  • [ ] Never commit secrets to git
  • [ ] Use NEXT_PUBLIC_ correctly
  • [ ] Set Cloudflare secrets via CLI
  • [ ] Configure environment protection
  • [ ] Document secret rotation process

Quick Reference

Platform Secret Storage
GitHub Settings → Secrets
Cloudflare wrangler secret put
Local .env.local, .dev.vars

Q&A

  1. Secret rotation policy?
  2. Có shared secrets giữa projects?
  3. Access audit cần không?