Deploy the complete Judge software supply chain security platform on AWS using Kubernetes and Helm.
Judge is a comprehensive software supply chain security platform that provides:
- Artifact Attestation: Cryptographically signed build provenance
- Policy Enforcement: Automated compliance verification for CI/CD pipelines
- Vulnerability Management: Supply chain risk assessment and reporting
- Audit & Compliance: Complete visibility into your software supply chain
These Helm charts are designed for AWS Marketplace customers deploying Judge on Amazon EKS. The charts provide production-ready configurations with smart defaults that work out-of-the-box with AWS services.
Prerequisites:
- Active AWS Marketplace subscription for Judge
- Amazon EKS cluster (Kubernetes 1.24+)
- Helm 3.8+
- AWS CLI configured with appropriate permissions
- Istio 1.18+ installed on your cluster
Create a values.yaml file with your AWS-specific settings:
# values.yaml - Minimal configuration for AWS Marketplace deployment
global:
domain: example.com
aws:
enabled: true
accountId: "YOUR_AWS_ACCOUNT_ID"
region: "us-east-1"
prefix: "prod-judge"
irsa:
enabled: true
registry:
marketplace:
enabled: true
secrets:
provider: "vault"
vault:
server: "https://vault.example.com"
env: "prod"
project: "mycompany-judge"
istio:
enabled: true
tlsSecretName: wildcard-tls
hosts:
web: "judge"
api: "api"
login: "login"See Global Configuration Reference for detailed explanation of all fields.
# Clone the repository
git clone https://github.com/testifysec/judge-helm-charts.git
cd judge-helm-charts
# Build Helm dependencies
make deps
# Install Judge platform
helm install judge-platform charts/judge \
--namespace judge \
--create-namespace \
--values values.yaml
# Verify deployment
kubectl get pods -n judge
kubectl get virtualservices -n judgeAfter deployment, services are accessible via Istio ingress:
# Get ingress gateway external IP
kubectl get svc -n istio-system istio-ingressgateway
# Access services (replace with your domain)
https://judge.example.com # Web UI
https://api.example.com # API
https://login.example.com # AuthenticationAll Judge platform settings are configured through global values. These provide smart defaults and can be overridden as needed.
global:
# Base domain for all external service URLs
# Used for Istio VirtualServices, OIDC redirects, browser URLs
domain: example.comNote: Internal service-to-service communication uses Kubernetes DNS (.svc.cluster.local) automatically. For hostname customization, see Istio Service Mesh Configuration.
global:
aws:
enabled: true # Enable AWS integrations
accountId: "123456789012" # Your 12-digit AWS account ID
region: "us-east-1" # AWS region for all resources
prefix: "prod-judge" # Resource naming prefix
# IAM Roles for Service Accounts (IRSA)
irsa:
enabled: true # Adds IAM role annotations to ServiceAccountsThe prefix field is used to construct all AWS resource names. This MUST match your EKS cluster name to avoid conflicts.
Pattern: {prefix}-{service}
| Service | Resource Type | Name Example |
|---|---|---|
| Judge API | S3 Bucket | prod-judge-judge |
| Judge API | IAM Role | prod-judge-judge-api |
| Archivista | S3 Bucket | prod-judge-archivista |
| Archivista | IAM Role | prod-judge-archivista |
| Messaging | SNS Topic | prod-judge-archivista-attestations |
| Messaging | SQS Queue | prod-judge-archivista-attestations |
Examples:
prefix: "demo-judge"→ Cluster:demo-judge, S3:demo-judge-judge, IAM:demo-judge-judge-apiprefix: "prod-judge"→ Cluster:prod-judge, S3:prod-judge-judge, IAM:prod-judge-judge-api
global:
versions:
platform: "v1.15.0" # Default version for all Judge services
# Individual service overrides (optional)
api: "" # Leave empty to use platform version
archivista: ""
gateway: ""
web: ""
# Supporting services (specify explicit versions)
dex: "v2.43.1" # OIDC provider
fulcio: "v1.4.5" # Code signing CA
tsa: "v1.6.0" # Timestamping authority
kratos: "v1.0.0" # Identity service
kratosUI: "v1.6.0" # Login UIBehavior:
- Services use
versions.platformby default - Override individual services only when needed (e.g., testing new API version)
- Supporting services must specify explicit versions
global:
istio:
enabled: true
tlsSecretName: wildcard-tls # Kubernetes secret with TLS certificate
# Ingress gateway selector (must match Istio gateway pod labels)
ingressGatewaySelector:
istio: ingress
# Hostname customization (subdomain prefixes only)
# Combined with global.domain to form full URLs
hosts:
web: "judge" # Results in: judge.{global.domain}
api: "api" # Results in: api.{global.domain}
gateway: "gateway" # Results in: gateway.{global.domain}
login: "login" # Results in: login.{global.domain}
kratos: "kratos" # Results in: kratos.{global.domain}
dex: "dex" # Results in: dex.{global.domain}
fulcio: "fulcio" # Results in: fulcio.{global.domain}
tsa: "tsa" # Results in: tsa.{global.domain}Customization Example:
global:
domain: mycompany.com
istio:
hosts:
web: "supply-chain" # supply-chain.mycompany.com
api: "supply-chain-api" # supply-chain-api.mycompany.comglobal:
mode: aws # Options: aws | dev
# Use mode: dev for local testing without AWS dependencies
# Deploys LocalStack (S3 emulation) + PostgreSQL in-clusterMode Comparison:
| Feature | aws Mode |
dev Mode |
|---|---|---|
| Database | AWS RDS | In-cluster PostgreSQL |
| Storage | AWS S3 | LocalStack S3 emulation |
| Messaging | AWS SNS/SQS | LocalStack SNS/SQS |
| Secrets | Vault + ESO | Kubernetes Secrets |
| IRSA | Enabled | Disabled |
| Use Case | Production AWS deployment | Local development/testing |
Before deploying Judge, provision these AWS resources (typically via Terraform):
Requirements:
- PostgreSQL 13+
- Three separate databases on one instance:
CREATE DATABASE judge_api; CREATE DATABASE archivista; CREATE DATABASE kratos;
- SSL/TLS enabled (
sslmode=require)
Configuration:
# Connection strings stored in Vault
# Path: {env}/kubernetes/rds/{project}
# Keys:
# - judge_api_dsn: postgres://user:pass@endpoint:5432/judge_api?sslmode=require
# - archivista_dsn: postgres://user:pass@endpoint:5432/archivista?sslmode=require
# - kratos_dsn: postgres://user:pass@endpoint:5432/kratos?sslmode=requireBuckets (named using {prefix}-{service} pattern):
{prefix}-judge- Judge API artifact storage{prefix}-archivista- Attestation storage
Configuration:
global:
aws:
prefix: "prod-judge" # Creates: prod-judge-judge, prod-judge-archivista
s3:
enabled: true
judge:
bucket: "prod-judge-judge"
region: "us-east-1"
archivista:
bucket: "prod-judge-archivista"
region: "us-east-1"Resources (named using {prefix}-{service} pattern):
- SNS Topic:
{prefix}-archivista-attestations - SQS Queue:
{prefix}-archivista-attestations
Configuration:
global:
aws:
messaging:
enabled: true
snsTopic: "prod-judge-archivista-attestations"
sqsQueue: "prod-judge-archivista-attestations"
region: "us-east-1"Roles (named using {prefix}-{service} pattern):
{prefix}-judge-api- S3 read/write for judge bucket{prefix}-archivista- S3 read/write for archivista bucket, SNS/SQS publish/subscribe{prefix}-kratos- (optional) additional permissions if needed
Trust Policy (Kubernetes ServiceAccount → IAM Role):
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::{account-id}:oidc-provider/{oidc-provider}"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"{oidc-provider}:sub": "system:serviceaccount:judge:{release-name}-judge-api"
}
}
}]
}IAM Permissions Example (Judge API):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::prod-judge-judge/*"
},
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::prod-judge-judge"
}
]
}Judge creates Kubernetes ServiceAccounts using this pattern: {release-name}-{service}
Examples:
-
Helm release:
judge-platform- Judge API ServiceAccount:
judge-platform-judge-api - Archivista ServiceAccount:
judge-platform-judge-archivista - Kratos ServiceAccount:
judge-platform-judge-kratos
- Judge API ServiceAccount:
-
Helm release:
prod-judge- Judge API ServiceAccount:
prod-judge-judge-api - Archivista ServiceAccount:
prod-judge-judge-archivista
- Judge API ServiceAccount:
IRSA Annotation (added automatically when global.aws.irsa.enabled: true):
apiVersion: v1
kind: ServiceAccount
metadata:
name: judge-platform-judge-api
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/prod-judge-judge-apiJudge uses External Secrets Operator (ESO) + HashiCorp Vault for secure, dynamic secret management.
global:
secrets:
provider: "vault" # Secret backend (vault is default)
vault:
server: "https://vault.example.com" # Vault server URL
path: "secret" # KV v2 mount path (default: secret)
version: "v2" # KV version (v1 or v2)
authMountPath: "kubernetes" # Kubernetes auth backend mount
env: "prod" # Environment: dev, staging, prod
project: "mycompany-judge" # Project identifier
# ServiceAccount names for Vault Kubernetes authentication
# These MUST match the actual Kubernetes ServiceAccount names
# Default: Leave empty to auto-compute from release name
serviceAccounts:
judgeApi: "" # Auto: {release-name}-judge-api
archivista: "" # Auto: {release-name}-judge-archivista
kratos: "" # Auto: {release-name}-judge-kratosSecrets are organized using this pattern: {env}/kubernetes/{type}/{project}
Examples:
- Production database:
prod/kubernetes/rds/mycompany-judge - Staging application:
staging/kubernetes/app/mycompany-judge - Development database:
dev/kubernetes/rds/mycompany-judge
Vault Path: {env}/kubernetes/rds/{project}
Keys:
judge_api_dsn: "postgres://user:password@endpoint:5432/judge_api?sslmode=require"
archivista_dsn: "postgres://user:password@endpoint:5432/archivista?sslmode=require"
kratos_dsn: "postgres://user:password@endpoint:5432/kratos?sslmode=require"Vault CLI Example:
vault kv put secret/prod/kubernetes/rds/mycompany-judge \
judge_api_dsn="postgres://judgeuser:[email protected]:5432/judge_api?sslmode=require" \
archivista_dsn="postgres://archivistauser:[email protected]:5432/archivista?sslmode=require" \
kratos_dsn="postgres://kratosuser:[email protected]:5432/kratos?sslmode=require"Vault Path: {env}/kubernetes/app/{project}
Keys:
kratos_secrets_cookie: "32-byte-random-string-base64" # Cookie encryption key
kratos_secrets_cipher: "32-byte-random-string-base64" # Data encryption key
oidc_github_client_id: "your-github-oauth-app-id" # GitHub OAuth client ID
oidc_github_client_secret: "your-github-oauth-secret" # GitHub OAuth secretGenerate Secrets:
# Generate random encryption keys
COOKIE_SECRET=$(openssl rand -base64 32)
CIPHER_SECRET=$(openssl rand -base64 32)
# Store in Vault
vault kv put secret/prod/kubernetes/app/mycompany-judge \
kratos_secrets_cookie="$COOKIE_SECRET" \
kratos_secrets_cipher="$CIPHER_SECRET" \
oidc_github_client_id="Iv1.your-github-client-id" \
oidc_github_client_secret="ghp_your-github-client-secret"Judge requires Vault Kubernetes auth roles for each service. These roles bind ServiceAccounts to Vault policies.
Prerequisites:
- Vault Kubernetes auth method enabled
- Kubernetes cluster configured in Vault
- Vault policies created for each service
Example Terraform Configuration:
# Vault Kubernetes auth role for Judge API
resource "vault_kubernetes_auth_backend_role" "judge_api" {
backend = "kubernetes"
role_name = "judge-api"
bound_service_account_names = ["judge-platform-judge-api"]
bound_service_account_namespaces = ["judge"]
token_ttl = 3600
token_policies = ["judge-api-policy"]
}
# Vault policy for Judge API
resource "vault_policy" "judge_api" {
name = "judge-api-policy"
policy = <<EOT
path "secret/data/prod/kubernetes/rds/mycompany-judge" {
capabilities = ["read"]
}
EOT
}Repeat for archivista and kratos with their respective ServiceAccount names.
Judge is distributed via AWS Marketplace using a shared ECR registry. This is enabled by default.
global:
registry:
marketplace:
enabled: true # Uses AWS Marketplace ECR (709825985650)Registry Details:
- Account:
709825985650(AWS Marketplace official) - Region:
us-east-1(hardcoded) - Seller Namespace:
testifysec(hardcoded) - Image Path Pattern:
709825985650.dkr.ecr.us-east-1.amazonaws.com/testifysec/{image}:{tag}
Authentication:
- Requires active AWS Marketplace subscription
- Node IAM role automatically authenticates to marketplace ECR
- No additional image pull secrets required
Available Images:
testifysec/judge-api:{version}testifysec/judge-web:{version}testifysec/judge-archivista:{version}testifysec/judge-dex:{version}testifysec/judge-fulcio:{version}testifysec/judge-kratos:{version}testifysec/judge-kratos-self-service:{version}testifysec/judge-timestamp-server:{version}
For private registries or self-hosted deployments:
global:
registry:
marketplace:
enabled: false # Disable marketplace ECR
url: "myregistry.example.com" # Your registry URL
repository: "myorg" # Repository namespace
imagePullSecrets: # Optional: registry authentication
- name: my-registry-secretImage Path Pattern: {url}/{repository}/{image}:{tag}
Example: myregistry.example.com/myorg/judge-api:v1.15.0
Override specific service versions:
global:
versions:
platform: "v1.15.0" # Default for all services
api: "v1.16.0-beta" # Test new API version
registry:
marketplace:
enabled: true
versions: # Per-service version overrides
dex: "v2.43.2" # Use newer Dex versionConfigure CPU and memory for production workloads:
judge-api:
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "1000m"
archivista:
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"Scale replicas for critical services:
judge-api:
replicaCount: 3
archivista:
replicaCount: 3
kratos:
kratos:
replicaCount: 2Enable or disable platform features:
# Disable code signing and timestamping (not yet integrated with CI/CD)
fulcio:
enabled: false
tsa:
enabled: false
# Disable AI proxy if not using AI-powered features
judge-ai-proxy:
enabled: false
# Disable OIDC if using only password authentication
global:
oidc:
enabled: falseEnable when deploying via ArgoCD:
global:
argocd:
enabled: true # Adds sync annotations to Jobs# Check all pods are running
kubectl get pods -n judge
# Expected output: All pods in Running status
# NAME READY STATUS
# judge-platform-judge-api-xxxx 1/1 Running
# judge-platform-judge-archivista-xxxx 1/1 Running
# judge-platform-judge-web-xxxx 1/1 Running
# judge-platform-judge-gateway-xxxx 1/1 Running
# judge-platform-judge-kratos-xxxx 1/1 Running
# judge-platform-judge-kratos-self-service-xxxx 1/1 Running
# judge-platform-judge-dex-xxxx 1/1 Running# Check VirtualServices are created
kubectl get virtualservices -n judge
# Expected: One VirtualService per public-facing service
# NAME GATEWAYS HOSTS
# judge-api-vs ["judge-gateway"] ["api.example.com"]
# judge-web-vs ["judge-gateway"] ["judge.example.com"]
# kratos-public-vs ["judge-gateway"] ["kratos.example.com"]# Check ExternalSecrets are synced
kubectl get externalsecrets -n judge
# Expected: All ExternalSecrets show "SecretSynced" status
# NAME STATUS LAST REFRESH
# judge-archivista-database SecretSynced 1m
# judge-api-database SecretSynced 1m
# judge-kratos SecretSynced 1m# Check ServiceAccount has IAM role annotation
kubectl get serviceaccount judge-platform-judge-api -n judge -o yaml
# Expected annotation:
# annotations:
# eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/prod-judge-judge-api# Get ingress gateway external IP/hostname
kubectl get svc -n istio-system istio-ingressgateway
# Test web UI access
curl -k https://judge.example.com
# Test API health endpoint
curl -k https://api.example.com/healthSymptom: Pods stuck in ImagePullBackOff
Causes:
- No active AWS Marketplace subscription
- Node IAM role missing ECR permissions
- Wrong registry configuration
Solution:
# Verify marketplace subscription in AWS Console
# AWS Marketplace → Manage subscriptions → Judge
# Check node IAM role has ECR permissions
aws iam get-role-policy \
--role-name {eks-node-role} \
--policy-name ECRReadOnly
# Verify registry configuration
kubectl get pods -n judge -o yaml | grep "image:"Symptom: kubectl get externalsecrets shows SyncError
Causes:
- Vault not reachable from cluster
- Vault Kubernetes auth not configured
- ServiceAccount names mismatch
- Missing secrets in Vault
Solution:
# Check ExternalSecret status
kubectl describe externalsecret judge-api-database -n judge
# Verify Vault connectivity
kubectl run vault-test --rm -it --image=curlimages/curl -- \
curl -k https://vault.example.com/v1/sys/health
# Verify Vault Kubernetes auth role
vault read auth/kubernetes/role/judge-api
# Check Vault secrets exist
vault kv get secret/prod/kubernetes/rds/mycompany-judgeSymptom: Pods can't access S3/SNS/SQS with permission denied errors
Causes:
- IAM role trust policy incorrect
- ServiceAccount annotation missing
- IAM role permissions insufficient
Solution:
# Verify ServiceAccount has correct annotation
kubectl get sa judge-platform-judge-api -n judge -o jsonpath='{.metadata.annotations}'
# Check IAM role trust policy allows ServiceAccount
aws iam get-role --role-name prod-judge-judge-api --query 'Role.AssumeRolePolicyDocument'
# Test IAM role from pod
kubectl exec -it {judge-api-pod} -n judge -- \
aws sts get-caller-identitySymptom: Pods crash with database connection errors
Causes:
- RDS security group doesn't allow EKS pods
- Database credentials incorrect
- Database doesn't exist
- SSL/TLS configuration mismatch
Solution:
# Verify RDS security group allows EKS CIDR
aws ec2 describe-security-groups \
--group-ids {rds-security-group-id}
# Test database connectivity from cluster
kubectl run psql-test --rm -it --image=postgres:13 -- \
psql "postgres://user:pass@endpoint:5432/judge_api?sslmode=require"
# Check database secret in Kubernetes
kubectl get secret judge-api-database -n judge -o yamlJudge requires TLS certificates for HTTPS access via Istio ingress.
Option 1: cert-manager (Recommended):
# Install cert-manager first
# helm install cert-manager jetstack/cert-manager ...
# Configure ClusterIssuer
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: istio
# Reference in values.yaml
global:
istio:
tlsSecretName: wildcard-tls # Created by cert-managerOption 2: Manual Certificate:
# Create TLS secret manually
kubectl create secret tls wildcard-tls \
--cert=tls.crt \
--key=tls.key \
-n judgeDeploy multiple Judge instances (dev, staging, prod) in the same cluster:
# values-dev.yaml
global:
domain: dev.example.com
aws:
prefix: "dev-judge"
secrets:
vault:
env: "dev"
# values-staging.yaml
global:
domain: staging.example.com
aws:
prefix: "staging-judge"
secrets:
vault:
env: "staging"
# values-prod.yaml
global:
domain: example.com
aws:
prefix: "prod-judge"
secrets:
vault:
env: "prod"Install with different release names and namespaces:
helm install judge-dev charts/judge -n judge-dev --values values-dev.yaml
helm install judge-staging charts/judge -n judge-staging --values values-staging.yaml
helm install judge-prod charts/judge -n judge-prod --values values-prod.yaml- Architecture Diagrams: docs/architecture/diagrams/
- Deployment Guides: docs/deployment/
- Configuration Reference: charts/judge/docs/
- Development Guide: DEVELOPMENT.md
- Contributing: CONTRIBUTING.md
Complete infrastructure-as-code examples for AWS:
Includes:
- EKS cluster with IRSA
- RDS PostgreSQL database
- S3 buckets
- SNS/SQS messaging
- Vault configuration
- IAM roles and policies
- Istio Documentation
- External Secrets Operator
- HashiCorp Vault Kubernetes Auth
- AWS EKS IRSA
- Helm Best Practices
For support, questions, or issues:
- Check Troubleshooting section above
- Review GitHub Issues
- Contact TestifySec support via AWS Marketplace support channel
- Email: [email protected]
Copyright © TestifySec. All rights reserved.
Proprietary software distributed via AWS Marketplace. See AWS Marketplace listing for license terms and conditions.