Testsigma Tunnel
Setup and Installation: Kubernetes (Helm)
The Testsigma Tunnel Client creates a secure connection between your Kubernetes cluster and the Testsigma platform. This connection lets you run automated tests against applications deployed in private networks or behind firewalls, without exposing those applications to the public internet.
This article discusses how to deploy the Testsigma Tunnel Client on Kubernetes using a Helm chart, configure it securely, and manage the deployment over time.
Prerequisites
Before you begin, ensure that you have:
- Referred to the documentation on key components.
- A Kubernetes cluster running version 1.19 or later.
- Helm 3.x installed on your local machine.
- kubectl configured with access to your cluster.
- Kubernetes nodes labeled with the appropriate pool-type value (for example, app or common).
How the Tunnel Works
Section titled “How the Tunnel Works”When you deploy the Helm chart, it creates the following Kubernetes resources:
| Resource | Description |
|---|---|
| StatefulSet | Runs the tunnel client pods using an OrderedReady pod management policy. Pods start one at a time in sequence. |
| ConfigMap | Mounts the tunnel configuration file (args.yaml) into each container at startup. |
| Service | (Optional) Provides a headless service for StatefulSet DNS resolution, required when you deploy multiple replicas. |
The chart uses a StatefulSet with an OrderedReady pod management policy. This policy ensures that the first pod registers the tunnel with the Testsigma server before any additional pods start. Each subsequent pod then joins the already-registered tunnel session rather than attempting to create a new registration.
Create the Helm Chart
Section titled “Create the Helm Chart”Follow these steps to build the Helm chart directory from scratch. If you already have a chart, skip to Configure Your Values File.
Step 1: Create the Chart Directory Structure
Section titled “Step 1: Create the Chart Directory Structure”Run the following commands to create the required directories:
mkdir -p testsigma-tunnel/templatescd testsigma-tunnelWhen you finish creating all the files described in the following steps, your chart directory should have this structure:
testsigma-tunnel/├── Chart.yaml├── values.yaml└── templates/ ├── deployment.yaml ├── configmap.yaml └── service.yamlStep 2: Create Chart.yaml
Section titled “Step 2: Create Chart.yaml”Create a file named Chart.yaml in the testsigma-tunnel/ directory with the following content:
apiVersion: v2name: testsigma-tunneldescription: Helm chart for Testsigma Tunnel Clienttype: applicationversion: 0.1.0appVersion: "0.1.0"Step 3: Create the StatefulSet Template
Section titled “Step 3: Create the StatefulSet Template”Create templates/deployment.yaml. This template defines the StatefulSet that runs the tunnel client pod and mounts the configuration file.
{{- range $name, $deployment := .Values.Deployments }}{{- if $deployment.enabled }}---kind: StatefulSetapiVersion: apps/v1metadata: name: {{ (index $.Values.Application $name).name }}-{{ $.Values.Environment.name }}-statefulset labels: app: {{ (index $.Values.Application $name).name }}-{{ $.Values.Environment.name }} namespace: {{ (index $.Values.Namespace $name).name }}spec: serviceName: {{ (index $.Values.Application $name).name }}-{{ $.Values.Environment.name }}-headless replicas: {{ $deployment.replicas }} podManagementPolicy: OrderedReady selector: matchLabels: app: {{ (index $.Values.Application $name).name }}-{{ $.Values.Environment.name }} template: metadata: labels: app: {{ (index $.Values.Application $name).name }}-{{ $.Values.Environment.name }} spec: containers: - name: {{ (index $.Values.Application $name).name }}-{{ $.Values.Environment.name }} image: {{ $deployment.containers.image }}:{{ $deployment.containers.version }} imagePullPolicy: {{ $deployment.containers.imagePullPolicy }} volumeMounts: - name: config mountPath: /app/args.yaml subPath: args.yaml readOnly: true volumes: - name: config configMap: name: {{ (index $.Values.Application $name).name }}-{{ $.Values.Environment.name }}-config nodeSelector: pool-type: {{ $deployment.poolType }}{{- end }}{{- end }}Step 4: Create the ConfigMap Template
Section titled “Step 4: Create the ConfigMap Template”Create templates/configmap.yaml. This template generates a ConfigMap that contains your tunnel configuration and mounts it as a file inside the container.
{{- range $name, $deployment := .Values.Deployments }}{{- if $deployment.enabled }}---apiVersion: v1kind: ConfigMapmetadata: name: {{ (index $.Values.Application $name).name }}-{{ $.Values.Environment.name }}-config namespace: {{ (index $.Values.Namespace $name).name }} labels: app: {{ (index $.Values.Application $name).name }}-{{ $.Values.Environment.name }}data: args.yaml: | key: {{ $deployment.config.key | quote }} tunnel-name: {{ $deployment.config.tunnelName | quote }} verbose: {{ $deployment.config.verbose }} {{- if $deployment.config.delegateSslValidation }} delegate-ssl-validation: {{ $deployment.config.delegateSslValidation }} {{- end }} {{- if $deployment.config.proxy }} proxy: {{ $deployment.config.proxy | quote }} {{- end }} {{- if $deployment.config.headerRules }} header-rules: {{- toYaml $deployment.config.headerRules | nindent 6 }} {{- end }}{{- end }}{{- end }}Step 5: (Optional) Create the Headless Service Template
Section titled “Step 5: (Optional) Create the Headless Service Template”If you need DNS resolution for StatefulSet pods, create templates/service.yaml. This file is required when you set enabledStatefulSet: true in your values file.
{{- range $name, $deployment := .Values.Deployments }}{{- if $deployment.enabled }}{{- if $deployment.enabledStatefulSet }}---apiVersion: v1kind: Servicemetadata: name: {{ (index $.Values.Application $name).name }}-{{ $.Values.Environment.name }}-headless namespace: {{ (index $.Values.Namespace $name).name }} labels: app: {{ (index $.Values.Application $name).name }}-{{ $.Values.Environment.name }}spec: clusterIP: None selector: app: {{ (index $.Values.Application $name).name }}-{{ $.Values.Environment.name }} ports: - protocol: TCP port: 80 targetPort: 80{{- end }}{{- end }}{{- end }}Configure Your Values File
Section titled “Configure Your Values File”Create a values.yaml file in the testsigma-tunnel/ directory. Use the following example as a starting point, and replace the placeholder values with your own.
Environment: name: production # Environment name (production, staging, etc.)
Application: myTunnel: name: tunnel-client-mycompany # Unique application name
Namespace: myTunnel: name: my-namespace # Kubernetes namespace for this tunnel
Deployments: myTunnel: enabled: true replicas: 1 poolType: app # Must match a node label: pool-type=<value> enabledStatefulSet: true # Set to true to create the headless Service containers: image: testsigmainc/testsigma-tunnel version: amd64-latest imagePullPolicy: Always config: key: "<YOUR_TUNNEL_API_KEY>" # API key from the Testsigma dashboard tunnelName: "my-tunnel" # Name that appears in the Testsigma UI verbose: true # Enable verbose logging delegateSslValidation: false # Set true to skip SSL certificate validation proxy: "" # HTTP proxy URL (leave empty if not required)Replace the following placeholder values in your file:
- <YOUR_TUNNEL_API_KEY>: Enter the API key from Settings > Tunnels in the Testsigma application.
- tunnel-client-mycompany: Enter a name that uniquely identifies this tunnel client in your cluster.
- my-namespace: Enter the Kubernetes namespace where you want to deploy the tunnel.
- my-tunnel: Enter the tunnel name that will appear in the Testsigma UI.
Deploy the Chart
Section titled “Deploy the Chart”-
Create the target namespace if it does not already exist:
Terminal window kubectl create namespace my-namespace --dry-run=client -o yaml | kubectl apply -f - -
Install the Helm chart from inside the testsigma-tunnel/ directory:
Terminal window helm install my-tunnel . -n my-namespace
Verify the Deployment
Section titled “Verify the Deployment”Run the following commands to confirm that the tunnel deployed successfully.
-
Check the StatefulSet status:
Terminal window kubectl get statefulset -n my-namespace -
Check pod status:
Terminal window kubectl get pods -n my-namespace -
View tunnel client logs to confirm the tunnel registered with the Testsigma server:
Terminal window kubectl logs -n my-namespace <pod-name> -f
In the logs, look for a message confirming that the tunnel registered successfully with the Testsigma server. If the tunnel does not appear in the Testsigma UI after a few minutes, see Troubleshoot.
Configuration Reference
Section titled “Configuration Reference”Top-level Keys
Section titled “Top-level Keys”| Key | Type | Required | Description |
|---|---|---|---|
| Environment.name | string | Yes | Environment identifier (for example, production or staging). Used in naming all Kubernetes resources. |
Deployment Keys (Deployments.<name>)
Section titled “Deployment Keys (Deployments.<name>)”| Key | Type | Required | Default | Description |
|---|---|---|---|---|
| enabled | boolean | Yes | — | Enables or disables this deployment. When false, all associated resources are removed. |
| replicas | integer | Yes | — | Number of tunnel client pods to run. |
| poolType | string | Yes | — | Node selector value for pool-type. Must match an existing node label. |
| enabledStatefulSet | boolean | No | false | When true, creates a headless Service for StatefulSet DNS resolution. |
| containers.image | string | Yes | — | Docker image name for the tunnel client. |
| containers.version | string | Yes | — | Docker image tag. |
| containers.imagePullPolicy | string | Yes | — | Image pull policy. Accepted values: Always, IfNotPresent, or Never. |
| config.key | string | Yes | — | API key from the Testsigma dashboard. |
| config.tunnelName | string | Yes | — | Tunnel name visible in the Testsigma UI. |
| config.verbose | boolean | No | true | Enables verbose logging for the tunnel client. |
| config.delegateSslValidation | boolean | No | false | When true, the tunnel skips SSL certificate validation for upstream requests. |
| config.proxy | string | No | "" | HTTP proxy URL for outbound traffic. Leave empty if a proxy is not required. |
| config.headerRules | list | No | — | Automatic HTTP header injection rules. See Inject HTTP Headers for details. |
Application and Namespace Keys
Section titled “Application and Namespace Keys”Each key that you define under Deployments must have a matching entry under both Application and Namespace. The keys must match exactly.
Application: myTunnel: # Must match the Deployments key name: tunnel-client-mycompany
Namespace: myTunnel: # Must match the Deployments key name: my-namespaceManage Secrets
Section titled “Manage Secrets”The config.key field contains an API key. Treat this value as a secret and never commit it in plain text to version control.
Option 1: Pass the Key at Deploy Time
Section titled “Option 1: Pass the Key at Deploy Time”Use the —set flag with helm install or helm upgrade to inject the API key without storing it in your values file:
helm install my-tunnel . -f my-values.yaml \ --set 'Deployments.myTunnel.config.key=eyJhbGciOi...' \ -n my-namespaceFor deployments with multiple tunnels, pass each key separately:
helm install my-tunnel . -f my-values.yaml \ --set 'Deployments.siteA.config.key=eyJhbGciOi...' \ --set 'Deployments.siteB.config.key=eyJhbGciOi...' \ -n my-namespaceOption 2: Use a Secrets Manager
Section titled “Option 2: Use a Secrets Manager”For GitOps workflows, use Sealed Secrets or External Secrets Operator to manage the API key:
- Store the API key in your secrets backend (for example, AWS Secrets Manager or HashiCorp Vault).
- Create an ExternalSecret resource that syncs the key into a Kubernetes Secret.
- Reference the secret in your values file or inject it with —set in your CI/CD pipeline.
Option 3: Use CI/CD Pipeline Variables
Section titled “Option 3: Use CI/CD Pipeline Variables”Store the API key as a pipeline secret and inject it during deployment. The following example shows a GitHub Actions workflow step:
- name: Deploy tunnel run: | helm upgrade --install my-tunnel ./testsigma-tunnel \ -f my-values.yaml \ --set "Deployments.myTunnel.config.key=${{ secrets.TUNNEL_API_KEY }}" \ -n my-namespaceRotate an API Key
Section titled “Rotate an API Key”When an API key expires or needs to be replaced:
- Obtain a new key from Settings > Tunnels in the Testsigma application.
- Update the key using your chosen secret management method.
- Run
helm upgradeto apply the updated ConfigMap. - The pods restart automatically and pick up the new configuration.
Scale and Manage the Tunnel
Section titled “Scale and Manage the Tunnel”Increase Replicas
Section titled “Increase Replicas”To handle more concurrent test traffic, increase the replicas value in your values file and apply the change:
# In values.yamlDeployments: myTunnel: replicas: 3 # Increase from 1 to 3 podshelm upgrade my-tunnel . -f my-values.yaml -n my-namespaceDecrease Replicas
Section titled “Decrease Replicas”To reduce the number of active pods, lower the replicas value in your values file and run helm upgrade. Alternatively, scale down directly with kubectl for a temporary change:
kubectl scale statefulset tunnel-client-mycompany-production-statefulset \ --replicas=1 -n my-namespaceStop the Tunnel Without Removing It
Section titled “Stop the Tunnel Without Removing It”To shut down the tunnel temporarily while keeping the Helm release and configuration intact, scale the StatefulSet to zero:
kubectl scale statefulset tunnel-client-mycompany-production-statefulset \ --replicas=0 -n my-namespaceTo make the change permanent through your values file, set replicas: 0 and run helm upgrade.
Disable a Specific Tunnel
Section titled “Disable a Specific Tunnel”When you manage multiple tunnels from a single values file, you can disable an individual tunnel by setting enabled: false. This removes all Kubernetes resources associated with that tunnel:
Deployments: siteA: enabled: true # Keep this tunnel running siteB: enabled: false # Remove this tunnel's resourceshelm upgrade my-tunnel . -f my-values.yaml -n my-namespaceRemove All Tunnel Resources
Section titled “Remove All Tunnel Resources”To completely uninstall the Helm release and remove all associated Kubernetes resources:
helm uninstall my-tunnel -n my-namespaceUpdate the Tunnel
Section titled “Update the Tunnel”To upgrade the tunnel client image or update the configuration, you must scale down to zero replicas before running helm upgrade. This ensures the tunnel cleanly deregisters from the Testsigma server before the updated version starts.
-
Scale down to zero pods:
Terminal window kubectl scale statefulset tunnel-client-mycompany-production-statefulset \--replicas=0 -n my-namespace -
Wait for all pods to terminate:
Terminal window kubectl get pods -n my-namespace \-l app=tunnel-client-mycompany-production -w -
Update your values file with the new image version or configuration changes:
Deployments:myTunnel:containers:version: amd64-1.2.0 # Updated version tag -
Apply the upgrade:
Terminal window helm upgrade my-tunnel . -n my-namespace -
Scale back up to the desired number of replicas:
Terminal window kubectl scale statefulset tunnel-client-mycompany-production-statefulset \--replicas=<desired-count> -n my-namespace -
Verify the upgrade by checking pod status and logs:
Terminal window # Confirm pods are running with the new imagekubectl get pods -n my-namespace -l app=tunnel-client-mycompany-production# Check logs to confirm successful registrationkubectl logs -n my-namespace \tunnel-client-mycompany-production-statefulset-0 -f
Advanced Configuration
Section titled “Advanced Configuration”Deploy Multiple Tunnels
Section titled “Deploy Multiple Tunnels”You can manage multiple tunnel clients from a single values file. Each tunnel requires its own key under Deployments, Application, and Namespace:
Application: siteA: name: tunnel-client-site-a siteB: name: tunnel-client-site-b
Namespace: siteA: name: my-namespace siteB: name: my-namespace
Deployments: siteA: enabled: true replicas: 1 poolType: app containers: image: testsigmainc/testsigma-tunnel version: amd64-latest imagePullPolicy: Always config: key: "<API_KEY_FOR_SITE_A>" tunnelName: "site-a-tunnel" verbose: true siteB: enabled: true replicas: 1 poolType: app containers: image: testsigmainc/testsigma-tunnel version: amd64-latest imagePullPolicy: Always config: key: "<API_KEY_FOR_SITE_B>" tunnelName: "site-b-tunnel" verbose: trueInject HTTP Headers
Section titled “Inject HTTP Headers”Configure the tunnel to automatically inject HTTP headers into requests for specific hostnames. This is useful for adding authentication credentials or custom metadata to outbound requests.
config: headerRules: # Inject a Basic Auth header for a specific host - hostname: "internal-app.example.com" headers: X-TS-BASIC-AUTH-HEADER: "Basic dXNlcm5hbWU6cGFzc3dvcmQ="
# Inject custom headers for an API host - hostname: "api.example.com" headers: X-Custom-Auth: "my-token" X-Environment: "staging"Route Traffic Through a Proxy
Section titled “Route Traffic Through a Proxy”If your cluster requires an outbound HTTP proxy, add the proxy URL to the config section of your values file:
config: proxy: "http://proxy.internal.example.com:8080"Troubleshoot
Section titled “Troubleshoot”| Symptom | Possible Cause | Resolution |
|---|---|---|
| Pod stuck in Pending | No nodes match the pool-type selector. | Run kubectl get nodes --show-labels and verify that at least one node has the expected pool-type label. |
| Pod in CrashLoopBackOff | Invalid API key or malformed tunnel configuration. | Run kubectl logs <pod-name> -n my-namespace to inspect the error output. Verify that config.key and config.tunnelName are correct. |
| Tunnel not visible in Testsigma UI | Incorrect tunnelName value or expired API key. | Confirm that config.tunnelName matches what you expect to see in the UI. Obtain a fresh API key from Settings > Tunnels if the key has expired. |
| Connection timeouts during testing | Network policy or proxy misconfiguration. | Verify that config.proxy is set correctly and that cluster network policies permit outbound connections to the Testsigma platform. |
| SSL errors during test execution | The upstream application uses a self-signed certificate. | Set config.delegateSslValidation: true in your values file and run helm upgrade to apply the change. |
If you cannot resolve the issue using the steps above, collect the full logs from the affected pod and contact Testsigma support.
kubectl describe statefulset tunnel-client-mycompany-production-statefulset \ -n my-namespace
kubectl logs -n my-namespace \ statefulset/tunnel-client-mycompany-production-statefulset -f