Go-Trust is a local trust engine that provides trust decisions based on ETSI TS 119612 Trust Status Lists (TSLs). It allows clients to abstract trust decisions through an AuthZEN policy decision point (PDP). The service evaluates trust in subjects identified by X509 certificates using a set of TSLs.
Go-Trust is not meant to be used cross trust boundaries. The reason for creating Go-Trust is to promote interoperability across implementations that rely on ETSI trust status lists such as the EUDI wallet. ETSI TS 119 612 is complex to implement correctly and hopefully Go-Trust provides a way to both ensure correct trust evaluation as well as provide performance enhancements by allowing for local caching etc. Go-Trust is an API service meant to be run inside the same trust domain as the entity that relies on trust evaluation (for instance a wallet unit or an issuer).
Go-Trust can also be used to maintain and publish ETSI trust status lists, act as a local distribution point and provide policy based transformation of trust status lists.
The design was inspired by the pyFF project which has been used to provide similar functions for SAML metadata at scale.
- AuthZEN Integration: Policy decision point for trust evaluation
- TSL Management: Process and validate ETSI TS 119612 Trust Status Lists
- Certificate Validation: Evaluate X509 certificates against trusted services
- Pipeline Processing: Flexible TSL processing with configurable steps
- XML Publishing: Serialize TSLs to XML for distribution
- XML Signing: Sign XML documents using file-based keys or PKCS#11 hardware security modules
- Health Checks: Kubernetes-compatible liveness and readiness endpoints
- Metrics: Comprehensive Prometheus metrics for observability
- Performance: Concurrent TSL processing with XSLT caching (2-3x speedup)
- Security: Input validation, rate limiting, and path traversal protection
- Configuration: Flexible YAML-based config with environment variable support
- Developer Tools: Full VS Code integration, pre-commit hooks, and comprehensive testing
- Test Coverage: >80% overall, >85% for critical packages (api, pipeline, dsig)
- Benchmarks: Performance validated with comprehensive benchmark suite
- Linting: Multiple linters (golangci-lint, gosec, staticcheck)
- CI/CD: Automated testing, coverage tracking, and security scanning
# Clone the repository
git clone https://github.com/SUNET/go-trust.git
cd go-trust
# Build the project
make build
# Run tests
make test
The example directory contains:
- Example directory structure for generating TSLs
- Sample pipeline configuration
- Usage examples for various trust scenarios
Go-Trust includes built-in tools for transforming TSLs into user-friendly HTML documents and creating index pages for collections of TSLs.
The stylesheet is embedded in the binary, so you don't need to distribute separate files:
- transform:
- embedded:tsl-to-html.xslt
- /output/directory
- html
This configuration transforms all TSLs in the pipeline to HTML using the embedded stylesheet and writes the output files to the specified directory.
- tsl-to-html.xslt: Transforms TSLs into comprehensive HTML documents with PicoCSS styling
After transforming TSLs to HTML, you can generate an index.html file that lists all the TSLs with key metadata:
- generate_index:
- /output/directory
- "Trust Service Lists Index"
The index page includes:
- Links to each TSL HTML file
- Territory codes and badges
- Sequence numbers and dates
- Service counts
- TSL types
For a complete example, see transform-with-index.yaml in the examples directory.
Go-Trust employs multiple performance optimizations for efficient TSL processing:
XSLT transformations run in parallel using a worker pool:
- Automatic parallelization: Multiple TSLs transformed concurrently
- 2-3x speedup: Significant performance gains on multi-core systems
- Smart scaling: Automatically scales to available CPU cores (up to 8 workers)
- Zero configuration: Enabled by default
Performance characteristics:
- 1 TSL: ~15ms per transformation
- 20 TSLs: ~300ms total (vs ~600ms sequential) - 2x faster
- 50 TSLs: ~700ms total (vs ~1500ms sequential) - 2.1x faster
XSLT stylesheets are cached after first use to reduce I/O overhead:
- Automatic caching: Both file-based and embedded XSLTs are cached
- 5-10% improvement: Additional speedup when processing multiple TSLs
- Thread-safe: Uses
sync.RWMutex
for concurrent access - Memory efficient: Caches only stylesheet content, not transformation results
Combined with concurrent processing, these optimizations make Go-Trust particularly efficient when processing EU Trust Lists with 20+ member state TSLs.
Go-Trust implements comprehensive input validation and sanitization to protect against common security vulnerabilities:
All external inputs are validated before processing:
- URL validation: Enforces allowed schemes (http/https/file), detects path traversal attempts
- File path validation: Prevents null byte injection, path traversal, and access to system directories
- XSLT path validation: Validates embedded and file-based XSLT references
- Output directory validation: Blocks writes to system directories (/etc, /sys, C:\Windows)
- Config file validation: Ensures proper YAML file extensions and safe paths
- Path traversal prevention: Detects and blocks
..
sequences in paths - Null byte detection: Prevents null byte injection attacks
- System directory protection: Blacklists known system directories
- Scheme whitelisting: Only allows explicitly permitted URL schemes
- Automatic sanitization: Cleans and normalizes file paths before use
The validation layer is automatically applied to:
- TSL loading from URLs or files
- XSLT transformation paths
- Output directories for publishing
- Certificate and key file paths for signing
- Configuration file paths
Go-Trust includes per-IP rate limiting to prevent API abuse and ensure fair usage:
- Token bucket algorithm: Uses
golang.org/x/time/rate
for smooth rate limiting - Per-IP tracking: Each client IP address has its own rate limit
- Configurable limits: Set requests per second (RPS) via configuration or environment variables
- Automatic burst handling: Allows brief bursts above the sustained rate limit
- 429 responses: Clients exceeding limits receive standard HTTP 429 (Too Many Requests)
Configuration options:
security:
rate_limit_rps: 100 # Maximum requests per second per IP
Or via environment variable:
GT_RATE_LIMIT_RPS=100 ./gt pipeline.yaml
Rate limiting is applied to all API endpoints when rate_limit_rps > 0
. Set to 0 to disable rate limiting entirely (not recommended for production).
Go-Trust includes a dedicated package for XML digital signatures in pkg/dsig. This package supports:
- File-based certificate and key signing
- PKCS#11 hardware security module integration
- Standardized interface for all signing methods
- Testing utilities for PKCS#11 with SoftHSM
Go-Trust provides a flexible command line interface with two operating modes:
Run as a continuous service with periodic TSL processing:
# Run the trust service with a pipeline configuration
./gt ./pipeline.yaml
# Run with custom settings
./gt --host 0.0.0.0 --port 8080 --frequency 1h ./pipeline.yaml
# With logging configuration
./gt --log-level debug --log-format json ./pipeline.yaml
Process pipelines once and exit (no API server):
# One-shot pipeline execution
./gt --no-server ./pipeline.yaml
# With debug logging
./gt --no-server --log-level debug ./pipeline.yaml
# With JSON logging for parsing
./gt --no-server --log-format json ./pipeline.yaml > output.json
# In a cron job (daily HTML generation)
0 2 * * * /usr/local/bin/gt --no-server /etc/go-trust/daily-processing.yaml
# In CI/CD pipelines
./gt --no-server --log-format json ./ci-pipeline.yaml
The --no-server
flag is useful for:
- Batch processing: Transform TSLs without running a server
- CI/CD pipelines: Generate reports in build systems
- Scheduled jobs: Cron jobs for periodic processing
- Development: Quick testing of pipeline configurations
See example/cmdline-processing.yaml for a complete example.
Usage: gt [options] <pipeline.yaml>
Options:
--help Show this help message and exit
--version Show version information and exit
--config Configuration file path (YAML format)
--host API server hostname (default: 127.0.0.1)
--port API server port (default: 6001)
--frequency Pipeline update frequency (default: 5m)
--no-server Run pipeline once and exit (no API server)
Logging options:
--log-level Logging level: debug, info, warn, error, fatal (default: info)
--log-format Logging format: text or json (default: text)
--log-output Log output: stdout, stderr, or file path (default: stdout)
Configuration precedence (highest to lowest):
1. Command-line flags
2. Environment variables (GT_* prefix)
3. Configuration file (--config)
4. Built-in defaults
Go-Trust supports configuration via YAML files for easier deployment and management. Create a config.yaml
file:
server:
host: "0.0.0.0"
port: "6001"
frequency: "5m"
logging:
level: "info"
format: "text"
output: "stdout"
pipeline:
timeout: "30s"
max_request_size: 10485760
max_redirects: 3
allowed_hosts:
- "*.europa.eu"
security:
rate_limit_rps: 100
enable_cors: false
allowed_origins: []
Use the config file:
gt --config config.yaml pipeline.yaml
All configuration options can be set via environment variables with the GT_
prefix:
export GT_HOST="0.0.0.0"
export GT_PORT="8080"
export GT_LOG_LEVEL="debug"
export GT_FREQUENCY="10m"
export GT_RATE_LIMIT_RPS="200"
gt pipeline.yaml
See example/config.yaml for a complete configuration example with all available options and documentation.
The service exposes several HTTP endpoints:
- GET /health or /healthz: Liveness probe (always returns 200 OK when service is running)
- GET /ready or /readiness: Readiness probe (returns 200 when TSLs loaded, 503 otherwise)
- GET /metrics: Prometheus metrics endpoint for monitoring and observability
- GET /status: Check service health status and loaded TSL count
- GET /info: Get detailed information about loaded TSLs
- POST /authzen/decision: Evaluate trust decisions for X509 certificates
The health endpoints follow Kubernetes best practices:
- Liveness checks if the service is alive (restarts unhealthy containers)
- Readiness checks if the service is ready to accept traffic (removes from load balancer if not)
See the Deployment Guide for Kubernetes integration examples.
The /metrics
endpoint exposes comprehensive operational metrics:
Pipeline Metrics:
pipeline_execution_duration_seconds
- Time to complete pipeline executionpipeline_execution_total
- Total pipeline executions (with success/failure labels)pipeline_execution_errors_total
- Pipeline execution errors by typepipeline_tsl_count
- Number of TSLs in current pipelinepipeline_tsl_processing_duration_seconds
- TSL processing time histogram
API Metrics:
api_requests_total
- HTTP requests by method, endpoint, and status codeapi_request_duration_seconds
- Request latency histogramapi_requests_in_flight
- Current number of active requests
Error Metrics:
errors_total
- Application errors by type and operation
Certificate Validation Metrics:
cert_validation_total
- Certificate validations by result (valid/invalid/error)cert_validation_duration_seconds
- Certificate validation latency
Example Prometheus queries:
# Request rate by endpoint
rate(api_requests_total[5m])
# 95th percentile latency
histogram_quantile(0.95, rate(api_request_duration_seconds_bucket[5m]))
# Pipeline success rate
rate(pipeline_execution_total{result="success"}[5m]) / rate(pipeline_execution_total[5m])
# Certificate validation error rate
rate(cert_validation_total{result="error"}[5m])
Example AuthZEN decision request:
{
"subject": {
"type": "x509_certificate",
"id": "subject-123",
"properties": {
"x5c": [
"MIIDQjCCAiqgAwIBAgIUJlq+zz4..."
]
}
},
"resource": {
"type": "service",
"id": "resource-123",
"properties": {}
},
"action": {
"name": "trust",
"properties": {}
},
"context": {}
}
Example response:
{
"decision": true
}
Or with error details:
{
"decision": false,
"context": {
"id": "err-123",
"reason_admin": {
"error": "certificate has expired or is not yet valid"
},
"reason_user": {
"message": "The certificate is not trusted"
}
}
}
Go-Trust uses a pipeline architecture for TSL processing:
- Load: Read TSLs from files or URLs
- Select: Filter TSLs based on criteria
- Publish: Serialize TSLs to XML files
- Custom: Add your own processing steps
Go-Trust supports XML-DSIG signatures for published TSLs using either:
- File-based certificates and keys: Standard PEM-encoded X.509 certificates and private keys
- PKCS#11 hardware tokens: HSMs or smart cards for secure key storage and operations
For development and testing environments, you can use file-based certificates and private keys:
- publish: ["./output", "/path/to/cert.pem", "/path/to/key.pem"]
This method reads the certificate and private key from PEM-encoded files.
For production environments, you can use PKCS#11 hardware security modules (HSMs) or smart cards:
- publish: ["./output", "pkcs11:module=/path/to/lib;pin=1234;slot-id=0", "key-label", "cert-label"]
The PKCS#11 URI format follows RFC 7512 and supports these parameters:
module
: Path to PKCS#11 library/middleware (required)pin
: PIN code for token access (required)slot-id
: Numeric slot identifier (optional)token
: Token label for identifying the token (optional, alternative to slot-id)
The key-label
and cert-label
arguments specify the labels used to identify the private key and certificate in the HSM.
Example pipeline configuration (YAML):
# Pipeline YAML Format
# IMPORTANT: Pipeline steps are defined as a direct sequence of operations
# Each step is a mapping with a single key (the method name) and a list of arguments
# Do NOT use a "steps:" key in your YAML - steps are defined directly at the top level
- generate: ["./example/example-tsl"] # Generate TSL from directory
- select: [] # Extract certificates into a pool
- publish: ["./output"] # Publish TSLs as XML files
- publish: ["./output", "/path/to/cert.pem", "/path/to/key.pem"] # Publish with file-based XML-DSIG signatures
- publish: ["./output", "pkcs11:module=/usr/lib/softhsm/libsofthsm2.so;pin=1234;slot-id=0", "tsl-signing-key", "tsl-signing-cert"] # Publish with PKCS#11 XML-DSIG signatures
The PKCS#11 implementation has been tested with:
- SoftHSM (for development/testing)
- Thales Luna HSM
- YubiKey (via PIV application)
For other HSMs, you may need to adjust the configuration according to your device's specifications.
The application is configured using command-line flags:
# Start the API server with custom settings
./gt --host 0.0.0.0 --port 8080 --frequency 1h ./path/to/pipeline.yaml
Available command-line options:
--host
: API server hostname (default: 127.0.0.1)--port
: API server port (default: 6001)--frequency
: Pipeline update frequency (default: 5m)--help
: Show help message--version
: Show version information
## Development
### Requirements
- Go 1.18+
- Access to ETSI TS 119612 TSLs or sample data
- Make for build automation
### Quick Start
For detailed developer documentation, see [DEVELOPER.md](DEVELOPER.md).
```bash
# Clone the repository
git clone https://github.com/SUNET/go-trust.git
cd go-trust
# Set up development environment
make setup
# Run tests
make test
# Build binary
make build
# Build binary
make build
# Run tests with coverage
make test
# Check code coverage
make coverage
# Run linters
make lint
# Run benchmarks
make bench
Run make help
to see all available targets:
make help
Key targets:
make all
- Run all checks and build (CI pipeline)make test
- Run tests with race detectionmake coverage
- Generate coverage reportmake lint
- Run all lintersmake fmt
- Format codemake quick
- Quick pre-commit checks (fmt + vet)make bench
- Run benchmarksmake clean
- Remove build artifacts
Build and run using Docker:
# Build Docker image
docker build -t go-trust:latest .
# Run container
docker run -d \
-p 6001:6001 \
-v $(pwd)/pipeline.yaml:/app/pipeline.yaml \
-v $(pwd)/config.yaml:/app/config.yaml \
go-trust:latest --config /app/config.yaml /app/pipeline.yaml
Deploy to Kubernetes with health checks and metrics:
apiVersion: apps/v1
kind: Deployment
metadata:
name: go-trust
labels:
app: go-trust
spec:
replicas: 3
selector:
matchLabels:
app: go-trust
template:
metadata:
labels:
app: go-trust
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "6001"
prometheus.io/path: "/metrics"
spec:
containers:
- name: go-trust
image: go-trust:latest
ports:
- name: http
containerPort: 6001
protocol: TCP
env:
- name: GT_HOST
value: "0.0.0.0"
- name: GT_PORT
value: "6001"
- name: GT_LOG_LEVEL
value: "info"
- name: GT_LOG_FORMAT
value: "json"
- name: GT_RATE_LIMIT_RPS
value: "100"
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 10
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /readiness
port: http
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "1000m"
volumeMounts:
- name: config
mountPath: /app/config.yaml
subPath: config.yaml
- name: pipeline
mountPath: /app/pipeline.yaml
subPath: pipeline.yaml
volumes:
- name: config
configMap:
name: go-trust-config
- name: pipeline
configMap:
name: go-trust-pipeline
---
apiVersion: v1
kind: Service
metadata:
name: go-trust
labels:
app: go-trust
spec:
type: ClusterIP
ports:
- port: 6001
targetPort: http
protocol: TCP
name: http
selector:
app: go-trust
---
apiVersion: v1
kind: ConfigMap
metadata:
name: go-trust-config
data:
config.yaml: |
server:
host: "0.0.0.0"
port: "6001"
frequency: "5m"
logging:
level: "info"
format: "json"
output: "stdout"
security:
rate_limit_rps: 100
---
apiVersion: v1
kind: ConfigMap
metadata:
name: go-trust-pipeline
data:
pipeline.yaml: |
# Your pipeline configuration here
Create a ServiceMonitor for Prometheus Operator:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: go-trust
labels:
app: go-trust
spec:
selector:
matchLabels:
app: go-trust
endpoints:
- port: http
path: /metrics
interval: 30s
The health endpoints are designed for Kubernetes probes:
-
Liveness probe (
/healthz
): Checks if the service is running- Returns 200 OK if the service is alive
- Should trigger container restart on failure
-
Readiness probe (
/readiness
): Checks if the service is ready to accept traffic- Returns 200 OK when TSLs are loaded and service is ready
- Returns 503 Service Unavailable during startup or when pipeline fails
- Should remove pod from load balancer on failure
Recommended probe configuration:
- Liveness:
initialDelaySeconds: 10
,periodSeconds: 30
,failureThreshold: 3
- Readiness:
initialDelaySeconds: 5
,periodSeconds: 10
,failureThreshold: 3
go-trust/
├── .github/ # GitHub configuration
│ └── workflows/ # GitHub Actions workflows
├── cmd/ # Command line tools
├── pkg/ # Core packages
│ ├── api/ # HTTP API implementation
│ ├── authzen/ # AuthZEN integration
│ └── pipeline/ # TSL processing pipeline
├── example/ # Example configurations and data
└── tests/ # Integration tests
This project uses GitHub Actions for continuous integration and delivery:
- Go Workflow (
go.yml
): Builds, tests, and checks code coverage - Release Workflow (
release.yml
): Creates releases when new tags are pushed - CodeQL Analysis (
codeql.yml
): Scans code for security vulnerabilities - Dependency Review (
dependency-review.yml
): Checks dependencies for security issues
Contributions are welcome! Please read CONTRIBUTING.md for detailed guidelines on:
- Setting up your development environment
- Code style and standards
- Testing requirements
- Submitting pull requests
- Release process
Quick contribution workflow:
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Run tests and linters (
make quick && make test
) - Commit your changes (
git commit -m 'feat: Add amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
For detailed development documentation, see DEVELOPER.md.
Go-Trust has comprehensive test coverage (>80% overall, >85% for critical packages).
Before submitting a pull request, ensure:
# Run all tests
make test
# Check coverage
make coverage
# Run linters
make lint
# Quick pre-commit checks
make quick
The CI pipeline automatically runs these checks on all pull requests. All checks must pass before merging.
For detailed testing guidelines, see CONTRIBUTING.md.
Go-Trust includes tests for PKCS#11-based XML-DSIG signing using SoftHSM. These tests are skipped if SoftHSM is not installed.
To run the PKCS#11 tests with SoftHSM:
-
Install SoftHSM version 2:
# Ubuntu/Debian sudo apt-get install softhsm2 # CentOS/RHEL sudo yum install softhsm2 # macOS with Homebrew brew install softhsm
-
Run the tests:
# Run all tests, including SoftHSM tests if available go test ./... # Run only the SoftHSM tests go test ./pkg/pipeline -run TestPKCS11SignerWithSoftHSM
The test will:
- Create a temporary SoftHSM token
- Generate a test certificate and private key
- Import them into the token
- Use the PKCS11Signer to sign XML data
- Clean up the temporary token when done
These tests ensure that the PKCS#11 signing functionality works correctly with hardware security modules.
Go-Trust now uses the improved Signer interface from the goxmldsig library to handle XML-DSIG signatures with PKCS#11 hardware tokens. This integration provides several benefits:
- More Consistent API: The signing code follows a unified interface approach
- Better Abstraction: The signing mechanism is abstracted behind the Signer interface
- Simpler Maintenance: Reduced code duplication and complexity
- Improved Security: Direct hardware token integration with no private key exposure
When using a PKCS#11 token for signing, the library:
- Connects to the token using the provided configuration
- Locates the key and certificate based on their labels/IDs
- Creates a PKCS11Signer that implements the goxmldsig Signer interface
- Uses the PKCS11Signer to sign the XML document without ever exposing the private key material
Go-Trust includes a flexible, structured logging system built around an abstract Logger interface, with implementations available for different logging backends.
The logging system is designed with the following features:
- Structured Logging: Log entries include structured fields, not just text messages
- Log Levels: Support for Debug, Info, Warn, Error, and Fatal levels
- Context Awareness: Logging with context propagation
- Extensible: Support for different logging backends through adapters
Logging is configured through command-line arguments, not in pipeline YAML files:
# Configure logging via command line
./gt --log-level debug --log-format json ./pipeline.yaml
Logging statements in pipeline steps:
# Example logging in pipeline
- log:
- "Processing TSL files"
- count=5
- source=example.com
- log:
- level=debug "Detailed debugging information"
- tsl_id=SETSL123
The log
pipeline step allows logging messages with structured data:
- log:
- "Message to log"
- key1=value1
- key2=value2
To specify a log level other than the default (info):
- log:
- level=debug "Debug message with more details"
- operation=validation
- result=success
When extending Go-Trust, you can use the logging system programmatically:
import "github.com/SUNET/go-trust/pkg/logging"
func MyFunction() {
logger := logging.DefaultLogger()
// Simple logging
logger.Info("Processing started")
// With structured fields
logger.Debug("Validation details",
logging.F("certCount", 5),
logging.F("valid", true),
)
// With context
ctx := context.Background()
ctxLogger := logger.WithContext(ctx)
ctxLogger.Info("Operation completed")
}
This project is licensed under the BSD 2-Clause License - see the LICENSE.txt file for details.
- ETSI TS 119612 - Trust-service status list format
- AuthZEN - Authorization framework
- SUNET - Swedish University Network