devops folders consolidate
This commit is contained in:
164
deploy/README.md
Normal file
164
deploy/README.md
Normal file
@@ -0,0 +1,164 @@
|
||||
# Deploy
|
||||
|
||||
Deployment infrastructure for StellaOps. Clean, consolidated deployment configurations.
|
||||
|
||||
## Infrastructure Stack
|
||||
|
||||
| Component | Technology | Version |
|
||||
|-----------|------------|---------|
|
||||
| Database | PostgreSQL | 18.1 |
|
||||
| Messaging/Cache | Valkey | 9.0.1 |
|
||||
| Object Storage | RustFS | latest |
|
||||
| Transparency Log | Rekor | v2 (tiles) |
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
deploy/
|
||||
├── compose/ # Docker Compose configurations
|
||||
│ ├── docker-compose.stella-ops.yml # Main stack
|
||||
│ ├── docker-compose.telemetry.yml # Observability (OTEL, Prometheus, Tempo, Loki)
|
||||
│ ├── docker-compose.testing.yml # CI/testing infrastructure
|
||||
│ ├── docker-compose.compliance-*.yml # Regional crypto overlays
|
||||
│ ├── env/ # Environment templates
|
||||
│ └── scripts/ # Compose lifecycle scripts
|
||||
│
|
||||
├── helm/ # Kubernetes Helm charts
|
||||
│ └── stellaops/ # Main chart with env-specific values
|
||||
│ ├── values-dev.yaml
|
||||
│ ├── values-stage.yaml
|
||||
│ ├── values-prod.yaml
|
||||
│ └── values-airgap.yaml
|
||||
│
|
||||
├── docker/ # Container build infrastructure
|
||||
│ ├── Dockerfile.hardened.template # Multi-stage hardened template
|
||||
│ ├── Dockerfile.console # Angular UI
|
||||
│ ├── build-all.sh # Build matrix
|
||||
│ └── services-matrix.env # Service build args
|
||||
│
|
||||
├── database/ # PostgreSQL infrastructure
|
||||
│ ├── migrations/ # Schema migrations
|
||||
│ ├── postgres/ # CloudNativePG configs
|
||||
│ ├── postgres-partitioning/ # Table partitioning
|
||||
│ └── postgres-validation/ # RLS validation
|
||||
│
|
||||
├── scripts/ # Operational scripts
|
||||
│ ├── bootstrap-trust.sh # TrustMonger initialization
|
||||
│ ├── rotate-rekor-key.sh # Key rotation
|
||||
│ ├── test-local.sh # Local testing
|
||||
│ └── lib/ # Shared script libraries
|
||||
│
|
||||
├── offline/ # Air-gap deployment
|
||||
│ ├── airgap/ # Bundle creation tools
|
||||
│ ├── kit/ # Installation kit
|
||||
│ └── templates/ # Offline config templates
|
||||
│
|
||||
├── telemetry/ # Observability (consolidated)
|
||||
│ ├── alerts/ # Prometheus/Alertmanager rules
|
||||
│ ├── dashboards/ # Grafana dashboards
|
||||
│ ├── collectors/ # OTEL collector configs
|
||||
│ └── storage/ # Prometheus/Loki/Tempo configs
|
||||
│
|
||||
├── secrets/ # Secret management templates
|
||||
│ └── *.example # Example secret structures
|
||||
│
|
||||
├── releases/ # Release manifests
|
||||
│ └── *.yaml # Version pinning per channel
|
||||
│
|
||||
└── tools/ # Curated operational tools
|
||||
├── ci/ # Build/CI tools (nuget-prime, determinism)
|
||||
├── feeds/ # Feed management (concelier, vex)
|
||||
├── security/ # Security (attest, cosign, crypto)
|
||||
└── validation/ # Validation scripts
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Local Development (Docker Compose)
|
||||
|
||||
```bash
|
||||
# Start full stack
|
||||
docker compose -f deploy/compose/docker-compose.stella-ops.yml up -d
|
||||
|
||||
# Start with telemetry
|
||||
docker compose -f deploy/compose/docker-compose.stella-ops.yml \
|
||||
-f deploy/compose/docker-compose.telemetry.yml up -d
|
||||
|
||||
# Regional compliance overlay (e.g., China SM2/SM3/SM4)
|
||||
docker compose -f deploy/compose/docker-compose.stella-ops.yml \
|
||||
-f deploy/compose/docker-compose.compliance-china.yml up -d
|
||||
```
|
||||
|
||||
### Kubernetes (Helm)
|
||||
|
||||
```bash
|
||||
# Install to dev environment
|
||||
helm install stellaops deploy/helm/stellaops \
|
||||
-f deploy/helm/stellaops/values-dev.yaml \
|
||||
-n stellaops --create-namespace
|
||||
|
||||
# Install to production
|
||||
helm install stellaops deploy/helm/stellaops \
|
||||
-f deploy/helm/stellaops/values-prod.yaml \
|
||||
-n stellaops --create-namespace
|
||||
```
|
||||
|
||||
### Air-Gapped Installation
|
||||
|
||||
```bash
|
||||
# Create offline bundle
|
||||
python deploy/offline/airgap/build_bootstrap_pack.py --version 2026.04
|
||||
|
||||
# Import on air-gapped system
|
||||
deploy/offline/airgap/import-bundle.sh stellaops-2026.04-bundle.tar.gz
|
||||
```
|
||||
|
||||
## Compose Profiles
|
||||
|
||||
| File | Purpose | Services |
|
||||
|------|---------|----------|
|
||||
| `stella-ops.yml` | Main stack | PostgreSQL, Valkey, RustFS, Rekor, all StellaOps services |
|
||||
| `telemetry.yml` | Observability | OTEL Collector, Prometheus, Tempo, Loki |
|
||||
| `testing.yml` | CI/Testing | postgres-test, valkey-test, mock-registry |
|
||||
| `compliance-china.yml` | China crypto | SM2/SM3/SM4 overlays |
|
||||
| `compliance-russia.yml` | Russia crypto | GOST R 34.10 overlays |
|
||||
| `compliance-eu.yml` | EU crypto | eIDAS overlays |
|
||||
| `dev.yml` | Development | Minimal stack with hot-reload |
|
||||
|
||||
## Connection Strings
|
||||
|
||||
```bash
|
||||
# PostgreSQL
|
||||
Host=stellaops-postgres;Port=5432;Database=stellaops;Username=stellaops;Password=<secret>
|
||||
|
||||
# Valkey
|
||||
stellaops-valkey:6379
|
||||
|
||||
# RustFS (S3-compatible)
|
||||
http://stellaops-rustfs:8080
|
||||
```
|
||||
|
||||
## Migration from devops/
|
||||
|
||||
This `deploy/` directory is the consolidated replacement for the scattered `devops/` directory.
|
||||
Content has been reorganized:
|
||||
|
||||
| Old Location | New Location |
|
||||
|--------------|--------------|
|
||||
| `devops/compose/` | `deploy/compose/` |
|
||||
| `devops/helm/` | `deploy/helm/` |
|
||||
| `devops/docker/` | `deploy/docker/` |
|
||||
| `devops/database/` | `deploy/database/` |
|
||||
| `devops/scripts/` | `deploy/scripts/` |
|
||||
| `devops/offline/` | `deploy/offline/` |
|
||||
| `devops/observability/` + `devops/telemetry/` | `deploy/telemetry/` |
|
||||
| `devops/secrets/` | `deploy/secrets/` |
|
||||
| `devops/releases/` | `deploy/releases/` |
|
||||
|
||||
The following `devops/` content was archived or removed:
|
||||
- `devops/services/` - Scattered service configs (use compose overlays or helm values)
|
||||
- `devops/tools/` - Move operational tools to `tools/` at repo root
|
||||
- `devops/artifacts/` - CI artifacts (transient, should not be committed)
|
||||
- `devops/.nuget/` - Package cache (restore during build)
|
||||
- `devops/docs/` - Move to `docs/operations/`
|
||||
- `devops/gitlab/` - Legacy CI templates (repo uses Gitea)
|
||||
459
deploy/compose/README.md
Normal file
459
deploy/compose/README.md
Normal file
@@ -0,0 +1,459 @@
|
||||
# Stella Ops Docker Compose Profiles
|
||||
|
||||
Consolidated Docker Compose configuration for the StellaOps platform. All profiles use immutable image digests from `deploy/releases/*.yaml` and are validated via `docker compose config` in CI.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| I want to... | Command |
|
||||
|--------------|---------|
|
||||
| Run the full platform | `docker compose -f docker-compose.stella-ops.yml up -d` |
|
||||
| Add observability | `docker compose -f docker-compose.stella-ops.yml -f docker-compose.telemetry.yml up -d` |
|
||||
| Run CI/testing infrastructure | `docker compose -f docker-compose.testing.yml --profile ci up -d` |
|
||||
| Deploy with China compliance | See [China Compliance](#china-compliance-sm2sm3sm4) |
|
||||
| Deploy with Russia compliance | See [Russia Compliance](#russia-compliance-gost) |
|
||||
| Deploy with EU compliance | See [EU Compliance](#eu-compliance-eidas) |
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
### Core Stack Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `docker-compose.stella-ops.yml` | **Main stack**: PostgreSQL 18.1, Valkey 9.0.1, RustFS, Rekor v2, all StellaOps services |
|
||||
| `docker-compose.telemetry.yml` | **Observability**: OpenTelemetry collector, Prometheus, Tempo, Loki |
|
||||
| `docker-compose.testing.yml` | **CI/Testing**: Test databases, mock services, Gitea for integration tests |
|
||||
| `docker-compose.dev.yml` | **Minimal dev infrastructure**: PostgreSQL, Valkey, RustFS only |
|
||||
|
||||
### Specialized Infrastructure
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `docker-compose.bsim.yml` | **BSim analysis**: PostgreSQL for Ghidra binary similarity corpus |
|
||||
| `docker-compose.corpus.yml` | **Function corpus**: PostgreSQL for function behavior database |
|
||||
| `docker-compose.sealed-ci.yml` | **Air-gapped CI**: Sealed testing environment with authority, signer, attestor |
|
||||
| `docker-compose.telemetry-offline.yml` | **Offline observability**: Air-gapped Loki, Promtail, OTEL collector, Tempo, Prometheus |
|
||||
|
||||
### Regional Compliance Overlays
|
||||
|
||||
| File | Purpose | Jurisdiction |
|
||||
|------|---------|--------------|
|
||||
| `docker-compose.compliance-china.yml` | SM2/SM3/SM4 ShangMi crypto configuration | China (OSCCA) |
|
||||
| `docker-compose.compliance-russia.yml` | GOST R 34.10-2012 crypto configuration | Russia (FSB) |
|
||||
| `docker-compose.compliance-eu.yml` | eIDAS qualified trust services configuration | EU |
|
||||
|
||||
### Crypto Provider Overlays
|
||||
|
||||
| File | Purpose | Use Case |
|
||||
|------|---------|----------|
|
||||
| `docker-compose.crypto-sim.yml` | Universal crypto simulation | Testing without licensed crypto |
|
||||
| `docker-compose.cryptopro.yml` | CryptoPro CSP (real GOST) | Production Russia deployments |
|
||||
| `docker-compose.sm-remote.yml` | SM Remote service (real SM2) | Production China deployments |
|
||||
|
||||
### Additional Overlays
|
||||
|
||||
| File | Purpose | Use Case |
|
||||
|------|---------|----------|
|
||||
| `docker-compose.gpu.yaml` | NVIDIA GPU acceleration | Advisory AI inference with GPU |
|
||||
| `docker-compose.cas.yaml` | Content Addressable Storage | Dedicated CAS with retention policies |
|
||||
| `docker-compose.tile-proxy.yml` | Rekor tile caching proxy | Air-gapped Sigstore deployments |
|
||||
|
||||
### Supporting Files
|
||||
|
||||
| Path | Purpose |
|
||||
|------|---------|
|
||||
| `env/*.env.example` | Environment variable templates per profile |
|
||||
| `scripts/backup.sh` | Create deterministic volume snapshots |
|
||||
| `scripts/reset.sh` | Stop stack and remove volumes (with confirmation) |
|
||||
|
||||
---
|
||||
|
||||
## Usage Patterns
|
||||
|
||||
### Basic Development
|
||||
|
||||
```bash
|
||||
# Copy environment template
|
||||
cp env/stellaops.env.example .env
|
||||
|
||||
# Validate configuration
|
||||
docker compose -f docker-compose.stella-ops.yml config
|
||||
|
||||
# Start the platform
|
||||
docker compose -f docker-compose.stella-ops.yml up -d
|
||||
|
||||
# View logs
|
||||
docker compose -f docker-compose.stella-ops.yml logs -f scanner-web
|
||||
```
|
||||
|
||||
### With Observability
|
||||
|
||||
```bash
|
||||
# Generate TLS certificates for telemetry
|
||||
./ops/devops/telemetry/generate_dev_tls.sh
|
||||
|
||||
# Start platform with telemetry
|
||||
docker compose -f docker-compose.stella-ops.yml \
|
||||
-f docker-compose.telemetry.yml up -d
|
||||
```
|
||||
|
||||
### CI/Testing Infrastructure
|
||||
|
||||
```bash
|
||||
# Start CI infrastructure only (different ports to avoid conflicts)
|
||||
docker compose -f docker-compose.testing.yml --profile ci up -d
|
||||
|
||||
# Start mock services for integration testing
|
||||
docker compose -f docker-compose.testing.yml --profile mock up -d
|
||||
|
||||
# Start Gitea for SCM integration tests
|
||||
docker compose -f docker-compose.testing.yml --profile gitea up -d
|
||||
|
||||
# Start everything
|
||||
docker compose -f docker-compose.testing.yml --profile all up -d
|
||||
```
|
||||
|
||||
**Test Infrastructure Ports:**
|
||||
| Service | Port | Purpose |
|
||||
|---------|------|---------|
|
||||
| postgres-test | 5433 | PostgreSQL 18 for tests |
|
||||
| valkey-test | 6380 | Valkey for cache/queue tests |
|
||||
| rustfs-test | 8180 | S3-compatible storage |
|
||||
| mock-registry | 5001 | Container registry mock |
|
||||
| gitea | 3000 | Git hosting for SCM tests |
|
||||
|
||||
---
|
||||
|
||||
## Regional Compliance Deployments
|
||||
|
||||
### China Compliance (SM2/SM3/SM4)
|
||||
|
||||
**For Testing (simulation):**
|
||||
```bash
|
||||
docker compose -f docker-compose.stella-ops.yml \
|
||||
-f docker-compose.compliance-china.yml \
|
||||
-f docker-compose.crypto-sim.yml up -d
|
||||
```
|
||||
|
||||
**For Production (real SM crypto):**
|
||||
```bash
|
||||
docker compose -f docker-compose.stella-ops.yml \
|
||||
-f docker-compose.compliance-china.yml \
|
||||
-f docker-compose.sm-remote.yml up -d
|
||||
```
|
||||
|
||||
**With OSCCA-certified HSM:**
|
||||
```bash
|
||||
# Set HSM connection details in environment
|
||||
export SM_REMOTE_HSM_URL="https://sm-hsm.example.com:8900"
|
||||
export SM_REMOTE_HSM_API_KEY="your-api-key"
|
||||
|
||||
docker compose -f docker-compose.stella-ops.yml \
|
||||
-f docker-compose.compliance-china.yml \
|
||||
-f docker-compose.sm-remote.yml up -d
|
||||
```
|
||||
|
||||
**Algorithms:**
|
||||
- SM2: Public key cryptography (GM/T 0003-2012)
|
||||
- SM3: Hash function, 256-bit (GM/T 0004-2012)
|
||||
- SM4: Block cipher, 128-bit (GM/T 0002-2012)
|
||||
|
||||
---
|
||||
|
||||
### Russia Compliance (GOST)
|
||||
|
||||
**For Testing (simulation):**
|
||||
```bash
|
||||
docker compose -f docker-compose.stella-ops.yml \
|
||||
-f docker-compose.compliance-russia.yml \
|
||||
-f docker-compose.crypto-sim.yml up -d
|
||||
```
|
||||
|
||||
**For Production (CryptoPro CSP):**
|
||||
```bash
|
||||
# CryptoPro requires EULA acceptance
|
||||
CRYPTOPRO_ACCEPT_EULA=1 docker compose -f docker-compose.stella-ops.yml \
|
||||
-f docker-compose.compliance-russia.yml \
|
||||
-f docker-compose.cryptopro.yml up -d
|
||||
```
|
||||
|
||||
**Requirements for CryptoPro:**
|
||||
- CryptoPro CSP license files in `opt/cryptopro/downloads/`
|
||||
- `CRYPTOPRO_ACCEPT_EULA=1` environment variable
|
||||
- Valid CryptoPro container images
|
||||
|
||||
**Algorithms:**
|
||||
- GOST R 34.10-2012: Digital signature (256/512-bit)
|
||||
- GOST R 34.11-2012: Hash function (Streebog, 256/512-bit)
|
||||
- GOST R 34.12-2015: Block cipher (Kuznyechik, Magma)
|
||||
|
||||
---
|
||||
|
||||
### EU Compliance (eIDAS)
|
||||
|
||||
**For Testing (simulation):**
|
||||
```bash
|
||||
docker compose -f docker-compose.stella-ops.yml \
|
||||
-f docker-compose.compliance-eu.yml \
|
||||
-f docker-compose.crypto-sim.yml up -d
|
||||
```
|
||||
|
||||
**For Production:**
|
||||
EU eIDAS deployments typically integrate with external Qualified Trust Service Providers (QTSPs) rather than hosting crypto locally. Configure your QTSP integration in the application settings.
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.stella-ops.yml \
|
||||
-f docker-compose.compliance-eu.yml up -d
|
||||
```
|
||||
|
||||
**Standards:**
|
||||
- ETSI TS 119 312 compliant algorithms
|
||||
- Qualified electronic signatures
|
||||
- QTSP integration for qualified trust services
|
||||
|
||||
---
|
||||
|
||||
## Crypto Simulation Details
|
||||
|
||||
The `docker-compose.crypto-sim.yml` overlay provides a unified simulation service for all sovereign crypto profiles:
|
||||
|
||||
| Algorithm ID | Simulation | Use Case |
|
||||
|--------------|------------|----------|
|
||||
| `SM2`, `sm.sim` | HMAC-SHA256 | China testing |
|
||||
| `GOST12-256`, `GOST12-512` | HMAC-SHA256 | Russia testing |
|
||||
| `ru.magma.sim`, `ru.kuznyechik.sim` | HMAC-SHA256 | Russia testing |
|
||||
| `DILITHIUM3`, `FALCON512`, `pq.sim` | HMAC-SHA256 | Post-quantum testing |
|
||||
| `fips.sim`, `eidas.sim`, `kcmvp.sim` | ECDSA P-256 | FIPS/EU/Korea testing |
|
||||
|
||||
**Important:** Simulation is for testing only. Uses deterministic HMAC or static ECDSA keys—not suitable for production or compliance certification.
|
||||
|
||||
---
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
### Infrastructure Services
|
||||
|
||||
| Service | Default Port | Purpose |
|
||||
|---------|--------------|---------|
|
||||
| PostgreSQL | 5432 | Primary database |
|
||||
| Valkey | 6379 | Cache, queues, events |
|
||||
| RustFS | 8080 | S3-compatible artifact storage |
|
||||
| Rekor v2 | (internal) | Sigstore transparency log |
|
||||
|
||||
### Application Services
|
||||
|
||||
| Service | Default Port | Purpose |
|
||||
|---------|--------------|---------|
|
||||
| Authority | 8440 | OAuth2/OIDC identity provider |
|
||||
| Signer | 8441 | Cryptographic signing |
|
||||
| Attestor | 8442 | SLSA attestation |
|
||||
| Scanner Web | 8444 | SBOM/vulnerability scanning API |
|
||||
| Concelier | 8445 | Advisory aggregation |
|
||||
| Notify Web | 8446 | Notification service |
|
||||
| Issuer Directory | 8447 | CSAF publisher registry |
|
||||
| Advisory AI Web | 8448 | AI-powered advisory analysis |
|
||||
| Web UI | 8443 | Angular frontend |
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Key variables (see `env/*.env.example` for complete list):
|
||||
|
||||
```bash
|
||||
# Database
|
||||
POSTGRES_USER=stellaops
|
||||
POSTGRES_PASSWORD=<secret>
|
||||
POSTGRES_DB=stellaops_platform
|
||||
|
||||
# Authority
|
||||
AUTHORITY_ISSUER=https://authority.example.com
|
||||
|
||||
# Scanner
|
||||
SCANNER_EVENTS_ENABLED=false
|
||||
SCANNER_OFFLINEKIT_ENABLED=false
|
||||
|
||||
# Crypto (for compliance overlays)
|
||||
STELLAOPS_CRYPTO_PROFILE=default # or: china, russia, eu
|
||||
STELLAOPS_CRYPTO_ENABLE_SIM=0 # set to 1 for simulation
|
||||
|
||||
# CryptoPro (Russia only)
|
||||
CRYPTOPRO_ACCEPT_EULA=0 # must be 1 to use CryptoPro
|
||||
|
||||
# SM Remote (China only)
|
||||
SM_SOFT_ALLOWED=1 # software-only SM2
|
||||
SM_REMOTE_HSM_URL= # optional: OSCCA-certified HSM
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Networking
|
||||
|
||||
All profiles use a shared `stellaops` Docker network. Production deployments can attach a `frontdoor` network for reverse proxy integration:
|
||||
|
||||
```bash
|
||||
# Create external network for load balancer
|
||||
docker network create stellaops_frontdoor
|
||||
|
||||
# Set in environment
|
||||
export FRONTDOOR_NETWORK=stellaops_frontdoor
|
||||
```
|
||||
|
||||
Only externally-reachable services (Authority, Signer, Attestor, Concelier, Scanner Web, Notify Web, UI) attach to the frontdoor network. Infrastructure services (PostgreSQL, Valkey, RustFS) remain on the private network.
|
||||
|
||||
---
|
||||
|
||||
## Sigstore Tools
|
||||
|
||||
Enable Sigstore CLI tools (rekor-cli, cosign) with the `sigstore` profile:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.stella-ops.yml --profile sigstore up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## GPU Support for Advisory AI
|
||||
|
||||
GPU is disabled by default. To enable NVIDIA GPU inference:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.stella-ops.yml \
|
||||
-f docker-compose.gpu.yaml up -d
|
||||
```
|
||||
|
||||
**Requirements:**
|
||||
- NVIDIA GPU with CUDA support
|
||||
- nvidia-container-toolkit installed
|
||||
- Docker configured with nvidia runtime
|
||||
|
||||
---
|
||||
|
||||
## Content Addressable Storage (CAS)
|
||||
|
||||
The CAS overlay provides dedicated RustFS instances with retention policies for different artifact types:
|
||||
|
||||
```bash
|
||||
# Standalone CAS infrastructure
|
||||
docker compose -f docker-compose.cas.yaml up -d
|
||||
|
||||
# Combined with main stack
|
||||
docker compose -f docker-compose.stella-ops.yml \
|
||||
-f docker-compose.cas.yaml up -d
|
||||
```
|
||||
|
||||
**CAS Services:**
|
||||
| Service | Port | Purpose |
|
||||
|---------|------|---------|
|
||||
| rustfs-cas | 8180 | Runtime facts, signals, replay artifacts |
|
||||
| rustfs-evidence | 8181 | Merkle roots, hash chains, evidence bundles (immutable) |
|
||||
| rustfs-attestation | 8182 | DSSE envelopes, in-toto attestations (immutable) |
|
||||
|
||||
**Retention Policies (configurable via `env/cas.env.example`):**
|
||||
- Vulnerability DB: 7 days
|
||||
- SBOM artifacts: 365 days
|
||||
- Scan results: 90 days
|
||||
- Evidence bundles: Indefinite (immutable)
|
||||
- Attestations: Indefinite (immutable)
|
||||
|
||||
---
|
||||
|
||||
## Tile Proxy (Air-Gapped Sigstore)
|
||||
|
||||
For air-gapped deployments, the tile-proxy caches Rekor transparency log tiles locally from public Sigstore:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.stella-ops.yml \
|
||||
-f docker-compose.tile-proxy.yml up -d
|
||||
```
|
||||
|
||||
**Tile Proxy vs Rekor v2:**
|
||||
- Use `--profile sigstore` when running your own Rekor transparency log locally
|
||||
- Use `docker-compose.tile-proxy.yml` when caching tiles from public Sigstore (rekor.sigstore.dev)
|
||||
|
||||
**Configuration:**
|
||||
| Variable | Default | Purpose |
|
||||
|----------|---------|---------|
|
||||
| `REKOR_SERVER_URL` | `https://rekor.sigstore.dev` | Upstream Rekor to proxy |
|
||||
| `TILE_PROXY_SYNC_ENABLED` | `true` | Enable periodic tile sync |
|
||||
| `TILE_PROXY_SYNC_SCHEDULE` | `0 */6 * * *` | Sync every 6 hours |
|
||||
| `TILE_PROXY_CACHE_MAX_SIZE_GB` | `10` | Local cache size limit |
|
||||
|
||||
The proxy syncs tiles on schedule and serves them to internal services for offline verification.
|
||||
|
||||
---
|
||||
|
||||
## Maintenance
|
||||
|
||||
### Backup
|
||||
|
||||
```bash
|
||||
./scripts/backup.sh # Creates timestamped tar.gz of volumes
|
||||
```
|
||||
|
||||
### Reset
|
||||
|
||||
```bash
|
||||
./scripts/reset.sh # Stops stack, removes volumes (requires confirmation)
|
||||
```
|
||||
|
||||
### Validate Configuration
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.stella-ops.yml config
|
||||
```
|
||||
|
||||
### Update to New Release
|
||||
|
||||
1. Import new manifest to `deploy/releases/`
|
||||
2. Update image digests in compose files
|
||||
3. Run `docker compose config` to validate
|
||||
4. Run `deploy/tools/validate-profiles.sh` for audit
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Port Conflicts
|
||||
|
||||
Override ports in your `.env` file:
|
||||
```bash
|
||||
POSTGRES_PORT=5433
|
||||
VALKEY_PORT=6380
|
||||
SCANNER_WEB_PORT=8544
|
||||
```
|
||||
|
||||
### Service Dependencies
|
||||
|
||||
Services declare `depends_on` with health checks. If a service fails to start, check its dependencies:
|
||||
```bash
|
||||
docker compose -f docker-compose.stella-ops.yml ps
|
||||
docker compose -f docker-compose.stella-ops.yml logs postgres
|
||||
docker compose -f docker-compose.stella-ops.yml logs valkey
|
||||
```
|
||||
|
||||
### Crypto Provider Issues
|
||||
|
||||
For crypto simulation issues:
|
||||
```bash
|
||||
# Check sim-crypto service
|
||||
docker compose logs sim-crypto
|
||||
curl http://localhost:18090/keys
|
||||
```
|
||||
|
||||
For CryptoPro issues:
|
||||
```bash
|
||||
# Verify EULA acceptance
|
||||
echo $CRYPTOPRO_ACCEPT_EULA # must be 1
|
||||
|
||||
# Check CryptoPro service
|
||||
docker compose logs cryptopro-csp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Deployment Upgrade Runbook](../../docs/operations/devops/runbooks/deployment-upgrade.md)
|
||||
- [Local CI Guide](../../docs/technical/testing/LOCAL_CI_GUIDE.md)
|
||||
- [Crypto Profile Configuration](../../docs/security/crypto-profile-configuration.md)
|
||||
- [Regional Deployments](../../docs/operations/regional-deployments.md)
|
||||
@@ -1,15 +1,14 @@
|
||||
# Copyright (c) StellaOps. All rights reserved.
|
||||
# Licensed under BUSL-1.1.
|
||||
|
||||
# BSim PostgreSQL Database and Ghidra Headless Services
|
||||
# =============================================================================
|
||||
# BSIM - BINARY SIMILARITY ANALYSIS
|
||||
# =============================================================================
|
||||
# BSim PostgreSQL Database and Ghidra Headless Services for binary analysis.
|
||||
#
|
||||
# Usage:
|
||||
# docker compose -f docker-compose.bsim.yml up -d
|
||||
#
|
||||
# Environment variables:
|
||||
# Environment:
|
||||
# BSIM_DB_PASSWORD - PostgreSQL password for BSim database
|
||||
|
||||
version: '3.8'
|
||||
# =============================================================================
|
||||
|
||||
services:
|
||||
bsim-postgres:
|
||||
@@ -22,9 +21,9 @@ services:
|
||||
POSTGRES_INITDB_ARGS: "-E UTF8 --locale=C"
|
||||
volumes:
|
||||
- bsim-data:/var/lib/postgresql/data
|
||||
- ./scripts/init-bsim.sql:/docker-entrypoint-initdb.d/10-init-bsim.sql:ro
|
||||
- ../docker/ghidra/scripts/init-bsim.sql:/docker-entrypoint-initdb.d/10-init-bsim.sql:ro
|
||||
ports:
|
||||
- "5433:5432"
|
||||
- "${BSIM_DB_PORT:-5433}:5432"
|
||||
networks:
|
||||
- stellaops-bsim
|
||||
healthcheck:
|
||||
@@ -34,10 +33,9 @@ services:
|
||||
retries: 5
|
||||
restart: unless-stopped
|
||||
|
||||
# Ghidra Headless service for BSim analysis
|
||||
ghidra-headless:
|
||||
build:
|
||||
context: .
|
||||
context: ../docker/ghidra
|
||||
dockerfile: Dockerfile.headless
|
||||
image: stellaops/ghidra-headless:11.2
|
||||
container_name: stellaops-ghidra
|
||||
@@ -61,13 +59,11 @@ services:
|
||||
limits:
|
||||
cpus: '4'
|
||||
memory: 8G
|
||||
# Keep container running for ad-hoc analysis
|
||||
entrypoint: ["tail", "-f", "/dev/null"]
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
bsim-data:
|
||||
driver: local
|
||||
ghidra-projects:
|
||||
ghidra-scripts:
|
||||
ghidra-output:
|
||||
@@ -75,4 +71,3 @@ volumes:
|
||||
networks:
|
||||
stellaops-bsim:
|
||||
driver: bridge
|
||||
|
||||
212
deploy/compose/docker-compose.cas.yaml
Normal file
212
deploy/compose/docker-compose.cas.yaml
Normal file
@@ -0,0 +1,212 @@
|
||||
# Content Addressable Storage (CAS) Infrastructure
|
||||
# Uses RustFS for S3-compatible immutable object storage
|
||||
# Aligned with best-in-class vulnerability scanner retention policies
|
||||
#
|
||||
# Usage (standalone):
|
||||
# docker compose -f docker-compose.cas.yaml up -d
|
||||
#
|
||||
# Usage (with main stack):
|
||||
# docker compose -f docker-compose.stella-ops.yml -f docker-compose.cas.yaml up -d
|
||||
|
||||
x-release-labels: &release-labels
|
||||
com.stellaops.release.version: "2025.10.0-edge"
|
||||
com.stellaops.release.channel: "edge"
|
||||
com.stellaops.profile: "cas"
|
||||
|
||||
x-cas-config: &cas-config
|
||||
# Retention policies (aligned with Trivy/Grype/Anchore Enterprise)
|
||||
# - vulnerability-db: 7 days (matches Trivy default)
|
||||
# - sbom-artifacts: 365 days (audit compliance)
|
||||
# - scan-results: 90 days (SOC2/ISO27001 typical)
|
||||
# - evidence-bundles: indefinite (immutable, content-addressed)
|
||||
# - attestations: indefinite (in-toto/DSSE signed)
|
||||
CAS__RETENTION__VULNERABILITY_DB_DAYS: "7"
|
||||
CAS__RETENTION__SBOM_ARTIFACTS_DAYS: "365"
|
||||
CAS__RETENTION__SCAN_RESULTS_DAYS: "90"
|
||||
CAS__RETENTION__EVIDENCE_BUNDLES_DAYS: "0" # 0 = indefinite
|
||||
CAS__RETENTION__ATTESTATIONS_DAYS: "0" # 0 = indefinite
|
||||
CAS__RETENTION__TEMP_ARTIFACTS_DAYS: "1"
|
||||
|
||||
networks:
|
||||
cas:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
rustfs-cas-data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: ${CAS_DATA_PATH:-/var/lib/stellaops/cas}
|
||||
rustfs-evidence-data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: ${CAS_EVIDENCE_PATH:-/var/lib/stellaops/evidence}
|
||||
rustfs-attestation-data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: ${CAS_ATTESTATION_PATH:-/var/lib/stellaops/attestations}
|
||||
|
||||
services:
|
||||
# Primary CAS storage - runtime facts, signals, replay artifacts
|
||||
rustfs-cas:
|
||||
image: registry.stella-ops.org/stellaops/rustfs:2025.09.2
|
||||
command: ["serve", "--listen", "0.0.0.0:8080", "--root", "/data"]
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
RUSTFS__LOG__LEVEL: "${RUSTFS_LOG_LEVEL:-info}"
|
||||
RUSTFS__STORAGE__PATH: /data
|
||||
RUSTFS__STORAGE__DEDUP: "true"
|
||||
RUSTFS__STORAGE__COMPRESSION: "${RUSTFS_COMPRESSION:-zstd}"
|
||||
RUSTFS__STORAGE__COMPRESSION_LEVEL: "${RUSTFS_COMPRESSION_LEVEL:-3}"
|
||||
# Bucket lifecycle (retention enforcement)
|
||||
RUSTFS__LIFECYCLE__ENABLED: "true"
|
||||
RUSTFS__LIFECYCLE__SCAN_INTERVAL_HOURS: "24"
|
||||
RUSTFS__LIFECYCLE__DEFAULT_RETENTION_DAYS: "90"
|
||||
# Access control
|
||||
RUSTFS__AUTH__ENABLED: "${RUSTFS_AUTH_ENABLED:-true}"
|
||||
RUSTFS__AUTH__API_KEY: "${RUSTFS_CAS_API_KEY:-cas-api-key-change-me}"
|
||||
RUSTFS__AUTH__READONLY_KEY: "${RUSTFS_CAS_READONLY_KEY:-cas-readonly-key-change-me}"
|
||||
# Service account configuration
|
||||
RUSTFS__ACCOUNTS__SCANNER__KEY: "${RUSTFS_SCANNER_KEY:-scanner-svc-key}"
|
||||
RUSTFS__ACCOUNTS__SCANNER__BUCKETS: "scanner-artifacts,surface-cache,runtime-facts"
|
||||
RUSTFS__ACCOUNTS__SCANNER__PERMISSIONS: "read,write"
|
||||
RUSTFS__ACCOUNTS__SIGNALS__KEY: "${RUSTFS_SIGNALS_KEY:-signals-svc-key}"
|
||||
RUSTFS__ACCOUNTS__SIGNALS__BUCKETS: "runtime-facts,signals-data,provenance-feed"
|
||||
RUSTFS__ACCOUNTS__SIGNALS__PERMISSIONS: "read,write"
|
||||
RUSTFS__ACCOUNTS__REPLAY__KEY: "${RUSTFS_REPLAY_KEY:-replay-svc-key}"
|
||||
RUSTFS__ACCOUNTS__REPLAY__BUCKETS: "replay-bundles,inputs-lock"
|
||||
RUSTFS__ACCOUNTS__REPLAY__PERMISSIONS: "read,write"
|
||||
RUSTFS__ACCOUNTS__READONLY__KEY: "${RUSTFS_READONLY_KEY:-readonly-svc-key}"
|
||||
RUSTFS__ACCOUNTS__READONLY__BUCKETS: "*"
|
||||
RUSTFS__ACCOUNTS__READONLY__PERMISSIONS: "read"
|
||||
<<: *cas-config
|
||||
volumes:
|
||||
- rustfs-cas-data:/data
|
||||
ports:
|
||||
- "${RUSTFS_CAS_PORT:-8180}:8080"
|
||||
networks:
|
||||
- cas
|
||||
labels: *release-labels
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
|
||||
# Evidence storage - Merkle roots, hash chains, evidence bundles (immutable)
|
||||
rustfs-evidence:
|
||||
image: registry.stella-ops.org/stellaops/rustfs:2025.09.2
|
||||
command: ["serve", "--listen", "0.0.0.0:8080", "--root", "/data", "--immutable"]
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
RUSTFS__LOG__LEVEL: "${RUSTFS_LOG_LEVEL:-info}"
|
||||
RUSTFS__STORAGE__PATH: /data
|
||||
RUSTFS__STORAGE__DEDUP: "true"
|
||||
RUSTFS__STORAGE__COMPRESSION: "${RUSTFS_COMPRESSION:-zstd}"
|
||||
RUSTFS__STORAGE__IMMUTABLE: "true" # Write-once, never delete
|
||||
# Access control
|
||||
RUSTFS__AUTH__ENABLED: "true"
|
||||
RUSTFS__AUTH__API_KEY: "${RUSTFS_EVIDENCE_API_KEY:-evidence-api-key-change-me}"
|
||||
RUSTFS__AUTH__READONLY_KEY: "${RUSTFS_EVIDENCE_READONLY_KEY:-evidence-readonly-key-change-me}"
|
||||
# Service accounts
|
||||
RUSTFS__ACCOUNTS__LEDGER__KEY: "${RUSTFS_LEDGER_KEY:-ledger-svc-key}"
|
||||
RUSTFS__ACCOUNTS__LEDGER__BUCKETS: "evidence-bundles,merkle-roots,hash-chains"
|
||||
RUSTFS__ACCOUNTS__LEDGER__PERMISSIONS: "read,write"
|
||||
RUSTFS__ACCOUNTS__EXPORTER__KEY: "${RUSTFS_EXPORTER_KEY:-exporter-svc-key}"
|
||||
RUSTFS__ACCOUNTS__EXPORTER__BUCKETS: "evidence-bundles"
|
||||
RUSTFS__ACCOUNTS__EXPORTER__PERMISSIONS: "read"
|
||||
volumes:
|
||||
- rustfs-evidence-data:/data
|
||||
ports:
|
||||
- "${RUSTFS_EVIDENCE_PORT:-8181}:8080"
|
||||
networks:
|
||||
- cas
|
||||
labels: *release-labels
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
|
||||
# Attestation storage - DSSE envelopes, in-toto attestations (immutable)
|
||||
rustfs-attestation:
|
||||
image: registry.stella-ops.org/stellaops/rustfs:2025.09.2
|
||||
command: ["serve", "--listen", "0.0.0.0:8080", "--root", "/data", "--immutable"]
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
RUSTFS__LOG__LEVEL: "${RUSTFS_LOG_LEVEL:-info}"
|
||||
RUSTFS__STORAGE__PATH: /data
|
||||
RUSTFS__STORAGE__DEDUP: "true"
|
||||
RUSTFS__STORAGE__COMPRESSION: "${RUSTFS_COMPRESSION:-zstd}"
|
||||
RUSTFS__STORAGE__IMMUTABLE: "true" # Write-once, never delete
|
||||
# Access control
|
||||
RUSTFS__AUTH__ENABLED: "true"
|
||||
RUSTFS__AUTH__API_KEY: "${RUSTFS_ATTESTATION_API_KEY:-attestation-api-key-change-me}"
|
||||
RUSTFS__AUTH__READONLY_KEY: "${RUSTFS_ATTESTATION_READONLY_KEY:-attestation-readonly-key-change-me}"
|
||||
# Service accounts
|
||||
RUSTFS__ACCOUNTS__ATTESTOR__KEY: "${RUSTFS_ATTESTOR_KEY:-attestor-svc-key}"
|
||||
RUSTFS__ACCOUNTS__ATTESTOR__BUCKETS: "attestations,dsse-envelopes,rekor-receipts"
|
||||
RUSTFS__ACCOUNTS__ATTESTOR__PERMISSIONS: "read,write"
|
||||
RUSTFS__ACCOUNTS__VERIFIER__KEY: "${RUSTFS_VERIFIER_KEY:-verifier-svc-key}"
|
||||
RUSTFS__ACCOUNTS__VERIFIER__BUCKETS: "attestations,dsse-envelopes,rekor-receipts"
|
||||
RUSTFS__ACCOUNTS__VERIFIER__PERMISSIONS: "read"
|
||||
volumes:
|
||||
- rustfs-attestation-data:/data
|
||||
ports:
|
||||
- "${RUSTFS_ATTESTATION_PORT:-8182}:8080"
|
||||
networks:
|
||||
- cas
|
||||
labels: *release-labels
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
|
||||
rekor-cli:
|
||||
image: ghcr.io/sigstore/rekor-cli:v1.4.3
|
||||
entrypoint: ["rekor-cli"]
|
||||
command: ["version"]
|
||||
profiles: ["sigstore"]
|
||||
networks:
|
||||
- cas
|
||||
labels: *release-labels
|
||||
|
||||
cosign:
|
||||
image: ghcr.io/sigstore/cosign:v3.0.4
|
||||
entrypoint: ["cosign"]
|
||||
command: ["version"]
|
||||
profiles: ["sigstore"]
|
||||
networks:
|
||||
- cas
|
||||
labels: *release-labels
|
||||
|
||||
# Lifecycle manager - enforces retention policies
|
||||
cas-lifecycle:
|
||||
image: registry.stella-ops.org/stellaops/cas-lifecycle:2025.10.0-edge
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
rustfs-cas:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
LIFECYCLE__CAS__ENDPOINT: "http://rustfs-cas:8080"
|
||||
LIFECYCLE__CAS__API_KEY: "${RUSTFS_CAS_API_KEY:-cas-api-key-change-me}"
|
||||
LIFECYCLE__SCHEDULE__CRON: "${LIFECYCLE_CRON:-0 3 * * *}" # 3 AM daily
|
||||
LIFECYCLE__POLICIES__VULNERABILITY_DB: "7d"
|
||||
LIFECYCLE__POLICIES__SBOM_ARTIFACTS: "365d"
|
||||
LIFECYCLE__POLICIES__SCAN_RESULTS: "90d"
|
||||
LIFECYCLE__POLICIES__TEMP_ARTIFACTS: "1d"
|
||||
LIFECYCLE__TELEMETRY__ENABLED: "${LIFECYCLE_TELEMETRY:-true}"
|
||||
LIFECYCLE__TELEMETRY__OTLP_ENDPOINT: "${OTLP_ENDPOINT:-}"
|
||||
networks:
|
||||
- cas
|
||||
labels: *release-labels
|
||||
|
||||
197
deploy/compose/docker-compose.compliance-china.yml
Normal file
197
deploy/compose/docker-compose.compliance-china.yml
Normal file
@@ -0,0 +1,197 @@
|
||||
# =============================================================================
|
||||
# STELLA OPS - COMPLIANCE OVERLAY: CHINA
|
||||
# =============================================================================
|
||||
# SM2/SM3/SM4 ShangMi (Commercial Cipher) crypto overlay.
|
||||
# This file extends docker-compose.stella-ops.yml with China-specific crypto.
|
||||
#
|
||||
# Usage:
|
||||
# docker compose -f devops/compose/docker-compose.stella-ops.yml \
|
||||
# -f devops/compose/docker-compose.compliance-china.yml up -d
|
||||
#
|
||||
# Cryptography:
|
||||
# - SM2: Elliptic curve cryptography (signature, key exchange)
|
||||
# - SM3: Hash function (256-bit digest)
|
||||
# - SM4: Block cipher (128-bit)
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
x-crypto-env: &crypto-env
|
||||
STELLAOPS_CRYPTO_PROFILE: "china"
|
||||
STELLAOPS_CRYPTO_CONFIG_PATH: "/app/etc/appsettings.crypto.yaml"
|
||||
STELLAOPS_CRYPTO_MANIFEST_PATH: "/app/etc/crypto-plugins-manifest.json"
|
||||
|
||||
x-crypto-volumes: &crypto-volumes
|
||||
- ../../etc/appsettings.crypto.china.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
|
||||
services:
|
||||
# ---------------------------------------------------------------------------
|
||||
# Authority - China crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
authority:
|
||||
image: registry.stella-ops.org/stellaops/authority:china
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/authority:/app/etc/authority:ro
|
||||
- ../../etc/certificates/trust-roots:/etc/ssl/certs/stellaops:ro
|
||||
- ../../etc/appsettings.crypto.china.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "china"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Signer - China crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
signer:
|
||||
image: registry.stella-ops.org/stellaops/signer:china
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.china.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "china"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Attestor - China crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
attestor:
|
||||
image: registry.stella-ops.org/stellaops/attestor:china
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.china.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "china"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Concelier - China crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
concelier:
|
||||
image: registry.stella-ops.org/stellaops/concelier:china
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- concelier-jobs:/var/lib/concelier/jobs
|
||||
- ../../etc/appsettings.crypto.china.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "china"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Scanner Web - China crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
scanner-web:
|
||||
image: registry.stella-ops.org/stellaops/scanner-web:china
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/scanner:/app/etc/scanner:ro
|
||||
- ../../etc/certificates/trust-roots:/etc/ssl/certs/stellaops:ro
|
||||
- scanner-surface-cache:/var/lib/stellaops/surface
|
||||
- ${SURFACE_SECRETS_HOST_PATH:-./offline/surface-secrets}:${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}:ro
|
||||
- ${SCANNER_OFFLINEKIT_TRUSTROOTS_HOST_PATH:-./offline/trust-roots}:${SCANNER_OFFLINEKIT_TRUSTROOTDIRECTORY:-/etc/stellaops/trust-roots}:ro
|
||||
- ${SCANNER_OFFLINEKIT_REKOR_SNAPSHOT_HOST_PATH:-./offline/rekor-snapshot}:${SCANNER_OFFLINEKIT_REKORSNAPSHOTDIRECTORY:-/var/lib/stellaops/rekor-snapshot}:ro
|
||||
- ../../etc/appsettings.crypto.china.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "china"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Scanner Worker - China crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
scanner-worker:
|
||||
image: registry.stella-ops.org/stellaops/scanner-worker:china
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- scanner-surface-cache:/var/lib/stellaops/surface
|
||||
- ${SURFACE_SECRETS_HOST_PATH:-./offline/surface-secrets}:${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}:ro
|
||||
- ../../etc/appsettings.crypto.china.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "china"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Scheduler Worker - China crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
scheduler-worker:
|
||||
image: registry.stella-ops.org/stellaops/scheduler-worker:china
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.china.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "china"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Notify Web - China crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
notify-web:
|
||||
image: registry.stella-ops.org/stellaops/notify-web:china
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/notify:/app/etc/notify:ro
|
||||
- ../../etc/appsettings.crypto.china.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "china"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Excititor - China crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
excititor:
|
||||
image: registry.stella-ops.org/stellaops/excititor:china
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.china.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "china"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Advisory AI Web - China crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
advisory-ai-web:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-web:china
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/llm-providers:/app/etc/llm-providers:ro
|
||||
- advisory-ai-queue:/var/lib/advisory-ai/queue
|
||||
- advisory-ai-plans:/var/lib/advisory-ai/plans
|
||||
- advisory-ai-outputs:/var/lib/advisory-ai/outputs
|
||||
- ../../etc/appsettings.crypto.china.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "china"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Advisory AI Worker - China crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
advisory-ai-worker:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-worker:china
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/llm-providers:/app/etc/llm-providers:ro
|
||||
- advisory-ai-queue:/var/lib/advisory-ai/queue
|
||||
- advisory-ai-plans:/var/lib/advisory-ai/plans
|
||||
- advisory-ai-outputs:/var/lib/advisory-ai/outputs
|
||||
- ../../etc/appsettings.crypto.china.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "china"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Web UI - China crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
web-ui:
|
||||
image: registry.stella-ops.org/stellaops/web-ui:china
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "china"
|
||||
209
deploy/compose/docker-compose.compliance-eu.yml
Normal file
209
deploy/compose/docker-compose.compliance-eu.yml
Normal file
@@ -0,0 +1,209 @@
|
||||
# =============================================================================
|
||||
# STELLA OPS - COMPLIANCE OVERLAY: EU
|
||||
# =============================================================================
|
||||
# eIDAS qualified trust services crypto overlay.
|
||||
# This file extends docker-compose.stella-ops.yml with EU-specific crypto.
|
||||
#
|
||||
# Usage:
|
||||
# docker compose -f devops/compose/docker-compose.stella-ops.yml \
|
||||
# -f devops/compose/docker-compose.compliance-eu.yml up -d
|
||||
#
|
||||
# Cryptography:
|
||||
# - eIDAS-compliant qualified electronic signatures
|
||||
# - ETSI TS 119 312 compliant algorithms
|
||||
# - Qualified Trust Service Provider (QTSP) integration
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
x-crypto-env: &crypto-env
|
||||
STELLAOPS_CRYPTO_PROFILE: "eu"
|
||||
STELLAOPS_CRYPTO_CONFIG_PATH: "/app/etc/appsettings.crypto.yaml"
|
||||
STELLAOPS_CRYPTO_MANIFEST_PATH: "/app/etc/crypto-plugins-manifest.json"
|
||||
|
||||
x-crypto-volumes: &crypto-volumes
|
||||
- ../../etc/appsettings.crypto.eu.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
|
||||
services:
|
||||
# ---------------------------------------------------------------------------
|
||||
# Authority - EU crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
authority:
|
||||
image: registry.stella-ops.org/stellaops/authority:eu
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/authority:/app/etc/authority:ro
|
||||
- ../../etc/certificates/trust-roots:/etc/ssl/certs/stellaops:ro
|
||||
- ../../etc/appsettings.crypto.eu.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "eu"
|
||||
com.stellaops.compliance: "eidas"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Signer - EU crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
signer:
|
||||
image: registry.stella-ops.org/stellaops/signer:eu
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.eu.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "eu"
|
||||
com.stellaops.compliance: "eidas"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Attestor - EU crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
attestor:
|
||||
image: registry.stella-ops.org/stellaops/attestor:eu
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.eu.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "eu"
|
||||
com.stellaops.compliance: "eidas"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Concelier - EU crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
concelier:
|
||||
image: registry.stella-ops.org/stellaops/concelier:eu
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- concelier-jobs:/var/lib/concelier/jobs
|
||||
- ../../etc/appsettings.crypto.eu.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "eu"
|
||||
com.stellaops.compliance: "eidas"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Scanner Web - EU crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
scanner-web:
|
||||
image: registry.stella-ops.org/stellaops/scanner-web:eu
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/scanner:/app/etc/scanner:ro
|
||||
- ../../etc/certificates/trust-roots:/etc/ssl/certs/stellaops:ro
|
||||
- scanner-surface-cache:/var/lib/stellaops/surface
|
||||
- ${SURFACE_SECRETS_HOST_PATH:-./offline/surface-secrets}:${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}:ro
|
||||
- ${SCANNER_OFFLINEKIT_TRUSTROOTS_HOST_PATH:-./offline/trust-roots}:${SCANNER_OFFLINEKIT_TRUSTROOTDIRECTORY:-/etc/stellaops/trust-roots}:ro
|
||||
- ${SCANNER_OFFLINEKIT_REKOR_SNAPSHOT_HOST_PATH:-./offline/rekor-snapshot}:${SCANNER_OFFLINEKIT_REKORSNAPSHOTDIRECTORY:-/var/lib/stellaops/rekor-snapshot}:ro
|
||||
- ../../etc/appsettings.crypto.eu.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "eu"
|
||||
com.stellaops.compliance: "eidas"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Scanner Worker - EU crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
scanner-worker:
|
||||
image: registry.stella-ops.org/stellaops/scanner-worker:eu
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- scanner-surface-cache:/var/lib/stellaops/surface
|
||||
- ${SURFACE_SECRETS_HOST_PATH:-./offline/surface-secrets}:${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}:ro
|
||||
- ../../etc/appsettings.crypto.eu.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "eu"
|
||||
com.stellaops.compliance: "eidas"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Scheduler Worker - EU crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
scheduler-worker:
|
||||
image: registry.stella-ops.org/stellaops/scheduler-worker:eu
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.eu.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "eu"
|
||||
com.stellaops.compliance: "eidas"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Notify Web - EU crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
notify-web:
|
||||
image: registry.stella-ops.org/stellaops/notify-web:eu
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/notify:/app/etc/notify:ro
|
||||
- ../../etc/appsettings.crypto.eu.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "eu"
|
||||
com.stellaops.compliance: "eidas"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Excititor - EU crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
excititor:
|
||||
image: registry.stella-ops.org/stellaops/excititor:eu
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.eu.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "eu"
|
||||
com.stellaops.compliance: "eidas"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Advisory AI Web - EU crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
advisory-ai-web:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-web:eu
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/llm-providers:/app/etc/llm-providers:ro
|
||||
- advisory-ai-queue:/var/lib/advisory-ai/queue
|
||||
- advisory-ai-plans:/var/lib/advisory-ai/plans
|
||||
- advisory-ai-outputs:/var/lib/advisory-ai/outputs
|
||||
- ../../etc/appsettings.crypto.eu.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "eu"
|
||||
com.stellaops.compliance: "eidas"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Advisory AI Worker - EU crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
advisory-ai-worker:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-worker:eu
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/llm-providers:/app/etc/llm-providers:ro
|
||||
- advisory-ai-queue:/var/lib/advisory-ai/queue
|
||||
- advisory-ai-plans:/var/lib/advisory-ai/plans
|
||||
- advisory-ai-outputs:/var/lib/advisory-ai/outputs
|
||||
- ../../etc/appsettings.crypto.eu.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "eu"
|
||||
com.stellaops.compliance: "eidas"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Web UI - EU crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
web-ui:
|
||||
image: registry.stella-ops.org/stellaops/web-ui:eu
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "eu"
|
||||
com.stellaops.compliance: "eidas"
|
||||
216
deploy/compose/docker-compose.compliance-russia.yml
Normal file
216
deploy/compose/docker-compose.compliance-russia.yml
Normal file
@@ -0,0 +1,216 @@
|
||||
# =============================================================================
|
||||
# STELLA OPS - COMPLIANCE OVERLAY: RUSSIA
|
||||
# =============================================================================
|
||||
# GOST R 34.10-2012, GOST R 34.11-2012 (Streebog) crypto overlay.
|
||||
# This file extends docker-compose.stella-ops.yml with Russia-specific crypto.
|
||||
#
|
||||
# Usage:
|
||||
# docker compose -f devops/compose/docker-compose.stella-ops.yml \
|
||||
# -f devops/compose/docker-compose.compliance-russia.yml up -d
|
||||
#
|
||||
# With CryptoPro CSP:
|
||||
# docker compose -f devops/compose/docker-compose.stella-ops.yml \
|
||||
# -f devops/compose/docker-compose.compliance-russia.yml \
|
||||
# -f devops/compose/docker-compose.cryptopro.yml up -d
|
||||
#
|
||||
# Cryptography:
|
||||
# - GOST R 34.10-2012: Digital signature
|
||||
# - GOST R 34.11-2012: Hash function (Streebog, 256/512-bit)
|
||||
# - GOST R 34.12-2015: Block cipher (Kuznyechik)
|
||||
#
|
||||
# Providers: openssl.gost, pkcs11.gost, cryptopro.gost
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
x-crypto-env: &crypto-env
|
||||
STELLAOPS_CRYPTO_PROFILE: "russia"
|
||||
STELLAOPS_CRYPTO_CONFIG_PATH: "/app/etc/appsettings.crypto.yaml"
|
||||
STELLAOPS_CRYPTO_MANIFEST_PATH: "/app/etc/crypto-plugins-manifest.json"
|
||||
STELLAOPS_CRYPTO_PROVIDERS: "openssl.gost,pkcs11.gost,cryptopro.gost"
|
||||
|
||||
x-crypto-volumes: &crypto-volumes
|
||||
- ../../etc/appsettings.crypto.russia.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
|
||||
services:
|
||||
# ---------------------------------------------------------------------------
|
||||
# Authority - Russia crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
authority:
|
||||
image: registry.stella-ops.org/stellaops/authority:russia
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/authority:/app/etc/authority:ro
|
||||
- ../../etc/certificates/trust-roots:/etc/ssl/certs/stellaops:ro
|
||||
- ../../etc/appsettings.crypto.russia.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "russia"
|
||||
com.stellaops.crypto.provider: "openssl.gost,pkcs11.gost,cryptopro.gost"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Signer - Russia crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
signer:
|
||||
image: registry.stella-ops.org/stellaops/signer:russia
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.russia.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "russia"
|
||||
com.stellaops.crypto.provider: "openssl.gost,pkcs11.gost,cryptopro.gost"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Attestor - Russia crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
attestor:
|
||||
image: registry.stella-ops.org/stellaops/attestor:russia
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.russia.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "russia"
|
||||
com.stellaops.crypto.provider: "openssl.gost,pkcs11.gost,cryptopro.gost"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Concelier - Russia crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
concelier:
|
||||
image: registry.stella-ops.org/stellaops/concelier:russia
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- concelier-jobs:/var/lib/concelier/jobs
|
||||
- ../../etc/appsettings.crypto.russia.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "russia"
|
||||
com.stellaops.crypto.provider: "openssl.gost,pkcs11.gost,cryptopro.gost"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Scanner Web - Russia crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
scanner-web:
|
||||
image: registry.stella-ops.org/stellaops/scanner-web:russia
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/scanner:/app/etc/scanner:ro
|
||||
- ../../etc/certificates/trust-roots:/etc/ssl/certs/stellaops:ro
|
||||
- scanner-surface-cache:/var/lib/stellaops/surface
|
||||
- ${SURFACE_SECRETS_HOST_PATH:-./offline/surface-secrets}:${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}:ro
|
||||
- ${SCANNER_OFFLINEKIT_TRUSTROOTS_HOST_PATH:-./offline/trust-roots}:${SCANNER_OFFLINEKIT_TRUSTROOTDIRECTORY:-/etc/stellaops/trust-roots}:ro
|
||||
- ${SCANNER_OFFLINEKIT_REKOR_SNAPSHOT_HOST_PATH:-./offline/rekor-snapshot}:${SCANNER_OFFLINEKIT_REKORSNAPSHOTDIRECTORY:-/var/lib/stellaops/rekor-snapshot}:ro
|
||||
- ../../etc/appsettings.crypto.russia.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "russia"
|
||||
com.stellaops.crypto.provider: "openssl.gost,pkcs11.gost,cryptopro.gost"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Scanner Worker - Russia crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
scanner-worker:
|
||||
image: registry.stella-ops.org/stellaops/scanner-worker:russia
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- scanner-surface-cache:/var/lib/stellaops/surface
|
||||
- ${SURFACE_SECRETS_HOST_PATH:-./offline/surface-secrets}:${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}:ro
|
||||
- ../../etc/appsettings.crypto.russia.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "russia"
|
||||
com.stellaops.crypto.provider: "openssl.gost,pkcs11.gost,cryptopro.gost"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Scheduler Worker - Russia crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
scheduler-worker:
|
||||
image: registry.stella-ops.org/stellaops/scheduler-worker:russia
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.russia.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "russia"
|
||||
com.stellaops.crypto.provider: "openssl.gost,pkcs11.gost,cryptopro.gost"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Notify Web - Russia crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
notify-web:
|
||||
image: registry.stella-ops.org/stellaops/notify-web:russia
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/notify:/app/etc/notify:ro
|
||||
- ../../etc/appsettings.crypto.russia.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "russia"
|
||||
com.stellaops.crypto.provider: "openssl.gost,pkcs11.gost,cryptopro.gost"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Excititor - Russia crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
excititor:
|
||||
image: registry.stella-ops.org/stellaops/excititor:russia
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/appsettings.crypto.russia.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "russia"
|
||||
com.stellaops.crypto.provider: "openssl.gost,pkcs11.gost,cryptopro.gost"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Advisory AI Web - Russia crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
advisory-ai-web:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-web:russia
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/llm-providers:/app/etc/llm-providers:ro
|
||||
- advisory-ai-queue:/var/lib/advisory-ai/queue
|
||||
- advisory-ai-plans:/var/lib/advisory-ai/plans
|
||||
- advisory-ai-outputs:/var/lib/advisory-ai/outputs
|
||||
- ../../etc/appsettings.crypto.russia.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "russia"
|
||||
com.stellaops.crypto.provider: "openssl.gost,pkcs11.gost,cryptopro.gost"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Advisory AI Worker - Russia crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
advisory-ai-worker:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-worker:russia
|
||||
environment:
|
||||
<<: *crypto-env
|
||||
volumes:
|
||||
- ../../etc/llm-providers:/app/etc/llm-providers:ro
|
||||
- advisory-ai-queue:/var/lib/advisory-ai/queue
|
||||
- advisory-ai-plans:/var/lib/advisory-ai/plans
|
||||
- advisory-ai-outputs:/var/lib/advisory-ai/outputs
|
||||
- ../../etc/appsettings.crypto.russia.yaml:/app/etc/appsettings.crypto.yaml:ro
|
||||
- ../../etc/crypto-plugins-manifest.json:/app/etc/crypto-plugins-manifest.json:ro
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "russia"
|
||||
com.stellaops.crypto.provider: "openssl.gost,pkcs11.gost,cryptopro.gost"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Web UI - Russia crypto overlay
|
||||
# ---------------------------------------------------------------------------
|
||||
web-ui:
|
||||
image: registry.stella-ops.org/stellaops/web-ui:russia
|
||||
labels:
|
||||
com.stellaops.crypto.profile: "russia"
|
||||
@@ -1,13 +1,14 @@
|
||||
# Copyright (c) StellaOps. All rights reserved.
|
||||
# Licensed under BUSL-1.1.
|
||||
|
||||
# Function Behavior Corpus PostgreSQL Database
|
||||
# =============================================================================
|
||||
# CORPUS - FUNCTION BEHAVIOR DATABASE
|
||||
# =============================================================================
|
||||
# PostgreSQL database for function behavior corpus analysis.
|
||||
#
|
||||
# Usage:
|
||||
# docker compose -f docker-compose.corpus.yml up -d
|
||||
#
|
||||
# Environment variables:
|
||||
# Environment:
|
||||
# CORPUS_DB_PASSWORD - PostgreSQL password for corpus database
|
||||
# =============================================================================
|
||||
|
||||
services:
|
||||
corpus-postgres:
|
||||
@@ -20,10 +21,10 @@ services:
|
||||
POSTGRES_INITDB_ARGS: "-E UTF8 --locale=C"
|
||||
volumes:
|
||||
- corpus-data:/var/lib/postgresql/data
|
||||
- ../../../docs/db/schemas/corpus.sql:/docker-entrypoint-initdb.d/10-corpus-schema.sql:ro
|
||||
- ./scripts/init-test-data.sql:/docker-entrypoint-initdb.d/20-test-data.sql:ro
|
||||
- ../../docs/db/schemas/corpus.sql:/docker-entrypoint-initdb.d/10-corpus-schema.sql:ro
|
||||
- ../docker/corpus/scripts/init-test-data.sql:/docker-entrypoint-initdb.d/20-test-data.sql:ro
|
||||
ports:
|
||||
- "5435:5432"
|
||||
- "${CORPUS_DB_PORT:-5435}:5432"
|
||||
networks:
|
||||
- stellaops-corpus
|
||||
healthcheck:
|
||||
@@ -35,9 +36,7 @@ services:
|
||||
|
||||
volumes:
|
||||
corpus-data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
stellaops-corpus:
|
||||
driver: bridge
|
||||
|
||||
119
deploy/compose/docker-compose.crypto-sim.yml
Normal file
119
deploy/compose/docker-compose.crypto-sim.yml
Normal file
@@ -0,0 +1,119 @@
|
||||
# =============================================================================
|
||||
# STELLA OPS - CRYPTO SIMULATION OVERLAY
|
||||
# =============================================================================
|
||||
# Universal crypto simulation service for testing sovereign crypto without
|
||||
# licensed hardware or certified modules.
|
||||
#
|
||||
# This overlay provides the sim-crypto-service which simulates:
|
||||
# - GOST R 34.10-2012 (Russia): GOST12-256, GOST12-512, ru.magma.sim, ru.kuznyechik.sim
|
||||
# - SM2/SM3/SM4 (China): SM2, sm.sim, sm2.sim
|
||||
# - Post-Quantum: DILITHIUM3, FALCON512, pq.sim
|
||||
# - FIPS/eIDAS/KCMVP: fips.sim, eidas.sim, kcmvp.sim, world.sim
|
||||
#
|
||||
# Usage with China compliance:
|
||||
# docker compose -f docker-compose.stella-ops.yml \
|
||||
# -f docker-compose.compliance-china.yml \
|
||||
# -f docker-compose.crypto-sim.yml up -d
|
||||
#
|
||||
# Usage with Russia compliance:
|
||||
# docker compose -f docker-compose.stella-ops.yml \
|
||||
# -f docker-compose.compliance-russia.yml \
|
||||
# -f docker-compose.crypto-sim.yml up -d
|
||||
#
|
||||
# Usage with EU compliance:
|
||||
# docker compose -f docker-compose.stella-ops.yml \
|
||||
# -f docker-compose.compliance-eu.yml \
|
||||
# -f docker-compose.crypto-sim.yml up -d
|
||||
#
|
||||
# IMPORTANT: This is for TESTING/DEVELOPMENT ONLY.
|
||||
# - Uses deterministic HMAC-SHA256 for SM/GOST/PQ (not real algorithms)
|
||||
# - Uses static ECDSA P-256 key for FIPS/eIDAS/KCMVP
|
||||
# - NOT suitable for production or compliance certification
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
x-crypto-sim-labels: &crypto-sim-labels
|
||||
com.stellaops.component: "crypto-sim"
|
||||
com.stellaops.profile: "simulation"
|
||||
com.stellaops.production: "false"
|
||||
|
||||
x-sim-crypto-env: &sim-crypto-env
|
||||
STELLAOPS_CRYPTO_ENABLE_SIM: "1"
|
||||
STELLAOPS_CRYPTO_SIM_URL: "http://sim-crypto:8080"
|
||||
|
||||
networks:
|
||||
stellaops:
|
||||
external: true
|
||||
name: stellaops
|
||||
|
||||
services:
|
||||
# ---------------------------------------------------------------------------
|
||||
# Sim Crypto Service - Universal sovereign crypto simulator
|
||||
# ---------------------------------------------------------------------------
|
||||
sim-crypto:
|
||||
build:
|
||||
context: ../services/crypto/sim-crypto-service
|
||||
dockerfile: Dockerfile
|
||||
image: registry.stella-ops.org/stellaops/sim-crypto:dev
|
||||
container_name: stellaops-sim-crypto
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
ASPNETCORE_URLS: "http://0.0.0.0:8080"
|
||||
ASPNETCORE_ENVIRONMENT: "Development"
|
||||
ports:
|
||||
- "${SIM_CRYPTO_PORT:-18090}:8080"
|
||||
networks:
|
||||
- stellaops
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/keys"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
labels: *crypto-sim-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Override services to use sim-crypto
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Authority - Enable sim crypto
|
||||
authority:
|
||||
environment:
|
||||
<<: *sim-crypto-env
|
||||
labels:
|
||||
com.stellaops.crypto.simulator: "enabled"
|
||||
|
||||
# Signer - Enable sim crypto
|
||||
signer:
|
||||
environment:
|
||||
<<: *sim-crypto-env
|
||||
labels:
|
||||
com.stellaops.crypto.simulator: "enabled"
|
||||
|
||||
# Attestor - Enable sim crypto
|
||||
attestor:
|
||||
environment:
|
||||
<<: *sim-crypto-env
|
||||
labels:
|
||||
com.stellaops.crypto.simulator: "enabled"
|
||||
|
||||
# Scanner Web - Enable sim crypto
|
||||
scanner-web:
|
||||
environment:
|
||||
<<: *sim-crypto-env
|
||||
labels:
|
||||
com.stellaops.crypto.simulator: "enabled"
|
||||
|
||||
# Scanner Worker - Enable sim crypto
|
||||
scanner-worker:
|
||||
environment:
|
||||
<<: *sim-crypto-env
|
||||
labels:
|
||||
com.stellaops.crypto.simulator: "enabled"
|
||||
|
||||
# Excititor - Enable sim crypto
|
||||
excititor:
|
||||
environment:
|
||||
<<: *sim-crypto-env
|
||||
labels:
|
||||
com.stellaops.crypto.simulator: "enabled"
|
||||
149
deploy/compose/docker-compose.cryptopro.yml
Normal file
149
deploy/compose/docker-compose.cryptopro.yml
Normal file
@@ -0,0 +1,149 @@
|
||||
# =============================================================================
|
||||
# STELLA OPS - CRYPTOPRO CSP OVERLAY (Russia)
|
||||
# =============================================================================
|
||||
# CryptoPro CSP licensed provider overlay for compliance-russia.yml.
|
||||
# Adds real CryptoPro CSP service for certified GOST R 34.10-2012 operations.
|
||||
#
|
||||
# IMPORTANT: Requires EULA acceptance before use.
|
||||
#
|
||||
# Usage (MUST be combined with stella-ops AND compliance-russia):
|
||||
# CRYPTOPRO_ACCEPT_EULA=1 docker compose \
|
||||
# -f docker-compose.stella-ops.yml \
|
||||
# -f docker-compose.compliance-russia.yml \
|
||||
# -f docker-compose.cryptopro.yml up -d
|
||||
#
|
||||
# For development/testing without CryptoPro license, use crypto-sim.yml instead:
|
||||
# docker compose \
|
||||
# -f docker-compose.stella-ops.yml \
|
||||
# -f docker-compose.compliance-russia.yml \
|
||||
# -f docker-compose.crypto-sim.yml up -d
|
||||
#
|
||||
# Requirements:
|
||||
# - CryptoPro CSP license files in opt/cryptopro/downloads/
|
||||
# - CRYPTOPRO_ACCEPT_EULA=1 environment variable
|
||||
# - CryptoPro container images with GOST engine
|
||||
#
|
||||
# GOST Algorithms Provided:
|
||||
# - GOST R 34.10-2012: Digital signature (256/512-bit)
|
||||
# - GOST R 34.11-2012: Hash function (Streebog, 256/512-bit)
|
||||
# - GOST R 34.12-2015: Block cipher (Kuznyechik, Magma)
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
x-cryptopro-labels: &cryptopro-labels
|
||||
com.stellaops.component: "cryptopro-csp"
|
||||
com.stellaops.crypto.provider: "cryptopro"
|
||||
com.stellaops.crypto.profile: "russia"
|
||||
com.stellaops.crypto.certified: "true"
|
||||
|
||||
x-cryptopro-env: &cryptopro-env
|
||||
STELLAOPS_CRYPTO_PROVIDERS: "cryptopro.gost"
|
||||
STELLAOPS_CRYPTO_CRYPTOPRO_URL: "http://cryptopro-csp:8080"
|
||||
STELLAOPS_CRYPTO_CRYPTOPRO_ENABLED: "true"
|
||||
|
||||
networks:
|
||||
stellaops:
|
||||
external: true
|
||||
name: stellaops
|
||||
|
||||
services:
|
||||
# ---------------------------------------------------------------------------
|
||||
# CryptoPro CSP - Certified GOST cryptography provider
|
||||
# ---------------------------------------------------------------------------
|
||||
cryptopro-csp:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: devops/services/cryptopro/linux-csp-service/Dockerfile
|
||||
args:
|
||||
CRYPTOPRO_ACCEPT_EULA: "${CRYPTOPRO_ACCEPT_EULA:-0}"
|
||||
image: registry.stella-ops.org/stellaops/cryptopro-csp:2025.10.0
|
||||
container_name: stellaops-cryptopro-csp
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
ASPNETCORE_URLS: "http://0.0.0.0:8080"
|
||||
CRYPTOPRO_ACCEPT_EULA: "${CRYPTOPRO_ACCEPT_EULA:-0}"
|
||||
# GOST algorithm configuration
|
||||
CRYPTOPRO_GOST_SIGNATURE_ALGORITHM: "GOST R 34.10-2012"
|
||||
CRYPTOPRO_GOST_HASH_ALGORITHM: "GOST R 34.11-2012"
|
||||
# Container and key store settings
|
||||
CRYPTOPRO_CONTAINER_NAME: "${CRYPTOPRO_CONTAINER_NAME:-stellaops-signing}"
|
||||
CRYPTOPRO_USE_MACHINE_STORE: "${CRYPTOPRO_USE_MACHINE_STORE:-true}"
|
||||
CRYPTOPRO_PROVIDER_TYPE: "${CRYPTOPRO_PROVIDER_TYPE:-80}"
|
||||
volumes:
|
||||
- ../../opt/cryptopro/downloads:/opt/cryptopro/downloads:ro
|
||||
- ../../etc/cryptopro:/app/etc/cryptopro:ro
|
||||
# Optional: Mount key containers
|
||||
- cryptopro-keys:/var/opt/cprocsp/keys
|
||||
ports:
|
||||
- "${CRYPTOPRO_PORT:-18080}:8080"
|
||||
networks:
|
||||
- stellaops
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
labels: *cryptopro-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Override services to use CryptoPro
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Authority - Use CryptoPro for GOST signatures
|
||||
authority:
|
||||
environment:
|
||||
<<: *cryptopro-env
|
||||
depends_on:
|
||||
- cryptopro-csp
|
||||
labels:
|
||||
com.stellaops.crypto.provider: "cryptopro"
|
||||
|
||||
# Signer - Use CryptoPro for GOST signatures
|
||||
signer:
|
||||
environment:
|
||||
<<: *cryptopro-env
|
||||
depends_on:
|
||||
- cryptopro-csp
|
||||
labels:
|
||||
com.stellaops.crypto.provider: "cryptopro"
|
||||
|
||||
# Attestor - Use CryptoPro for GOST signatures
|
||||
attestor:
|
||||
environment:
|
||||
<<: *cryptopro-env
|
||||
depends_on:
|
||||
- cryptopro-csp
|
||||
labels:
|
||||
com.stellaops.crypto.provider: "cryptopro"
|
||||
|
||||
# Scanner Web - Use CryptoPro for verification
|
||||
scanner-web:
|
||||
environment:
|
||||
<<: *cryptopro-env
|
||||
depends_on:
|
||||
- cryptopro-csp
|
||||
labels:
|
||||
com.stellaops.crypto.provider: "cryptopro"
|
||||
|
||||
# Scanner Worker - Use CryptoPro for verification
|
||||
scanner-worker:
|
||||
environment:
|
||||
<<: *cryptopro-env
|
||||
depends_on:
|
||||
- cryptopro-csp
|
||||
labels:
|
||||
com.stellaops.crypto.provider: "cryptopro"
|
||||
|
||||
# Excititor - Use CryptoPro for VEX signing
|
||||
excititor:
|
||||
environment:
|
||||
<<: *cryptopro-env
|
||||
depends_on:
|
||||
- cryptopro-csp
|
||||
labels:
|
||||
com.stellaops.crypto.provider: "cryptopro"
|
||||
|
||||
volumes:
|
||||
cryptopro-keys:
|
||||
name: stellaops-cryptopro-keys
|
||||
73
deploy/compose/docker-compose.dev.yml
Normal file
73
deploy/compose/docker-compose.dev.yml
Normal file
@@ -0,0 +1,73 @@
|
||||
# =============================================================================
|
||||
# DEVELOPMENT STACK - MINIMAL LOCAL DEVELOPMENT
|
||||
# =============================================================================
|
||||
# Minimal infrastructure for local development. Use this when you only need
|
||||
# the core infrastructure without all application services.
|
||||
#
|
||||
# For full platform, use docker-compose.stella-ops.yml instead.
|
||||
#
|
||||
# Usage:
|
||||
# docker compose -f docker-compose.dev.yml up -d
|
||||
#
|
||||
# This provides:
|
||||
# - PostgreSQL 18.1 on port 5432
|
||||
# - Valkey 9.0.1 on port 6379
|
||||
# - RustFS on port 8080
|
||||
# =============================================================================
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:18.1-alpine
|
||||
container_name: stellaops-dev-postgres
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER:-stellaops}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-stellaops}
|
||||
POSTGRES_DB: ${POSTGRES_DB:-stellaops_dev}
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "${POSTGRES_PORT:-5432}:5432"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-stellaops}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
valkey:
|
||||
image: valkey/valkey:9.0.1-alpine
|
||||
container_name: stellaops-dev-valkey
|
||||
restart: unless-stopped
|
||||
command: ["valkey-server", "--appendonly", "yes"]
|
||||
volumes:
|
||||
- valkey-data:/data
|
||||
ports:
|
||||
- "${VALKEY_PORT:-6379}:6379"
|
||||
healthcheck:
|
||||
test: ["CMD", "valkey-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
rustfs:
|
||||
image: registry.stella-ops.org/stellaops/rustfs:2025.09.2
|
||||
container_name: stellaops-dev-rustfs
|
||||
restart: unless-stopped
|
||||
command: ["serve", "--listen", "0.0.0.0:8080", "--root", "/data"]
|
||||
environment:
|
||||
RUSTFS__LOG__LEVEL: info
|
||||
RUSTFS__STORAGE__PATH: /data
|
||||
volumes:
|
||||
- rustfs-data:/data
|
||||
ports:
|
||||
- "${RUSTFS_PORT:-8080}:8080"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
valkey-data:
|
||||
rustfs-data:
|
||||
40
deploy/compose/docker-compose.gpu.yaml
Normal file
40
deploy/compose/docker-compose.gpu.yaml
Normal file
@@ -0,0 +1,40 @@
|
||||
# =============================================================================
|
||||
# STELLA OPS GPU OVERLAY
|
||||
# =============================================================================
|
||||
# Enables NVIDIA GPU acceleration for Advisory AI inference services.
|
||||
#
|
||||
# Prerequisites:
|
||||
# - NVIDIA GPU with CUDA support
|
||||
# - nvidia-container-toolkit installed
|
||||
# - Docker configured with nvidia runtime
|
||||
#
|
||||
# Usage:
|
||||
# docker compose -f docker-compose.stella-ops.yml \
|
||||
# -f docker-compose.gpu.yaml up -d
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
services:
|
||||
advisory-ai-worker:
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- capabilities: [gpu]
|
||||
driver: nvidia
|
||||
count: 1
|
||||
environment:
|
||||
ADVISORY_AI_INFERENCE_GPU: "true"
|
||||
runtime: nvidia
|
||||
|
||||
advisory-ai-web:
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- capabilities: [gpu]
|
||||
driver: nvidia
|
||||
count: 1
|
||||
environment:
|
||||
ADVISORY_AI_INFERENCE_GPU: "true"
|
||||
runtime: nvidia
|
||||
121
deploy/compose/docker-compose.sealed-ci.yml
Normal file
121
deploy/compose/docker-compose.sealed-ci.yml
Normal file
@@ -0,0 +1,121 @@
|
||||
# =============================================================================
|
||||
# SEALED CI - AIR-GAPPED TESTING ENVIRONMENT
|
||||
# =============================================================================
|
||||
# Sealed/air-gapped CI environment for testing offline functionality.
|
||||
# All services run in isolated network with no external egress.
|
||||
#
|
||||
# Usage:
|
||||
# docker compose -f docker-compose.sealed-ci.yml up -d
|
||||
# =============================================================================
|
||||
|
||||
x-release-labels: &release-labels
|
||||
com.stellaops.profile: 'sealed-ci'
|
||||
com.stellaops.airgap.mode: 'sealed'
|
||||
|
||||
networks:
|
||||
sealed-ci:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
sealed-postgres-data:
|
||||
sealed-valkey-data:
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: docker.io/library/postgres@sha256:8e97b8526ed19304b144f7478bc9201646acf0723cdc6e4b19bc9eb34879a27e
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: sealedci
|
||||
POSTGRES_PASSWORD: sealedci-secret
|
||||
POSTGRES_DB: stellaops
|
||||
volumes:
|
||||
- sealed-postgres-data:/var/lib/postgresql/data
|
||||
networks:
|
||||
- sealed-ci
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U sealedci -d stellaops"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
labels: *release-labels
|
||||
|
||||
valkey:
|
||||
image: docker.io/valkey/valkey:9.0.1-alpine
|
||||
restart: unless-stopped
|
||||
command: ["valkey-server", "--appendonly", "yes"]
|
||||
volumes:
|
||||
- sealed-valkey-data:/data
|
||||
networks:
|
||||
- sealed-ci
|
||||
healthcheck:
|
||||
test: ["CMD", "valkey-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
labels: *release-labels
|
||||
|
||||
authority:
|
||||
image: registry.stella-ops.org/stellaops/authority@sha256:a8e8faec44a579aa5714e58be835f25575710430b1ad2ccd1282a018cd9ffcdd
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
valkey:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
ASPNETCORE_URLS: http://+:5088
|
||||
STELLAOPS_AUTHORITY__ISSUER: http://authority.sealed-ci.local
|
||||
STELLAOPS_AUTHORITY__STORAGE__DRIVER: postgres
|
||||
STELLAOPS_AUTHORITY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=authority;Username=sealedci;Password=sealedci-secret"
|
||||
STELLAOPS_AUTHORITY__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: /app/plugins
|
||||
STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: /app/plugins
|
||||
STELLAOPS_AUTHORITY__SECURITY__SENDERCONSTRAINTS__DPOP__ENABLED: 'true'
|
||||
STELLAOPS_AUTHORITY__SECURITY__SENDERCONSTRAINTS__MTLS__ENABLED: 'true'
|
||||
STELLAOPS_AUTHORITY__AIRGAP__EGRESS__MODE: Sealed
|
||||
volumes:
|
||||
- ../services/sealed-mode-ci/authority.harness.yaml:/etc/authority.yaml:ro
|
||||
- ../services/sealed-mode-ci/plugins:/app/plugins:ro
|
||||
- ../../certificates:/certificates:ro
|
||||
ports:
|
||||
- '5088:5088'
|
||||
networks:
|
||||
- sealed-ci
|
||||
labels: *release-labels
|
||||
|
||||
signer:
|
||||
image: registry.stella-ops.org/stellaops/signer@sha256:8bfef9a75783883d49fc18e3566553934e970b00ee090abee9cb110d2d5c3298
|
||||
depends_on:
|
||||
- authority
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
ASPNETCORE_URLS: http://+:6088
|
||||
SIGNER__AUTHORITY__BASEURL: http://authority:5088
|
||||
SIGNER__POE__INTROSPECTURL: http://authority:5088/device-code
|
||||
SIGNER__STORAGE__DRIVER: postgres
|
||||
SIGNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=signer;Username=sealedci;Password=sealedci-secret"
|
||||
SIGNER__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
SIGNER__SEALED__MODE: Enabled
|
||||
ports:
|
||||
- '6088:6088'
|
||||
networks:
|
||||
- sealed-ci
|
||||
labels: *release-labels
|
||||
|
||||
attestor:
|
||||
image: registry.stella-ops.org/stellaops/attestor@sha256:5cc417948c029da01dccf36e4645d961a3f6d8de7e62fe98d845f07cd2282114
|
||||
depends_on:
|
||||
- signer
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
ASPNETCORE_URLS: http://+:7088
|
||||
ATTESTOR__SIGNER__BASEURL: http://signer:6088
|
||||
ATTESTOR__STORAGE__DRIVER: postgres
|
||||
ATTESTOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=attestor;Username=sealedci;Password=sealedci-secret"
|
||||
ATTESTOR__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
ATTESTOR__SEALED__MODE: Enabled
|
||||
ports:
|
||||
- '7088:7088'
|
||||
networks:
|
||||
- sealed-ci
|
||||
labels: *release-labels
|
||||
153
deploy/compose/docker-compose.sm-remote.yml
Normal file
153
deploy/compose/docker-compose.sm-remote.yml
Normal file
@@ -0,0 +1,153 @@
|
||||
# =============================================================================
|
||||
# STELLA OPS - SM REMOTE OVERLAY (China)
|
||||
# =============================================================================
|
||||
# SM Remote service overlay for compliance-china.yml.
|
||||
# Provides SM2/SM3/SM4 (ShangMi) cryptographic operations via software provider
|
||||
# or integration with OSCCA-certified hardware security modules.
|
||||
#
|
||||
# Usage (MUST be combined with stella-ops AND compliance-china):
|
||||
# docker compose \
|
||||
# -f docker-compose.stella-ops.yml \
|
||||
# -f docker-compose.compliance-china.yml \
|
||||
# -f docker-compose.sm-remote.yml up -d
|
||||
#
|
||||
# For development/testing without SM hardware, use crypto-sim.yml instead:
|
||||
# docker compose \
|
||||
# -f docker-compose.stella-ops.yml \
|
||||
# -f docker-compose.compliance-china.yml \
|
||||
# -f docker-compose.crypto-sim.yml up -d
|
||||
#
|
||||
# SM Algorithms Provided:
|
||||
# - SM2: Public key cryptography (ECDSA-like, 256-bit curve) - GM/T 0003-2012
|
||||
# - SM3: Cryptographic hash function (256-bit output) - GM/T 0004-2012
|
||||
# - SM4: Block cipher (128-bit key/block, AES-like) - GM/T 0002-2012
|
||||
# - SM9: Identity-based cryptography - GM/T 0044-2016
|
||||
#
|
||||
# Providers:
|
||||
# - cn.sm.soft: Software-only implementation using BouncyCastle
|
||||
# - cn.sm.remote.http: Remote HSM integration via HTTP API
|
||||
#
|
||||
# OSCCA Compliance:
|
||||
# - All cryptographic operations use SM algorithms exclusively
|
||||
# - Hardware Security Modules should be OSCCA-certified
|
||||
# - Certificates comply with GM/T 0015 (Certificate Profile)
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
x-sm-remote-labels: &sm-remote-labels
|
||||
com.stellaops.component: "sm-remote"
|
||||
com.stellaops.crypto.provider: "sm"
|
||||
com.stellaops.crypto.profile: "china"
|
||||
com.stellaops.crypto.jurisdiction: "china"
|
||||
|
||||
x-sm-remote-env: &sm-remote-env
|
||||
STELLAOPS_CRYPTO_PROVIDERS: "cn.sm.soft,cn.sm.remote.http"
|
||||
STELLAOPS_CRYPTO_SM_REMOTE_URL: "http://sm-remote:56080"
|
||||
STELLAOPS_CRYPTO_SM_ENABLED: "true"
|
||||
SM_SOFT_ALLOWED: "1"
|
||||
|
||||
networks:
|
||||
stellaops:
|
||||
external: true
|
||||
name: stellaops
|
||||
|
||||
services:
|
||||
# ---------------------------------------------------------------------------
|
||||
# SM Remote Service - ShangMi cryptography provider
|
||||
# ---------------------------------------------------------------------------
|
||||
sm-remote:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: devops/services/sm-remote/Dockerfile
|
||||
image: registry.stella-ops.org/stellaops/sm-remote:2025.10.0
|
||||
container_name: stellaops-sm-remote
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
ASPNETCORE_URLS: "http://0.0.0.0:56080"
|
||||
ASPNETCORE_ENVIRONMENT: "Production"
|
||||
# Enable software-only SM2 provider (for testing/development)
|
||||
SM_SOFT_ALLOWED: "${SM_SOFT_ALLOWED:-1}"
|
||||
# Optional: Remote HSM configuration (for production with OSCCA-certified HSM)
|
||||
SM_REMOTE_HSM_URL: "${SM_REMOTE_HSM_URL:-}"
|
||||
SM_REMOTE_HSM_API_KEY: "${SM_REMOTE_HSM_API_KEY:-}"
|
||||
SM_REMOTE_HSM_TIMEOUT: "${SM_REMOTE_HSM_TIMEOUT:-30000}"
|
||||
# Optional: Client certificate authentication for HSM
|
||||
SM_REMOTE_CLIENT_CERT_PATH: "${SM_REMOTE_CLIENT_CERT_PATH:-}"
|
||||
SM_REMOTE_CLIENT_CERT_PASSWORD: "${SM_REMOTE_CLIENT_CERT_PASSWORD:-}"
|
||||
volumes:
|
||||
- ../../etc/sm-remote:/app/etc/sm-remote:ro
|
||||
# Optional: Mount SM key containers
|
||||
- sm-remote-keys:/var/lib/stellaops/sm-keys
|
||||
ports:
|
||||
- "${SM_REMOTE_PORT:-56080}:56080"
|
||||
networks:
|
||||
- stellaops
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:56080/status"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 15s
|
||||
labels: *sm-remote-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Override services to use SM Remote
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Authority - Use SM Remote for SM2 signatures
|
||||
authority:
|
||||
environment:
|
||||
<<: *sm-remote-env
|
||||
depends_on:
|
||||
- sm-remote
|
||||
labels:
|
||||
com.stellaops.crypto.provider: "sm"
|
||||
|
||||
# Signer - Use SM Remote for SM2 signatures
|
||||
signer:
|
||||
environment:
|
||||
<<: *sm-remote-env
|
||||
depends_on:
|
||||
- sm-remote
|
||||
labels:
|
||||
com.stellaops.crypto.provider: "sm"
|
||||
|
||||
# Attestor - Use SM Remote for SM2 signatures
|
||||
attestor:
|
||||
environment:
|
||||
<<: *sm-remote-env
|
||||
depends_on:
|
||||
- sm-remote
|
||||
labels:
|
||||
com.stellaops.crypto.provider: "sm"
|
||||
|
||||
# Scanner Web - Use SM Remote for verification
|
||||
scanner-web:
|
||||
environment:
|
||||
<<: *sm-remote-env
|
||||
depends_on:
|
||||
- sm-remote
|
||||
labels:
|
||||
com.stellaops.crypto.provider: "sm"
|
||||
|
||||
# Scanner Worker - Use SM Remote for verification
|
||||
scanner-worker:
|
||||
environment:
|
||||
<<: *sm-remote-env
|
||||
depends_on:
|
||||
- sm-remote
|
||||
labels:
|
||||
com.stellaops.crypto.provider: "sm"
|
||||
|
||||
# Excititor - Use SM Remote for VEX signing
|
||||
excititor:
|
||||
environment:
|
||||
<<: *sm-remote-env
|
||||
depends_on:
|
||||
- sm-remote
|
||||
labels:
|
||||
com.stellaops.crypto.provider: "sm"
|
||||
|
||||
volumes:
|
||||
sm-remote-keys:
|
||||
name: stellaops-sm-remote-keys
|
||||
@@ -1,54 +1,148 @@
|
||||
x-release-labels: &release-labels
|
||||
com.stellaops.release.version: "2025.09.2"
|
||||
com.stellaops.release.channel: "stable"
|
||||
com.stellaops.profile: "prod"
|
||||
|
||||
networks:
|
||||
stellaops:
|
||||
driver: bridge
|
||||
frontdoor:
|
||||
external: true
|
||||
name: ${FRONTDOOR_NETWORK:-stellaops_frontdoor}
|
||||
|
||||
volumes:
|
||||
valkey-data:
|
||||
rustfs-data:
|
||||
concelier-jobs:
|
||||
nats-data:
|
||||
scanner-surface-cache:
|
||||
postgres-data:
|
||||
advisory-ai-queue:
|
||||
advisory-ai-plans:
|
||||
advisory-ai-outputs:
|
||||
|
||||
services:
|
||||
valkey:
|
||||
image: docker.io/valkey/valkey:9.0.1
|
||||
restart: unless-stopped
|
||||
command: ["valkey-server", "--appendonly", "yes"]
|
||||
volumes:
|
||||
- valkey-data:/data
|
||||
ports:
|
||||
- "${VALKEY_PORT:-6379}:6379"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
# =============================================================================
|
||||
# STELLA OPS - MAIN STACK
|
||||
# =============================================================================
|
||||
# Consolidated Docker Compose for the complete StellaOps platform.
|
||||
# Infrastructure: PostgreSQL 18.1, Valkey 9.0.1, RustFS, Rekor v2
|
||||
#
|
||||
# Usage:
|
||||
# docker compose -f devops/compose/docker-compose.stella-ops.yml up -d
|
||||
#
|
||||
# With Sigstore tools:
|
||||
# docker compose -f devops/compose/docker-compose.stella-ops.yml --profile sigstore up -d
|
||||
#
|
||||
# With Telemetry:
|
||||
# docker compose -f devops/compose/docker-compose.stella-ops.yml \
|
||||
# -f devops/compose/docker-compose.telemetry.yml up -d
|
||||
#
|
||||
# With Compliance overlay (e.g., China):
|
||||
# docker compose -f devops/compose/docker-compose.stella-ops.yml \
|
||||
# -f devops/compose/docker-compose.compliance-china.yml up -d
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
x-release-labels: &release-labels
|
||||
com.stellaops.release.version: "2025.10.0"
|
||||
com.stellaops.release.channel: "stable"
|
||||
com.stellaops.profile: "default"
|
||||
|
||||
x-postgres-connection: &postgres-connection
|
||||
"Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
|
||||
networks:
|
||||
stellaops:
|
||||
driver: bridge
|
||||
name: stellaops
|
||||
frontdoor:
|
||||
external: true
|
||||
name: ${FRONTDOOR_NETWORK:-stellaops_frontdoor}
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
valkey-data:
|
||||
rustfs-data:
|
||||
rekor-tiles-data:
|
||||
concelier-jobs:
|
||||
scanner-surface-cache:
|
||||
advisory-ai-queue:
|
||||
advisory-ai-plans:
|
||||
advisory-ai-outputs:
|
||||
|
||||
services:
|
||||
# ===========================================================================
|
||||
# INFRASTRUCTURE SERVICES
|
||||
# ===========================================================================
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# PostgreSQL 18.1 - Primary database
|
||||
# ---------------------------------------------------------------------------
|
||||
postgres:
|
||||
image: docker.io/library/postgres:18.1
|
||||
container_name: stellaops-postgres
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: "${POSTGRES_USER:-stellaops}"
|
||||
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-stellaops}"
|
||||
POSTGRES_DB: "${POSTGRES_DB:-stellaops_platform}"
|
||||
PGDATA: /var/lib/postgresql/data/pgdata
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
- ./postgres-init:/docker-entrypoint-initdb.d:ro
|
||||
ports:
|
||||
- "${POSTGRES_PORT:-5432}:5432"
|
||||
networks:
|
||||
- stellaops
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-stellaops} -d ${POSTGRES_DB:-stellaops_platform}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
labels: *release-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Valkey 9.0.1 - Cache and message queue (Redis-compatible)
|
||||
# ---------------------------------------------------------------------------
|
||||
valkey:
|
||||
image: docker.io/valkey/valkey:9.0.1
|
||||
container_name: stellaops-valkey
|
||||
restart: unless-stopped
|
||||
command: ["valkey-server", "--appendonly", "yes"]
|
||||
volumes:
|
||||
- valkey-data:/data
|
||||
ports:
|
||||
- "${VALKEY_PORT:-6379}:6379"
|
||||
networks:
|
||||
- stellaops
|
||||
healthcheck:
|
||||
test: ["CMD", "valkey-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
labels: *release-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# RustFS - S3-compatible object storage
|
||||
# ---------------------------------------------------------------------------
|
||||
rustfs:
|
||||
image: registry.stella-ops.org/stellaops/rustfs:2025.09.2
|
||||
container_name: stellaops-rustfs
|
||||
command: ["serve", "--listen", "0.0.0.0:8080", "--root", "/data"]
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
RUSTFS__LOG__LEVEL: info
|
||||
RUSTFS__STORAGE__PATH: /data
|
||||
volumes:
|
||||
- rustfs-data:/data
|
||||
environment:
|
||||
RUSTFS__LOG__LEVEL: info
|
||||
RUSTFS__STORAGE__PATH: /data
|
||||
volumes:
|
||||
- rustfs-data:/data
|
||||
ports:
|
||||
- "${RUSTFS_HTTP_PORT:-8080}:8080"
|
||||
networks:
|
||||
- stellaops
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
labels: *release-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Rekor v2 (tiles) - Sigstore transparency log
|
||||
# ---------------------------------------------------------------------------
|
||||
rekor-v2:
|
||||
image: ${REKOR_TILES_IMAGE:-ghcr.io/sigstore/rekor-tiles:latest}
|
||||
container_name: stellaops-rekor
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- rekor-tiles-data:/var/lib/rekor-tiles
|
||||
networks:
|
||||
- stellaops
|
||||
profiles: ["sigstore"]
|
||||
labels:
|
||||
<<: *release-labels
|
||||
com.stellaops.component: "rekor-v2"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Sigstore CLI tools (on-demand)
|
||||
# ---------------------------------------------------------------------------
|
||||
rekor-cli:
|
||||
image: ghcr.io/sigstore/rekor-cli:v1.4.3
|
||||
entrypoint: ["rekor-cli"]
|
||||
@@ -67,334 +161,378 @@ services:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
nats:
|
||||
image: docker.io/library/nats@sha256:c82559e4476289481a8a5196e675ebfe67eea81d95e5161e3e78eccfe766608e
|
||||
command:
|
||||
- "-js"
|
||||
- "-sd"
|
||||
- /data
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${NATS_CLIENT_PORT:-4222}:4222"
|
||||
volumes:
|
||||
- nats-data:/data
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
authority:
|
||||
image: registry.stella-ops.org/stellaops/authority@sha256:b0348bad1d0b401cc3c71cb40ba034c8043b6c8874546f90d4783c9dbfcc0bf5
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- valkey
|
||||
environment:
|
||||
STELLAOPS_AUTHORITY__ISSUER: "${AUTHORITY_ISSUER}"
|
||||
STELLAOPS_AUTHORITY__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_AUTHORITY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
STELLAOPS_AUTHORITY__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: "/app/plugins"
|
||||
STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: "/app/etc/authority.plugins"
|
||||
volumes:
|
||||
- ../../etc/authority.yaml:/etc/authority.yaml:ro
|
||||
- ../../etc/authority.plugins:/app/etc/authority.plugins:ro
|
||||
ports:
|
||||
- "${AUTHORITY_PORT:-8440}:8440"
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
|
||||
signer:
|
||||
image: registry.stella-ops.org/stellaops/signer@sha256:8ad574e61f3a9e9bda8a58eb2700ae46813284e35a150b1137bc7c2b92ac0f2e
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- authority
|
||||
environment:
|
||||
SIGNER__AUTHORITY__BASEURL: "https://authority:8440"
|
||||
SIGNER__POE__INTROSPECTURL: "${SIGNER_POE_INTROSPECT_URL}"
|
||||
SIGNER__STORAGE__DRIVER: "postgres"
|
||||
SIGNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
ports:
|
||||
- "${SIGNER_PORT:-8441}:8441"
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
|
||||
attestor:
|
||||
image: registry.stella-ops.org/stellaops/attestor@sha256:0534985f978b0b5d220d73c96fddd962cd9135f616811cbe3bff4666c5af568f
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- signer
|
||||
- postgres
|
||||
environment:
|
||||
ATTESTOR__SIGNER__BASEURL: "https://signer:8441"
|
||||
ATTESTOR__STORAGE__DRIVER: "postgres"
|
||||
ATTESTOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
ports:
|
||||
- "${ATTESTOR_PORT:-8442}:8442"
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
|
||||
postgres:
|
||||
image: docker.io/library/postgres:18.1
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: "${POSTGRES_USER:-stellaops}"
|
||||
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-stellaops}"
|
||||
POSTGRES_DB: "${POSTGRES_DB:-stellaops_platform}"
|
||||
PGDATA: /var/lib/postgresql/data/pgdata
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "${POSTGRES_PORT:-5432}:5432"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
issuer-directory:
|
||||
image: registry.stella-ops.org/stellaops/issuer-directory-web:2025.10.0-edge
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- authority
|
||||
environment:
|
||||
ISSUERDIRECTORY__CONFIG: "/etc/issuer-directory.yaml"
|
||||
ISSUERDIRECTORY__AUTHORITY__ISSUER: "${AUTHORITY_ISSUER}"
|
||||
ISSUERDIRECTORY__AUTHORITY__BASEURL: "https://authority:8440"
|
||||
ISSUERDIRECTORY__STORAGE__DRIVER: "postgres"
|
||||
ISSUERDIRECTORY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
ISSUERDIRECTORY__SEEDCSAFPUBLISHERS: "${ISSUER_DIRECTORY_SEED_CSAF:-true}"
|
||||
volumes:
|
||||
- ../../etc/issuer-directory.yaml:/etc/issuer-directory.yaml:ro
|
||||
ports:
|
||||
- "${ISSUER_DIRECTORY_PORT:-8447}:8080"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
concelier:
|
||||
image: registry.stella-ops.org/stellaops/concelier@sha256:c58cdcaee1d266d68d498e41110a589dd204b487d37381096bd61ab345a867c5
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- valkey
|
||||
environment:
|
||||
CONCELIER__STORAGE__DRIVER: "postgres"
|
||||
CONCELIER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
CONCELIER__STORAGE__S3__ENDPOINT: "http://rustfs:8080"
|
||||
CONCELIER__AUTHORITY__BASEURL: "https://authority:8440"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__ALLOWOFFLINECACHEFALLBACK: "true"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__OFFLINECACHETOLERANCE: "${AUTHORITY_OFFLINE_CACHE_TOLERANCE:-00:30:00}"
|
||||
volumes:
|
||||
- concelier-jobs:/var/lib/concelier/jobs
|
||||
ports:
|
||||
- "${CONCELIER_PORT:-8445}:8445"
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
|
||||
scanner-web:
|
||||
image: registry.stella-ops.org/stellaops/scanner-web@sha256:14b23448c3f9586a9156370b3e8c1991b61907efa666ca37dd3aaed1e79fe3b7
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- valkey
|
||||
- concelier
|
||||
- rustfs
|
||||
environment:
|
||||
SCANNER__STORAGE__DRIVER: "postgres"
|
||||
SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
SCANNER__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
|
||||
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://rustfs:8080/api/v1"
|
||||
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
|
||||
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
|
||||
SCANNER__QUEUE__BROKER: "${SCANNER_QUEUE_BROKER:-valkey://valkey:6379}"
|
||||
SCANNER__EVENTS__ENABLED: "${SCANNER_EVENTS_ENABLED:-false}"
|
||||
SCANNER__EVENTS__DRIVER: "${SCANNER_EVENTS_DRIVER:-valkey}"
|
||||
SCANNER__EVENTS__DSN: "${SCANNER_EVENTS_DSN:-}"
|
||||
SCANNER__EVENTS__STREAM: "${SCANNER_EVENTS_STREAM:-stella.events}"
|
||||
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "${SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS:-5}"
|
||||
SCANNER__EVENTS__MAXSTREAMLENGTH: "${SCANNER_EVENTS_MAX_STREAM_LENGTH:-10000}"
|
||||
SCANNER__OFFLINEKIT__ENABLED: "${SCANNER_OFFLINEKIT_ENABLED:-false}"
|
||||
SCANNER__OFFLINEKIT__REQUIREDSSE: "${SCANNER_OFFLINEKIT_REQUIREDSSE:-true}"
|
||||
SCANNER__OFFLINEKIT__REKOROFFLINEMODE: "${SCANNER_OFFLINEKIT_REKOROFFLINEMODE:-true}"
|
||||
SCANNER__OFFLINEKIT__TRUSTROOTDIRECTORY: "${SCANNER_OFFLINEKIT_TRUSTROOTDIRECTORY:-/etc/stellaops/trust-roots}"
|
||||
SCANNER__OFFLINEKIT__REKORSNAPSHOTDIRECTORY: "${SCANNER_OFFLINEKIT_REKORSNAPSHOTDIRECTORY:-/var/lib/stellaops/rekor-snapshot}"
|
||||
SCANNER_SURFACE_FS_ENDPOINT: "${SCANNER_SURFACE_FS_ENDPOINT:-http://rustfs:8080}"
|
||||
SCANNER_SURFACE_FS_BUCKET: "${SCANNER_SURFACE_FS_BUCKET:-surface-cache}"
|
||||
SCANNER_SURFACE_CACHE_ROOT: "${SCANNER_SURFACE_CACHE_ROOT:-/var/lib/stellaops/surface}"
|
||||
SCANNER_SURFACE_CACHE_QUOTA_MB: "${SCANNER_SURFACE_CACHE_QUOTA_MB:-4096}"
|
||||
SCANNER_SURFACE_PREFETCH_ENABLED: "${SCANNER_SURFACE_PREFETCH_ENABLED:-false}"
|
||||
SCANNER_SURFACE_TENANT: "${SCANNER_SURFACE_TENANT:-default}"
|
||||
SCANNER_SURFACE_FEATURES: "${SCANNER_SURFACE_FEATURES:-}"
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER: "${SCANNER_SURFACE_SECRETS_PROVIDER:-file}"
|
||||
SCANNER_SURFACE_SECRETS_NAMESPACE: "${SCANNER_SURFACE_SECRETS_NAMESPACE:-}"
|
||||
SCANNER_SURFACE_SECRETS_ROOT: "${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}"
|
||||
SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER: "${SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER:-}"
|
||||
SCANNER_SURFACE_SECRETS_ALLOW_INLINE: "${SCANNER_SURFACE_SECRETS_ALLOW_INLINE:-false}"
|
||||
volumes:
|
||||
- scanner-surface-cache:/var/lib/stellaops/surface
|
||||
- ${SURFACE_SECRETS_HOST_PATH:-./offline/surface-secrets}:${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}:ro
|
||||
- ${SCANNER_OFFLINEKIT_TRUSTROOTS_HOST_PATH:-./offline/trust-roots}:${SCANNER_OFFLINEKIT_TRUSTROOTDIRECTORY:-/etc/stellaops/trust-roots}:ro
|
||||
- ${SCANNER_OFFLINEKIT_REKOR_SNAPSHOT_HOST_PATH:-./offline/rekor-snapshot}:${SCANNER_OFFLINEKIT_REKORSNAPSHOTDIRECTORY:-/var/lib/stellaops/rekor-snapshot}:ro
|
||||
ports:
|
||||
- "${SCANNER_WEB_PORT:-8444}:8444"
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
|
||||
scanner-worker:
|
||||
image: registry.stella-ops.org/stellaops/scanner-worker@sha256:32e25e76386eb9ea8bee0a1ad546775db9a2df989fab61ac877e351881960dab
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- valkey
|
||||
- scanner-web
|
||||
- rustfs
|
||||
environment:
|
||||
SCANNER__STORAGE__DRIVER: "postgres"
|
||||
SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
SCANNER__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
|
||||
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://rustfs:8080/api/v1"
|
||||
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
|
||||
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
|
||||
SCANNER__QUEUE__BROKER: "${SCANNER_QUEUE_BROKER:-valkey://valkey:6379}"
|
||||
SCANNER_SURFACE_FS_ENDPOINT: "${SCANNER_SURFACE_FS_ENDPOINT:-http://rustfs:8080}"
|
||||
SCANNER_SURFACE_FS_BUCKET: "${SCANNER_SURFACE_FS_BUCKET:-surface-cache}"
|
||||
SCANNER_SURFACE_CACHE_ROOT: "${SCANNER_SURFACE_CACHE_ROOT:-/var/lib/stellaops/surface}"
|
||||
SCANNER_SURFACE_CACHE_QUOTA_MB: "${SCANNER_SURFACE_CACHE_QUOTA_MB:-4096}"
|
||||
SCANNER_SURFACE_PREFETCH_ENABLED: "${SCANNER_SURFACE_PREFETCH_ENABLED:-false}"
|
||||
SCANNER_SURFACE_TENANT: "${SCANNER_SURFACE_TENANT:-default}"
|
||||
SCANNER_SURFACE_FEATURES: "${SCANNER_SURFACE_FEATURES:-}"
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER: "${SCANNER_SURFACE_SECRETS_PROVIDER:-file}"
|
||||
SCANNER_SURFACE_SECRETS_NAMESPACE: "${SCANNER_SURFACE_SECRETS_NAMESPACE:-}"
|
||||
SCANNER_SURFACE_SECRETS_ROOT: "${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}"
|
||||
SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER: "${SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER:-}"
|
||||
SCANNER_SURFACE_SECRETS_ALLOW_INLINE: "${SCANNER_SURFACE_SECRETS_ALLOW_INLINE:-false}"
|
||||
volumes:
|
||||
- scanner-surface-cache:/var/lib/stellaops/surface
|
||||
- ${SURFACE_SECRETS_HOST_PATH:-./offline/surface-secrets}:${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}:ro
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
scheduler-worker:
|
||||
image: registry.stella-ops.org/stellaops/scheduler-worker:2025.10.0-edge
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- valkey
|
||||
- scanner-web
|
||||
command:
|
||||
- "dotnet"
|
||||
- "StellaOps.Scheduler.Worker.Host.dll"
|
||||
environment:
|
||||
SCHEDULER__STORAGE__DRIVER: "postgres"
|
||||
SCHEDULER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
SCHEDULER__QUEUE__KIND: "${SCHEDULER_QUEUE_KIND:-Valkey}"
|
||||
SCHEDULER__QUEUE__VALKEY__URL: "${SCHEDULER_QUEUE_VALKEY_URL:-valkey:6379}"
|
||||
SCHEDULER__WORKER__RUNNER__SCANNER__BASEADDRESS: "${SCHEDULER_SCANNER_BASEADDRESS:-http://scanner-web:8444}"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
notify-web:
|
||||
image: ${NOTIFY_WEB_IMAGE:-registry.stella-ops.org/stellaops/notify-web:2025.09.2}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- authority
|
||||
environment:
|
||||
DOTNET_ENVIRONMENT: Production
|
||||
volumes:
|
||||
- ../../etc/notify.prod.yaml:/app/etc/notify.yaml:ro
|
||||
ports:
|
||||
- "${NOTIFY_WEB_PORT:-8446}:8446"
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
|
||||
excititor:
|
||||
image: registry.stella-ops.org/stellaops/excititor@sha256:59022e2016aebcef5c856d163ae705755d3f81949d41195256e935ef40a627fa
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- concelier
|
||||
environment:
|
||||
EXCITITOR__CONCELIER__BASEURL: "https://concelier:8445"
|
||||
EXCITITOR__STORAGE__DRIVER: "postgres"
|
||||
EXCITITOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB:-stellaops_platform};Username=${POSTGRES_USER:-stellaops};Password=${POSTGRES_PASSWORD:-stellaops}"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
advisory-ai-web:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-web:2025.09.2
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- scanner-web
|
||||
environment:
|
||||
ADVISORYAI__AdvisoryAI__SbomBaseAddress: "${ADVISORY_AI_SBOM_BASEADDRESS:-http://scanner-web:8444}"
|
||||
ADVISORYAI__AdvisoryAI__Queue__DirectoryPath: "/var/lib/advisory-ai/queue"
|
||||
ADVISORYAI__AdvisoryAI__Storage__PlanCacheDirectory: "/var/lib/advisory-ai/plans"
|
||||
ADVISORYAI__AdvisoryAI__Storage__OutputDirectory: "/var/lib/advisory-ai/outputs"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Mode: "${ADVISORY_AI_INFERENCE_MODE:-Local}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__BaseAddress: "${ADVISORY_AI_REMOTE_BASEADDRESS:-}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__ApiKey: "${ADVISORY_AI_REMOTE_APIKEY:-}"
|
||||
ports:
|
||||
- "${ADVISORY_AI_WEB_PORT:-8448}:8448"
|
||||
volumes:
|
||||
- advisory-ai-queue:/var/lib/advisory-ai/queue
|
||||
- advisory-ai-plans:/var/lib/advisory-ai/plans
|
||||
- advisory-ai-outputs:/var/lib/advisory-ai/outputs
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
|
||||
advisory-ai-worker:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-worker:2025.09.2
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- advisory-ai-web
|
||||
environment:
|
||||
ADVISORYAI__AdvisoryAI__SbomBaseAddress: "${ADVISORY_AI_SBOM_BASEADDRESS:-http://scanner-web:8444}"
|
||||
ADVISORYAI__AdvisoryAI__Queue__DirectoryPath: "/var/lib/advisory-ai/queue"
|
||||
ADVISORYAI__AdvisoryAI__Storage__PlanCacheDirectory: "/var/lib/advisory-ai/plans"
|
||||
ADVISORYAI__AdvisoryAI__Storage__OutputDirectory: "/var/lib/advisory-ai/outputs"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Mode: "${ADVISORY_AI_INFERENCE_MODE:-Local}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__BaseAddress: "${ADVISORY_AI_REMOTE_BASEADDRESS:-}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__ApiKey: "${ADVISORY_AI_REMOTE_APIKEY:-}"
|
||||
volumes:
|
||||
- advisory-ai-queue:/var/lib/advisory-ai/queue
|
||||
- advisory-ai-plans:/var/lib/advisory-ai/plans
|
||||
- advisory-ai-outputs:/var/lib/advisory-ai/outputs
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
web-ui:
|
||||
image: registry.stella-ops.org/stellaops/web-ui@sha256:10d924808c48e4353e3a241da62eb7aefe727a1d6dc830eb23a8e181013b3a23
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- scanner-web
|
||||
environment:
|
||||
STELLAOPS_UI__BACKEND__BASEURL: "https://scanner-web:8444"
|
||||
ports:
|
||||
- "${UI_PORT:-8443}:8443"
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# APPLICATION SERVICES
|
||||
# ===========================================================================
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Authority - OAuth2/OIDC identity provider
|
||||
# ---------------------------------------------------------------------------
|
||||
authority:
|
||||
image: registry.stella-ops.org/stellaops/authority@sha256:b0348bad1d0b401cc3c71cb40ba034c8043b6c8874546f90d4783c9dbfcc0bf5
|
||||
container_name: stellaops-authority
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
valkey:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
STELLAOPS_AUTHORITY__ISSUER: "${AUTHORITY_ISSUER}"
|
||||
STELLAOPS_AUTHORITY__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_AUTHORITY__STORAGE__POSTGRES__CONNECTIONSTRING: *postgres-connection
|
||||
STELLAOPS_AUTHORITY__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: "/app/plugins"
|
||||
STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: "/app/etc/authority/plugins"
|
||||
volumes:
|
||||
- ../../etc/authority:/app/etc/authority:ro
|
||||
- ../../etc/certificates/trust-roots:/etc/ssl/certs/stellaops:ro
|
||||
ports:
|
||||
- "${AUTHORITY_PORT:-8440}:8440"
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Signer - Cryptographic signing service
|
||||
# ---------------------------------------------------------------------------
|
||||
signer:
|
||||
image: registry.stella-ops.org/stellaops/signer@sha256:8ad574e61f3a9e9bda8a58eb2700ae46813284e35a150b1137bc7c2b92ac0f2e
|
||||
container_name: stellaops-signer
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- authority
|
||||
- valkey
|
||||
environment:
|
||||
SIGNER__AUTHORITY__BASEURL: "https://authority:8440"
|
||||
SIGNER__POE__INTROSPECTURL: "${SIGNER_POE_INTROSPECT_URL}"
|
||||
SIGNER__STORAGE__DRIVER: "postgres"
|
||||
SIGNER__STORAGE__POSTGRES__CONNECTIONSTRING: *postgres-connection
|
||||
SIGNER__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
ports:
|
||||
- "${SIGNER_PORT:-8441}:8441"
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Attestor - SLSA attestation service
|
||||
# ---------------------------------------------------------------------------
|
||||
attestor:
|
||||
image: registry.stella-ops.org/stellaops/attestor@sha256:0534985f978b0b5d220d73c96fddd962cd9135f616811cbe3bff4666c5af568f
|
||||
container_name: stellaops-attestor
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- signer
|
||||
environment:
|
||||
ATTESTOR__SIGNER__BASEURL: "https://signer:8441"
|
||||
ATTESTOR__STORAGE__DRIVER: "postgres"
|
||||
ATTESTOR__STORAGE__POSTGRES__CONNECTIONSTRING: *postgres-connection
|
||||
ATTESTOR__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
ports:
|
||||
- "${ATTESTOR_PORT:-8442}:8442"
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Issuer Directory - CSAF publisher registry
|
||||
# ---------------------------------------------------------------------------
|
||||
issuer-directory:
|
||||
image: registry.stella-ops.org/stellaops/issuer-directory-web:2025.10.0
|
||||
container_name: stellaops-issuer-directory
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- authority
|
||||
environment:
|
||||
ISSUERDIRECTORY__CONFIG: "/app/etc/issuer-directory/issuer-directory.yaml"
|
||||
ISSUERDIRECTORY__AUTHORITY__ISSUER: "${AUTHORITY_ISSUER}"
|
||||
ISSUERDIRECTORY__AUTHORITY__BASEURL: "https://authority:8440"
|
||||
ISSUERDIRECTORY__STORAGE__DRIVER: "postgres"
|
||||
ISSUERDIRECTORY__STORAGE__POSTGRES__CONNECTIONSTRING: *postgres-connection
|
||||
ISSUERDIRECTORY__SEEDCSAFPUBLISHERS: "${ISSUER_DIRECTORY_SEED_CSAF:-true}"
|
||||
volumes:
|
||||
- ../../etc/issuer-directory:/app/etc/issuer-directory:ro
|
||||
ports:
|
||||
- "${ISSUER_DIRECTORY_PORT:-8447}:8080"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Concelier - Advisory aggregation service
|
||||
# ---------------------------------------------------------------------------
|
||||
concelier:
|
||||
image: registry.stella-ops.org/stellaops/concelier@sha256:c58cdcaee1d266d68d498e41110a589dd204b487d37381096bd61ab345a867c5
|
||||
container_name: stellaops-concelier
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- valkey
|
||||
- rustfs
|
||||
environment:
|
||||
CONCELIER__STORAGE__DRIVER: "postgres"
|
||||
CONCELIER__STORAGE__POSTGRES__CONNECTIONSTRING: *postgres-connection
|
||||
CONCELIER__STORAGE__S3__ENDPOINT: "http://rustfs:8080"
|
||||
CONCELIER__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
CONCELIER__AUTHORITY__BASEURL: "https://authority:8440"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__ALLOWOFFLINECACHEFALLBACK: "true"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__OFFLINECACHETOLERANCE: "${AUTHORITY_OFFLINE_CACHE_TOLERANCE:-00:30:00}"
|
||||
volumes:
|
||||
- concelier-jobs:/var/lib/concelier/jobs
|
||||
ports:
|
||||
- "${CONCELIER_PORT:-8445}:8445"
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Scanner Web - SBOM/vulnerability scanning API
|
||||
# ---------------------------------------------------------------------------
|
||||
scanner-web:
|
||||
image: registry.stella-ops.org/stellaops/scanner-web@sha256:14b23448c3f9586a9156370b3e8c1991b61907efa666ca37dd3aaed1e79fe3b7
|
||||
container_name: stellaops-scanner-web
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- valkey
|
||||
- concelier
|
||||
- rustfs
|
||||
environment:
|
||||
SCANNER__STORAGE__DRIVER: "postgres"
|
||||
SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: *postgres-connection
|
||||
SCANNER__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
|
||||
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://rustfs:8080/api/v1"
|
||||
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
|
||||
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
|
||||
# Queue configuration - Valkey only
|
||||
SCANNER__QUEUE__BROKER: "valkey://valkey:6379"
|
||||
# Event streaming
|
||||
SCANNER__EVENTS__ENABLED: "${SCANNER_EVENTS_ENABLED:-false}"
|
||||
SCANNER__EVENTS__DRIVER: "valkey"
|
||||
SCANNER__EVENTS__DSN: "valkey:6379"
|
||||
SCANNER__EVENTS__STREAM: "${SCANNER_EVENTS_STREAM:-stella.events}"
|
||||
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "${SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS:-5}"
|
||||
SCANNER__EVENTS__MAXSTREAMLENGTH: "${SCANNER_EVENTS_MAX_STREAM_LENGTH:-10000}"
|
||||
# Offline kit
|
||||
SCANNER__OFFLINEKIT__ENABLED: "${SCANNER_OFFLINEKIT_ENABLED:-false}"
|
||||
SCANNER__OFFLINEKIT__REQUIREDSSE: "${SCANNER_OFFLINEKIT_REQUIREDSSE:-true}"
|
||||
SCANNER__OFFLINEKIT__REKOROFFLINEMODE: "${SCANNER_OFFLINEKIT_REKOROFFLINEMODE:-true}"
|
||||
SCANNER__OFFLINEKIT__TRUSTROOTDIRECTORY: "${SCANNER_OFFLINEKIT_TRUSTROOTDIRECTORY:-/etc/stellaops/trust-roots}"
|
||||
SCANNER__OFFLINEKIT__REKORSNAPSHOTDIRECTORY: "${SCANNER_OFFLINEKIT_REKORSNAPSHOTDIRECTORY:-/var/lib/stellaops/rekor-snapshot}"
|
||||
# Surface cache
|
||||
SCANNER_SURFACE_FS_ENDPOINT: "${SCANNER_SURFACE_FS_ENDPOINT:-http://rustfs:8080}"
|
||||
SCANNER_SURFACE_FS_BUCKET: "${SCANNER_SURFACE_FS_BUCKET:-surface-cache}"
|
||||
SCANNER_SURFACE_CACHE_ROOT: "${SCANNER_SURFACE_CACHE_ROOT:-/var/lib/stellaops/surface}"
|
||||
SCANNER_SURFACE_CACHE_QUOTA_MB: "${SCANNER_SURFACE_CACHE_QUOTA_MB:-4096}"
|
||||
SCANNER_SURFACE_PREFETCH_ENABLED: "${SCANNER_SURFACE_PREFETCH_ENABLED:-false}"
|
||||
SCANNER_SURFACE_TENANT: "${SCANNER_SURFACE_TENANT:-default}"
|
||||
SCANNER_SURFACE_FEATURES: "${SCANNER_SURFACE_FEATURES:-}"
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER: "${SCANNER_SURFACE_SECRETS_PROVIDER:-file}"
|
||||
SCANNER_SURFACE_SECRETS_NAMESPACE: "${SCANNER_SURFACE_SECRETS_NAMESPACE:-}"
|
||||
SCANNER_SURFACE_SECRETS_ROOT: "${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}"
|
||||
SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER: "${SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER:-}"
|
||||
SCANNER_SURFACE_SECRETS_ALLOW_INLINE: "${SCANNER_SURFACE_SECRETS_ALLOW_INLINE:-false}"
|
||||
volumes:
|
||||
- ../../etc/scanner:/app/etc/scanner:ro
|
||||
- ../../etc/certificates/trust-roots:/etc/ssl/certs/stellaops:ro
|
||||
- scanner-surface-cache:/var/lib/stellaops/surface
|
||||
- ${SURFACE_SECRETS_HOST_PATH:-./offline/surface-secrets}:${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}:ro
|
||||
- ${SCANNER_OFFLINEKIT_TRUSTROOTS_HOST_PATH:-./offline/trust-roots}:${SCANNER_OFFLINEKIT_TRUSTROOTDIRECTORY:-/etc/stellaops/trust-roots}:ro
|
||||
- ${SCANNER_OFFLINEKIT_REKOR_SNAPSHOT_HOST_PATH:-./offline/rekor-snapshot}:${SCANNER_OFFLINEKIT_REKORSNAPSHOTDIRECTORY:-/var/lib/stellaops/rekor-snapshot}:ro
|
||||
ports:
|
||||
- "${SCANNER_WEB_PORT:-8444}:8444"
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Scanner Worker - Background scanning jobs
|
||||
# ---------------------------------------------------------------------------
|
||||
scanner-worker:
|
||||
image: registry.stella-ops.org/stellaops/scanner-worker@sha256:32e25e76386eb9ea8bee0a1ad546775db9a2df989fab61ac877e351881960dab
|
||||
container_name: stellaops-scanner-worker
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- scanner-web
|
||||
- valkey
|
||||
- rustfs
|
||||
environment:
|
||||
SCANNER__STORAGE__DRIVER: "postgres"
|
||||
SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: *postgres-connection
|
||||
SCANNER__CACHE__REDIS__CONNECTIONSTRING: "valkey:6379"
|
||||
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
|
||||
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://rustfs:8080/api/v1"
|
||||
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
|
||||
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
|
||||
# Queue configuration - Valkey only
|
||||
SCANNER__QUEUE__BROKER: "valkey://valkey:6379"
|
||||
# Surface cache
|
||||
SCANNER_SURFACE_FS_ENDPOINT: "${SCANNER_SURFACE_FS_ENDPOINT:-http://rustfs:8080}"
|
||||
SCANNER_SURFACE_FS_BUCKET: "${SCANNER_SURFACE_FS_BUCKET:-surface-cache}"
|
||||
SCANNER_SURFACE_CACHE_ROOT: "${SCANNER_SURFACE_CACHE_ROOT:-/var/lib/stellaops/surface}"
|
||||
SCANNER_SURFACE_CACHE_QUOTA_MB: "${SCANNER_SURFACE_CACHE_QUOTA_MB:-4096}"
|
||||
SCANNER_SURFACE_PREFETCH_ENABLED: "${SCANNER_SURFACE_PREFETCH_ENABLED:-false}"
|
||||
SCANNER_SURFACE_TENANT: "${SCANNER_SURFACE_TENANT:-default}"
|
||||
SCANNER_SURFACE_FEATURES: "${SCANNER_SURFACE_FEATURES:-}"
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER: "${SCANNER_SURFACE_SECRETS_PROVIDER:-file}"
|
||||
SCANNER_SURFACE_SECRETS_NAMESPACE: "${SCANNER_SURFACE_SECRETS_NAMESPACE:-}"
|
||||
SCANNER_SURFACE_SECRETS_ROOT: "${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}"
|
||||
SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER: "${SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER:-}"
|
||||
SCANNER_SURFACE_SECRETS_ALLOW_INLINE: "${SCANNER_SURFACE_SECRETS_ALLOW_INLINE:-false}"
|
||||
volumes:
|
||||
- scanner-surface-cache:/var/lib/stellaops/surface
|
||||
- ${SURFACE_SECRETS_HOST_PATH:-./offline/surface-secrets}:${SCANNER_SURFACE_SECRETS_ROOT:-/etc/stellaops/secrets}:ro
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Scheduler Worker - Background job scheduling
|
||||
# ---------------------------------------------------------------------------
|
||||
scheduler-worker:
|
||||
image: registry.stella-ops.org/stellaops/scheduler-worker:2025.10.0
|
||||
container_name: stellaops-scheduler-worker
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- valkey
|
||||
- scanner-web
|
||||
command:
|
||||
- "dotnet"
|
||||
- "StellaOps.Scheduler.Worker.Host.dll"
|
||||
environment:
|
||||
SCHEDULER__STORAGE__DRIVER: "postgres"
|
||||
SCHEDULER__STORAGE__POSTGRES__CONNECTIONSTRING: *postgres-connection
|
||||
# Queue configuration - Valkey only
|
||||
SCHEDULER__QUEUE__KIND: "Valkey"
|
||||
SCHEDULER__QUEUE__VALKEY__URL: "valkey:6379"
|
||||
SCHEDULER__WORKER__RUNNER__SCANNER__BASEADDRESS: "${SCHEDULER_SCANNER_BASEADDRESS:-http://scanner-web:8444}"
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Notify Web - Notification service
|
||||
# ---------------------------------------------------------------------------
|
||||
notify-web:
|
||||
image: ${NOTIFY_WEB_IMAGE:-registry.stella-ops.org/stellaops/notify-web:2025.10.0}
|
||||
container_name: stellaops-notify-web
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- authority
|
||||
- valkey
|
||||
environment:
|
||||
DOTNET_ENVIRONMENT: Production
|
||||
NOTIFY__STORAGE__DRIVER: "postgres"
|
||||
NOTIFY__STORAGE__POSTGRES__CONNECTIONSTRING: *postgres-connection
|
||||
# Queue configuration - Valkey only
|
||||
NOTIFY__QUEUE__DRIVER: "valkey"
|
||||
NOTIFY__QUEUE__VALKEY__URL: "valkey:6379"
|
||||
volumes:
|
||||
- ../../etc/notify:/app/etc/notify:ro
|
||||
ports:
|
||||
- "${NOTIFY_WEB_PORT:-8446}:8446"
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Excititor - VEX generation service
|
||||
# ---------------------------------------------------------------------------
|
||||
excititor:
|
||||
image: registry.stella-ops.org/stellaops/excititor@sha256:59022e2016aebcef5c856d163ae705755d3f81949d41195256e935ef40a627fa
|
||||
container_name: stellaops-excititor
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- concelier
|
||||
environment:
|
||||
EXCITITOR__CONCELIER__BASEURL: "https://concelier:8445"
|
||||
EXCITITOR__STORAGE__DRIVER: "postgres"
|
||||
EXCITITOR__STORAGE__POSTGRES__CONNECTIONSTRING: *postgres-connection
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Advisory AI Web - AI-powered advisory analysis API
|
||||
# ---------------------------------------------------------------------------
|
||||
advisory-ai-web:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-web:2025.10.0
|
||||
container_name: stellaops-advisory-ai-web
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- scanner-web
|
||||
environment:
|
||||
ADVISORYAI__AdvisoryAI__SbomBaseAddress: "${ADVISORY_AI_SBOM_BASEADDRESS:-http://scanner-web:8444}"
|
||||
ADVISORYAI__AdvisoryAI__Queue__DirectoryPath: "/var/lib/advisory-ai/queue"
|
||||
ADVISORYAI__AdvisoryAI__Storage__PlanCacheDirectory: "/var/lib/advisory-ai/plans"
|
||||
ADVISORYAI__AdvisoryAI__Storage__OutputDirectory: "/var/lib/advisory-ai/outputs"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Mode: "${ADVISORY_AI_INFERENCE_MODE:-Local}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__BaseAddress: "${ADVISORY_AI_REMOTE_BASEADDRESS:-}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__ApiKey: "${ADVISORY_AI_REMOTE_APIKEY:-}"
|
||||
ports:
|
||||
- "${ADVISORY_AI_WEB_PORT:-8448}:8448"
|
||||
volumes:
|
||||
- ../../etc/llm-providers:/app/etc/llm-providers:ro
|
||||
- advisory-ai-queue:/var/lib/advisory-ai/queue
|
||||
- advisory-ai-plans:/var/lib/advisory-ai/plans
|
||||
- advisory-ai-outputs:/var/lib/advisory-ai/outputs
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Advisory AI Worker - Background AI processing
|
||||
# ---------------------------------------------------------------------------
|
||||
advisory-ai-worker:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-worker:2025.10.0
|
||||
container_name: stellaops-advisory-ai-worker
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- advisory-ai-web
|
||||
environment:
|
||||
ADVISORYAI__AdvisoryAI__SbomBaseAddress: "${ADVISORY_AI_SBOM_BASEADDRESS:-http://scanner-web:8444}"
|
||||
ADVISORYAI__AdvisoryAI__Queue__DirectoryPath: "/var/lib/advisory-ai/queue"
|
||||
ADVISORYAI__AdvisoryAI__Storage__PlanCacheDirectory: "/var/lib/advisory-ai/plans"
|
||||
ADVISORYAI__AdvisoryAI__Storage__OutputDirectory: "/var/lib/advisory-ai/outputs"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Mode: "${ADVISORY_AI_INFERENCE_MODE:-Local}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__BaseAddress: "${ADVISORY_AI_REMOTE_BASEADDRESS:-}"
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__ApiKey: "${ADVISORY_AI_REMOTE_APIKEY:-}"
|
||||
volumes:
|
||||
- ../../etc/llm-providers:/app/etc/llm-providers:ro
|
||||
- advisory-ai-queue:/var/lib/advisory-ai/queue
|
||||
- advisory-ai-plans:/var/lib/advisory-ai/plans
|
||||
- advisory-ai-outputs:/var/lib/advisory-ai/outputs
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Web UI - Angular frontend
|
||||
# ---------------------------------------------------------------------------
|
||||
web-ui:
|
||||
image: registry.stella-ops.org/stellaops/web-ui@sha256:10d924808c48e4353e3a241da62eb7aefe727a1d6dc830eb23a8e181013b3a23
|
||||
container_name: stellaops-web-ui
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- scanner-web
|
||||
environment:
|
||||
STELLAOPS_UI__BACKEND__BASEURL: "https://scanner-web:8444"
|
||||
ports:
|
||||
- "${UI_PORT:-8443}:8443"
|
||||
networks:
|
||||
- stellaops
|
||||
- frontdoor
|
||||
labels: *release-labels
|
||||
90
deploy/compose/docker-compose.telemetry-offline.yml
Normal file
90
deploy/compose/docker-compose.telemetry-offline.yml
Normal file
@@ -0,0 +1,90 @@
|
||||
# =============================================================================
|
||||
# TELEMETRY OFFLINE - AIR-GAPPED OBSERVABILITY
|
||||
# =============================================================================
|
||||
# Offline-compatible telemetry stack for air-gapped deployments.
|
||||
# Does not require external connectivity.
|
||||
#
|
||||
# Usage:
|
||||
# docker compose -f docker-compose.telemetry-offline.yml up -d
|
||||
#
|
||||
# For online deployments, use docker-compose.telemetry.yml instead.
|
||||
# =============================================================================
|
||||
|
||||
services:
|
||||
loki:
|
||||
image: grafana/loki:3.0.1
|
||||
container_name: stellaops-loki-offline
|
||||
command: ["-config.file=/etc/loki/local-config.yaml"]
|
||||
volumes:
|
||||
- loki-data:/loki
|
||||
- ../offline/airgap/observability/loki-config.yaml:/etc/loki/local-config.yaml:ro
|
||||
ports:
|
||||
- "${LOKI_PORT:-3100}:3100"
|
||||
networks:
|
||||
- sealed
|
||||
restart: unless-stopped
|
||||
|
||||
promtail:
|
||||
image: grafana/promtail:3.0.1
|
||||
container_name: stellaops-promtail-offline
|
||||
command: ["-config.file=/etc/promtail/config.yml"]
|
||||
volumes:
|
||||
- promtail-data:/var/log
|
||||
- ../offline/airgap/promtail-config.yaml:/etc/promtail/config.yml:ro
|
||||
networks:
|
||||
- sealed
|
||||
restart: unless-stopped
|
||||
|
||||
otel-collector:
|
||||
image: otel/opentelemetry-collector-contrib:0.97.0
|
||||
container_name: stellaops-otel-offline
|
||||
command: ["--config=/etc/otel/config.yaml"]
|
||||
volumes:
|
||||
- ../offline/airgap/otel-offline.yaml:/etc/otel/config.yaml:ro
|
||||
- otel-data:/var/otel
|
||||
ports:
|
||||
- "${OTEL_GRPC_PORT:-4317}:4317"
|
||||
- "${OTEL_HTTP_PORT:-4318}:4318"
|
||||
networks:
|
||||
- sealed
|
||||
restart: unless-stopped
|
||||
|
||||
tempo:
|
||||
image: grafana/tempo:2.4.1
|
||||
container_name: stellaops-tempo-offline
|
||||
command: ["-config.file=/etc/tempo/config.yaml"]
|
||||
volumes:
|
||||
- tempo-data:/var/tempo
|
||||
- ../offline/airgap/observability/tempo-config.yaml:/etc/tempo/config.yaml:ro
|
||||
ports:
|
||||
- "${TEMPO_PORT:-3200}:3200"
|
||||
networks:
|
||||
- sealed
|
||||
restart: unless-stopped
|
||||
|
||||
prometheus:
|
||||
image: prom/prometheus:v2.51.0
|
||||
container_name: stellaops-prometheus-offline
|
||||
command:
|
||||
- '--config.file=/etc/prometheus/prometheus.yml'
|
||||
- '--storage.tsdb.path=/prometheus'
|
||||
- '--storage.tsdb.retention.time=15d'
|
||||
volumes:
|
||||
- prometheus-data:/prometheus
|
||||
- ../offline/airgap/observability/prometheus.yml:/etc/prometheus/prometheus.yml:ro
|
||||
ports:
|
||||
- "${PROMETHEUS_PORT:-9090}:9090"
|
||||
networks:
|
||||
- sealed
|
||||
restart: unless-stopped
|
||||
|
||||
networks:
|
||||
sealed:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
loki-data:
|
||||
promtail-data:
|
||||
otel-data:
|
||||
tempo-data:
|
||||
prometheus-data:
|
||||
144
deploy/compose/docker-compose.telemetry.yml
Normal file
144
deploy/compose/docker-compose.telemetry.yml
Normal file
@@ -0,0 +1,144 @@
|
||||
# =============================================================================
|
||||
# STELLA OPS - TELEMETRY STACK
|
||||
# =============================================================================
|
||||
# All-in-one observability: OpenTelemetry Collector, Prometheus, Tempo, Loki
|
||||
#
|
||||
# Usage:
|
||||
# docker compose -f devops/compose/docker-compose.telemetry.yml up -d
|
||||
#
|
||||
# With main stack:
|
||||
# docker compose -f devops/compose/docker-compose.stella-ops.yml \
|
||||
# -f devops/compose/docker-compose.telemetry.yml up -d
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
x-telemetry-labels: &telemetry-labels
|
||||
com.stellaops.component: "telemetry"
|
||||
com.stellaops.profile: "observability"
|
||||
|
||||
networks:
|
||||
stellaops-telemetry:
|
||||
driver: bridge
|
||||
name: stellaops-telemetry
|
||||
stellaops:
|
||||
external: true
|
||||
name: stellaops
|
||||
|
||||
volumes:
|
||||
prometheus-data:
|
||||
tempo-data:
|
||||
loki-data:
|
||||
|
||||
services:
|
||||
# ---------------------------------------------------------------------------
|
||||
# OpenTelemetry Collector - Unified telemetry ingestion
|
||||
# ---------------------------------------------------------------------------
|
||||
otel-collector:
|
||||
image: otel/opentelemetry-collector:0.105.0
|
||||
container_name: stellaops-otel-collector
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- "--config=/etc/otel-collector/config.yaml"
|
||||
environment:
|
||||
STELLAOPS_OTEL_TLS_CERT: /etc/otel-collector/tls/collector.crt
|
||||
STELLAOPS_OTEL_TLS_KEY: /etc/otel-collector/tls/collector.key
|
||||
STELLAOPS_OTEL_TLS_CA: /etc/otel-collector/tls/ca.crt
|
||||
STELLAOPS_OTEL_PROMETHEUS_ENDPOINT: 0.0.0.0:9464
|
||||
STELLAOPS_OTEL_REQUIRE_CLIENT_CERT: "true"
|
||||
STELLAOPS_TENANT_ID: ${STELLAOPS_TENANT_ID:-default}
|
||||
STELLAOPS_TEMPO_ENDPOINT: http://tempo:3200
|
||||
STELLAOPS_TEMPO_TLS_CERT_FILE: /etc/otel-collector/tls/client.crt
|
||||
STELLAOPS_TEMPO_TLS_KEY_FILE: /etc/otel-collector/tls/client.key
|
||||
STELLAOPS_TEMPO_TLS_CA_FILE: /etc/otel-collector/tls/ca.crt
|
||||
STELLAOPS_LOKI_ENDPOINT: http://loki:3100/loki/api/v1/push
|
||||
STELLAOPS_LOKI_TLS_CERT_FILE: /etc/otel-collector/tls/client.crt
|
||||
STELLAOPS_LOKI_TLS_KEY_FILE: /etc/otel-collector/tls/client.key
|
||||
STELLAOPS_LOKI_TLS_CA_FILE: /etc/otel-collector/tls/ca.crt
|
||||
volumes:
|
||||
- ../telemetry/otel-collector-config.yaml:/etc/otel-collector/config.yaml:ro
|
||||
- ../telemetry/certs:/etc/otel-collector/tls:ro
|
||||
ports:
|
||||
- "${OTEL_GRPC_PORT:-4317}:4317" # OTLP gRPC
|
||||
- "${OTEL_HTTP_PORT:-4318}:4318" # OTLP HTTP
|
||||
- "${OTEL_PROMETHEUS_PORT:-9464}:9464" # Prometheus exporter
|
||||
- "${OTEL_HEALTH_PORT:-13133}:13133" # Health check
|
||||
- "${OTEL_PPROF_PORT:-1777}:1777" # pprof
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:13133/healthz"]
|
||||
interval: 30s
|
||||
start_period: 15s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
networks:
|
||||
- stellaops-telemetry
|
||||
- stellaops
|
||||
labels: *telemetry-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Prometheus - Metrics storage
|
||||
# ---------------------------------------------------------------------------
|
||||
prometheus:
|
||||
image: prom/prometheus:v2.53.0
|
||||
container_name: stellaops-prometheus
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- "--config.file=/etc/prometheus/prometheus.yaml"
|
||||
- "--storage.tsdb.path=/prometheus"
|
||||
- "--storage.tsdb.retention.time=${PROMETHEUS_RETENTION:-15d}"
|
||||
- "--web.enable-lifecycle"
|
||||
volumes:
|
||||
- ../telemetry/storage/prometheus.yaml:/etc/prometheus/prometheus.yaml:ro
|
||||
- prometheus-data:/prometheus
|
||||
- ../telemetry/certs:/etc/telemetry/tls:ro
|
||||
- ../telemetry/storage/auth:/etc/telemetry/auth:ro
|
||||
environment:
|
||||
PROMETHEUS_COLLECTOR_TARGET: otel-collector:9464
|
||||
ports:
|
||||
- "${PROMETHEUS_PORT:-9090}:9090"
|
||||
depends_on:
|
||||
- otel-collector
|
||||
networks:
|
||||
- stellaops-telemetry
|
||||
labels: *telemetry-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tempo - Distributed tracing backend
|
||||
# ---------------------------------------------------------------------------
|
||||
tempo:
|
||||
image: grafana/tempo:2.5.0
|
||||
container_name: stellaops-tempo
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- "-config.file=/etc/tempo/tempo.yaml"
|
||||
volumes:
|
||||
- ../telemetry/storage/tempo.yaml:/etc/tempo/tempo.yaml:ro
|
||||
- ../telemetry/storage/tenants/tempo-overrides.yaml:/etc/telemetry/tenants/tempo-overrides.yaml:ro
|
||||
- ../telemetry/certs:/etc/telemetry/tls:ro
|
||||
- tempo-data:/var/tempo
|
||||
environment:
|
||||
TEMPO_ZONE: docker
|
||||
ports:
|
||||
- "${TEMPO_PORT:-3200}:3200"
|
||||
networks:
|
||||
- stellaops-telemetry
|
||||
labels: *telemetry-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Loki - Log aggregation
|
||||
# ---------------------------------------------------------------------------
|
||||
loki:
|
||||
image: grafana/loki:3.1.0
|
||||
container_name: stellaops-loki
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- "-config.file=/etc/loki/loki.yaml"
|
||||
volumes:
|
||||
- ../telemetry/storage/loki.yaml:/etc/loki/loki.yaml:ro
|
||||
- ../telemetry/storage/tenants/loki-overrides.yaml:/etc/telemetry/tenants/loki-overrides.yaml:ro
|
||||
- ../telemetry/certs:/etc/telemetry/tls:ro
|
||||
- loki-data:/var/loki
|
||||
ports:
|
||||
- "${LOKI_PORT:-3100}:3100"
|
||||
networks:
|
||||
- stellaops-telemetry
|
||||
labels: *telemetry-labels
|
||||
327
deploy/compose/docker-compose.testing.yml
Normal file
327
deploy/compose/docker-compose.testing.yml
Normal file
@@ -0,0 +1,327 @@
|
||||
# =============================================================================
|
||||
# STELLA OPS - TESTING STACK
|
||||
# =============================================================================
|
||||
# Consolidated CI, mock services, and Gitea for integration testing.
|
||||
# Uses different ports to avoid conflicts with development/production services.
|
||||
#
|
||||
# Usage:
|
||||
# docker compose -f devops/compose/docker-compose.testing.yml up -d
|
||||
#
|
||||
# CI infrastructure only:
|
||||
# docker compose -f devops/compose/docker-compose.testing.yml --profile ci up -d
|
||||
#
|
||||
# Mock services only:
|
||||
# docker compose -f devops/compose/docker-compose.testing.yml --profile mock up -d
|
||||
#
|
||||
# Gitea only:
|
||||
# docker compose -f devops/compose/docker-compose.testing.yml --profile gitea up -d
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
x-testing-labels: &testing-labels
|
||||
com.stellaops.profile: "testing"
|
||||
com.stellaops.environment: "ci"
|
||||
|
||||
networks:
|
||||
testing-net:
|
||||
driver: bridge
|
||||
name: stellaops-testing
|
||||
|
||||
volumes:
|
||||
# CI volumes
|
||||
ci-postgres-data:
|
||||
name: stellaops-ci-postgres
|
||||
ci-valkey-data:
|
||||
name: stellaops-ci-valkey
|
||||
ci-rustfs-data:
|
||||
name: stellaops-ci-rustfs
|
||||
# Gitea volumes
|
||||
gitea-data:
|
||||
gitea-config:
|
||||
|
||||
services:
|
||||
# ===========================================================================
|
||||
# CI INFRASTRUCTURE (different ports to avoid conflicts)
|
||||
# ===========================================================================
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# PostgreSQL 18.1 - Test database (port 5433)
|
||||
# ---------------------------------------------------------------------------
|
||||
postgres-test:
|
||||
image: postgres:18.1-alpine
|
||||
container_name: stellaops-postgres-test
|
||||
profiles: ["ci", "all"]
|
||||
environment:
|
||||
POSTGRES_USER: stellaops_ci
|
||||
POSTGRES_PASSWORD: ci_test_password
|
||||
POSTGRES_DB: stellaops_test
|
||||
POSTGRES_INITDB_ARGS: "--data-checksums"
|
||||
ports:
|
||||
- "${TEST_POSTGRES_PORT:-5433}:5432"
|
||||
volumes:
|
||||
- ci-postgres-data:/var/lib/postgresql/data
|
||||
networks:
|
||||
- testing-net
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U stellaops_ci -d stellaops_test"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
start_period: 10s
|
||||
restart: unless-stopped
|
||||
labels: *testing-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Valkey 9.0.1 - Test cache/queue (port 6380)
|
||||
# ---------------------------------------------------------------------------
|
||||
valkey-test:
|
||||
image: valkey/valkey:9.0.1-alpine
|
||||
container_name: stellaops-valkey-test
|
||||
profiles: ["ci", "all"]
|
||||
command: ["valkey-server", "--appendonly", "yes", "--maxmemory", "256mb", "--maxmemory-policy", "allkeys-lru"]
|
||||
ports:
|
||||
- "${TEST_VALKEY_PORT:-6380}:6379"
|
||||
volumes:
|
||||
- ci-valkey-data:/data
|
||||
networks:
|
||||
- testing-net
|
||||
healthcheck:
|
||||
test: ["CMD", "valkey-cli", "ping"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: unless-stopped
|
||||
labels: *testing-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# RustFS - Test artifact storage (port 8180)
|
||||
# ---------------------------------------------------------------------------
|
||||
rustfs-test:
|
||||
image: registry.stella-ops.org/stellaops/rustfs:2025.09.2
|
||||
container_name: stellaops-rustfs-test
|
||||
profiles: ["ci", "all"]
|
||||
command: ["serve", "--listen", "0.0.0.0:8080", "--root", "/data"]
|
||||
environment:
|
||||
RUSTFS__LOG__LEVEL: info
|
||||
RUSTFS__STORAGE__PATH: /data
|
||||
ports:
|
||||
- "${TEST_RUSTFS_PORT:-8180}:8080"
|
||||
volumes:
|
||||
- ci-rustfs-data:/data
|
||||
networks:
|
||||
- testing-net
|
||||
restart: unless-stopped
|
||||
labels: *testing-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Mock Container Registry (port 5001)
|
||||
# ---------------------------------------------------------------------------
|
||||
mock-registry:
|
||||
image: registry:2
|
||||
container_name: stellaops-registry-test
|
||||
profiles: ["ci", "all"]
|
||||
ports:
|
||||
- "${TEST_REGISTRY_PORT:-5001}:5000"
|
||||
environment:
|
||||
REGISTRY_STORAGE_DELETE_ENABLED: "true"
|
||||
networks:
|
||||
- testing-net
|
||||
restart: unless-stopped
|
||||
labels: *testing-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Sigstore CLI tools (on-demand)
|
||||
# ---------------------------------------------------------------------------
|
||||
rekor-cli:
|
||||
image: ghcr.io/sigstore/rekor-cli:v1.4.3
|
||||
entrypoint: ["rekor-cli"]
|
||||
command: ["version"]
|
||||
profiles: ["sigstore"]
|
||||
networks:
|
||||
- testing-net
|
||||
labels: *testing-labels
|
||||
|
||||
cosign:
|
||||
image: ghcr.io/sigstore/cosign:v3.0.4
|
||||
entrypoint: ["cosign"]
|
||||
command: ["version"]
|
||||
profiles: ["sigstore"]
|
||||
networks:
|
||||
- testing-net
|
||||
labels: *testing-labels
|
||||
|
||||
# ===========================================================================
|
||||
# MOCK SERVICES (for extended integration testing)
|
||||
# ===========================================================================
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Orchestrator mock
|
||||
# ---------------------------------------------------------------------------
|
||||
orchestrator:
|
||||
image: registry.stella-ops.org/stellaops/orchestrator@sha256:97f12856ce870bafd3328bda86833bcccbf56d255941d804966b5557f6610119
|
||||
container_name: stellaops-orchestrator-mock
|
||||
profiles: ["mock", "all"]
|
||||
command: ["dotnet", "StellaOps.Orchestrator.WebService.dll"]
|
||||
depends_on:
|
||||
- postgres-test
|
||||
- valkey-test
|
||||
environment:
|
||||
ORCHESTRATOR__STORAGE__DRIVER: "postgres"
|
||||
ORCHESTRATOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres-test;Port=5432;Database=stellaops_test;Username=stellaops_ci;Password=ci_test_password"
|
||||
ORCHESTRATOR__QUEUE__DRIVER: "valkey"
|
||||
ORCHESTRATOR__QUEUE__VALKEY__URL: "valkey-test:6379"
|
||||
networks:
|
||||
- testing-net
|
||||
labels: *testing-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Policy Registry mock
|
||||
# ---------------------------------------------------------------------------
|
||||
policy-registry:
|
||||
image: registry.stella-ops.org/stellaops/policy-registry@sha256:c6cad8055e9827ebcbebb6ad4d6866dce4b83a0a49b0a8a6500b736a5cb26fa7
|
||||
container_name: stellaops-policy-registry-mock
|
||||
profiles: ["mock", "all"]
|
||||
command: ["dotnet", "StellaOps.Policy.Engine.dll"]
|
||||
depends_on:
|
||||
- postgres-test
|
||||
environment:
|
||||
POLICY__STORAGE__DRIVER: "postgres"
|
||||
POLICY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres-test;Port=5432;Database=stellaops_test;Username=stellaops_ci;Password=ci_test_password"
|
||||
networks:
|
||||
- testing-net
|
||||
labels: *testing-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# VEX Lens mock
|
||||
# ---------------------------------------------------------------------------
|
||||
vex-lens:
|
||||
image: registry.stella-ops.org/stellaops/vex-lens@sha256:b44e63ecfeebc345a70c073c1ce5ace709c58be0ffaad0e2862758aeee3092fb
|
||||
container_name: stellaops-vex-lens-mock
|
||||
profiles: ["mock", "all"]
|
||||
command: ["dotnet", "StellaOps.VexLens.dll"]
|
||||
depends_on:
|
||||
- postgres-test
|
||||
environment:
|
||||
VEXLENS__STORAGE__DRIVER: "postgres"
|
||||
VEXLENS__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres-test;Port=5432;Database=stellaops_test;Username=stellaops_ci;Password=ci_test_password"
|
||||
networks:
|
||||
- testing-net
|
||||
labels: *testing-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Findings Ledger mock
|
||||
# ---------------------------------------------------------------------------
|
||||
findings-ledger:
|
||||
image: registry.stella-ops.org/stellaops/findings-ledger@sha256:71d4c361ba8b2f8b69d652597bc3f2efc8a64f93fab854ce25272a88506df49c
|
||||
container_name: stellaops-findings-ledger-mock
|
||||
profiles: ["mock", "all"]
|
||||
command: ["dotnet", "StellaOps.Findings.Ledger.WebService.dll"]
|
||||
depends_on:
|
||||
- postgres-test
|
||||
environment:
|
||||
FINDINGSLEDGER__STORAGE__DRIVER: "postgres"
|
||||
FINDINGSLEDGER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres-test;Port=5432;Database=stellaops_test;Username=stellaops_ci;Password=ci_test_password"
|
||||
networks:
|
||||
- testing-net
|
||||
labels: *testing-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Vuln Explorer API mock
|
||||
# ---------------------------------------------------------------------------
|
||||
vuln-explorer-api:
|
||||
image: registry.stella-ops.org/stellaops/vuln-explorer-api@sha256:7fc7e43a05cbeb0106ce7d4d634612e83de6fdc119aaab754a71c1d60b82841d
|
||||
container_name: stellaops-vuln-explorer-mock
|
||||
profiles: ["mock", "all"]
|
||||
command: ["dotnet", "StellaOps.VulnExplorer.Api.dll"]
|
||||
depends_on:
|
||||
- findings-ledger
|
||||
networks:
|
||||
- testing-net
|
||||
labels: *testing-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Packs Registry mock
|
||||
# ---------------------------------------------------------------------------
|
||||
packs-registry:
|
||||
image: registry.stella-ops.org/stellaops/packs-registry@sha256:1f5e9416c4dc608594ad6fad87c24d72134427f899c192b494e22b268499c791
|
||||
container_name: stellaops-packs-registry-mock
|
||||
profiles: ["mock", "all"]
|
||||
command: ["dotnet", "StellaOps.PacksRegistry.dll"]
|
||||
depends_on:
|
||||
- postgres-test
|
||||
environment:
|
||||
PACKSREGISTRY__STORAGE__DRIVER: "postgres"
|
||||
PACKSREGISTRY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres-test;Port=5432;Database=stellaops_test;Username=stellaops_ci;Password=ci_test_password"
|
||||
networks:
|
||||
- testing-net
|
||||
labels: *testing-labels
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Task Runner mock
|
||||
# ---------------------------------------------------------------------------
|
||||
task-runner:
|
||||
image: registry.stella-ops.org/stellaops/task-runner@sha256:eb5ad992b49a41554f41516be1a6afcfa6522faf2111c08ff2b3664ad2fc954b
|
||||
container_name: stellaops-task-runner-mock
|
||||
profiles: ["mock", "all"]
|
||||
command: ["dotnet", "StellaOps.TaskRunner.WebService.dll"]
|
||||
depends_on:
|
||||
- packs-registry
|
||||
- postgres-test
|
||||
environment:
|
||||
TASKRUNNER__STORAGE__DRIVER: "postgres"
|
||||
TASKRUNNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=postgres-test;Port=5432;Database=stellaops_test;Username=stellaops_ci;Password=ci_test_password"
|
||||
networks:
|
||||
- testing-net
|
||||
labels: *testing-labels
|
||||
|
||||
# ===========================================================================
|
||||
# GITEA (SCM integration testing)
|
||||
# ===========================================================================
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Gitea - Git hosting with package registry
|
||||
# ---------------------------------------------------------------------------
|
||||
gitea:
|
||||
image: gitea/gitea:1.21
|
||||
container_name: stellaops-gitea-test
|
||||
profiles: ["gitea", "all"]
|
||||
environment:
|
||||
- USER_UID=1000
|
||||
- USER_GID=1000
|
||||
# Enable package registry
|
||||
- GITEA__packages__ENABLED=true
|
||||
- GITEA__packages__CHUNKED_UPLOAD_PATH=/data/tmp/package-upload
|
||||
# Enable NuGet
|
||||
- GITEA__packages__NUGET_ENABLED=true
|
||||
# Enable Container registry
|
||||
- GITEA__packages__CONTAINER_ENABLED=true
|
||||
# Database (SQLite for simplicity)
|
||||
- GITEA__database__DB_TYPE=sqlite3
|
||||
- GITEA__database__PATH=/data/gitea/gitea.db
|
||||
# Server config
|
||||
- GITEA__server__ROOT_URL=http://localhost:${TEST_GITEA_PORT:-3000}/
|
||||
- GITEA__server__HTTP_PORT=3000
|
||||
# Disable metrics/telemetry
|
||||
- GITEA__metrics__ENABLED=false
|
||||
# Session config
|
||||
- GITEA__session__PROVIDER=memory
|
||||
# Cache config
|
||||
- GITEA__cache__ADAPTER=memory
|
||||
# Log level
|
||||
- GITEA__log__LEVEL=Warn
|
||||
volumes:
|
||||
- gitea-data:/data
|
||||
- gitea-config:/etc/gitea
|
||||
ports:
|
||||
- "${TEST_GITEA_PORT:-3000}:3000"
|
||||
- "${TEST_GITEA_SSH_PORT:-3022}:22"
|
||||
networks:
|
||||
- testing-net
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/api/healthz"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
labels: *testing-labels
|
||||
80
deploy/compose/docker-compose.tile-proxy.yml
Normal file
80
deploy/compose/docker-compose.tile-proxy.yml
Normal file
@@ -0,0 +1,80 @@
|
||||
# =============================================================================
|
||||
# STELLA OPS TILE PROXY OVERLAY
|
||||
# =============================================================================
|
||||
# Rekor tile caching proxy for air-gapped and offline deployments.
|
||||
# Caches tiles from upstream Rekor (public Sigstore or private) locally.
|
||||
#
|
||||
# Use Cases:
|
||||
# - Air-gapped deployments with periodic sync
|
||||
# - Reduce latency by caching frequently-accessed tiles
|
||||
# - Offline verification when upstream is unavailable
|
||||
#
|
||||
# Note: This is an ALTERNATIVE to running your own rekor-v2 instance.
|
||||
# Use tile-proxy when you want to cache from public Sigstore.
|
||||
# Use rekor-v2 (--profile sigstore) when running your own transparency log.
|
||||
#
|
||||
# Usage:
|
||||
# docker compose -f docker-compose.stella-ops.yml \
|
||||
# -f docker-compose.tile-proxy.yml up -d
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
x-release-labels: &release-labels
|
||||
com.stellaops.release.version: "2025.10.0"
|
||||
com.stellaops.release.channel: "stable"
|
||||
com.stellaops.component: "tile-proxy"
|
||||
|
||||
volumes:
|
||||
tile-cache:
|
||||
driver: local
|
||||
tuf-cache:
|
||||
driver: local
|
||||
|
||||
services:
|
||||
tile-proxy:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: src/Attestor/StellaOps.Attestor.TileProxy/Dockerfile
|
||||
image: registry.stella-ops.org/stellaops/tile-proxy:2025.10.0
|
||||
container_name: stellaops-tile-proxy
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${TILE_PROXY_PORT:-8090}:8080"
|
||||
volumes:
|
||||
- tile-cache:/var/cache/stellaops/tiles
|
||||
- tuf-cache:/var/cache/stellaops/tuf
|
||||
environment:
|
||||
# Upstream Rekor configuration
|
||||
TILE_PROXY__UPSTREAMURL: "${REKOR_SERVER_URL:-https://rekor.sigstore.dev}"
|
||||
TILE_PROXY__ORIGIN: "${REKOR_ORIGIN:-rekor.sigstore.dev - 1985497715}"
|
||||
|
||||
# TUF configuration (optional - for checkpoint signature validation)
|
||||
TILE_PROXY__TUF__ENABLED: "${TILE_PROXY_TUF_ENABLED:-false}"
|
||||
TILE_PROXY__TUF__URL: "${TILE_PROXY_TUF_ROOT_URL:-}"
|
||||
TILE_PROXY__TUF__VALIDATECHECKPOINTSIGNATURE: "${TILE_PROXY_TUF_VALIDATE_CHECKPOINT:-true}"
|
||||
|
||||
# Cache configuration
|
||||
TILE_PROXY__CACHE__BASEPATH: /var/cache/stellaops/tiles
|
||||
TILE_PROXY__CACHE__MAXSIZEGB: "${TILE_PROXY_CACHE_MAX_SIZE_GB:-10}"
|
||||
TILE_PROXY__CACHE__CHECKPOINTTTLMINUTES: "${TILE_PROXY_CHECKPOINT_TTL_MINUTES:-5}"
|
||||
|
||||
# Sync job configuration (for air-gapped pre-fetching)
|
||||
TILE_PROXY__SYNC__ENABLED: "${TILE_PROXY_SYNC_ENABLED:-true}"
|
||||
TILE_PROXY__SYNC__SCHEDULE: "${TILE_PROXY_SYNC_SCHEDULE:-0 */6 * * *}"
|
||||
TILE_PROXY__SYNC__DEPTH: "${TILE_PROXY_SYNC_DEPTH:-10000}"
|
||||
|
||||
# Request handling
|
||||
TILE_PROXY__REQUEST__COALESCINGENABLED: "${TILE_PROXY_COALESCING_ENABLED:-true}"
|
||||
TILE_PROXY__REQUEST__TIMEOUTSECONDS: "${TILE_PROXY_REQUEST_TIMEOUT_SECONDS:-30}"
|
||||
|
||||
# Logging
|
||||
Serilog__MinimumLevel__Default: "${TILE_PROXY_LOG_LEVEL:-Information}"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/_admin/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
networks:
|
||||
- stellaops
|
||||
labels: *release-labels
|
||||
118
deploy/compose/env/cas.env.example
vendored
Normal file
118
deploy/compose/env/cas.env.example
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
# CAS (Content Addressable Storage) Environment Configuration
|
||||
# Copy to .env and customize for your deployment
|
||||
#
|
||||
# Aligned with best-in-class vulnerability scanner retention policies:
|
||||
# - Trivy: 7 days vulnerability DB
|
||||
# - Grype: 5 days DB, configurable
|
||||
# - Anchore Enterprise: 90-365 days typical
|
||||
# - Snyk Enterprise: 365 days
|
||||
|
||||
# =============================================================================
|
||||
# DATA PATHS (ensure directories exist with proper permissions)
|
||||
# =============================================================================
|
||||
CAS_DATA_PATH=/var/lib/stellaops/cas
|
||||
CAS_EVIDENCE_PATH=/var/lib/stellaops/evidence
|
||||
CAS_ATTESTATION_PATH=/var/lib/stellaops/attestations
|
||||
|
||||
# =============================================================================
|
||||
# RUSTFS CONFIGURATION
|
||||
# =============================================================================
|
||||
RUSTFS_LOG_LEVEL=info
|
||||
RUSTFS_COMPRESSION=zstd
|
||||
RUSTFS_COMPRESSION_LEVEL=3
|
||||
|
||||
# =============================================================================
|
||||
# PORTS
|
||||
# =============================================================================
|
||||
RUSTFS_CAS_PORT=8180
|
||||
RUSTFS_EVIDENCE_PORT=8181
|
||||
RUSTFS_ATTESTATION_PORT=8182
|
||||
|
||||
# =============================================================================
|
||||
# ACCESS CONTROL - API KEYS
|
||||
# IMPORTANT: Change these in production!
|
||||
# =============================================================================
|
||||
|
||||
# CAS Storage (mutable, lifecycle-managed)
|
||||
RUSTFS_CAS_API_KEY=cas-api-key-CHANGE-IN-PRODUCTION
|
||||
RUSTFS_CAS_READONLY_KEY=cas-readonly-key-CHANGE-IN-PRODUCTION
|
||||
|
||||
# Evidence Storage (immutable)
|
||||
RUSTFS_EVIDENCE_API_KEY=evidence-api-key-CHANGE-IN-PRODUCTION
|
||||
RUSTFS_EVIDENCE_READONLY_KEY=evidence-readonly-key-CHANGE-IN-PRODUCTION
|
||||
|
||||
# Attestation Storage (immutable)
|
||||
RUSTFS_ATTESTATION_API_KEY=attestation-api-key-CHANGE-IN-PRODUCTION
|
||||
RUSTFS_ATTESTATION_READONLY_KEY=attestation-readonly-key-CHANGE-IN-PRODUCTION
|
||||
|
||||
# =============================================================================
|
||||
# SERVICE ACCOUNT KEYS
|
||||
# Each service has its own key for fine-grained access control
|
||||
# IMPORTANT: Generate unique keys per environment!
|
||||
# =============================================================================
|
||||
|
||||
# Scanner service - access to scanner artifacts, surface cache, runtime facts
|
||||
RUSTFS_SCANNER_KEY=scanner-svc-key-GENERATE-UNIQUE
|
||||
# Bucket access: scanner-artifacts (rw), surface-cache (rw), runtime-facts (rw)
|
||||
|
||||
# Signals service - access to runtime facts, signals data, provenance feed
|
||||
RUSTFS_SIGNALS_KEY=signals-svc-key-GENERATE-UNIQUE
|
||||
# Bucket access: runtime-facts (rw), signals-data (rw), provenance-feed (rw)
|
||||
|
||||
# Replay service - access to replay bundles, inputs lock files
|
||||
RUSTFS_REPLAY_KEY=replay-svc-key-GENERATE-UNIQUE
|
||||
# Bucket access: replay-bundles (rw), inputs-lock (rw)
|
||||
|
||||
# Ledger service - access to evidence bundles, merkle roots, hash chains
|
||||
RUSTFS_LEDGER_KEY=ledger-svc-key-GENERATE-UNIQUE
|
||||
# Bucket access: evidence-bundles (rw), merkle-roots (rw), hash-chains (rw)
|
||||
|
||||
# Exporter service - read-only access to evidence bundles
|
||||
RUSTFS_EXPORTER_KEY=exporter-svc-key-GENERATE-UNIQUE
|
||||
# Bucket access: evidence-bundles (r)
|
||||
|
||||
# Attestor service - access to attestations, DSSE envelopes, Rekor receipts
|
||||
RUSTFS_ATTESTOR_KEY=attestor-svc-key-GENERATE-UNIQUE
|
||||
# Bucket access: attestations (rw), dsse-envelopes (rw), rekor-receipts (rw)
|
||||
|
||||
# Verifier service - read-only access to attestations
|
||||
RUSTFS_VERIFIER_KEY=verifier-svc-key-GENERATE-UNIQUE
|
||||
# Bucket access: attestations (r), dsse-envelopes (r), rekor-receipts (r)
|
||||
|
||||
# Global read-only key (for debugging/auditing)
|
||||
RUSTFS_READONLY_KEY=readonly-global-key-GENERATE-UNIQUE
|
||||
# Bucket access: * (r)
|
||||
|
||||
# =============================================================================
|
||||
# LIFECYCLE MANAGEMENT
|
||||
# =============================================================================
|
||||
# Cron schedule for retention policy enforcement (default: 3 AM daily)
|
||||
LIFECYCLE_CRON=0 3 * * *
|
||||
LIFECYCLE_TELEMETRY=true
|
||||
|
||||
# =============================================================================
|
||||
# RETENTION POLICIES (days, 0 = indefinite)
|
||||
# Aligned with enterprise vulnerability scanner best practices
|
||||
# =============================================================================
|
||||
# Vulnerability DB: 7 days (matches Trivy default, Grype uses 5)
|
||||
CAS_RETENTION_VULNERABILITY_DB_DAYS=7
|
||||
|
||||
# SBOM artifacts: 365 days (audit compliance - SOC2, ISO27001, FedRAMP)
|
||||
CAS_RETENTION_SBOM_ARTIFACTS_DAYS=365
|
||||
|
||||
# Scan results: 90 days (common compliance window)
|
||||
CAS_RETENTION_SCAN_RESULTS_DAYS=90
|
||||
|
||||
# Evidence bundles: indefinite (content-addressed, immutable, audit trail)
|
||||
CAS_RETENTION_EVIDENCE_BUNDLES_DAYS=0
|
||||
|
||||
# Attestations: indefinite (signed, immutable, verifiable)
|
||||
CAS_RETENTION_ATTESTATIONS_DAYS=0
|
||||
|
||||
# Temporary artifacts: 1 day (work-in-progress, intermediate files)
|
||||
CAS_RETENTION_TEMP_ARTIFACTS_DAYS=1
|
||||
|
||||
# =============================================================================
|
||||
# TELEMETRY (optional)
|
||||
# =============================================================================
|
||||
OTLP_ENDPOINT=
|
||||
48
deploy/compose/env/compliance-china.env.example
vendored
Normal file
48
deploy/compose/env/compliance-china.env.example
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
# =============================================================================
|
||||
# STELLA OPS CHINA COMPLIANCE ENVIRONMENT
|
||||
# =============================================================================
|
||||
# Environment template for China (SM2/SM3/SM4) compliance deployments.
|
||||
#
|
||||
# Usage with simulation:
|
||||
# cp env/compliance-china.env.example .env
|
||||
# docker compose -f docker-compose.stella-ops.yml \
|
||||
# -f docker-compose.compliance-china.yml \
|
||||
# -f docker-compose.crypto-sim.yml up -d
|
||||
#
|
||||
# Usage with SM Remote (production):
|
||||
# docker compose -f docker-compose.stella-ops.yml \
|
||||
# -f docker-compose.compliance-china.yml \
|
||||
# -f docker-compose.sm-remote.yml up -d
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
# Crypto profile
|
||||
STELLAOPS_CRYPTO_PROFILE=china
|
||||
|
||||
# =============================================================================
|
||||
# SM REMOTE SERVICE CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
SM_REMOTE_PORT=56080
|
||||
|
||||
# Software-only SM2 provider (for testing/development)
|
||||
SM_SOFT_ALLOWED=1
|
||||
|
||||
# OSCCA-certified HSM configuration (for production)
|
||||
# Set these when using a certified hardware security module
|
||||
SM_REMOTE_HSM_URL=
|
||||
SM_REMOTE_HSM_API_KEY=
|
||||
SM_REMOTE_HSM_TIMEOUT=30000
|
||||
|
||||
# Client certificate authentication for HSM (optional)
|
||||
SM_REMOTE_CLIENT_CERT_PATH=
|
||||
SM_REMOTE_CLIENT_CERT_PASSWORD=
|
||||
|
||||
# =============================================================================
|
||||
# CRYPTO SIMULATION (for testing only)
|
||||
# =============================================================================
|
||||
|
||||
# Enable simulation mode
|
||||
STELLAOPS_CRYPTO_ENABLE_SIM=1
|
||||
STELLAOPS_CRYPTO_SIM_URL=http://sim-crypto:8080
|
||||
SIM_CRYPTO_PORT=18090
|
||||
40
deploy/compose/env/compliance-eu.env.example
vendored
Normal file
40
deploy/compose/env/compliance-eu.env.example
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
# =============================================================================
|
||||
# STELLA OPS EU COMPLIANCE ENVIRONMENT
|
||||
# =============================================================================
|
||||
# Environment template for EU (eIDAS) compliance deployments.
|
||||
#
|
||||
# Usage with simulation:
|
||||
# cp env/compliance-eu.env.example .env
|
||||
# docker compose -f docker-compose.stella-ops.yml \
|
||||
# -f docker-compose.compliance-eu.yml \
|
||||
# -f docker-compose.crypto-sim.yml up -d
|
||||
#
|
||||
# Usage for production:
|
||||
# docker compose -f docker-compose.stella-ops.yml \
|
||||
# -f docker-compose.compliance-eu.yml up -d
|
||||
#
|
||||
# Note: EU eIDAS deployments typically integrate with external Qualified Trust
|
||||
# Service Providers (QTSPs) rather than hosting crypto locally.
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
# Crypto profile
|
||||
STELLAOPS_CRYPTO_PROFILE=eu
|
||||
|
||||
# =============================================================================
|
||||
# eIDAS / QTSP CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
# Qualified Trust Service Provider integration (configure in application settings)
|
||||
# EIDAS_QTSP_URL=https://qtsp.example.eu
|
||||
# EIDAS_QTSP_CLIENT_ID=
|
||||
# EIDAS_QTSP_CLIENT_SECRET=
|
||||
|
||||
# =============================================================================
|
||||
# CRYPTO SIMULATION (for testing only)
|
||||
# =============================================================================
|
||||
|
||||
# Enable simulation mode
|
||||
STELLAOPS_CRYPTO_ENABLE_SIM=1
|
||||
STELLAOPS_CRYPTO_SIM_URL=http://sim-crypto:8080
|
||||
SIM_CRYPTO_PORT=18090
|
||||
51
deploy/compose/env/compliance-russia.env.example
vendored
Normal file
51
deploy/compose/env/compliance-russia.env.example
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
# =============================================================================
|
||||
# STELLA OPS RUSSIA COMPLIANCE ENVIRONMENT
|
||||
# =============================================================================
|
||||
# Environment template for Russia (GOST R 34.10-2012) compliance deployments.
|
||||
#
|
||||
# Usage with simulation:
|
||||
# cp env/compliance-russia.env.example .env
|
||||
# docker compose -f docker-compose.stella-ops.yml \
|
||||
# -f docker-compose.compliance-russia.yml \
|
||||
# -f docker-compose.crypto-sim.yml up -d
|
||||
#
|
||||
# Usage with CryptoPro CSP (production):
|
||||
# CRYPTOPRO_ACCEPT_EULA=1 docker compose -f docker-compose.stella-ops.yml \
|
||||
# -f docker-compose.compliance-russia.yml \
|
||||
# -f docker-compose.cryptopro.yml up -d
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
# Crypto profile
|
||||
STELLAOPS_CRYPTO_PROFILE=russia
|
||||
|
||||
# =============================================================================
|
||||
# CRYPTOPRO CSP CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
CRYPTOPRO_PORT=18080
|
||||
|
||||
# IMPORTANT: Set to 1 to accept CryptoPro EULA (required for production)
|
||||
CRYPTOPRO_ACCEPT_EULA=0
|
||||
|
||||
# CryptoPro container settings
|
||||
CRYPTOPRO_CONTAINER_NAME=stellaops-signing
|
||||
CRYPTOPRO_USE_MACHINE_STORE=true
|
||||
CRYPTOPRO_PROVIDER_TYPE=80
|
||||
|
||||
# =============================================================================
|
||||
# GOST ALGORITHM CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
# Default GOST algorithms
|
||||
CRYPTOPRO_GOST_SIGNATURE_ALGORITHM=GOST R 34.10-2012
|
||||
CRYPTOPRO_GOST_HASH_ALGORITHM=GOST R 34.11-2012
|
||||
|
||||
# =============================================================================
|
||||
# CRYPTO SIMULATION (for testing only)
|
||||
# =============================================================================
|
||||
|
||||
# Enable simulation mode
|
||||
STELLAOPS_CRYPTO_ENABLE_SIM=1
|
||||
STELLAOPS_CRYPTO_SIM_URL=http://sim-crypto:8080
|
||||
SIM_CRYPTO_PORT=18090
|
||||
171
deploy/compose/env/stellaops.env.example
vendored
Normal file
171
deploy/compose/env/stellaops.env.example
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
# =============================================================================
|
||||
# STELLA OPS ENVIRONMENT CONFIGURATION
|
||||
# =============================================================================
|
||||
# Main environment template for docker-compose.stella-ops.yml
|
||||
# Copy to .env and customize for your deployment.
|
||||
#
|
||||
# Usage:
|
||||
# cp env/stellaops.env.example .env
|
||||
# docker compose -f docker-compose.stella-ops.yml up -d
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
# =============================================================================
|
||||
# INFRASTRUCTURE
|
||||
# =============================================================================
|
||||
|
||||
# PostgreSQL Database
|
||||
POSTGRES_USER=stellaops
|
||||
POSTGRES_PASSWORD=REPLACE_WITH_STRONG_PASSWORD
|
||||
POSTGRES_DB=stellaops_platform
|
||||
POSTGRES_PORT=5432
|
||||
|
||||
# Valkey (Redis-compatible cache and messaging)
|
||||
VALKEY_PORT=6379
|
||||
|
||||
# RustFS Object Storage
|
||||
RUSTFS_HTTP_PORT=8080
|
||||
|
||||
# =============================================================================
|
||||
# CORE SERVICES
|
||||
# =============================================================================
|
||||
|
||||
# Authority (OAuth2/OIDC)
|
||||
AUTHORITY_ISSUER=https://authority.example.com
|
||||
AUTHORITY_PORT=8440
|
||||
AUTHORITY_OFFLINE_CACHE_TOLERANCE=00:30:00
|
||||
|
||||
# Signer
|
||||
SIGNER_POE_INTROSPECT_URL=https://licensing.example.com/introspect
|
||||
SIGNER_PORT=8441
|
||||
|
||||
# Attestor
|
||||
ATTESTOR_PORT=8442
|
||||
|
||||
# Issuer Directory
|
||||
ISSUER_DIRECTORY_PORT=8447
|
||||
ISSUER_DIRECTORY_SEED_CSAF=true
|
||||
|
||||
# Concelier
|
||||
CONCELIER_PORT=8445
|
||||
|
||||
# Notify
|
||||
NOTIFY_WEB_PORT=8446
|
||||
|
||||
# Web UI
|
||||
UI_PORT=8443
|
||||
|
||||
# =============================================================================
|
||||
# SCANNER CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
SCANNER_WEB_PORT=8444
|
||||
|
||||
# Queue configuration (Valkey only - NATS removed)
|
||||
SCANNER__QUEUE__BROKER=valkey://valkey:6379
|
||||
|
||||
# Event streaming
|
||||
SCANNER_EVENTS_ENABLED=false
|
||||
SCANNER_EVENTS_DRIVER=valkey
|
||||
SCANNER_EVENTS_DSN=valkey:6379
|
||||
SCANNER_EVENTS_STREAM=stella.events
|
||||
SCANNER_EVENTS_PUBLISH_TIMEOUT_SECONDS=5
|
||||
SCANNER_EVENTS_MAX_STREAM_LENGTH=10000
|
||||
|
||||
# Surface cache configuration
|
||||
SCANNER_SURFACE_FS_ENDPOINT=http://rustfs:8080
|
||||
SCANNER_SURFACE_FS_BUCKET=surface-cache
|
||||
SCANNER_SURFACE_CACHE_ROOT=/var/lib/stellaops/surface
|
||||
SCANNER_SURFACE_CACHE_QUOTA_MB=4096
|
||||
SCANNER_SURFACE_PREFETCH_ENABLED=false
|
||||
SCANNER_SURFACE_TENANT=default
|
||||
SCANNER_SURFACE_FEATURES=
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER=file
|
||||
SCANNER_SURFACE_SECRETS_NAMESPACE=
|
||||
SCANNER_SURFACE_SECRETS_ROOT=/etc/stellaops/secrets
|
||||
SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER=
|
||||
SCANNER_SURFACE_SECRETS_ALLOW_INLINE=false
|
||||
SURFACE_SECRETS_HOST_PATH=./offline/surface-secrets
|
||||
|
||||
# Offline Kit configuration
|
||||
SCANNER_OFFLINEKIT_ENABLED=false
|
||||
SCANNER_OFFLINEKIT_REQUIREDSSE=true
|
||||
SCANNER_OFFLINEKIT_REKOROFFLINEMODE=true
|
||||
SCANNER_OFFLINEKIT_TRUSTROOTDIRECTORY=/etc/stellaops/trust-roots
|
||||
SCANNER_OFFLINEKIT_REKORSNAPSHOTDIRECTORY=/var/lib/stellaops/rekor-snapshot
|
||||
SCANNER_OFFLINEKIT_TRUSTROOTS_HOST_PATH=./offline/trust-roots
|
||||
SCANNER_OFFLINEKIT_REKOR_SNAPSHOT_HOST_PATH=./offline/rekor-snapshot
|
||||
|
||||
# =============================================================================
|
||||
# SCHEDULER CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
# Queue configuration (Valkey only - NATS removed)
|
||||
SCHEDULER__QUEUE__KIND=Valkey
|
||||
SCHEDULER__QUEUE__VALKEY__URL=valkey:6379
|
||||
SCHEDULER_SCANNER_BASEADDRESS=http://scanner-web:8444
|
||||
|
||||
# =============================================================================
|
||||
# REKOR / SIGSTORE CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
# Rekor server URL (default: public Sigstore, use http://rekor-v2:3000 for local)
|
||||
REKOR_SERVER_URL=https://rekor.sigstore.dev
|
||||
REKOR_VERSION=V2
|
||||
REKOR_TILE_BASE_URL=
|
||||
REKOR_LOG_ID=c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d
|
||||
REKOR_TILES_IMAGE=ghcr.io/sigstore/rekor-tiles:latest
|
||||
|
||||
# =============================================================================
|
||||
# ADVISORY AI CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
ADVISORY_AI_WEB_PORT=8448
|
||||
ADVISORY_AI_SBOM_BASEADDRESS=http://scanner-web:8444
|
||||
ADVISORY_AI_INFERENCE_MODE=Local
|
||||
ADVISORY_AI_REMOTE_BASEADDRESS=
|
||||
ADVISORY_AI_REMOTE_APIKEY=
|
||||
|
||||
# =============================================================================
|
||||
# CRYPTO CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
# Crypto profile: default, china, russia, eu
|
||||
STELLAOPS_CRYPTO_PROFILE=default
|
||||
|
||||
# Enable crypto simulation (for testing)
|
||||
STELLAOPS_CRYPTO_ENABLE_SIM=0
|
||||
STELLAOPS_CRYPTO_SIM_URL=http://sim-crypto:8080
|
||||
|
||||
# CryptoPro (Russia only) - requires EULA acceptance
|
||||
CRYPTOPRO_PORT=18080
|
||||
CRYPTOPRO_ACCEPT_EULA=0
|
||||
CRYPTOPRO_CONTAINER_NAME=stellaops-signing
|
||||
CRYPTOPRO_USE_MACHINE_STORE=true
|
||||
CRYPTOPRO_PROVIDER_TYPE=80
|
||||
|
||||
# SM Remote (China only)
|
||||
SM_REMOTE_PORT=56080
|
||||
SM_SOFT_ALLOWED=1
|
||||
SM_REMOTE_HSM_URL=
|
||||
SM_REMOTE_HSM_API_KEY=
|
||||
SM_REMOTE_HSM_TIMEOUT=30000
|
||||
|
||||
# =============================================================================
|
||||
# NETWORKING
|
||||
# =============================================================================
|
||||
|
||||
# External reverse proxy network (Traefik, Envoy, etc.)
|
||||
FRONTDOOR_NETWORK=stellaops_frontdoor
|
||||
|
||||
# =============================================================================
|
||||
# TELEMETRY (optional)
|
||||
# =============================================================================
|
||||
|
||||
OTEL_GRPC_PORT=4317
|
||||
OTEL_HTTP_PORT=4318
|
||||
OTEL_PROMETHEUS_PORT=9464
|
||||
PROMETHEUS_PORT=9090
|
||||
TEMPO_PORT=3200
|
||||
LOKI_PORT=3100
|
||||
PROMETHEUS_RETENTION=15d
|
||||
45
deploy/compose/env/testing.env.example
vendored
Normal file
45
deploy/compose/env/testing.env.example
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
# =============================================================================
|
||||
# STELLA OPS TESTING ENVIRONMENT CONFIGURATION
|
||||
# =============================================================================
|
||||
# Environment template for docker-compose.testing.yml
|
||||
# Uses different ports to avoid conflicts with development/production.
|
||||
#
|
||||
# Usage:
|
||||
# cp env/testing.env.example .env
|
||||
# docker compose -f docker-compose.testing.yml --profile ci up -d
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
# =============================================================================
|
||||
# CI INFRASTRUCTURE (different ports to avoid conflicts)
|
||||
# =============================================================================
|
||||
|
||||
# PostgreSQL Test Database (port 5433)
|
||||
TEST_POSTGRES_PORT=5433
|
||||
TEST_POSTGRES_USER=stellaops_ci
|
||||
TEST_POSTGRES_PASSWORD=ci_test_password
|
||||
TEST_POSTGRES_DB=stellaops_test
|
||||
|
||||
# Valkey Test (port 6380)
|
||||
TEST_VALKEY_PORT=6380
|
||||
|
||||
# RustFS Test (port 8180)
|
||||
TEST_RUSTFS_PORT=8180
|
||||
|
||||
# Mock Registry (port 5001)
|
||||
TEST_REGISTRY_PORT=5001
|
||||
|
||||
# =============================================================================
|
||||
# GITEA CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
TEST_GITEA_PORT=3000
|
||||
TEST_GITEA_SSH_PORT=3022
|
||||
|
||||
# =============================================================================
|
||||
# SIGSTORE TOOLS
|
||||
# =============================================================================
|
||||
|
||||
# Rekor CLI and Cosign versions (for sigstore profile)
|
||||
REKOR_CLI_VERSION=v1.4.3
|
||||
COSIGN_VERSION=v3.0.4
|
||||
28
deploy/compose/scripts/backup.sh
Normal file
28
deploy/compose/scripts/backup.sh
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "StellaOps Compose Backup"
|
||||
echo "This will create a tar.gz of PostgreSQL, RustFS (object-store), and Valkey data volumes."
|
||||
read -rp "Proceed? [y/N] " ans
|
||||
[[ ${ans:-N} =~ ^[Yy]$ ]] || { echo "Aborted."; exit 1; }
|
||||
|
||||
TS=$(date -u +%Y%m%dT%H%M%SZ)
|
||||
OUT_DIR=${BACKUP_DIR:-backups}
|
||||
mkdir -p "$OUT_DIR"
|
||||
|
||||
docker compose ps >/dev/null
|
||||
|
||||
echo "Pausing worker containers for consistency..."
|
||||
docker compose pause scanner-worker scheduler-worker taskrunner-worker || true
|
||||
|
||||
echo "Backing up volumes..."
|
||||
docker run --rm \
|
||||
-v stellaops-postgres:/data/postgres:ro \
|
||||
-v stellaops-rustfs:/data/rustfs:ro \
|
||||
-v stellaops-valkey:/data/valkey:ro \
|
||||
-v "$PWD/$OUT_DIR":/out \
|
||||
alpine sh -c "cd / && tar czf /out/stellaops-backup-$TS.tar.gz data"
|
||||
|
||||
docker compose unpause scanner-worker scheduler-worker taskrunner-worker || true
|
||||
|
||||
echo "Backup written to $OUT_DIR/stellaops-backup-$TS.tar.gz"
|
||||
25
deploy/compose/scripts/quickstart.sh
Normal file
25
deploy/compose/scripts/quickstart.sh
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
COMPOSE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
ENV_FILE="${1:-$COMPOSE_DIR/env/dev.env.example}"
|
||||
USE_MOCK="${USE_MOCK:-0}"
|
||||
|
||||
FILES=(-f "$COMPOSE_DIR/docker-compose.dev.yaml")
|
||||
ENV_FILES=(--env-file "$ENV_FILE")
|
||||
|
||||
if [[ "$USE_MOCK" == "1" ]]; then
|
||||
FILES+=(-f "$COMPOSE_DIR/docker-compose.mock.yaml")
|
||||
ENV_FILES+=(--env-file "$COMPOSE_DIR/env/mock.env.example")
|
||||
fi
|
||||
|
||||
echo "Validating compose config..."
|
||||
docker compose "${ENV_FILES[@]}" "${FILES[@]}" config > /tmp/compose-validated.yaml
|
||||
echo "Config written to /tmp/compose-validated.yaml"
|
||||
|
||||
echo "Starting stack..."
|
||||
docker compose "${ENV_FILES[@]}" "${FILES[@]}" up -d
|
||||
|
||||
echo "Stack started. To stop: docker compose ${ENV_FILES[*]} ${FILES[*]} down"
|
||||
15
deploy/compose/scripts/reset.sh
Normal file
15
deploy/compose/scripts/reset.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "WARNING: This will stop the stack and wipe PostgreSQL, RustFS, and Valkey volumes."
|
||||
read -rp "Type 'RESET' to continue: " ans
|
||||
[[ ${ans:-} == "RESET" ]] || { echo "Aborted."; exit 1; }
|
||||
|
||||
docker compose down
|
||||
|
||||
for vol in stellaops-postgres stellaops-rustfs stellaops-valkey; do
|
||||
echo "Removing volume $vol"
|
||||
docker volume rm "$vol" || true
|
||||
done
|
||||
|
||||
echo "Reset complete. Re-run compose with your env file to recreate volumes."
|
||||
69
deploy/database/migrations/005_timestamp_evidence.sql
Normal file
69
deploy/database/migrations/005_timestamp_evidence.sql
Normal file
@@ -0,0 +1,69 @@
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- 005_timestamp_evidence.sql
|
||||
-- Sprint: SPRINT_20260119_009 Evidence Storage for Timestamps
|
||||
-- Task: EVT-002 - PostgreSQL Schema Extension
|
||||
-- Description: Schema for storing timestamp and revocation evidence.
|
||||
-- -----------------------------------------------------------------------------
|
||||
|
||||
-- Ensure the evidence schema exists
|
||||
CREATE SCHEMA IF NOT EXISTS evidence;
|
||||
|
||||
-- Timestamp evidence storage
|
||||
CREATE TABLE IF NOT EXISTS evidence.timestamp_tokens (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
artifact_digest TEXT NOT NULL,
|
||||
digest_algorithm TEXT NOT NULL,
|
||||
tst_blob BYTEA NOT NULL,
|
||||
generation_time TIMESTAMPTZ NOT NULL,
|
||||
tsa_name TEXT NOT NULL,
|
||||
tsa_policy_oid TEXT NOT NULL,
|
||||
serial_number TEXT NOT NULL,
|
||||
tsa_chain_pem TEXT NOT NULL,
|
||||
ocsp_response BYTEA,
|
||||
crl_snapshot BYTEA,
|
||||
captured_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
provider_name TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT uq_timestamp_artifact_time UNIQUE (artifact_digest, generation_time)
|
||||
);
|
||||
|
||||
-- Indexes for timestamp queries
|
||||
CREATE INDEX IF NOT EXISTS idx_timestamp_artifact ON evidence.timestamp_tokens(artifact_digest);
|
||||
CREATE INDEX IF NOT EXISTS idx_timestamp_generation ON evidence.timestamp_tokens(generation_time);
|
||||
CREATE INDEX IF NOT EXISTS idx_timestamp_provider ON evidence.timestamp_tokens(provider_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_timestamp_created ON evidence.timestamp_tokens(created_at);
|
||||
|
||||
-- Revocation evidence storage
|
||||
CREATE TABLE IF NOT EXISTS evidence.revocation_snapshots (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
certificate_fingerprint TEXT NOT NULL,
|
||||
source TEXT NOT NULL CHECK (source IN ('Ocsp', 'Crl', 'None')),
|
||||
raw_response BYTEA NOT NULL,
|
||||
response_time TIMESTAMPTZ NOT NULL,
|
||||
valid_until TIMESTAMPTZ NOT NULL,
|
||||
status TEXT NOT NULL CHECK (status IN ('Good', 'Revoked', 'Unknown')),
|
||||
revocation_time TIMESTAMPTZ,
|
||||
reason TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Indexes for revocation queries
|
||||
CREATE INDEX IF NOT EXISTS idx_revocation_cert ON evidence.revocation_snapshots(certificate_fingerprint);
|
||||
CREATE INDEX IF NOT EXISTS idx_revocation_valid ON evidence.revocation_snapshots(valid_until);
|
||||
CREATE INDEX IF NOT EXISTS idx_revocation_status ON evidence.revocation_snapshots(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_revocation_created ON evidence.revocation_snapshots(created_at);
|
||||
|
||||
-- Comments
|
||||
COMMENT ON TABLE evidence.timestamp_tokens IS 'RFC-3161 TimeStampToken evidence for long-term validation';
|
||||
COMMENT ON TABLE evidence.revocation_snapshots IS 'OCSP/CRL certificate revocation evidence snapshots';
|
||||
|
||||
COMMENT ON COLUMN evidence.timestamp_tokens.artifact_digest IS 'SHA-256 digest of the timestamped artifact';
|
||||
COMMENT ON COLUMN evidence.timestamp_tokens.tst_blob IS 'Raw DER-encoded RFC 3161 TimeStampToken';
|
||||
COMMENT ON COLUMN evidence.timestamp_tokens.tsa_chain_pem IS 'PEM-encoded TSA certificate chain for LTV';
|
||||
COMMENT ON COLUMN evidence.timestamp_tokens.ocsp_response IS 'Stapled OCSP response at signing time';
|
||||
COMMENT ON COLUMN evidence.timestamp_tokens.crl_snapshot IS 'CRL snapshot at signing time (fallback for OCSP)';
|
||||
|
||||
COMMENT ON COLUMN evidence.revocation_snapshots.certificate_fingerprint IS 'SHA-256 fingerprint of the certificate';
|
||||
COMMENT ON COLUMN evidence.revocation_snapshots.raw_response IS 'Raw OCSP response or CRL bytes';
|
||||
COMMENT ON COLUMN evidence.revocation_snapshots.response_time IS 'thisUpdate from the response';
|
||||
COMMENT ON COLUMN evidence.revocation_snapshots.valid_until IS 'nextUpdate from the response';
|
||||
@@ -0,0 +1,21 @@
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- 005_timestamp_evidence_rollback.sql
|
||||
-- Sprint: SPRINT_20260119_009 Evidence Storage for Timestamps
|
||||
-- Task: EVT-002 - PostgreSQL Schema Extension
|
||||
-- Description: Rollback migration for timestamp and revocation evidence.
|
||||
-- -----------------------------------------------------------------------------
|
||||
|
||||
-- Drop indexes first
|
||||
DROP INDEX IF EXISTS evidence.idx_timestamp_artifact;
|
||||
DROP INDEX IF EXISTS evidence.idx_timestamp_generation;
|
||||
DROP INDEX IF EXISTS evidence.idx_timestamp_provider;
|
||||
DROP INDEX IF EXISTS evidence.idx_timestamp_created;
|
||||
|
||||
DROP INDEX IF EXISTS evidence.idx_revocation_cert;
|
||||
DROP INDEX IF EXISTS evidence.idx_revocation_valid;
|
||||
DROP INDEX IF EXISTS evidence.idx_revocation_status;
|
||||
DROP INDEX IF EXISTS evidence.idx_revocation_created;
|
||||
|
||||
-- Drop tables
|
||||
DROP TABLE IF EXISTS evidence.revocation_snapshots;
|
||||
DROP TABLE IF EXISTS evidence.timestamp_tokens;
|
||||
120
deploy/database/migrations/005_validation_harness.sql
Normal file
120
deploy/database/migrations/005_validation_harness.sql
Normal file
@@ -0,0 +1,120 @@
|
||||
-- Validation harness schema for tracking validation runs and match results
|
||||
-- Migration: 005_validation_harness.sql
|
||||
|
||||
-- Validation runs table
|
||||
CREATE TABLE IF NOT EXISTS groundtruth.validation_runs (
|
||||
run_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
|
||||
-- Configuration (stored as JSONB)
|
||||
config JSONB NOT NULL,
|
||||
|
||||
-- Timestamps
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ,
|
||||
|
||||
-- Metrics (populated after completion)
|
||||
total_pairs INT,
|
||||
total_functions INT,
|
||||
true_positives INT,
|
||||
false_positives INT,
|
||||
true_negatives INT,
|
||||
false_negatives INT,
|
||||
match_rate DOUBLE PRECISION,
|
||||
precision_score DOUBLE PRECISION,
|
||||
recall_score DOUBLE PRECISION,
|
||||
f1_score DOUBLE PRECISION,
|
||||
average_match_score DOUBLE PRECISION,
|
||||
|
||||
-- Mismatch counts by bucket (JSONB map)
|
||||
mismatch_counts JSONB,
|
||||
|
||||
-- Metadata
|
||||
corpus_snapshot_id TEXT,
|
||||
matcher_version TEXT,
|
||||
error_message TEXT,
|
||||
tags TEXT[] DEFAULT '{}',
|
||||
|
||||
-- Constraints
|
||||
CONSTRAINT valid_status CHECK (status IN ('pending', 'running', 'completed', 'failed', 'cancelled'))
|
||||
);
|
||||
|
||||
-- Indexes for validation runs
|
||||
CREATE INDEX IF NOT EXISTS idx_validation_runs_status ON groundtruth.validation_runs(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_validation_runs_created_at ON groundtruth.validation_runs(created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_validation_runs_tags ON groundtruth.validation_runs USING GIN (tags);
|
||||
|
||||
-- Match results table
|
||||
CREATE TABLE IF NOT EXISTS groundtruth.match_results (
|
||||
result_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
run_id UUID NOT NULL REFERENCES groundtruth.validation_runs(run_id) ON DELETE CASCADE,
|
||||
security_pair_id UUID NOT NULL,
|
||||
|
||||
-- Source function
|
||||
source_name TEXT NOT NULL,
|
||||
source_demangled_name TEXT,
|
||||
source_address BIGINT NOT NULL,
|
||||
source_size BIGINT,
|
||||
source_build_id TEXT NOT NULL,
|
||||
source_binary_name TEXT NOT NULL,
|
||||
|
||||
-- Expected target
|
||||
expected_name TEXT NOT NULL,
|
||||
expected_demangled_name TEXT,
|
||||
expected_address BIGINT NOT NULL,
|
||||
expected_size BIGINT,
|
||||
expected_build_id TEXT NOT NULL,
|
||||
expected_binary_name TEXT NOT NULL,
|
||||
|
||||
-- Actual matched target (nullable if no match found)
|
||||
actual_name TEXT,
|
||||
actual_demangled_name TEXT,
|
||||
actual_address BIGINT,
|
||||
actual_size BIGINT,
|
||||
actual_build_id TEXT,
|
||||
actual_binary_name TEXT,
|
||||
|
||||
-- Outcome
|
||||
outcome TEXT NOT NULL,
|
||||
match_score DOUBLE PRECISION,
|
||||
confidence TEXT,
|
||||
|
||||
-- Mismatch analysis
|
||||
inferred_cause TEXT,
|
||||
mismatch_detail JSONB,
|
||||
|
||||
-- Performance
|
||||
match_duration_ms DOUBLE PRECISION,
|
||||
|
||||
-- Constraints
|
||||
CONSTRAINT valid_outcome CHECK (outcome IN ('true_positive', 'false_positive', 'true_negative', 'false_negative'))
|
||||
);
|
||||
|
||||
-- Indexes for match results
|
||||
CREATE INDEX IF NOT EXISTS idx_match_results_run_id ON groundtruth.match_results(run_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_match_results_security_pair_id ON groundtruth.match_results(security_pair_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_match_results_outcome ON groundtruth.match_results(outcome);
|
||||
CREATE INDEX IF NOT EXISTS idx_match_results_inferred_cause ON groundtruth.match_results(inferred_cause) WHERE inferred_cause IS NOT NULL;
|
||||
|
||||
-- View for run summaries
|
||||
CREATE OR REPLACE VIEW groundtruth.validation_run_summaries AS
|
||||
SELECT
|
||||
run_id AS id,
|
||||
name,
|
||||
status,
|
||||
created_at,
|
||||
completed_at,
|
||||
match_rate,
|
||||
f1_score,
|
||||
total_pairs AS pair_count,
|
||||
total_functions AS function_count,
|
||||
tags
|
||||
FROM groundtruth.validation_runs;
|
||||
|
||||
-- Comments
|
||||
COMMENT ON TABLE groundtruth.validation_runs IS 'Validation harness runs with aggregate metrics';
|
||||
COMMENT ON TABLE groundtruth.match_results IS 'Per-function match results from validation runs';
|
||||
COMMENT ON VIEW groundtruth.validation_run_summaries IS 'Summary view for listing validation runs';
|
||||
27
deploy/database/migrations/006_timestamp_supersession.sql
Normal file
27
deploy/database/migrations/006_timestamp_supersession.sql
Normal file
@@ -0,0 +1,27 @@
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- 006_timestamp_supersession.sql
|
||||
-- Sprint: SPRINT_20260119_009 Evidence Storage for Timestamps
|
||||
-- Task: EVT-005 - Re-Timestamping Support
|
||||
-- Description: Schema extension for timestamp supersession chain.
|
||||
-- -----------------------------------------------------------------------------
|
||||
|
||||
-- Add supersession column for re-timestamping chain
|
||||
ALTER TABLE evidence.timestamp_tokens
|
||||
ADD COLUMN IF NOT EXISTS supersedes_id UUID REFERENCES evidence.timestamp_tokens(id);
|
||||
|
||||
-- Index for finding superseding timestamps
|
||||
CREATE INDEX IF NOT EXISTS idx_timestamp_supersedes ON evidence.timestamp_tokens(supersedes_id);
|
||||
|
||||
-- Index for finding timestamps by expiry (for re-timestamp scheduling)
|
||||
-- Note: We need to track TSA certificate expiry separately - for now use generation_time + typical cert lifetime
|
||||
CREATE INDEX IF NOT EXISTS idx_timestamp_for_retimestamp
|
||||
ON evidence.timestamp_tokens(generation_time)
|
||||
WHERE supersedes_id IS NULL; -- Only query leaf timestamps (not already superseded)
|
||||
|
||||
-- Comments
|
||||
COMMENT ON COLUMN evidence.timestamp_tokens.supersedes_id IS 'ID of the timestamp this supersedes (for re-timestamping chain)';
|
||||
|
||||
-- Rollback script (execute separately if needed):
|
||||
-- ALTER TABLE evidence.timestamp_tokens DROP COLUMN IF EXISTS supersedes_id;
|
||||
-- DROP INDEX IF EXISTS evidence.idx_timestamp_supersedes;
|
||||
-- DROP INDEX IF EXISTS evidence.idx_timestamp_for_retimestamp;
|
||||
@@ -0,0 +1,108 @@
|
||||
-- OpsMemory and AdvisoryAI PostgreSQL Schema Migration
|
||||
-- Version: 20260108
|
||||
-- Author: StellaOps Agent
|
||||
-- Sprint: SPRINT_20260107_006_004 (OpsMemory), SPRINT_20260107_006_003 (AdvisoryAI)
|
||||
|
||||
-- ============================================================================
|
||||
-- OpsMemory Schema
|
||||
-- ============================================================================
|
||||
|
||||
CREATE SCHEMA IF NOT EXISTS opsmemory;
|
||||
|
||||
-- Decision records table
|
||||
CREATE TABLE IF NOT EXISTS opsmemory.decisions (
|
||||
memory_id TEXT PRIMARY KEY,
|
||||
tenant_id TEXT NOT NULL,
|
||||
recorded_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
|
||||
-- Situation context
|
||||
cve_id TEXT,
|
||||
component_purl TEXT,
|
||||
severity TEXT,
|
||||
reachability TEXT,
|
||||
epss_score DECIMAL(5, 4),
|
||||
cvss_score DECIMAL(3, 1),
|
||||
context_tags TEXT[],
|
||||
similarity_vector DOUBLE PRECISION[],
|
||||
|
||||
-- Decision details
|
||||
action TEXT NOT NULL,
|
||||
rationale TEXT,
|
||||
decided_by TEXT NOT NULL,
|
||||
policy_reference TEXT,
|
||||
mitigation_type TEXT,
|
||||
mitigation_details TEXT,
|
||||
|
||||
-- Outcome (nullable until recorded)
|
||||
outcome_status TEXT,
|
||||
resolution_time INTERVAL,
|
||||
actual_impact TEXT,
|
||||
lessons_learned TEXT,
|
||||
outcome_recorded_by TEXT,
|
||||
outcome_recorded_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
-- Indexes for querying
|
||||
CREATE INDEX IF NOT EXISTS idx_opsmemory_decisions_tenant ON opsmemory.decisions(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_opsmemory_decisions_cve ON opsmemory.decisions(cve_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_opsmemory_decisions_component ON opsmemory.decisions(component_purl);
|
||||
CREATE INDEX IF NOT EXISTS idx_opsmemory_decisions_recorded ON opsmemory.decisions(recorded_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_opsmemory_decisions_action ON opsmemory.decisions(action);
|
||||
CREATE INDEX IF NOT EXISTS idx_opsmemory_decisions_outcome ON opsmemory.decisions(outcome_status);
|
||||
|
||||
-- ============================================================================
|
||||
-- AdvisoryAI Schema
|
||||
-- ============================================================================
|
||||
|
||||
CREATE SCHEMA IF NOT EXISTS advisoryai;
|
||||
|
||||
-- Conversations table
|
||||
CREATE TABLE IF NOT EXISTS advisoryai.conversations (
|
||||
conversation_id TEXT PRIMARY KEY,
|
||||
tenant_id TEXT NOT NULL,
|
||||
user_id TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
context JSONB,
|
||||
metadata JSONB
|
||||
);
|
||||
|
||||
-- Conversation turns table
|
||||
CREATE TABLE IF NOT EXISTS advisoryai.turns (
|
||||
turn_id TEXT PRIMARY KEY,
|
||||
conversation_id TEXT NOT NULL REFERENCES advisoryai.conversations(conversation_id) ON DELETE CASCADE,
|
||||
role TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
evidence_links JSONB,
|
||||
proposed_actions JSONB,
|
||||
metadata JSONB
|
||||
);
|
||||
|
||||
-- Indexes for querying
|
||||
CREATE INDEX IF NOT EXISTS idx_advisoryai_conv_tenant ON advisoryai.conversations(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_advisoryai_conv_user ON advisoryai.conversations(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_advisoryai_conv_updated ON advisoryai.conversations(updated_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_advisoryai_turns_conv ON advisoryai.turns(conversation_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_advisoryai_turns_timestamp ON advisoryai.turns(timestamp);
|
||||
|
||||
-- ============================================================================
|
||||
-- Comments for documentation
|
||||
-- ============================================================================
|
||||
|
||||
COMMENT ON SCHEMA opsmemory IS 'OpsMemory: Decision ledger for security playbook learning';
|
||||
COMMENT ON SCHEMA advisoryai IS 'AdvisoryAI: Chat conversation storage';
|
||||
|
||||
COMMENT ON TABLE opsmemory.decisions IS 'Stores security decisions and their outcomes for playbook suggestions';
|
||||
COMMENT ON TABLE advisoryai.conversations IS 'Stores AI chat conversations with context';
|
||||
COMMENT ON TABLE advisoryai.turns IS 'Individual messages in conversations';
|
||||
|
||||
-- ============================================================================
|
||||
-- Grants (adjust as needed for your environment)
|
||||
-- ============================================================================
|
||||
|
||||
-- GRANT USAGE ON SCHEMA opsmemory TO stellaops_app;
|
||||
-- GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA opsmemory TO stellaops_app;
|
||||
|
||||
-- GRANT USAGE ON SCHEMA advisoryai TO stellaops_app;
|
||||
-- GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA advisoryai TO stellaops_app;
|
||||
@@ -0,0 +1,220 @@
|
||||
-- CVE-Symbol Mapping PostgreSQL Schema Migration
|
||||
-- Version: 20260110
|
||||
-- Author: StellaOps Agent
|
||||
-- Sprint: SPRINT_20260109_009_003_BE_cve_symbol_mapping
|
||||
|
||||
-- ============================================================================
|
||||
-- Reachability Schema
|
||||
-- ============================================================================
|
||||
|
||||
CREATE SCHEMA IF NOT EXISTS reachability;
|
||||
|
||||
-- ============================================================================
|
||||
-- CVE-Symbol Mapping Tables
|
||||
-- ============================================================================
|
||||
|
||||
-- Mapping source enumeration type
|
||||
CREATE TYPE reachability.mapping_source AS ENUM (
|
||||
'patch_analysis',
|
||||
'osv_advisory',
|
||||
'nvd_cpe',
|
||||
'manual_curation',
|
||||
'fuzzing_corpus',
|
||||
'exploit_database',
|
||||
'unknown'
|
||||
);
|
||||
|
||||
-- Vulnerability type enumeration (for taint analysis)
|
||||
CREATE TYPE reachability.vulnerability_type AS ENUM (
|
||||
'source',
|
||||
'sink',
|
||||
'gadget',
|
||||
'both_source_and_sink',
|
||||
'unknown'
|
||||
);
|
||||
|
||||
-- Main CVE-symbol mapping table
|
||||
CREATE TABLE IF NOT EXISTS reachability.cve_symbol_mappings (
|
||||
mapping_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
-- CVE identification
|
||||
cve_id TEXT NOT NULL,
|
||||
cve_id_normalized TEXT NOT NULL GENERATED ALWAYS AS (UPPER(cve_id)) STORED,
|
||||
|
||||
-- Affected package (PURL format)
|
||||
purl TEXT NOT NULL,
|
||||
affected_versions TEXT[], -- Version ranges like [">=1.0.0,<2.0.0"]
|
||||
fixed_versions TEXT[], -- Versions where fix is applied
|
||||
|
||||
-- Vulnerable symbol details
|
||||
symbol_name TEXT NOT NULL,
|
||||
canonical_id TEXT, -- Normalized symbol ID from canonicalization service
|
||||
file_path TEXT,
|
||||
start_line INTEGER,
|
||||
end_line INTEGER,
|
||||
|
||||
-- Metadata
|
||||
source reachability.mapping_source NOT NULL DEFAULT 'unknown',
|
||||
vulnerability_type reachability.vulnerability_type NOT NULL DEFAULT 'unknown',
|
||||
confidence DECIMAL(3, 2) NOT NULL DEFAULT 0.5 CHECK (confidence >= 0 AND confidence <= 1),
|
||||
|
||||
-- Provenance
|
||||
evidence_uri TEXT, -- stella:// URI to evidence
|
||||
source_commit_url TEXT,
|
||||
patch_url TEXT,
|
||||
|
||||
-- Timestamps
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
verified_at TIMESTAMPTZ,
|
||||
verified_by TEXT,
|
||||
|
||||
-- Tenant support
|
||||
tenant_id TEXT NOT NULL DEFAULT 'default'
|
||||
);
|
||||
|
||||
-- Vulnerable symbol detail records (for additional symbol metadata)
|
||||
CREATE TABLE IF NOT EXISTS reachability.vulnerable_symbols (
|
||||
symbol_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
mapping_id UUID NOT NULL REFERENCES reachability.cve_symbol_mappings(mapping_id) ON DELETE CASCADE,
|
||||
|
||||
-- Symbol identification
|
||||
symbol_name TEXT NOT NULL,
|
||||
canonical_id TEXT,
|
||||
symbol_type TEXT, -- 'function', 'method', 'class', 'module'
|
||||
|
||||
-- Location
|
||||
file_path TEXT,
|
||||
start_line INTEGER,
|
||||
end_line INTEGER,
|
||||
|
||||
-- Code context
|
||||
signature TEXT, -- Function signature
|
||||
containing_class TEXT,
|
||||
namespace TEXT,
|
||||
|
||||
-- Vulnerability context
|
||||
vulnerability_type reachability.vulnerability_type NOT NULL DEFAULT 'unknown',
|
||||
is_entry_point BOOLEAN DEFAULT FALSE,
|
||||
requires_control_flow BOOLEAN DEFAULT FALSE,
|
||||
|
||||
-- Metadata
|
||||
confidence DECIMAL(3, 2) NOT NULL DEFAULT 0.5,
|
||||
notes TEXT,
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Patch analysis results (cached)
|
||||
CREATE TABLE IF NOT EXISTS reachability.patch_analysis (
|
||||
analysis_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
-- Source identification
|
||||
commit_url TEXT NOT NULL UNIQUE,
|
||||
repository_url TEXT,
|
||||
commit_sha TEXT,
|
||||
|
||||
-- Analysis results (stored as JSONB for flexibility)
|
||||
diff_content TEXT,
|
||||
extracted_symbols JSONB NOT NULL DEFAULT '[]',
|
||||
language_detected TEXT,
|
||||
|
||||
-- Metadata
|
||||
analyzed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
analyzer_version TEXT,
|
||||
|
||||
-- Error tracking
|
||||
analysis_status TEXT NOT NULL DEFAULT 'pending',
|
||||
error_message TEXT
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- Indexes
|
||||
-- ============================================================================
|
||||
|
||||
-- CVE lookup indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_cve_mapping_cve_normalized ON reachability.cve_symbol_mappings(cve_id_normalized);
|
||||
CREATE INDEX IF NOT EXISTS idx_cve_mapping_purl ON reachability.cve_symbol_mappings(purl);
|
||||
CREATE INDEX IF NOT EXISTS idx_cve_mapping_symbol ON reachability.cve_symbol_mappings(symbol_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_cve_mapping_canonical ON reachability.cve_symbol_mappings(canonical_id) WHERE canonical_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_cve_mapping_tenant ON reachability.cve_symbol_mappings(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_cve_mapping_source ON reachability.cve_symbol_mappings(source);
|
||||
CREATE INDEX IF NOT EXISTS idx_cve_mapping_confidence ON reachability.cve_symbol_mappings(confidence);
|
||||
CREATE INDEX IF NOT EXISTS idx_cve_mapping_created ON reachability.cve_symbol_mappings(created_at);
|
||||
|
||||
-- Composite index for common queries
|
||||
CREATE INDEX IF NOT EXISTS idx_cve_mapping_cve_purl ON reachability.cve_symbol_mappings(cve_id_normalized, purl);
|
||||
|
||||
-- Symbol indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_vuln_symbol_mapping ON reachability.vulnerable_symbols(mapping_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_vuln_symbol_name ON reachability.vulnerable_symbols(symbol_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_vuln_symbol_canonical ON reachability.vulnerable_symbols(canonical_id) WHERE canonical_id IS NOT NULL;
|
||||
|
||||
-- Patch analysis indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_patch_analysis_commit ON reachability.patch_analysis(commit_sha);
|
||||
CREATE INDEX IF NOT EXISTS idx_patch_analysis_repo ON reachability.patch_analysis(repository_url);
|
||||
|
||||
-- ============================================================================
|
||||
-- Full-text search
|
||||
-- ============================================================================
|
||||
|
||||
-- Add tsvector column for symbol search
|
||||
ALTER TABLE reachability.cve_symbol_mappings
|
||||
ADD COLUMN IF NOT EXISTS symbol_search_vector tsvector
|
||||
GENERATED ALWAYS AS (to_tsvector('simple', coalesce(symbol_name, '') || ' ' || coalesce(file_path, ''))) STORED;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_cve_mapping_fts ON reachability.cve_symbol_mappings USING GIN(symbol_search_vector);
|
||||
|
||||
-- ============================================================================
|
||||
-- Trigger for updated_at
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION reachability.update_modified_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER update_cve_mapping_modtime
|
||||
BEFORE UPDATE ON reachability.cve_symbol_mappings
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION reachability.update_modified_column();
|
||||
|
||||
-- ============================================================================
|
||||
-- Comments for documentation
|
||||
-- ============================================================================
|
||||
|
||||
COMMENT ON SCHEMA reachability IS 'Hybrid reachability analysis: CVE-symbol mappings, static/runtime evidence';
|
||||
|
||||
COMMENT ON TABLE reachability.cve_symbol_mappings IS 'Maps CVE IDs to vulnerable symbols with confidence scores';
|
||||
COMMENT ON COLUMN reachability.cve_symbol_mappings.cve_id_normalized IS 'Uppercase normalized CVE ID for case-insensitive lookup';
|
||||
COMMENT ON COLUMN reachability.cve_symbol_mappings.canonical_id IS 'Symbol canonical ID from canonicalization service';
|
||||
COMMENT ON COLUMN reachability.cve_symbol_mappings.evidence_uri IS 'stella:// URI pointing to evidence bundle';
|
||||
|
||||
COMMENT ON TABLE reachability.vulnerable_symbols IS 'Additional symbol details for a CVE mapping';
|
||||
COMMENT ON TABLE reachability.patch_analysis IS 'Cached patch analysis results for commit URLs';
|
||||
|
||||
-- ============================================================================
|
||||
-- Initial data / seed (optional well-known CVEs for testing)
|
||||
-- ============================================================================
|
||||
|
||||
-- Example: Log4Shell (CVE-2021-44228)
|
||||
INSERT INTO reachability.cve_symbol_mappings (cve_id, purl, symbol_name, file_path, source, confidence, vulnerability_type)
|
||||
VALUES
|
||||
('CVE-2021-44228', 'pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1', 'JndiLookup.lookup', 'log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/JndiLookup.java', 'manual_curation', 0.99, 'sink'),
|
||||
('CVE-2021-44228', 'pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1', 'JndiManager.lookup', 'log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java', 'manual_curation', 0.95, 'sink')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Example: Spring4Shell (CVE-2022-22965)
|
||||
INSERT INTO reachability.cve_symbol_mappings (cve_id, purl, symbol_name, file_path, source, confidence, vulnerability_type)
|
||||
VALUES
|
||||
('CVE-2022-22965', 'pkg:maven/org.springframework/spring-beans@5.3.17', 'CachedIntrospectionResults.getBeanInfo', 'spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java', 'patch_analysis', 0.90, 'source')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Example: polyfill.io supply chain (CVE-2024-38526)
|
||||
INSERT INTO reachability.cve_symbol_mappings (cve_id, purl, symbol_name, source, confidence, vulnerability_type)
|
||||
VALUES
|
||||
('CVE-2024-38526', 'pkg:npm/polyfill.io', 'window.polyfill', 'manual_curation', 0.85, 'source')
|
||||
ON CONFLICT DO NOTHING;
|
||||
@@ -0,0 +1,38 @@
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- V20260117__create_doctor_reports_table.sql
|
||||
-- Sprint: SPRINT_20260117_025_Doctor_coverage_expansion
|
||||
-- Task: DOC-EXP-005 - Persistent Report Storage
|
||||
-- Description: Migration to create doctor_reports table for persistent storage
|
||||
-- -----------------------------------------------------------------------------
|
||||
|
||||
-- Doctor reports table for persistent storage
|
||||
CREATE TABLE IF NOT EXISTS doctor_reports (
|
||||
run_id VARCHAR(64) PRIMARY KEY,
|
||||
started_at TIMESTAMPTZ NOT NULL,
|
||||
completed_at TIMESTAMPTZ,
|
||||
overall_severity VARCHAR(16) NOT NULL,
|
||||
passed_count INTEGER NOT NULL DEFAULT 0,
|
||||
warning_count INTEGER NOT NULL DEFAULT 0,
|
||||
failed_count INTEGER NOT NULL DEFAULT 0,
|
||||
skipped_count INTEGER NOT NULL DEFAULT 0,
|
||||
info_count INTEGER NOT NULL DEFAULT 0,
|
||||
total_count INTEGER NOT NULL DEFAULT 0,
|
||||
report_json_compressed BYTEA NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Index for listing reports by date
|
||||
CREATE INDEX IF NOT EXISTS idx_doctor_reports_started_at
|
||||
ON doctor_reports (started_at DESC);
|
||||
|
||||
-- Index for retention cleanup
|
||||
CREATE INDEX IF NOT EXISTS idx_doctor_reports_created_at
|
||||
ON doctor_reports (created_at);
|
||||
|
||||
-- Index for filtering by severity
|
||||
CREATE INDEX IF NOT EXISTS idx_doctor_reports_severity
|
||||
ON doctor_reports (overall_severity);
|
||||
|
||||
-- Comment on table
|
||||
COMMENT ON TABLE doctor_reports IS 'Stores Doctor diagnostic reports with compression for audit trail';
|
||||
COMMENT ON COLUMN doctor_reports.report_json_compressed IS 'GZip compressed JSON report data';
|
||||
153
deploy/database/migrations/V20260117__vex_rekor_linkage.sql
Normal file
153
deploy/database/migrations/V20260117__vex_rekor_linkage.sql
Normal file
@@ -0,0 +1,153 @@
|
||||
-- Migration: V20260117__vex_rekor_linkage.sql
|
||||
-- Sprint: SPRINT_20260117_002_EXCITITOR_vex_rekor_linkage
|
||||
-- Task: VRL-004, VRL-005 - Create Excititor and VexHub database migrations
|
||||
-- Description: Add Rekor transparency log linkage columns to VEX tables
|
||||
-- Author: StellaOps
|
||||
-- Date: 2026-01-17
|
||||
|
||||
-- ============================================================================
|
||||
-- EXCITITOR SCHEMA: vex_observations table
|
||||
-- ============================================================================
|
||||
|
||||
-- Add Rekor linkage columns to vex_observations
|
||||
ALTER TABLE IF EXISTS excititor.vex_observations
|
||||
ADD COLUMN IF NOT EXISTS rekor_uuid TEXT,
|
||||
ADD COLUMN IF NOT EXISTS rekor_log_index BIGINT,
|
||||
ADD COLUMN IF NOT EXISTS rekor_integrated_time TIMESTAMPTZ,
|
||||
ADD COLUMN IF NOT EXISTS rekor_log_url TEXT,
|
||||
ADD COLUMN IF NOT EXISTS rekor_tree_root TEXT,
|
||||
ADD COLUMN IF NOT EXISTS rekor_tree_size BIGINT,
|
||||
ADD COLUMN IF NOT EXISTS rekor_inclusion_proof JSONB,
|
||||
ADD COLUMN IF NOT EXISTS rekor_entry_body_hash TEXT,
|
||||
ADD COLUMN IF NOT EXISTS rekor_entry_kind TEXT,
|
||||
ADD COLUMN IF NOT EXISTS rekor_linked_at TIMESTAMPTZ;
|
||||
|
||||
-- Index for Rekor queries by UUID
|
||||
CREATE INDEX IF NOT EXISTS idx_vex_observations_rekor_uuid
|
||||
ON excititor.vex_observations(rekor_uuid)
|
||||
WHERE rekor_uuid IS NOT NULL;
|
||||
|
||||
-- Index for Rekor queries by log index (for ordered traversal)
|
||||
CREATE INDEX IF NOT EXISTS idx_vex_observations_rekor_log_index
|
||||
ON excititor.vex_observations(rekor_log_index DESC)
|
||||
WHERE rekor_log_index IS NOT NULL;
|
||||
|
||||
-- Index for finding unlinked observations (for retry/backfill)
|
||||
CREATE INDEX IF NOT EXISTS idx_vex_observations_pending_rekor
|
||||
ON excititor.vex_observations(created_at)
|
||||
WHERE rekor_uuid IS NULL;
|
||||
|
||||
-- Comment on columns
|
||||
COMMENT ON COLUMN excititor.vex_observations.rekor_uuid IS 'Rekor entry UUID (64-char hex)';
|
||||
COMMENT ON COLUMN excititor.vex_observations.rekor_log_index IS 'Monotonically increasing log position';
|
||||
COMMENT ON COLUMN excititor.vex_observations.rekor_integrated_time IS 'Time entry was integrated into Rekor log';
|
||||
COMMENT ON COLUMN excititor.vex_observations.rekor_log_url IS 'Rekor server URL where entry was submitted';
|
||||
COMMENT ON COLUMN excititor.vex_observations.rekor_tree_root IS 'Merkle tree root hash at submission time (base64)';
|
||||
COMMENT ON COLUMN excititor.vex_observations.rekor_tree_size IS 'Tree size at submission time';
|
||||
COMMENT ON COLUMN excititor.vex_observations.rekor_inclusion_proof IS 'RFC 6962 inclusion proof for offline verification';
|
||||
COMMENT ON COLUMN excititor.vex_observations.rekor_entry_body_hash IS 'SHA-256 hash of entry body';
|
||||
COMMENT ON COLUMN excititor.vex_observations.rekor_entry_kind IS 'Entry kind (dsse, intoto, hashedrekord)';
|
||||
COMMENT ON COLUMN excititor.vex_observations.rekor_linked_at IS 'When linkage was recorded locally';
|
||||
|
||||
-- ============================================================================
|
||||
-- EXCITITOR SCHEMA: vex_statement_change_events table
|
||||
-- ============================================================================
|
||||
|
||||
-- Add Rekor linkage to change events
|
||||
ALTER TABLE IF EXISTS excititor.vex_statement_change_events
|
||||
ADD COLUMN IF NOT EXISTS rekor_entry_id TEXT,
|
||||
ADD COLUMN IF NOT EXISTS rekor_log_index BIGINT;
|
||||
|
||||
-- Index for Rekor queries on change events
|
||||
CREATE INDEX IF NOT EXISTS idx_vex_change_events_rekor
|
||||
ON excititor.vex_statement_change_events(rekor_entry_id)
|
||||
WHERE rekor_entry_id IS NOT NULL;
|
||||
|
||||
COMMENT ON COLUMN excititor.vex_statement_change_events.rekor_entry_id IS 'Rekor entry UUID for change attestation';
|
||||
COMMENT ON COLUMN excititor.vex_statement_change_events.rekor_log_index IS 'Rekor log index for change attestation';
|
||||
|
||||
-- ============================================================================
|
||||
-- VEXHUB SCHEMA: vex_statements table
|
||||
-- ============================================================================
|
||||
|
||||
-- Add Rekor linkage columns to vex_statements
|
||||
ALTER TABLE IF EXISTS vexhub.vex_statements
|
||||
ADD COLUMN IF NOT EXISTS rekor_uuid TEXT,
|
||||
ADD COLUMN IF NOT EXISTS rekor_log_index BIGINT,
|
||||
ADD COLUMN IF NOT EXISTS rekor_integrated_time TIMESTAMPTZ,
|
||||
ADD COLUMN IF NOT EXISTS rekor_inclusion_proof JSONB;
|
||||
|
||||
-- Index for Rekor queries
|
||||
CREATE INDEX IF NOT EXISTS idx_vexhub_statements_rekor_uuid
|
||||
ON vexhub.vex_statements(rekor_uuid)
|
||||
WHERE rekor_uuid IS NOT NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_vexhub_statements_rekor_log_index
|
||||
ON vexhub.vex_statements(rekor_log_index DESC)
|
||||
WHERE rekor_log_index IS NOT NULL;
|
||||
|
||||
COMMENT ON COLUMN vexhub.vex_statements.rekor_uuid IS 'Rekor entry UUID for statement attestation';
|
||||
COMMENT ON COLUMN vexhub.vex_statements.rekor_log_index IS 'Rekor log index for statement attestation';
|
||||
COMMENT ON COLUMN vexhub.vex_statements.rekor_integrated_time IS 'Time statement was integrated into Rekor log';
|
||||
COMMENT ON COLUMN vexhub.vex_statements.rekor_inclusion_proof IS 'RFC 6962 inclusion proof for offline verification';
|
||||
|
||||
-- ============================================================================
|
||||
-- ATTESTOR SCHEMA: rekor_entries verification tracking
|
||||
-- Sprint: SPRINT_20260117_001_ATTESTOR_periodic_rekor_verification (PRV-003)
|
||||
-- ============================================================================
|
||||
|
||||
-- Add verification tracking columns to existing rekor_entries table
|
||||
ALTER TABLE IF EXISTS attestor.rekor_entries
|
||||
ADD COLUMN IF NOT EXISTS last_verified_at TIMESTAMPTZ,
|
||||
ADD COLUMN IF NOT EXISTS verification_count INT NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS last_verification_result TEXT;
|
||||
|
||||
-- Index for verification queries (find entries needing verification)
|
||||
CREATE INDEX IF NOT EXISTS idx_rekor_entries_verification
|
||||
ON attestor.rekor_entries(created_at DESC, last_verified_at NULLS FIRST)
|
||||
WHERE last_verification_result IS DISTINCT FROM 'invalid';
|
||||
|
||||
-- Index for finding never-verified entries
|
||||
CREATE INDEX IF NOT EXISTS idx_rekor_entries_unverified
|
||||
ON attestor.rekor_entries(created_at DESC)
|
||||
WHERE last_verified_at IS NULL;
|
||||
|
||||
COMMENT ON COLUMN attestor.rekor_entries.last_verified_at IS 'Timestamp of last successful verification';
|
||||
COMMENT ON COLUMN attestor.rekor_entries.verification_count IS 'Number of times entry has been verified';
|
||||
COMMENT ON COLUMN attestor.rekor_entries.last_verification_result IS 'Result of last verification: valid, invalid, skipped';
|
||||
|
||||
-- ============================================================================
|
||||
-- ATTESTOR SCHEMA: rekor_root_checkpoints table
|
||||
-- Stores tree root checkpoints for consistency verification
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS attestor.rekor_root_checkpoints (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tree_root TEXT NOT NULL,
|
||||
tree_size BIGINT NOT NULL,
|
||||
log_id TEXT NOT NULL,
|
||||
log_url TEXT,
|
||||
checkpoint_envelope TEXT,
|
||||
captured_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
verified_at TIMESTAMPTZ,
|
||||
is_consistent BOOLEAN,
|
||||
inconsistency_reason TEXT,
|
||||
CONSTRAINT uq_root_checkpoint UNIQUE (log_id, tree_root, tree_size)
|
||||
);
|
||||
|
||||
-- Index for finding latest checkpoints per log
|
||||
CREATE INDEX IF NOT EXISTS idx_rekor_root_checkpoints_latest
|
||||
ON attestor.rekor_root_checkpoints(log_id, captured_at DESC);
|
||||
|
||||
-- Index for consistency verification
|
||||
CREATE INDEX IF NOT EXISTS idx_rekor_root_checkpoints_unverified
|
||||
ON attestor.rekor_root_checkpoints(captured_at DESC)
|
||||
WHERE verified_at IS NULL;
|
||||
|
||||
COMMENT ON TABLE attestor.rekor_root_checkpoints IS 'Stores Rekor tree root checkpoints for consistency verification';
|
||||
COMMENT ON COLUMN attestor.rekor_root_checkpoints.tree_root IS 'Merkle tree root hash (base64)';
|
||||
COMMENT ON COLUMN attestor.rekor_root_checkpoints.tree_size IS 'Tree size at checkpoint';
|
||||
COMMENT ON COLUMN attestor.rekor_root_checkpoints.log_id IS 'Rekor log identifier (hash of public key)';
|
||||
COMMENT ON COLUMN attestor.rekor_root_checkpoints.checkpoint_envelope IS 'Signed checkpoint in note format';
|
||||
COMMENT ON COLUMN attestor.rekor_root_checkpoints.is_consistent IS 'Whether checkpoint was consistent with previous';
|
||||
COMMENT ON COLUMN attestor.rekor_root_checkpoints.inconsistency_reason IS 'Reason for inconsistency if detected';
|
||||
@@ -0,0 +1,139 @@
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- V20260119_001__Add_UnderReview_Escalated_Rejected_States.sql
|
||||
-- Sprint: SPRINT_20260118_018_Unknowns_queue_enhancement
|
||||
-- Task: UQ-005 - Migration for existing entries (map to new states)
|
||||
-- Description: Adds new state machine states and required columns
|
||||
-- -----------------------------------------------------------------------------
|
||||
|
||||
-- Add new columns for UnderReview and Escalated states
|
||||
ALTER TABLE grey_queue_entries
|
||||
ADD COLUMN IF NOT EXISTS assignee VARCHAR(255) NULL,
|
||||
ADD COLUMN IF NOT EXISTS assigned_at TIMESTAMPTZ NULL,
|
||||
ADD COLUMN IF NOT EXISTS escalated_at TIMESTAMPTZ NULL,
|
||||
ADD COLUMN IF NOT EXISTS escalation_reason TEXT NULL;
|
||||
|
||||
-- Add new enum values to grey_queue_status
|
||||
-- Note: PostgreSQL requires special handling for enum additions
|
||||
|
||||
-- First, check if we need to add the values (idempotent)
|
||||
DO $$
|
||||
BEGIN
|
||||
-- Add 'under_review' if not exists
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_enum
|
||||
WHERE enumlabel = 'under_review'
|
||||
AND enumtypid = 'grey_queue_status'::regtype
|
||||
) THEN
|
||||
ALTER TYPE grey_queue_status ADD VALUE 'under_review' AFTER 'retrying';
|
||||
END IF;
|
||||
|
||||
-- Add 'escalated' if not exists
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_enum
|
||||
WHERE enumlabel = 'escalated'
|
||||
AND enumtypid = 'grey_queue_status'::regtype
|
||||
) THEN
|
||||
ALTER TYPE grey_queue_status ADD VALUE 'escalated' AFTER 'under_review';
|
||||
END IF;
|
||||
|
||||
-- Add 'rejected' if not exists
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_enum
|
||||
WHERE enumlabel = 'rejected'
|
||||
AND enumtypid = 'grey_queue_status'::regtype
|
||||
) THEN
|
||||
ALTER TYPE grey_queue_status ADD VALUE 'rejected' AFTER 'resolved';
|
||||
END IF;
|
||||
EXCEPTION
|
||||
WHEN others THEN
|
||||
-- Enum values may already exist, which is fine
|
||||
NULL;
|
||||
END $$;
|
||||
|
||||
-- Add indexes for new query patterns
|
||||
CREATE INDEX IF NOT EXISTS idx_grey_queue_assignee
|
||||
ON grey_queue_entries(assignee)
|
||||
WHERE assignee IS NOT NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_grey_queue_status_assignee
|
||||
ON grey_queue_entries(status, assignee)
|
||||
WHERE status IN ('under_review', 'escalated');
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_grey_queue_escalated_at
|
||||
ON grey_queue_entries(escalated_at DESC)
|
||||
WHERE escalated_at IS NOT NULL;
|
||||
|
||||
-- Add audit trigger for state transitions
|
||||
CREATE TABLE IF NOT EXISTS grey_queue_state_transitions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
entry_id UUID NOT NULL REFERENCES grey_queue_entries(id),
|
||||
tenant_id VARCHAR(128) NOT NULL,
|
||||
from_state VARCHAR(32) NOT NULL,
|
||||
to_state VARCHAR(32) NOT NULL,
|
||||
transitioned_by VARCHAR(255),
|
||||
reason TEXT,
|
||||
transitioned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
metadata JSONB
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_grey_queue_transitions_entry
|
||||
ON grey_queue_state_transitions(entry_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_grey_queue_transitions_tenant_time
|
||||
ON grey_queue_state_transitions(tenant_id, transitioned_at DESC);
|
||||
|
||||
-- Function to record state transitions
|
||||
CREATE OR REPLACE FUNCTION record_grey_queue_transition()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF OLD.status IS DISTINCT FROM NEW.status THEN
|
||||
INSERT INTO grey_queue_state_transitions (
|
||||
entry_id, tenant_id, from_state, to_state,
|
||||
transitioned_by, transitioned_at
|
||||
) VALUES (
|
||||
NEW.id,
|
||||
NEW.tenant_id,
|
||||
OLD.status::text,
|
||||
NEW.status::text,
|
||||
COALESCE(NEW.assignee, current_user),
|
||||
NOW()
|
||||
);
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Create trigger if not exists
|
||||
DROP TRIGGER IF EXISTS trg_grey_queue_state_transition ON grey_queue_entries;
|
||||
CREATE TRIGGER trg_grey_queue_state_transition
|
||||
AFTER UPDATE ON grey_queue_entries
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION record_grey_queue_transition();
|
||||
|
||||
-- Update summary view to include new states
|
||||
CREATE OR REPLACE VIEW grey_queue_summary AS
|
||||
SELECT
|
||||
tenant_id,
|
||||
COUNT(*) FILTER (WHERE status = 'pending') as pending_count,
|
||||
COUNT(*) FILTER (WHERE status = 'processing') as processing_count,
|
||||
COUNT(*) FILTER (WHERE status = 'retrying') as retrying_count,
|
||||
COUNT(*) FILTER (WHERE status = 'under_review') as under_review_count,
|
||||
COUNT(*) FILTER (WHERE status = 'escalated') as escalated_count,
|
||||
COUNT(*) FILTER (WHERE status = 'resolved') as resolved_count,
|
||||
COUNT(*) FILTER (WHERE status = 'rejected') as rejected_count,
|
||||
COUNT(*) FILTER (WHERE status = 'failed') as failed_count,
|
||||
COUNT(*) FILTER (WHERE status = 'expired') as expired_count,
|
||||
COUNT(*) FILTER (WHERE status = 'dismissed') as dismissed_count,
|
||||
COUNT(*) as total_count
|
||||
FROM grey_queue_entries
|
||||
GROUP BY tenant_id;
|
||||
|
||||
-- Comment for documentation
|
||||
COMMENT ON COLUMN grey_queue_entries.assignee IS
|
||||
'Assignee for entries in UnderReview state (Sprint UQ-005)';
|
||||
COMMENT ON COLUMN grey_queue_entries.assigned_at IS
|
||||
'When the entry was assigned for review (Sprint UQ-005)';
|
||||
COMMENT ON COLUMN grey_queue_entries.escalated_at IS
|
||||
'When the entry was escalated to security team (Sprint UQ-005)';
|
||||
COMMENT ON COLUMN grey_queue_entries.escalation_reason IS
|
||||
'Reason for escalation (Sprint UQ-005)';
|
||||
130
deploy/database/migrations/V20260119__scanner_layer_diffid.sql
Normal file
130
deploy/database/migrations/V20260119__scanner_layer_diffid.sql
Normal file
@@ -0,0 +1,130 @@
|
||||
-- Migration: Add diff_id column to scanner layers table
|
||||
-- Sprint: SPRINT_025_Scanner_layer_manifest_infrastructure
|
||||
-- Task: TASK-025-03
|
||||
|
||||
-- Add diff_id column to layers table (sha256:64hex = 71 chars)
|
||||
ALTER TABLE scanner.layers
|
||||
ADD COLUMN IF NOT EXISTS diff_id VARCHAR(71);
|
||||
|
||||
-- Add timestamp for when diffID was computed
|
||||
ALTER TABLE scanner.layers
|
||||
ADD COLUMN IF NOT EXISTS diff_id_computed_at_utc TIMESTAMP;
|
||||
|
||||
-- Create index on diff_id for fast lookups
|
||||
CREATE INDEX IF NOT EXISTS idx_layers_diff_id
|
||||
ON scanner.layers (diff_id)
|
||||
WHERE diff_id IS NOT NULL;
|
||||
|
||||
-- Create image_layers junction table if it doesn't exist
|
||||
-- This tracks which layers belong to which images
|
||||
CREATE TABLE IF NOT EXISTS scanner.image_layers (
|
||||
image_reference VARCHAR(512) NOT NULL,
|
||||
layer_digest VARCHAR(71) NOT NULL,
|
||||
layer_index INT NOT NULL,
|
||||
created_at_utc TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (image_reference, layer_digest)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_image_layers_digest
|
||||
ON scanner.image_layers (layer_digest);
|
||||
|
||||
-- DiffID cache table for resolved diffIDs
|
||||
CREATE TABLE IF NOT EXISTS scanner.scanner_diffid_cache (
|
||||
layer_digest VARCHAR(71) PRIMARY KEY,
|
||||
diff_id VARCHAR(71) NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Base image fingerprint tables for layer reuse detection
|
||||
CREATE TABLE IF NOT EXISTS scanner.scanner_base_image_fingerprints (
|
||||
image_reference VARCHAR(512) PRIMARY KEY,
|
||||
layer_count INT NOT NULL,
|
||||
registered_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
detection_count BIGINT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS scanner.scanner_base_image_layers (
|
||||
image_reference VARCHAR(512) NOT NULL REFERENCES scanner.scanner_base_image_fingerprints(image_reference) ON DELETE CASCADE,
|
||||
layer_index INT NOT NULL,
|
||||
diff_id VARCHAR(71) NOT NULL,
|
||||
PRIMARY KEY (image_reference, layer_index)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_base_image_layers_diff_id
|
||||
ON scanner.scanner_base_image_layers (diff_id);
|
||||
|
||||
-- Manifest snapshots table for IOciManifestSnapshotService
|
||||
CREATE TABLE IF NOT EXISTS scanner.manifest_snapshots (
|
||||
id UUID PRIMARY KEY,
|
||||
image_reference VARCHAR(512) NOT NULL,
|
||||
registry VARCHAR(256) NOT NULL,
|
||||
repository VARCHAR(256) NOT NULL,
|
||||
tag VARCHAR(128),
|
||||
manifest_digest VARCHAR(71) NOT NULL,
|
||||
config_digest VARCHAR(71) NOT NULL,
|
||||
media_type VARCHAR(128) NOT NULL,
|
||||
layers JSONB NOT NULL,
|
||||
diff_ids JSONB NOT NULL,
|
||||
platform JSONB,
|
||||
total_size BIGINT NOT NULL,
|
||||
captured_at TIMESTAMPTZ NOT NULL,
|
||||
snapshot_version VARCHAR(32),
|
||||
UNIQUE (manifest_digest)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_manifest_snapshots_image_ref
|
||||
ON scanner.manifest_snapshots (image_reference);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_manifest_snapshots_repository
|
||||
ON scanner.manifest_snapshots (registry, repository);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_manifest_snapshots_captured_at
|
||||
ON scanner.manifest_snapshots (captured_at DESC);
|
||||
|
||||
-- Layer scan history for reuse detection (TASK-025-04)
|
||||
CREATE TABLE IF NOT EXISTS scanner.layer_scans (
|
||||
diff_id VARCHAR(71) PRIMARY KEY,
|
||||
scanned_at TIMESTAMPTZ NOT NULL,
|
||||
finding_count INT,
|
||||
scanned_by VARCHAR(128) NOT NULL,
|
||||
scanner_version VARCHAR(64)
|
||||
);
|
||||
|
||||
-- Layer reuse counts for statistics
|
||||
CREATE TABLE IF NOT EXISTS scanner.layer_reuse_counts (
|
||||
diff_id VARCHAR(71) PRIMARY KEY,
|
||||
reuse_count INT NOT NULL DEFAULT 1,
|
||||
first_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_layer_reuse_counts_count
|
||||
ON scanner.layer_reuse_counts (reuse_count DESC);
|
||||
|
||||
COMMENT ON COLUMN scanner.layers.diff_id IS 'Uncompressed layer content hash (sha256:hex64). Immutable once computed.';
|
||||
COMMENT ON TABLE scanner.scanner_diffid_cache IS 'Cache of layer digest to diffID mappings. Layer digests are immutable so cache entries never expire.';
|
||||
COMMENT ON TABLE scanner.scanner_base_image_fingerprints IS 'Known base image fingerprints for layer reuse detection.';
|
||||
COMMENT ON TABLE scanner.manifest_snapshots IS 'Point-in-time captures of OCI image manifests for delta scanning.';
|
||||
COMMENT ON TABLE scanner.layer_scans IS 'History of layer scans for deduplication. One entry per diffID.';
|
||||
COMMENT ON TABLE scanner.layer_reuse_counts IS 'Counts of how many times each layer appears across images.';
|
||||
|
||||
-- Layer SBOM CAS for per-layer SBOM storage (TASK-026-02)
|
||||
CREATE TABLE IF NOT EXISTS scanner.layer_sbom_cas (
|
||||
diff_id VARCHAR(71) NOT NULL,
|
||||
format VARCHAR(20) NOT NULL,
|
||||
content BYTEA NOT NULL,
|
||||
size_bytes BIGINT NOT NULL,
|
||||
compressed BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
last_accessed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (diff_id, format)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_layer_sbom_cas_last_accessed
|
||||
ON scanner.layer_sbom_cas (last_accessed_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_layer_sbom_cas_format
|
||||
ON scanner.layer_sbom_cas (format);
|
||||
|
||||
COMMENT ON TABLE scanner.layer_sbom_cas IS 'Content-addressable storage for per-layer SBOMs. Keyed by diffID (immutable).';
|
||||
COMMENT ON COLUMN scanner.layer_sbom_cas.content IS 'Compressed (gzip) SBOM content.';
|
||||
COMMENT ON COLUMN scanner.layer_sbom_cas.last_accessed_at IS 'For TTL-based eviction of cold entries.';
|
||||
@@ -0,0 +1,561 @@
|
||||
-- Partitioning Infrastructure Migration 001: Foundation
|
||||
-- Sprint: SPRINT_3422_0001_0001 - Time-Based Partitioning
|
||||
-- Category: C (infrastructure setup, requires planned maintenance)
|
||||
--
|
||||
-- Purpose: Create partition management infrastructure including:
|
||||
-- - Helper functions for partition creation and maintenance
|
||||
-- - Utility functions for BRIN index optimization
|
||||
-- - Partition maintenance scheduling support
|
||||
--
|
||||
-- This migration creates the foundation; table conversion is done in separate migrations.
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 1: Create partition management schema
|
||||
-- ============================================================================
|
||||
|
||||
CREATE SCHEMA IF NOT EXISTS partition_mgmt;
|
||||
|
||||
COMMENT ON SCHEMA partition_mgmt IS
|
||||
'Partition management utilities for time-series tables';
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 2: Managed table registration
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS partition_mgmt.managed_tables (
|
||||
schema_name TEXT NOT NULL,
|
||||
table_name TEXT NOT NULL,
|
||||
partition_key TEXT NOT NULL,
|
||||
partition_type TEXT NOT NULL,
|
||||
retention_months INT NOT NULL DEFAULT 0,
|
||||
months_ahead INT NOT NULL DEFAULT 3,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (schema_name, table_name)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE partition_mgmt.managed_tables IS
|
||||
'Tracks partitioned tables with retention and creation settings';
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 3: Partition creation function
|
||||
-- ============================================================================
|
||||
|
||||
-- Creates a new partition for a given table and date range
|
||||
CREATE OR REPLACE FUNCTION partition_mgmt.create_partition(
|
||||
p_schema_name TEXT,
|
||||
p_table_name TEXT,
|
||||
p_partition_column TEXT,
|
||||
p_start_date DATE,
|
||||
p_end_date DATE,
|
||||
p_partition_suffix TEXT DEFAULT NULL
|
||||
)
|
||||
RETURNS TEXT
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_partition_name TEXT;
|
||||
v_parent_table TEXT;
|
||||
v_sql TEXT;
|
||||
BEGIN
|
||||
v_parent_table := format('%I.%I', p_schema_name, p_table_name);
|
||||
|
||||
-- Generate partition name: tablename_YYYY_MM or tablename_YYYY_Q#
|
||||
IF p_partition_suffix IS NOT NULL THEN
|
||||
v_partition_name := format('%s_%s', p_table_name, p_partition_suffix);
|
||||
ELSE
|
||||
v_partition_name := format('%s_%s', p_table_name, to_char(p_start_date, 'YYYY_MM'));
|
||||
END IF;
|
||||
|
||||
-- Check if partition already exists
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
WHERE n.nspname = p_schema_name AND c.relname = v_partition_name
|
||||
) THEN
|
||||
RAISE NOTICE 'Partition % already exists, skipping', v_partition_name;
|
||||
RETURN v_partition_name;
|
||||
END IF;
|
||||
|
||||
-- Create partition
|
||||
v_sql := format(
|
||||
'CREATE TABLE %I.%I PARTITION OF %s FOR VALUES FROM (%L) TO (%L)',
|
||||
p_schema_name,
|
||||
v_partition_name,
|
||||
v_parent_table,
|
||||
p_start_date,
|
||||
p_end_date
|
||||
);
|
||||
|
||||
EXECUTE v_sql;
|
||||
|
||||
RAISE NOTICE 'Created partition %.%', p_schema_name, v_partition_name;
|
||||
RETURN v_partition_name;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 4: Monthly partition creation helper
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION partition_mgmt.create_monthly_partitions(
|
||||
p_schema_name TEXT,
|
||||
p_table_name TEXT,
|
||||
p_partition_column TEXT,
|
||||
p_start_month DATE,
|
||||
p_months_ahead INT DEFAULT 3
|
||||
)
|
||||
RETURNS SETOF TEXT
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_current_month DATE;
|
||||
v_end_month DATE;
|
||||
v_partition_name TEXT;
|
||||
BEGIN
|
||||
v_current_month := date_trunc('month', p_start_month)::DATE;
|
||||
v_end_month := date_trunc('month', NOW() + (p_months_ahead || ' months')::INTERVAL)::DATE;
|
||||
|
||||
WHILE v_current_month <= v_end_month LOOP
|
||||
v_partition_name := partition_mgmt.create_partition(
|
||||
p_schema_name,
|
||||
p_table_name,
|
||||
p_partition_column,
|
||||
v_current_month,
|
||||
(v_current_month + INTERVAL '1 month')::DATE
|
||||
);
|
||||
RETURN NEXT v_partition_name;
|
||||
v_current_month := (v_current_month + INTERVAL '1 month')::DATE;
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 5: Quarterly partition creation helper
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION partition_mgmt.create_quarterly_partitions(
|
||||
p_schema_name TEXT,
|
||||
p_table_name TEXT,
|
||||
p_partition_column TEXT,
|
||||
p_start_quarter DATE,
|
||||
p_quarters_ahead INT DEFAULT 2
|
||||
)
|
||||
RETURNS SETOF TEXT
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_current_quarter DATE;
|
||||
v_end_quarter DATE;
|
||||
v_partition_name TEXT;
|
||||
v_suffix TEXT;
|
||||
BEGIN
|
||||
v_current_quarter := date_trunc('quarter', p_start_quarter)::DATE;
|
||||
v_end_quarter := date_trunc('quarter', NOW() + (p_quarters_ahead * 3 || ' months')::INTERVAL)::DATE;
|
||||
|
||||
WHILE v_current_quarter <= v_end_quarter LOOP
|
||||
-- Generate suffix like 2025_Q1, 2025_Q2, etc.
|
||||
v_suffix := to_char(v_current_quarter, 'YYYY') || '_Q' ||
|
||||
EXTRACT(QUARTER FROM v_current_quarter)::TEXT;
|
||||
|
||||
v_partition_name := partition_mgmt.create_partition(
|
||||
p_schema_name,
|
||||
p_table_name,
|
||||
p_partition_column,
|
||||
v_current_quarter,
|
||||
(v_current_quarter + INTERVAL '3 months')::DATE,
|
||||
v_suffix
|
||||
);
|
||||
RETURN NEXT v_partition_name;
|
||||
v_current_quarter := (v_current_quarter + INTERVAL '3 months')::DATE;
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 6: Ensure future partitions exist
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION partition_mgmt.ensure_future_partitions(
|
||||
p_schema_name TEXT,
|
||||
p_table_name TEXT,
|
||||
p_months_ahead INT
|
||||
)
|
||||
RETURNS INT
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_partition_key TEXT;
|
||||
v_partition_type TEXT;
|
||||
v_months_ahead INT;
|
||||
v_created INT := 0;
|
||||
v_current DATE;
|
||||
v_end DATE;
|
||||
v_suffix TEXT;
|
||||
v_partition_name TEXT;
|
||||
BEGIN
|
||||
SELECT partition_key, partition_type, months_ahead
|
||||
INTO v_partition_key, v_partition_type, v_months_ahead
|
||||
FROM partition_mgmt.managed_tables
|
||||
WHERE schema_name = p_schema_name
|
||||
AND table_name = p_table_name;
|
||||
|
||||
IF v_partition_key IS NULL THEN
|
||||
RETURN 0;
|
||||
END IF;
|
||||
|
||||
IF p_months_ahead IS NOT NULL AND p_months_ahead > 0 THEN
|
||||
v_months_ahead := p_months_ahead;
|
||||
END IF;
|
||||
|
||||
IF v_months_ahead IS NULL OR v_months_ahead <= 0 THEN
|
||||
RETURN 0;
|
||||
END IF;
|
||||
|
||||
v_partition_type := lower(coalesce(v_partition_type, 'monthly'));
|
||||
|
||||
IF v_partition_type = 'monthly' THEN
|
||||
v_current := date_trunc('month', NOW())::DATE;
|
||||
v_end := date_trunc('month', NOW() + (v_months_ahead || ' months')::INTERVAL)::DATE;
|
||||
|
||||
WHILE v_current <= v_end LOOP
|
||||
v_partition_name := format('%s_%s', p_table_name, to_char(v_current, 'YYYY_MM'));
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
WHERE n.nspname = p_schema_name AND c.relname = v_partition_name
|
||||
) THEN
|
||||
PERFORM partition_mgmt.create_partition(
|
||||
p_schema_name,
|
||||
p_table_name,
|
||||
v_partition_key,
|
||||
v_current,
|
||||
(v_current + INTERVAL '1 month')::DATE
|
||||
);
|
||||
v_created := v_created + 1;
|
||||
END IF;
|
||||
|
||||
v_current := (v_current + INTERVAL '1 month')::DATE;
|
||||
END LOOP;
|
||||
ELSIF v_partition_type = 'quarterly' THEN
|
||||
v_current := date_trunc('quarter', NOW())::DATE;
|
||||
v_end := date_trunc('quarter', NOW() + (v_months_ahead || ' months')::INTERVAL)::DATE;
|
||||
|
||||
WHILE v_current <= v_end LOOP
|
||||
v_suffix := to_char(v_current, 'YYYY') || '_Q' ||
|
||||
EXTRACT(QUARTER FROM v_current)::TEXT;
|
||||
v_partition_name := format('%s_%s', p_table_name, v_suffix);
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
WHERE n.nspname = p_schema_name AND c.relname = v_partition_name
|
||||
) THEN
|
||||
PERFORM partition_mgmt.create_partition(
|
||||
p_schema_name,
|
||||
p_table_name,
|
||||
v_partition_key,
|
||||
v_current,
|
||||
(v_current + INTERVAL '3 months')::DATE,
|
||||
v_suffix
|
||||
);
|
||||
v_created := v_created + 1;
|
||||
END IF;
|
||||
|
||||
v_current := (v_current + INTERVAL '3 months')::DATE;
|
||||
END LOOP;
|
||||
END IF;
|
||||
|
||||
RETURN v_created;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 7: Retention enforcement function
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION partition_mgmt.enforce_retention(
|
||||
p_schema_name TEXT,
|
||||
p_table_name TEXT,
|
||||
p_retention_months INT
|
||||
)
|
||||
RETURNS INT
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_retention_months INT;
|
||||
v_cutoff_date DATE;
|
||||
v_partition RECORD;
|
||||
v_dropped INT := 0;
|
||||
BEGIN
|
||||
SELECT retention_months
|
||||
INTO v_retention_months
|
||||
FROM partition_mgmt.managed_tables
|
||||
WHERE schema_name = p_schema_name
|
||||
AND table_name = p_table_name;
|
||||
|
||||
IF p_retention_months IS NOT NULL AND p_retention_months > 0 THEN
|
||||
v_retention_months := p_retention_months;
|
||||
END IF;
|
||||
|
||||
IF v_retention_months IS NULL OR v_retention_months <= 0 THEN
|
||||
RETURN 0;
|
||||
END IF;
|
||||
|
||||
v_cutoff_date := (NOW() - (v_retention_months || ' months')::INTERVAL)::DATE;
|
||||
|
||||
FOR v_partition IN
|
||||
SELECT partition_name, partition_end
|
||||
FROM partition_mgmt.partition_stats
|
||||
WHERE schema_name = p_schema_name
|
||||
AND table_name = p_table_name
|
||||
LOOP
|
||||
IF v_partition.partition_end IS NOT NULL AND v_partition.partition_end < v_cutoff_date THEN
|
||||
EXECUTE format('DROP TABLE IF EXISTS %I.%I', p_schema_name, v_partition.partition_name);
|
||||
v_dropped := v_dropped + 1;
|
||||
END IF;
|
||||
END LOOP;
|
||||
|
||||
RETURN v_dropped;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 8: Partition detach and archive function
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION partition_mgmt.detach_partition(
|
||||
p_schema_name TEXT,
|
||||
p_table_name TEXT,
|
||||
p_partition_name TEXT,
|
||||
p_archive_schema TEXT DEFAULT 'archive'
|
||||
)
|
||||
RETURNS BOOLEAN
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_parent_table TEXT;
|
||||
v_partition_full TEXT;
|
||||
v_archive_table TEXT;
|
||||
BEGIN
|
||||
v_parent_table := format('%I.%I', p_schema_name, p_table_name);
|
||||
v_partition_full := format('%I.%I', p_schema_name, p_partition_name);
|
||||
v_archive_table := format('%I.%I', p_archive_schema, p_partition_name);
|
||||
|
||||
-- Create archive schema if not exists
|
||||
EXECUTE format('CREATE SCHEMA IF NOT EXISTS %I', p_archive_schema);
|
||||
|
||||
-- Detach partition
|
||||
EXECUTE format(
|
||||
'ALTER TABLE %s DETACH PARTITION %s',
|
||||
v_parent_table,
|
||||
v_partition_full
|
||||
);
|
||||
|
||||
-- Move to archive schema
|
||||
EXECUTE format(
|
||||
'ALTER TABLE %s SET SCHEMA %I',
|
||||
v_partition_full,
|
||||
p_archive_schema
|
||||
);
|
||||
|
||||
RAISE NOTICE 'Detached and archived partition % to %', p_partition_name, v_archive_table;
|
||||
RETURN TRUE;
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN
|
||||
RAISE WARNING 'Failed to detach partition %: %', p_partition_name, SQLERRM;
|
||||
RETURN FALSE;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 9: Partition retention cleanup function
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION partition_mgmt.cleanup_old_partitions(
|
||||
p_schema_name TEXT,
|
||||
p_table_name TEXT,
|
||||
p_retention_months INT,
|
||||
p_archive_schema TEXT DEFAULT 'archive',
|
||||
p_dry_run BOOLEAN DEFAULT TRUE
|
||||
)
|
||||
RETURNS TABLE(partition_name TEXT, action TEXT)
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_cutoff_date DATE;
|
||||
v_partition RECORD;
|
||||
v_partition_end DATE;
|
||||
BEGIN
|
||||
v_cutoff_date := (NOW() - (p_retention_months || ' months')::INTERVAL)::DATE;
|
||||
|
||||
FOR v_partition IN
|
||||
SELECT c.relname as name,
|
||||
pg_get_expr(c.relpartbound, c.oid) as bound_expr
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
JOIN pg_inherits i ON c.oid = i.inhrelid
|
||||
JOIN pg_class parent ON i.inhparent = parent.oid
|
||||
WHERE n.nspname = p_schema_name
|
||||
AND parent.relname = p_table_name
|
||||
AND c.relkind = 'r'
|
||||
LOOP
|
||||
-- Parse the partition bound to get end date
|
||||
-- Format: FOR VALUES FROM ('2024-01-01') TO ('2024-02-01')
|
||||
v_partition_end := (regexp_match(v_partition.bound_expr,
|
||||
'TO \(''([^'']+)''\)'))[1]::DATE;
|
||||
|
||||
IF v_partition_end IS NOT NULL AND v_partition_end < v_cutoff_date THEN
|
||||
partition_name := v_partition.name;
|
||||
|
||||
IF p_dry_run THEN
|
||||
action := 'WOULD_ARCHIVE';
|
||||
ELSE
|
||||
IF partition_mgmt.detach_partition(
|
||||
p_schema_name, p_table_name, v_partition.name, p_archive_schema
|
||||
) THEN
|
||||
action := 'ARCHIVED';
|
||||
ELSE
|
||||
action := 'FAILED';
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
RETURN NEXT;
|
||||
END IF;
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 10: Partition statistics view
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE VIEW partition_mgmt.partition_stats AS
|
||||
SELECT
|
||||
n.nspname AS schema_name,
|
||||
parent.relname AS table_name,
|
||||
c.relname AS partition_name,
|
||||
pg_get_expr(c.relpartbound, c.oid) AS partition_range,
|
||||
(regexp_match(pg_get_expr(c.relpartbound, c.oid), 'FROM \(''([^'']+)''\)'))[1]::DATE AS partition_start,
|
||||
(regexp_match(pg_get_expr(c.relpartbound, c.oid), 'TO \(''([^'']+)''\)'))[1]::DATE AS partition_end,
|
||||
pg_size_pretty(pg_relation_size(c.oid)) AS size,
|
||||
pg_relation_size(c.oid) AS size_bytes,
|
||||
COALESCE(s.n_live_tup, 0) AS estimated_rows,
|
||||
s.last_vacuum,
|
||||
s.last_autovacuum,
|
||||
s.last_analyze,
|
||||
s.last_autoanalyze
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
JOIN pg_inherits i ON c.oid = i.inhrelid
|
||||
JOIN pg_class parent ON i.inhparent = parent.oid
|
||||
LEFT JOIN pg_stat_user_tables s ON c.oid = s.relid
|
||||
WHERE c.relkind = 'r'
|
||||
AND parent.relkind = 'p'
|
||||
ORDER BY n.nspname, parent.relname, c.relname;
|
||||
|
||||
COMMENT ON VIEW partition_mgmt.partition_stats IS
|
||||
'Statistics for all partitioned tables in the database';
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 11: BRIN index optimization helper
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION partition_mgmt.create_brin_index_if_not_exists(
|
||||
p_schema_name TEXT,
|
||||
p_table_name TEXT,
|
||||
p_column_name TEXT,
|
||||
p_pages_per_range INT DEFAULT 128
|
||||
)
|
||||
RETURNS BOOLEAN
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_index_name TEXT;
|
||||
v_sql TEXT;
|
||||
BEGIN
|
||||
v_index_name := format('brin_%s_%s', p_table_name, p_column_name);
|
||||
|
||||
-- Check if index exists
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM pg_indexes
|
||||
WHERE schemaname = p_schema_name AND indexname = v_index_name
|
||||
) THEN
|
||||
RAISE NOTICE 'BRIN index % already exists', v_index_name;
|
||||
RETURN FALSE;
|
||||
END IF;
|
||||
|
||||
v_sql := format(
|
||||
'CREATE INDEX %I ON %I.%I USING brin (%I) WITH (pages_per_range = %s)',
|
||||
v_index_name,
|
||||
p_schema_name,
|
||||
p_table_name,
|
||||
p_column_name,
|
||||
p_pages_per_range
|
||||
);
|
||||
|
||||
EXECUTE v_sql;
|
||||
|
||||
RAISE NOTICE 'Created BRIN index % on %.%(%)',
|
||||
v_index_name, p_schema_name, p_table_name, p_column_name;
|
||||
RETURN TRUE;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 12: Maintenance job tracking table
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS partition_mgmt.maintenance_log (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
operation TEXT NOT NULL,
|
||||
schema_name TEXT NOT NULL,
|
||||
table_name TEXT NOT NULL,
|
||||
partition_name TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'started',
|
||||
details JSONB NOT NULL DEFAULT '{}',
|
||||
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
completed_at TIMESTAMPTZ,
|
||||
error_message TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX idx_maintenance_log_table ON partition_mgmt.maintenance_log(schema_name, table_name);
|
||||
CREATE INDEX idx_maintenance_log_status ON partition_mgmt.maintenance_log(status, started_at);
|
||||
|
||||
-- ============================================================================
|
||||
-- Step 13: Archive schema for detached partitions
|
||||
-- ============================================================================
|
||||
|
||||
CREATE SCHEMA IF NOT EXISTS archive;
|
||||
|
||||
COMMENT ON SCHEMA archive IS
|
||||
'Storage for detached/archived partitions awaiting deletion or offload';
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- ============================================================================
|
||||
-- Usage Examples (commented out)
|
||||
-- ============================================================================
|
||||
|
||||
/*
|
||||
-- Create monthly partitions for audit table, 3 months ahead
|
||||
SELECT partition_mgmt.create_monthly_partitions(
|
||||
'scheduler', 'audit', 'created_at', '2024-01-01'::DATE, 3
|
||||
);
|
||||
|
||||
-- Preview old partitions that would be archived (dry run)
|
||||
SELECT * FROM partition_mgmt.cleanup_old_partitions(
|
||||
'scheduler', 'audit', 12, 'archive', TRUE
|
||||
);
|
||||
|
||||
-- Actually archive old partitions
|
||||
SELECT * FROM partition_mgmt.cleanup_old_partitions(
|
||||
'scheduler', 'audit', 12, 'archive', FALSE
|
||||
);
|
||||
|
||||
-- View partition statistics
|
||||
SELECT * FROM partition_mgmt.partition_stats
|
||||
WHERE schema_name = 'scheduler'
|
||||
ORDER BY table_name, partition_name;
|
||||
*/
|
||||
143
deploy/database/postgres-partitioning/002_calibration_schema.sql
Normal file
143
deploy/database/postgres-partitioning/002_calibration_schema.sql
Normal file
@@ -0,0 +1,143 @@
|
||||
-- Migration: Trust Vector Calibration Schema
|
||||
-- Sprint: 7100.0002.0002
|
||||
-- Description: Creates schema and tables for trust vector calibration system
|
||||
|
||||
-- Create calibration schema
|
||||
CREATE SCHEMA IF NOT EXISTS excititor_calibration;
|
||||
|
||||
-- Calibration manifests table
|
||||
-- Stores signed manifests for each calibration epoch
|
||||
CREATE TABLE IF NOT EXISTS excititor_calibration.calibration_manifests (
|
||||
manifest_id TEXT PRIMARY KEY,
|
||||
tenant_id TEXT NOT NULL,
|
||||
epoch_number INTEGER NOT NULL,
|
||||
epoch_start_utc TIMESTAMP NOT NULL,
|
||||
epoch_end_utc TIMESTAMP NOT NULL,
|
||||
sample_count INTEGER NOT NULL,
|
||||
learning_rate DOUBLE PRECISION NOT NULL,
|
||||
policy_hash TEXT,
|
||||
lattice_version TEXT NOT NULL,
|
||||
manifest_json JSONB NOT NULL,
|
||||
signature_envelope JSONB,
|
||||
created_at_utc TIMESTAMP NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC'),
|
||||
created_by TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT uq_calibration_manifest_tenant_epoch UNIQUE (tenant_id, epoch_number)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_calibration_manifests_tenant
|
||||
ON excititor_calibration.calibration_manifests(tenant_id);
|
||||
CREATE INDEX idx_calibration_manifests_created
|
||||
ON excititor_calibration.calibration_manifests(created_at_utc DESC);
|
||||
|
||||
-- Trust vector adjustments table
|
||||
-- Records each provider's trust vector changes per epoch
|
||||
CREATE TABLE IF NOT EXISTS excititor_calibration.trust_vector_adjustments (
|
||||
adjustment_id BIGSERIAL PRIMARY KEY,
|
||||
manifest_id TEXT NOT NULL REFERENCES excititor_calibration.calibration_manifests(manifest_id),
|
||||
source_id TEXT NOT NULL,
|
||||
old_provenance DOUBLE PRECISION NOT NULL,
|
||||
old_coverage DOUBLE PRECISION NOT NULL,
|
||||
old_replayability DOUBLE PRECISION NOT NULL,
|
||||
new_provenance DOUBLE PRECISION NOT NULL,
|
||||
new_coverage DOUBLE PRECISION NOT NULL,
|
||||
new_replayability DOUBLE PRECISION NOT NULL,
|
||||
adjustment_magnitude DOUBLE PRECISION NOT NULL,
|
||||
confidence_in_adjustment DOUBLE PRECISION NOT NULL,
|
||||
sample_count_for_source INTEGER NOT NULL,
|
||||
created_at_utc TIMESTAMP NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC'),
|
||||
|
||||
CONSTRAINT chk_old_provenance_range CHECK (old_provenance >= 0 AND old_provenance <= 1),
|
||||
CONSTRAINT chk_old_coverage_range CHECK (old_coverage >= 0 AND old_coverage <= 1),
|
||||
CONSTRAINT chk_old_replayability_range CHECK (old_replayability >= 0 AND old_replayability <= 1),
|
||||
CONSTRAINT chk_new_provenance_range CHECK (new_provenance >= 0 AND new_provenance <= 1),
|
||||
CONSTRAINT chk_new_coverage_range CHECK (new_coverage >= 0 AND new_coverage <= 1),
|
||||
CONSTRAINT chk_new_replayability_range CHECK (new_replayability >= 0 AND new_replayability <= 1),
|
||||
CONSTRAINT chk_confidence_range CHECK (confidence_in_adjustment >= 0 AND confidence_in_adjustment <= 1)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_trust_adjustments_manifest
|
||||
ON excititor_calibration.trust_vector_adjustments(manifest_id);
|
||||
CREATE INDEX idx_trust_adjustments_source
|
||||
ON excititor_calibration.trust_vector_adjustments(source_id);
|
||||
|
||||
-- Calibration feedback samples table
|
||||
-- Stores empirical evidence used for calibration
|
||||
CREATE TABLE IF NOT EXISTS excititor_calibration.calibration_samples (
|
||||
sample_id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id TEXT NOT NULL,
|
||||
source_id TEXT NOT NULL,
|
||||
cve_id TEXT NOT NULL,
|
||||
purl TEXT NOT NULL,
|
||||
expected_status TEXT NOT NULL,
|
||||
actual_status TEXT NOT NULL,
|
||||
verdict_confidence DOUBLE PRECISION NOT NULL,
|
||||
is_match BOOLEAN NOT NULL,
|
||||
feedback_source TEXT NOT NULL, -- 'reachability', 'customer_feedback', 'integration_tests'
|
||||
feedback_weight DOUBLE PRECISION NOT NULL DEFAULT 1.0,
|
||||
scan_id TEXT,
|
||||
collected_at_utc TIMESTAMP NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC'),
|
||||
processed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
processed_in_manifest_id TEXT REFERENCES excititor_calibration.calibration_manifests(manifest_id),
|
||||
|
||||
CONSTRAINT chk_verdict_confidence_range CHECK (verdict_confidence >= 0 AND verdict_confidence <= 1),
|
||||
CONSTRAINT chk_feedback_weight_range CHECK (feedback_weight >= 0 AND feedback_weight <= 1)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_calibration_samples_tenant
|
||||
ON excititor_calibration.calibration_samples(tenant_id);
|
||||
CREATE INDEX idx_calibration_samples_source
|
||||
ON excititor_calibration.calibration_samples(source_id);
|
||||
CREATE INDEX idx_calibration_samples_collected
|
||||
ON excititor_calibration.calibration_samples(collected_at_utc DESC);
|
||||
CREATE INDEX idx_calibration_samples_processed
|
||||
ON excititor_calibration.calibration_samples(processed) WHERE NOT processed;
|
||||
|
||||
-- Calibration metrics table
|
||||
-- Tracks performance metrics per source/severity/status
|
||||
CREATE TABLE IF NOT EXISTS excititor_calibration.calibration_metrics (
|
||||
metric_id BIGSERIAL PRIMARY KEY,
|
||||
manifest_id TEXT NOT NULL REFERENCES excititor_calibration.calibration_manifests(manifest_id),
|
||||
source_id TEXT,
|
||||
severity TEXT,
|
||||
status TEXT,
|
||||
precision DOUBLE PRECISION NOT NULL,
|
||||
recall DOUBLE PRECISION NOT NULL,
|
||||
f1_score DOUBLE PRECISION NOT NULL,
|
||||
false_positive_rate DOUBLE PRECISION NOT NULL,
|
||||
false_negative_rate DOUBLE PRECISION NOT NULL,
|
||||
sample_count INTEGER NOT NULL,
|
||||
created_at_utc TIMESTAMP NOT NULL DEFAULT (NOW() AT TIME ZONE 'UTC'),
|
||||
|
||||
CONSTRAINT chk_precision_range CHECK (precision >= 0 AND precision <= 1),
|
||||
CONSTRAINT chk_recall_range CHECK (recall >= 0 AND recall <= 1),
|
||||
CONSTRAINT chk_f1_range CHECK (f1_score >= 0 AND f1_score <= 1),
|
||||
CONSTRAINT chk_fpr_range CHECK (false_positive_rate >= 0 AND false_positive_rate <= 1),
|
||||
CONSTRAINT chk_fnr_range CHECK (false_negative_rate >= 0 AND false_negative_rate <= 1)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_calibration_metrics_manifest
|
||||
ON excititor_calibration.calibration_metrics(manifest_id);
|
||||
CREATE INDEX idx_calibration_metrics_source
|
||||
ON excititor_calibration.calibration_metrics(source_id) WHERE source_id IS NOT NULL;
|
||||
|
||||
-- Grant permissions to excititor service role
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'excititor_service') THEN
|
||||
GRANT USAGE ON SCHEMA excititor_calibration TO excititor_service;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA excititor_calibration TO excititor_service;
|
||||
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA excititor_calibration TO excititor_service;
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA excititor_calibration
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO excititor_service;
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA excititor_calibration
|
||||
GRANT USAGE, SELECT ON SEQUENCES TO excititor_service;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Comments for documentation
|
||||
COMMENT ON SCHEMA excititor_calibration IS 'Trust vector calibration data for VEX source scoring';
|
||||
COMMENT ON TABLE excititor_calibration.calibration_manifests IS 'Signed calibration epoch results';
|
||||
COMMENT ON TABLE excititor_calibration.trust_vector_adjustments IS 'Per-source trust vector changes per epoch';
|
||||
COMMENT ON TABLE excititor_calibration.calibration_samples IS 'Empirical feedback samples for calibration';
|
||||
COMMENT ON TABLE excititor_calibration.calibration_metrics IS 'Performance metrics per calibration epoch';
|
||||
@@ -0,0 +1,97 @@
|
||||
-- Provcache schema migration
|
||||
-- Run as: psql -d stellaops -f create_provcache_schema.sql
|
||||
|
||||
-- Create schema
|
||||
CREATE SCHEMA IF NOT EXISTS provcache;
|
||||
|
||||
-- Main cache items table
|
||||
CREATE TABLE IF NOT EXISTS provcache.provcache_items (
|
||||
verikey TEXT PRIMARY KEY,
|
||||
digest_version TEXT NOT NULL DEFAULT 'v1',
|
||||
verdict_hash TEXT NOT NULL,
|
||||
proof_root TEXT NOT NULL,
|
||||
replay_seed JSONB NOT NULL,
|
||||
policy_hash TEXT NOT NULL,
|
||||
signer_set_hash TEXT NOT NULL,
|
||||
feed_epoch TEXT NOT NULL,
|
||||
trust_score INTEGER NOT NULL CHECK (trust_score >= 0 AND trust_score <= 100),
|
||||
hit_count BIGINT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
last_accessed_at TIMESTAMPTZ,
|
||||
|
||||
-- Constraint: expires_at must be after created_at
|
||||
CONSTRAINT provcache_items_expires_check CHECK (expires_at > created_at)
|
||||
);
|
||||
|
||||
-- Indexes for invalidation queries
|
||||
CREATE INDEX IF NOT EXISTS idx_provcache_policy_hash
|
||||
ON provcache.provcache_items(policy_hash);
|
||||
CREATE INDEX IF NOT EXISTS idx_provcache_signer_set_hash
|
||||
ON provcache.provcache_items(signer_set_hash);
|
||||
CREATE INDEX IF NOT EXISTS idx_provcache_feed_epoch
|
||||
ON provcache.provcache_items(feed_epoch);
|
||||
CREATE INDEX IF NOT EXISTS idx_provcache_expires_at
|
||||
ON provcache.provcache_items(expires_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_provcache_created_at
|
||||
ON provcache.provcache_items(created_at);
|
||||
|
||||
-- Evidence chunks table for large evidence storage
|
||||
CREATE TABLE IF NOT EXISTS provcache.prov_evidence_chunks (
|
||||
chunk_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
proof_root TEXT NOT NULL,
|
||||
chunk_index INTEGER NOT NULL,
|
||||
chunk_hash TEXT NOT NULL,
|
||||
blob BYTEA NOT NULL,
|
||||
blob_size INTEGER NOT NULL,
|
||||
content_type TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
|
||||
CONSTRAINT prov_evidence_chunks_unique_index
|
||||
UNIQUE (proof_root, chunk_index)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_prov_chunks_proof_root
|
||||
ON provcache.prov_evidence_chunks(proof_root);
|
||||
|
||||
-- Revocation audit log
|
||||
CREATE TABLE IF NOT EXISTS provcache.prov_revocations (
|
||||
revocation_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
revocation_type TEXT NOT NULL,
|
||||
target_hash TEXT NOT NULL,
|
||||
reason TEXT,
|
||||
actor TEXT,
|
||||
entries_affected BIGINT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_prov_revocations_created_at
|
||||
ON provcache.prov_revocations(created_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_prov_revocations_target_hash
|
||||
ON provcache.prov_revocations(target_hash);
|
||||
|
||||
-- Function to update updated_at timestamp
|
||||
CREATE OR REPLACE FUNCTION provcache.update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
-- Trigger for auto-updating updated_at
|
||||
DROP TRIGGER IF EXISTS update_provcache_items_updated_at ON provcache.provcache_items;
|
||||
CREATE TRIGGER update_provcache_items_updated_at
|
||||
BEFORE UPDATE ON provcache.provcache_items
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION provcache.update_updated_at_column();
|
||||
|
||||
-- Grant permissions (adjust role as needed)
|
||||
-- GRANT USAGE ON SCHEMA provcache TO stellaops_app;
|
||||
-- GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA provcache TO stellaops_app;
|
||||
-- GRANT USAGE ON ALL SEQUENCES IN SCHEMA provcache TO stellaops_app;
|
||||
|
||||
COMMENT ON TABLE provcache.provcache_items IS 'Provenance cache entries for cached security decisions';
|
||||
COMMENT ON TABLE provcache.prov_evidence_chunks IS 'Chunked evidence storage for large SBOMs and attestations';
|
||||
COMMENT ON TABLE provcache.prov_revocations IS 'Audit log of cache invalidation events';
|
||||
159
deploy/database/postgres-validation/001_validate_rls.sql
Normal file
159
deploy/database/postgres-validation/001_validate_rls.sql
Normal file
@@ -0,0 +1,159 @@
|
||||
-- RLS Validation Script
|
||||
-- Sprint: SPRINT_3421_0001_0001 - RLS Expansion
|
||||
--
|
||||
-- Purpose: Verify that RLS is properly configured on all tenant-scoped tables
|
||||
-- Run this script after deploying RLS migrations to validate configuration
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 1: List all tables with RLS status
|
||||
-- ============================================================================
|
||||
|
||||
\echo '=== RLS Status for All Schemas ==='
|
||||
|
||||
SELECT
|
||||
schemaname AS schema,
|
||||
tablename AS table_name,
|
||||
rowsecurity AS rls_enabled,
|
||||
forcerowsecurity AS rls_forced,
|
||||
CASE
|
||||
WHEN rowsecurity AND forcerowsecurity THEN 'OK'
|
||||
WHEN rowsecurity AND NOT forcerowsecurity THEN 'WARN: Not forced'
|
||||
ELSE 'MISSING'
|
||||
END AS status
|
||||
FROM pg_tables
|
||||
WHERE schemaname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns')
|
||||
ORDER BY schemaname, tablename;
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 2: List all RLS policies
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== RLS Policies ==='
|
||||
|
||||
SELECT
|
||||
schemaname AS schema,
|
||||
tablename AS table_name,
|
||||
policyname AS policy_name,
|
||||
permissive,
|
||||
roles,
|
||||
cmd AS applies_to,
|
||||
qual IS NOT NULL AS has_using,
|
||||
with_check IS NOT NULL AS has_check
|
||||
FROM pg_policies
|
||||
WHERE schemaname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns')
|
||||
ORDER BY schemaname, tablename, policyname;
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 3: Tables missing RLS that should have it (have tenant_id column)
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== Tables with tenant_id but NO RLS ==='
|
||||
|
||||
SELECT
|
||||
c.table_schema AS schema,
|
||||
c.table_name AS table_name,
|
||||
'MISSING RLS' AS issue
|
||||
FROM information_schema.columns c
|
||||
JOIN pg_tables t ON c.table_schema = t.schemaname AND c.table_name = t.tablename
|
||||
WHERE c.column_name IN ('tenant_id', 'tenant')
|
||||
AND c.table_schema IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns')
|
||||
AND NOT t.rowsecurity
|
||||
ORDER BY c.table_schema, c.table_name;
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 4: Verify helper functions exist
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== RLS Helper Functions ==='
|
||||
|
||||
SELECT
|
||||
n.nspname AS schema,
|
||||
p.proname AS function_name,
|
||||
CASE
|
||||
WHEN p.prosecdef THEN 'SECURITY DEFINER'
|
||||
ELSE 'SECURITY INVOKER'
|
||||
END AS security,
|
||||
CASE
|
||||
WHEN p.provolatile = 's' THEN 'STABLE'
|
||||
WHEN p.provolatile = 'i' THEN 'IMMUTABLE'
|
||||
ELSE 'VOLATILE'
|
||||
END AS volatility
|
||||
FROM pg_proc p
|
||||
JOIN pg_namespace n ON p.pronamespace = n.oid
|
||||
WHERE p.proname = 'require_current_tenant'
|
||||
AND n.nspname LIKE '%_app'
|
||||
ORDER BY n.nspname;
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 5: Test RLS enforcement (expect failure without tenant context)
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== RLS Enforcement Test ==='
|
||||
\echo 'Testing RLS on scheduler.runs (should fail without tenant context)...'
|
||||
|
||||
-- Reset tenant context
|
||||
SELECT set_config('app.tenant_id', '', false);
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
-- This should raise an exception if RLS is working
|
||||
PERFORM * FROM scheduler.runs LIMIT 1;
|
||||
RAISE NOTICE 'WARNING: Query succeeded without tenant context - RLS may not be working!';
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN
|
||||
RAISE NOTICE 'OK: RLS blocked query without tenant context: %', SQLERRM;
|
||||
END
|
||||
$$;
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 6: Admin bypass role verification
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== Admin Bypass Roles ==='
|
||||
|
||||
SELECT
|
||||
rolname AS role_name,
|
||||
rolbypassrls AS can_bypass_rls,
|
||||
rolcanlogin AS can_login
|
||||
FROM pg_roles
|
||||
WHERE rolname LIKE '%_admin'
|
||||
AND rolbypassrls = TRUE
|
||||
ORDER BY rolname;
|
||||
|
||||
-- ============================================================================
|
||||
-- Summary
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== Summary ==='
|
||||
|
||||
SELECT
|
||||
'Total Tables' AS metric,
|
||||
COUNT(*)::TEXT AS value
|
||||
FROM pg_tables
|
||||
WHERE schemaname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns')
|
||||
UNION ALL
|
||||
SELECT
|
||||
'Tables with RLS Enabled',
|
||||
COUNT(*)::TEXT
|
||||
FROM pg_tables
|
||||
WHERE schemaname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns')
|
||||
AND rowsecurity = TRUE
|
||||
UNION ALL
|
||||
SELECT
|
||||
'Tables with RLS Forced',
|
||||
COUNT(*)::TEXT
|
||||
FROM pg_tables
|
||||
WHERE schemaname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns')
|
||||
AND forcerowsecurity = TRUE
|
||||
UNION ALL
|
||||
SELECT
|
||||
'Active Policies',
|
||||
COUNT(*)::TEXT
|
||||
FROM pg_policies
|
||||
WHERE schemaname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns');
|
||||
238
deploy/database/postgres-validation/002_validate_partitions.sql
Normal file
238
deploy/database/postgres-validation/002_validate_partitions.sql
Normal file
@@ -0,0 +1,238 @@
|
||||
-- Partition Validation Script
|
||||
-- Sprint: SPRINT_3422_0001_0001 - Time-Based Partitioning
|
||||
--
|
||||
-- Purpose: Verify that partitioned tables are properly configured and healthy
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 1: List all partitioned tables
|
||||
-- ============================================================================
|
||||
|
||||
\echo '=== Partitioned Tables ==='
|
||||
|
||||
SELECT
|
||||
n.nspname AS schema,
|
||||
c.relname AS table_name,
|
||||
CASE pt.partstrat
|
||||
WHEN 'r' THEN 'RANGE'
|
||||
WHEN 'l' THEN 'LIST'
|
||||
WHEN 'h' THEN 'HASH'
|
||||
END AS partition_strategy,
|
||||
array_to_string(array_agg(a.attname ORDER BY k.col), ', ') AS partition_key
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
JOIN pg_partitioned_table pt ON c.oid = pt.partrelid
|
||||
JOIN LATERAL unnest(pt.partattrs) WITH ORDINALITY AS k(col, idx) ON true
|
||||
LEFT JOIN pg_attribute a ON a.attrelid = c.oid AND a.attnum = k.col
|
||||
WHERE n.nspname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns', 'vuln')
|
||||
GROUP BY n.nspname, c.relname, pt.partstrat
|
||||
ORDER BY n.nspname, c.relname;
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 2: Partition inventory with sizes
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== Partition Inventory ==='
|
||||
|
||||
SELECT
|
||||
n.nspname AS schema,
|
||||
parent.relname AS parent_table,
|
||||
c.relname AS partition_name,
|
||||
pg_get_expr(c.relpartbound, c.oid) AS bounds,
|
||||
pg_size_pretty(pg_relation_size(c.oid)) AS size,
|
||||
s.n_live_tup AS estimated_rows
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
JOIN pg_inherits i ON c.oid = i.inhrelid
|
||||
JOIN pg_class parent ON i.inhparent = parent.oid
|
||||
LEFT JOIN pg_stat_user_tables s ON c.oid = s.relid
|
||||
WHERE n.nspname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns', 'vuln')
|
||||
AND c.relkind = 'r'
|
||||
AND parent.relkind = 'p'
|
||||
ORDER BY n.nspname, parent.relname, c.relname;
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 3: Check for missing future partitions
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== Future Partition Coverage ==='
|
||||
|
||||
WITH partition_bounds AS (
|
||||
SELECT
|
||||
n.nspname AS schema_name,
|
||||
parent.relname AS table_name,
|
||||
c.relname AS partition_name,
|
||||
-- Extract the TO date from partition bound
|
||||
(regexp_match(pg_get_expr(c.relpartbound, c.oid), 'TO \(''([^'']+)''\)'))[1]::DATE AS end_date
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
JOIN pg_inherits i ON c.oid = i.inhrelid
|
||||
JOIN pg_class parent ON i.inhparent = parent.oid
|
||||
WHERE c.relkind = 'r'
|
||||
AND parent.relkind = 'p'
|
||||
AND c.relname NOT LIKE '%_default'
|
||||
),
|
||||
max_bounds AS (
|
||||
SELECT
|
||||
schema_name,
|
||||
table_name,
|
||||
MAX(end_date) AS max_partition_date
|
||||
FROM partition_bounds
|
||||
WHERE end_date IS NOT NULL
|
||||
GROUP BY schema_name, table_name
|
||||
)
|
||||
SELECT
|
||||
schema_name,
|
||||
table_name,
|
||||
max_partition_date,
|
||||
(max_partition_date - CURRENT_DATE) AS days_ahead,
|
||||
CASE
|
||||
WHEN (max_partition_date - CURRENT_DATE) < 30 THEN 'CRITICAL: Create partitions!'
|
||||
WHEN (max_partition_date - CURRENT_DATE) < 60 THEN 'WARNING: Running low'
|
||||
ELSE 'OK'
|
||||
END AS status
|
||||
FROM max_bounds
|
||||
ORDER BY days_ahead;
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 4: Check for orphaned data in default partitions
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== Default Partition Data (should be empty) ==='
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
v_schema TEXT;
|
||||
v_table TEXT;
|
||||
v_count BIGINT;
|
||||
v_sql TEXT;
|
||||
BEGIN
|
||||
FOR v_schema, v_table IN
|
||||
SELECT n.nspname, c.relname
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
WHERE c.relname LIKE '%_default'
|
||||
AND n.nspname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns', 'vuln')
|
||||
LOOP
|
||||
v_sql := format('SELECT COUNT(*) FROM %I.%I', v_schema, v_table);
|
||||
EXECUTE v_sql INTO v_count;
|
||||
|
||||
IF v_count > 0 THEN
|
||||
RAISE NOTICE 'WARNING: %.% has % rows in default partition!',
|
||||
v_schema, v_table, v_count;
|
||||
ELSE
|
||||
RAISE NOTICE 'OK: %.% is empty', v_schema, v_table;
|
||||
END IF;
|
||||
END LOOP;
|
||||
END
|
||||
$$;
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 5: Index health on partitions
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== Partition Index Coverage ==='
|
||||
|
||||
SELECT
|
||||
schemaname AS schema,
|
||||
tablename AS table_name,
|
||||
indexname AS index_name,
|
||||
indexdef
|
||||
FROM pg_indexes
|
||||
WHERE schemaname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns', 'vuln')
|
||||
AND tablename LIKE '%_partitioned' OR tablename LIKE '%_202%'
|
||||
ORDER BY schemaname, tablename, indexname;
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 6: BRIN index effectiveness check
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== BRIN Index Statistics ==='
|
||||
|
||||
SELECT
|
||||
schemaname AS schema,
|
||||
tablename AS table_name,
|
||||
indexrelname AS index_name,
|
||||
idx_scan AS scans,
|
||||
idx_tup_read AS tuples_read,
|
||||
idx_tup_fetch AS tuples_fetched,
|
||||
pg_size_pretty(pg_relation_size(indexrelid)) AS index_size
|
||||
FROM pg_stat_user_indexes
|
||||
WHERE indexrelname LIKE 'brin_%'
|
||||
ORDER BY schemaname, tablename;
|
||||
|
||||
-- ============================================================================
|
||||
-- Part 7: Partition maintenance recommendations
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== Maintenance Recommendations ==='
|
||||
|
||||
WITH partition_ages AS (
|
||||
SELECT
|
||||
n.nspname AS schema_name,
|
||||
parent.relname AS table_name,
|
||||
c.relname AS partition_name,
|
||||
(regexp_match(pg_get_expr(c.relpartbound, c.oid), 'FROM \(''([^'']+)''\)'))[1]::DATE AS start_date,
|
||||
(regexp_match(pg_get_expr(c.relpartbound, c.oid), 'TO \(''([^'']+)''\)'))[1]::DATE AS end_date
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
JOIN pg_inherits i ON c.oid = i.inhrelid
|
||||
JOIN pg_class parent ON i.inhparent = parent.oid
|
||||
WHERE c.relkind = 'r'
|
||||
AND parent.relkind = 'p'
|
||||
AND c.relname NOT LIKE '%_default'
|
||||
)
|
||||
SELECT
|
||||
schema_name,
|
||||
table_name,
|
||||
partition_name,
|
||||
start_date,
|
||||
end_date,
|
||||
(CURRENT_DATE - end_date) AS days_old,
|
||||
CASE
|
||||
WHEN (CURRENT_DATE - end_date) > 365 THEN 'Consider archiving (>1 year old)'
|
||||
WHEN (CURRENT_DATE - end_date) > 180 THEN 'Review retention policy (>6 months old)'
|
||||
ELSE 'Current'
|
||||
END AS recommendation
|
||||
FROM partition_ages
|
||||
WHERE start_date IS NOT NULL
|
||||
ORDER BY schema_name, table_name, start_date;
|
||||
|
||||
-- ============================================================================
|
||||
-- Summary
|
||||
-- ============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== Summary ==='
|
||||
|
||||
SELECT
|
||||
'Partitioned Tables' AS metric,
|
||||
COUNT(DISTINCT parent.relname)::TEXT AS value
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
JOIN pg_inherits i ON c.oid = i.inhrelid
|
||||
JOIN pg_class parent ON i.inhparent = parent.oid
|
||||
WHERE n.nspname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns', 'vuln')
|
||||
AND parent.relkind = 'p'
|
||||
UNION ALL
|
||||
SELECT
|
||||
'Total Partitions',
|
||||
COUNT(*)::TEXT
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
JOIN pg_inherits i ON c.oid = i.inhrelid
|
||||
JOIN pg_class parent ON i.inhparent = parent.oid
|
||||
WHERE n.nspname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns', 'vuln')
|
||||
AND parent.relkind = 'p'
|
||||
UNION ALL
|
||||
SELECT
|
||||
'BRIN Indexes',
|
||||
COUNT(*)::TEXT
|
||||
FROM pg_indexes
|
||||
WHERE indexname LIKE 'brin_%'
|
||||
AND schemaname IN ('scheduler', 'notify', 'authority', 'vex', 'policy', 'unknowns', 'vuln');
|
||||
66
deploy/database/postgres/README.md
Normal file
66
deploy/database/postgres/README.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# PostgreSQL 16 Cluster (staging / production)
|
||||
|
||||
This directory provisions StellaOps PostgreSQL clusters with **CloudNativePG (CNPG)**. It is pinned to Postgres 16.x, includes connection pooling (PgBouncer), Prometheus scraping, and S3-compatible backups. Everything is air-gap friendly: fetch the operator and images once, then render/apply manifests offline.
|
||||
|
||||
## Targets
|
||||
- **Staging:** `stellaops-pg-stg` (2 instances, 200 Gi data, WAL 64 Gi, PgBouncer x2)
|
||||
- **Production:** `stellaops-pg-prod` (3 instances, 500 Gi data, WAL 128 Gi, PgBouncer x3)
|
||||
- **Namespace:** `platform-postgres`
|
||||
|
||||
## Prerequisites
|
||||
- Kubernetes ≥ 1.27 with CSI storage classes `fast-ssd` (data) and `fast-wal` (WAL) available.
|
||||
- CloudNativePG operator 1.23.x mirrored or downloaded to `artifacts/cloudnative-pg-1.23.0.yaml`.
|
||||
- Images mirrored to your registry (example tags):
|
||||
- `ghcr.io/cloudnative-pg/postgresql:16.4`
|
||||
- `ghcr.io/cloudnative-pg/postgresql-operator:1.23.0`
|
||||
- `ghcr.io/cloudnative-pg/pgbouncer:1.23.0`
|
||||
- Secrets created from the templates under `ops/devops/postgres/secrets/` (superuser, app user, backup credentials).
|
||||
|
||||
## Render & Apply (deterministic)
|
||||
```bash
|
||||
# 1) Create namespace
|
||||
kubectl apply -f ops/devops/postgres/namespace.yaml
|
||||
|
||||
# 2) Install operator (offline-friendly: use the pinned manifest you mirrored)
|
||||
kubectl apply -f artifacts/cloudnative-pg-1.23.0.yaml
|
||||
|
||||
# 3) Create secrets (replace passwords/keys first)
|
||||
kubectl apply -f ops/devops/postgres/secrets/example-superuser.yaml
|
||||
kubectl apply -f ops/devops/postgres/secrets/example-app.yaml
|
||||
kubectl apply -f ops/devops/postgres/secrets/example-backup-credentials.yaml
|
||||
|
||||
# 4) Apply the cluster and pooler for the target environment
|
||||
kubectl apply -f ops/devops/postgres/cluster-staging.yaml
|
||||
kubectl apply -f ops/devops/postgres/pooler-staging.yaml
|
||||
# or
|
||||
kubectl apply -f ops/devops/postgres/cluster-production.yaml
|
||||
kubectl apply -f ops/devops/postgres/pooler-production.yaml
|
||||
```
|
||||
|
||||
## Connection Endpoints
|
||||
- RW service: `<cluster>-rw` (e.g., `stellaops-pg-stg-rw:5432`)
|
||||
- RO service: `<cluster>-ro`
|
||||
- PgBouncer pooler: `<pooler-name>` (e.g., `stellaops-pg-stg-pooler:6432`)
|
||||
|
||||
**Application connection string (matches library defaults):**
|
||||
`Host=stellaops-pg-stg-pooler;Port=6432;Username=stellaops_app;Password=<app-password>;Database=stellaops;Pooling=true;Timeout=15;CommandTimeout=30;Ssl Mode=Require;`
|
||||
|
||||
## Monitoring & Backups
|
||||
- `monitoring.enablePodMonitor: true` exposes PodMonitor for Prometheus Operator.
|
||||
- Barman/S3 backups are enabled by default; set `backup.barmanObjectStore.destinationPath` per env and populate `stellaops-pg-backup` credentials.
|
||||
- WAL compression is `gzip`; retention is operator-managed (configure via Barman bucket policies).
|
||||
|
||||
## Alignment with code defaults
|
||||
- Session settings: UTC timezone, 30s `statement_timeout`, tenant context via `set_config('app.current_tenant', ...)`.
|
||||
- Connection pooler uses **transaction** mode with a `server_reset_query` that clears session state, keeping RepositoryBase deterministic.
|
||||
|
||||
## Verification checklist
|
||||
- `kubectl get cluster -n platform-postgres` shows `Ready` replicas matching `instances`.
|
||||
- `kubectl logs deploy/cnpg-controller-manager -n cnpg-system` has no failing webhooks.
|
||||
- `kubectl get podmonitor -n platform-postgres` returns entries for the cluster and pooler.
|
||||
- `psql "<rw-connection-string>" -c 'select 1'` works from CI runner subnet.
|
||||
- `cnpg` `barman-cloud-backup-list` shows successful full + WAL backups.
|
||||
|
||||
## Offline notes
|
||||
- Mirror the operator manifest and container images to the approved registry first; no live downloads occur at runtime.
|
||||
- If Prometheus is not present, leave PodMonitor applied; it is inert without the CRD.
|
||||
57
deploy/database/postgres/cluster-production.yaml
Normal file
57
deploy/database/postgres/cluster-production.yaml
Normal file
@@ -0,0 +1,57 @@
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Cluster
|
||||
metadata:
|
||||
name: stellaops-pg-prod
|
||||
namespace: platform-postgres
|
||||
spec:
|
||||
instances: 3
|
||||
imageName: ghcr.io/cloudnative-pg/postgresql:16.4
|
||||
primaryUpdateStrategy: unsupervised
|
||||
storage:
|
||||
size: 500Gi
|
||||
storageClass: fast-ssd
|
||||
walStorage:
|
||||
size: 128Gi
|
||||
storageClass: fast-wal
|
||||
superuserSecret:
|
||||
name: stellaops-pg-superuser
|
||||
bootstrap:
|
||||
initdb:
|
||||
database: stellaops
|
||||
owner: stellaops_app
|
||||
secret:
|
||||
name: stellaops-pg-app
|
||||
monitoring:
|
||||
enablePodMonitor: true
|
||||
postgresql:
|
||||
parameters:
|
||||
max_connections: "900"
|
||||
shared_buffers: "4096MB"
|
||||
work_mem: "96MB"
|
||||
maintenance_work_mem: "768MB"
|
||||
wal_level: "replica"
|
||||
max_wal_size: "4GB"
|
||||
timezone: "UTC"
|
||||
log_min_duration_statement: "250"
|
||||
statement_timeout: "30000"
|
||||
resources:
|
||||
requests:
|
||||
cpu: "4"
|
||||
memory: "16Gi"
|
||||
limits:
|
||||
cpu: "8"
|
||||
memory: "24Gi"
|
||||
backup:
|
||||
barmanObjectStore:
|
||||
destinationPath: s3://stellaops-backups/production
|
||||
s3Credentials:
|
||||
accessKeyId:
|
||||
name: stellaops-pg-backup
|
||||
key: ACCESS_KEY_ID
|
||||
secretAccessKey:
|
||||
name: stellaops-pg-backup
|
||||
key: SECRET_ACCESS_KEY
|
||||
wal:
|
||||
compression: gzip
|
||||
maxParallel: 4
|
||||
logLevel: info
|
||||
57
deploy/database/postgres/cluster-staging.yaml
Normal file
57
deploy/database/postgres/cluster-staging.yaml
Normal file
@@ -0,0 +1,57 @@
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Cluster
|
||||
metadata:
|
||||
name: stellaops-pg-stg
|
||||
namespace: platform-postgres
|
||||
spec:
|
||||
instances: 2
|
||||
imageName: ghcr.io/cloudnative-pg/postgresql:16.4
|
||||
primaryUpdateStrategy: unsupervised
|
||||
storage:
|
||||
size: 200Gi
|
||||
storageClass: fast-ssd
|
||||
walStorage:
|
||||
size: 64Gi
|
||||
storageClass: fast-wal
|
||||
superuserSecret:
|
||||
name: stellaops-pg-superuser
|
||||
bootstrap:
|
||||
initdb:
|
||||
database: stellaops
|
||||
owner: stellaops_app
|
||||
secret:
|
||||
name: stellaops-pg-app
|
||||
monitoring:
|
||||
enablePodMonitor: true
|
||||
postgresql:
|
||||
parameters:
|
||||
max_connections: "600"
|
||||
shared_buffers: "2048MB"
|
||||
work_mem: "64MB"
|
||||
maintenance_work_mem: "512MB"
|
||||
wal_level: "replica"
|
||||
max_wal_size: "2GB"
|
||||
timezone: "UTC"
|
||||
log_min_duration_statement: "500"
|
||||
statement_timeout: "30000"
|
||||
resources:
|
||||
requests:
|
||||
cpu: "2"
|
||||
memory: "8Gi"
|
||||
limits:
|
||||
cpu: "4"
|
||||
memory: "12Gi"
|
||||
backup:
|
||||
barmanObjectStore:
|
||||
destinationPath: s3://stellaops-backups/staging
|
||||
s3Credentials:
|
||||
accessKeyId:
|
||||
name: stellaops-pg-backup
|
||||
key: ACCESS_KEY_ID
|
||||
secretAccessKey:
|
||||
name: stellaops-pg-backup
|
||||
key: SECRET_ACCESS_KEY
|
||||
wal:
|
||||
compression: gzip
|
||||
maxParallel: 2
|
||||
logLevel: info
|
||||
4
deploy/database/postgres/namespace.yaml
Normal file
4
deploy/database/postgres/namespace.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: platform-postgres
|
||||
29
deploy/database/postgres/pooler-production.yaml
Normal file
29
deploy/database/postgres/pooler-production.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Pooler
|
||||
metadata:
|
||||
name: stellaops-pg-prod-pooler
|
||||
namespace: platform-postgres
|
||||
spec:
|
||||
cluster:
|
||||
name: stellaops-pg-prod
|
||||
instances: 3
|
||||
type: rw
|
||||
pgbouncer:
|
||||
parameters:
|
||||
pool_mode: transaction
|
||||
max_client_conn: "1500"
|
||||
default_pool_size: "80"
|
||||
server_reset_query: "RESET ALL; SET SESSION AUTHORIZATION DEFAULT; SET TIME ZONE 'UTC';"
|
||||
authQuerySecret:
|
||||
name: stellaops-pg-app
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: pgbouncer
|
||||
resources:
|
||||
requests:
|
||||
cpu: "150m"
|
||||
memory: "192Mi"
|
||||
limits:
|
||||
cpu: "750m"
|
||||
memory: "384Mi"
|
||||
29
deploy/database/postgres/pooler-staging.yaml
Normal file
29
deploy/database/postgres/pooler-staging.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Pooler
|
||||
metadata:
|
||||
name: stellaops-pg-stg-pooler
|
||||
namespace: platform-postgres
|
||||
spec:
|
||||
cluster:
|
||||
name: stellaops-pg-stg
|
||||
instances: 2
|
||||
type: rw
|
||||
pgbouncer:
|
||||
parameters:
|
||||
pool_mode: transaction
|
||||
max_client_conn: "800"
|
||||
default_pool_size: "50"
|
||||
server_reset_query: "RESET ALL; SET SESSION AUTHORIZATION DEFAULT; SET TIME ZONE 'UTC';"
|
||||
authQuerySecret:
|
||||
name: stellaops-pg-app
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: pgbouncer
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "128Mi"
|
||||
limits:
|
||||
cpu: "500m"
|
||||
memory: "256Mi"
|
||||
9
deploy/database/postgres/secrets/example-app.yaml
Normal file
9
deploy/database/postgres/secrets/example-app.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: stellaops-pg-app
|
||||
namespace: platform-postgres
|
||||
type: kubernetes.io/basic-auth
|
||||
stringData:
|
||||
username: stellaops_app
|
||||
password: CHANGEME_APP_PASSWORD
|
||||
@@ -0,0 +1,9 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: stellaops-pg-backup
|
||||
namespace: platform-postgres
|
||||
type: Opaque
|
||||
stringData:
|
||||
ACCESS_KEY_ID: CHANGEME_ACCESS_KEY
|
||||
SECRET_ACCESS_KEY: CHANGEME_SECRET_KEY
|
||||
9
deploy/database/postgres/secrets/example-superuser.yaml
Normal file
9
deploy/database/postgres/secrets/example-superuser.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: stellaops-pg-superuser
|
||||
namespace: platform-postgres
|
||||
type: kubernetes.io/basic-auth
|
||||
stringData:
|
||||
username: postgres
|
||||
password: CHANGEME_SUPERUSER_PASSWORD
|
||||
173
deploy/docker/Dockerfile.ci
Normal file
173
deploy/docker/Dockerfile.ci
Normal file
@@ -0,0 +1,173 @@
|
||||
# Dockerfile.ci - Local CI testing container matching Gitea runner environment
|
||||
# Sprint: SPRINT_20251226_006_CICD
|
||||
#
|
||||
# Usage:
|
||||
# docker build -t stellaops-ci:local -f devops/docker/Dockerfile.ci .
|
||||
# docker run --rm -v $(pwd):/src stellaops-ci:local ./devops/scripts/test-local.sh
|
||||
|
||||
FROM ubuntu:22.04
|
||||
|
||||
LABEL org.opencontainers.image.title="StellaOps CI"
|
||||
LABEL org.opencontainers.image.description="Local CI testing environment matching Gitea runner"
|
||||
LABEL org.opencontainers.image.source="https://git.stella-ops.org/stella-ops.org/git.stella-ops.org"
|
||||
|
||||
# Environment variables
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV DOTNET_VERSION=10.0.100
|
||||
ENV NODE_VERSION=20
|
||||
ENV HELM_VERSION=3.16.0
|
||||
ENV COSIGN_VERSION=3.0.4
|
||||
ENV REKOR_VERSION=1.4.3
|
||||
ENV TZ=UTC
|
||||
|
||||
# Disable .NET telemetry
|
||||
ENV DOTNET_NOLOGO=1
|
||||
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||
|
||||
# .NET paths
|
||||
ENV DOTNET_ROOT=/usr/share/dotnet
|
||||
ENV PATH="/usr/share/dotnet:/root/.dotnet/tools:${PATH}"
|
||||
|
||||
# ===========================================================================
|
||||
# BASE DEPENDENCIES
|
||||
# ===========================================================================
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
# Core utilities
|
||||
curl \
|
||||
wget \
|
||||
gnupg2 \
|
||||
ca-certificates \
|
||||
git \
|
||||
unzip \
|
||||
jq \
|
||||
# Build tools
|
||||
build-essential \
|
||||
# Cross-compilation
|
||||
binutils-aarch64-linux-gnu \
|
||||
# Python (for scripts)
|
||||
python3 \
|
||||
python3-pip \
|
||||
# .NET dependencies
|
||||
libicu70 \
|
||||
# Locales
|
||||
locales \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# ===========================================================================
|
||||
# DOCKER CLI & COMPOSE (from official Docker repo)
|
||||
# ===========================================================================
|
||||
|
||||
RUN install -m 0755 -d /etc/apt/keyrings \
|
||||
&& curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc \
|
||||
&& chmod a+r /etc/apt/keyrings/docker.asc \
|
||||
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu jammy stable" > /etc/apt/sources.list.d/docker.list \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y --no-install-recommends docker-ce-cli docker-compose-plugin \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& docker --version
|
||||
|
||||
# Set locale
|
||||
RUN locale-gen en_US.UTF-8
|
||||
ENV LANG=en_US.UTF-8
|
||||
ENV LANGUAGE=en_US:en
|
||||
ENV LC_ALL=en_US.UTF-8
|
||||
|
||||
# ===========================================================================
|
||||
# POSTGRESQL CLIENT 16
|
||||
# ===========================================================================
|
||||
|
||||
RUN curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor -o /usr/share/keyrings/postgresql-archive-keyring.gpg \
|
||||
&& echo "deb [signed-by=/usr/share/keyrings/postgresql-archive-keyring.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y --no-install-recommends postgresql-client-16 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# ===========================================================================
|
||||
# .NET 10 SDK
|
||||
# ===========================================================================
|
||||
|
||||
RUN curl -fsSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh \
|
||||
&& chmod +x /tmp/dotnet-install.sh \
|
||||
&& /tmp/dotnet-install.sh --version ${DOTNET_VERSION} --install-dir /usr/share/dotnet \
|
||||
&& rm /tmp/dotnet-install.sh \
|
||||
&& dotnet --version
|
||||
|
||||
# Install common .NET tools
|
||||
RUN dotnet tool install -g trx2junit \
|
||||
&& dotnet tool install -g dotnet-reportgenerator-globaltool
|
||||
|
||||
# ===========================================================================
|
||||
# NODE.JS 20
|
||||
# ===========================================================================
|
||||
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
|
||||
&& apt-get install -y --no-install-recommends nodejs \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& node --version \
|
||||
&& npm --version
|
||||
|
||||
# ===========================================================================
|
||||
# HELM 3.16.0
|
||||
# ===========================================================================
|
||||
|
||||
RUN curl -fsSL https://get.helm.sh/helm-v${HELM_VERSION}-linux-amd64.tar.gz | \
|
||||
tar -xzf - -C /tmp \
|
||||
&& mv /tmp/linux-amd64/helm /usr/local/bin/helm \
|
||||
&& rm -rf /tmp/linux-amd64 \
|
||||
&& helm version
|
||||
|
||||
# ===========================================================================
|
||||
# COSIGN
|
||||
# ===========================================================================
|
||||
|
||||
RUN curl -fsSL https://github.com/sigstore/cosign/releases/download/v${COSIGN_VERSION}/cosign-linux-amd64 \
|
||||
-o /usr/local/bin/cosign \
|
||||
&& chmod +x /usr/local/bin/cosign \
|
||||
&& cosign version
|
||||
|
||||
# ===========================================================================
|
||||
# REKOR CLI
|
||||
# ===========================================================================
|
||||
|
||||
RUN curl -fsSL https://github.com/sigstore/rekor/releases/download/v${REKOR_VERSION}/rekor-cli-linux-amd64 \
|
||||
-o /usr/local/bin/rekor-cli \
|
||||
&& chmod +x /usr/local/bin/rekor-cli \
|
||||
&& rekor-cli version
|
||||
|
||||
# ===========================================================================
|
||||
# SYFT (SBOM generation)
|
||||
# ===========================================================================
|
||||
|
||||
RUN curl -fsSL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
|
||||
|
||||
# ===========================================================================
|
||||
# SETUP
|
||||
# ===========================================================================
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
# Create non-root user for safer execution (optional)
|
||||
RUN useradd -m -s /bin/bash ciuser \
|
||||
&& mkdir -p /home/ciuser/.dotnet/tools \
|
||||
&& chown -R ciuser:ciuser /home/ciuser
|
||||
|
||||
# Health check script
|
||||
RUN printf '%s\n' \
|
||||
'#!/bin/bash' \
|
||||
'set -e' \
|
||||
'echo "=== CI Environment Health Check ==="' \
|
||||
'echo "OS: $(cat /etc/os-release | grep PRETTY_NAME | cut -d= -f2)"' \
|
||||
'echo ".NET: $(dotnet --version)"' \
|
||||
'echo "Node: $(node --version)"' \
|
||||
'echo "npm: $(npm --version)"' \
|
||||
'echo "Helm: $(helm version --short)"' \
|
||||
'echo "Cosign: $(cosign version 2>&1 | head -1)"' \
|
||||
'echo "Rekor CLI: $(rekor-cli version 2>&1 | head -1)"' \
|
||||
'echo "Docker: $(docker --version 2>/dev/null || echo Not available)"' \
|
||||
'echo "PostgreSQL client: $(psql --version)"' \
|
||||
'echo "=== All checks passed ==="' \
|
||||
> /usr/local/bin/ci-health-check \
|
||||
&& chmod +x /usr/local/bin/ci-health-check
|
||||
|
||||
ENTRYPOINT ["/bin/bash"]
|
||||
40
deploy/docker/Dockerfile.console
Normal file
40
deploy/docker/Dockerfile.console
Normal file
@@ -0,0 +1,40 @@
|
||||
# syntax=docker/dockerfile:1.7
|
||||
# Multi-stage Angular console image with non-root runtime (DOCKER-44-001)
|
||||
ARG NODE_IMAGE=node:20-bullseye-slim
|
||||
ARG NGINX_IMAGE=nginxinc/nginx-unprivileged:1.27-alpine
|
||||
ARG APP_DIR=src/Web/StellaOps.Web
|
||||
ARG DIST_DIR=dist
|
||||
ARG APP_PORT=8080
|
||||
|
||||
FROM ${NODE_IMAGE} AS build
|
||||
ENV npm_config_fund=false npm_config_audit=false SOURCE_DATE_EPOCH=1704067200
|
||||
WORKDIR /app
|
||||
COPY ${APP_DIR}/package*.json ./
|
||||
RUN npm ci --prefer-offline --no-progress --cache .npm
|
||||
COPY ${APP_DIR}/ ./
|
||||
RUN npm run build -- --configuration=production --output-path=${DIST_DIR}
|
||||
|
||||
FROM ${NGINX_IMAGE} AS runtime
|
||||
ARG APP_PORT
|
||||
ENV APP_PORT=${APP_PORT}
|
||||
USER 101
|
||||
WORKDIR /
|
||||
COPY --from=build /app/${DIST_DIR}/ /usr/share/nginx/html/
|
||||
COPY ops/devops/docker/healthcheck-frontend.sh /usr/local/bin/healthcheck-frontend.sh
|
||||
RUN rm -f /etc/nginx/conf.d/default.conf && \
|
||||
cat > /etc/nginx/conf.d/default.conf <<CONF
|
||||
server {
|
||||
listen ${APP_PORT};
|
||||
listen [::]:${APP_PORT};
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
location / {
|
||||
try_files $$uri $$uri/ /index.html;
|
||||
}
|
||||
}
|
||||
CONF
|
||||
|
||||
EXPOSE ${APP_PORT}
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||
CMD /usr/local/bin/healthcheck-frontend.sh
|
||||
CMD ["nginx","-g","daemon off;"]
|
||||
172
deploy/docker/Dockerfile.crypto-profile
Normal file
172
deploy/docker/Dockerfile.crypto-profile
Normal file
@@ -0,0 +1,172 @@
|
||||
# syntax=docker/dockerfile:1.4
|
||||
# StellaOps Regional Crypto Profile
|
||||
# Selects regional cryptographic configuration at build time
|
||||
|
||||
# ============================================================================
|
||||
# Build Arguments
|
||||
# ============================================================================
|
||||
ARG CRYPTO_PROFILE=international
|
||||
ARG BASE_IMAGE=stellaops/platform:latest
|
||||
ARG SERVICE_NAME=authority
|
||||
|
||||
# ============================================================================
|
||||
# Regional Crypto Profile Layer
|
||||
# ============================================================================
|
||||
FROM ${BASE_IMAGE} AS regional-profile
|
||||
|
||||
# Copy regional cryptographic configuration
|
||||
ARG CRYPTO_PROFILE
|
||||
COPY etc/appsettings.crypto.${CRYPTO_PROFILE}.yaml /app/etc/appsettings.crypto.yaml
|
||||
COPY etc/crypto-plugins-manifest.json /app/etc/crypto-plugins-manifest.json
|
||||
|
||||
# Set environment variable for runtime verification
|
||||
ENV STELLAOPS_CRYPTO_PROFILE=${CRYPTO_PROFILE}
|
||||
ENV STELLAOPS_CRYPTO_CONFIG_PATH=/app/etc/appsettings.crypto.yaml
|
||||
ENV STELLAOPS_CRYPTO_MANIFEST_PATH=/app/etc/crypto-plugins-manifest.json
|
||||
|
||||
# Add labels for metadata
|
||||
LABEL com.stellaops.crypto.profile="${CRYPTO_PROFILE}"
|
||||
LABEL com.stellaops.crypto.config="/app/etc/appsettings.crypto.${CRYPTO_PROFILE}.yaml"
|
||||
LABEL com.stellaops.crypto.runtime-selection="true"
|
||||
|
||||
# ============================================================================
|
||||
# Service-Specific Regional Images
|
||||
# ============================================================================
|
||||
|
||||
# Authority with Regional Crypto
|
||||
FROM regional-profile AS authority
|
||||
WORKDIR /app/authority
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Authority.WebService.dll"]
|
||||
|
||||
# Signer with Regional Crypto
|
||||
FROM regional-profile AS signer
|
||||
WORKDIR /app/signer
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Signer.WebService.dll"]
|
||||
|
||||
# Attestor with Regional Crypto
|
||||
FROM regional-profile AS attestor
|
||||
WORKDIR /app/attestor
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Attestor.WebService.dll"]
|
||||
|
||||
# Concelier with Regional Crypto
|
||||
FROM regional-profile AS concelier
|
||||
WORKDIR /app/concelier
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Concelier.WebService.dll"]
|
||||
|
||||
# Scanner with Regional Crypto
|
||||
FROM regional-profile AS scanner
|
||||
WORKDIR /app/scanner
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Scanner.WebService.dll"]
|
||||
|
||||
# Excititor with Regional Crypto
|
||||
FROM regional-profile AS excititor
|
||||
WORKDIR /app/excititor
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Excititor.WebService.dll"]
|
||||
|
||||
# Policy with Regional Crypto
|
||||
FROM regional-profile AS policy
|
||||
WORKDIR /app/policy
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Policy.WebService.dll"]
|
||||
|
||||
# Scheduler with Regional Crypto
|
||||
FROM regional-profile AS scheduler
|
||||
WORKDIR /app/scheduler
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Scheduler.WebService.dll"]
|
||||
|
||||
# Notify with Regional Crypto
|
||||
FROM regional-profile AS notify
|
||||
WORKDIR /app/notify
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Notify.WebService.dll"]
|
||||
|
||||
# Zastava with Regional Crypto
|
||||
FROM regional-profile AS zastava
|
||||
WORKDIR /app/zastava
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Zastava.WebService.dll"]
|
||||
|
||||
# Gateway with Regional Crypto
|
||||
FROM regional-profile AS gateway
|
||||
WORKDIR /app/gateway
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Gateway.WebService.dll"]
|
||||
|
||||
# AirGap Importer with Regional Crypto
|
||||
FROM regional-profile AS airgap-importer
|
||||
WORKDIR /app/airgap-importer
|
||||
ENTRYPOINT ["dotnet", "StellaOps.AirGap.Importer.dll"]
|
||||
|
||||
# AirGap Exporter with Regional Crypto
|
||||
FROM regional-profile AS airgap-exporter
|
||||
WORKDIR /app/airgap-exporter
|
||||
ENTRYPOINT ["dotnet", "StellaOps.AirGap.Exporter.dll"]
|
||||
|
||||
# CLI with Regional Crypto
|
||||
FROM regional-profile AS cli
|
||||
WORKDIR /app/cli
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Cli.dll"]
|
||||
|
||||
# ============================================================================
|
||||
# Build Instructions
|
||||
# ============================================================================
|
||||
# Build international profile (default):
|
||||
# docker build -f deploy/docker/Dockerfile.crypto-profile \
|
||||
# --build-arg CRYPTO_PROFILE=international \
|
||||
# --target authority \
|
||||
# -t stellaops/authority:international .
|
||||
#
|
||||
# Build Russia (GOST) profile:
|
||||
# docker build -f deploy/docker/Dockerfile.crypto-profile \
|
||||
# --build-arg CRYPTO_PROFILE=russia \
|
||||
# --target scanner \
|
||||
# -t stellaops/scanner:russia .
|
||||
#
|
||||
# Build EU (eIDAS) profile:
|
||||
# docker build -f deploy/docker/Dockerfile.crypto-profile \
|
||||
# --build-arg CRYPTO_PROFILE=eu \
|
||||
# --target signer \
|
||||
# -t stellaops/signer:eu .
|
||||
#
|
||||
# Build China (SM) profile:
|
||||
# docker build -f deploy/docker/Dockerfile.crypto-profile \
|
||||
# --build-arg CRYPTO_PROFILE=china \
|
||||
# --target attestor \
|
||||
# -t stellaops/attestor:china .
|
||||
#
|
||||
# ============================================================================
|
||||
# Regional Profile Descriptions
|
||||
# ============================================================================
|
||||
# international: Default NIST algorithms (ES256, RS256, SHA-256)
|
||||
# Uses offline-verification plugin
|
||||
# Jurisdiction: world
|
||||
#
|
||||
# russia: GOST R 34.10-2012, GOST R 34.11-2012
|
||||
# Uses CryptoPro CSP plugin
|
||||
# Jurisdiction: russia
|
||||
# Requires: CryptoPro CSP SDK
|
||||
#
|
||||
# eu: eIDAS-compliant qualified trust services
|
||||
# Uses eIDAS plugin with qualified certificates
|
||||
# Jurisdiction: eu
|
||||
# Requires: eIDAS trust service provider integration
|
||||
#
|
||||
# china: SM2, SM3, SM4 algorithms
|
||||
# Uses SM crypto plugin
|
||||
# Jurisdiction: china
|
||||
# Requires: GmSSL or BouncyCastle SM extensions
|
||||
#
|
||||
# ============================================================================
|
||||
# Runtime Configuration
|
||||
# ============================================================================
|
||||
# The crypto provider is selected at runtime based on:
|
||||
# 1. STELLAOPS_CRYPTO_PROFILE environment variable
|
||||
# 2. /app/etc/appsettings.crypto.yaml configuration file
|
||||
# 3. /app/etc/crypto-plugins-manifest.json plugin metadata
|
||||
#
|
||||
# Plugin loading sequence:
|
||||
# 1. Application starts
|
||||
# 2. CryptoPluginLoader reads /app/etc/appsettings.crypto.yaml
|
||||
# 3. Loads enabled plugins from manifest
|
||||
# 4. Validates platform compatibility
|
||||
# 5. Validates jurisdiction compliance
|
||||
# 6. Registers providers with DI container
|
||||
# 7. Application uses ICryptoProvider abstraction
|
||||
#
|
||||
# No cryptographic code is executed until runtime plugin selection completes.
|
||||
56
deploy/docker/Dockerfile.hardened.template
Normal file
56
deploy/docker/Dockerfile.hardened.template
Normal file
@@ -0,0 +1,56 @@
|
||||
# syntax=docker/dockerfile:1.7
|
||||
# Hardened multi-stage template for StellaOps services
|
||||
# Parameters are build-time ARGs so this file can be re-used across services.
|
||||
|
||||
ARG SDK_IMAGE=mcr.microsoft.com/dotnet/sdk:10.0-bookworm-slim
|
||||
ARG RUNTIME_IMAGE=mcr.microsoft.com/dotnet/aspnet:10.0-bookworm-slim
|
||||
ARG APP_PROJECT=src/Service/Service.csproj
|
||||
ARG CONFIGURATION=Release
|
||||
ARG PUBLISH_DIR=/app/publish
|
||||
ARG APP_BINARY=StellaOps.Service
|
||||
ARG APP_USER=stella
|
||||
ARG APP_UID=10001
|
||||
ARG APP_GID=10001
|
||||
ARG APP_PORT=8080
|
||||
|
||||
FROM ${SDK_IMAGE} AS build
|
||||
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 \
|
||||
DOTNET_NOLOGO=1 \
|
||||
SOURCE_DATE_EPOCH=1704067200
|
||||
WORKDIR /src
|
||||
# Expect restore sources to be available offline via /.nuget/
|
||||
COPY . .
|
||||
RUN dotnet restore ${APP_PROJECT} --packages /.nuget/packages && \
|
||||
dotnet publish ${APP_PROJECT} -c ${CONFIGURATION} -o ${PUBLISH_DIR} \
|
||||
/p:UseAppHost=true /p:PublishTrimmed=false
|
||||
|
||||
FROM ${RUNTIME_IMAGE} AS runtime
|
||||
# Create non-root user/group with stable ids for auditability
|
||||
RUN groupadd -r -g ${APP_GID} ${APP_USER} && \
|
||||
useradd -r -u ${APP_UID} -g ${APP_GID} -d /var/lib/${APP_USER} ${APP_USER} && \
|
||||
mkdir -p /app /var/lib/${APP_USER} /var/run/${APP_USER} /tmp && \
|
||||
chown -R ${APP_UID}:${APP_GID} /app /var/lib/${APP_USER} /var/run/${APP_USER} /tmp
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=build --chown=${APP_UID}:${APP_GID} ${PUBLISH_DIR}/ ./
|
||||
# Ship healthcheck helper; callers may override with their own script
|
||||
COPY --chown=${APP_UID}:${APP_GID} ops/devops/docker/healthcheck.sh /usr/local/bin/healthcheck.sh
|
||||
|
||||
ENV ASPNETCORE_URLS=http://+:${APP_PORT} \
|
||||
DOTNET_EnableDiagnostics=0 \
|
||||
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 \
|
||||
COMPlus_EnableDiagnostics=0 \
|
||||
APP_BINARY=${APP_BINARY}
|
||||
|
||||
USER ${APP_UID}:${APP_GID}
|
||||
EXPOSE ${APP_PORT}
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
|
||||
CMD /usr/local/bin/healthcheck.sh
|
||||
|
||||
# Harden filesystem; deploys should also set readOnlyRootFilesystem true
|
||||
RUN chmod 500 /app && \
|
||||
find /app -maxdepth 1 -type f -exec chmod 400 {} \; && \
|
||||
find /app -maxdepth 1 -type d -exec chmod 500 {} \;
|
||||
|
||||
# Use shell form so APP_BINARY env can be expanded without duplicating the template per service
|
||||
ENTRYPOINT ["sh","-c","exec ./\"$APP_BINARY\""]
|
||||
212
deploy/docker/Dockerfile.platform
Normal file
212
deploy/docker/Dockerfile.platform
Normal file
@@ -0,0 +1,212 @@
|
||||
# syntax=docker/dockerfile:1.4
|
||||
# StellaOps Platform Image - Build Once, Deploy Everywhere
|
||||
# Builds ALL crypto plugins unconditionally for runtime selection
|
||||
|
||||
# ============================================================================
|
||||
# Stage 1: SDK Build - Build ALL Projects and Crypto Plugins
|
||||
# ============================================================================
|
||||
FROM mcr.microsoft.com/dotnet/sdk:10.0-preview AS build
|
||||
WORKDIR /src
|
||||
|
||||
# Copy solution and project files for dependency restore
|
||||
COPY Directory.Build.props Directory.Build.targets nuget.config ./
|
||||
COPY src/StellaOps.sln ./src/
|
||||
|
||||
# Copy all crypto plugin projects
|
||||
COPY src/__Libraries/StellaOps.Cryptography/ ./src/__Libraries/StellaOps.Cryptography/
|
||||
COPY src/__Libraries/StellaOps.Cryptography.DependencyInjection/ ./src/__Libraries/StellaOps.Cryptography.DependencyInjection/
|
||||
COPY src/__Libraries/StellaOps.Cryptography.PluginLoader/ ./src/__Libraries/StellaOps.Cryptography.PluginLoader/
|
||||
|
||||
# Crypto plugins - ALL built unconditionally
|
||||
COPY src/__Libraries/StellaOps.Cryptography.Plugin.OfflineVerification/ ./src/__Libraries/StellaOps.Cryptography.Plugin.OfflineVerification/
|
||||
# Note: Additional crypto plugins can be added here when available:
|
||||
# COPY src/__Libraries/StellaOps.Cryptography.Plugin.eIDAS/ ./src/__Libraries/StellaOps.Cryptography.Plugin.eIDAS/
|
||||
# COPY src/__Libraries/StellaOps.Cryptography.Plugin.CryptoPro/ ./src/__Libraries/StellaOps.Cryptography.Plugin.CryptoPro/
|
||||
# COPY src/__Libraries/StellaOps.Cryptography.Plugin.SM/ ./src/__Libraries/StellaOps.Cryptography.Plugin.SM/
|
||||
|
||||
# Copy all module projects
|
||||
COPY src/Authority/ ./src/Authority/
|
||||
COPY src/Signer/ ./src/Signer/
|
||||
COPY src/Attestor/ ./src/Attestor/
|
||||
COPY src/Concelier/ ./src/Concelier/
|
||||
COPY src/Scanner/ ./src/Scanner/
|
||||
COPY src/AirGap/ ./src/AirGap/
|
||||
COPY src/Excititor/ ./src/Excititor/
|
||||
COPY src/Policy/ ./src/Policy/
|
||||
COPY src/Scheduler/ ./src/Scheduler/
|
||||
COPY src/Notify/ ./src/Notify/
|
||||
COPY src/Zastava/ ./src/Zastava/
|
||||
COPY src/Gateway/ ./src/Gateway/
|
||||
COPY src/Cli/ ./src/Cli/
|
||||
|
||||
# Copy shared libraries
|
||||
COPY src/__Libraries/ ./src/__Libraries/
|
||||
|
||||
# Restore dependencies
|
||||
RUN dotnet restore src/StellaOps.sln
|
||||
|
||||
# Build entire solution (Release configuration)
|
||||
RUN dotnet build src/StellaOps.sln --configuration Release --no-restore
|
||||
|
||||
# Publish all web services and libraries
|
||||
# This creates /app/publish with all assemblies including crypto plugins
|
||||
RUN dotnet publish src/Authority/StellaOps.Authority.WebService/StellaOps.Authority.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/authority
|
||||
|
||||
RUN dotnet publish src/Signer/StellaOps.Signer.WebService/StellaOps.Signer.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/signer
|
||||
|
||||
RUN dotnet publish src/Attestor/StellaOps.Attestor.WebService/StellaOps.Attestor.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/attestor
|
||||
|
||||
RUN dotnet publish src/Concelier/StellaOps.Concelier.WebService/StellaOps.Concelier.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/concelier
|
||||
|
||||
RUN dotnet publish src/Scanner/StellaOps.Scanner.WebService/StellaOps.Scanner.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/scanner
|
||||
|
||||
RUN dotnet publish src/Excititor/StellaOps.Excititor.WebService/StellaOps.Excititor.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/excititor
|
||||
|
||||
RUN dotnet publish src/Policy/StellaOps.Policy.WebService/StellaOps.Policy.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/policy
|
||||
|
||||
RUN dotnet publish src/Scheduler/StellaOps.Scheduler.WebService/StellaOps.Scheduler.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/scheduler
|
||||
|
||||
RUN dotnet publish src/Notify/StellaOps.Notify.WebService/StellaOps.Notify.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/notify
|
||||
|
||||
RUN dotnet publish src/Zastava/StellaOps.Zastava.WebService/StellaOps.Zastava.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/zastava
|
||||
|
||||
RUN dotnet publish src/Gateway/StellaOps.Gateway.WebService/StellaOps.Gateway.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/gateway
|
||||
|
||||
RUN dotnet publish src/AirGap/StellaOps.AirGap.Importer/StellaOps.AirGap.Importer.csproj \
|
||||
--configuration Release --no-build --output /app/publish/airgap-importer
|
||||
|
||||
RUN dotnet publish src/AirGap/StellaOps.AirGap.Exporter/StellaOps.AirGap.Exporter.csproj \
|
||||
--configuration Release --no-build --output /app/publish/airgap-exporter
|
||||
|
||||
RUN dotnet publish src/Cli/StellaOps.Cli/StellaOps.Cli.csproj \
|
||||
--configuration Release --no-build --output /app/publish/cli
|
||||
|
||||
# Copy crypto plugin manifest
|
||||
COPY etc/crypto-plugins-manifest.json /app/publish/etc/
|
||||
|
||||
# ============================================================================
|
||||
# Stage 2: Runtime Base - Contains ALL Crypto Plugins
|
||||
# ============================================================================
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:10.0-preview AS runtime-base
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies for crypto providers
|
||||
# PostgreSQL client for Authority/Concelier/etc
|
||||
RUN apt-get update && apt-get install -y \
|
||||
postgresql-client \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy all published assemblies (includes all crypto plugins)
|
||||
COPY --from=build /app/publish /app/
|
||||
|
||||
# Expose common ports (these can be overridden by docker-compose)
|
||||
EXPOSE 8080 8443
|
||||
|
||||
# Labels
|
||||
LABEL com.stellaops.image.type="platform"
|
||||
LABEL com.stellaops.image.variant="all-plugins"
|
||||
LABEL com.stellaops.crypto.plugins="offline-verification"
|
||||
# Additional plugins will be added as they become available:
|
||||
# LABEL com.stellaops.crypto.plugins="offline-verification,eidas,cryptopro,sm"
|
||||
|
||||
# Health check placeholder (can be overridden per service)
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD curl -f http://localhost:8080/health || exit 1
|
||||
|
||||
# ============================================================================
|
||||
# Service-Specific Final Stages
|
||||
# ============================================================================
|
||||
|
||||
# Authority Service
|
||||
FROM runtime-base AS authority
|
||||
WORKDIR /app/authority
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Authority.WebService.dll"]
|
||||
|
||||
# Signer Service
|
||||
FROM runtime-base AS signer
|
||||
WORKDIR /app/signer
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Signer.WebService.dll"]
|
||||
|
||||
# Attestor Service
|
||||
FROM runtime-base AS attestor
|
||||
WORKDIR /app/attestor
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Attestor.WebService.dll"]
|
||||
|
||||
# Concelier Service
|
||||
FROM runtime-base AS concelier
|
||||
WORKDIR /app/concelier
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Concelier.WebService.dll"]
|
||||
|
||||
# Scanner Service
|
||||
FROM runtime-base AS scanner
|
||||
WORKDIR /app/scanner
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Scanner.WebService.dll"]
|
||||
|
||||
# Excititor Service
|
||||
FROM runtime-base AS excititor
|
||||
WORKDIR /app/excititor
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Excititor.WebService.dll"]
|
||||
|
||||
# Policy Service
|
||||
FROM runtime-base AS policy
|
||||
WORKDIR /app/policy
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Policy.WebService.dll"]
|
||||
|
||||
# Scheduler Service
|
||||
FROM runtime-base AS scheduler
|
||||
WORKDIR /app/scheduler
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Scheduler.WebService.dll"]
|
||||
|
||||
# Notify Service
|
||||
FROM runtime-base AS notify
|
||||
WORKDIR /app/notify
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Notify.WebService.dll"]
|
||||
|
||||
# Zastava Service
|
||||
FROM runtime-base AS zastava
|
||||
WORKDIR /app/zastava
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Zastava.WebService.dll"]
|
||||
|
||||
# Gateway Service
|
||||
FROM runtime-base AS gateway
|
||||
WORKDIR /app/gateway
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Gateway.WebService.dll"]
|
||||
|
||||
# AirGap Importer (CLI tool)
|
||||
FROM runtime-base AS airgap-importer
|
||||
WORKDIR /app/airgap-importer
|
||||
ENTRYPOINT ["dotnet", "StellaOps.AirGap.Importer.dll"]
|
||||
|
||||
# AirGap Exporter (CLI tool)
|
||||
FROM runtime-base AS airgap-exporter
|
||||
WORKDIR /app/airgap-exporter
|
||||
ENTRYPOINT ["dotnet", "StellaOps.AirGap.Exporter.dll"]
|
||||
|
||||
# CLI Tool
|
||||
FROM runtime-base AS cli
|
||||
WORKDIR /app/cli
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Cli.dll"]
|
||||
|
||||
# ============================================================================
|
||||
# Build Instructions
|
||||
# ============================================================================
|
||||
# Build platform image:
|
||||
# docker build -f deploy/docker/Dockerfile.platform --target runtime-base -t stellaops/platform:latest .
|
||||
#
|
||||
# Build specific service:
|
||||
# docker build -f deploy/docker/Dockerfile.platform --target authority -t stellaops/authority:latest .
|
||||
# docker build -f deploy/docker/Dockerfile.platform --target scanner -t stellaops/scanner:latest .
|
||||
#
|
||||
# The platform image contains ALL crypto plugins.
|
||||
# Regional selection happens at runtime via configuration (see Dockerfile.crypto-profile).
|
||||
50
deploy/docker/build-all.sh
Normal file
50
deploy/docker/build-all.sh
Normal file
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build hardened images for the core services using the shared template/matrix (DOCKER-44-001)
|
||||
set -euo pipefail
|
||||
|
||||
ROOT=${ROOT:-"$(git rev-parse --show-toplevel)"}
|
||||
MATRIX=${MATRIX:-"${ROOT}/ops/devops/docker/services-matrix.env"}
|
||||
REGISTRY=${REGISTRY:-"stellaops"}
|
||||
TAG_SUFFIX=${TAG_SUFFIX:-"dev"}
|
||||
SDK_IMAGE=${SDK_IMAGE:-"mcr.microsoft.com/dotnet/sdk:10.0-bookworm-slim"}
|
||||
RUNTIME_IMAGE=${RUNTIME_IMAGE:-"mcr.microsoft.com/dotnet/aspnet:10.0-bookworm-slim"}
|
||||
|
||||
if [[ ! -f "${MATRIX}" ]]; then
|
||||
echo "matrix file not found: ${MATRIX}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Building services from ${MATRIX} -> ${REGISTRY}/<service>:${TAG_SUFFIX}" >&2
|
||||
|
||||
while IFS='|' read -r service dockerfile project binary port; do
|
||||
[[ -z "${service}" || "${service}" =~ ^# ]] && continue
|
||||
image="${REGISTRY}/${service}:${TAG_SUFFIX}"
|
||||
df_path="${ROOT}/${dockerfile}"
|
||||
if [[ ! -f "${df_path}" ]]; then
|
||||
echo "skipping ${service}: dockerfile missing (${df_path})" >&2
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ "${dockerfile}" == *"Dockerfile.console"* ]]; then
|
||||
# Angular console build uses its dedicated Dockerfile
|
||||
echo "[console] ${service} -> ${image}" >&2
|
||||
docker build \
|
||||
-f "${df_path}" "${ROOT}" \
|
||||
--build-arg APP_DIR="${project}" \
|
||||
--build-arg APP_PORT="${port}" \
|
||||
-t "${image}"
|
||||
else
|
||||
echo "[service] ${service} -> ${image}" >&2
|
||||
docker build \
|
||||
-f "${df_path}" "${ROOT}" \
|
||||
--build-arg SDK_IMAGE="${SDK_IMAGE}" \
|
||||
--build-arg RUNTIME_IMAGE="${RUNTIME_IMAGE}" \
|
||||
--build-arg APP_PROJECT="${project}" \
|
||||
--build-arg APP_BINARY="${binary}" \
|
||||
--build-arg APP_PORT="${port}" \
|
||||
-t "${image}"
|
||||
fi
|
||||
|
||||
done < "${MATRIX}"
|
||||
|
||||
echo "Build complete. Remember to enforce readOnlyRootFilesystem at deploy time and run sbom_attest.sh (DOCKER-44-002)." >&2
|
||||
10
deploy/docker/healthcheck-frontend.sh
Normal file
10
deploy/docker/healthcheck-frontend.sh
Normal file
@@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
HOST="${HEALTH_HOST:-127.0.0.1}"
|
||||
PORT="${HEALTH_PORT:-8080}"
|
||||
PATH_CHECK="${HEALTH_PATH:-/}"
|
||||
USER_AGENT="stellaops-frontend-healthcheck"
|
||||
|
||||
wget -qO- "http://${HOST}:${PORT}${PATH_CHECK}" \
|
||||
--header="User-Agent: ${USER_AGENT}" \
|
||||
--timeout="${HEALTH_TIMEOUT:-4}" >/dev/null
|
||||
24
deploy/docker/healthcheck.sh
Normal file
24
deploy/docker/healthcheck.sh
Normal file
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
HOST="${HEALTH_HOST:-127.0.0.1}"
|
||||
PORT="${HEALTH_PORT:-8080}"
|
||||
LIVENESS_PATH="${LIVENESS_PATH:-/health/liveness}"
|
||||
READINESS_PATH="${READINESS_PATH:-/health/readiness}"
|
||||
USER_AGENT="stellaops-healthcheck"
|
||||
|
||||
fetch() {
|
||||
target_path="$1"
|
||||
# BusyBox wget is available in Alpine; curl not assumed.
|
||||
wget -qO- "http://${HOST}:${PORT}${target_path}" \
|
||||
--header="User-Agent: ${USER_AGENT}" \
|
||||
--timeout="${HEALTH_TIMEOUT:-4}" >/dev/null
|
||||
}
|
||||
|
||||
fail=0
|
||||
if ! fetch "$LIVENESS_PATH"; then
|
||||
fail=1
|
||||
fi
|
||||
if ! fetch "$READINESS_PATH"; then
|
||||
fail=1
|
||||
fi
|
||||
exit "$fail"
|
||||
48
deploy/docker/sbom_attest.sh
Normal file
48
deploy/docker/sbom_attest.sh
Normal file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
# Deterministic SBOM + attestation helper for DOCKER-44-002
|
||||
# Usage: ./sbom_attest.sh <image-ref> [output-dir] [cosign-key]
|
||||
# - image-ref: fully qualified image (e.g., ghcr.io/stellaops/policy:1.2.3)
|
||||
# - output-dir: defaults to ./sbom
|
||||
# - cosign-key: path to cosign key (PEM). If omitted, uses keyless if allowed (COSIGN_EXPERIMENTAL=1)
|
||||
|
||||
set -euo pipefail
|
||||
IMAGE_REF=${1:?"image ref required"}
|
||||
OUT_DIR=${2:-sbom}
|
||||
COSIGN_KEY=${3:-}
|
||||
|
||||
mkdir -p "${OUT_DIR}"
|
||||
|
||||
# Normalize filename (replace / and : with _)
|
||||
name_safe() {
|
||||
echo "$1" | tr '/:' '__'
|
||||
}
|
||||
|
||||
BASENAME=$(name_safe "${IMAGE_REF}")
|
||||
SPDX_JSON="${OUT_DIR}/${BASENAME}.spdx.json"
|
||||
CDX_JSON="${OUT_DIR}/${BASENAME}.cdx.json"
|
||||
ATTESTATION="${OUT_DIR}/${BASENAME}.sbom.att"
|
||||
|
||||
# Freeze timestamps for reproducibility
|
||||
export SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH:-1704067200}
|
||||
|
||||
# Generate SPDX 3.0-ish JSON (syft formats are stable and offline-friendly)
|
||||
syft "${IMAGE_REF}" -o spdx-json > "${SPDX_JSON}"
|
||||
# Generate CycloneDX 1.6 JSON
|
||||
syft "${IMAGE_REF}" -o cyclonedx-json > "${CDX_JSON}"
|
||||
|
||||
# Attach SBOMs as cosign attestations (one per format)
|
||||
export COSIGN_EXPERIMENTAL=${COSIGN_EXPERIMENTAL:-1}
|
||||
COSIGN_ARGS=("attest" "--predicate" "${SPDX_JSON}" "--type" "spdx" "${IMAGE_REF}")
|
||||
if [[ -n "${COSIGN_KEY}" ]]; then
|
||||
COSIGN_ARGS+=("--key" "${COSIGN_KEY}")
|
||||
fi
|
||||
cosign "${COSIGN_ARGS[@]}"
|
||||
|
||||
COSIGN_ARGS=("attest" "--predicate" "${CDX_JSON}" "--type" "cyclonedx" "${IMAGE_REF}")
|
||||
if [[ -n "${COSIGN_KEY}" ]]; then
|
||||
COSIGN_ARGS+=("--key" "${COSIGN_KEY}")
|
||||
fi
|
||||
cosign "${COSIGN_ARGS[@]}"
|
||||
|
||||
echo "SBOMs written to ${SPDX_JSON} and ${CDX_JSON}" >&2
|
||||
echo "Attestations pushed for ${IMAGE_REF}" >&2
|
||||
12
deploy/docker/services-matrix.env
Normal file
12
deploy/docker/services-matrix.env
Normal file
@@ -0,0 +1,12 @@
|
||||
# service|dockerfile|project|binary|port
|
||||
# Paths are relative to repo root; dockerfile is usually the shared hardened template.
|
||||
api|ops/devops/docker/Dockerfile.hardened.template|src/VulnExplorer/StellaOps.VulnExplorer.Api/StellaOps.VulnExplorer.Api.csproj|StellaOps.VulnExplorer.Api|8080
|
||||
orchestrator|ops/devops/docker/Dockerfile.hardened.template|src/Orchestrator/StellaOps.Orchestrator.WebService/StellaOps.Orchestrator.WebService.csproj|StellaOps.Orchestrator.WebService|8080
|
||||
task-runner|ops/devops/docker/Dockerfile.hardened.template|src/Orchestrator/StellaOps.Orchestrator.Worker/StellaOps.Orchestrator.Worker.csproj|StellaOps.Orchestrator.Worker|8081
|
||||
concelier|ops/devops/docker/Dockerfile.hardened.template|src/Concelier/StellaOps.Concelier.WebService/StellaOps.Concelier.WebService.csproj|StellaOps.Concelier.WebService|8080
|
||||
excititor|ops/devops/docker/Dockerfile.hardened.template|src/Excititor/StellaOps.Excititor.WebService/StellaOps.Excititor.WebService.csproj|StellaOps.Excititor.WebService|8080
|
||||
policy|ops/devops/docker/Dockerfile.hardened.template|src/Policy/StellaOps.Policy.Gateway/StellaOps.Policy.Gateway.csproj|StellaOps.Policy.Gateway|8084
|
||||
notify|ops/devops/docker/Dockerfile.hardened.template|src/Notify/StellaOps.Notify.WebService/StellaOps.Notify.WebService.csproj|StellaOps.Notify.WebService|8080
|
||||
export|ops/devops/docker/Dockerfile.hardened.template|src/ExportCenter/StellaOps.ExportCenter.WebService/StellaOps.ExportCenter.WebService.csproj|StellaOps.ExportCenter.WebService|8080
|
||||
advisoryai|ops/devops/docker/Dockerfile.hardened.template|src/AdvisoryAI/StellaOps.AdvisoryAI.WebService/StellaOps.AdvisoryAI.WebService.csproj|StellaOps.AdvisoryAI.WebService|8080
|
||||
console|ops/devops/docker/Dockerfile.console|src/Web/StellaOps.Web|StellaOps.Web|8080
|
||||
70
deploy/docker/verify_health_endpoints.sh
Normal file
70
deploy/docker/verify_health_endpoints.sh
Normal file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env bash
|
||||
# Smoke-check /health and capability endpoints for a built image (DOCKER-44-003)
|
||||
# Usage: ./verify_health_endpoints.sh <image-ref> [port]
|
||||
# Requires: docker, curl or wget
|
||||
set -euo pipefail
|
||||
IMAGE=${1:?"image ref required"}
|
||||
PORT=${2:-8080}
|
||||
CONTAINER_NAME="healthcheck-$$"
|
||||
TIMEOUT=30
|
||||
SLEEP=1
|
||||
|
||||
have_curl=1
|
||||
if ! command -v curl >/dev/null 2>&1; then
|
||||
have_curl=0
|
||||
fi
|
||||
|
||||
req() {
|
||||
local path=$1
|
||||
local url="http://127.0.0.1:${PORT}${path}"
|
||||
if [[ $have_curl -eq 1 ]]; then
|
||||
curl -fsS --max-time 3 "$url" >/dev/null
|
||||
else
|
||||
wget -qO- --timeout=3 "$url" >/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
echo "[info] starting container ${IMAGE} on port ${PORT}" >&2
|
||||
cleanup
|
||||
if ! docker run -d --rm --name "$CONTAINER_NAME" -p "${PORT}:${PORT}" "$IMAGE" >/dev/null; then
|
||||
echo "[error] failed to start image ${IMAGE}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# wait for readiness
|
||||
start=$(date +%s)
|
||||
while true; do
|
||||
if req /health/liveness 2>/dev/null; then break; fi
|
||||
now=$(date +%s)
|
||||
if (( now - start > TIMEOUT )); then
|
||||
echo "[error] liveness endpoint did not come up in ${TIMEOUT}s" >&2
|
||||
exit 1
|
||||
fi
|
||||
sleep $SLEEP
|
||||
done
|
||||
|
||||
# verify endpoints
|
||||
fail=0
|
||||
for path in /health/liveness /health/readiness /version /metrics; do
|
||||
if ! req "$path"; then
|
||||
echo "[error] missing or failing ${path}" >&2
|
||||
fail=1
|
||||
fi
|
||||
done
|
||||
|
||||
# capability endpoint optional; if present ensure merge=false for Concelier/Excititor
|
||||
if req /capabilities 2>/dev/null; then
|
||||
body="$(curl -fsS "http://127.0.0.1:${PORT}/capabilities" 2>/dev/null || true)"
|
||||
if echo "$body" | grep -q '"merge"[[:space:]]*:[[:space:]]*false'; then
|
||||
:
|
||||
else
|
||||
echo "[warn] /capabilities present but merge flag not false" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
exit $fail
|
||||
6
deploy/helm/stellaops/Chart.yaml
Normal file
6
deploy/helm/stellaops/Chart.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
apiVersion: v2
|
||||
name: stellaops
|
||||
description: Stella Ops core stack (authority, signing, scanner, UI) with infrastructure primitives.
|
||||
type: application
|
||||
version: 0.1.0
|
||||
appVersion: "2025.10.0"
|
||||
64
deploy/helm/stellaops/INSTALL.md
Normal file
64
deploy/helm/stellaops/INSTALL.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# StellaOps Helm Install Guide
|
||||
|
||||
This guide ships with the `stellaops` chart and provides deterministic install steps for **prod** and **airgap** profiles. All images are pinned by digest from `deploy/releases/<channel>.yaml`.
|
||||
|
||||
## Prerequisites
|
||||
- Helm ≥ 3.14 and kubectl configured for the target cluster.
|
||||
- Pull secrets for `registry.stella-ops.org` (or your mirrored registry in air-gapped mode).
|
||||
- TLS/ingress secrets created if you enable ingress in the values files.
|
||||
|
||||
## Channels and values
|
||||
- Prod/stable: `deploy/releases/2025.09-stable.yaml` + `values-prod.yaml`
|
||||
- Airgap: `deploy/releases/2025.09-airgap.yaml` + `values-airgap.yaml`
|
||||
- Mirror (optional): `values-mirror.yaml` overlays registry endpoints when using a private mirror.
|
||||
|
||||
## Quick install (prod)
|
||||
```bash
|
||||
export RELEASE_CHANNEL=2025.09-stable
|
||||
export NAMESPACE=stellaops
|
||||
|
||||
helm upgrade --install stellaops ./deploy/helm/stellaops \
|
||||
--namespace "$NAMESPACE" --create-namespace \
|
||||
-f deploy/helm/stellaops/values-prod.yaml \
|
||||
--set global.release.channel=stable \
|
||||
--set global.release.version="2025.09.2" \
|
||||
--set global.release.manifestSha256="dc3c8fe1ab83941c838ccc5a8a5862f7ddfa38c2078e580b5649db26554565b7"
|
||||
```
|
||||
|
||||
## Quick install (airgap)
|
||||
Assumes images are already loaded into your private registry and `values-airgap.yaml` points to that registry.
|
||||
```bash
|
||||
export NAMESPACE=stellaops
|
||||
|
||||
helm upgrade --install stellaops ./deploy/helm/stellaops \
|
||||
--namespace "$NAMESPACE" --create-namespace \
|
||||
-f deploy/helm/stellaops/values-airgap.yaml \
|
||||
--set global.release.channel=airgap \
|
||||
--set global.release.version="2025.09.0-airgap" \
|
||||
--set global.release.manifestSha256="d422ae3ea01d5f27ea8b5fdc5b19667cb4e3e2c153a35cb761cb53a6ce4f6ba4"
|
||||
```
|
||||
|
||||
## Mirror overlay
|
||||
If using a mirrored registry, layer the mirror values:
|
||||
```bash
|
||||
helm upgrade --install stellaops ./deploy/helm/stellaops \
|
||||
--namespace "$NAMESPACE" --create-namespace \
|
||||
-f deploy/helm/stellaops/values-prod.yaml \
|
||||
-f deploy/helm/stellaops/values-mirror.yaml \
|
||||
--set global.release.version="2025.09.2" \
|
||||
--set global.release.manifestSha256="dc3c8fe1ab83941c838ccc5a8a5862f7ddfa38c2078e580b5649db26554565b7"
|
||||
```
|
||||
|
||||
## Validate chart and digests
|
||||
```bash
|
||||
deploy/tools/check-channel-alignment.py --manifest deploy/releases/$RELEASE_CHANNEL.yaml \
|
||||
--values deploy/helm/stellaops/values-prod.yaml
|
||||
|
||||
helm lint ./deploy/helm/stellaops
|
||||
helm template stellaops ./deploy/helm/stellaops -f deploy/helm/stellaops/values-prod.yaml >/tmp/stellaops.yaml
|
||||
```
|
||||
|
||||
## Notes
|
||||
- Surface.Env and Surface.Secrets defaults are defined in `values*.yaml`; adjust endpoints, cache roots, and providers before promotion.
|
||||
- Keep `global.release.*` in sync with the chosen release manifest; never deploy with empty version/channel/manifestSha256.
|
||||
- For offline clusters, run image preload and secret creation before `helm upgrade` to avoid pull failures.
|
||||
16
deploy/helm/stellaops/README-mock.md
Normal file
16
deploy/helm/stellaops/README-mock.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Mock Overlay (Dev Only)
|
||||
|
||||
Purpose: let deployment tasks progress with placeholder digests until real releases land.
|
||||
|
||||
Use:
|
||||
```bash
|
||||
helm template mock ./deploy/helm/stellaops -f deploy/helm/stellaops/values-mock.yaml
|
||||
```
|
||||
|
||||
Contents:
|
||||
- Mock deployments for orchestrator, policy-registry, packs-registry, task-runner, VEX Lens, issuer-directory, findings-ledger, vuln-explorer-api.
|
||||
- Image pins pulled from `deploy/releases/2025.09-mock-dev.yaml`.
|
||||
|
||||
Notes:
|
||||
- Annotated with `stellaops.dev/mock: "true"` to discourage production use.
|
||||
- Swap to real values once official digests publish; keep mock overlay gated behind `mock.enabled`.
|
||||
64
deploy/helm/stellaops/files/otel-collector-config.yaml
Normal file
64
deploy/helm/stellaops/files/otel-collector-config.yaml
Normal file
@@ -0,0 +1,64 @@
|
||||
receivers:
|
||||
otlp:
|
||||
protocols:
|
||||
grpc:
|
||||
endpoint: 0.0.0.0:4317
|
||||
tls:
|
||||
cert_file: ${STELLAOPS_OTEL_TLS_CERT:?STELLAOPS_OTEL_TLS_CERT not set}
|
||||
key_file: ${STELLAOPS_OTEL_TLS_KEY:?STELLAOPS_OTEL_TLS_KEY not set}
|
||||
client_ca_file: ${STELLAOPS_OTEL_TLS_CA:?STELLAOPS_OTEL_TLS_CA not set}
|
||||
require_client_certificate: ${STELLAOPS_OTEL_REQUIRE_CLIENT_CERT:true}
|
||||
http:
|
||||
endpoint: 0.0.0.0:4318
|
||||
tls:
|
||||
cert_file: ${STELLAOPS_OTEL_TLS_CERT:?STELLAOPS_OTEL_TLS_CERT not set}
|
||||
key_file: ${STELLAOPS_OTEL_TLS_KEY:?STELLAOPS_OTEL_TLS_KEY not set}
|
||||
client_ca_file: ${STELLAOPS_OTEL_TLS_CA:?STELLAOPS_OTEL_TLS_CA not set}
|
||||
require_client_certificate: ${STELLAOPS_OTEL_REQUIRE_CLIENT_CERT:true}
|
||||
|
||||
processors:
|
||||
attributes/tenant-tag:
|
||||
actions:
|
||||
- key: tenant.id
|
||||
action: insert
|
||||
value: ${STELLAOPS_TENANT_ID:unknown}
|
||||
batch:
|
||||
send_batch_size: 1024
|
||||
timeout: 5s
|
||||
|
||||
exporters:
|
||||
logging:
|
||||
verbosity: normal
|
||||
prometheus:
|
||||
endpoint: ${STELLAOPS_OTEL_PROMETHEUS_ENDPOINT:0.0.0.0:9464}
|
||||
enable_open_metrics: true
|
||||
metric_expiration: 5m
|
||||
tls:
|
||||
cert_file: ${STELLAOPS_OTEL_TLS_CERT:?STELLAOPS_OTEL_TLS_CERT not set}
|
||||
key_file: ${STELLAOPS_OTEL_TLS_KEY:?STELLAOPS_OTEL_TLS_KEY not set}
|
||||
client_ca_file: ${STELLAOPS_OTEL_TLS_CA:?STELLAOPS_OTEL_TLS_CA not set}
|
||||
|
||||
extensions:
|
||||
health_check:
|
||||
endpoint: ${STELLAOPS_OTEL_HEALTH_ENDPOINT:0.0.0.0:13133}
|
||||
pprof:
|
||||
endpoint: ${STELLAOPS_OTEL_PPROF_ENDPOINT:0.0.0.0:1777}
|
||||
|
||||
service:
|
||||
telemetry:
|
||||
logs:
|
||||
level: ${STELLAOPS_OTEL_LOG_LEVEL:info}
|
||||
extensions: [health_check, pprof]
|
||||
pipelines:
|
||||
traces:
|
||||
receivers: [otlp]
|
||||
processors: [attributes/tenant-tag, batch]
|
||||
exporters: [logging]
|
||||
metrics:
|
||||
receivers: [otlp]
|
||||
processors: [attributes/tenant-tag, batch]
|
||||
exporters: [logging, prometheus]
|
||||
logs:
|
||||
receivers: [otlp]
|
||||
processors: [attributes/tenant-tag, batch]
|
||||
exporters: [logging]
|
||||
43
deploy/helm/stellaops/templates/_helpers.tpl
Normal file
43
deploy/helm/stellaops/templates/_helpers.tpl
Normal file
@@ -0,0 +1,43 @@
|
||||
{{- define "stellaops.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "stellaops.telemetryCollector.config" -}}
|
||||
{{- if .Values.telemetry.collector.config }}
|
||||
{{ tpl .Values.telemetry.collector.config . }}
|
||||
{{- else }}
|
||||
{{ tpl (.Files.Get "files/otel-collector-config.yaml") . }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "stellaops.telemetryCollector.fullname" -}}
|
||||
{{- printf "%s-otel-collector" (include "stellaops.name" .) | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "stellaops.fullname" -}}
|
||||
{{- $name := default .root.Chart.Name .root.Values.fullnameOverride -}}
|
||||
{{- printf "%s-%s" $name .name | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "stellaops.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "stellaops.name" .root | quote }}
|
||||
app.kubernetes.io/instance: {{ .root.Release.Name | quote }}
|
||||
app.kubernetes.io/component: {{ .name | quote }}
|
||||
{{- if .svc.class }}
|
||||
app.kubernetes.io/part-of: {{ printf "stellaops-%s" .svc.class | quote }}
|
||||
{{- else }}
|
||||
app.kubernetes.io/part-of: "stellaops-core"
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "stellaops.labels" -}}
|
||||
{{ include "stellaops.selectorLabels" . }}
|
||||
helm.sh/chart: {{ printf "%s-%s" .root.Chart.Name .root.Chart.Version | quote }}
|
||||
app.kubernetes.io/version: {{ .root.Values.global.release.version | quote }}
|
||||
app.kubernetes.io/managed-by: {{ .root.Release.Service | quote }}
|
||||
stellaops.release/channel: {{ .root.Values.global.release.channel | quote }}
|
||||
stellaops.profile: {{ .root.Values.global.profile | quote }}
|
||||
{{- range $k, $v := .root.Values.global.labels }}
|
||||
{{ $k }}: {{ $v | quote }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
10
deploy/helm/stellaops/templates/configmap-release.yaml
Normal file
10
deploy/helm/stellaops/templates/configmap-release.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "stellaops.fullname" (dict "root" . "name" "release") }}
|
||||
labels:
|
||||
{{- include "stellaops.labels" (dict "root" . "name" "release" "svc" (dict "class" "meta")) | nindent 4 }}
|
||||
data:
|
||||
version: {{ .Values.global.release.version | quote }}
|
||||
channel: {{ .Values.global.release.channel | quote }}
|
||||
manifestSha256: {{ default "" .Values.global.release.manifestSha256 | quote }}
|
||||
15
deploy/helm/stellaops/templates/configmaps.yaml
Normal file
15
deploy/helm/stellaops/templates/configmaps.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
{{- $root := . -}}
|
||||
{{- range $name, $cfg := .Values.configMaps }}
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "stellaops.fullname" (dict "root" $root "name" $name) }}
|
||||
labels:
|
||||
{{- include "stellaops.labels" (dict "root" $root "name" $name "svc" (dict "class" "config")) | nindent 4 }}
|
||||
data:
|
||||
{{- range $fileName, $content := $cfg.data }}
|
||||
{{ $fileName }}: |
|
||||
{{ tpl $content $root | nindent 4 }}
|
||||
{{- end }}
|
||||
---
|
||||
{{- end }}
|
||||
108
deploy/helm/stellaops/templates/console.yaml
Normal file
108
deploy/helm/stellaops/templates/console.yaml
Normal file
@@ -0,0 +1,108 @@
|
||||
{{- if .Values.console.enabled }}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "stellaops.fullname" . }}-console
|
||||
labels:
|
||||
app.kubernetes.io/component: console
|
||||
{{- include "stellaops.labels" . | nindent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.console.replicas | default 1 }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/component: console
|
||||
{{- include "stellaops.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: console
|
||||
{{- include "stellaops.selectorLabels" . | nindent 8 }}
|
||||
spec:
|
||||
securityContext:
|
||||
{{- toYaml .Values.console.securityContext | nindent 8 }}
|
||||
containers:
|
||||
- name: console
|
||||
image: {{ .Values.console.image }}
|
||||
imagePullPolicy: {{ .Values.global.image.pullPolicy | default "IfNotPresent" }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: {{ .Values.console.port | default 8080 }}
|
||||
protocol: TCP
|
||||
securityContext:
|
||||
{{- toYaml .Values.console.containerSecurityContext | nindent 12 }}
|
||||
livenessProbe:
|
||||
{{- toYaml .Values.console.livenessProbe | nindent 12 }}
|
||||
readinessProbe:
|
||||
{{- toYaml .Values.console.readinessProbe | nindent 12 }}
|
||||
resources:
|
||||
{{- toYaml .Values.console.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
{{- toYaml .Values.console.volumeMounts | nindent 12 }}
|
||||
env:
|
||||
- name: APP_PORT
|
||||
value: "{{ .Values.console.port | default 8080 }}"
|
||||
volumes:
|
||||
{{- toYaml .Values.console.volumes | nindent 8 }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "stellaops.fullname" . }}-console
|
||||
labels:
|
||||
app.kubernetes.io/component: console
|
||||
{{- include "stellaops.labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.console.service.type | default "ClusterIP" }}
|
||||
ports:
|
||||
- port: {{ .Values.console.service.port | default 80 }}
|
||||
targetPort: {{ .Values.console.service.targetPort | default 8080 }}
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app.kubernetes.io/component: console
|
||||
{{- include "stellaops.selectorLabels" . | nindent 4 }}
|
||||
{{- if .Values.console.ingress.enabled }}
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ include "stellaops.fullname" . }}-console
|
||||
labels:
|
||||
app.kubernetes.io/component: console
|
||||
{{- include "stellaops.labels" . | nindent 4 }}
|
||||
{{- with .Values.console.ingress.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .Values.console.ingress.className }}
|
||||
ingressClassName: {{ .Values.console.ingress.className }}
|
||||
{{- end }}
|
||||
{{- if .Values.console.ingress.tls }}
|
||||
tls:
|
||||
{{- range .Values.console.ingress.tls }}
|
||||
- hosts:
|
||||
{{- range .hosts }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
secretName: {{ .secretName }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- range .Values.console.ingress.hosts }}
|
||||
- host: {{ .host | quote }}
|
||||
http:
|
||||
paths:
|
||||
{{- range .paths }}
|
||||
- path: {{ .path }}
|
||||
pathType: {{ .pathType | default "Prefix" }}
|
||||
backend:
|
||||
service:
|
||||
name: {{ include "stellaops.fullname" $ }}-console
|
||||
port:
|
||||
name: http
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
225
deploy/helm/stellaops/templates/core.yaml
Normal file
225
deploy/helm/stellaops/templates/core.yaml
Normal file
@@ -0,0 +1,225 @@
|
||||
{{- $root := . -}}
|
||||
{{- $configMaps := default (dict) .Values.configMaps -}}
|
||||
{{- $hasPolicyActivationConfig := hasKey $configMaps "policy-engine-activation" -}}
|
||||
{{- $policyActivationConfigName := "" -}}
|
||||
{{- if $hasPolicyActivationConfig -}}
|
||||
{{- $policyActivationConfigName = include "stellaops.fullname" (dict "root" $root "name" "policy-engine-activation") -}}
|
||||
{{- end -}}
|
||||
{{- $policyActivationTargets := dict "policy-engine" true "policy-gateway" true -}}
|
||||
{{- range $name, $svc := .Values.services }}
|
||||
{{- $configMounts := (default (list) $svc.configMounts) }}
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "stellaops.fullname" (dict "root" $root "name" $name) }}
|
||||
labels:
|
||||
{{- include "stellaops.labels" (dict "root" $root "name" $name "svc" $svc) | nindent 4 }}
|
||||
spec:
|
||||
replicas: {{ default 1 $svc.replicas }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "stellaops.selectorLabels" (dict "root" $root "name" $name "svc" $svc) | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "stellaops.selectorLabels" (dict "root" $root "name" $name "svc" $svc) | nindent 8 }}
|
||||
{{- if $svc.podAnnotations }}
|
||||
annotations:
|
||||
{{ toYaml $svc.podAnnotations | nindent 8 }}
|
||||
{{- end }}
|
||||
annotations:
|
||||
stellaops.release/version: {{ $root.Values.global.release.version | quote }}
|
||||
stellaops.release/channel: {{ $root.Values.global.release.channel | quote }}
|
||||
spec:
|
||||
{{- if $svc.podSecurityContext }}
|
||||
securityContext:
|
||||
{{ toYaml $svc.podSecurityContext | nindent 6 }}
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: {{ $name }}
|
||||
image: {{ $svc.image | quote }}
|
||||
imagePullPolicy: {{ default $root.Values.global.image.pullPolicy $svc.imagePullPolicy }}
|
||||
{{- if $svc.securityContext }}
|
||||
securityContext:
|
||||
{{ toYaml $svc.securityContext | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if $svc.command }}
|
||||
command:
|
||||
{{- range $cmd := $svc.command }}
|
||||
- {{ $cmd | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if $svc.args }}
|
||||
args:
|
||||
{{- range $arg := $svc.args }}
|
||||
- {{ $arg | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if $svc.env }}
|
||||
env:
|
||||
{{- range $envName, $envValue := $svc.env }}
|
||||
- name: {{ $envName }}
|
||||
value: {{ $envValue | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- $needsPolicyActivation := and $hasPolicyActivationConfig (hasKey $policyActivationTargets $name) }}
|
||||
{{- $envFrom := default (list) $svc.envFrom }}
|
||||
{{- if and (hasKey $root.Values.configMaps "surface-env") (or (hasPrefix "scanner-" $name) (hasPrefix "zastava-" $name)) }}
|
||||
{{- $envFrom = append $envFrom (dict "configMapRef" (dict "name" (include "stellaops.fullname" (dict "root" $root "name" "surface-env")))) }}
|
||||
{{- end }}
|
||||
{{- if and $needsPolicyActivation (ne $policyActivationConfigName "") }}
|
||||
{{- $hasActivationReference := false }}
|
||||
{{- range $envFromEntry := $envFrom }}
|
||||
{{- if and (hasKey $envFromEntry "configMapRef") (eq (index (index $envFromEntry "configMapRef") "name") $policyActivationConfigName) }}
|
||||
{{- $hasActivationReference = true }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if not $hasActivationReference }}
|
||||
{{- $envFrom = append $envFrom (dict "configMapRef" (dict "name" $policyActivationConfigName)) }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if $envFrom }}
|
||||
envFrom:
|
||||
{{ toYaml $envFrom | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if $svc.ports }}
|
||||
ports:
|
||||
{{- range $port := $svc.ports }}
|
||||
- name: {{ default (printf "%s-%v" $name $port.containerPort) $port.name | trunc 63 | trimSuffix "-" }}
|
||||
containerPort: {{ $port.containerPort }}
|
||||
protocol: {{ default "TCP" $port.protocol }}
|
||||
{{- end }}
|
||||
{{- else if and $svc.service (hasKey $svc.service "port") }}
|
||||
{{- $svcService := $svc.service }}
|
||||
ports:
|
||||
- name: {{ printf "%s-http" $name | trunc 63 | trimSuffix "-" }}
|
||||
containerPort: {{ default (index $svcService "port") (index $svcService "targetPort") }}
|
||||
protocol: {{ default "TCP" (index $svcService "protocol") }}
|
||||
{{- end }}
|
||||
{{- if $svc.resources }}
|
||||
resources:
|
||||
{{ toYaml $svc.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if $svc.securityContext }}
|
||||
securityContext:
|
||||
{{ toYaml $svc.securityContext | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if $svc.securityContext }}
|
||||
securityContext:
|
||||
{{ toYaml $svc.securityContext | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if $svc.livenessProbe }}
|
||||
livenessProbe:
|
||||
{{ toYaml $svc.livenessProbe | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if $svc.readinessProbe }}
|
||||
readinessProbe:
|
||||
{{ toYaml $svc.readinessProbe | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if $svc.prometheus }}
|
||||
{{- $pr := $svc.prometheus }}
|
||||
{{- if $pr.enabled }}
|
||||
{{- if not $svc.podAnnotations }}
|
||||
{{- $svc = merge $svc (dict "podAnnotations" (dict)) }}
|
||||
{{- end }}
|
||||
{{- $svc.podAnnotations = merge $svc.podAnnotations (dict "prometheus.io/scrape" "true" "prometheus.io/path" (default "/metrics" $pr.path) "prometheus.io/port" (toString (default 8080 $pr.port)) "prometheus.io/scheme" (default "http" $pr.scheme))) }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if or $svc.volumeMounts $configMounts }}
|
||||
volumeMounts:
|
||||
{{- if $svc.volumeMounts }}
|
||||
{{ toYaml $svc.volumeMounts | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- range $mount := $configMounts }}
|
||||
- name: {{ $mount.name }}
|
||||
mountPath: {{ $mount.mountPath }}
|
||||
{{- if $mount.subPath }}
|
||||
subPath: {{ $mount.subPath }}
|
||||
{{- end }}
|
||||
{{- if hasKey $mount "readOnly" }}
|
||||
readOnly: {{ $mount.readOnly }}
|
||||
{{- else }}
|
||||
readOnly: true
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if or $svc.volumes (or $svc.volumeClaims $configMounts) }}
|
||||
volumes:
|
||||
{{- if $svc.volumes }}
|
||||
{{ toYaml $svc.volumes | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if $svc.volumeClaims }}
|
||||
{{- range $claim := $svc.volumeClaims }}
|
||||
- name: {{ $claim.name }}
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ $claim.claimName }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- range $mount := $configMounts }}
|
||||
- name: {{ $mount.name }}
|
||||
configMap:
|
||||
name: {{ include "stellaops.fullname" (dict "root" $root "name" $mount.configMap) }}
|
||||
{{- if $mount.items }}
|
||||
items:
|
||||
{{ toYaml $mount.items | nindent 12 }}
|
||||
{{- else if $mount.subPath }}
|
||||
items:
|
||||
- key: {{ $mount.subPath }}
|
||||
path: {{ $mount.subPath }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if $svc.serviceAccount }}
|
||||
serviceAccountName: {{ $svc.serviceAccount | quote }}
|
||||
{{- end }}
|
||||
{{- if $svc.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{ toYaml $svc.nodeSelector | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if $svc.affinity }}
|
||||
affinity:
|
||||
{{ toYaml $svc.affinity | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if $svc.tolerations }}
|
||||
tolerations:
|
||||
{{ toYaml $svc.tolerations | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if $svc.pdb }}
|
||||
---
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: {{ include "stellaops.fullname" (dict "root" $root "name" $name) }}
|
||||
labels:
|
||||
{{- include "stellaops.labels" (dict "root" $root "name" $name "svc" $svc) | nindent 4 }}
|
||||
spec:
|
||||
{{- if $svc.pdb.minAvailable }}
|
||||
minAvailable: {{ $svc.pdb.minAvailable }}
|
||||
{{- end }}
|
||||
{{- if $svc.pdb.maxUnavailable }}
|
||||
maxUnavailable: {{ $svc.pdb.maxUnavailable }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "stellaops.selectorLabels" (dict "root" $root "name" $name "svc" $svc) | nindent 6 }}
|
||||
{{- end }}
|
||||
---
|
||||
{{- if $svc.service }}
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "stellaops.fullname" (dict "root" $root "name" $name) }}
|
||||
labels:
|
||||
{{- include "stellaops.labels" (dict "root" $root "name" $name "svc" $svc) | nindent 4 }}
|
||||
spec:
|
||||
type: {{ default "ClusterIP" $svc.service.type }}
|
||||
selector:
|
||||
{{- include "stellaops.selectorLabels" (dict "root" $root "name" $name "svc" $svc) | nindent 4 }}
|
||||
ports:
|
||||
- name: {{ default "http" $svc.service.portName }}
|
||||
port: {{ $svc.service.port }}
|
||||
targetPort: {{ $svc.service.targetPort | default $svc.service.port }}
|
||||
protocol: {{ default "TCP" $svc.service.protocol }}
|
||||
---
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
28
deploy/helm/stellaops/templates/externalsecrets.yaml
Normal file
28
deploy/helm/stellaops/templates/externalsecrets.yaml
Normal file
@@ -0,0 +1,28 @@
|
||||
{{- if and .Values.externalSecrets.enabled .Values.externalSecrets.secrets }}
|
||||
{{- range $secret := .Values.externalSecrets.secrets }}
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: {{ include "stellaops.fullname" $ }}-{{ $secret.name }}
|
||||
labels:
|
||||
{{- include "stellaops.labels" $ | nindent 4 }}
|
||||
spec:
|
||||
refreshInterval: {{ default "1h" $secret.refreshInterval }}
|
||||
secretStoreRef:
|
||||
name: {{ $secret.storeRef.name }}
|
||||
kind: {{ default "ClusterSecretStore" $secret.storeRef.kind }}
|
||||
target:
|
||||
name: {{ $secret.target.name | default (printf "%s-%s" (include "stellaops.fullname" $) $secret.name) }}
|
||||
creationPolicy: {{ default "Owner" $secret.target.creationPolicy }}
|
||||
data:
|
||||
{{- range $secret.data }}
|
||||
- secretKey: {{ .key }}
|
||||
remoteRef:
|
||||
key: {{ .remoteKey }}
|
||||
{{- if .property }}
|
||||
property: {{ .property }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
---
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
39
deploy/helm/stellaops/templates/hpa.yaml
Normal file
39
deploy/helm/stellaops/templates/hpa.yaml
Normal file
@@ -0,0 +1,39 @@
|
||||
{{- if and .Values.hpa.enabled .Values.services }}
|
||||
{{- range $name, $svc := .Values.services }}
|
||||
{{- if and $svc.hpa $svc.hpa.enabled }}
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ include "stellaops.fullname" (dict "root" $ "name" $name) }}
|
||||
labels:
|
||||
{{- include "stellaops.labels" (dict "root" $ "name" $name "svc" $svc) | nindent 4 }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ include "stellaops.fullname" (dict "root" $ "name" $name) }}
|
||||
minReplicas: {{ default $.Values.hpa.minReplicas $svc.hpa.minReplicas }}
|
||||
maxReplicas: {{ default $.Values.hpa.maxReplicas $svc.hpa.maxReplicas }}
|
||||
metrics:
|
||||
{{- $cpu := coalesce $svc.hpa.cpu.targetPercentage $.Values.hpa.cpu.targetPercentage -}}
|
||||
{{- if $cpu }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ $cpu }}
|
||||
{{- end }}
|
||||
{{- $mem := coalesce $svc.hpa.memory.targetPercentage $.Values.hpa.memory.targetPercentage -}}
|
||||
{{- if $mem }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ $mem }}
|
||||
{{- end }}
|
||||
---
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
32
deploy/helm/stellaops/templates/ingress.yaml
Normal file
32
deploy/helm/stellaops/templates/ingress.yaml
Normal file
@@ -0,0 +1,32 @@
|
||||
{{- if and .Values.ingress.enabled .Values.ingress.hosts }}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ include "stellaops.fullname" . }}
|
||||
labels:
|
||||
{{- include "stellaops.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
{{- range $k, $v := .Values.ingress.annotations }}
|
||||
{{ $k }}: {{ $v | quote }}
|
||||
{{- end }}
|
||||
spec:
|
||||
ingressClassName: {{ .Values.ingress.className | default "nginx" | quote }}
|
||||
tls:
|
||||
{{- range .Values.ingress.tls }}
|
||||
- hosts: {{ toYaml .hosts | nindent 6 }}
|
||||
secretName: {{ .secretName }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- range .Values.ingress.hosts }}
|
||||
- host: {{ .host }}
|
||||
http:
|
||||
paths:
|
||||
- path: {{ .path | default "/" }}
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: {{ include "stellaops.fullname" $ }}-gateway
|
||||
port:
|
||||
number: {{ .servicePort | default 80 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
50
deploy/helm/stellaops/templates/migrations.yaml
Normal file
50
deploy/helm/stellaops/templates/migrations.yaml
Normal file
@@ -0,0 +1,50 @@
|
||||
{{- if and .Values.migrations.enabled .Values.migrations.jobs }}
|
||||
{{- range $job := .Values.migrations.jobs }}
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: {{ include "stellaops.fullname" $ }}-migration-{{ $job.name | trunc 30 | trimSuffix "-" }}
|
||||
labels:
|
||||
{{- include "stellaops.labels" $ | nindent 4 }}
|
||||
stellaops.io/component: migration
|
||||
stellaops.io/migration-name: {{ $job.name | quote }}
|
||||
spec:
|
||||
backoffLimit: {{ default 3 $job.backoffLimit }}
|
||||
ttlSecondsAfterFinished: {{ default 3600 $job.ttlSecondsAfterFinished }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "stellaops.selectorLabels" $ | nindent 8 }}
|
||||
stellaops.io/component: migration
|
||||
stellaops.io/migration-name: {{ $job.name | quote }}
|
||||
spec:
|
||||
restartPolicy: {{ default "Never" $job.restartPolicy }}
|
||||
serviceAccountName: {{ default "default" $job.serviceAccountName }}
|
||||
containers:
|
||||
- name: {{ $job.name | trunc 50 | trimSuffix "-" }}
|
||||
image: {{ $job.image | quote }}
|
||||
imagePullPolicy: {{ default "IfNotPresent" $job.imagePullPolicy }}
|
||||
command: {{- if $job.command }} {{ toJson $job.command }} {{- else }} null {{- end }}
|
||||
args: {{- if $job.args }} {{ toJson $job.args }} {{- else }} null {{- end }}
|
||||
env:
|
||||
{{- if $job.env }}
|
||||
{{- range $k, $v := $job.env }}
|
||||
- name: {{ $k }}
|
||||
value: {{ $v | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
envFrom:
|
||||
{{- if $job.envFrom }}
|
||||
{{- toYaml $job.envFrom | nindent 12 }}
|
||||
{{- end }}
|
||||
resources:
|
||||
{{- if $job.resources }}
|
||||
{{- toYaml $job.resources | nindent 12 }}
|
||||
{{- else }}{}
|
||||
{{- end }}
|
||||
imagePullSecrets:
|
||||
{{- if $.Values.global.image.pullSecrets }}
|
||||
{{- toYaml $.Values.global.image.pullSecrets | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
45
deploy/helm/stellaops/templates/networkpolicy.yaml
Normal file
45
deploy/helm/stellaops/templates/networkpolicy.yaml
Normal file
@@ -0,0 +1,45 @@
|
||||
{{- if .Values.networkPolicy.enabled }}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: {{ include "stellaops.fullname" . }}-default
|
||||
labels:
|
||||
{{- include "stellaops.labels" . | nindent 4 }}
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
{{- include "stellaops.selectorLabelsRoot" . | nindent 6 }}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
ingress:
|
||||
- from:
|
||||
{{- if .Values.networkPolicy.ingressNamespaces }}
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
{{- toYaml .Values.networkPolicy.ingressNamespaces | nindent 14 }}
|
||||
{{- end }}
|
||||
{{- if .Values.networkPolicy.ingressPods }}
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
{{- toYaml .Values.networkPolicy.ingressPods | nindent 14 }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: {{ default 80 .Values.networkPolicy.ingressPort }}
|
||||
egress:
|
||||
- to:
|
||||
{{- if .Values.networkPolicy.egressNamespaces }}
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
{{- toYaml .Values.networkPolicy.egressNamespaces | nindent 14 }}
|
||||
{{- end }}
|
||||
{{- if .Values.networkPolicy.egressPods }}
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
{{- toYaml .Values.networkPolicy.egressPods | nindent 14 }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: {{ default 443 .Values.networkPolicy.egressPort }}
|
||||
{{- end }}
|
||||
22
deploy/helm/stellaops/templates/orchestrator-mock.yaml
Normal file
22
deploy/helm/stellaops/templates/orchestrator-mock.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
{{- if .Values.mock.enabled }}
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: orchestrator-mock
|
||||
annotations:
|
||||
stellaops.dev/mock: "true"
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: orchestrator-mock
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: orchestrator-mock
|
||||
spec:
|
||||
containers:
|
||||
- name: orchestrator
|
||||
image: "{{ .Values.mock.orchestrator.image }}"
|
||||
args: ["dotnet", "StellaOps.Orchestrator.WebService.dll"]
|
||||
{{- end }}
|
||||
121
deploy/helm/stellaops/templates/otel-collector.yaml
Normal file
121
deploy/helm/stellaops/templates/otel-collector.yaml
Normal file
@@ -0,0 +1,121 @@
|
||||
{{- if .Values.telemetry.collector.enabled }}
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "stellaops.telemetryCollector.fullname" . }}
|
||||
labels:
|
||||
{{- include "stellaops.labels" (dict "root" . "name" "otel-collector" "svc" (dict "class" "telemetry")) | nindent 4 }}
|
||||
data:
|
||||
config.yaml: |
|
||||
{{ include "stellaops.telemetryCollector.config" . | indent 4 }}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "stellaops.telemetryCollector.fullname" . }}
|
||||
labels:
|
||||
{{- include "stellaops.labels" (dict "root" . "name" "otel-collector" "svc" (dict "class" "telemetry")) | nindent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.telemetry.collector.replicas | default 1 }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: {{ include "stellaops.name" . | quote }}
|
||||
app.kubernetes.io/component: "otel-collector"
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ include "stellaops.name" . | quote }}
|
||||
app.kubernetes.io/component: "otel-collector"
|
||||
stellaops.profile: {{ .Values.global.profile | quote }}
|
||||
spec:
|
||||
containers:
|
||||
- name: otel-collector
|
||||
image: {{ .Values.telemetry.collector.image | default "otel/opentelemetry-collector:0.105.0" | quote }}
|
||||
args:
|
||||
- "--config=/etc/otel/config.yaml"
|
||||
ports:
|
||||
- name: otlp-grpc
|
||||
containerPort: 4317
|
||||
- name: otlp-http
|
||||
containerPort: 4318
|
||||
- name: metrics
|
||||
containerPort: 9464
|
||||
- name: health
|
||||
containerPort: 13133
|
||||
- name: pprof
|
||||
containerPort: 1777
|
||||
env:
|
||||
- name: STELLAOPS_OTEL_TLS_CERT
|
||||
value: {{ .Values.telemetry.collector.tls.certPath | default "/etc/otel/tls/tls.crt" | quote }}
|
||||
- name: STELLAOPS_OTEL_TLS_KEY
|
||||
value: {{ .Values.telemetry.collector.tls.keyPath | default "/etc/otel/tls/tls.key" | quote }}
|
||||
- name: STELLAOPS_OTEL_TLS_CA
|
||||
value: {{ .Values.telemetry.collector.tls.caPath | default "/etc/otel/tls/ca.crt" | quote }}
|
||||
- name: STELLAOPS_OTEL_PROMETHEUS_ENDPOINT
|
||||
value: {{ .Values.telemetry.collector.prometheusEndpoint | default "0.0.0.0:9464" | quote }}
|
||||
- name: STELLAOPS_OTEL_REQUIRE_CLIENT_CERT
|
||||
value: {{ .Values.telemetry.collector.requireClientCert | default true | quote }}
|
||||
- name: STELLAOPS_TENANT_ID
|
||||
value: {{ .Values.telemetry.collector.defaultTenant | default "unknown" | quote }}
|
||||
- name: STELLAOPS_OTEL_LOG_LEVEL
|
||||
value: {{ .Values.telemetry.collector.logLevel | default "info" | quote }}
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /etc/otel/config.yaml
|
||||
subPath: config.yaml
|
||||
readOnly: true
|
||||
- name: tls
|
||||
mountPath: /etc/otel/tls
|
||||
readOnly: true
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
scheme: HTTPS
|
||||
port: health
|
||||
path: /healthz
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 30
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
scheme: HTTPS
|
||||
port: health
|
||||
path: /healthz
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 15
|
||||
{{- with .Values.telemetry.collector.resources }}
|
||||
resources:
|
||||
{{ toYaml . | indent 12 }}
|
||||
{{- end }}
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: {{ include "stellaops.telemetryCollector.fullname" . }}
|
||||
- name: tls
|
||||
secret:
|
||||
secretName: {{ .Values.telemetry.collector.tls.secretName | required "telemetry.collector.tls.secretName is required" }}
|
||||
{{- if .Values.telemetry.collector.tls.items }}
|
||||
items:
|
||||
{{ toYaml .Values.telemetry.collector.tls.items | indent 14 }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "stellaops.telemetryCollector.fullname" . }}
|
||||
labels:
|
||||
{{- include "stellaops.labels" (dict "root" . "name" "otel-collector" "svc" (dict "class" "telemetry")) | nindent 4 }}
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app.kubernetes.io/name: {{ include "stellaops.name" . | quote }}
|
||||
app.kubernetes.io/component: "otel-collector"
|
||||
ports:
|
||||
- name: otlp-grpc
|
||||
port: {{ .Values.telemetry.collector.service.grpcPort | default 4317 }}
|
||||
targetPort: otlp-grpc
|
||||
- name: otlp-http
|
||||
port: {{ .Values.telemetry.collector.service.httpPort | default 4318 }}
|
||||
targetPort: otlp-http
|
||||
- name: metrics
|
||||
port: {{ .Values.telemetry.collector.service.metricsPort | default 9464 }}
|
||||
targetPort: metrics
|
||||
{{- end }}
|
||||
44
deploy/helm/stellaops/templates/packs-mock.yaml
Normal file
44
deploy/helm/stellaops/templates/packs-mock.yaml
Normal file
@@ -0,0 +1,44 @@
|
||||
{{- if .Values.mock.enabled }}
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: packs-registry-mock
|
||||
annotations:
|
||||
stellaops.dev/mock: "true"
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: packs-registry-mock
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: packs-registry-mock
|
||||
spec:
|
||||
containers:
|
||||
- name: packs-registry
|
||||
image: "{{ .Values.mock.packsRegistry.image }}"
|
||||
args: ["dotnet", "StellaOps.PacksRegistry.dll"]
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: task-runner-mock
|
||||
annotations:
|
||||
stellaops.dev/mock: "true"
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: task-runner-mock
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: task-runner-mock
|
||||
spec:
|
||||
containers:
|
||||
- name: task-runner
|
||||
image: "{{ .Values.mock.taskRunner.image }}"
|
||||
args: ["dotnet", "StellaOps.TaskRunner.WebService.dll"]
|
||||
{{- end }}
|
||||
22
deploy/helm/stellaops/templates/policy-mock.yaml
Normal file
22
deploy/helm/stellaops/templates/policy-mock.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
{{- if .Values.mock.enabled }}
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: policy-registry-mock
|
||||
annotations:
|
||||
stellaops.dev/mock: "true"
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: policy-registry-mock
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: policy-registry-mock
|
||||
spec:
|
||||
containers:
|
||||
- name: policy-registry
|
||||
image: "{{ .Values.mock.policyRegistry.image }}"
|
||||
args: ["dotnet", "StellaOps.Policy.Engine.dll"]
|
||||
{{- end }}
|
||||
22
deploy/helm/stellaops/templates/vex-mock.yaml
Normal file
22
deploy/helm/stellaops/templates/vex-mock.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
{{- if .Values.mock.enabled }}
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: vex-lens-mock
|
||||
annotations:
|
||||
stellaops.dev/mock: "true"
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: vex-lens-mock
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: vex-lens-mock
|
||||
spec:
|
||||
containers:
|
||||
- name: vex-lens
|
||||
image: "{{ .Values.mock.vexLens.image }}"
|
||||
args: ["dotnet", "StellaOps.VexLens.dll"]
|
||||
{{- end }}
|
||||
44
deploy/helm/stellaops/templates/vuln-mock.yaml
Normal file
44
deploy/helm/stellaops/templates/vuln-mock.yaml
Normal file
@@ -0,0 +1,44 @@
|
||||
{{- if .Values.mock.enabled }}
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: findings-ledger-mock
|
||||
annotations:
|
||||
stellaops.dev/mock: "true"
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: findings-ledger-mock
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: findings-ledger-mock
|
||||
spec:
|
||||
containers:
|
||||
- name: findings-ledger
|
||||
image: "{{ .Values.mock.findingsLedger.image }}"
|
||||
args: ["dotnet", "StellaOps.Findings.Ledger.WebService.dll"]
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: vuln-explorer-api-mock
|
||||
annotations:
|
||||
stellaops.dev/mock: "true"
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: vuln-explorer-api-mock
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: vuln-explorer-api-mock
|
||||
spec:
|
||||
containers:
|
||||
- name: vuln-explorer-api
|
||||
image: "{{ .Values.mock.vulnExplorerApi.image }}"
|
||||
args: ["dotnet", "StellaOps.VulnExplorer.Api.dll"]
|
||||
{{- end }}
|
||||
318
deploy/helm/stellaops/values-airgap.yaml
Normal file
318
deploy/helm/stellaops/values-airgap.yaml
Normal file
@@ -0,0 +1,318 @@
|
||||
global:
|
||||
profile: airgap
|
||||
release:
|
||||
version: "2025.09.2-airgap"
|
||||
channel: airgap
|
||||
manifestSha256: "b787b833dddd73960c31338279daa0b0a0dce2ef32bd32ef1aaf953d66135f94"
|
||||
image:
|
||||
pullPolicy: IfNotPresent
|
||||
labels:
|
||||
stellaops.io/channel: airgap
|
||||
|
||||
migrations:
|
||||
enabled: false
|
||||
jobs: []
|
||||
|
||||
networkPolicy:
|
||||
enabled: true
|
||||
ingressPort: 8443
|
||||
egressPort: 443
|
||||
ingressNamespaces:
|
||||
kubernetes.io/metadata.name: stellaops
|
||||
egressNamespaces:
|
||||
kubernetes.io/metadata.name: stellaops
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
className: nginx
|
||||
annotations: {}
|
||||
hosts: []
|
||||
tls: []
|
||||
|
||||
externalSecrets:
|
||||
enabled: false
|
||||
secrets: []
|
||||
|
||||
prometheus:
|
||||
enabled: true
|
||||
path: /metrics
|
||||
port: 8080
|
||||
scheme: http
|
||||
|
||||
hpa:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
maxReplicas: 3
|
||||
cpu:
|
||||
targetPercentage: 70
|
||||
memory:
|
||||
targetPercentage: 80
|
||||
|
||||
configMaps:
|
||||
notify-config:
|
||||
data:
|
||||
notify.yaml: |
|
||||
storage:
|
||||
driver: postgres
|
||||
connectionString: "Host=stellaops-postgres;Port=5432;Database=notify;Username=stellaops;Password=stellaops"
|
||||
commandTimeoutSeconds: 60
|
||||
|
||||
authority:
|
||||
enabled: true
|
||||
issuer: "https://authority.stella-ops.org"
|
||||
metadataAddress: "https://authority.stella-ops.org/.well-known/openid-configuration"
|
||||
requireHttpsMetadata: true
|
||||
allowAnonymousFallback: false
|
||||
backchannelTimeoutSeconds: 30
|
||||
tokenClockSkewSeconds: 60
|
||||
audiences:
|
||||
- notify
|
||||
readScope: notify.read
|
||||
adminScope: notify.admin
|
||||
|
||||
api:
|
||||
basePath: "/api/v1/notify"
|
||||
internalBasePath: "/internal/notify"
|
||||
tenantHeader: "X-StellaOps-Tenant"
|
||||
|
||||
plugins:
|
||||
baseDirectory: "/var/opt/stellaops"
|
||||
directory: "plugins/notify"
|
||||
searchPatterns:
|
||||
- "StellaOps.Notify.Connectors.*.dll"
|
||||
orderedPlugins:
|
||||
- StellaOps.Notify.Connectors.Slack
|
||||
- StellaOps.Notify.Connectors.Teams
|
||||
- StellaOps.Notify.Connectors.Email
|
||||
- StellaOps.Notify.Connectors.Webhook
|
||||
|
||||
telemetry:
|
||||
enableRequestLogging: true
|
||||
minimumLogLevel: Warning
|
||||
policy-engine-activation:
|
||||
data:
|
||||
STELLAOPS_POLICY_ENGINE__ACTIVATION__FORCETWOPERSONAPPROVAL: "true"
|
||||
STELLAOPS_POLICY_ENGINE__ACTIVATION__DEFAULTREQUIRESTWOPERSONAPPROVAL: "true"
|
||||
STELLAOPS_POLICY_ENGINE__ACTIVATION__EMITAUDITLOGS: "true"
|
||||
|
||||
|
||||
services:
|
||||
authority:
|
||||
image: registry.stella-ops.org/stellaops/authority@sha256:5551a3269b7008cd5aceecf45df018c67459ed519557ccbe48b093b926a39bcc
|
||||
service:
|
||||
port: 8440
|
||||
env:
|
||||
STELLAOPS_AUTHORITY__ISSUER: "https://stellaops-authority:8440"
|
||||
STELLAOPS_AUTHORITY__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_AUTHORITY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=authority;Username=stellaops;Password=stellaops"
|
||||
STELLAOPS_AUTHORITY__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
STELLAOPS_AUTHORITY__ALLOWANONYMOUSFALLBACK: "false"
|
||||
signer:
|
||||
image: registry.stella-ops.org/stellaops/signer@sha256:ddbbd664a42846cea6b40fca6465bc679b30f72851158f300d01a8571c5478fc
|
||||
service:
|
||||
port: 8441
|
||||
env:
|
||||
SIGNER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
|
||||
SIGNER__POE__INTROSPECTURL: "file:///offline/poe/introspect.json"
|
||||
SIGNER__STORAGE__DRIVER: "postgres"
|
||||
SIGNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=signer;Username=stellaops;Password=stellaops"
|
||||
SIGNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
attestor:
|
||||
image: registry.stella-ops.org/stellaops/attestor@sha256:1ff0a3124d66d3a2702d8e421df40fbd98cc75cb605d95510598ebbae1433c50
|
||||
service:
|
||||
port: 8442
|
||||
env:
|
||||
ATTESTOR__SIGNER__BASEURL: "https://stellaops-signer:8441"
|
||||
ATTESTOR__STORAGE__DRIVER: "postgres"
|
||||
ATTESTOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=attestor;Username=stellaops;Password=stellaops"
|
||||
ATTESTOR__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
concelier:
|
||||
image: registry.stella-ops.org/stellaops/concelier@sha256:29e2e1a0972707e092cbd3d370701341f9fec2aa9316fb5d8100480f2a1c76b5
|
||||
service:
|
||||
port: 8445
|
||||
env:
|
||||
CONCELIER__STORAGE__DRIVER: "postgres"
|
||||
CONCELIER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=concelier;Username=stellaops;Password=stellaops"
|
||||
CONCELIER__STORAGE__S3__ENDPOINT: "http://stellaops-rustfs:8080"
|
||||
CONCELIER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
CONCELIER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__ALLOWOFFLINECACHEFALLBACK: "true"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__OFFLINECACHETOLERANCE: "00:45:00"
|
||||
volumeMounts:
|
||||
- name: concelier-jobs
|
||||
mountPath: /var/lib/concelier/jobs
|
||||
volumeClaims:
|
||||
- name: concelier-jobs
|
||||
claimName: stellaops-concelier-jobs
|
||||
scanner-web:
|
||||
image: registry.stella-ops.org/stellaops/scanner-web@sha256:3df8ca21878126758203c1a0444e39fd97f77ddacf04a69685cda9f1e5e94718
|
||||
service:
|
||||
port: 8444
|
||||
env:
|
||||
SCANNER__STORAGE__DRIVER: "postgres"
|
||||
SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=scanner;Username=stellaops;Password=stellaops"
|
||||
SCANNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
|
||||
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
|
||||
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
|
||||
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
|
||||
SCANNER__QUEUE__BROKER: "valkey://stellaops-valkey:6379"
|
||||
SCANNER__EVENTS__ENABLED: "false"
|
||||
SCANNER__EVENTS__DRIVER: "valkey"
|
||||
SCANNER__EVENTS__DSN: "stellaops-valkey:6379"
|
||||
SCANNER__EVENTS__STREAM: "stella.events"
|
||||
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
|
||||
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
|
||||
SCANNER__OFFLINEKIT__ENABLED: "false"
|
||||
SCANNER__OFFLINEKIT__REQUIREDSSE: "true"
|
||||
SCANNER__OFFLINEKIT__REKOROFFLINEMODE: "true"
|
||||
SCANNER__OFFLINEKIT__TRUSTROOTDIRECTORY: "/etc/stellaops/trust-roots"
|
||||
SCANNER__OFFLINEKIT__REKORSNAPSHOTDIRECTORY: "/var/lib/stellaops/rekor-snapshot"
|
||||
SCANNER_SURFACE_FS_ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
|
||||
SCANNER_SURFACE_CACHE_ROOT: "/var/lib/stellaops/surface"
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER: "file"
|
||||
SCANNER_SURFACE_SECRETS_ROOT: "/etc/stellaops/secrets"
|
||||
scanner-worker:
|
||||
image: registry.stella-ops.org/stellaops/scanner-worker@sha256:eea5d6cfe7835950c5ec7a735a651f2f0d727d3e470cf9027a4a402ea89c4fb5
|
||||
env:
|
||||
SCANNER__STORAGE__DRIVER: "postgres"
|
||||
SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=scanner;Username=stellaops;Password=stellaops"
|
||||
SCANNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
|
||||
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
|
||||
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
|
||||
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
|
||||
SCANNER__QUEUE__BROKER: "valkey://stellaops-valkey:6379"
|
||||
SCANNER__EVENTS__ENABLED: "false"
|
||||
SCANNER__EVENTS__DRIVER: "valkey"
|
||||
SCANNER__EVENTS__DSN: "stellaops-valkey:6379"
|
||||
SCANNER__EVENTS__STREAM: "stella.events"
|
||||
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
|
||||
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
|
||||
SCANNER_SURFACE_FS_ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
|
||||
SCANNER_SURFACE_CACHE_ROOT: "/var/lib/stellaops/surface"
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER: "file"
|
||||
SCANNER_SURFACE_SECRETS_ROOT: "/etc/stellaops/secrets"
|
||||
# Secret Detection Rules Bundle
|
||||
SCANNER__FEATURES__EXPERIMENTAL__SECRETLEAKDETECTION: "false"
|
||||
SCANNER__SECRETS__BUNDLEPATH: "/opt/stellaops/plugins/scanner/analyzers/secrets"
|
||||
SCANNER__SECRETS__REQUIRESIGNATURE: "true"
|
||||
volumeMounts:
|
||||
- name: secrets-rules
|
||||
mountPath: /opt/stellaops/plugins/scanner/analyzers/secrets
|
||||
readOnly: true
|
||||
volumeClaims:
|
||||
- name: secrets-rules
|
||||
claimName: stellaops-secrets-rules
|
||||
notify-web:
|
||||
image: registry.stella-ops.org/stellaops/notify-web:2025.09.2
|
||||
service:
|
||||
port: 8446
|
||||
env:
|
||||
DOTNET_ENVIRONMENT: Production
|
||||
NOTIFY__QUEUE__DRIVER: "valkey"
|
||||
NOTIFY__QUEUE__VALKEY__URL: "stellaops-valkey:6379"
|
||||
configMounts:
|
||||
- name: notify-config
|
||||
mountPath: /app/etc/notify.yaml
|
||||
subPath: notify.yaml
|
||||
configMap: notify-config
|
||||
excititor:
|
||||
image: registry.stella-ops.org/stellaops/excititor@sha256:65c0ee13f773efe920d7181512349a09d363ab3f3e177d276136bd2742325a68
|
||||
env:
|
||||
EXCITITOR__CONCELIER__BASEURL: "https://stellaops-concelier:8445"
|
||||
EXCITITOR__STORAGE__DRIVER: "postgres"
|
||||
EXCITITOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=excititor;Username=stellaops;Password=stellaops"
|
||||
advisory-ai-web:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-web:2025.09.2-airgap
|
||||
service:
|
||||
port: 8448
|
||||
env:
|
||||
ADVISORYAI__AdvisoryAI__SbomBaseAddress: https://stellaops-scanner-web:8444
|
||||
ADVISORYAI__AdvisoryAI__Queue__DirectoryPath: /var/lib/advisory-ai/queue
|
||||
ADVISORYAI__AdvisoryAI__Storage__PlanCacheDirectory: /var/lib/advisory-ai/plans
|
||||
ADVISORYAI__AdvisoryAI__Storage__OutputDirectory: /var/lib/advisory-ai/outputs
|
||||
ADVISORYAI__AdvisoryAI__Inference__Mode: Local
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__BaseAddress: ""
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__ApiKey: ""
|
||||
volumeMounts:
|
||||
- name: advisory-ai-data
|
||||
mountPath: /var/lib/advisory-ai
|
||||
volumeClaims:
|
||||
- name: advisory-ai-data
|
||||
claimName: stellaops-advisory-ai-data
|
||||
advisory-ai-worker:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-worker:2025.09.2-airgap
|
||||
env:
|
||||
ADVISORYAI__AdvisoryAI__SbomBaseAddress: https://stellaops-scanner-web:8444
|
||||
ADVISORYAI__AdvisoryAI__Queue__DirectoryPath: /var/lib/advisory-ai/queue
|
||||
ADVISORYAI__AdvisoryAI__Storage__PlanCacheDirectory: /var/lib/advisory-ai/plans
|
||||
ADVISORYAI__AdvisoryAI__Storage__OutputDirectory: /var/lib/advisory-ai/outputs
|
||||
ADVISORYAI__AdvisoryAI__Inference__Mode: Local
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__BaseAddress: ""
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__ApiKey: ""
|
||||
volumeMounts:
|
||||
- name: advisory-ai-data
|
||||
mountPath: /var/lib/advisory-ai
|
||||
volumeClaims:
|
||||
- name: advisory-ai-data
|
||||
claimName: stellaops-advisory-ai-data
|
||||
web-ui:
|
||||
image: registry.stella-ops.org/stellaops/web-ui@sha256:bee9668011ff414572131dc777faab4da24473fe12c230893f161cabee092a1d
|
||||
service:
|
||||
port: 9443
|
||||
targetPort: 8443
|
||||
env:
|
||||
STELLAOPS_UI__BACKEND__BASEURL: "https://stellaops-scanner-web:8444"
|
||||
|
||||
# Infrastructure services
|
||||
postgres:
|
||||
class: infrastructure
|
||||
image: docker.io/library/postgres@sha256:8e97b8526ed19304b144f7478bc9201646acf0723cdc6e4b19bc9eb34879a27e
|
||||
service:
|
||||
port: 5432
|
||||
env:
|
||||
POSTGRES_USER: stellaops
|
||||
POSTGRES_PASSWORD: stellaops
|
||||
POSTGRES_DB: stellaops
|
||||
volumeMounts:
|
||||
- name: postgres-data
|
||||
mountPath: /var/lib/postgresql/data
|
||||
volumeClaims:
|
||||
- name: postgres-data
|
||||
claimName: stellaops-postgres-data
|
||||
valkey:
|
||||
class: infrastructure
|
||||
image: docker.io/valkey/valkey:9.0.1-alpine
|
||||
service:
|
||||
port: 6379
|
||||
command:
|
||||
- valkey-server
|
||||
- --appendonly
|
||||
- "yes"
|
||||
volumeMounts:
|
||||
- name: valkey-data
|
||||
mountPath: /data
|
||||
volumeClaims:
|
||||
- name: valkey-data
|
||||
claimName: stellaops-valkey-data
|
||||
rustfs:
|
||||
class: infrastructure
|
||||
image: registry.stella-ops.org/stellaops/rustfs:2025.09.2
|
||||
service:
|
||||
port: 8080
|
||||
command:
|
||||
- serve
|
||||
- --listen
|
||||
- 0.0.0.0:8080
|
||||
- --root
|
||||
- /data
|
||||
env:
|
||||
RUSTFS__LOG__LEVEL: info
|
||||
RUSTFS__STORAGE__PATH: /data
|
||||
volumeMounts:
|
||||
- name: rustfs-data
|
||||
mountPath: /data
|
||||
volumeClaims:
|
||||
- name: rustfs-data
|
||||
claimName: stellaops-rustfs-data
|
||||
104
deploy/helm/stellaops/values-bluegreen-blue.yaml
Normal file
104
deploy/helm/stellaops/values-bluegreen-blue.yaml
Normal file
@@ -0,0 +1,104 @@
|
||||
# Blue/Green Deployment: Blue Environment
|
||||
# Use this file alongside values-prod.yaml for the blue (current) environment
|
||||
#
|
||||
# Deploy with:
|
||||
# helm upgrade stellaops-blue ./devops/helm/stellaops \
|
||||
# --namespace stellaops-blue \
|
||||
# --values devops/helm/stellaops/values-prod.yaml \
|
||||
# --values devops/helm/stellaops/values-bluegreen-blue.yaml \
|
||||
# --wait
|
||||
|
||||
# Environment identification
|
||||
global:
|
||||
profile: prod-blue
|
||||
labels:
|
||||
stellaops.io/environment: blue
|
||||
stellaops.io/deployment-strategy: blue-green
|
||||
|
||||
# Deployment identification
|
||||
deployment:
|
||||
environment: blue
|
||||
color: blue
|
||||
namespace: stellaops-blue
|
||||
|
||||
# Ingress for direct blue access (for validation/debugging)
|
||||
ingress:
|
||||
enabled: true
|
||||
hosts:
|
||||
- host: stellaops-blue.example.com
|
||||
path: /
|
||||
servicePort: 80
|
||||
annotations:
|
||||
# Not a canary - this is the primary ingress for blue
|
||||
nginx.ingress.kubernetes.io/canary: "false"
|
||||
|
||||
# Service naming for traffic routing
|
||||
services:
|
||||
api:
|
||||
name: stellaops-blue-api
|
||||
web:
|
||||
name: stellaops-blue-web
|
||||
scanner:
|
||||
name: stellaops-blue-scanner
|
||||
|
||||
# Pod labels for service selector
|
||||
podLabels:
|
||||
stellaops.io/color: blue
|
||||
|
||||
# Shared resources (same for both blue and green)
|
||||
database:
|
||||
# IMPORTANT: Blue and Green share the same database
|
||||
# Ensure migrations are N-1 compatible
|
||||
host: postgres.shared.svc.cluster.local
|
||||
database: stellaops_production
|
||||
# Connection pool tuning for blue/green (half of normal)
|
||||
pool:
|
||||
minSize: 5
|
||||
maxSize: 25
|
||||
|
||||
valkey:
|
||||
# Separate Valkey (Redis-compatible) instance per environment to avoid cache conflicts
|
||||
host: valkey-blue.stellaops-blue.svc.cluster.local
|
||||
database: 0
|
||||
|
||||
evidence:
|
||||
storage:
|
||||
# IMPORTANT: Shared evidence storage for continuity
|
||||
bucket: stellaops-evidence-production
|
||||
prefix: "" # No prefix - shared namespace
|
||||
|
||||
# Health check configuration
|
||||
healthCheck:
|
||||
readiness:
|
||||
path: /health/ready
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 15
|
||||
liveness:
|
||||
path: /health/live
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
|
||||
# Resource allocation (half of normal for blue/green)
|
||||
resources:
|
||||
api:
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: 2000m
|
||||
memory: 2Gi
|
||||
scanner:
|
||||
requests:
|
||||
cpu: 1000m
|
||||
memory: 1Gi
|
||||
limits:
|
||||
cpu: 4000m
|
||||
memory: 4Gi
|
||||
|
||||
# Replica count (half of normal for blue/green)
|
||||
replicaCount:
|
||||
api: 2
|
||||
web: 2
|
||||
scanner: 2
|
||||
signer: 1
|
||||
attestor: 1
|
||||
126
deploy/helm/stellaops/values-bluegreen-green.yaml
Normal file
126
deploy/helm/stellaops/values-bluegreen-green.yaml
Normal file
@@ -0,0 +1,126 @@
|
||||
# Blue/Green Deployment: Green Environment
|
||||
# Use this file alongside values-prod.yaml for the green (new version) environment
|
||||
#
|
||||
# Deploy with:
|
||||
# helm upgrade stellaops-green ./devops/helm/stellaops \
|
||||
# --namespace stellaops-green \
|
||||
# --create-namespace \
|
||||
# --values devops/helm/stellaops/values-prod.yaml \
|
||||
# --values devops/helm/stellaops/values-bluegreen-green.yaml \
|
||||
# --set global.release.version="NEW_VERSION" \
|
||||
# --wait
|
||||
|
||||
# Environment identification
|
||||
global:
|
||||
profile: prod-green
|
||||
labels:
|
||||
stellaops.io/environment: green
|
||||
stellaops.io/deployment-strategy: blue-green
|
||||
|
||||
# Deployment identification
|
||||
deployment:
|
||||
environment: green
|
||||
color: green
|
||||
namespace: stellaops-green
|
||||
|
||||
# Ingress for green - starts as canary with 0% weight
|
||||
ingress:
|
||||
enabled: true
|
||||
hosts:
|
||||
- host: stellaops-green.example.com
|
||||
path: /
|
||||
servicePort: 80
|
||||
annotations:
|
||||
# Canary ingress for gradual traffic shifting
|
||||
nginx.ingress.kubernetes.io/canary: "true"
|
||||
nginx.ingress.kubernetes.io/canary-weight: "0"
|
||||
# Optional: header-based routing for testing
|
||||
nginx.ingress.kubernetes.io/canary-by-header: "X-Canary"
|
||||
nginx.ingress.kubernetes.io/canary-by-header-value: "green"
|
||||
|
||||
# Canary ingress for production hostname (traffic shifting)
|
||||
canaryIngress:
|
||||
enabled: true
|
||||
host: stellaops.example.com
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/canary: "true"
|
||||
nginx.ingress.kubernetes.io/canary-weight: "0" # Start at 0%, increase during cutover
|
||||
|
||||
# Service naming for traffic routing
|
||||
services:
|
||||
api:
|
||||
name: stellaops-green-api
|
||||
web:
|
||||
name: stellaops-green-web
|
||||
scanner:
|
||||
name: stellaops-green-scanner
|
||||
|
||||
# Pod labels for service selector
|
||||
podLabels:
|
||||
stellaops.io/color: green
|
||||
|
||||
# Shared resources (same for both blue and green)
|
||||
database:
|
||||
# IMPORTANT: Blue and Green share the same database
|
||||
# Ensure migrations are N-1 compatible
|
||||
host: postgres.shared.svc.cluster.local
|
||||
database: stellaops_production
|
||||
# Connection pool tuning for blue/green (half of normal)
|
||||
pool:
|
||||
minSize: 5
|
||||
maxSize: 25
|
||||
|
||||
valkey:
|
||||
# Separate Valkey (Redis-compatible) instance per environment to avoid cache conflicts
|
||||
host: valkey-green.stellaops-green.svc.cluster.local
|
||||
database: 0
|
||||
|
||||
evidence:
|
||||
storage:
|
||||
# IMPORTANT: Shared evidence storage for continuity
|
||||
bucket: stellaops-evidence-production
|
||||
prefix: "" # No prefix - shared namespace
|
||||
|
||||
# Health check configuration
|
||||
healthCheck:
|
||||
readiness:
|
||||
path: /health/ready
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 15
|
||||
liveness:
|
||||
path: /health/live
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
|
||||
# Resource allocation (half of normal for blue/green)
|
||||
resources:
|
||||
api:
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: 2000m
|
||||
memory: 2Gi
|
||||
scanner:
|
||||
requests:
|
||||
cpu: 1000m
|
||||
memory: 1Gi
|
||||
limits:
|
||||
cpu: 4000m
|
||||
memory: 4Gi
|
||||
|
||||
# Replica count (half of normal for blue/green)
|
||||
replicaCount:
|
||||
api: 2
|
||||
web: 2
|
||||
scanner: 2
|
||||
signer: 1
|
||||
attestor: 1
|
||||
|
||||
# Migration jobs - enable for green environment
|
||||
migrations:
|
||||
enabled: true
|
||||
# Run migrations before main deployment
|
||||
preUpgrade:
|
||||
enabled: true
|
||||
backoffLimit: 3
|
||||
84
deploy/helm/stellaops/values-console.yaml
Normal file
84
deploy/helm/stellaops/values-console.yaml
Normal file
@@ -0,0 +1,84 @@
|
||||
# Console (Angular SPA) values overlay
|
||||
# Use: helm install stellaops . -f values-console.yaml
|
||||
|
||||
console:
|
||||
enabled: true
|
||||
image: registry.stella-ops.org/stellaops/console:2025.10.0-edge
|
||||
replicas: 1
|
||||
port: 8080
|
||||
|
||||
# Backend API URL injected via config.json at startup
|
||||
apiBaseUrl: ""
|
||||
# Authority URL for OAuth/OIDC
|
||||
authorityUrl: ""
|
||||
# Tenant header name
|
||||
tenantHeader: "X-StellaOps-Tenant"
|
||||
|
||||
# Resource limits (nginx is lightweight)
|
||||
resources:
|
||||
limits:
|
||||
cpu: "200m"
|
||||
memory: "128Mi"
|
||||
requests:
|
||||
cpu: "50m"
|
||||
memory: "64Mi"
|
||||
|
||||
# Service configuration
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
targetPort: 8080
|
||||
|
||||
# Ingress configuration (enable for external access)
|
||||
ingress:
|
||||
enabled: false
|
||||
className: nginx
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
|
||||
hosts:
|
||||
- host: console.local
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls: []
|
||||
|
||||
# Health probes
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 8080
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 30
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 8080
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
|
||||
# Pod security context (non-root per DOCKER-44-001)
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 101
|
||||
runAsGroup: 101
|
||||
fsGroup: 101
|
||||
|
||||
# Container security context
|
||||
containerSecurityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
|
||||
# Volume mounts for nginx temp directories (RO rootfs)
|
||||
volumeMounts:
|
||||
- name: nginx-cache
|
||||
mountPath: /var/cache/nginx
|
||||
- name: nginx-run
|
||||
mountPath: /var/run
|
||||
volumes:
|
||||
- name: nginx-cache
|
||||
emptyDir: {}
|
||||
- name: nginx-run
|
||||
emptyDir: {}
|
||||
266
deploy/helm/stellaops/values-dev.yaml
Normal file
266
deploy/helm/stellaops/values-dev.yaml
Normal file
@@ -0,0 +1,266 @@
|
||||
global:
|
||||
profile: dev
|
||||
release:
|
||||
version: "2025.10.0-edge"
|
||||
channel: edge
|
||||
manifestSha256: "822f82987529ea38d2321dbdd2ef6874a4062a117116a20861c26a8df1807beb"
|
||||
image:
|
||||
pullPolicy: IfNotPresent
|
||||
labels:
|
||||
stellaops.io/channel: edge
|
||||
|
||||
telemetry:
|
||||
collector:
|
||||
enabled: true
|
||||
defaultTenant: dev
|
||||
tls:
|
||||
secretName: stellaops-otel-tls
|
||||
|
||||
configMaps:
|
||||
notify-config:
|
||||
data:
|
||||
notify.yaml: |
|
||||
storage:
|
||||
driver: postgres
|
||||
connectionString: "Host=stellaops-postgres;Port=5432;Database=notify;Username=stellaops;Password=stellaops"
|
||||
commandTimeoutSeconds: 30
|
||||
|
||||
authority:
|
||||
enabled: true
|
||||
issuer: "https://authority.dev.stella-ops.local"
|
||||
metadataAddress: "https://authority.dev.stella-ops.local/.well-known/openid-configuration"
|
||||
requireHttpsMetadata: false
|
||||
allowAnonymousFallback: false
|
||||
backchannelTimeoutSeconds: 30
|
||||
tokenClockSkewSeconds: 60
|
||||
audiences:
|
||||
- notify.dev
|
||||
readScope: notify.read
|
||||
adminScope: notify.admin
|
||||
|
||||
api:
|
||||
basePath: "/api/v1/notify"
|
||||
internalBasePath: "/internal/notify"
|
||||
tenantHeader: "X-StellaOps-Tenant"
|
||||
|
||||
plugins:
|
||||
baseDirectory: "../"
|
||||
directory: "plugins/notify"
|
||||
searchPatterns:
|
||||
- "StellaOps.Notify.Connectors.*.dll"
|
||||
orderedPlugins:
|
||||
- StellaOps.Notify.Connectors.Slack
|
||||
- StellaOps.Notify.Connectors.Teams
|
||||
- StellaOps.Notify.Connectors.Email
|
||||
- StellaOps.Notify.Connectors.Webhook
|
||||
|
||||
telemetry:
|
||||
enableRequestLogging: true
|
||||
minimumLogLevel: Debug
|
||||
policy-engine-activation:
|
||||
data:
|
||||
STELLAOPS_POLICY_ENGINE__ACTIVATION__FORCETWOPERSONAPPROVAL: "false"
|
||||
STELLAOPS_POLICY_ENGINE__ACTIVATION__DEFAULTREQUIRESTWOPERSONAPPROVAL: "false"
|
||||
STELLAOPS_POLICY_ENGINE__ACTIVATION__EMITAUDITLOGS: "true"
|
||||
|
||||
services:
|
||||
authority:
|
||||
image: registry.stella-ops.org/stellaops/authority@sha256:a8e8faec44a579aa5714e58be835f25575710430b1ad2ccd1282a018cd9ffcdd
|
||||
service:
|
||||
port: 8440
|
||||
env:
|
||||
STELLAOPS_AUTHORITY__ISSUER: "https://stellaops-authority:8440"
|
||||
STELLAOPS_AUTHORITY__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_AUTHORITY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=authority;Username=stellaops;Password=stellaops"
|
||||
STELLAOPS_AUTHORITY__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: "/app/plugins"
|
||||
STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: "/app/etc/authority.plugins"
|
||||
signer:
|
||||
image: registry.stella-ops.org/stellaops/signer@sha256:8bfef9a75783883d49fc18e3566553934e970b00ee090abee9cb110d2d5c3298
|
||||
service:
|
||||
port: 8441
|
||||
env:
|
||||
SIGNER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
|
||||
SIGNER__POE__INTROSPECTURL: "https://licensing.svc.local/introspect"
|
||||
SIGNER__STORAGE__DRIVER: "postgres"
|
||||
SIGNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=signer;Username=stellaops;Password=stellaops"
|
||||
SIGNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
attestor:
|
||||
image: registry.stella-ops.org/stellaops/attestor@sha256:5cc417948c029da01dccf36e4645d961a3f6d8de7e62fe98d845f07cd2282114
|
||||
service:
|
||||
port: 8442
|
||||
env:
|
||||
ATTESTOR__SIGNER__BASEURL: "https://stellaops-signer:8441"
|
||||
ATTESTOR__STORAGE__DRIVER: "postgres"
|
||||
ATTESTOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=attestor;Username=stellaops;Password=stellaops"
|
||||
ATTESTOR__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
concelier:
|
||||
image: registry.stella-ops.org/stellaops/concelier@sha256:dafef3954eb4b837e2c424dd2d23e1e4d60fa83794840fac9cd3dea1d43bd085
|
||||
service:
|
||||
port: 8445
|
||||
env:
|
||||
CONCELIER__STORAGE__DRIVER: "postgres"
|
||||
CONCELIER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=concelier;Username=stellaops;Password=stellaops"
|
||||
CONCELIER__STORAGE__S3__ENDPOINT: "http://stellaops-rustfs:8080"
|
||||
CONCELIER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
CONCELIER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
|
||||
volumeMounts:
|
||||
- name: concelier-jobs
|
||||
mountPath: /var/lib/concelier/jobs
|
||||
volumes:
|
||||
- name: concelier-jobs
|
||||
emptyDir: {}
|
||||
scanner-web:
|
||||
image: registry.stella-ops.org/stellaops/scanner-web@sha256:e0dfdb087e330585a5953029fb4757f5abdf7610820a085bd61b457dbead9a11
|
||||
service:
|
||||
port: 8444
|
||||
env:
|
||||
SCANNER__STORAGE__DRIVER: "postgres"
|
||||
SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=scanner;Username=stellaops;Password=stellaops"
|
||||
SCANNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
|
||||
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
|
||||
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
|
||||
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
|
||||
SCANNER__QUEUE__BROKER: "valkey://stellaops-valkey:6379"
|
||||
SCANNER__EVENTS__ENABLED: "false"
|
||||
SCANNER__EVENTS__DRIVER: "valkey"
|
||||
SCANNER__EVENTS__DSN: "stellaops-valkey:6379"
|
||||
SCANNER__EVENTS__STREAM: "stella.events"
|
||||
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
|
||||
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
|
||||
SCANNER__OFFLINEKIT__ENABLED: "false"
|
||||
SCANNER__OFFLINEKIT__REQUIREDSSE: "true"
|
||||
SCANNER__OFFLINEKIT__REKOROFFLINEMODE: "true"
|
||||
SCANNER__OFFLINEKIT__TRUSTROOTDIRECTORY: "/etc/stellaops/trust-roots"
|
||||
SCANNER__OFFLINEKIT__REKORSNAPSHOTDIRECTORY: "/var/lib/stellaops/rekor-snapshot"
|
||||
SCANNER_SURFACE_FS_ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
|
||||
SCANNER_SURFACE_CACHE_ROOT: "/var/lib/stellaops/surface"
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER: "inline"
|
||||
SCANNER_SURFACE_SECRETS_ROOT: ""
|
||||
scanner-worker:
|
||||
image: registry.stella-ops.org/stellaops/scanner-worker@sha256:92dda42f6f64b2d9522104a5c9ffb61d37b34dd193132b68457a259748008f37
|
||||
env:
|
||||
SCANNER__STORAGE__DRIVER: "postgres"
|
||||
SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=scanner;Username=stellaops;Password=stellaops"
|
||||
SCANNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
|
||||
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
|
||||
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
|
||||
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
|
||||
SCANNER__QUEUE__BROKER: "valkey://stellaops-valkey:6379"
|
||||
SCANNER__EVENTS__ENABLED: "false"
|
||||
SCANNER__EVENTS__DRIVER: "valkey"
|
||||
SCANNER__EVENTS__DSN: "stellaops-valkey:6379"
|
||||
SCANNER__EVENTS__STREAM: "stella.events"
|
||||
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
|
||||
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
|
||||
SCANNER_SURFACE_FS_ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
|
||||
SCANNER_SURFACE_CACHE_ROOT: "/var/lib/stellaops/surface"
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER: "inline"
|
||||
SCANNER_SURFACE_SECRETS_ROOT: ""
|
||||
notify-web:
|
||||
image: registry.stella-ops.org/stellaops/notify-web:2025.10.0-edge
|
||||
service:
|
||||
port: 8446
|
||||
env:
|
||||
DOTNET_ENVIRONMENT: Development
|
||||
NOTIFY__QUEUE__DRIVER: "valkey"
|
||||
NOTIFY__QUEUE__VALKEY__URL: "stellaops-valkey:6379"
|
||||
configMounts:
|
||||
- name: notify-config
|
||||
mountPath: /app/etc/notify.yaml
|
||||
subPath: notify.yaml
|
||||
configMap: notify-config
|
||||
excititor:
|
||||
image: registry.stella-ops.org/stellaops/excititor@sha256:d9bd5cadf1eab427447ce3df7302c30ded837239771cc6433b9befb895054285
|
||||
env:
|
||||
EXCITITOR__CONCELIER__BASEURL: "https://stellaops-concelier:8445"
|
||||
EXCITITOR__STORAGE__DRIVER: "postgres"
|
||||
EXCITITOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=excititor;Username=stellaops;Password=stellaops"
|
||||
advisory-ai-web:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-web:2025.10.0-edge
|
||||
service:
|
||||
port: 8448
|
||||
env:
|
||||
ADVISORYAI__AdvisoryAI__SbomBaseAddress: http://stellaops-scanner-web:8444
|
||||
ADVISORYAI__AdvisoryAI__Queue__DirectoryPath: /var/lib/advisory-ai/queue
|
||||
ADVISORYAI__AdvisoryAI__Storage__PlanCacheDirectory: /var/lib/advisory-ai/plans
|
||||
ADVISORYAI__AdvisoryAI__Storage__OutputDirectory: /var/lib/advisory-ai/outputs
|
||||
ADVISORYAI__AdvisoryAI__Inference__Mode: Local
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__BaseAddress: ""
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__ApiKey: ""
|
||||
volumeMounts:
|
||||
- name: advisory-ai-data
|
||||
mountPath: /var/lib/advisory-ai
|
||||
volumeClaims:
|
||||
- name: advisory-ai-data
|
||||
claimName: stellaops-advisory-ai-data
|
||||
advisory-ai-worker:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-worker:2025.10.0-edge
|
||||
env:
|
||||
ADVISORYAI__AdvisoryAI__SbomBaseAddress: http://stellaops-scanner-web:8444
|
||||
ADVISORYAI__AdvisoryAI__Queue__DirectoryPath: /var/lib/advisory-ai/queue
|
||||
ADVISORYAI__AdvisoryAI__Storage__PlanCacheDirectory: /var/lib/advisory-ai/plans
|
||||
ADVISORYAI__AdvisoryAI__Storage__OutputDirectory: /var/lib/advisory-ai/outputs
|
||||
ADVISORYAI__AdvisoryAI__Inference__Mode: Local
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__BaseAddress: ""
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__ApiKey: ""
|
||||
volumeMounts:
|
||||
- name: advisory-ai-data
|
||||
mountPath: /var/lib/advisory-ai
|
||||
volumeClaims:
|
||||
- name: advisory-ai-data
|
||||
claimName: stellaops-advisory-ai-data
|
||||
web-ui:
|
||||
image: registry.stella-ops.org/stellaops/web-ui@sha256:38b225fa7767a5b94ebae4dae8696044126aac429415e93de514d5dd95748dcf
|
||||
service:
|
||||
port: 8443
|
||||
env:
|
||||
STELLAOPS_UI__BACKEND__BASEURL: "https://stellaops-scanner-web:8444"
|
||||
|
||||
# Infrastructure services
|
||||
postgres:
|
||||
class: infrastructure
|
||||
image: docker.io/library/postgres@sha256:8e97b8526ed19304b144f7478bc9201646acf0723cdc6e4b19bc9eb34879a27e
|
||||
service:
|
||||
port: 5432
|
||||
env:
|
||||
POSTGRES_USER: stellaops
|
||||
POSTGRES_PASSWORD: stellaops
|
||||
POSTGRES_DB: stellaops
|
||||
volumeMounts:
|
||||
- name: postgres-data
|
||||
mountPath: /var/lib/postgresql/data
|
||||
volumes:
|
||||
- name: postgres-data
|
||||
emptyDir: {}
|
||||
valkey:
|
||||
class: infrastructure
|
||||
image: docker.io/valkey/valkey:9.0.1-alpine
|
||||
service:
|
||||
port: 6379
|
||||
command:
|
||||
- valkey-server
|
||||
- --appendonly
|
||||
- "yes"
|
||||
volumeMounts:
|
||||
- name: valkey-data
|
||||
mountPath: /data
|
||||
volumes:
|
||||
- name: valkey-data
|
||||
emptyDir: {}
|
||||
rustfs:
|
||||
class: infrastructure
|
||||
image: registry.stella-ops.org/stellaops/rustfs:2025.09.2
|
||||
service:
|
||||
port: 8080
|
||||
env:
|
||||
RUSTFS__LOG__LEVEL: info
|
||||
RUSTFS__STORAGE__PATH: /data
|
||||
volumeMounts:
|
||||
- name: rustfs-data
|
||||
mountPath: /data
|
||||
volumes:
|
||||
- name: rustfs-data
|
||||
emptyDir: {}
|
||||
14
deploy/helm/stellaops/values-export.yaml
Normal file
14
deploy/helm/stellaops/values-export.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
exportcenter:
|
||||
image:
|
||||
repository: registry.stella-ops.org/export-center
|
||||
tag: latest
|
||||
objectStorage:
|
||||
endpoint: http://rustfs:8080
|
||||
bucket: export-prod
|
||||
accessKeySecret: exportcenter-rustfs
|
||||
secretKeySecret: exportcenter-rustfs
|
||||
signing:
|
||||
kmsKey: exportcenter-kms
|
||||
kmsRegion: us-east-1
|
||||
dsse:
|
||||
enabled: true
|
||||
58
deploy/helm/stellaops/values-exporter.yaml
Normal file
58
deploy/helm/stellaops/values-exporter.yaml
Normal file
@@ -0,0 +1,58 @@
|
||||
# Exporter (Export Center) values overlay
|
||||
# Use: helm install stellaops . -f values-exporter.yaml
|
||||
|
||||
exporter:
|
||||
enabled: true
|
||||
image: registry.stella-ops.org/stellaops/exporter:2025.10.0-edge
|
||||
replicas: 1
|
||||
port: 8080
|
||||
|
||||
# Export configuration
|
||||
storage:
|
||||
# Object store for export artifacts
|
||||
endpoint: ""
|
||||
bucket: "stellaops-exports"
|
||||
region: ""
|
||||
|
||||
# Retention policy
|
||||
retention:
|
||||
defaultDays: 30
|
||||
maxDays: 365
|
||||
|
||||
resources:
|
||||
limits:
|
||||
cpu: "500m"
|
||||
memory: "512Mi"
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "256Mi"
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 8080
|
||||
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health/liveness
|
||||
port: 8080
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 30
|
||||
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health/readiness
|
||||
port: 8080
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 10001
|
||||
runAsGroup: 10001
|
||||
|
||||
containerSecurityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
59
deploy/helm/stellaops/values-ledger.yaml
Normal file
59
deploy/helm/stellaops/values-ledger.yaml
Normal file
@@ -0,0 +1,59 @@
|
||||
# Ledger (Findings Ledger) values overlay
|
||||
# Use: helm install stellaops . -f values-ledger.yaml
|
||||
|
||||
ledger:
|
||||
enabled: true
|
||||
image: registry.stella-ops.org/stellaops/findings-ledger:2025.10.0-edge
|
||||
replicas: 1
|
||||
port: 8080
|
||||
|
||||
# Database configuration
|
||||
postgres:
|
||||
host: ""
|
||||
port: 5432
|
||||
database: "stellaops_ledger"
|
||||
schema: "findings"
|
||||
# Connection string override (takes precedence)
|
||||
connectionString: ""
|
||||
|
||||
# Tenant isolation
|
||||
multiTenant: true
|
||||
defaultTenant: "default"
|
||||
|
||||
resources:
|
||||
limits:
|
||||
cpu: "1000m"
|
||||
memory: "1Gi"
|
||||
requests:
|
||||
cpu: "200m"
|
||||
memory: "512Mi"
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 8080
|
||||
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health/liveness
|
||||
port: 8080
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 30
|
||||
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health/readiness
|
||||
port: 8080
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 10001
|
||||
runAsGroup: 10001
|
||||
|
||||
containerSecurityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
305
deploy/helm/stellaops/values-mirror.yaml
Normal file
305
deploy/helm/stellaops/values-mirror.yaml
Normal file
@@ -0,0 +1,305 @@
|
||||
global:
|
||||
profile: mirror-managed
|
||||
release:
|
||||
version: "2025.10.0-edge"
|
||||
channel: edge
|
||||
manifestSha256: "822f82987529ea38d2321dbdd2ef6874a4062a117116a20861c26a8df1807beb"
|
||||
image:
|
||||
pullPolicy: IfNotPresent
|
||||
labels:
|
||||
stellaops.io/channel: edge
|
||||
|
||||
configMaps:
|
||||
mirror-gateway:
|
||||
data:
|
||||
mirror.conf: |
|
||||
proxy_cache_path /var/cache/nginx/mirror levels=1:2 keys_zone=mirror_cache:100m max_size=10g inactive=12h use_temp_path=off;
|
||||
|
||||
map $request_uri $mirror_cache_key {
|
||||
default $scheme$request_method$host$request_uri;
|
||||
}
|
||||
|
||||
upstream concelier_backend {
|
||||
server stellaops-concelier:8445;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
upstream excititor_backend {
|
||||
server stellaops-excititor:8448;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name mirror-primary.stella-ops.org;
|
||||
|
||||
ssl_certificate /etc/nginx/tls/mirror-primary.crt;
|
||||
ssl_certificate_key /etc/nginx/tls/mirror-primary.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
auth_basic "StellaOps Mirror – primary";
|
||||
auth_basic_user_file /etc/nginx/secrets/mirror-primary.htpasswd;
|
||||
|
||||
include /etc/nginx/conf.d/mirror-locations.conf;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name mirror-community.stella-ops.org;
|
||||
|
||||
ssl_certificate /etc/nginx/tls/mirror-community.crt;
|
||||
ssl_certificate_key /etc/nginx/tls/mirror-community.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
auth_basic "StellaOps Mirror – community";
|
||||
auth_basic_user_file /etc/nginx/secrets/mirror-community.htpasswd;
|
||||
|
||||
include /etc/nginx/conf.d/mirror-locations.conf;
|
||||
}
|
||||
mirror-locations.conf: |
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_redirect off;
|
||||
|
||||
add_header X-Cache-Status $upstream_cache_status always;
|
||||
|
||||
location = /healthz {
|
||||
default_type application/json;
|
||||
return 200 '{"status":"ok"}';
|
||||
}
|
||||
|
||||
location /concelier/exports/ {
|
||||
proxy_pass http://concelier_backend/concelier/exports/;
|
||||
proxy_cache mirror_cache;
|
||||
proxy_cache_key $mirror_cache_key;
|
||||
proxy_cache_valid 200 5m;
|
||||
proxy_cache_valid 404 1m;
|
||||
add_header Cache-Control "public, max-age=300, immutable" always;
|
||||
}
|
||||
|
||||
location /concelier/ {
|
||||
proxy_pass http://concelier_backend/concelier/;
|
||||
proxy_cache off;
|
||||
}
|
||||
|
||||
location /excititor/mirror/ {
|
||||
proxy_pass http://excititor_backend/excititor/mirror/;
|
||||
proxy_cache mirror_cache;
|
||||
proxy_cache_key $mirror_cache_key;
|
||||
proxy_cache_valid 200 5m;
|
||||
proxy_cache_valid 404 1m;
|
||||
add_header Cache-Control "public, max-age=300, immutable" always;
|
||||
}
|
||||
|
||||
location /excititor/ {
|
||||
proxy_pass http://excititor_backend/excititor/;
|
||||
proxy_cache off;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 404;
|
||||
}
|
||||
|
||||
|
||||
policy-engine-activation:
|
||||
data:
|
||||
STELLAOPS_POLICY_ENGINE__ACTIVATION__FORCETWOPERSONAPPROVAL: "true"
|
||||
STELLAOPS_POLICY_ENGINE__ACTIVATION__DEFAULTREQUIRESTWOPERSONAPPROVAL: "true"
|
||||
STELLAOPS_POLICY_ENGINE__ACTIVATION__EMITAUDITLOGS: "true"
|
||||
|
||||
services:
|
||||
concelier:
|
||||
image: registry.stella-ops.org/stellaops/concelier@sha256:dafef3954eb4b837e2c424dd2d23e1e4d60fa83794840fac9cd3dea1d43bd085
|
||||
service:
|
||||
port: 8445
|
||||
env:
|
||||
ASPNETCORE_URLS: "http://+:8445"
|
||||
CONCELIER__STORAGE__DRIVER: "postgres"
|
||||
CONCELIER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=concelier;Username=stellaops;Password=stellaops"
|
||||
CONCELIER__STORAGE__S3__ENDPOINT: "http://stellaops-rustfs:8080"
|
||||
CONCELIER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
CONCELIER__TELEMETRY__SERVICENAME: "stellaops-concelier-mirror"
|
||||
CONCELIER__MIRROR__ENABLED: "true"
|
||||
CONCELIER__MIRROR__EXPORTROOT: "/exports/json"
|
||||
CONCELIER__MIRROR__LATESTDIRECTORYNAME: "latest"
|
||||
CONCELIER__MIRROR__MIRRORDIRECTORYNAME: "mirror"
|
||||
CONCELIER__MIRROR__REQUIREAUTHENTICATION: "true"
|
||||
CONCELIER__MIRROR__MAXINDEXREQUESTSPERHOUR: "600"
|
||||
CONCELIER__MIRROR__DOMAINS__0__ID: "primary"
|
||||
CONCELIER__MIRROR__DOMAINS__0__DISPLAYNAME: "Primary Mirror"
|
||||
CONCELIER__MIRROR__DOMAINS__0__REQUIREAUTHENTICATION: "true"
|
||||
CONCELIER__MIRROR__DOMAINS__0__MAXDOWNLOADREQUESTSPERHOUR: "3600"
|
||||
CONCELIER__MIRROR__DOMAINS__1__ID: "community"
|
||||
CONCELIER__MIRROR__DOMAINS__1__DISPLAYNAME: "Community Mirror"
|
||||
CONCELIER__MIRROR__DOMAINS__1__REQUIREAUTHENTICATION: "false"
|
||||
CONCELIER__MIRROR__DOMAINS__1__MAXDOWNLOADREQUESTSPERHOUR: "1800"
|
||||
CONCELIER__AUTHORITY__ENABLED: "true"
|
||||
CONCELIER__AUTHORITY__ALLOWANONYMOUSFALLBACK: "false"
|
||||
CONCELIER__AUTHORITY__ISSUER: "https://authority.stella-ops.org"
|
||||
CONCELIER__AUTHORITY__METADATAADDRESS: ""
|
||||
CONCELIER__AUTHORITY__CLIENTID: "stellaops-concelier-mirror"
|
||||
CONCELIER__AUTHORITY__CLIENTSECRETFILE: "/run/secrets/concelier-authority-client"
|
||||
CONCELIER__AUTHORITY__CLIENTSCOPES__0: "concelier.mirror.read"
|
||||
CONCELIER__AUTHORITY__AUDIENCES__0: "api://concelier.mirror"
|
||||
CONCELIER__AUTHORITY__BYPASSNETWORKS__0: "10.0.0.0/8"
|
||||
CONCELIER__AUTHORITY__BYPASSNETWORKS__1: "127.0.0.1/32"
|
||||
CONCELIER__AUTHORITY__BYPASSNETWORKS__2: "::1/128"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__ENABLERETRIES: "true"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__RETRYDELAYS__0: "00:00:01"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__RETRYDELAYS__1: "00:00:02"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__RETRYDELAYS__2: "00:00:05"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__ALLOWOFFLINECACHEFALLBACK: "true"
|
||||
CONCELIER__AUTHORITY__RESILIENCE__OFFLINECACHETOLERANCE: "00:10:00"
|
||||
volumeMounts:
|
||||
- name: concelier-jobs
|
||||
mountPath: /var/lib/concelier/jobs
|
||||
- name: concelier-exports
|
||||
mountPath: /exports/json
|
||||
- name: concelier-secrets
|
||||
mountPath: /run/secrets
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: concelier-jobs
|
||||
persistentVolumeClaim:
|
||||
claimName: concelier-mirror-jobs
|
||||
- name: concelier-exports
|
||||
persistentVolumeClaim:
|
||||
claimName: concelier-mirror-exports
|
||||
- name: concelier-secrets
|
||||
secret:
|
||||
secretName: concelier-mirror-auth
|
||||
|
||||
excititor:
|
||||
image: registry.stella-ops.org/stellaops/excititor@sha256:d9bd5cadf1eab427447ce3df7302c30ded837239771cc6433b9befb895054285
|
||||
env:
|
||||
ASPNETCORE_URLS: "http://+:8448"
|
||||
EXCITITOR__STORAGE__DRIVER: "postgres"
|
||||
EXCITITOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=excititor;Username=stellaops;Password=stellaops"
|
||||
EXCITITOR__ARTIFACTS__FILESYSTEM__ROOT: "/exports"
|
||||
EXCITITOR__ARTIFACTS__FILESYSTEM__OVERWRITEEXISTING: "false"
|
||||
EXCITITOR__MIRROR__DOMAINS__0__ID: "primary"
|
||||
EXCITITOR__MIRROR__DOMAINS__0__DISPLAYNAME: "Primary Mirror"
|
||||
EXCITITOR__MIRROR__DOMAINS__0__REQUIREAUTHENTICATION: "true"
|
||||
EXCITITOR__MIRROR__DOMAINS__0__MAXINDEXREQUESTSPERHOUR: "300"
|
||||
EXCITITOR__MIRROR__DOMAINS__0__MAXDOWNLOADREQUESTSPERHOUR: "2400"
|
||||
EXCITITOR__MIRROR__DOMAINS__0__EXPORTS__0__KEY: "consensus-json"
|
||||
EXCITITOR__MIRROR__DOMAINS__0__EXPORTS__0__FORMAT: "json"
|
||||
EXCITITOR__MIRROR__DOMAINS__0__EXPORTS__0__VIEW: "consensus"
|
||||
EXCITITOR__MIRROR__DOMAINS__0__EXPORTS__1__KEY: "consensus-openvex"
|
||||
EXCITITOR__MIRROR__DOMAINS__0__EXPORTS__1__FORMAT: "openvex"
|
||||
EXCITITOR__MIRROR__DOMAINS__0__EXPORTS__1__VIEW: "consensus"
|
||||
EXCITITOR__MIRROR__DOMAINS__1__ID: "community"
|
||||
EXCITITOR__MIRROR__DOMAINS__1__DISPLAYNAME: "Community Mirror"
|
||||
EXCITITOR__MIRROR__DOMAINS__1__REQUIREAUTHENTICATION: "false"
|
||||
EXCITITOR__MIRROR__DOMAINS__1__MAXINDEXREQUESTSPERHOUR: "120"
|
||||
EXCITITOR__MIRROR__DOMAINS__1__MAXDOWNLOADREQUESTSPERHOUR: "600"
|
||||
EXCITITOR__MIRROR__DOMAINS__1__EXPORTS__0__KEY: "community-consensus"
|
||||
EXCITITOR__MIRROR__DOMAINS__1__EXPORTS__0__FORMAT: "json"
|
||||
EXCITITOR__MIRROR__DOMAINS__1__EXPORTS__0__VIEW: "consensus"
|
||||
volumeMounts:
|
||||
- name: excititor-exports
|
||||
mountPath: /exports
|
||||
- name: excititor-secrets
|
||||
mountPath: /run/secrets
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: excititor-exports
|
||||
persistentVolumeClaim:
|
||||
claimName: excititor-mirror-exports
|
||||
- name: excititor-secrets
|
||||
secret:
|
||||
secretName: excititor-mirror-auth
|
||||
|
||||
# Infrastructure services
|
||||
postgres:
|
||||
class: infrastructure
|
||||
image: docker.io/library/postgres@sha256:8e97b8526ed19304b144f7478bc9201646acf0723cdc6e4b19bc9eb34879a27e
|
||||
service:
|
||||
port: 5432
|
||||
env:
|
||||
POSTGRES_USER: stellaops
|
||||
POSTGRES_PASSWORD: stellaops
|
||||
POSTGRES_DB: stellaops
|
||||
volumeMounts:
|
||||
- name: postgres-data
|
||||
mountPath: /var/lib/postgresql/data
|
||||
volumeClaims:
|
||||
- name: postgres-data
|
||||
claimName: mirror-postgres-data
|
||||
|
||||
valkey:
|
||||
class: infrastructure
|
||||
image: docker.io/valkey/valkey:9.0.1-alpine
|
||||
service:
|
||||
port: 6379
|
||||
command:
|
||||
- valkey-server
|
||||
- --appendonly
|
||||
- "yes"
|
||||
volumeMounts:
|
||||
- name: valkey-data
|
||||
mountPath: /data
|
||||
volumeClaims:
|
||||
- name: valkey-data
|
||||
claimName: mirror-valkey-data
|
||||
|
||||
rustfs:
|
||||
class: infrastructure
|
||||
image: registry.stella-ops.org/stellaops/rustfs:2025.09.2
|
||||
service:
|
||||
port: 8080
|
||||
command:
|
||||
- serve
|
||||
- --listen
|
||||
- 0.0.0.0:8080
|
||||
- --root
|
||||
- /data
|
||||
env:
|
||||
RUSTFS__LOG__LEVEL: info
|
||||
RUSTFS__STORAGE__PATH: /data
|
||||
volumeMounts:
|
||||
- name: rustfs-data
|
||||
mountPath: /data
|
||||
volumeClaims:
|
||||
- name: rustfs-data
|
||||
claimName: mirror-rustfs-data
|
||||
|
||||
mirror-gateway:
|
||||
image: docker.io/library/nginx@sha256:208b70eefac13ee9be00e486f79c695b15cef861c680527171a27d253d834be9
|
||||
service:
|
||||
type: LoadBalancer
|
||||
port: 443
|
||||
portName: https
|
||||
targetPort: 443
|
||||
configMounts:
|
||||
- name: mirror-gateway-conf
|
||||
mountPath: /etc/nginx/conf.d
|
||||
configMap: mirror-gateway
|
||||
volumeMounts:
|
||||
- name: mirror-gateway-tls
|
||||
mountPath: /etc/nginx/tls
|
||||
readOnly: true
|
||||
- name: mirror-gateway-secrets
|
||||
mountPath: /etc/nginx/secrets
|
||||
readOnly: true
|
||||
- name: mirror-cache
|
||||
mountPath: /var/cache/nginx
|
||||
volumes:
|
||||
- name: mirror-gateway-tls
|
||||
secret:
|
||||
secretName: mirror-gateway-tls
|
||||
- name: mirror-gateway-secrets
|
||||
secret:
|
||||
secretName: mirror-gateway-htpasswd
|
||||
- name: mirror-cache
|
||||
emptyDir: {}
|
||||
18
deploy/helm/stellaops/values-mock.yaml
Normal file
18
deploy/helm/stellaops/values-mock.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
mock:
|
||||
enabled: true
|
||||
orchestrator:
|
||||
image: registry.stella-ops.org/stellaops/orchestrator@sha256:97f12856ce870bafd3328bda86833bcccbf56d255941d804966b5557f6610119
|
||||
policyRegistry:
|
||||
image: registry.stella-ops.org/stellaops/policy-registry@sha256:c6cad8055e9827ebcbebb6ad4d6866dce4b83a0a49b0a8a6500b736a5cb26fa7
|
||||
packsRegistry:
|
||||
image: registry.stella-ops.org/stellaops/packs-registry@sha256:1f5e9416c4dc608594ad6fad87c24d72134427f899c192b494e22b268499c791
|
||||
taskRunner:
|
||||
image: registry.stella-ops.org/stellaops/task-runner@sha256:eb5ad992b49a41554f41516be1a6afcfa6522faf2111c08ff2b3664ad2fc954b
|
||||
vexLens:
|
||||
image: registry.stella-ops.org/stellaops/vex-lens@sha256:b44e63ecfeebc345a70c073c1ce5ace709c58be0ffaad0e2862758aeee3092fb
|
||||
issuerDirectory:
|
||||
image: registry.stella-ops.org/stellaops/issuer-directory@sha256:67e8ef02c97d3156741e857756994888f30c373ace8e84886762edba9dc51914
|
||||
findingsLedger:
|
||||
image: registry.stella-ops.org/stellaops/findings-ledger@sha256:71d4c361ba8b2f8b69d652597bc3f2efc8a64f93fab854ce25272a88506df49c
|
||||
vulnExplorerApi:
|
||||
image: registry.stella-ops.org/stellaops/vuln-explorer-api@sha256:7fc7e43a05cbeb0106ce7d4d634612e83de6fdc119aaab754a71c1d60b82841d
|
||||
15
deploy/helm/stellaops/values-notify.yaml
Normal file
15
deploy/helm/stellaops/values-notify.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
notify:
|
||||
image:
|
||||
repository: registry.stella-ops.org/notify
|
||||
tag: latest
|
||||
smtp:
|
||||
host: smtp.example.com
|
||||
port: 587
|
||||
usernameSecret: notify-smtp
|
||||
passwordSecret: notify-smtp
|
||||
webhook:
|
||||
allowedHosts: ["https://hooks.slack.com"]
|
||||
chat:
|
||||
webhookSecret: notify-chat
|
||||
tls:
|
||||
secretName: notify-tls
|
||||
209
deploy/helm/stellaops/values-orchestrator.yaml
Normal file
209
deploy/helm/stellaops/values-orchestrator.yaml
Normal file
@@ -0,0 +1,209 @@
|
||||
# Orchestrator Service Helm Values Overlay
|
||||
# Enables job scheduling, DAG planning, and worker coordination.
|
||||
#
|
||||
# Usage:
|
||||
# helm upgrade stellaops ./stellaops -f values.yaml -f values-orchestrator.yaml
|
||||
|
||||
global:
|
||||
labels:
|
||||
stellaops.io/component: orchestrator
|
||||
|
||||
# Orchestrator-specific ConfigMaps
|
||||
configMaps:
|
||||
orchestrator-config:
|
||||
data:
|
||||
orchestrator.yaml: |
|
||||
Orchestrator:
|
||||
# Telemetry configuration
|
||||
telemetry:
|
||||
minimumLogLevel: Information
|
||||
enableRequestLogging: true
|
||||
otelEndpoint: ""
|
||||
|
||||
# Authority integration (disable for standalone testing)
|
||||
authority:
|
||||
enabled: true
|
||||
issuer: https://authority.svc.cluster.local/realms/stellaops
|
||||
requireHttpsMetadata: true
|
||||
audiences:
|
||||
- stellaops-platform
|
||||
readScope: orchestrator:read
|
||||
writeScope: orchestrator:write
|
||||
adminScope: orchestrator:admin
|
||||
|
||||
# Tenant resolution
|
||||
tenantHeader: X-StellaOps-Tenant
|
||||
|
||||
# PostgreSQL connection
|
||||
storage:
|
||||
connectionString: "Host=orchestrator-postgres;Database=stellaops_orchestrator;Username=orchestrator;Password=${POSTGRES_PASSWORD}"
|
||||
commandTimeoutSeconds: 60
|
||||
enableSensitiveDataLogging: false
|
||||
|
||||
# Scheduler configuration
|
||||
scheduler:
|
||||
# Maximum concurrent jobs per tenant
|
||||
defaultConcurrencyLimit: 100
|
||||
# Default rate limit (requests per second)
|
||||
defaultRateLimit: 50
|
||||
# Job claim timeout before re-queue
|
||||
claimTimeoutMinutes: 30
|
||||
# Heartbeat interval for active jobs
|
||||
heartbeatIntervalSeconds: 30
|
||||
# Maximum heartbeat misses before job marked stale
|
||||
maxHeartbeatMisses: 3
|
||||
|
||||
# Autoscaling configuration
|
||||
autoscaling:
|
||||
# Enable autoscaling metrics endpoint
|
||||
enabled: true
|
||||
# Queue depth threshold for scale-up signal
|
||||
queueDepthThreshold: 10000
|
||||
# Dispatch latency P95 threshold (ms)
|
||||
latencyP95ThresholdMs: 150
|
||||
# Scale-up cooldown period
|
||||
scaleUpCooldownSeconds: 60
|
||||
# Scale-down cooldown period
|
||||
scaleDownCooldownSeconds: 300
|
||||
|
||||
# Load shedding configuration
|
||||
loadShedding:
|
||||
enabled: true
|
||||
# Warning threshold (load factor)
|
||||
warningThreshold: 0.8
|
||||
# Critical threshold (load factor)
|
||||
criticalThreshold: 1.0
|
||||
# Emergency threshold (load factor)
|
||||
emergencyThreshold: 1.5
|
||||
# Recovery cooldown
|
||||
recoveryCooldownSeconds: 30
|
||||
|
||||
# Dead letter configuration
|
||||
deadLetter:
|
||||
# Maximum replay attempts
|
||||
maxReplayAttempts: 3
|
||||
# Entry expiration (days)
|
||||
expirationDays: 30
|
||||
# Purge interval
|
||||
purgeIntervalHours: 24
|
||||
|
||||
# Backfill configuration
|
||||
backfill:
|
||||
# Maximum concurrent backfill requests
|
||||
maxConcurrentRequests: 5
|
||||
# Default batch size
|
||||
defaultBatchSize: 1000
|
||||
# Maximum retention lookback (days)
|
||||
maxRetentionDays: 90
|
||||
|
||||
# Service definitions
|
||||
services:
|
||||
orchestrator-web:
|
||||
image: registry.stella-ops.org/stellaops/orchestrator-web:2025.10.0-edge
|
||||
replicas: 2
|
||||
service:
|
||||
port: 8080
|
||||
configMounts:
|
||||
- name: orchestrator-config
|
||||
configMap: orchestrator-config
|
||||
mountPath: /app/etc/orchestrator.yaml
|
||||
subPath: orchestrator.yaml
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: orchestrator-secrets
|
||||
env:
|
||||
ASPNETCORE_ENVIRONMENT: Production
|
||||
ORCHESTRATOR__CONFIG: /app/etc/orchestrator.yaml
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
resources:
|
||||
requests:
|
||||
memory: "256Mi"
|
||||
cpu: "250m"
|
||||
limits:
|
||||
memory: "1Gi"
|
||||
cpu: "1000m"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /readyz
|
||||
port: 8080
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /livez
|
||||
port: 8080
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 20
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /startupz
|
||||
port: 8080
|
||||
initialDelaySeconds: 3
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 30
|
||||
|
||||
orchestrator-worker:
|
||||
image: registry.stella-ops.org/stellaops/orchestrator-worker:2025.10.0-edge
|
||||
replicas: 1
|
||||
configMounts:
|
||||
- name: orchestrator-config
|
||||
configMap: orchestrator-config
|
||||
mountPath: /app/etc/orchestrator.yaml
|
||||
subPath: orchestrator.yaml
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: orchestrator-secrets
|
||||
env:
|
||||
DOTNET_ENVIRONMENT: Production
|
||||
ORCHESTRATOR__CONFIG: /app/etc/orchestrator.yaml
|
||||
resources:
|
||||
requests:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
|
||||
orchestrator-postgres:
|
||||
class: infrastructure
|
||||
image: docker.io/library/postgres:16-alpine
|
||||
service:
|
||||
port: 5432
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: orchestrator-postgres-secrets
|
||||
env:
|
||||
POSTGRES_DB: stellaops_orchestrator
|
||||
POSTGRES_USER: orchestrator
|
||||
volumeMounts:
|
||||
- name: postgres-data
|
||||
mountPath: /var/lib/postgresql/data
|
||||
volumeClaims:
|
||||
- name: postgres-data
|
||||
claimName: orchestrator-postgres-data
|
||||
readinessProbe:
|
||||
exec:
|
||||
command:
|
||||
- pg_isready
|
||||
- -U
|
||||
- orchestrator
|
||||
- -d
|
||||
- stellaops_orchestrator
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
exec:
|
||||
command:
|
||||
- pg_isready
|
||||
- -U
|
||||
- orchestrator
|
||||
- -d
|
||||
- stellaops_orchestrator
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 30
|
||||
356
deploy/helm/stellaops/values-prod.yaml
Normal file
356
deploy/helm/stellaops/values-prod.yaml
Normal file
@@ -0,0 +1,356 @@
|
||||
global:
|
||||
profile: prod
|
||||
release:
|
||||
version: "2025.09.2"
|
||||
channel: stable
|
||||
manifestSha256: "dc3c8fe1ab83941c838ccc5a8a5862f7ddfa38c2078e580b5649db26554565b7"
|
||||
image:
|
||||
pullPolicy: IfNotPresent
|
||||
labels:
|
||||
stellaops.io/channel: stable
|
||||
stellaops.io/profile: prod
|
||||
|
||||
# Migration jobs for controlled rollouts (disabled by default)
|
||||
migrations:
|
||||
enabled: false
|
||||
jobs: []
|
||||
|
||||
networkPolicy:
|
||||
enabled: true
|
||||
ingressPort: 8443
|
||||
egressPort: 443
|
||||
ingressNamespaces:
|
||||
kubernetes.io/metadata.name: stellaops
|
||||
egressNamespaces:
|
||||
kubernetes.io/metadata.name: stellaops
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
className: nginx
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
|
||||
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||
hosts:
|
||||
- host: gateway.prod.stella-ops.org
|
||||
path: /
|
||||
servicePort: 80
|
||||
tls:
|
||||
- secretName: stellaops-prod-tls
|
||||
hosts:
|
||||
- gateway.prod.stella-ops.org
|
||||
|
||||
externalSecrets:
|
||||
enabled: true
|
||||
secrets:
|
||||
- name: core-secrets
|
||||
storeRef:
|
||||
name: stellaops-secret-store
|
||||
kind: ClusterSecretStore
|
||||
target:
|
||||
name: stellaops-prod-core
|
||||
data:
|
||||
- key: STELLAOPS_AUTHORITY__JWT__SIGNINGKEY
|
||||
remoteKey: prod/authority/jwt-signing-key
|
||||
- key: STELLAOPS_SECRETS_ENCRYPTION_KEY
|
||||
remoteKey: prod/core/secrets-encryption-key
|
||||
|
||||
prometheus:
|
||||
enabled: true
|
||||
path: /metrics
|
||||
port: 8080
|
||||
scheme: http
|
||||
|
||||
hpa:
|
||||
enabled: true
|
||||
minReplicas: 2
|
||||
maxReplicas: 6
|
||||
cpu:
|
||||
targetPercentage: 70
|
||||
memory:
|
||||
targetPercentage: 75
|
||||
|
||||
configMaps:
|
||||
notify-config:
|
||||
data:
|
||||
notify.yaml: |
|
||||
storage:
|
||||
driver: postgres
|
||||
connectionString: "Host=stellaops-postgres;Port=5432;Database=notify;Username=stellaops;Password=stellaops"
|
||||
commandTimeoutSeconds: 45
|
||||
|
||||
authority:
|
||||
enabled: true
|
||||
issuer: "https://authority.prod.stella-ops.org"
|
||||
metadataAddress: "https://authority.prod.stella-ops.org/.well-known/openid-configuration"
|
||||
requireHttpsMetadata: true
|
||||
allowAnonymousFallback: false
|
||||
backchannelTimeoutSeconds: 30
|
||||
tokenClockSkewSeconds: 60
|
||||
audiences:
|
||||
- notify
|
||||
readScope: notify.read
|
||||
adminScope: notify.admin
|
||||
|
||||
api:
|
||||
basePath: "/api/v1/notify"
|
||||
internalBasePath: "/internal/notify"
|
||||
tenantHeader: "X-StellaOps-Tenant"
|
||||
|
||||
plugins:
|
||||
baseDirectory: "/opt/stellaops"
|
||||
directory: "plugins/notify"
|
||||
searchPatterns:
|
||||
- "StellaOps.Notify.Connectors.*.dll"
|
||||
orderedPlugins:
|
||||
- StellaOps.Notify.Connectors.Slack
|
||||
- StellaOps.Notify.Connectors.Teams
|
||||
- StellaOps.Notify.Connectors.Email
|
||||
- StellaOps.Notify.Connectors.Webhook
|
||||
|
||||
telemetry:
|
||||
enableRequestLogging: true
|
||||
minimumLogLevel: Information
|
||||
policy-engine-activation:
|
||||
data:
|
||||
STELLAOPS_POLICY_ENGINE__ACTIVATION__FORCETWOPERSONAPPROVAL: "true"
|
||||
STELLAOPS_POLICY_ENGINE__ACTIVATION__DEFAULTREQUIRESTWOPERSONAPPROVAL: "true"
|
||||
STELLAOPS_POLICY_ENGINE__ACTIVATION__EMITAUDITLOGS: "true"
|
||||
services:
|
||||
authority:
|
||||
image: registry.stella-ops.org/stellaops/authority@sha256:b0348bad1d0b401cc3c71cb40ba034c8043b6c8874546f90d4783c9dbfcc0bf5
|
||||
service:
|
||||
port: 8440
|
||||
env:
|
||||
STELLAOPS_AUTHORITY__ISSUER: "https://authority.prod.stella-ops.org"
|
||||
STELLAOPS_AUTHORITY__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_AUTHORITY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=authority;Username=stellaops;Password=stellaops"
|
||||
STELLAOPS_AUTHORITY__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: "/app/plugins"
|
||||
STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: "/app/etc/authority.plugins"
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: stellaops-prod-core
|
||||
signer:
|
||||
image: registry.stella-ops.org/stellaops/signer@sha256:8ad574e61f3a9e9bda8a58eb2700ae46813284e35a150b1137bc7c2b92ac0f2e
|
||||
service:
|
||||
port: 8441
|
||||
env:
|
||||
SIGNER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
|
||||
SIGNER__POE__INTROSPECTURL: "https://licensing.prod.stella-ops.org/introspect"
|
||||
SIGNER__STORAGE__DRIVER: "postgres"
|
||||
SIGNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=signer;Username=stellaops;Password=stellaops"
|
||||
SIGNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: stellaops-prod-core
|
||||
attestor:
|
||||
image: registry.stella-ops.org/stellaops/attestor@sha256:0534985f978b0b5d220d73c96fddd962cd9135f616811cbe3bff4666c5af568f
|
||||
service:
|
||||
port: 8442
|
||||
env:
|
||||
ATTESTOR__SIGNER__BASEURL: "https://stellaops-signer:8441"
|
||||
ATTESTOR__STORAGE__DRIVER: "postgres"
|
||||
ATTESTOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=attestor;Username=stellaops;Password=stellaops"
|
||||
ATTESTOR__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: stellaops-prod-core
|
||||
concelier:
|
||||
image: registry.stella-ops.org/stellaops/concelier@sha256:c58cdcaee1d266d68d498e41110a589dd204b487d37381096bd61ab345a867c5
|
||||
service:
|
||||
port: 8445
|
||||
env:
|
||||
CONCELIER__STORAGE__DRIVER: "postgres"
|
||||
CONCELIER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=concelier;Username=stellaops;Password=stellaops"
|
||||
CONCELIER__STORAGE__S3__ENDPOINT: "http://stellaops-rustfs:8080"
|
||||
CONCELIER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
CONCELIER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: stellaops-prod-core
|
||||
volumeMounts:
|
||||
- name: concelier-jobs
|
||||
mountPath: /var/lib/concelier/jobs
|
||||
volumeClaims:
|
||||
- name: concelier-jobs
|
||||
claimName: stellaops-concelier-jobs
|
||||
scanner-web:
|
||||
image: registry.stella-ops.org/stellaops/scanner-web@sha256:14b23448c3f9586a9156370b3e8c1991b61907efa666ca37dd3aaed1e79fe3b7
|
||||
service:
|
||||
port: 8444
|
||||
env:
|
||||
SCANNER__STORAGE__DRIVER: "postgres"
|
||||
SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=scanner;Username=stellaops;Password=stellaops"
|
||||
SCANNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
|
||||
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
|
||||
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
|
||||
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
|
||||
SCANNER__QUEUE__BROKER: "valkey://stellaops-valkey:6379"
|
||||
SCANNER__EVENTS__ENABLED: "true"
|
||||
SCANNER__EVENTS__DRIVER: "valkey"
|
||||
SCANNER__EVENTS__DSN: "stellaops-valkey:6379"
|
||||
SCANNER__EVENTS__STREAM: "stella.events"
|
||||
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
|
||||
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
|
||||
SCANNER__OFFLINEKIT__ENABLED: "false"
|
||||
SCANNER__OFFLINEKIT__REQUIREDSSE: "true"
|
||||
SCANNER__OFFLINEKIT__REKOROFFLINEMODE: "true"
|
||||
SCANNER__OFFLINEKIT__TRUSTROOTDIRECTORY: "/etc/stellaops/trust-roots"
|
||||
SCANNER__OFFLINEKIT__REKORSNAPSHOTDIRECTORY: "/var/lib/stellaops/rekor-snapshot"
|
||||
SCANNER_SURFACE_FS_ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
|
||||
SCANNER_SURFACE_CACHE_ROOT: "/var/lib/stellaops/surface"
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER: "kubernetes"
|
||||
SCANNER_SURFACE_SECRETS_ROOT: "stellaops/scanner"
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: stellaops-prod-core
|
||||
scanner-worker:
|
||||
image: registry.stella-ops.org/stellaops/scanner-worker@sha256:32e25e76386eb9ea8bee0a1ad546775db9a2df989fab61ac877e351881960dab
|
||||
replicas: 3
|
||||
env:
|
||||
SCANNER__STORAGE__DRIVER: "postgres"
|
||||
SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=scanner;Username=stellaops;Password=stellaops"
|
||||
SCANNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
|
||||
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
|
||||
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
|
||||
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
|
||||
SCANNER__QUEUE__BROKER: "valkey://stellaops-valkey:6379"
|
||||
SCANNER__EVENTS__ENABLED: "true"
|
||||
SCANNER__EVENTS__DRIVER: "valkey"
|
||||
SCANNER__EVENTS__DSN: "stellaops-valkey:6379"
|
||||
SCANNER__EVENTS__STREAM: "stella.events"
|
||||
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
|
||||
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
|
||||
SCANNER_SURFACE_FS_ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
|
||||
SCANNER_SURFACE_CACHE_ROOT: "/var/lib/stellaops/surface"
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER: "kubernetes"
|
||||
SCANNER_SURFACE_SECRETS_ROOT: "stellaops/scanner"
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: stellaops-prod-core
|
||||
notify-web:
|
||||
image: registry.stella-ops.org/stellaops/notify-web:2025.09.2
|
||||
service:
|
||||
port: 8446
|
||||
env:
|
||||
DOTNET_ENVIRONMENT: Production
|
||||
NOTIFY__QUEUE__DRIVER: "valkey"
|
||||
NOTIFY__QUEUE__VALKEY__URL: "stellaops-valkey:6379"
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: stellaops-prod-notify
|
||||
configMounts:
|
||||
- name: notify-config
|
||||
mountPath: /app/etc/notify.yaml
|
||||
subPath: notify.yaml
|
||||
configMap: notify-config
|
||||
excititor:
|
||||
image: registry.stella-ops.org/stellaops/excititor@sha256:59022e2016aebcef5c856d163ae705755d3f81949d41195256e935ef40a627fa
|
||||
env:
|
||||
EXCITITOR__CONCELIER__BASEURL: "https://stellaops-concelier:8445"
|
||||
EXCITITOR__STORAGE__DRIVER: "postgres"
|
||||
EXCITITOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=excititor;Username=stellaops;Password=stellaops"
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: stellaops-prod-core
|
||||
advisory-ai-web:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-web:2025.09.2
|
||||
service:
|
||||
port: 8448
|
||||
env:
|
||||
ADVISORYAI__AdvisoryAI__SbomBaseAddress: https://stellaops-scanner-web:8444
|
||||
ADVISORYAI__AdvisoryAI__Queue__DirectoryPath: /var/lib/advisory-ai/queue
|
||||
ADVISORYAI__AdvisoryAI__Storage__PlanCacheDirectory: /var/lib/advisory-ai/plans
|
||||
ADVISORYAI__AdvisoryAI__Storage__OutputDirectory: /var/lib/advisory-ai/outputs
|
||||
ADVISORYAI__AdvisoryAI__Inference__Mode: Local
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__BaseAddress: ""
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__ApiKey: ""
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: stellaops-prod-core
|
||||
volumeMounts:
|
||||
- name: advisory-ai-data
|
||||
mountPath: /var/lib/advisory-ai
|
||||
volumeClaims:
|
||||
- name: advisory-ai-data
|
||||
claimName: stellaops-advisory-ai-data
|
||||
advisory-ai-worker:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-worker:2025.09.2
|
||||
env:
|
||||
ADVISORYAI__AdvisoryAI__SbomBaseAddress: https://stellaops-scanner-web:8444
|
||||
ADVISORYAI__AdvisoryAI__Queue__DirectoryPath: /var/lib/advisory-ai/queue
|
||||
ADVISORYAI__AdvisoryAI__Storage__PlanCacheDirectory: /var/lib/advisory-ai/plans
|
||||
ADVISORYAI__AdvisoryAI__Storage__OutputDirectory: /var/lib/advisory-ai/outputs
|
||||
ADVISORYAI__AdvisoryAI__Inference__Mode: Local
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__BaseAddress: ""
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__ApiKey: ""
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: stellaops-prod-core
|
||||
volumeMounts:
|
||||
- name: advisory-ai-data
|
||||
mountPath: /var/lib/advisory-ai
|
||||
volumeClaims:
|
||||
- name: advisory-ai-data
|
||||
claimName: stellaops-advisory-ai-data
|
||||
web-ui:
|
||||
image: registry.stella-ops.org/stellaops/web-ui@sha256:10d924808c48e4353e3a241da62eb7aefe727a1d6dc830eb23a8e181013b3a23
|
||||
service:
|
||||
port: 8443
|
||||
env:
|
||||
STELLAOPS_UI__BACKEND__BASEURL: "https://stellaops-scanner-web:8444"
|
||||
# Infrastructure services
|
||||
postgres:
|
||||
class: infrastructure
|
||||
image: docker.io/library/postgres@sha256:8e97b8526ed19304b144f7478bc9201646acf0723cdc6e4b19bc9eb34879a27e
|
||||
service:
|
||||
port: 5432
|
||||
env:
|
||||
POSTGRES_USER: stellaops
|
||||
POSTGRES_PASSWORD: stellaops
|
||||
POSTGRES_DB: stellaops
|
||||
volumeMounts:
|
||||
- name: postgres-data
|
||||
mountPath: /var/lib/postgresql/data
|
||||
volumeClaims:
|
||||
- name: postgres-data
|
||||
claimName: stellaops-postgres-data
|
||||
valkey:
|
||||
class: infrastructure
|
||||
image: docker.io/valkey/valkey:9.0.1-alpine
|
||||
service:
|
||||
port: 6379
|
||||
command:
|
||||
- valkey-server
|
||||
- --appendonly
|
||||
- "yes"
|
||||
volumeMounts:
|
||||
- name: valkey-data
|
||||
mountPath: /data
|
||||
volumeClaims:
|
||||
- name: valkey-data
|
||||
claimName: stellaops-valkey-data
|
||||
rustfs:
|
||||
class: infrastructure
|
||||
image: registry.stella-ops.org/stellaops/rustfs:2025.09.2
|
||||
service:
|
||||
port: 8080
|
||||
command:
|
||||
- serve
|
||||
- --listen
|
||||
- 0.0.0.0:8080
|
||||
- --root
|
||||
- /data
|
||||
env:
|
||||
RUSTFS__LOG__LEVEL: info
|
||||
RUSTFS__STORAGE__PATH: /data
|
||||
volumeMounts:
|
||||
- name: rustfs-data
|
||||
mountPath: /data
|
||||
volumeClaims:
|
||||
- name: rustfs-data
|
||||
claimName: stellaops-rustfs-data
|
||||
|
||||
238
deploy/helm/stellaops/values-stage.yaml
Normal file
238
deploy/helm/stellaops/values-stage.yaml
Normal file
@@ -0,0 +1,238 @@
|
||||
global:
|
||||
profile: stage
|
||||
release:
|
||||
version: "2025.09.2"
|
||||
channel: stable
|
||||
manifestSha256: "dc3c8fe1ab83941c838ccc5a8a5862f7ddfa38c2078e580b5649db26554565b7"
|
||||
image:
|
||||
pullPolicy: IfNotPresent
|
||||
labels:
|
||||
stellaops.io/channel: stable
|
||||
|
||||
telemetry:
|
||||
collector:
|
||||
enabled: true
|
||||
defaultTenant: stage
|
||||
tls:
|
||||
secretName: stellaops-otel-tls-stage
|
||||
|
||||
configMaps:
|
||||
notify-config:
|
||||
data:
|
||||
notify.yaml: |
|
||||
storage:
|
||||
driver: postgres
|
||||
connectionString: "Host=stellaops-postgres;Port=5432;Database=notify;Username=stellaops;Password=stellaops"
|
||||
commandTimeoutSeconds: 45
|
||||
|
||||
authority:
|
||||
enabled: true
|
||||
issuer: "https://authority.stage.stella-ops.org"
|
||||
metadataAddress: "https://authority.stage.stella-ops.org/.well-known/openid-configuration"
|
||||
requireHttpsMetadata: true
|
||||
allowAnonymousFallback: false
|
||||
backchannelTimeoutSeconds: 30
|
||||
tokenClockSkewSeconds: 60
|
||||
audiences:
|
||||
- notify
|
||||
readScope: notify.read
|
||||
adminScope: notify.admin
|
||||
|
||||
api:
|
||||
basePath: "/api/v1/notify"
|
||||
internalBasePath: "/internal/notify"
|
||||
tenantHeader: "X-StellaOps-Tenant"
|
||||
|
||||
plugins:
|
||||
baseDirectory: "/opt/stellaops"
|
||||
directory: "plugins/notify"
|
||||
searchPatterns:
|
||||
- "StellaOps.Notify.Connectors.*.dll"
|
||||
orderedPlugins:
|
||||
- StellaOps.Notify.Connectors.Slack
|
||||
- StellaOps.Notify.Connectors.Teams
|
||||
- StellaOps.Notify.Connectors.Email
|
||||
- StellaOps.Notify.Connectors.Webhook
|
||||
|
||||
telemetry:
|
||||
enableRequestLogging: true
|
||||
minimumLogLevel: Information
|
||||
policy-engine-activation:
|
||||
data:
|
||||
STELLAOPS_POLICY_ENGINE__ACTIVATION__FORCETWOPERSONAPPROVAL: "true"
|
||||
STELLAOPS_POLICY_ENGINE__ACTIVATION__DEFAULTREQUIRESTWOPERSONAPPROVAL: "true"
|
||||
STELLAOPS_POLICY_ENGINE__ACTIVATION__EMITAUDITLOGS: "true"
|
||||
services:
|
||||
authority:
|
||||
image: registry.stella-ops.org/stellaops/authority@sha256:b0348bad1d0b401cc3c71cb40ba034c8043b6c8874546f90d4783c9dbfcc0bf5
|
||||
service:
|
||||
port: 8440
|
||||
env:
|
||||
STELLAOPS_AUTHORITY__ISSUER: "https://stellaops-authority:8440"
|
||||
STELLAOPS_AUTHORITY__STORAGE__DRIVER: "postgres"
|
||||
STELLAOPS_AUTHORITY__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=authority;Username=stellaops;Password=stellaops"
|
||||
STELLAOPS_AUTHORITY__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: "/app/plugins"
|
||||
STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: "/app/etc/authority.plugins"
|
||||
signer:
|
||||
image: registry.stella-ops.org/stellaops/signer@sha256:8ad574e61f3a9e9bda8a58eb2700ae46813284e35a150b1137bc7c2b92ac0f2e
|
||||
service:
|
||||
port: 8441
|
||||
env:
|
||||
SIGNER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
|
||||
SIGNER__POE__INTROSPECTURL: "https://licensing.stage.stella-ops.internal/introspect"
|
||||
SIGNER__STORAGE__DRIVER: "postgres"
|
||||
SIGNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=signer;Username=stellaops;Password=stellaops"
|
||||
SIGNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
attestor:
|
||||
image: registry.stella-ops.org/stellaops/attestor@sha256:0534985f978b0b5d220d73c96fddd962cd9135f616811cbe3bff4666c5af568f
|
||||
service:
|
||||
port: 8442
|
||||
env:
|
||||
ATTESTOR__SIGNER__BASEURL: "https://stellaops-signer:8441"
|
||||
ATTESTOR__STORAGE__DRIVER: "postgres"
|
||||
ATTESTOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=attestor;Username=stellaops;Password=stellaops"
|
||||
ATTESTOR__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
concelier:
|
||||
image: registry.stella-ops.org/stellaops/concelier@sha256:c58cdcaee1d266d68d498e41110a589dd204b487d37381096bd61ab345a867c5
|
||||
service:
|
||||
port: 8445
|
||||
env:
|
||||
CONCELIER__STORAGE__DRIVER: "postgres"
|
||||
CONCELIER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=concelier;Username=stellaops;Password=stellaops"
|
||||
CONCELIER__STORAGE__S3__ENDPOINT: "http://stellaops-rustfs:8080"
|
||||
CONCELIER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
CONCELIER__AUTHORITY__BASEURL: "https://stellaops-authority:8440"
|
||||
volumeMounts:
|
||||
- name: concelier-jobs
|
||||
mountPath: /var/lib/concelier/jobs
|
||||
volumeClaims:
|
||||
- name: concelier-jobs
|
||||
claimName: stellaops-concelier-jobs
|
||||
scanner-web:
|
||||
image: registry.stella-ops.org/stellaops/scanner-web@sha256:14b23448c3f9586a9156370b3e8c1991b61907efa666ca37dd3aaed1e79fe3b7
|
||||
service:
|
||||
port: 8444
|
||||
env:
|
||||
SCANNER__STORAGE__DRIVER: "postgres"
|
||||
SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=scanner;Username=stellaops;Password=stellaops"
|
||||
SCANNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
|
||||
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
|
||||
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
|
||||
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
|
||||
SCANNER__QUEUE__BROKER: "valkey://stellaops-valkey:6379"
|
||||
SCANNER__EVENTS__ENABLED: "false"
|
||||
SCANNER__EVENTS__DRIVER: "valkey"
|
||||
SCANNER__EVENTS__DSN: "stellaops-valkey:6379"
|
||||
SCANNER__EVENTS__STREAM: "stella.events"
|
||||
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
|
||||
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
|
||||
SCANNER__OFFLINEKIT__ENABLED: "false"
|
||||
SCANNER__OFFLINEKIT__REQUIREDSSE: "true"
|
||||
SCANNER__OFFLINEKIT__REKOROFFLINEMODE: "true"
|
||||
SCANNER__OFFLINEKIT__TRUSTROOTDIRECTORY: "/etc/stellaops/trust-roots"
|
||||
SCANNER__OFFLINEKIT__REKORSNAPSHOTDIRECTORY: "/var/lib/stellaops/rekor-snapshot"
|
||||
SCANNER_SURFACE_FS_ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
|
||||
SCANNER_SURFACE_CACHE_ROOT: "/var/lib/stellaops/surface"
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER: "kubernetes"
|
||||
SCANNER_SURFACE_SECRETS_ROOT: "stellaops/scanner"
|
||||
scanner-worker:
|
||||
image: registry.stella-ops.org/stellaops/scanner-worker@sha256:32e25e76386eb9ea8bee0a1ad546775db9a2df989fab61ac877e351881960dab
|
||||
replicas: 2
|
||||
env:
|
||||
SCANNER__STORAGE__DRIVER: "postgres"
|
||||
SCANNER__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=scanner;Username=stellaops;Password=stellaops"
|
||||
SCANNER__CACHE__REDIS__CONNECTIONSTRING: "stellaops-valkey:6379"
|
||||
SCANNER__ARTIFACTSTORE__DRIVER: "rustfs"
|
||||
SCANNER__ARTIFACTSTORE__ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
|
||||
SCANNER__ARTIFACTSTORE__BUCKET: "scanner-artifacts"
|
||||
SCANNER__ARTIFACTSTORE__TIMEOUTSECONDS: "30"
|
||||
SCANNER__QUEUE__BROKER: "valkey://stellaops-valkey:6379"
|
||||
SCANNER__EVENTS__ENABLED: "false"
|
||||
SCANNER__EVENTS__DRIVER: "valkey"
|
||||
SCANNER__EVENTS__DSN: "stellaops-valkey:6379"
|
||||
SCANNER__EVENTS__STREAM: "stella.events"
|
||||
SCANNER__EVENTS__PUBLISHTIMEOUTSECONDS: "5"
|
||||
SCANNER__EVENTS__MAXSTREAMLENGTH: "10000"
|
||||
SCANNER_SURFACE_FS_ENDPOINT: "http://stellaops-rustfs:8080/api/v1"
|
||||
SCANNER_SURFACE_CACHE_ROOT: "/var/lib/stellaops/surface"
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER: "kubernetes"
|
||||
SCANNER_SURFACE_SECRETS_ROOT: "stellaops/scanner"
|
||||
notify-web:
|
||||
image: registry.stella-ops.org/stellaops/notify-web:2025.09.2
|
||||
service:
|
||||
port: 8446
|
||||
env:
|
||||
DOTNET_ENVIRONMENT: Production
|
||||
NOTIFY__QUEUE__DRIVER: "valkey"
|
||||
NOTIFY__QUEUE__VALKEY__URL: "stellaops-valkey:6379"
|
||||
configMounts:
|
||||
- name: notify-config
|
||||
mountPath: /app/etc/notify.yaml
|
||||
subPath: notify.yaml
|
||||
configMap: notify-config
|
||||
excititor:
|
||||
image: registry.stella-ops.org/stellaops/excititor@sha256:59022e2016aebcef5c856d163ae705755d3f81949d41195256e935ef40a627fa
|
||||
env:
|
||||
EXCITITOR__CONCELIER__BASEURL: "https://stellaops-concelier:8445"
|
||||
EXCITITOR__STORAGE__DRIVER: "postgres"
|
||||
EXCITITOR__STORAGE__POSTGRES__CONNECTIONSTRING: "Host=stellaops-postgres;Port=5432;Database=excititor;Username=stellaops;Password=stellaops"
|
||||
web-ui:
|
||||
image: registry.stella-ops.org/stellaops/web-ui@sha256:10d924808c48e4353e3a241da62eb7aefe727a1d6dc830eb23a8e181013b3a23
|
||||
service:
|
||||
port: 8443
|
||||
env:
|
||||
STELLAOPS_UI__BACKEND__BASEURL: "https://stellaops-scanner-web:8444"
|
||||
|
||||
# Infrastructure services
|
||||
postgres:
|
||||
class: infrastructure
|
||||
image: docker.io/library/postgres@sha256:8e97b8526ed19304b144f7478bc9201646acf0723cdc6e4b19bc9eb34879a27e
|
||||
service:
|
||||
port: 5432
|
||||
env:
|
||||
POSTGRES_USER: stellaops
|
||||
POSTGRES_PASSWORD: stellaops
|
||||
POSTGRES_DB: stellaops
|
||||
volumeMounts:
|
||||
- name: postgres-data
|
||||
mountPath: /var/lib/postgresql/data
|
||||
volumeClaims:
|
||||
- name: postgres-data
|
||||
claimName: stellaops-postgres-data
|
||||
valkey:
|
||||
class: infrastructure
|
||||
image: docker.io/valkey/valkey:9.0.1-alpine
|
||||
service:
|
||||
port: 6379
|
||||
command:
|
||||
- valkey-server
|
||||
- --appendonly
|
||||
- "yes"
|
||||
volumeMounts:
|
||||
- name: valkey-data
|
||||
mountPath: /data
|
||||
volumeClaims:
|
||||
- name: valkey-data
|
||||
claimName: stellaops-valkey-data
|
||||
rustfs:
|
||||
class: infrastructure
|
||||
image: registry.stella-ops.org/stellaops/rustfs:2025.09.2
|
||||
service:
|
||||
port: 8080
|
||||
command:
|
||||
- serve
|
||||
- --listen
|
||||
- 0.0.0.0:8080
|
||||
- --root
|
||||
- /data
|
||||
env:
|
||||
RUSTFS__LOG__LEVEL: info
|
||||
RUSTFS__STORAGE__PATH: /data
|
||||
volumeMounts:
|
||||
- name: rustfs-data
|
||||
mountPath: /data
|
||||
volumeClaims:
|
||||
- name: rustfs-data
|
||||
claimName: stellaops-rustfs-data
|
||||
281
deploy/helm/stellaops/values.yaml
Normal file
281
deploy/helm/stellaops/values.yaml
Normal file
@@ -0,0 +1,281 @@
|
||||
global:
|
||||
release:
|
||||
version: ""
|
||||
channel: ""
|
||||
manifestSha256: ""
|
||||
profile: ""
|
||||
image:
|
||||
pullPolicy: IfNotPresent
|
||||
labels: {}
|
||||
|
||||
migrations:
|
||||
enabled: false
|
||||
jobs: []
|
||||
|
||||
networkPolicy:
|
||||
enabled: false
|
||||
ingressPort: 80
|
||||
egressPort: 443
|
||||
ingressNamespaces: {}
|
||||
ingressPods: {}
|
||||
egressNamespaces: {}
|
||||
egressPods: {}
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
className: nginx
|
||||
annotations: {}
|
||||
hosts: []
|
||||
tls: []
|
||||
|
||||
externalSecrets:
|
||||
enabled: false
|
||||
secrets: []
|
||||
|
||||
prometheus:
|
||||
enabled: false
|
||||
path: /metrics
|
||||
port: 8080
|
||||
scheme: http
|
||||
|
||||
hpa:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
maxReplicas: 3
|
||||
cpu:
|
||||
targetPercentage: 75
|
||||
memory:
|
||||
targetPercentage: null
|
||||
|
||||
# Surface.Env configuration for Scanner/Zastava components
|
||||
# See docs/modules/scanner/design/surface-env.md for details
|
||||
surface:
|
||||
# Surface.FS storage configuration
|
||||
fs:
|
||||
# Base URI for Surface.FS / RustFS / S3-compatible store (required)
|
||||
endpoint: ""
|
||||
# Bucket/container for manifests and artefacts
|
||||
bucket: "surface-cache"
|
||||
# Optional region for S3-compatible stores (AWS/GCS)
|
||||
region: ""
|
||||
# Local cache configuration
|
||||
cache:
|
||||
# Local directory for warm caches
|
||||
root: "/var/lib/stellaops/surface"
|
||||
# Soft limit for on-disk cache usage in MB (64-262144)
|
||||
quotaMb: 4096
|
||||
# Enable manifest prefetch threads
|
||||
prefetchEnabled: false
|
||||
# Tenant configuration
|
||||
tenant: "default"
|
||||
# Comma-separated feature switches
|
||||
features: ""
|
||||
# TLS configuration for client authentication
|
||||
tls:
|
||||
# Path to PEM/PKCS#12 certificate file
|
||||
certPath: ""
|
||||
# Optional private key path when cert/key stored separately
|
||||
keyPath: ""
|
||||
# Secret name containing TLS cert/key
|
||||
secretName: ""
|
||||
# Secrets provider configuration
|
||||
secrets:
|
||||
# Provider ID: kubernetes, file, inline
|
||||
provider: "kubernetes"
|
||||
# Kubernetes namespace for secrets provider
|
||||
namespace: ""
|
||||
# Path or base for file provider
|
||||
root: ""
|
||||
# Optional fallback provider ID
|
||||
fallbackProvider: ""
|
||||
# Allow inline secrets (disable in production)
|
||||
allowInline: false
|
||||
|
||||
telemetry:
|
||||
collector:
|
||||
enabled: false
|
||||
replicas: 1
|
||||
image: otel/opentelemetry-collector:0.105.0
|
||||
requireClientCert: true
|
||||
defaultTenant: unknown
|
||||
logLevel: info
|
||||
tls:
|
||||
secretName: ""
|
||||
certPath: /etc/otel/tls/tls.crt
|
||||
keyPath: /etc/otel/tls/tls.key
|
||||
caPath: /etc/otel/tls/ca.crt
|
||||
items:
|
||||
- key: tls.crt
|
||||
path: tls.crt
|
||||
- key: tls.key
|
||||
path: tls.key
|
||||
- key: ca.crt
|
||||
path: ca.crt
|
||||
service:
|
||||
grpcPort: 4317
|
||||
httpPort: 4318
|
||||
metricsPort: 9464
|
||||
resources: {}
|
||||
|
||||
configMaps:
|
||||
# Surface.Env environment variables for Scanner/Zastava components
|
||||
surface-env:
|
||||
data:
|
||||
SCANNER_SURFACE_FS_ENDPOINT: "{{ .Values.surface.fs.endpoint }}"
|
||||
SCANNER_SURFACE_FS_BUCKET: "{{ .Values.surface.fs.bucket }}"
|
||||
SCANNER_SURFACE_FS_REGION: "{{ .Values.surface.fs.region }}"
|
||||
SCANNER_SURFACE_CACHE_ROOT: "{{ .Values.surface.cache.root }}"
|
||||
SCANNER_SURFACE_CACHE_QUOTA_MB: "{{ .Values.surface.cache.quotaMb }}"
|
||||
SCANNER_SURFACE_PREFETCH_ENABLED: "{{ .Values.surface.cache.prefetchEnabled }}"
|
||||
SCANNER_SURFACE_TENANT: "{{ .Values.surface.tenant }}"
|
||||
SCANNER_SURFACE_FEATURES: "{{ .Values.surface.features }}"
|
||||
SCANNER_SURFACE_TLS_CERT_PATH: "{{ .Values.surface.tls.certPath }}"
|
||||
SCANNER_SURFACE_TLS_KEY_PATH: "{{ .Values.surface.tls.keyPath }}"
|
||||
SCANNER_SURFACE_SECRETS_PROVIDER: "{{ .Values.surface.secrets.provider }}"
|
||||
SCANNER_SURFACE_SECRETS_NAMESPACE: "{{ .Values.surface.secrets.namespace }}"
|
||||
SCANNER_SURFACE_SECRETS_ROOT: "{{ .Values.surface.secrets.root }}"
|
||||
SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER: "{{ .Values.surface.secrets.fallbackProvider }}"
|
||||
SCANNER_SURFACE_SECRETS_ALLOW_INLINE: "{{ .Values.surface.secrets.allowInline }}"
|
||||
# Zastava consumers inherit Scanner defaults but can be overridden via ZASTAVA_* envs
|
||||
ZASTAVA_SURFACE_FS_ENDPOINT: "{{ .Values.surface.fs.endpoint }}"
|
||||
ZASTAVA_SURFACE_FS_BUCKET: "{{ .Values.surface.fs.bucket }}"
|
||||
ZASTAVA_SURFACE_FS_REGION: "{{ .Values.surface.fs.region }}"
|
||||
ZASTAVA_SURFACE_CACHE_ROOT: "{{ .Values.surface.cache.root }}"
|
||||
ZASTAVA_SURFACE_CACHE_QUOTA_MB: "{{ .Values.surface.cache.quotaMb }}"
|
||||
ZASTAVA_SURFACE_PREFETCH_ENABLED: "{{ .Values.surface.cache.prefetchEnabled }}"
|
||||
ZASTAVA_SURFACE_TENANT: "{{ .Values.surface.tenant }}"
|
||||
ZASTAVA_SURFACE_FEATURES: "{{ .Values.surface.features }}"
|
||||
ZASTAVA_SURFACE_TLS_CERT_PATH: "{{ .Values.surface.tls.certPath }}"
|
||||
ZASTAVA_SURFACE_TLS_KEY_PATH: "{{ .Values.surface.tls.keyPath }}"
|
||||
ZASTAVA_SURFACE_SECRETS_PROVIDER: "{{ .Values.surface.secrets.provider }}"
|
||||
ZASTAVA_SURFACE_SECRETS_NAMESPACE: "{{ .Values.surface.secrets.namespace }}"
|
||||
ZASTAVA_SURFACE_SECRETS_ROOT: "{{ .Values.surface.secrets.root }}"
|
||||
ZASTAVA_SURFACE_SECRETS_FALLBACK_PROVIDER: "{{ .Values.surface.secrets.fallbackProvider }}"
|
||||
ZASTAVA_SURFACE_SECRETS_ALLOW_INLINE: "{{ .Values.surface.secrets.allowInline }}"
|
||||
|
||||
issuer-directory-config:
|
||||
data:
|
||||
issuer-directory.yaml: |
|
||||
IssuerDirectory:
|
||||
telemetry:
|
||||
minimumLogLevel: Information
|
||||
authority:
|
||||
enabled: true
|
||||
issuer: https://authority.svc.cluster.local/realms/stellaops
|
||||
requireHttpsMetadata: true
|
||||
audiences:
|
||||
- stellaops-platform
|
||||
readScope: issuer-directory:read
|
||||
writeScope: issuer-directory:write
|
||||
adminScope: issuer-directory:admin
|
||||
tenantHeader: X-StellaOps-Tenant
|
||||
seedCsafPublishers: true
|
||||
csafSeedPath: data/csaf-publishers.json
|
||||
Storage:
|
||||
Driver: postgres
|
||||
Postgres:
|
||||
ConnectionString: Host=postgres;Port=5432;Database=issuer_directory;Username=stellaops;Password=stellaops
|
||||
|
||||
policy-engine-activation:
|
||||
data:
|
||||
STELLAOPS_POLICY_ENGINE__ACTIVATION__FORCETWOPERSONAPPROVAL: "false"
|
||||
STELLAOPS_POLICY_ENGINE__ACTIVATION__DEFAULTREQUIRESTWOPERSONAPPROVAL: "false"
|
||||
STELLAOPS_POLICY_ENGINE__ACTIVATION__EMITAUDITLOGS: "true"
|
||||
|
||||
services:
|
||||
issuer-directory:
|
||||
image: registry.stella-ops.org/stellaops/issuer-directory-web:2025.10.0-edge
|
||||
replicas: 1
|
||||
configMounts:
|
||||
- name: issuer-directory-config
|
||||
configMap: issuer-directory-config
|
||||
mountPath: /etc/issuer-directory.yaml
|
||||
subPath: issuer-directory.yaml
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: issuer-directory-secrets
|
||||
env:
|
||||
ISSUERDIRECTORY__CONFIG: /etc/issuer-directory.yaml
|
||||
ISSUERDIRECTORY__AUTHORITY__BASEURL: https://authority:8440
|
||||
ISSUERDIRECTORY__SEEDCSAFPUBLISHERS: "true"
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
service:
|
||||
port: 8080
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health/live
|
||||
port: 8080
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health/live
|
||||
port: 8080
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 20
|
||||
scheduler-worker:
|
||||
image: registry.stella-ops.org/stellaops/scheduler-worker:2025.10.0-edge
|
||||
replicas: 1
|
||||
command:
|
||||
- dotnet
|
||||
- StellaOps.Scheduler.Worker.Host.dll
|
||||
env:
|
||||
SCHEDULER__QUEUE__KIND: Valkey
|
||||
SCHEDULER__QUEUE__VALKEY__URL: valkey:6379
|
||||
SCHEDULER__STORAGE__DRIVER: postgres
|
||||
SCHEDULER__STORAGE__POSTGRES__CONNECTIONSTRING: Host=postgres;Port=5432;Database=scheduler;Username=stellaops;Password=stellaops
|
||||
SCHEDULER__WORKER__RUNNER__SCANNER__BASEADDRESS: http://scanner-web:8444
|
||||
advisory-ai-web:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-web:2025.10.0-edge
|
||||
service:
|
||||
port: 8448
|
||||
env:
|
||||
ADVISORYAI__AdvisoryAI__SbomBaseAddress: http://scanner-web:8444
|
||||
ADVISORYAI__AdvisoryAI__Queue__DirectoryPath: /var/lib/advisory-ai/queue
|
||||
ADVISORYAI__AdvisoryAI__Storage__PlanCacheDirectory: /var/lib/advisory-ai/plans
|
||||
ADVISORYAI__AdvisoryAI__Storage__OutputDirectory: /var/lib/advisory-ai/outputs
|
||||
ADVISORYAI__AdvisoryAI__Inference__Mode: Local
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__BaseAddress: ""
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__ApiKey: ""
|
||||
volumeMounts:
|
||||
- name: advisory-ai-data
|
||||
mountPath: /var/lib/advisory-ai
|
||||
volumeClaims:
|
||||
- name: advisory-ai-data
|
||||
claimName: stellaops-advisory-ai-data
|
||||
advisory-ai-worker:
|
||||
image: registry.stella-ops.org/stellaops/advisory-ai-worker:2025.10.0-edge
|
||||
env:
|
||||
ADVISORYAI__AdvisoryAI__SbomBaseAddress: http://scanner-web:8444
|
||||
ADVISORYAI__AdvisoryAI__Queue__DirectoryPath: /var/lib/advisory-ai/queue
|
||||
ADVISORYAI__AdvisoryAI__Storage__PlanCacheDirectory: /var/lib/advisory-ai/plans
|
||||
ADVISORYAI__AdvisoryAI__Storage__OutputDirectory: /var/lib/advisory-ai/outputs
|
||||
ADVISORYAI__AdvisoryAI__Inference__Mode: Local
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__BaseAddress: ""
|
||||
ADVISORYAI__AdvisoryAI__Inference__Remote__ApiKey: ""
|
||||
volumeMounts:
|
||||
- name: advisory-ai-data
|
||||
mountPath: /var/lib/advisory-ai
|
||||
volumeClaims:
|
||||
- name: advisory-ai-data
|
||||
claimName: stellaops-advisory-ai-data
|
||||
|
||||
mock:
|
||||
enabled: false
|
||||
orchestrator:
|
||||
image: registry.stella-ops.org/stellaops/orchestrator@sha256:97f12856ce870bafd3328bda86833bcccbf56d255941d804966b5557f6610119
|
||||
policyRegistry:
|
||||
image: registry.stella-ops.org/stellaops/policy-registry@sha256:c6cad8055e9827ebcbebb6ad4d6866dce4b83a0a49b0a8a6500b736a5cb26fa7
|
||||
packsRegistry:
|
||||
image: registry.stella-ops.org/stellaops/packs-registry@sha256:1f5e9416c4dc608594ad6fad87c24d72134427f899c192b494e22b268499c791
|
||||
taskRunner:
|
||||
image: registry.stella-ops.org/stellaops/task-runner@sha256:eb5ad992b49a41554f41516be1a6afcfa6522faf2111c08ff2b3664ad2fc954b
|
||||
vexLens:
|
||||
image: registry.stella-ops.org/stellaops/vex-lens@sha256:b44e63ecfeebc345a70c073c1ce5ace709c58be0ffaad0e2862758aeee3092fb
|
||||
issuerDirectory:
|
||||
image: registry.stella-ops.org/stellaops/issuer-directory@sha256:67e8ef02c97d3156741e857756994888f30c373ace8e84886762edba9dc51914
|
||||
findingsLedger:
|
||||
image: registry.stella-ops.org/stellaops/findings-ledger@sha256:71d4c361ba8b2f8b69d652597bc3f2efc8a64f93fab854ce25272a88506df49c
|
||||
vulnExplorerApi:
|
||||
image: registry.stella-ops.org/stellaops/vuln-explorer-api@sha256:7fc7e43a05cbeb0106ce7d4d634612e83de6fdc119aaab754a71c1d60b82841d
|
||||
22
deploy/offline/airgap/README.md
Normal file
22
deploy/offline/airgap/README.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Air-gap Egress Guard Rails
|
||||
|
||||
Artifacts supporting `DEVOPS-AIRGAP-56-001`:
|
||||
|
||||
- `k8s-deny-egress.yaml` — NetworkPolicy template that denies all egress for pods labeled `sealed=true`, except optional in-cluster DNS when enabled.
|
||||
- `compose-egress-guard.sh` — Idempotent iptables guard for Docker/compose using the `DOCKER-USER` chain to drop all outbound traffic from a compose project network while allowing loopback and RFC1918 intra-cluster ranges.
|
||||
- `verify-egress-block.sh` — Verification harness that runs curl probes from Docker or Kubernetes and reports JSON results; exits non-zero if any target is reachable.
|
||||
- `bundle_stage_import.py` — Deterministic bundle staging helper: validates sha256 manifest, copies bundles to staging dir as `<sha256>-<basename>`, emits `staging-report.json` for evidence.
|
||||
- `stage-bundle.sh` — Thin wrapper around `bundle_stage_import.py` with positional args.
|
||||
- `build_bootstrap_pack.py` — Builds a Bootstrap Pack from images/charts/extras listed in a JSON config, writing `bootstrap-manifest.json` + `checksums.sha256` deterministically.
|
||||
- `build_bootstrap_pack.sh` — Wrapper for the bootstrap pack builder.
|
||||
- `build_mirror_bundle.py` — Generates mirror bundle manifest + checksums with dual-control approvals; optional cosign signing. Outputs `mirror-bundle-manifest.json`, `checksums.sha256`, and optional signature/cert.
|
||||
- `compose-syslog-smtp.yaml` — Local SMTP (MailHog) + syslog-ng stack for sealed environments.
|
||||
- `health_syslog_smtp.sh` — Brings up the syslog/SMTP stack via docker compose and performs health checks (MailHog API + syslog logger).
|
||||
- `compose-observability.yaml` — Sealed-mode observability stack (Prometheus, Grafana, Tempo, Loki) with offline configs and healthchecks.
|
||||
- `health_observability.sh` — Starts the observability stack and probes Prometheus/Grafana/Tempo/Loki readiness.
|
||||
- `compose-syslog-smtp.yaml` + `syslog-ng.conf` — Local SMTP + syslog stack for sealed-mode notifications; run via `scripts/devops/run-smtp-syslog.sh` (health check `health_syslog_smtp.sh`).
|
||||
- `observability-offline-compose.yml` + `otel-offline.yaml` + `promtail-config.yaml` — Sealed-mode observability stack (Loki, Promtail, OTEL collector with file exporters) to satisfy DEVOPS-AIRGAP-58-002.
|
||||
- `compose-syslog-smtp.yaml` — Local SMTP (MailHog) + syslog-ng stack for sealed environments.
|
||||
- `health_syslog_smtp.sh` — Brings up the syslog/SMTP stack via docker compose and performs health checks (MailHog API + syslog logger).
|
||||
|
||||
See also `ops/devops/sealed-mode-ci/` for the full sealed-mode compose harness and `egress_probe.py`, which this verification script wraps.
|
||||
174
deploy/offline/airgap/build_bootstrap_pack.py
Normal file
174
deploy/offline/airgap/build_bootstrap_pack.py
Normal file
@@ -0,0 +1,174 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build a deterministic Bootstrap Pack bundle for sealed/offline transfer.
|
||||
|
||||
- Reads a JSON config listing artefacts to include (images, Helm charts, extras).
|
||||
- Copies artefacts into an output directory with preserved basenames.
|
||||
- Generates `bootstrap-manifest.json` and `checksums.sha256` with sha256 hashes
|
||||
and sizes for evidence/verification.
|
||||
- Intended to satisfy DEVOPS-AIRGAP-56-003.
|
||||
|
||||
Config schema (JSON):
|
||||
{
|
||||
"name": "bootstrap-pack",
|
||||
"images": ["release/containers/taskrunner.tar", "release/containers/orchestrator.tar"],
|
||||
"charts": ["deploy/helm/stella.tgz"],
|
||||
"extras": ["docs/24_OFFLINE_KIT.md"]
|
||||
}
|
||||
|
||||
Usage:
|
||||
build_bootstrap_pack.py --config bootstrap.json --output out/bootstrap-pack
|
||||
build_bootstrap_pack.py --self-test
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Tuple
|
||||
|
||||
DEFAULT_NAME = "bootstrap-pack"
|
||||
|
||||
|
||||
def sha256_file(path: Path) -> Tuple[str, int]:
|
||||
h = hashlib.sha256()
|
||||
size = 0
|
||||
with path.open("rb") as f:
|
||||
for chunk in iter(lambda: f.read(1024 * 1024), b""):
|
||||
h.update(chunk)
|
||||
size += len(chunk)
|
||||
return h.hexdigest(), size
|
||||
|
||||
|
||||
def load_config(path: Path) -> Dict:
|
||||
with path.open("r", encoding="utf-8") as handle:
|
||||
cfg = json.load(handle)
|
||||
if not isinstance(cfg, dict):
|
||||
raise ValueError("config must be a JSON object")
|
||||
return cfg
|
||||
|
||||
|
||||
def ensure_list(cfg: Dict, key: str) -> List[str]:
|
||||
value = cfg.get(key, [])
|
||||
if value is None:
|
||||
return []
|
||||
if not isinstance(value, list):
|
||||
raise ValueError(f"config.{key} must be a list")
|
||||
return [str(x) for x in value]
|
||||
|
||||
|
||||
def copy_item(src: Path, dest_root: Path, rel_dir: str) -> Tuple[str, str, int]:
|
||||
dest_dir = dest_root / rel_dir
|
||||
dest_dir.mkdir(parents=True, exist_ok=True)
|
||||
dest_path = dest_dir / src.name
|
||||
shutil.copy2(src, dest_path)
|
||||
digest, size = sha256_file(dest_path)
|
||||
rel_path = dest_path.relative_to(dest_root).as_posix()
|
||||
return rel_path, digest, size
|
||||
|
||||
|
||||
def build_pack(config_path: Path, output_dir: Path) -> Dict:
|
||||
cfg = load_config(config_path)
|
||||
name = cfg.get("name", DEFAULT_NAME)
|
||||
images = ensure_list(cfg, "images")
|
||||
charts = ensure_list(cfg, "charts")
|
||||
extras = ensure_list(cfg, "extras")
|
||||
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
items = []
|
||||
|
||||
def process_list(paths: List[str], kind: str, rel_dir: str):
|
||||
for raw in sorted(paths):
|
||||
src = Path(raw).expanduser().resolve()
|
||||
if not src.exists():
|
||||
items.append({
|
||||
"type": kind,
|
||||
"source": raw,
|
||||
"status": "missing"
|
||||
})
|
||||
continue
|
||||
rel_path, digest, size = copy_item(src, output_dir, rel_dir)
|
||||
items.append({
|
||||
"type": kind,
|
||||
"source": raw,
|
||||
"path": rel_path,
|
||||
"sha256": digest,
|
||||
"size": size,
|
||||
"status": "ok",
|
||||
})
|
||||
|
||||
process_list(images, "image", "images")
|
||||
process_list(charts, "chart", "charts")
|
||||
process_list(extras, "extra", "extras")
|
||||
|
||||
manifest = {
|
||||
"name": name,
|
||||
"created": datetime.now(timezone.utc).isoformat(),
|
||||
"items": items,
|
||||
}
|
||||
|
||||
# checksums file (only for ok items)
|
||||
checksum_lines = [f"{item['sha256']} {item['path']}" for item in items if item.get("status") == "ok"]
|
||||
(output_dir / "checksums.sha256").write_text("\n".join(checksum_lines) + ("\n" if checksum_lines else ""), encoding="utf-8")
|
||||
(output_dir / "bootstrap-manifest.json").write_text(json.dumps(manifest, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||
return manifest
|
||||
|
||||
|
||||
def parse_args(argv: List[str]) -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--config", type=Path, help="Path to bootstrap pack config JSON")
|
||||
parser.add_argument("--output", type=Path, help="Output directory for the pack")
|
||||
parser.add_argument("--self-test", action="store_true", help="Run internal self-test and exit")
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
def self_test() -> int:
|
||||
import tempfile
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
tmpdir = Path(tmp)
|
||||
files = []
|
||||
for name, content in [("img1.tar", b"image-one"), ("chart1.tgz", b"chart-one"), ("readme.txt", b"hello")]:
|
||||
p = tmpdir / name
|
||||
p.write_bytes(content)
|
||||
files.append(p)
|
||||
cfg = {
|
||||
"images": [str(files[0])],
|
||||
"charts": [str(files[1])],
|
||||
"extras": [str(files[2])],
|
||||
}
|
||||
cfg_path = tmpdir / "bootstrap.json"
|
||||
cfg_path.write_text(json.dumps(cfg), encoding="utf-8")
|
||||
outdir = tmpdir / "out"
|
||||
manifest = build_pack(cfg_path, outdir)
|
||||
assert all(item.get("status") == "ok" for item in manifest["items"]), manifest
|
||||
for rel in ["images/img1.tar", "charts/chart1.tgz", "extras/readme.txt", "checksums.sha256", "bootstrap-manifest.json"]:
|
||||
assert (outdir / rel).exists(), f"missing {rel}"
|
||||
print("self-test passed")
|
||||
return 0
|
||||
|
||||
|
||||
def main(argv: List[str]) -> int:
|
||||
args = parse_args(argv)
|
||||
if args.self_test:
|
||||
return self_test()
|
||||
if not (args.config and args.output):
|
||||
print("--config and --output are required unless --self-test", file=sys.stderr)
|
||||
return 2
|
||||
manifest = build_pack(args.config, args.output)
|
||||
missing = [i for i in manifest["items"] if i.get("status") == "missing"]
|
||||
if missing:
|
||||
print("Pack built with missing items:")
|
||||
for item in missing:
|
||||
print(f" - {item['source']}")
|
||||
return 1
|
||||
print(f"Bootstrap pack written to {args.output}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user