Skip to main content

Kubernetes Security Hardening

This guide covers best practices for securing GateKey's publicly exposed services when deployed on Kubernetes. These recommendations apply to any internet-facing deployment and complement GateKey's built-in zero-trust security model.

Overview

GateKey exposes several services that may be publicly accessible:

ServicePortPurpose
Web UI443Admin dashboard and user portal
API Server443REST API for clients and gateways
Gateway VPN1194/UDP or 51820/UDPOpenVPN or WireGuard connections

TLS Termination

Istio Service Mesh

Istio provides robust TLS termination with automatic certificate management and mTLS between services.

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: gatekey-gateway
namespace: istio-system
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: gatekey-tls-cert
hosts:
- vpn.yourcompany.com

Enable strict mTLS for the namespace:

apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: gatekey
spec:
mtls:
mode: STRICT

NGINX Ingress Controller

For NGINX-based ingress with hardened TLS settings:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gatekey
namespace: gatekey
annotations:
nginx.ingress.kubernetes.io/ssl-protocols: "TLSv1.3"
nginx.ingress.kubernetes.io/ssl-ciphers: "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384"
nginx.ingress.kubernetes.io/ssl-prefer-server-ciphers: "true"
nginx.ingress.kubernetes.io/hsts: "true"
nginx.ingress.kubernetes.io/hsts-max-age: "31536000"
nginx.ingress.kubernetes.io/hsts-include-subdomains: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- vpn.yourcompany.com
secretName: gatekey-tls
rules:
- host: vpn.yourcompany.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gatekey-web
port:
number: 80

Traefik

Traefik configuration with TLS hardening:

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: gatekey
namespace: gatekey
spec:
entryPoints:
- websecure
routes:
- match: Host(`vpn.yourcompany.com`)
kind: Rule
services:
- name: gatekey-web
port: 80
tls:
secretName: gatekey-tls
options:
name: hardened-tls
---
apiVersion: traefik.io/v1alpha1
kind: TLSOption
metadata:
name: hardened-tls
namespace: gatekey
spec:
minVersion: VersionTLS13
cipherSuites:
- TLS_AES_256_GCM_SHA384
- TLS_CHACHA20_POLY1305_SHA256

Certificate Management with cert-manager

Automate certificate issuance and renewal:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@yourcompany.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: nginx
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: gatekey-tls
namespace: gatekey
spec:
secretName: gatekey-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
commonName: vpn.yourcompany.com
dnsNames:
- vpn.yourcompany.com

Network Policies

Restrict pod-to-pod communication to only what's necessary.

Default Deny All

Start with a default deny policy:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: gatekey
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress

Allow Required Traffic

Then explicitly allow required traffic:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-gatekey-traffic
namespace: gatekey
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: gatekey-server
policyTypes:
- Ingress
- Egress
ingress:
# Allow from ingress controller
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 8080
# Allow from web UI
- from:
- podSelector:
matchLabels:
app.kubernetes.io/name: gatekey-web
ports:
- protocol: TCP
port: 8080
egress:
# Allow to PostgreSQL
- to:
- podSelector:
matchLabels:
app.kubernetes.io/name: postgresql
ports:
- protocol: TCP
port: 5432
# Allow DNS
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53

Pod Security

Pod Security Standards

Apply restricted pod security standards:

apiVersion: v1
kind: Namespace
metadata:
name: gatekey
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted

Security Context

Configure pods with minimal privileges:

apiVersion: apps/v1
kind: Deployment
metadata:
name: gatekey-server
namespace: gatekey
spec:
template:
spec:
securityContext:
runAsNonRoot: true
runAsUser: 65534
runAsGroup: 65534
fsGroup: 65534
seccompProfile:
type: RuntimeDefault
containers:
- name: server
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}

Secrets Management

External Secrets Operator

Integrate with external secret stores like AWS Secrets Manager, HashiCorp Vault, or Azure Key Vault:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: gatekey-secrets
namespace: gatekey
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: gatekey-secrets
data:
- secretKey: admin-password
remoteRef:
key: gatekey/admin
property: password
- secretKey: database-url
remoteRef:
key: gatekey/database
property: connection-string

HashiCorp Vault with Sidecar Injection

apiVersion: apps/v1
kind: Deployment
metadata:
name: gatekey-server
namespace: gatekey
spec:
template:
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "gatekey"
vault.hashicorp.com/agent-inject-secret-config: "secret/data/gatekey/config"

Sealed Secrets

For GitOps workflows, use Sealed Secrets to encrypt secrets in Git:

# Encrypt a secret
kubeseal --format yaml < secret.yaml > sealed-secret.yaml
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: gatekey-secrets
namespace: gatekey
spec:
encryptedData:
admin-password: AgBy8hCi...
database-url: AgCtr4Nk...

Rate Limiting and DDoS Protection

