Cloud network context
Enrich network flow data with cloud provider region, zone, and subnet information.
Cloud network context enriches your network flow data with cloud provider context — VPC/subnet CIDRs, regions, availability zones, peering connections, and Transit Gateway topology. This lets you attribute network flows to specific cloud regions and availability zones for cost analysis and security visibility.
Overview
When enabled, Kvisor's cloud state controller:
- Fetches VPC network state from your cloud provider API on startup
- Caches network state and refreshes it periodically (default: every hour)
- Fetches cloud service IP ranges (e.g.
ip-ranges.amazonaws.com) separately — no auth needed, refreshed daily - Enriches network flow events with VPC context (region, zone, subnet info)
IP lookup uses a three-tier priority: static mappings > cloud-discovered > public cloud service ranges.
WarningZone information is not available for GCP. GCP subnets are regional, not zonal, so enriched flows will include region but not availability zone.
Before you start
- Kvisor installed and running (Installation guide)
- Network traffic monitoring enabled (
--set agent.extraArgs.netflow-enabled=true) — see Configuring Kvisor features - Your cloud provider VPC ID (AWS) or VPC network name (GCP)
- IAM permissions configured (see below)
Configure cloud provider authentication
IAM policy
Create an IAM policy with the following permissions. This covers VPC, peering, and Transit Gateway discovery. If you don't use Transit Gateway, the TransitGateway* and SearchTransitGatewayRoutes actions are unused but harmless.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:DescribeVpcs",
"ec2:DescribeSubnets",
"ec2:DescribeVpcPeeringConnections",
"ec2:DescribeTransitGatewayAttachments",
"ec2:DescribeTransitGatewayPeeringAttachments",
"ec2:DescribeTransitGatewayRouteTables",
"ec2:SearchTransitGatewayRoutes"
],
"Resource": "*"
}
]
}Option 1: IRSA — IAM roles for service accounts (recommended)
Step 1: Create IAM policy
export AWS_REGION="us-east-1"
export POLICY_NAME="KvisorVPCReaderPolicy"
cat > kvisor-vpc-policy.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:DescribeVpcs",
"ec2:DescribeSubnets",
"ec2:DescribeVpcPeeringConnections",
"ec2:DescribeTransitGatewayAttachments",
"ec2:DescribeTransitGatewayPeeringAttachments",
"ec2:DescribeTransitGatewayRouteTables",
"ec2:SearchTransitGatewayRoutes"
],
"Resource": "*"
}
]
}
EOF
aws iam create-policy \
--policy-name ${POLICY_NAME} \
--policy-document file://kvisor-vpc-policy.json \
--region ${AWS_REGION}Step 2: Create IAM role for service account
Option A: Use existing Kvisor service account (recommended)
export CLUSTER_NAME="your-eks-cluster"
export NAMESPACE="castai-agent"
export SERVICE_ACCOUNT_NAME="castai-kvisor-controller"
export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
eksctl utils associate-iam-oidc-provider \
--cluster ${CLUSTER_NAME} --region ${AWS_REGION} --approve
export OIDC_PROVIDER=$(aws eks describe-cluster --name ${CLUSTER_NAME} --region ${AWS_REGION} \
--query "cluster.identity.oidc.issuer" --output text | sed -e "s/^https:\/\///")
cat > trust-policy.json <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::${AWS_ACCOUNT_ID}:oidc-provider/${OIDC_PROVIDER}"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"${OIDC_PROVIDER}:sub": "system:serviceaccount:${NAMESPACE}:${SERVICE_ACCOUNT_NAME}",
"${OIDC_PROVIDER}:aud": "sts.amazonaws.com"
}
}
}
]
}
EOF
aws iam create-role \
--role-name KvisorVPCReaderRole \
--assume-role-policy-document file://trust-policy.json
aws iam attach-role-policy \
--role-name KvisorVPCReaderRole \
--policy-arn arn:aws:iam::${AWS_ACCOUNT_ID}:policy/${POLICY_NAME}
export ROLE_ARN=$(aws iam get-role --role-name KvisorVPCReaderRole --query 'Role.Arn' --output text)
echo "Role ARN: ${ROLE_ARN}"Option B: Let eksctl create a new service account
export CLUSTER_NAME="your-eks-cluster"
export NAMESPACE="castai-agent"
export SERVICE_ACCOUNT_NAME="castai-kvisor-controller"
export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
eksctl utils associate-iam-oidc-provider \
--cluster ${CLUSTER_NAME} --region ${AWS_REGION} --approve
eksctl create iamserviceaccount \
--cluster=${CLUSTER_NAME} \
--namespace=${NAMESPACE} \
--name=${SERVICE_ACCOUNT_NAME} \
--attach-policy-arn=arn:aws:iam::${AWS_ACCOUNT_ID}:policy/${POLICY_NAME} \
--region=${AWS_REGION} \
--approveStep 3: Configure Helm
For Option A (existing service account):
controller:
extraArgs:
cloud-provider: aws
cloud-provider-vpc-sync-enabled: true
cloud-provider-vpc-name: "vpc-0123456789abcdef0"
serviceAccount:
create: true
annotations:
eks.amazonaws.com/role-arn: "arn:aws:iam::YOUR_ACCOUNT_ID:role/KvisorVPCReaderRole"For Option B (eksctl-created service account), set serviceAccount.create: false.
Option 2: IAM user with access keys
Step 1: Create IAM user and policy
export USER_NAME="kvisor-vpc-reader"
export POLICY_NAME="KvisorVPCReaderPolicy"
export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
cat > kvisor-vpc-policy.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:DescribeVpcs",
"ec2:DescribeSubnets",
"ec2:DescribeVpcPeeringConnections",
"ec2:DescribeTransitGatewayAttachments",
"ec2:DescribeTransitGatewayPeeringAttachments",
"ec2:DescribeTransitGatewayRouteTables",
"ec2:SearchTransitGatewayRoutes"
],
"Resource": "*"
}
]
}
EOF
aws iam create-policy --policy-name ${POLICY_NAME} --policy-document file://kvisor-vpc-policy.json
aws iam create-user --user-name ${USER_NAME}
aws iam attach-user-policy --user-name ${USER_NAME} \
--policy-arn arn:aws:iam::${AWS_ACCOUNT_ID}:policy/${POLICY_NAME}
aws iam create-access-key --user-name ${USER_NAME} > access-key.jsonStep 2: Create Kubernetes secret
export AWS_ACCESS_KEY_ID=$(jq -r '.AccessKey.AccessKeyId' access-key.json)
export AWS_SECRET_ACCESS_KEY=$(jq -r '.AccessKey.SecretAccessKey' access-key.json)
kubectl create secret generic aws-credentials \
--from-literal=AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} \
--from-literal=AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} \
--namespace castai-agent
rm access-key.jsonStep 3: Configure Helm
controller:
extraArgs:
cloud-provider: aws
cloud-provider-vpc-sync-enabled: true
cloud-provider-vpc-name: "vpc-0123456789abcdef0"
envFrom:
- secretRef:
name: aws-credentials
AWS zone IDsBy default, Kvisor uses zone names (e.g.
us-east-1a). AWS also exposes zone IDs (e.g.use1-az1) which are consistent across accounts — useful for correct cross-account traffic attribution. Enable with--set controller.extraArgs.cloud-provider-aws-use-zone-id=true.When this flag is enabled, the
zonefield in any static CIDR mappings must also use zone IDs (e.g.use1-az1), not zone names.
Azure supportAzure cloud network context is coming soon.
Enable VPC state discovery
After configuring authentication, enable VPC state discovery with Helm:
helm upgrade castai-kvisor castai-helm/castai-kvisor -n castai-agent \
--reset-then-reuse-values \
--set controller.extraArgs.cloud-provider=aws \
--set controller.extraArgs.cloud-provider-aws-region=<your-aws-region> \
--set controller.extraArgs.cloud-provider-vpc-sync-enabled=true \
--set controller.extraArgs.cloud-provider-vpc-name=<your-vpc-id>Helm flags reference
| Flag | Description | Default |
|---|---|---|
cloud-provider | Cloud provider: aws or gcp | — |
cloud-provider-vpc-sync-enabled | Enable VPC state discovery | false |
cloud-provider-vpc-name | VPC ID (AWS) or network name (GCP) | — |
cloud-provider-vpc-sync-interval | How often to refresh VPC state | 1h |
cloud-provider-vpc-cache-size | LRU cache size for IP lookups | 10000 |
cloud-provider-gcp-project-id | GCP project ID (GCP only) | — |
cloud-provider-aws-region | AWS region of the cluster. Recommended; required for correct TGW region attribution (AWS only) | — |
cloud-provider-aws-use-zone-id | Use zone IDs (use1-az1) instead of zone names (us-east-1a) (AWS only) | false |
cloud-provider-aws-cross-account-role | Cross-account role ARN template (see Transit Gateway discovery) | — |
cloud-provider-public-cidrs-sync-interval | How often to refresh public cloud service IP ranges | 24h |
cloud-provider-static-cidrs-file | Path to static CIDR YAML (see Static CIDR mappings) | — |
All flags are set under controller.extraArgs in the Helm values.
Advanced configuration
AWS Transit Gateway discovery (AWS only)
When your cluster VPC is connected to other VPCs via AWS Transit Gateway, Kvisor can automatically discover remote VPCs and their CIDR ranges — removing the need for manual static CIDR mappings for TGW-connected networks.
How it works
- Kvisor discovers Transit Gateway attachments for the cluster VPC
- For each TGW, it enumerates all VPC and peering attachments (including remote accounts)
- It queries TGW route tables to discover destination CIDRs per attachment
- If a cross-account role ARN template is configured, Kvisor assumes that role via STS to fetch subnet-level detail (zone, zone ID) from remote accounts
- For TGW peering attachments, it resolves the peer TGW's region and discovers VPCs behind it
┌──────────────────────────────────────────────────────┐
│ Same Region (e.g. us-east-1) │
│ │
┌─────────────────┐ │ ┌──────────────────┐ ┌─────────────────┐ │
│ Cluster VPC │ │ │ Transit Gateway │ │ Remote VPC A │ │
│ Account 111 │─────┼────▶│ │◀────│ Account 222 │ │
│ 10.0.0.0/16 │ │ │ TGW route table │ │ 10.1.0.0/16 │ │
└─────────────────┘ │ │ has CIDR routes │ └─────────────────┘ │
│ │ for all attached │ │
│ │ VPCs │ ┌─────────────────┐ │
│ │ │◀────│ Remote VPC B │ │
│ └──────────────────┘ │ Account 333 │ │
│ │ 10.2.0.0/16 │ │
│ └─────────────────┘ │
└──────────────────────────────────────────────────────┘Basic setup (route-level CIDRs only)
TGW discovery works automatically when VPC sync is enabled — no extra flags needed. Kvisor discovers TGW-attached VPCs and indexes their route-table CIDRs (region attribution, no per-AZ detail).
Requires the IAM permissions from the AWS authentication section (the DescribeTransitGateway* and SearchTransitGatewayRoutes actions).
controller:
extraArgs:
cloud-provider: aws
cloud-provider-aws-region: "us-east-1"
cloud-provider-vpc-sync-enabled: true
cloud-provider-vpc-name: "vpc-0123456789abcdef0"
ImportantSet
cloud-provider-aws-regionto your cluster's AWS region. This is used as the region for discovered TGW VPCs and for targeting cross-account API calls. Without it, discovered VPCs may have no region attribution in netflow records.
Cross-account subnet discovery (optional)
For full subnet-level detail (zone names and zone IDs), configure a cross-account IAM role that Kvisor can assume in each remote account.
Step 1: Create IAM role in each remote account
Permissions policy (minimal — only needs to read subnets and TGW attachments):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:DescribeSubnets",
"ec2:DescribeTransitGatewayAttachments"
],
"Resource": "*"
}
]
}Trust policy (allows the Kvisor role in the cluster account to assume this role):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::CLUSTER_ACCOUNT_ID:role/KvisorVPCReaderRole"
},
"Action": "sts:AssumeRole"
}
]
}Create the role using the AWS CLI (run once per remote account):
export CLUSTER_ACCOUNT_ID="123456789012"
cat > trust-policy.json <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::${CLUSTER_ACCOUNT_ID}:role/KvisorVPCReaderRole"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
cat > permissions-policy.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:DescribeSubnets",
"ec2:DescribeTransitGatewayAttachments"
],
"Resource": "*"
}
]
}
EOF
aws iam create-role \
--role-name KvisorCrossAccountReader \
--assume-role-policy-document file://trust-policy.json \
--description "Allows kvisor in cluster account to read subnet info for TGW discovery"
REMOTE_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
aws iam create-policy \
--policy-name KvisorCrossAccountReaderPolicy \
--policy-document file://permissions-policy.json
aws iam attach-role-policy \
--role-name KvisorCrossAccountReader \
--policy-arn arn:aws:iam::${REMOTE_ACCOUNT_ID}:policy/KvisorCrossAccountReaderPolicy
rm trust-policy.json permissions-policy.jsonStep 2: Add sts:AssumeRole to the cluster account role
The Kvisor role in the cluster account needs permission to assume the remote roles:
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::*:role/KvisorCrossAccountReader"
}Using the AWS CLI (run in the cluster account):
cat > assume-policy.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::*:role/KvisorCrossAccountReader"
}
]
}
EOF
aws iam put-role-policy \
--role-name KvisorVPCReaderRole \
--policy-name KvisorCrossAccountAssumePolicy \
--policy-document file://assume-policy.json
rm assume-policy.jsonStep 3: Configure the cross-account role ARN template
Add the cloud-provider-aws-cross-account-role flag. Kvisor replaces {account-id} with the actual AWS account ID of each remote VPC discovered via the Transit Gateway.
controller:
extraArgs:
cloud-provider: aws
cloud-provider-aws-region: "us-east-1"
cloud-provider-vpc-sync-enabled: true
cloud-provider-vpc-name: "vpc-0123456789abcdef0"
cloud-provider-aws-cross-account-role: "arn:aws:iam::{account-id}:role/KvisorCrossAccountReader"Fallback behavior
| Configuration | What you get |
|---|---|
| VPC sync only (no cross-account role) | TGW route CIDRs, region only. Auto-discovers connected VPCs but no AZ detail. |
| VPC sync + cross-account role | Subnet CIDRs, zone name + zone ID. Full zone-aware network mapping. |
| Role configured but STS fails | Falls back to TGW route CIDRs (logged as warning, non-fatal). |
Static CIDR mappings
Static CIDR mappings let you manually annotate IP ranges that are not automatically discovered, such as on-premises networks, VPN endpoints, or cross-account VPCs without Transit Gateway connectivity.
Static entries take highest priority in IP lookups — a static /32 will override a broader cloud-discovered subnet.
When to use
- The destination is in another account or cloud and not reachable via the cloud API
- You use Direct Connect, Site-to-Site VPN, or other connectivity not automatically discovered
- You have managed services (RDS, Cloud SQL, etc.) with known private IPs
- Cloud provider API discovery is not enabled
Configuration via Helm values
Add staticCIDRs under controller.netflow in your values.yaml. Helm creates a ConfigMap and mounts it automatically when the list is non-empty:
controller:
netflow:
staticCIDRs:
mappings:
- cidr: "10.1.0.0/24"
zone: "us-east-1a"
region: "us-east-1"
name: "production-vpc"
kind: "VPC"
connectivityMethod: "TransitGateway"
- cidr: "10.0.0.13/32"
zone: "us-east-1a"
region: "us-east-1"
name: "production-rds"
kind: "RDS"
connectivityMethod: "PrivateLink"
- cidr: "10.2.0.0/16"
region: "us-east-1"
name: "peered-vpc"
kind: "VPC"
connectivityMethod: "VPCPeering"Mapping fields
| Field | Required | Description |
|---|---|---|
cidr | Yes | IP range in CIDR notation (e.g. 10.1.0.0/24) or single IP (10.0.0.1/32) |
region | Yes | Cloud region (e.g. us-east-1 for AWS, us-central1 for GCP) |
zone | No | Availability zone (e.g. us-east-1a for AWS, us-central1-a for GCP). Leave empty for regional ranges. For AWS, when cloud-provider-aws-use-zone-id: true is set, use zone IDs here (e.g. use1-az1) |
name | No | Human-readable name (e.g. production-rds, cloud-sql-instance) |
kind | No | Resource type (e.g. VPC, RDS, CloudSQL, OnPrem) |
connectivityMethod | No | Connectivity label — see values below |
connectivityMethod values
This field is optional and free-form. Recommended values for common networking paths:
| Value | Description | Provider |
|---|---|---|
VPCPeering | VPC Peering / VPC Network Peering | AWS, GCP |
TransitGateway | AWS Transit Gateway | AWS |
PrivateLink | AWS PrivateLink / VPC Endpoints | AWS |
DirectConnect | AWS Direct Connect | AWS |
SiteToSiteVPN | AWS Site-to-Site VPN | AWS |
CloudVPN | GCP Cloud VPN | GCP |
CloudInterconnect | GCP Cloud Interconnect | GCP |
NATGateway | NAT Gateway / Cloud NAT | AWS, GCP |
IntraVPC | Same VPC / same VPC network | AWS, GCP |
Custom values (e.g. "on-prem-mpls", "ExpressRoute") are passed through as-is in netflow records.
NoteKvisor does not perform any cost calculations. The
connectivityMethodfield is purely a label attached to netflow records so that downstream systems (e.g. Cast AI cost attribution) can distinguish traffic by connectivity type.
Using a pre-existing ConfigMap
If you manage your own ConfigMap (e.g. via GitOps):
controller:
extraArgs:
cloud-provider-static-cidrs-file: /etc/kvisor/my-cidrs/static-cidrs.yaml
extraVolumes:
- name: my-static-cidrs
configMap:
name: my-existing-configmap
extraVolumeMounts:
- name: my-static-cidrs
mountPath: /etc/kvisor/my-cidrs
readOnly: trueThe ConfigMap file must be valid YAML:
staticCIDRMappings:
- cidr: "10.1.0.0/24"
zone: "us-east-1a"
region: "us-east-1"
name: "production-vpc"
kind: "VPC"
connectivityMethod: "TransitGateway"
- cidr: "10.128.0.0/20"
region: "us-central1"
name: "gcp-subnet"
kind: "VPC"
connectivityMethod: "VPCPeering"Combining with cloud discovery
Static mappings and cloud provider discovery work together. Static entries always win for the same IP:
controller:
extraArgs:
cloud-provider: aws
cloud-provider-vpc-sync-enabled: true
cloud-provider-vpc-name: "vpc-0123456789abcdef0"
netflow:
staticCIDRs:
mappings:
# This /32 overrides whatever the cloud API says about 10.0.0.13
- cidr: "10.0.0.13/32"
region: "us-east-1"
name: "cross-account-rds"
connectivityMethod: "TransitGateway"Verify the configuration
Check that VPC state sync is working:
kubectl logs -l app.kubernetes.io/name=castai-kvisor-controller -n castai-agent | grep -i "vpc\|cloud"Look for log messages indicating successful VPC state sync, such as:
- VPC state loaded with subnet/CIDR counts
- Cloud service IP ranges fetched successfully
If Transit Gateway discovery is enabled, you should also see logs about TGW attachments and route table entries being indexed.
Troubleshooting
VPC sync not starting
Verify the cloud provider flags are set:
kubectl get deployment castai-kvisor-controller -n castai-agent -o yaml | grep -A 20 "args"Ensure cloud-provider-vpc-sync-enabled is true and cloud-provider-vpc-name is set.
Authentication errors
Check controller logs for credential errors:
kubectl logs -l app.kubernetes.io/name=castai-kvisor-controller -n castai-agent | grep -i "error\|auth\|credential\|forbidden"Common causes:
- IRSA: Service account annotation missing or OIDC provider not configured
- Access keys: Secret not created in the correct namespace (
castai-agent) - Insufficient permissions: IAM policy missing required
ec2:Describe*actions
Cross-account role assumption failing (AWS only)
If TGW cross-account discovery falls back to route-level CIDRs, check for STS errors:
kubectl logs -l app.kubernetes.io/name=castai-kvisor-controller -n castai-agent | grep -i "assume\|sts"Verify the trust policy in the remote account allows the Kvisor role ARN and that the cluster account role has sts:AssumeRole permission.
Updated about 6 hours ago
