diff --git a/config/configStructs/tapConfig.go b/config/configStructs/tapConfig.go index e49da8f9d..c1a85af99 100644 --- a/config/configStructs/tapConfig.go +++ b/config/configStructs/tapConfig.go @@ -310,11 +310,22 @@ type RawCaptureConfig struct { StorageSize string `yaml:"storageSize" json:"storageSize" default:"1Gi"` } -type SnapshotsConfig struct { +type SnapshotsLocalConfig struct { StorageClass string `yaml:"storageClass" json:"storageClass" default:""` StorageSize string `yaml:"storageSize" json:"storageSize" default:"20Gi"` } +type SnapshotsCloudConfig struct { + Provider string `yaml:"provider" json:"provider" default:""` + ConfigMaps []string `yaml:"configMaps" json:"configMaps" default:"[]"` + Secrets []string `yaml:"secrets" json:"secrets" default:"[]"` +} + +type SnapshotsConfig struct { + Local SnapshotsLocalConfig `yaml:"local" json:"local"` + Cloud SnapshotsCloudConfig `yaml:"cloud" json:"cloud"` +} + type DelayedDissectionConfig struct { CPU string `yaml:"cpu" json:"cpu" default:"1"` Memory string `yaml:"memory" json:"memory" default:"4Gi"` diff --git a/helm-chart/README.md b/helm-chart/README.md index 390214b12..272e47dd7 100644 --- a/helm-chart/README.md +++ b/helm-chart/README.md @@ -143,8 +143,11 @@ Example for overriding image names: | `tap.capture.raw.enabled` | Enable raw capture of packets and syscalls to disk for offline analysis | `true` | | `tap.capture.raw.storageSize` | Maximum storage size for raw capture files (supports K8s quantity format: `1Gi`, `500Mi`, etc.) | `1Gi` | | `tap.capture.dbMaxSize` | Maximum size for capture database (e.g., `4Gi`, `2000Mi`). When empty, automatically uses 80% of allocated storage (`tap.storageLimit`). | `""` | -| `tap.snapshots.storageClass` | Storage class for snapshots volume. When empty, uses `emptyDir`. When set, creates a PVC with this storage class | `""` | -| `tap.snapshots.storageSize` | Storage size for snapshots volume (supports K8s quantity format: `1Gi`, `500Mi`, etc.) | `10Gi` | +| `tap.snapshots.local.storageClass` | Storage class for local snapshots volume. When empty, uses `emptyDir`. When set, creates a PVC with this storage class | `""` | +| `tap.snapshots.local.storageSize` | Storage size for local snapshots volume (supports K8s quantity format: `1Gi`, `500Mi`, etc.) | `20Gi` | +| `tap.snapshots.cloud.provider` | Cloud storage provider for snapshots: `s3` or `azblob`. Empty string disables cloud storage. See [Cloud Storage docs](docs/snapshots_cloud_storage.md). | `""` | +| `tap.snapshots.cloud.configMaps` | Names of ConfigMaps containing cloud storage environment variables. See [Cloud Storage docs](docs/snapshots_cloud_storage.md). | `[]` | +| `tap.snapshots.cloud.secrets` | Names of Secrets containing cloud storage credentials. See [Cloud Storage docs](docs/snapshots_cloud_storage.md). | `[]` | | `tap.release.repo` | URL of the Helm chart repository | `https://helm.kubeshark.com` | | `tap.release.name` | Helm release name | `kubeshark` | | `tap.release.namespace` | Helm release namespace | `default` | diff --git a/helm-chart/docs/snapshots_cloud_storage.md b/helm-chart/docs/snapshots_cloud_storage.md new file mode 100644 index 000000000..11ccb2775 --- /dev/null +++ b/helm-chart/docs/snapshots_cloud_storage.md @@ -0,0 +1,226 @@ +# Cloud Storage for Snapshots + +Kubeshark can upload and download snapshots to cloud object storage, enabling cross-cluster sharing, backup/restore, and long-term retention. + +Supported providers: **Amazon S3** (`s3`) and **Azure Blob Storage** (`azblob`). + +## Helm Values + +```yaml +tap: + snapshots: + cloud: + provider: "" # "s3" or "azblob" (empty = disabled) + configMaps: [] # names of pre-existing ConfigMaps with cloud config env vars + secrets: [] # names of pre-existing Secrets with cloud credentials +``` + +- `provider` selects which cloud backend to use. Leave empty to disable cloud storage. +- `configMaps` and `secrets` are lists of names of existing ConfigMap/Secret resources. They are mounted as `envFrom` on the hub pod, injecting all their keys as environment variables. + +--- + +## Amazon S3 + +### Environment Variables + +| Variable | Required | Description | +|----------|----------|-------------| +| `SNAPSHOT_AWS_BUCKET` | Yes | S3 bucket name | +| `SNAPSHOT_AWS_REGION` | No | AWS region (uses SDK default if empty) | +| `SNAPSHOT_AWS_ACCESS_KEY` | No | Static access key ID (empty = use default credential chain) | +| `SNAPSHOT_AWS_SECRET_KEY` | No | Static secret access key | +| `SNAPSHOT_AWS_ROLE_ARN` | No | IAM role ARN to assume via STS (for cross-account access) | +| `SNAPSHOT_AWS_EXTERNAL_ID` | No | External ID for the STS AssumeRole call | +| `SNAPSHOT_CLOUD_PREFIX` | No | Key prefix in the bucket (e.g. `snapshots/`) | + +### Authentication Methods + +Credentials are resolved in this order: + +1. **Static credentials** -- If `SNAPSHOT_AWS_ACCESS_KEY` is set, static credentials are used directly. +2. **STS AssumeRole** -- If `SNAPSHOT_AWS_ROLE_ARN` is also set, the static (or default) credentials are used to assume the given IAM role. This is useful for cross-account S3 access. +3. **AWS default credential chain** -- When no static credentials are provided, the SDK default chain is used: + - **IRSA** (EKS service account token) -- recommended for production on EKS + - EC2 instance profile + - Standard AWS environment variables (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, etc.) + - Shared credentials file (`~/.aws/credentials`) + +The provider validates bucket access on startup via `HeadBucket`. If the bucket is inaccessible, the hub will fail to start. + +### Example: IRSA (recommended for EKS) + +Create a ConfigMap with bucket configuration: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: kubeshark-s3-config +data: + SNAPSHOT_AWS_BUCKET: my-kubeshark-snapshots + SNAPSHOT_AWS_REGION: us-east-1 +``` + +Set Helm values: + +```yaml +tap: + snapshots: + cloud: + provider: "s3" + configMaps: + - kubeshark-s3-config +``` + +The hub pod's service account must be annotated for IRSA with an IAM role that has S3 access to the bucket. + +### Example: Static Credentials + +Create a Secret with credentials: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: kubeshark-s3-creds +type: Opaque +stringData: + SNAPSHOT_AWS_ACCESS_KEY: AKIA... + SNAPSHOT_AWS_SECRET_KEY: wJal... +``` + +Create a ConfigMap with bucket configuration: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: kubeshark-s3-config +data: + SNAPSHOT_AWS_BUCKET: my-kubeshark-snapshots + SNAPSHOT_AWS_REGION: us-east-1 +``` + +Set Helm values: + +```yaml +tap: + snapshots: + cloud: + provider: "s3" + configMaps: + - kubeshark-s3-config + secrets: + - kubeshark-s3-creds +``` + +### Example: Cross-Account Access via AssumeRole + +Add the role ARN to your ConfigMap: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: kubeshark-s3-config +data: + SNAPSHOT_AWS_BUCKET: other-account-bucket + SNAPSHOT_AWS_REGION: eu-west-1 + SNAPSHOT_AWS_ROLE_ARN: arn:aws:iam::123456789012:role/KubesharkCrossAccountRole + SNAPSHOT_AWS_EXTERNAL_ID: my-external-id # optional, if required by the trust policy +``` + +The hub will first authenticate using its own credentials (IRSA, static, or default chain), then assume the specified role to access the bucket. + +--- + +## Azure Blob Storage + +### Environment Variables + +| Variable | Required | Description | +|----------|----------|-------------| +| `SNAPSHOT_AZBLOB_STORAGE_ACCOUNT` | Yes | Azure storage account name | +| `SNAPSHOT_AZBLOB_CONTAINER` | Yes | Blob container name | +| `SNAPSHOT_AZBLOB_STORAGE_KEY` | No | Storage account access key (empty = use DefaultAzureCredential) | +| `SNAPSHOT_CLOUD_PREFIX` | No | Key prefix in the container (e.g. `snapshots/`) | + +### Authentication Methods + +Credentials are resolved in this order: + +1. **Shared Key** -- If `SNAPSHOT_AZBLOB_STORAGE_KEY` is set, the storage account key is used directly. +2. **DefaultAzureCredential** -- When no storage key is provided, the Azure SDK default credential chain is used: + - **Workload Identity** (AKS pod identity) -- recommended for production on AKS + - Managed Identity (system or user-assigned) + - Azure CLI credentials + - Environment variables (`AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, `AZURE_CLIENT_SECRET`) + +The provider validates container access on startup via `GetProperties`. If the container is inaccessible, the hub will fail to start. + +### Example: Workload Identity (recommended for AKS) + +Create a ConfigMap with storage configuration: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: kubeshark-azblob-config +data: + SNAPSHOT_AZBLOB_STORAGE_ACCOUNT: mykubesharksa + SNAPSHOT_AZBLOB_CONTAINER: snapshots +``` + +Set Helm values: + +```yaml +tap: + snapshots: + cloud: + provider: "azblob" + configMaps: + - kubeshark-azblob-config +``` + +The hub pod's service account must be configured for AKS Workload Identity with a managed identity that has the **Storage Blob Data Contributor** role on the container. + +### Example: Storage Account Key + +Create a Secret with the storage key: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: kubeshark-azblob-creds +type: Opaque +stringData: + SNAPSHOT_AZBLOB_STORAGE_KEY: "base64-encoded-storage-key..." +``` + +Create a ConfigMap with storage configuration: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: kubeshark-azblob-config +data: + SNAPSHOT_AZBLOB_STORAGE_ACCOUNT: mykubesharksa + SNAPSHOT_AZBLOB_CONTAINER: snapshots +``` + +Set Helm values: + +```yaml +tap: + snapshots: + cloud: + provider: "azblob" + configMaps: + - kubeshark-azblob-config + secrets: + - kubeshark-azblob-creds +``` diff --git a/helm-chart/templates/04-hub-deployment.yaml b/helm-chart/templates/04-hub-deployment.yaml index 60e14b054..6f897b2f9 100644 --- a/helm-chart/templates/04-hub-deployment.yaml +++ b/helm-chart/templates/04-hub-deployment.yaml @@ -61,12 +61,24 @@ spec: {{- end }} - -cloud-api-url - '{{ .Values.cloudApiUrl }}' - {{- if .Values.tap.secrets }} + {{- if .Values.tap.snapshots.cloud.provider }} + - -cloud-storage-provider + - '{{ .Values.tap.snapshots.cloud.provider }}' + {{- end }} + {{- if or .Values.tap.secrets .Values.tap.snapshots.cloud.configMaps .Values.tap.snapshots.cloud.secrets }} envFrom: {{- range .Values.tap.secrets }} - secretRef: name: {{ . }} {{- end }} + {{- range .Values.tap.snapshots.cloud.configMaps }} + - configMapRef: + name: {{ . }} + {{- end }} + {{- range .Values.tap.snapshots.cloud.secrets }} + - secretRef: + name: {{ . }} + {{- end }} {{- end }} env: - name: POD_NAME @@ -188,10 +200,10 @@ spec: - key: AUTH_SAML_X509_KEY path: kubeshark.key - name: snapshots-volume - {{- if .Values.tap.snapshots.storageClass }} + {{- if .Values.tap.snapshots.local.storageClass }} persistentVolumeClaim: claimName: {{ include "kubeshark.name" . }}-snapshots-pvc {{- else }} emptyDir: - sizeLimit: {{ .Values.tap.snapshots.storageSize }} + sizeLimit: {{ .Values.tap.snapshots.local.storageSize }} {{- end }} diff --git a/helm-chart/templates/09-snapshots-pvc.yaml b/helm-chart/templates/09-snapshots-pvc.yaml index c9270d347..a77f1e4f8 100644 --- a/helm-chart/templates/09-snapshots-pvc.yaml +++ b/helm-chart/templates/09-snapshots-pvc.yaml @@ -1,5 +1,5 @@ --- -{{- if .Values.tap.snapshots.storageClass }} +{{- if .Values.tap.snapshots.local.storageClass }} apiVersion: v1 kind: PersistentVolumeClaim metadata: @@ -16,7 +16,7 @@ spec: - ReadWriteOnce resources: requests: - storage: {{ .Values.tap.snapshots.storageSize }} - storageClassName: {{ .Values.tap.snapshots.storageClass }} + storage: {{ .Values.tap.snapshots.local.storageSize }} + storageClassName: {{ .Values.tap.snapshots.local.storageClass }} status: {} {{- end }} diff --git a/helm-chart/values.yaml b/helm-chart/values.yaml index e3096d077..00f5c8d50 100644 --- a/helm-chart/values.yaml +++ b/helm-chart/values.yaml @@ -38,8 +38,13 @@ tap: cpu: "1" memory: 4Gi snapshots: - storageClass: "" - storageSize: 20Gi + local: + storageClass: "" + storageSize: 20Gi + cloud: + provider: "" # cloud storage provider: "s3" (empty = disabled) + configMaps: [] # names of ConfigMaps with cloud storage env vars + secrets: [] # names of Secrets with cloud storage credentials release: repo: https://helm.kubeshark.com name: kubeshark