NGINX Ingress Rate Limiting

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gatekey
namespace: gatekey
annotations:
nginx.ingress.kubernetes.io/limit-rps: "10"
nginx.ingress.kubernetes.io/limit-connections: "5"
nginx.ingress.kubernetes.io/limit-rpm: "100"
nginx.ingress.kubernetes.io/limit-whitelist: "10.0.0.0/8"

Istio Rate Limiting

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: ratelimit
namespace: istio-system
spec:
workloadSelector:
labels:
istio: ingressgateway
configPatches:
- applyTo: HTTP_FILTER
match:
context: GATEWAY
listener:
filterChain:
filter:
name: envoy.filters.network.http_connection_manager
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.local_ratelimit
typed_config:
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
type_url: type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
value:
stat_prefix: http_local_rate_limiter
token_bucket:
max_tokens: 100
tokens_per_fill: 10
fill_interval: 1s

Web Application Firewall (WAF)

ModSecurity with NGINX Ingress

Enable ModSecurity WAF:

apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-ingress-controller
namespace: ingress-nginx
data:
enable-modsecurity: "true"
enable-owasp-modsecurity-crs: "true"
modsecurity-snippet: |
SecRuleEngine On
SecAuditLog /var/log/modsec_audit.log

Cloud-Based WAF

Consider placing a cloud WAF in front of your cluster:

  • AWS WAF with Application Load Balancer
  • Cloudflare with proxy mode enabled
  • Google Cloud Armor with GKE Ingress
  • Azure Front Door with WAF policies

Example Cloudflare configuration in Terraform:

resource "cloudflare_ruleset" "gatekey_waf" {
zone_id = var.cloudflare_zone_id
name = "GateKey WAF Rules"
kind = "zone"
phase = "http_request_firewall_managed"

rules {
action = "execute"
action_parameters {
id = "efb7b8c949ac4650a09736fc376e9aee" # Cloudflare Managed Ruleset
}
expression = "true"
description = "Execute Cloudflare Managed Ruleset"
enabled = true
}
}

RBAC and Service Accounts

Minimal Service Account Permissions

apiVersion: v1
kind: ServiceAccount
metadata:
name: gatekey-server
namespace: gatekey
automountServiceAccountToken: false
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: gatekey-server
namespace: gatekey
rules:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["gatekey-config"]
verbs: ["get", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: gatekey-server
namespace: gatekey
subjects:
- kind: ServiceAccount
name: gatekey-server
roleRef:
kind: Role
name: gatekey-server
apiGroup: rbac.authorization.k8s.io

Image Security

Image Scanning with Trivy

Integrate vulnerability scanning in your CI/CD:

# GitHub Actions example
- name: Scan image
uses: aquasecurity/trivy-action@master
with:
image-ref: dyetech/gatekey-server:${{ github.sha }}
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH
exit-code: 1

Image Policy with Kyverno

Enforce signed images:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signature
spec:
validationFailureAction: Enforce
rules:
- name: verify-signature
match:
resources:
kinds:
- Pod
namespaces:
- gatekey
verifyImages:
- imageReferences:
- "dyetech/gatekey-*"
attestors:
- entries:
- keyless:
subject: "https://github.com/dye-tech/*"
issuer: "https://token.actions.githubusercontent.com"

Audit Logging

Enable Kubernetes Audit Logging

apiVersion: audit.k8s.io/v1
kind: Policy
metadata:
name: gatekey-audit
rules:
- level: RequestResponse
namespaces: ["gatekey"]
verbs: ["create", "update", "patch", "delete"]
resources:
- group: ""
resources: ["secrets", "configmaps"]
- level: Metadata
namespaces: ["gatekey"]

Falco Runtime Security

Deploy Falco for runtime threat detection:

apiVersion: v1
kind: ConfigMap
metadata:
name: falco-rules
namespace: falco
data:
gatekey-rules.yaml: |
- rule: Unexpected outbound connection from GateKey
desc: Detect unexpected outbound connections
condition: >
outbound and container.name startswith "gatekey"
and not (fd.sip in (allowed_ips))
output: >
Unexpected outbound connection from GateKey
(connection=%fd.name container=%container.name)
priority: WARNING

Security Checklist

Use this checklist when deploying GateKey to production:

Network Security

  • TLS 1.3 enforced on all public endpoints
  • Valid certificates from trusted CA (not self-signed)
  • HSTS headers enabled with long max-age
  • Network policies restrict pod-to-pod traffic
  • Ingress only allows required ports

Pod Security

  • Pods run as non-root user
  • Read-only root filesystem enabled
  • No privileged containers
  • Resource limits configured
  • Security contexts applied

Secrets

  • No secrets in environment variables or config files
  • Secrets encrypted at rest (etcd encryption)
  • External secrets management for sensitive data
  • Regular secret rotation configured

Access Control

  • Minimal RBAC permissions
  • Service account tokens not auto-mounted
  • Admin access requires MFA
  • Audit logging enabled

Monitoring

  • Security events logged and alerted
  • Image vulnerability scanning in CI/CD
  • Runtime security monitoring (Falco or similar)
  • Regular penetration testing scheduled