Skip to content

Security

No long-lived credentials; least privilege everywhere.

Updated View as Markdown

Least privilege, no long-lived credentials, and defense in depth. Controls map to the OWASP Top 10 and ASVS, so “secure” can be measured, not just claimed.

Identity and secrets

  • Do not put long-lived credentials in code or CI. Store secrets in Secret Manager and access them through Workload Identity. If a credential must persist, such as a signing key, store it there and rotate it through an assigned owner. Service-to-service identity is covered in authentication.
  • Secret scanning blocks credentials before they enter history: GitHub push protection runs on every repository, backed by a pre-commit hook.
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.0          # pin a current release
    hooks:
      - id: gitleaks       # blocks a commit that contains a credential

Supply chain

  • Use a minimum-release-age quarantine, pinned overrides, and reviewed lockfiles.
  • Generate an SBOM for each build. Attest production images through Binary Authorization and scan them before promotion. Get signing material for mobile and desktop artifacts from the secret store during the build.
  • Automate dependency scanning and review advisories. Scan IaC with (tfsec or Checkov) in the Atlantis and Terraform path. See quality gates for where these checks run and SLSA for the provenance model.

Container and network hardening

Pods run as non-root users with a read-only root filesystem and no added capabilities. They use the Pod Security Admission restricted profile:

# containers[].securityContext — readOnlyRootFilesystem, capabilities and
# allowPrivilegeEscalation are container-level fields, not pod-level
securityContext:
  runAsNonRoot: true
  runAsUser: 10001
  readOnlyRootFilesystem: true
  allowPrivilegeEscalation: false
  seccompProfile: { type: RuntimeDefault }
  capabilities: { drop: ["ALL"] }

East-west traffic is denied by default. A namespace starts closed in both directions and adds specific allow rules:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: default-deny }
spec:
  podSelector: {}                     # all pods in the namespace
  policyTypes: ["Ingress", "Egress"]  # deny both ways until a later policy allows

Data protection

  • Use TLS 1.3+ for data in transit and AES-256 for data at rest. Use KMS-managed keys with a defined rotation schedule.
  • Use a data-classification scheme to control where sensitive data can live and how to mask or tokenize it in logs and lower environments.
  • Do not put PII in logs or traces. This matches token redaction on the authentication page and the logging rule in observability.

Application security

  • Run SAST on every pull request and grade it against the OWASP Top 10. Critical and High findings block a release.
  • Use lightweight threat modeling, such as STRIDE or a data-flow diagram, for new services and changes that affect auth, crypto, PII, or a customer-facing API. Review it with the Security Team.
  • Put public workloads behind WAF and DDoS protection. Apply rate limiting at the edge first.
  • Use least privilege and require MFA for all human access. Service identities use Workload Identity, not passwords.

Reviews and disclosure

  • Review packages quarterly. Set aside sprint time to keep libraries within one major version of the latest.
  • Review vulnerability alerts weekly. Remediation time must meet the SLA.
  • Use a coordinated disclosure path (security.txt) to triage external reports against the same SLA.

In-house Workload Identity references: NT-NinjaTrader/ntc-core (deploy/arc) and NT-NinjaTrader/gcp-infrastructure-base.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close