Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion cloudformation/values/dns-override.values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ replicaCount: 2

image:
repository: socketdev/socket-registry-firewall
tag: "1.1.346" # pin; do not use :latest for a security product
Comment thread
flowstate marked this conversation as resolved.
# tag intentionally omitted: it inherits the chart's appVersion (helm/Chart.yaml),
# the single source of truth for the firewall version. Never use :latest for a
# security product. Set a value here only to override the pinned version.

socket:
# Injected at install time from AWS Secrets Manager by the CloudFormation
Expand Down
4 changes: 3 additions & 1 deletion helm/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ apiVersion: v2
name: socket-firewall
description: Socket.dev Registry Firewall - Block vulnerable packages before they reach your cluster
type: application
version: 0.4.3
version: 0.5.0
# appVersion is the single source of truth for the firewall image version.
# image.tag in values.yaml defaults to this (see templates/_helpers.tpl).
appVersion: "1.1.346"
keywords:
- security
Expand Down
34 changes: 32 additions & 2 deletions helm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ registries:
| Parameter | Description | Default |
|-----------|-------------|---------|
| `image.repository` | Docker image | `socketdev/socket-registry-firewall` |
| `image.tag` | Image tag | `latest` |
| `image.pullPolicy` | Image pull policy | `IfNotPresent` |
| `image.tag` | Image tag. Empty means track the chart's `appVersion` (single source of truth). Set to override. | `""` |
| `image.pullPolicy` | Image pull policy | `Always` |
| `replicaCount` | Number of replicas (ignored if autoscaling enabled) | `1` |
| `socket.apiToken` | Socket API token | `""` |
| `socket.existingSecret` | Use existing secret | `""` |
Expand Down Expand Up @@ -122,6 +122,7 @@ registries:
| `ingress.className` | Ingress class (nginx, alb, traefik) | `""` |
| `autoscaling.enabled` | Enable HorizontalPodAutoscaler | `false` |
| `podDisruptionBudget.enabled` | Keep pods available during node maintenance | `true` |
| `topologySpreadConstraints` | Evenly spread replicas across zones/nodes | `[]` |
| `extraContainers` | Sidecar containers (auth proxies, log collectors) | `[]` |
| `resources.limits.cpu` | CPU limit | `4` |
| `resources.limits.memory` | Memory limit | `8Gi` |
Expand Down Expand Up @@ -492,6 +493,35 @@ kubectl get hpa socket-firewall
kubectl describe hpa socket-firewall
```

## Spreading Replicas Across Zones

When you run more than one replica (via `replicaCount` or autoscaling), use
`topologySpreadConstraints` to distribute pods evenly across availability zones
(or nodes) so a single zone/node failure can't take down a disproportionate share
of the fleet. This is preferred over soft pod anti-affinity, which the scheduler is
free to ignore and can pile replicas into one zone.

```yaml
replicaCount: 3
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway # best-effort even spread; never blocks scheduling
labelSelector:
matchLabels:
app.kubernetes.io/name: socket-firewall
```

- `maxSkew: 1` keeps zones within one pod of each other.
- `whenUnsatisfiable: ScheduleAnyway` is a soft guarantee (recommended). Use
`DoNotSchedule` for a hard guarantee — but be aware pods can stay `Pending` if a
zone is full or you have fewer zones than replicas.
- Pin spreading to a specific revision by adding `matchLabelKeys: [pod-template-hash]`
(requires Kubernetes 1.27+, satisfied by all currently-supported EKS and GKE versions).

**Compatibility:** `topologySpreadConstraints` is GA since Kubernetes 1.19, so it works
on every currently-supported cluster. The chart sets no `kubeVersion` floor.

## Using an Existing Secret for API Token

```bash
Expand Down
9 changes: 9 additions & 0 deletions helm/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ Create a default fully qualified app name.
{{- end }}
{{- end }}

{{/*
Fully-qualified firewall image reference.
The tag defaults to the chart's appVersion so the firewall version is defined in
exactly one place (Chart.yaml appVersion). Override via image.tag in values.yaml.
*/}}
{{- define "socket-firewall.image" -}}
{{- printf "%s:%s" .Values.image.repository (.Values.image.tag | default .Chart.AppVersion) -}}
{{- end }}

{{/*
Create chart name and version as used by the chart label.
*/}}
Expand Down
8 changes: 6 additions & 2 deletions helm/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ spec:
{{- end }}
initContainers:
- name: copy-app
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
image: "{{ include "socket-firewall.image" . }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
command: ["/bin/sh", "-c", "cp -r /app/. /mnt/app/"]
{{- with .Values.initContainers.copyApp.securityContext }}
Expand Down Expand Up @@ -175,7 +175,7 @@ spec:
{{- end }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
image: "{{ include "socket-firewall.image" . }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
{{- with .Values.securityContext }}
securityContext:
Expand Down Expand Up @@ -369,6 +369,10 @@ spec:
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
Expand Down
24 changes: 20 additions & 4 deletions helm/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
# Image configuration
image:
repository: socketdev/socket-registry-firewall
# Defaults to the latest published image so deployments pick up new releases
# without a chart bump. Pin a version for reproducibility, e.g. tag: "1.1.327".
tag: "1.1.346"
# Always (not IfNotPresent) so "latest" actually refreshes on pod restart.
# Leave empty to track the chart's appVersion (Chart.yaml), which is the single
# source of truth for the firewall version. Set a value here only to override
# the pinned version, e.g. tag: "1.1.327".
tag: ""
pullPolicy: Always

# Image pull secrets for private registries
Expand Down Expand Up @@ -432,6 +432,22 @@ tolerations: []
# Affinity
affinity: {}

# Topology spread constraints (evenly distribute replicas across failure domains
# such as zones or nodes). Preferred over soft pod anti-affinity for even spreading.
# Empty by default; only takes effect with replicaCount > 1 or autoscaling enabled.
# The base field is GA since Kubernetes 1.19 (universally available). The optional
# matchLabelKeys field needs Kubernetes 1.27+ (satisfied by all current EKS/GKE versions).
topologySpreadConstraints: []
# - maxSkew: 1
# topologyKey: topology.kubernetes.io/zone
# whenUnsatisfiable: ScheduleAnyway # use DoNotSchedule for a hard guarantee
# labelSelector:
# matchLabels:
# app.kubernetes.io/name: socket-firewall
# # matchLabelKeys requires Kubernetes 1.27+
# # matchLabelKeys:
# # - pod-template-hash

# Extra containers to run alongside the firewall (e.g., auth sidecars, log collectors)
extraContainers: []
# - name: auth-sidecar
Expand Down