Appearance
Security Technology Reference
Detailed reference for every security tool and control used in the Microtec ERP platform — identity, pipeline scanning, runtime protection, and compliance.
Identity Provider
Keycloak
Version: 22.x (Bitnami image bitnami/keycloak:22)
Role: Central identity and access management — OIDC/OAuth2, SSO, MFA, federation
Deployment: Public CAE (internet-facing) — 2 replicas minimum in production
CPU/Memory: 2.0 vCPU / 4Gi (production), 0.5 vCPU / 1Gi (dev)
Realms:
| Realm | Used by | Purpose |
|---|---|---|
microtec | ERP Angular apps (ports 4401–4409) | ERP user login |
businessowner | Business Owner app (port 4301) | Tenant admin login |
Custom SPIs (7 total — Java extensions in KeycloakProviders/):
| SPI | Type | Purpose |
|---|---|---|
TenantAuthenticator | Authenticator | Validates tenant context on login |
TenantEventListener | Event Listener | Audits login/logout events to ERP audit log |
CustomUserStorageProvider | User Storage | Federated user lookup from ERP database |
TenantProtocolMapper | Protocol Mapper | Injects tenant claims into JWT |
XApiKeyAuthenticator | Authenticator | Validates internal XApiKey for service-to-service calls |
RealmBrandingTheme | Theme | Per-realm custom login UI |
SessionPolicyExtension | Policy | Enforces session limits per tenant |
Keycloak provisioning: Automated via .NET provisioning service — flow priority, client configuration, realm creation run at startup. See Keycloak Provisioning.
Authentication Protocols
OIDC / OAuth2
Role: Standard authentication flow for all browser-based and mobile clients
Flows used:
| Flow | Client type | Usage |
|---|---|---|
| Authorization Code + PKCE | Angular apps, Flutter mobile | Human user login |
| Client Credentials | Internal services | Service-to-service calls (machine identity) |
| Refresh Token | All clients | Silent token renewal |
JWT token structure (claims injected by Keycloak):
| Claim | Value | Purpose |
|---|---|---|
sub | User UUID | User identity |
tenant_id | Tenant UUID | Multi-tenant routing |
realm_access.roles | ["erp-user", "accountant"] | RBAC roles |
preferred_username | john.doe | Display name |
exp | Unix timestamp | Token expiry |
Token lifetimes:
| Token | Lifetime |
|---|---|
| Access token | 15 minutes |
| Refresh token | 8 hours (sliding) |
| SSO session | 24 hours |
Internal Service Authentication
XApiKey
Value: 3bb564df-0f24-4ea6-82c1-d99f368cac8a (stored in Key Vault XApiKey--Value)
Role: Shared secret for direct service-to-service HTTP calls within the private CAE — bypasses OIDC overhead for trusted internal calls
Header: X-Api-Key: {value}
XApiKey scope
XApiKey is only valid inside the private CAE. Gateway.API strips the header before forwarding to any external consumer. Never expose this key outside the VNet.
mTLS (Mutual TLS)
Implementation: Azure Container Apps Environment — transparent, no application code required
Scope: Private CAE only — all inter-service traffic is mutually authenticated at the sidecar level
Certificates: ACA-managed, auto-rotated — no manual certificate management
Secret Scanning
Gitleaks
Version: Latest (pipeline-pinned)
Role: Stage 1 of the 16-stage security pipeline — detects secrets committed to source code
Scope: Entire Git history on every PR and merge
Configuration: .gitleaks.toml per repository (allowlist for test fixtures)
Pipeline integration:
yaml
# Stage 1: Secret scanning
- stage: SecretScanning
jobs:
- job: Gitleaks
steps:
- task: Bash@3
inputs:
script: |
docker run --rm -v $(Build.SourcesDirectory):/path \
zricethezav/gitleaks:latest detect \
--source /path \
--config /path/.gitleaks.toml \
--report-format json \
--report-path $(Build.ArtifactStagingDirectory)/gitleaks-report.jsonBlocking: Yes — any detected secret fails the pipeline immediately. No bypass permitted.
Common patterns detected: AWS keys, Azure SAS tokens, Stripe keys, JWT secrets, database connection strings with passwords, .env files accidentally committed.
Static Application Security Testing (SAST)
SonarCloud
Version: SaaS (latest)
Role: SAST + code quality gate — runs on every PR and merge
Organisation: microtec on sonarcloud.io
Languages: C# (.NET), TypeScript, Dart — auto-detected per repository
Configuration: .sonarcloud.properties in each repository root
Quality gate thresholds:
| Metric | Threshold |
|---|---|
| Coverage (new code) | ≥ 80% |
| Duplicated lines (new code) | < 3% |
| Security hotspots reviewed | 100% |
| Reliability rating | A |
| Security rating | A |
| Maintainability rating | A |
Pipeline step:
yaml
- task: SonarCloudPrepare@1
inputs:
SonarCloud: 'SonarCloud-Connection'
organization: 'microtec'
projectKey: '$(SONAR_PROJECT_KEY)'
projectName: '$(Build.Repository.Name)'
# ... build and test steps ...
- task: SonarCloudAnalyze@1
- task: SonarCloudPublish@1
inputs:
pollingTimeoutSec: '300'On-prem SonarQube (legacy)
An older SonarQube 10.x instance exists on eg-sv-ai (192.168.120.254:8095, admin/Admin@2024) for on-prem pipeline runs. New projects should prefer SonarCloud.
Dependency Vulnerability Scanning
OWASP Dependency-Check
Version: Latest (pipeline-pinned)
Role: Stage 3 — CVE scanning of .NET NuGet packages against the NVD
Blocking threshold: Any Critical or High CVE
Report format: HTML + XML published as pipeline artifacts
Trivy (Dependencies)
Version: Latest (pipeline-pinned)
Role: Stage 4 — SCA (Software Composition Analysis) for all ecosystems: NuGet, npm, Dart pub
Blocking threshold: Critical severity
Additional mode: IaC scanning (Stage 5) — scans Bicep and pipeline YAML for misconfigurations
bash
# Trivy dependency scan (run in pipeline)
trivy fs . \
--scanners vuln \
--severity CRITICAL,HIGH \
--format json \
--output trivy-deps-report.jsonDockerfile Linting
Hadolint
Version: 2.x
Role: Stage 6 — lint all Dockerfiles against Dockerfile best practices (follows Docker OCI spec)
Blocking: Yes — any Error-level finding fails the pipeline
Common rules enforced:
| Rule | Description |
|---|---|
| DL3008 | Pin package versions in apt-get install |
| DL3018 | Pin package versions in apk add |
| DL3025 | Use JSON array form for CMD and ENTRYPOINT |
| DL4006 | Set SHELL options when using set -o pipefail |
bash
# Hadolint check (run in pipeline)
hadolint --format json \
--failure-threshold error \
$(find . -name "Dockerfile*") \
> hadolint-report.jsonContainer Image Scanning
Trivy (Container Image)
Version: Latest (pipeline-pinned)
Role: Stage 9 — CVE scan of the built Docker image after docker build
Scope: OS packages, language libraries, and application dependencies embedded in the image
Blocking threshold: Critical CVE
bash
# Trivy image scan (run in pipeline after docker build)
trivy image \
--severity CRITICAL,HIGH \
--format json \
--output trivy-image-report.json \
"${ACR_NAME}.azurecr.io/${SERVICE_NAME}:${BUILD_ID}"SBOM Generation
Syft + CycloneDX
Version: Syft 1.x
Role: Stage 10 — generates Software Bill of Materials (SBOM) for every container image
Format: CycloneDX JSON (industry standard, accepted by regulators)
Blocking: No — informational artifact only
Storage: Published to Azure Blob Storage — retained for audit trail
bash
syft "${ACR_NAME}.azurecr.io/${SERVICE_NAME}:${BUILD_ID}" \
--output cyclonedx-json@1.4 \
--file sbom-${SERVICE_NAME}.cdx.jsonDynamic Application Security Testing (DAST)
OWASP ZAP
Version: 2.14.x
Role: Stage 13 — DAST against deployed application endpoints
Scan type: Active scan (dev/stage) — submits malicious inputs
Blocking threshold: High severity findings
Authentication: ZAP is configured with a Keycloak bearer token for authenticated scans
Scan targets per environment:
| Environment | ZAP target |
|---|---|
| dev | https://microtec-test.com/api |
| stage | https://microtecstage.com/api |
Rules suppressed (documented false positives):
90033— Loosely Scoped Cookie (Keycloak SSO domain cookies)10036— Server Leaks Version via Server Header (Gateway sets minimal headers)
AI Code Review
Azure OpenAI GPT-4o
Role: Stage 12 — AI-assisted code review on every PR diff
Integration: Custom ADO pipeline task that posts diff to Azure OpenAI and publishes findings as PR comments
Blocking: No — advisory only
Focus areas: Logic errors, security anti-patterns, performance issues, missing null checks
WAF
Azure Front Door WAF
Rule set: OWASP Core Rule Set 3.2
Mode: Prevention (production), Detection (dev/stage)
Custom rules:
- IP allowlist for admin endpoints (
/api/admin/*) - Rate limit: 1000 requests/minute per IP on
/api/auth/* - Geo-blocking: Configurable per-environment
Secret Management Runtime
Azure Key Vault
Role: Runtime secrets store — all credentials injected into Container Apps as environment variables via KV references
Authentication: Managed identity (no stored credentials)
Audit log: All key access logged to Azure Monitor — retained 90 days
See Infrastructure Technology Reference for Key Vault names and secret naming conventions.
Security Pipeline: 16-Stage Overview
| Stage | Tool | Blocks? | Category |
|---|---|---|---|
| 1 | Gitleaks | Yes | Secret detection |
| 2 | SonarCloud | Yes | SAST + quality gate |
| 3 | OWASP Dependency-Check | Yes | CVE / NuGet |
| 4 | Trivy (deps) | Yes | SCA |
| 5 | Trivy (IaC) | Yes | Misconfiguration |
| 6 | Hadolint | Yes | Dockerfile lint |
| 7 | Unit tests | Yes | Functional |
| 8 | Docker build | Yes | CI artifact |
| 9 | Trivy (image) | Yes | Container CVE |
| 10 | Syft / CycloneDX | No | SBOM |
| 11 | Integration tests | Yes | Functional |
| 12 | Azure OpenAI GPT-4o | No | AI code review |
| 13 | OWASP ZAP | Yes | DAST |
| 14 | Report generator | No | Reporting |
| 15 | Teams webhook | No | Notification |
| 16 | Storage archiver | No | Audit archive |