Implement Secrets Management System: Gitea Actions + User-Secrets (AGENTS.md v16.0)
ci / backend (push) Failing after 0s
Build & Test with Secrets / build (push) Failing after 2s
ci / static (push) Failing after 7s
Build & Test with Secrets / security-scan (push) Successful in 5s
ci / frontend (push) Failing after 1m3s
Build & Test with Secrets / frontend (push) Failing after 1m1s
Build & Test with Secrets / notification (push) Failing after 1s
ci / backend (push) Failing after 0s
Build & Test with Secrets / build (push) Failing after 2s
ci / static (push) Failing after 7s
Build & Test with Secrets / security-scan (push) Successful in 5s
ci / frontend (push) Failing after 1m3s
Build & Test with Secrets / frontend (push) Failing after 1m1s
Build & Test with Secrets / notification (push) Failing after 1s
## Changes
### Security Infrastructure
- **Program.cs**: ResolveSecret() helper for secure secret resolution
- Priority: environment variables (CI/CD) → user-secrets (local) → appsettings (fallback)
- Validates all required secrets at startup (fail-fast)
- **ExternalApiOptions.cs**: Type-safe configuration for external APIs
- KRX OpenAPI (Korea Exchange market data)
- OpenDart API (financial disclosures)
- KIS API (trading & orders)
- Injected via IOptions<T> dependency injection
- **appsettings.json**: Safe placeholders (${VAR_NAME}) instead of hardcoded secrets
- Never stores actual credentials
- Production uses environment variable substitution
### CI/CD Integration
- **.gitea/workflows/secrets-injection.yml**: Automated secret injection
- Receives secrets from Gitea Actions Secrets
- Injects as environment variables at build time
- Masks secrets in logs
- No secrets stored in artifacts
### Local Development
- **docs/SECRETS_LOCAL_DEVELOPMENT.md**: Complete setup guide
- One-time user-secrets initialization
- How to store/update secrets locally
- Troubleshooting for common issues
- **SECRETS_CONFIGURATION_SUMMARY.md**: Architecture & security properties
- Secret resolution priority
- Usage patterns in application code
- Security audit checklist
- Rotation procedures
## Security Properties
✅ Secrets never hardcoded in code
✅ Secrets never committed to git
✅ Secrets never logged or exposed in traces
✅ Secrets never stored in CI artifacts
✅ Local isolation via ~/.microsoft/usersecrets/
✅ CI/CD isolation via Gitea Actions Secrets (encrypted)
✅ Rotation support (update secret → next build uses new value)
## Compliance
- Follows AGENTS.md v16.0 security guardrails
- No magic numbers or hardcoded API keys
- All external API keys managed through centralized options
- Type-safe dependency injection eliminates string-based configuration
## Next Steps
1. Local dev: Run `dotnet user-secrets init` and configure
2. CI/CD: Add secrets to Gitea Actions Secrets
3. Verify: `dotnet run` should work without "secret is required" errors
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
name: Build & Test with Secrets
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop]
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
|
||||
env:
|
||||
# Inject secrets from Gitea Actions Secrets
|
||||
KARTSELL_POSTGRES: ${{ secrets.KARTSELL_POSTGRES }}
|
||||
KRX_API_KEY: ${{ secrets.KRX_API_KEY }}
|
||||
OPENDART_API_KEY: ${{ secrets.OPENDART_API_KEY }}
|
||||
KIS_API_KEY: ${{ secrets.KIS_API_KEY }}
|
||||
KIS_SECRET_KEY: ${{ secrets.KIS_SECRET_KEY }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16
|
||||
env:
|
||||
POSTGRES_USER: kartsell
|
||||
POSTGRES_PASSWORD: kartsell
|
||||
POSTGRES_DB: kartsell
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '10.0.x'
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore KArtSell.sln
|
||||
|
||||
- name: Build (Release)
|
||||
run: dotnet build KArtSell.sln -c Release --no-restore
|
||||
|
||||
- name: Run database migrations
|
||||
run: dotnet run --project src/KArtSell.DbMigrator -c Release
|
||||
env:
|
||||
# PostgreSQL in GitHub Actions is on localhost:5432
|
||||
KARTSELL_POSTGRES: "Host=localhost;Port=5432;Database=kartsell;Username=kartsell;Password=kartsell"
|
||||
|
||||
- name: Run tests
|
||||
run: dotnet test KArtSell.sln -c Release --no-build --logger "trx" --collect:"XPlat Code Coverage"
|
||||
env:
|
||||
# Use test database
|
||||
KARTSELL_POSTGRES: "Host=localhost;Port=5432;Database=kartsell;Username=kartsell;Password=kartsell"
|
||||
# Secrets available for integration tests
|
||||
KRX_API_KEY: ${{ secrets.KRX_API_KEY }}
|
||||
|
||||
- name: Upload test results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-results
|
||||
path: '**/TestResults/**/*.trx'
|
||||
|
||||
frontend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
|
||||
- name: Install pnpm
|
||||
run: npm install -g pnpm@10
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
cd frontend
|
||||
pnpm install --frozen-lockfile
|
||||
|
||||
- name: Type check
|
||||
run: |
|
||||
cd frontend
|
||||
pnpm typecheck
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
cd frontend
|
||||
pnpm test
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cd frontend
|
||||
pnpm build
|
||||
|
||||
- name: E2E Tests
|
||||
run: |
|
||||
cd frontend
|
||||
pnpm exec playwright install --with-deps chromium
|
||||
pnpm e2e
|
||||
env:
|
||||
# API secrets available for E2E if needed
|
||||
KRX_API_KEY: ${{ secrets.KRX_API_KEY }}
|
||||
|
||||
security-scan:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Verify no secrets in code
|
||||
run: |
|
||||
# Fail if credentials detected in source files
|
||||
! grep -r "password\|api_key\|secret" src/ --include="*.cs" --include="*.ts" --include="*.tsx" | grep -v "Configuration\|Options\|secrets"
|
||||
|
||||
notification:
|
||||
needs: [build, frontend]
|
||||
if: always()
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Report build status
|
||||
run: |
|
||||
echo "Build Status: ${{ needs.build.result }}"
|
||||
echo "Frontend Status: ${{ needs.frontend.result }}"
|
||||
|
||||
# Optional: Send to Telegram/Slack notification
|
||||
if [ "${{ needs.build.result }}" == "success" ] && [ "${{ needs.frontend.result }}" == "success" ]; then
|
||||
echo "✅ All checks passed"
|
||||
else
|
||||
echo "❌ Build failed"
|
||||
exit 1
|
||||
fi
|
||||
Reference in New Issue
Block a user