Skip to content
You are viewing the documentation for Solo Enterprise for Istio, formerly known as Gloo Mesh (OSS APIs).

Add EC2 instances to the mesh

EnterpriseAlpha
Page as Markdown

Onboard workloads that run on Amazon EC2 instances to your ambient mesh.

About

As you build your ambient mesh, you might want to add a workload that runs on an Amazon EC2 instance to your cluster environment. For example, you might run an app or service on EC2 that must communicate with services in the Istio ambient mesh of your Kubernetes cluster. Additionally, you might need to secure the connections between EC2 and in-cluster services with mTLS, and centrally manage routing and policies by using Istio APIs.

In the Solo distribution of Istio version 1.30 and later, you can extend the mesh to include workloads running on EC2 instances by leveraging the istioctl ec2 add-workload command. This command bootstraps the connection from the EC2 instance to istiod, and uses the EC2 instance’s IAM instance profile and the EC2 Instance Metadata Service (IMDS) for authentication. The EC2 workloads can then communicate with in-mesh services in your Kubernetes cluster as well as securely communicate over mTLS with other mesh workloads.

EC2 discovery is opt-in. To allow istiod to discover an EC2 instance, you tag the instance with ec2.solo.io/* metadata tags. The discovery controller in istiod auto-discovers tagged EC2 instances and automatically creates the WorkloadEntry, Service, and gateway WorkloadEntry resources to represent the instances in the mesh.

The following diagram shows the EKS and EC2 architecture, in which EKS services and EC2 workloads can communicate through mTLS-secured connections in your ambient mesh. The steps in this guide enable bi-directional communication, both from and to your EC2 workloads.

Figure: EC2 workload connecting to EKS cluster echo server via an mTLS-secured ambient mesh connection.
Figure: EC2 workload connecting to EKS cluster echo server via an mTLS-secured ambient mesh connection.

Version and license requirements

This feature requires your mesh to be installed with the Solo distribution of Istio and an Enterprise-level license for Solo Enterprise for Istio. Contact your account representative to obtain a valid license.

EC2 integration into an ambient mesh is an alpha feature in the Solo distribution of Istio 1.30 and later. Alpha features are likely to change, are not fully tested, and are not supported for production. For more information, see Solo feature maturity.

Platform considerations

The east-west gateway of your ambient mesh must be accessible to all EC2 instances that are integrated into the mesh. The easiest way to accomplish this setup is to run your ambient mesh in an Amazon Elastic Kubernetes Service (EKS) cluster and run EC2 instances in the same Virtual Private Cloud (VPC) as the EKS nodes. However, the cluster can be hosted anywhere as long as the east-west gateway is accessible to the EC2 instances.

Multi-region support

In Solo Enterprise for Istio version 1.30 and later, istiod supports discovering and managing EC2 instances across multiple AWS accounts and regions. You can configure specific regions for each AWS account, allowing your ambient mesh to span EC2 instances in different regions. If no regions are specified for an account, discovery operates on the default region of the configured AWS role.

Multi-workload instances

EC2 integration supports multi-workload instances, in which a single EC2 instance runs multiple distinct workloads. Each workload runs with its own identity. The ztunnel on the EC2 instance runs as a shared proxy with a SOCKS5 handler to route outbound traffic per workload identity.

Step 1: Set up tools

  1. Install the following CLI tools.

    • aws, the AWS command line tool.
    • eksctl, the CLI tool for creating and managing EKS clusters.
    • kubectl, the Kubernetes command line tool. Download the kubectl version that is within one minor version of the EKS cluster you plan to use.
    • helm, the Kubernetes package manager.
    • jq, a command-line JSON processor.
    • envsubst, a command-line tool for substituting environment variables in files. This tool is typically available via the gettext package, such as through brew install gettext on macOS, or apt-get install gettext-base on Ubuntu.
  2. Clone the doc-examples repository, which contains scripts and manifests for this guide, and navigate to the ec2 directory.

    git clone https://github.com/solo-io/doc-examples.git
    cd doc-examples/istio/ec2
  3. Get the Solo distribution of Istio binary and install istioctl, which you use for the ec2 add-workload command in this guide.

    1. Save the Solo distribution of Istio version and Helm repository location.

      export ISTIO_IMAGE=1.30.3-solo
      export HELM_REPO=us-docker.pkg.dev/soloio-img/istio-helm
    2. Download the Solo distribution of Istio binary and install istioctl. This script automatically detects your OS and architecture, downloads the appropriate Solo distribution of Istio binary, and verifies the installation.

      scripts/build/install-istioctl.sh
      export PATH=${HOME}/.istioctl/bin:${PATH}

Step 2: Set up the EKS cluster with Istio

Create IAM roles for Istio. Then, either create a new EKS cluster and deploy an Istio ambient mesh to the cluster, or update an existing EKS cluster where a Solo distribution of Istio ambient mesh runs. The ambient mesh setup must include an east-west gateway to facilitate traffic between the EKS cluster and EC2 instances.

  1. Save the following details in environment variables.

    • AWS_ACCOUNT: The ID of the AWS account that you want to use.
    • AWS_REGION: The AWS region for your EKS cluster. You can configure the regions for EC2 instance discovery later on.
    • CLUSTER_NAME: A name for the EKS cluster. If you already created a cluster where you installed the Solo distribution of Istio ambient mesh, use that cluster name. The cluster name must start with a letter or number and can contain only letters, numbers, hyphens, and underscores.
    export AWS_ACCOUNT=<ID>
    export AWS_REGION=us-east-1
    export CLUSTER_NAME=demo
  2. Create IAM permissions for istiod to read from the EC2 API, which allow istiod to perform automatic discovery of EC2 instances.

    The following script checks if the required IAM roles and policies already exist in your AWS account. If they do not exist, the script creates the following resources that are automatically associated with the istio-system/istiod Kubernetes service account when you create the EKS cluster:

    • istiod IAM role with permissions to read from the EC2 API to be assumed by EKS pods
    • istiod-ec2 IAM role that provides read-only access to the EC2 API
    • ec2-read-only IAM policy attached to the istiod-ec2 role
    • istiod-${AWS_ACCOUNT} IAM policy that allows the istiod role to assume the istiod-ec2 role
    scripts/build/iam-istiod.sh

    Example output:

    Starting creation of Istiod IAM roles and policies...
    Creating IAM role 'istiod'...
    Successfully created istiod role.
    Waiting for IAM role to propagate...
    Creating IAM role 'istiod-ec2'...
    Successfully created istiod-ec2 role.
    Creating IAM policy 'ec2-read-only'...
    Successfully created ec2-read-only policy.
    Successfully attached ec2-read-only policy to istiod-ec2 role.
    Creating IAM policy 'istiod-123456789012'...
    Successfully created istiod-123456789012 policy.
    Successfully attached istiod-123456789012 policy to istiod role.
    
    Istiod IAM setup completed successfully.
    Created resources:
      - IAM Role: istiod
      - IAM Role: istiod-ec2
      - IAM Policy: ec2-read-only (attached to istiod-ec2)
      - IAM Policy: istiod-123456789012 (attached to istiod)

    The istiod-ec2 role requires only the ec2:DescribeInstances permission. This guide requires that you create the EKS cluster and EC2 instances in the same account. For cross-account setups where EC2 instances run in a different AWS account than the EKS cluster, the istiod role also requires sts:AssumeRole permission to assume the istiod-ec2 role in each remote account. The sts:AssumeRole permission is not needed when the EKS cluster and EC2 instances are in the same account.

  3. Create or update an existing EKS cluster with a pod identity association that links the istio-system/istiod Kubernetes service account with the istiod IAM role. If you follow the steps to create an EKS cluster, you also deploy an ambient mesh into the cluster.

    Create an EKS cluster, deploy the Solo distribution of Istio ambient mesh, and create an east-west gateway.

    1. Set configuration details for the EKS cluster. Update the values as needed. For more information, see the EKS documentation.

      • OWNER_NAME: The name of the cluster owner.
      • EKS_VERSION: The EKS version for the cluster to run.
      • NUMBER_NODES: The number of nodes.
      • NODE_TYPE: The instance type for the nodes.
      export OWNER_NAME=$(whoami)
      export EKS_VERSION=1.33
      export NUMBER_NODES=2
      export NODE_TYPE="t2.medium"
    2. Set your Enterprise-level license for Solo Enterprise for Istio as an environment variable. If you do not have one, contact an account representative. If you prefer to specify license keys in a secret instead of an environment variable, see Licensing.

      export SOLO_ISTIO_LICENSE_KEY=<enterprise_license_key>
    3. Set the Solo distribution of Istio version and repository details.

      export ISTIO_IMAGE=1.30.3-solo
    4. Create an EKS cluster. This configuration includes a pod identity association that links the istio-system/istiod Kubernetes service account with the IAM role you created earlier. To review the configuration before applying it, open the manifests/eks-cluster.yaml file.

      envsubst < manifests/eks-cluster.yaml | eksctl create cluster --config-file -
    5. Apply the CRDs for the Kubernetes Gateway API to your cluster, which are required to create components such as waypoint proxies for L7 traffic policies, gateways with the Gateway resource, and more.

      kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.5.0/standard-install.yaml
    6. Install the Istio ambient mesh components in your EKS cluster. This script performs the following actions:

      • Installs the following Istio Helm charts:
        • The Istio base chart with CRDs and cluster roles required for the Istio control plane
        • The istiod control plane chart, configured for EC2 integration
        • The Istio CNI node agent daemonset
        • The ztunnel chart that deploys the ztunnel daemonset
      • Waits for each component to be ready before proceeding.
      • Labels the istio-system namespace with the cluster network name, which is set to your EKS cluster’s name in the global.network field of the istiod installation. The ambient mesh uses this label for multi-network routing.
      • Verifies all deployments and provides detailed progress output.

      To review the Helm values for each component before installing, open the files in the manifests/ directory.

      scripts/build/istio-ambient.sh

      Example output:

      Detected Istio 1.29 or later, using updated repository locations.
      =========================================
      Installing Istio ambient mesh components
      =========================================
      
      Cluster: demo
      Istio Version: 1.30.3-solo
      Helm Repository: us-docker.pkg.dev/soloio-img/istio-helm
      
      Step 1/4: Installing istio-base chart...
      ✓ istio-base installed successfully
      
      Step 2/4: Installing istiod control plane...
      ✓ istiod installed successfully
      Waiting for istiod to be ready...
      ✓ istiod is ready
      
      Step 3/4: Installing Istio CNI node agent daemonset...
      ✓ istio-cni installed successfully
      Waiting for CNI daemonset to be ready...
      ✓ istio-cni is ready
      
      Step 4/4: Installing ztunnel daemonset...
      ✓ ztunnel installed successfully
      Waiting for ztunnel daemonset to be ready...
      ✓ ztunnel is ready
      
      =========================================
      Verifying ambient mesh components
      =========================================
      
      NAME                      READY   STATUS    RESTARTS   AGE
      istio-cni-node-pr5rl      1/1     Running   0          45s
      istio-cni-node-pvmx2      1/1     Running   0          45s
      istiod-85c4dfd97f-mncj5   1/1     Running   0          90s
      ztunnel-vtpjm             1/1     Running   0          30s
      ztunnel-hllxg             1/1     Running   0          30s
      
      =========================================
      Labeling istio-system namespace
      =========================================
      
      Labeling istio-system namespace with network: demo...
      ✓ Namespace labeled successfully
      
      =========================================
      Installation complete!
      =========================================
      
      Istio ambient mesh has been successfully installed and configured.
      Next steps: Tag EC2 instances with ec2.solo.io/* labels to enable discovery.
    7. Create an east-west gateway in the istio-eastwest namespace. The east-west gateway facilitates traffic between services in the EKS cluster and EC2 instances.

      • Solo distribution of istioctl: For more information about this command, see the CLI reference.
        kubectl create namespace istio-eastwest
        istioctl multicluster expose --namespace istio-eastwest --generate > ew-gateway.yaml
        kubectl apply -f ew-gateway.yaml
      • Helm: For more information, see the Helm values reference. For recommendations on customizing the east-west gateway for resiliency and availability with the Helm chart, see the best practices for multicluster peering.
        helm upgrade -i peering-eastwest oci://${HELM_REPO}/peering \
          --version ${ISTIO_IMAGE} \
          --namespace istio-eastwest \
          --create-namespace \
          -f - <<EOF
        eastwest:
          create: true
          cluster: ${CLUSTER_NAME}
          # The network that the istio-system namespace is labeled with.
          # In prod environments, network and cluster are likely not the same value.
          network: ${CLUSTER_NAME}
          deployment: {}
        EOF
    8. Verify that the east-west gateway is successfully deployed.

      kubectl get pods -n istio-eastwest

      Example output:

      NAME                              READY   STATUS    RESTARTS   AGE
      istio-eastwest-5d4f757664-6hw7b   1/1     Running   0          9s

    If you already installed the Solo distribution of Istio ambient mesh in an existing EKS cluster, update that cluster with the pod identity association that links the istio-system/istiod Kubernetes service account with the istiod IAM role.

    1. Install the EKS Pod Identity Agent addon, which is required for pod identity associations to inject AWS credentials into pods. If the addon is already installed, this command returns an error that you can ignore.

      aws eks create-addon \
        --cluster-name ${CLUSTER_NAME} \
        --addon-name eks-pod-identity-agent \
        --region ${AWS_REGION}
      aws eks wait addon-active \
        --cluster-name ${CLUSTER_NAME} \
        --addon-name eks-pod-identity-agent \
        --region ${AWS_REGION}
    2. Add a pod identity association to link the istio-system/istiod service account with the istiod IAM role. This association allows istiod to assume the necessary IAM permissions for EC2 discovery. The following script checks if the pod identity association already exists. If it does not exist, the script creates it.

      scripts/build/add-pod-identity.sh

      Example output:

      =========================================
      Adding pod identity association for istiod
      =========================================
      
      Cluster: demo
      Namespace: istio-system
      Service Account: istiod
      IAM Role: arn:aws:iam::123456789012:role/istiod
      
      Checking for existing pod identity association...
      Creating new pod identity association...
      ✓ Pod identity association created successfully (ID: a-abcdef1234567890)
      
      =========================================
      Pod identity association setup complete!
      =========================================
      
      The istio-system/istiod service account can now assume the arn:aws:iam::123456789012:role/istiod role.
      This enables istiod to discover EC2 instances.
    3. Restart istiod so that the pod identity agent injects the AWS credential environment variables into the new pod.

      kubectl rollout restart deployment/istiod -n istio-system
      kubectl rollout status deployment/istiod -n istio-system
    4. Verify that AWS credentials were injected into the istiod pod. The AWS_CONTAINER_CREDENTIALS_FULL_URI environment variable must be present for istiod to authenticate with AWS and perform EC2 discovery.

      kubectl exec -n istio-system deployment/istiod -- env | grep AWS_CONTAINER_CREDENTIALS_FULL_URI

      If the environment variable is present, the pod identity agent is injecting credentials correctly. If it is absent, verify that the eks-pod-identity-agent addon is installed and all of its pods are running.

      aws eks describe-addon \
        --cluster-name ${CLUSTER_NAME} \
        --addon-name eks-pod-identity-agent \
        --region ${AWS_REGION} \
        --query 'addon.status'
      kubectl get pods -n kube-system -l app.kubernetes.io/name=eks-pod-identity-agent
    5. Update your istiod Helm installation to enable EC2 instance discovery. The --reuse-values flag preserves your existing Helm values and applies only the EC2 platform configuration.

      helm upgrade istiod oci://${HELM_REPO}/istiod \
        -n istio-system \
        --reuse-values \
        --version ${ISTIO_IMAGE} \
        -f - <<EOF
      platforms:
        aws:
          ec2Discovery: true
          accounts:
          - role: arn:aws:iam::${AWS_ACCOUNT}:role/istiod-ec2
      EOF
    6. Before continuing to the next section, verify that an east-west gateway exists in the cluster.

      kubectl get pods -n istio-eastwest

      If no east-west gateway exists, use the following commands to create one in the istio-eastwest namespace. The east-west gateway facilitates traffic between services in the EKS cluster and EC2 instances.

      • Solo distribution of istioctl: For more information about this command, see the CLI reference.
        kubectl create namespace istio-eastwest
        istioctl multicluster expose --namespace istio-eastwest --generate > ew-gateway.yaml
        kubectl apply -f ew-gateway.yaml
      • Helm: For more information, see the Helm values reference. For recommendations on customizing the east-west gateway for resiliency and availability with the Helm chart, see the best practices for multicluster peering.
        helm upgrade -i peering-eastwest oci://${HELM_REPO}/peering \
          --version ${ISTIO_IMAGE} \
          --namespace istio-eastwest \
          --create-namespace \
          -f - <<EOF
        eastwest:
          create: true
          cluster: ${CLUSTER_NAME}
          # The network that the istio-system namespace is labeled with.
          # In prod environments, network and cluster are likely not the same value.
          network: ${CLUSTER_NAME}
          deployment: {}
        EOF

Step 3: Set up the EC2 instance

On each EC2 instance that you want to add to the mesh, ensure that the security group allows HBONE traffic, that an IAM instance profile is attached, and that the ztunnel binary is installed.

  1. Save the EC2 instance ID for the instance you want to add to the mesh.

    export EC2_INSTANCE_ID=<instance-id>
  2. Configure the security group rules required for HBONE traffic (port 15008) in both directions. The following script checks whether the rules already exist and adds them if not:

    • EC2 instance security group: Allows inbound port 15008 from the VPC CIDR so that EKS ztunnel can send encrypted HBONE traffic to the EC2 instance.
    • EKS node security group: Allows inbound port 15008 from the VPC CIDR so that the EC2 ztunnel can send encrypted HBONE traffic to EKS pods.
      For this demo, the security group settings are relaxed to simplify testing and ensure connectivity between EC2 and EKS workloads. In a production environment, be sure to configure security group settings to restrict access based on specific CIDR ranges, protocols, and ports. Limiting access helps to maintain security and prevent unwanted traffic between EC2 and EKS environments.
    scripts/build/security-groups.sh

    Example output:

    =========================================
    Configuring security groups for EC2 ambient mesh
    =========================================
    
    Cluster:     demo
    Instance:    i-0abc1234567890def
    Region:      us-east-1
    
    VPC CIDR:        10.0.0.0/16
    EC2 SG:          sg-0abc1234567890def
    EKS node SG:     sg-0def1234567890abc
    
    Adding port 15008 inbound rule to sg-0abc1234567890def (EC2 instance, allows EKS-to-EC2 HBONE)...
    ✓ Added port 15008 inbound rule to sg-0abc1234567890def (EC2 instance, allows EKS-to-EC2 HBONE)
    Adding port 15008 inbound rule to sg-0def1234567890abc (EKS nodes, allows EC2-to-EKS HBONE)...
    ✓ Added port 15008 inbound rule to sg-0def1234567890abc (EKS nodes, allows EC2-to-EKS HBONE)
    
    =========================================
    Security group configuration complete!
    =========================================
  3. Ensure that the EC2 instance has an IAM instance profile attached. The istioctl ec2 add-workload command automatically discovers the IAM role ARN from the instance profile, and ztunnel uses the instance profile for credential retrieval.

    Check whether an instance profile is already attached.

    aws ec2 describe-instances \
      --instance-ids ${EC2_INSTANCE_ID} \
      --query 'Reservations[0].Instances[0].IamInstanceProfile'

    If a profile ARN is returned, continue to the next step. If the output returns null, no profile is attached and you must complete the following sub-steps.

    1. Create an instance profile and associate the IAM role that represents the workload.
      aws iam create-instance-profile --instance-profile-name <profile-name>
      aws iam add-role-to-instance-profile \
        --instance-profile-name <profile-name> \
        --role-name <role-name>
    2. Attach the instance profile to the EC2 instance.
      aws ec2 associate-iam-instance-profile \
        --instance-id ${EC2_INSTANCE_ID} \
        --iam-instance-profile Name=<profile-name>
  4. Start an application on the EC2 instance. For this guide, use Docker to run the Istio demo app on the ports that you configure in later steps.

    1. SSH into the EC2 instance.

    2. Set the workload name. You use this name as the Docker container name and later as the workload identity in the mesh.

      export EC2_WORKLOAD_NAME=web-api
    3. Run the demo app.

      docker run -d -p 8080:8080 --name ${EC2_WORKLOAD_NAME} gcr.io/istio-testing/app
    4. Verify that the application is running and responding.

      curl localhost:8080

      Example output:

      ServiceVersion=
      ServicePort=8080
      Host=localhost:8080
      URL=/
      Method=GET
      Proto=HTTP/1.1
      IP=172.17.0.1
      RequestHeader=Accept:*/*
      RequestHeader=User-Agent:curl/8.17.0
      Hostname=<container-id>

