Helm¶
The chart in deploy/helm/vpcatlas/ runs the
dashboard as one pod, like the plain manifests, and turns the settings into values. With no values
it renders the same pod: one replica, non-root, a read-only root file system, probes on /healthz, an emptyDir of
256 MiB at /tmp, no Kubernetes API token, and the password read from a Secret. The manifests stay for people who
prefer plain kubectl.
The chart version and the app version are the package version of the release (0.5.0 is the first release with a chart), and the image
tag defaults to the app version, so helm install with a given chart version runs the matching image.
Install¶
The chart is an OCI artifact on GitHub Container Registry. Create the namespace and the password Secret first, then install a version:
kubectl create namespace vpcatlas
kubectl create secret generic vpcatlas-auth --namespace vpcatlas --from-literal=password='a-long-random-password'
helm install atlas oci://ghcr.io/karina-pp/charts/vpcatlas --version 0.5.0 --namespace vpcatlas
Open the dashboard through a port-forward, and check the release from inside the cluster:
kubectl port-forward --namespace vpcatlas svc/atlas-vpcatlas 8080:8080
helm test atlas --namespace vpcatlas
The service is named <release>-vpcatlas, or just the release name when it already contains vpcatlas. helm test
starts a pod with a pinned curl image that requests /healthz, which needs no password, and fails otherwise.
Every value is documented in values.yaml
and in the table of the chart README.
Print the defaults of a version with helm show values oci://ghcr.io/karina-pp/charts/vpcatlas --version 0.5.0.
The values that matter¶
The IAM role (IRSA)¶
On EKS the annotation on the service account maps it to the IAM role that carries the auditor policy. The role and
its trust policy are the ones in the Kubernetes page; the trust policy names
system:serviceaccount:<namespace>:<service account>, and the service account is called after the service.
serviceAccount:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/vpcatlas-auditor
To audit other accounts, list their roles. The role of the pod needs sts:AssumeRole on them, and each one a trust
policy (cross-account use):
config:
roleArns:
- arn:aws:iam::210987654321:role/network-auditor
externalIdSecret:
name: vpcatlas-external-id
key: external-id
config.externalId also exists, but its value is visible in the Deployment; the Secret wins when both are set.
Outside EKS the pod needs credentials some other way: put the standard AWS_* variables in a Secret and pass it with
extraEnvFrom. The chart never stores an access key.
The password¶
auth.enabled is on by default. With auth.password empty the pod reads the password from the Secret
auth.existingSecret.name (vpcatlas-auth, key password) with optional: false: if the Secret does not exist the
pod stays in CreateContainerConfigError instead of starting open, the same as the raw manifests. With
auth.password set the chart creates the Secret <fullname>-auth and uses it, but then the password sits in the
release values; use that for a trial. The pod then also carries the annotation checksum/auth, a SHA-256 of the
rendered Secret, so a new password restarts it: anyone who can read the pod can try passwords against that hash
offline, which a long random password defeats.
With auth.enabled: false there is no VPCATLAS_AUTH_PASSWORD, the panel is open, and the chart prints a warning in
its notes. Without a password the panel checks the Host header, so the chart sets VPCATLAS_ALLOWED_HOSTS from
config.allowedHosts and the host of every ingress rule. Keep such an install on a private network.
S3 snapshots¶
The snapshot lives in the memory of the pod. To keep the history across restarts, point it at S3 and add the access to the role (the permissions are listed in the Kubernetes page):
config:
snapshotUri: s3://my-vpcatlas-snapshots/atlas
Several replicas would each scan on their own, so replicaCount stays at 1.
Ingress and base path¶
The service is ClusterIP. Put a private ingress in front of it and terminate TLS there:
ingress:
enabled: true
className: alb-internal
hosts:
- host: tools.example.internal
paths:
- path: /atlas
pathType: Prefix
tls:
- secretName: tools-example-internal-tls
hosts:
- tools.example.internal
config:
basePath: /atlas
When the dashboard is served below a path, set config.basePath to the prefix the browser sees (see
the dashboard page); the notes of the chart remind you when an ingress
path is not /. Check one page and one download through the ingress.
Persistence¶
persistence:
enabled: true
size: 1Gi
The chart creates a claim (or uses persistence.existingClaim), mounts it at /var/lib/vpcatlas and, with
config.snapshotUri empty, sets VPCATLAS_SNAPSHOT_URI=file:///var/lib/vpcatlas. The volume is ReadWriteOnce, so
the Deployment switches to the Recreate strategy: the old pod stops before the new one starts.
Network policy¶
networkPolicy:
enabled: true
ingress:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
The policy allows port 8080 from the peers in networkPolicy.ingress (any pod in the cluster when the list is empty),
DNS on port 53 and HTTPS on port 443 to anywhere, which the AWS APIs need. networkPolicy.egress.extra adds rules,
for example port 80 to 169.254.169.254 for a pod that reads credentials from the instance metadata service. It
takes effect only if the cluster's network plugin enforces policies. The pod of helm test runs in the release
namespace, so it must be allowed by the ingress peers too.
Verify the chart¶
Each release publishes the chart in two places, and the release workflow checks the first one after the push:
- The OCI artifact
oci://ghcr.io/karina-pp/charts/vpcatlas, signed with cosign (keyless: the identity is the release workflow) and attested. - The
.tgz(vpcatlas-<version>.tgz) attached to the GitHub release.
Check the signature and the attestation before you install. Verifying the signature needs cosign 3 or newer:
cosign verify ghcr.io/karina-pp/charts/vpcatlas:0.5.0 \
--certificate-identity-regexp '^https://github.com/karina-pp/vpcatlas/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
gh attestation verify oci://ghcr.io/karina-pp/charts/vpcatlas:0.5.0 --owner karina-pp
Upgrade and uninstall¶
helm upgrade atlas oci://ghcr.io/karina-pp/charts/vpcatlas --version <new-version> --namespace vpcatlas \
--reuse-values
helm uninstall atlas --namespace vpcatlas
--reuse-values keeps what you set and ignores new defaults; pass -f values.yaml instead if you keep your values in
a file. The password Secret that you created yourself, and a claim from persistence.existingClaim, are not removed
by helm uninstall. The claim the chart created is removed with the release, and so is the history on it.
What it renders¶
The objects are the ones of the Kubernetes page, made from values: a ServiceAccount (with
automountServiceAccountToken: false), a Deployment, a ClusterIP Service, and, only when asked, a Secret
for the password, a ConfigMap for config.accountNames (mounted read-only at
/etc/vpcatlas/account-names.json), an Ingress, a PersistentVolumeClaim and a NetworkPolicy. A pod with a
changed ConfigMap or chart-made Secret restarts on the next upgrade, because the Deployment carries a checksum of
them. Every config key becomes a VPCATLAS_* variable and is rendered only when it is set.
The chart is linted and rendered on every change, and installed and upgraded in a throw-away kind cluster with
the image built from the same commit (see engineering.md).