Step 4: Tag EC2 instances and add them to the ambient mesh

Add each EC2 workload to the ambient mesh.

Configure EC2 workloads

  1. In your cluster, create the namespace for your EC2 workloads and label it for ambient mesh inclusion.

    kubectl create namespace ec2-workloads
    kubectl label namespace ec2-workloads istio.io/dataplane-mode=ambient
  2. Save the name of the workload you run on the EC2 instance.

    export EC2_WORKLOAD_NAME=web-api
  3. Add each EC2 workload to the ambient mesh. You can use the istioctl ec2 add-workload command, which automates the tagging, Kubernetes resource creation, and bootstrap token generation. Alternatively, you can perform these steps manually if you prefer to use IaC tools like Terraform or do not have istioctl available.

    Run the istioctl ec2 add-workload command for each workload on each EC2 instance. This command applies the ec2.solo.io/discovery-enabled and workload metadata tags to the EC2 instance, which triggers istiod to discover it; creates the Kubernetes resources for the workload identity, including the namespace if it does not already exist; and generates the bootstrap token to start ztunnel on the instance. If a single instance runs multiple workloads, see Multi-workload instances. For more information about this command, see the CLI reference.

    • If your EC2 instance is in a different VPC or network than the EKS cluster, add the --external flag so that traffic is routed through the east-west gateway, and add --use-public-ip if there is no VPC peering between the networks.
    • If your EC2 instance is in a different AWS region than the EKS cluster, set AWS_DEFAULT_REGION to the EC2 instance’s region before running the command. The command uses the AWS SDK default region to call the EC2 API, and does not have a --region flag.
    istioctl ec2 add-workload ${EC2_WORKLOAD_NAME} \
      --instance ${EC2_INSTANCE_ID} \
      --namespace ec2-workloads \
      --ports http:80:8080

    Review the following flags that you can specify in the istioctl ec2 add-workload command.

    OptionRequired?DefaultDescription
    <workload-name>The name of the workload running on the EC2 instance. Used as the workload identity and as the default Kubernetes ServiceAccount name.
    --instanceThe EC2 instance ID (for example, i-0abc1234567890def).
    --namespaceistio-systemThe Kubernetes namespace to associate the workload with. The ServiceAccount and WorkloadEntry are created in this namespace.
    --service-accountworkload nameThe Kubernetes ServiceAccount for the workload identity. Defaults to the workload name.
    --portshttp:80Port configuration as a /-separated list of protocol:port[:targetPort] pairs, such as http:80:8080/grpc:9090.
    --use-public-ipfalseUse the instance’s public IP for routing instead of the private IP.
    --externalfalseMark the workload as external to the cluster network, so that traffic is routed through the east-west gateway. Use this when the EC2 instance is in a different VPC or network than the EKS cluster.
    --role-arnauto-discoveredIAM role ARN to associate with the workload. Auto-discovered from the EC2 instance profile if not specified.
    --bootstrapfalseForce re-generation of the bootstrap token, even if the instance already has discovery tags applied.
    --profileAWS CLI profile to use. Defaults to the default AWS CLI profile.

    Example output:

    • Found EC2 instance i-0abc1234567890def (AZ: us-east-1f)
    • Using IAM role arn:aws:iam::123456789012:role/istio-ec2-role
    • Tagged instance i-0abc1234567890def with workload "web-api"
    • Configured service account ec2-workloads/web-api with role annotation
    • Configured gateway service account ec2-workloads/ec2-i-0abc1234567890def-gateway
    • Tagged instance i-0abc1234567890def with gateway config
    • Generating a bootstrap token for ec2-workloads/ec2-i-0abc1234567890def-gateway...
    • Fetched Istiod Root Cert
    • Fetched Istio network (my-cluster)
    • Fetching Istiod URL...
      • Service "istio-eastwest" provides Istiod access on port 15012
    • Fetching Istiod URL (https://a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4-1234567890.us-east-1.elb.amazonaws.com:15012)
    • Workload is authorized to run as role "arn:aws:iam::123456789012:role/istio-ec2-role"
    • Start ztunnel on the VM with:
      BOOTSTRAP_TOKEN=<generated_token> ztunnel

    The following steps can be repeated for any EC2 workload to include it in the mesh. You can automate these steps with IaC tools like Terraform.

    1. Tag the EC2 instance for discovery by istiod. The ec2.solo.io/discovery-enabled tag opts the instance in for automatic discovery. Workload metadata tags use the format ec2.solo.io/workload/<name> for the workload name and ec2.solo.io/workload/<name>/<property> for each property.

      aws ec2 create-tags \
        --resources ${EC2_INSTANCE_ID} \
        --tags \
          Key=ec2.solo.io/discovery-enabled,Value=true \
          Key=ec2.solo.io/workload/${EC2_WORKLOAD_NAME},Value=true \
          Key=ec2.solo.io/workload/${EC2_WORKLOAD_NAME}/namespace,Value=ec2-workloads \
          Key=ec2.solo.io/workload/${EC2_WORKLOAD_NAME}/ports,Value="http:80:8080" \
          Key=ec2.solo.io/workload/${EC2_WORKLOAD_NAME}/service-account,Value=${EC2_WORKLOAD_NAME}
    2. Get the IAM role ARN from the instance profile attached to the EC2 instance.

      INSTANCE_PROFILE_ARN=$(aws ec2 describe-instances \
        --instance-ids ${EC2_INSTANCE_ID} \
        --query 'Reservations[0].Instances[0].IamInstanceProfile.Arn' \
        --output text)
      INSTANCE_PROFILE_NAME=$(echo ${INSTANCE_PROFILE_ARN} | awk -F'/' '{print $NF}')
      ROLE_ARN=$(aws iam get-instance-profile \
        --instance-profile-name ${INSTANCE_PROFILE_NAME} \
        --query 'InstanceProfile.Roles[0].Arn' \
        --output text)
    3. Create the Kubernetes ServiceAccount for the workload and annotate it with the IAM role ARN.

      kubectl create serviceaccount ${EC2_WORKLOAD_NAME} -n ec2-workloads
      kubectl annotate serviceaccount ${EC2_WORKLOAD_NAME} -n ec2-workloads \
        ec2.solo.io/role-arn=${ROLE_ARN}
    4. Create a Kubernetes ServiceAccount for the gateway and annotate it with the IAM role ARN.

      kubectl create serviceaccount ec2-${EC2_INSTANCE_ID}-gateway -n ec2-workloads
      kubectl annotate serviceaccount ec2-${EC2_INSTANCE_ID}-gateway -n ec2-workloads \
        ec2.solo.io/role-arn=${ROLE_ARN}
    5. Create a bootstrap token for the EC2 workload. The bootstrap token contains the information ztunnel needs to connect to istiod.

      ISTIOD_EXPOSE_HOSTNAME=https://$(kubectl get service -n istio-eastwest istio-eastwest -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
      ISTIOD_EXPOSE_PORT=$(kubectl get service -n istio-eastwest istio-eastwest -o jsonpath='{.metadata.labels.istio\.io\/expose-istiod}')
      # Adjust this command when using a custom root cert
      ISTIOD_CA_ROOT_CERT=$(kubectl get configmap -n istio-system istio-ca-root-cert -o json | jq '.data."root-cert.pem"')
      NAMESPACE="ec2-workloads"
      SERVICE_ACCOUNT="ec2-${EC2_INSTANCE_ID}-gateway"
      NETWORK=$(kubectl get namespace istio-system -o jsonpath='{.metadata.labels.topology\.istio\.io\/network}')
      # Set to true when the EC2 instance is in a different VPC or network than the EKS cluster
      # (equivalent to the --external flag in istioctl ec2 add-workload)
      REMOTE=false
      
      BOOTSTRAP_TOKEN=$(echo -n "{\"url\":\"${ISTIOD_EXPOSE_HOSTNAME}:${ISTIOD_EXPOSE_PORT}\",\"caCert\":${ISTIOD_CA_ROOT_CERT},\"namespace\":\"${NAMESPACE}\",\"serviceAccount\":\"${SERVICE_ACCOUNT}\",\"platform\":\"ec2\",\"network\":\"${NETWORK}\",\"remote\":${REMOTE}}" | base64 | base64)

      The bootstrap token JSON contains the following fields:

      FieldDescription
      urlThe east-west gateway URL that ztunnel uses to reach istiod.
      caCertThe istiod root CA certificate for TLS verification.
      namespaceThe Kubernetes namespace for the workload identity.
      serviceAccountThe Kubernetes ServiceAccount (gateway SA) for the workload.
      platformMust be ec2 for EC2 workloads.
      networkThe Istio network name, matching the istio-system namespace label.
      remoteSet to true to route traffic through the east-west gateway (equivalent to the --external flag in istioctl). Set to false when the EC2 instance is in the same VPC as the EKS cluster.
    6. Repeat these steps for each workload on each EC2 instance. If a single instance runs multiple workloads, see Multi-workload instances.

Start ztunnel and verify

  1. SSH into the EC2 instance and save the BOOTSTRAP_TOKEN value from the previous step’s command output as an environment variable.

    export BOOTSTRAP_TOKEN=<generated_token>
  2. On the EC2 instance, start ztunnel with the bootstrap token.

    docker run -d \
      --name ztunnel \
      --network host \
      -e BOOTSTRAP_TOKEN="${BOOTSTRAP_TOKEN}" \
      us-docker.pkg.dev/soloio-img/istio/ztunnel:1.30.3-solo \
      proxy
  3. In your cluster, verify that istiod automatically discovered the EC2 instance and created the mesh resources.

    kubectl get workloadentry -n ec2-workloads
    kubectl get service -n ec2-workloads

    Example output, including the web-api workload:

    NAME                              AGE     ADDRESS
    ec2-<instance-id>-gateway         7m12s   10.0.77.33
    ec2-<instance-id>-web-api         9m38s   127.0.0.1
    NAME      TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
    web-api   ClusterIP   172.20.226.38   <none>        80/TCP    9m39s
  4. Verify that the ztunnel workload is registered and using HBONE for mTLS-secured communication.

    istioctl ztunnel-config workloads | grep ec2-workloads

    Example output: The PROTOCOL value of HBONE confirms that the workload can communicate with other services in the mesh using mTLS.

    ec2-workloads   ec2-<instance-id>-gateway   10.0.1.42   <instance-id>         None     HBONE    cluster1  istio-eastwest/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-1111111111.us-east-1.elb.amazonaws.com
    ec2-workloads   ec2-<instance-id>-web-api   127.0.0.1   <instance-id>         None     HBONE    cluster1  cluster1/10.0.1.42

Step 5: Test connectivity

Verify that all traffic between EC2 and EKS workloads is secured with mTLS, ensuring that it is encrypted, verified, and routed through the Istio ztunnel.

EKS to EC2

After the EC2 workload joins the mesh, applications in the EKS ambient mesh can reach it by calling its Kubernetes service hostname, ${EC2_WORKLOAD_NAME}.ec2-workloads. The EC2 discovery controller automatically creates a Kubernetes Service for each discovered workload in the workload namespace. All traffic is encrypted with mTLS to the ztunnel binary on the EC2 instance, which then proxies requests to the application. Test this by making HTTP requests from an example EKS shell pod to the EC2 workload.

  1. Label the default namespace in the EKS cluster for ambient mesh inclusion.

    kubectl label namespace default istio.io/dataplane-mode=ambient
  2. Deploy shell and echo applications to the default namespace.

    kubectl apply -f manifests/eks-echo.yaml
    kubectl apply -f manifests/eks-shell.yaml
  3. Send a curl request from the EKS shell pod to the EC2 workload. Because you use the EC2 workload’s Kubernetes service hostname, the request is routed through the mesh.

    kubectl exec deploy/eks-shell -- curl -s ${EC2_WORKLOAD_NAME}.ec2-workloads:80

    Example output:

    ServiceVersion=
    ServicePort=8080
    Host=web-api.ec2-workloads
    URL=/
    Method=GET
    Proto=HTTP/1.1
    IP=172.17.0.1
    RequestHeader=Accept:*/*
    RequestHeader=User-Agent:curl/8.7.1
    Hostname=08b73388c4cb

EC2 to EKS

Test connectivity from the EC2 instance to the EKS echo service. Traffic from the EC2 instance is routed through ztunnel’s SOCKS5 proxy on port 15080. The SOCKS5 username selects which workload identity ztunnel uses for the outbound connection.

For this demo, the security group settings are relaxed to simplify testing and ensure connectivity between EC2 and EKS workloads. In a production environment, be sure to configure security group settings to restrict access based on specific CIDR ranges, protocols, and ports. Limiting access helps to maintain security and prevent unwanted traffic between EC2 and EKS environments.
  1. SSH into the EC2 instance.

  2. Send a request to the EKS echo service using the SOCKS5 proxy, specifying the workload identity as the username.

    ALL_PROXY=socks5h://${EC2_WORKLOAD_NAME}.ec2-workloads:pass@127.0.0.1:15080 curl eks-echo.default:8080

    Example output:

    ServiceVersion=
    ServicePort=8080
    Host=eks-echo.default:8080
    URL=/
    Method=GET
    Proto=HTTP/1.1
    IP=10.0.1.42
    RequestHeader=Accept:*/*
    RequestHeader=User-Agent:curl/8.17.0
    Hostname=eks-echo-a1b2c3d4-x5y6z

Optional: EC2 to EC2

Test connectivity between two workloads on the same EC2 instance. Even though both workloads share an instance, traffic between them flows through the mesh and is protected by mTLS. Each workload uses its own SPIFFE identity, selected via the SOCKS5 username.

To run this test, add a second workload to the same EC2 instance by following the Multi-workload instances steps. The following example assumes two workloads, web-api and grpc-backend, both running on the same instance in the ec2-workloads namespace.

  1. SSH into the EC2 instance.

  2. Send a request from web-api to grpc-backend.

    ALL_PROXY=socks5h://web-api.ec2-workloads:pass@127.0.0.1:15080 curl grpc-backend.ec2-workloads:9090
  3. Send a request in the opposite direction, from grpc-backend to web-api.

    ALL_PROXY=socks5h://grpc-backend.ec2-workloads:pass@127.0.0.1:15080 curl web-api.ec2-workloads:80

    Successful responses in both directions confirm that per-workload identity selection is working and that each workload’s traffic is independently secured by mTLS.

Step 6: Control access with authorization policies

Test access control by applying Layer 4 authorization policies to both EKS and EC2 workloads in the ambient mesh.

EC2 workloads use their Kubernetes ServiceAccount as their SPIFFE identity (spiffe://cluster.local/ns/<namespace>/sa/<workload-name>), the same as in-cluster pods.

L4 authorization for EKS workloads

  1. Apply a deny-all L4 authorization policy to block all traffic to the EKS echo service.

    kubectl apply -f manifests/eks-l4-deny.yaml
  2. SSH into the EC2 instance and run the following command to test that EC2 to EKS communication is now blocked by the L4 policy.

    ALL_PROXY=socks5h://${EC2_WORKLOAD_NAME}.ec2-workloads:pass@127.0.0.1:15080 curl eks-echo.default:8080

    The request fails with a connection error, indicating that the L4 policy is blocking the traffic.

    curl: (56) Recv failure: Connection reset by peer
  3. Verify that EKS to EC2 communication still succeeds, because no policy is blocking it.

    kubectl exec deploy/eks-shell -- curl -s ${EC2_WORKLOAD_NAME}.ec2-workloads:80

    Example output:

    ServiceVersion=
    ServicePort=8080
    Host=web-api.ec2-workloads
    URL=/
    Method=GET
    Proto=HTTP/1.1
    IP=172.17.0.1
    RequestHeader=Accept:*/*
    RequestHeader=User-Agent:curl/8.20.0
    Hostname=08b73388c4cb
  4. Remove the EKS authorization policy to restore traffic flow.

    kubectl delete -n default authorizationpolicies eks-echo-deny

L4 authorization for EC2 workloads

  1. Apply a deny-all L4 authorization policy to the ec2-workloads namespace to block all traffic to EC2 workloads.

    kubectl apply -f manifests/ec2-l4-deny.yaml
  2. SSH into the EC2 instance and run the following command to verify that EC2 to EKS communication is now allowed, since no L4 policy applies to EKS workloads.

    ALL_PROXY=socks5h://${EC2_WORKLOAD_NAME}.ec2-workloads:pass@127.0.0.1:15080 curl eks-echo.default:8080

    Example output:

    ServiceVersion=
    ServicePort=8080
    Host=eks-echo.default:8080
    URL=/
    Method=GET
    Proto=HTTP/1.1
    IP=10.0.77.33
    RequestHeader=Accept:*/*
    RequestHeader=User-Agent:curl/8.17.0
    Hostname=eks-echo-a1b2c3d4-x5y6z
  3. Verify that EKS to EC2 communication is now blocked by the L4 policy applied to EC2 workloads.

    kubectl exec deploy/eks-shell -- curl -s ${EC2_WORKLOAD_NAME}.ec2-workloads:80

    The request fails with a connection error, indicating that the L4 policy is blocking traffic to the EC2 workload.

    command terminated with exit code 56
  4. Clean up the authorization policy.

    kubectl delete -n ec2-workloads authorizationpolicies ec2-deny

Per-workload authorization

For EC2 instances that run multiple workloads, you can write authorization policies that target a specific workload using the ec2.solo.io/workload label. Istiod automatically applies this label to each WorkloadEntry it creates during EC2 discovery, using the workload name as the value.

  1. Apply a deny-all policy targeting only ${EC2_WORKLOAD_NAME} by using the ec2.solo.io/workload label selector. Other workloads on the same instance are unaffected.

    kubectl apply -f - <<EOF
    apiVersion: security.istio.io/v1
    kind: AuthorizationPolicy
    metadata:
      name: deny-ec2-workload
      namespace: ec2-workloads
    spec:
      selector:
        matchLabels:
          ec2.solo.io/workload: ${EC2_WORKLOAD_NAME}
      action: DENY
      rules:
      - {}
    EOF
  2. Verify that traffic to ${EC2_WORKLOAD_NAME} is blocked but other workloads on the same instance remain reachable.

    kubectl exec deploy/eks-shell -- curl -s ${EC2_WORKLOAD_NAME}.ec2-workloads:80

    The request fails with a connection error, indicating that the policy is blocking traffic to ${EC2_WORKLOAD_NAME}.

    command terminated with exit code 56
  3. Clean up the policy.

    kubectl delete -n ec2-workloads authorizationpolicies deny-ec2-workload

Further configuration

Using waypoints with EC2 workloads

To apply Layer 7 policies to EC2 workloads, deploy a waypoint proxy to the namespace where your EC2 WorkloadEntries and Services are created. EC2 workloads are represented as WorkloadEntries rather than native Kubernetes pods, so the waypoint must be scoped to all using the istio.io/waypoint-for: all label. Without this label, the default namespace scope only processes traffic to Kubernetes Services and does not include WorkloadEntries.

  1. Deploy a waypoint in the EC2 workloads namespace with the istio.io/waypoint-for: all label.

    kubectl apply -f - <<EOF
    apiVersion: gateway.networking.k8s.io/v1
    kind: Gateway
    metadata:
      name: waypoint
      namespace: ec2-workloads
      labels:
        istio.io/waypoint-for: all
    spec:
      gatewayClassName: istio-waypoint
      listeners:
      - name: mesh
        port: 15008
        protocol: HBONE
    EOF
  2. Label the namespace to use the waypoint.

    kubectl label namespace ec2-workloads istio.io/use-waypoint=waypoint
  3. Apply your Layer 7 policies, such as HTTPRoute or AuthorizationPolicy resources, to the namespace. The waypoint proxy enforces these policies on all traffic to EC2 workloads in the namespace.

Updating workload configuration

After you integrate an EC2 workload, you might need to modify its routing and configuration. Workload properties use the tag format ec2.solo.io/workload/<name>/<property>. Istiod picks up tag changes automatically during its next discovery cycle.

TagDefaultDescription
ec2.solo.io/workload/<name>/portshttp:80Port configuration as a /-separated list of protocol:port[:targetPort] pairs, such as http:80:8080/grpc:9090.
ec2.solo.io/workload/<name>/namespaceistio-systemThe Kubernetes namespace to associate the workload with. The ServiceAccount and WorkloadEntry are created in this namespace.
ec2.solo.io/workload/<name>/service-accountworkload nameThe Kubernetes ServiceAccount for the workload identity.
ec2.solo.io/workload/<name>/hostname<name>.<domain>Override the hostname used as the WorkloadEntry selector.
ec2.solo.io/use-public-ipfalseUse the instance’s public IP for routing instead of the private IP.

Example to update the ports for an EC2 workload:

aws ec2 create-tags \
  --resources ${EC2_INSTANCE_ID} \
  --tags Key=ec2.solo.io/workload/${EC2_WORKLOAD_NAME}/ports,Value="http:80:8080/grpc:9090"

AWS tag passthrough

In addition to the ec2.solo.io/* configuration tags, istiod passes through AWS resource tags as labels on the Kubernetes objects it creates during EC2 discovery. Only tags whose key contains one of the following domain prefixes are passed through:

  • *.istio.io/
  • *.solo.io/
  • *.kubernetes.io/

Both the tag key and value must be valid Kubernetes label key/value pairs.

Multi-account and multi-region EC2 discovery

You can configure istiod to discover EC2 instances across multiple AWS accounts and regions by adding entries to the platforms.aws.accounts list in your istiod configuration. Specify an array of regions for each account, or leave regions empty to use the account’s default region.

platforms:
  aws:
    ec2Discovery: true
    accounts:
      - domain: example1.ec2
        role: arn:aws:iam::111111111111:role/istiod-ec2
        regions: ["us-east-1", "us-west-2"]
      - domain: example2.ec2
        role: arn:aws:iam::222222222222:role/istiod-ec2
        regions: ["eu-west-1"]

When using multi-account discovery, ensure that the istiod role in your EKS account has permission to assume the istiod-ec2 role in each additional account.

Public and private IP addresses

By default, EC2 WorkloadEntries use the private IP address of the instance. If your EC2 instances are not in the same VPC as the EKS cluster or if VPC peering is not configured, you can configure EC2 WorkloadEntries to use the public IP address of the instance.

To use the public IP address, add the ec2.solo.io/use-public-ip=true tag to the EC2 instance.

aws ec2 create-tags \
  --resources ${EC2_INSTANCE_ID} \
  --tags Key=ec2.solo.io/use-public-ip,Value=true

Alternatively, pass --use-public-ip to istioctl ec2 add-workload to set the tag automatically. If the ec2.solo.io/use-public-ip=true tag is set but the instance does not have a public IP assigned, the WorkloadEntry is created with a ec2.solo.io/Configured=false condition and traffic is not routed to the instance until the address can be resolved.

Multi-workload instances

A single EC2 instance can host multiple distinct workloads, each with its own identity. To add multiple workloads to the same EC2 instance, run istioctl ec2 add-workload once per workload, specifying the same --instance ID each time. On the second and subsequent workloads, the instance is already bootstrapped, so no new bootstrap token is generated.

# First workload — also bootstraps ztunnel on the instance
istioctl ec2 add-workload web-api \
  --instance ${EC2_INSTANCE_ID} \
  --namespace ec2-workloads \
  --ports http:80:8080

# Second workload — instance already bootstrapped, only tags and K8s resources are created
istioctl ec2 add-workload grpc-backend \
  --instance ${EC2_INSTANCE_ID} \
  --namespace ec2-workloads \
  --ports grpc:9090

Each workload uses its own Kubernetes ServiceAccount and SPIFFE identity. On the EC2 instance, ztunnel runs as a shared proxy and uses the SOCKS5 proxy (port 15080) with per-workload identity selection for outbound traffic. To send outbound traffic as a specific workload identity, set the SOCKS5 username to the workload’s service account.

IAM roles and credentials

EC2 ztunnel instances authenticate with istiod using the IAM instance profile attached to the EC2 instance. ztunnel fetches credentials through the EC2 Instance Metadata Service (IMDS), so no separate bootstrap token distribution is required for credential management. Ensure that:

  • Each EC2 instance has an IAM instance profile attached with a role that the Kubernetes ServiceAccount annotation references.
  • The istioctl ec2 add-workload command auto-discovers the role ARN from the instance profile. No additional IAM permissions are required on the instance role itself — ztunnel authenticates with istiod using the role ARN annotated on the Kubernetes ServiceAccount.

Rotating Istio certificates

Istio workload certificates for EC2 workloads are automatically rotated by istiod, which is the same as for in-cluster workloads. Ztunnel on the EC2 instance handles workload certificate rotation automatically.

However, when you rotate the Istio control plane root certificate (the istiod root CA), the ztunnel on the EC2 instance uses the istiod root cert embedded within the BOOTSTRAP_TOKEN. Because of this, EC2 ztunnels do not automatically pick up a rotated root certificate, and you must push out the new configuration to EC2 workloads as part of your root certificate rotation process.

To accomplish a zero-downtime rotation, perform a multi-root rotation on the control plane so that both the old and new root certificates are trusted during a transition window. While the control plane trusts both roots, update BOOTSTRAP_TOKEN values using one of the following methods so EC2 ztunnels are bootstrapped with the new root as well. After all EC2 and Kubernetes ztunnels are rotated to the new root certificate and traffic is healthy, remove the old root certificate from the control plane.

Update EC2 configuration

The simplest and recommended approach is to re-run the istioctl ec2 add-workload command with --bootstrap that you originally used to add the workload. This command fetches the new istiod root certificate and generates a fresh BOOTSTRAP_TOKEN that embeds the new cert.

Be sure that you installed the Solo distribution of istioctl binary at the same version that you want to use.
  1. Regenerate the bootstrap token.

    istioctl ec2 add-workload <workload-name> \
      --instance ${EC2_INSTANCE_ID} \
      --namespace ec2-workloads \
      --bootstrap

    Note the new BOOTSTRAP_TOKEN value from the command output.

  2. SSH into the EC2 instance and restart ztunnel with the new token.

    pkill ztunnel && BOOTSTRAP_TOKEN=<new_token> ztunnel &

The following steps can be repeated for any EC2 workload to update its bootstrap token after a root certificate rotation. You can automate these steps with IaC tools like Terraform.

  1. Create a new bootstrap token for the EC2 workload.

    ISTIOD_EXPOSE_HOSTNAME=https://$(kubectl get service -n istio-eastwest istio-eastwest -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
    ISTIOD_EXPOSE_PORT=$(kubectl get service -n istio-eastwest istio-eastwest -o jsonpath='{.metadata.labels.istio\.io\/expose-istiod}')
    # Adjust this command when using a custom root cert
    ISTIOD_CA_ROOT_CERT=$(kubectl get configmap -n istio-system istio-ca-root-cert -o json | jq '.data."root-cert.pem"')
    NAMESPACE="ec2-workloads"
    SERVICE_ACCOUNT="ec2-${EC2_INSTANCE_ID}-gateway"
    NETWORK=$(kubectl get namespace istio-system -o jsonpath='{.metadata.labels.topology\.istio\.io\/network}')
    # Set to true when the EC2 instance is in a different VPC or network than the EKS cluster
    # (equivalent to the --external flag in istioctl ec2 add-workload)
    REMOTE=false
    
    BOOTSTRAP_TOKEN=$(echo -n "{\"url\":\"${ISTIOD_EXPOSE_HOSTNAME}:${ISTIOD_EXPOSE_PORT}\",\"caCert\":${ISTIOD_CA_ROOT_CERT},\"namespace\":\"${NAMESPACE}\",\"serviceAccount\":\"${SERVICE_ACCOUNT}\",\"platform\":\"ec2\",\"network\":\"${NETWORK}\",\"remote\":${REMOTE}}" | base64 | base64)
  2. SSH into the EC2 instance and restart ztunnel with the new token.

    pkill ztunnel && BOOTSTRAP_TOKEN=<new_token> ztunnel &
  3. Repeat these steps for each EC2 workload in your mesh.

Verification

Confirm that the EC2 workload is reconnected and healthy.

kubectl get workloadentry -n ec2-workloads
istioctl ztunnel-config workloads | grep ec2-workloads

Example output:

ec2-workloads ec2-<instance-id>-web-api  10.0.1.42   None  HBONE
If you see failures after rotation, you can roll back by restarting ztunnel on the EC2 instance with the previous BOOTSTRAP_TOKEN that contained the old root certificate.

Upgrading the Istio version of ztunnel on EC2

When you upgrade Istio, ztunnels running in Kubernetes are upgraded automatically. However, ztunnels on EC2 instances use the binary version that was installed manually. You must upgrade them as part of your upgrade process.

The simplest and recommended approach is to re-run the istioctl ec2 add-workload command with --bootstrap to regenerate the bootstrap token, then replace the ztunnel binary and restart it on the EC2 instance with the new token.

Be sure that you installed the Solo distribution of istioctl binary at the same version that you want to upgrade to.
  1. Regenerate the bootstrap token.

    istioctl ec2 add-workload <workload-name> \
      --instance ${EC2_INSTANCE_ID} \
      --namespace ec2-workloads \
      --bootstrap

    Note the new BOOTSTRAP_TOKEN value from the command output.

  2. SSH into the EC2 instance to stop ztunnel, download the new binary, and restart ztunnel with the new token.

    pkill ztunnel
    curl -sSL https://storage.googleapis.com/soloio-istio-binaries/release/1.30.3-solo/ztunnel-1.30.3-solo-linux-amd64 \
      -o /usr/local/bin/ztunnel
    chmod +x /usr/local/bin/ztunnel
    BOOTSTRAP_TOKEN=<new_token> ztunnel &
  1. SSH into the EC2 instance.

  2. Stop the running ztunnel process.

    pkill ztunnel
  3. Download the upgraded ztunnel binary.

    curl -sSL https://storage.googleapis.com/soloio-istio-binaries/release/1.30.3-solo/ztunnel-1.30.3-solo-linux-amd64 \
      -o /usr/local/bin/ztunnel
    chmod +x /usr/local/bin/ztunnel
  4. Start ztunnel with the same bootstrap token.

    BOOTSTRAP_TOKEN=<token> ztunnel &
  5. Repeat for each EC2 instance in the mesh.

Verification

Confirm that the upgraded ztunnel is connected and reports HBONE connectivity.

istioctl ztunnel-config workloads | grep ec2-workloads

Example output:

ec2-workloads ec2-<instance-id>-web-api  10.0.1.42   None  HBONE
If you see failures after upgrading, you can roll back by stopping ztunnel, reinstalling the previous binary version, and restarting with the same bootstrap token.

Cleanup

Remove the resources that you created in this guide.

  1. On each EC2 instance, stop and remove the ztunnel container and any sample workload containers you started.

    docker kill ztunnel && docker rm ztunnel
    docker kill ${EC2_WORKLOAD_NAME} && docker rm ${EC2_WORKLOAD_NAME}
  2. Remove EC2 instances from the mesh by deleting the ec2.solo.io/discovery-enabled tag or by terminating the instances.

    aws ec2 delete-tags \
      --resources ${EC2_INSTANCE_ID} \
      --tags Key=ec2.solo.io/discovery-enabled
  3. Delete the Kubernetes namespace for the EC2 workloads.

    kubectl delete namespace ec2-workloads
  4. Delete the pod identity association and IAM roles and policies. This script performs the following cleanup actions:

    • Deletes the pod identity association for the istio-system/istiod service account
    • Detaches and deletes the istiod-${AWS_ACCOUNT} policy from the istiod role
    • Deletes the istiod IAM role
    • Detaches and deletes the ec2-read-only policy from the istiod-ec2 role
    • Deletes the istiod-ec2 IAM role
    scripts/cleanup/iam.sh
  5. If you created an EKS cluster for this guide, delete the cluster. The security group rules added for HBONE traffic are removed automatically when the cluster and its VPC are deleted. This script performs the following cleanup actions:

    • Deletes the ${CLUSTER_NAME} EKS cluster
    • Removes all associated node groups
    • Deletes the VPC and networking resources created by eksctl
    scripts/cleanup/eks-cluster.sh
    This step can take several minutes to complete as eksctl removes all cluster resources.