Skip to content

Bastion Host Access Guide

Overview

The bastion host is the only way to access the private GKE cluster. This guide explains how to use it effectively.

What is a Bastion Host?

A bastion host (also called a jump host) is a server that acts as a secure gateway to access private resources. In this lab:

  • The bastion has an external IP for SSH access
  • The bastion is in the same VPC as the GKE cluster
  • The bastion can access the private GKE master endpoint
  • You SSH to the bastion, then use kubectl from there

Quick Access

Automated Method

bash
cd labs/03-private-network-deployment
./scripts/bastion-access.sh

This script will:

  1. Get bastion details from Terraform output
  2. Provide the SSH command
  3. Optionally SSH to the bastion automatically

Manual Method

bash
# Get bastion details
cd labs/03-private-network-deployment
terraform output

# SSH to bastion
gcloud compute ssh <bastion-name> \
  --zone <bastion-zone> \
  --project <project-id>

First-Time Setup

1. Get Cluster Credentials

Once connected to the bastion:

bash
# Get cluster credentials using internal IP
gcloud container clusters get-credentials <cluster-name> \
  --region <region> \
  --project <project-id> \
  --internal-ip

The --internal-ip flag is crucial - it uses the private endpoint instead of the public one.

2. Verify Access

bash
# Test kubectl access
kubectl get nodes
kubectl get namespaces

3. Deploy Applications

bash
# Deploy Argo Workflows
./scripts/deploy-argo.sh

# Or manually
helm repo add argo https://argoproj.github.io/argo-helm
helm install argo-workflows argo/argo-workflows \
  --namespace argo \
  --create-namespace

Common Workflows

Port Forwarding

Access services from your local machine via the bastion:

bash
# From your local machine, create SSH tunnel
gcloud compute ssh <bastion-name> \
  --zone <bastion-zone> \
  --project <project-id> \
  --ssh-flag="-L 8080:localhost:8080"

# In another terminal, from bastion
kubectl port-forward -n argo svc/argo-workflows-server 8080:2746

# Access from local browser: http://localhost:8080

Copying Files

bash
# Copy file to bastion
gcloud compute scp local-file.txt <bastion-name>:~/ \
  --zone <bastion-zone> \
  --project <project-id>

# Copy file from bastion
gcloud compute scp <bastion-name>:~/remote-file.txt ./ \
  --zone <bastion-zone> \
  --project <project-id>

Running Commands Remotely

bash
# Run command on bastion without SSH session
gcloud compute ssh <bastion-name> \
  --zone <bastion-zone> \
  --project <project-id> \
  --command "kubectl get pods -n argo"

Security Best Practices

1. Restrict SSH Access

IMPORTANT: Update bastion_authorized_networks in terraform.tfvars:

hcl
# Restrict to your IP or company network
bastion_authorized_networks = ["YOUR.IP.ADDRESS/32"]

2. Use OS Login

The bastion has OS Login enabled, which provides:

  • Centralized SSH key management
  • Audit logging
  • IAM-based access control

3. Rotate Keys Regularly

bash
# Generate new SSH key
ssh-keygen -t rsa -b 4096 -C "your-email@example.com"

# Add to GCP project
gcloud compute os-login ssh-keys add \
  --key-file ~/.ssh/id_rsa.pub \
  --project <project-id>

4. Use IAM for Access Control

Grant bastion access only to authorized users:

bash
# Grant access to specific user
gcloud projects add-iam-policy-binding <project-id> \
  --member="user:user@example.com" \
  --role="roles/compute.instanceAdmin"

5. Monitor Access

bash
# View SSH access logs
gcloud logging read "resource.type=gce_instance AND resource.labels.instance_id=<instance-id>" \
  --limit 50 \
  --format json

Troubleshooting

Cannot SSH to Bastion

Problem: Connection timeout or refused

Solutions:

  1. Check firewall rules: gcloud compute firewall-rules list
  2. Verify your IP is in authorized networks
  3. Check bastion is running: gcloud compute instances list
  4. Verify external IP: gcloud compute instances describe <bastion-name>

Cannot Access Cluster from Bastion

Problem: kubectl commands fail with connection errors

Solutions:

  1. Verify you used --internal-ip flag
  2. Check cluster endpoint: gcloud container clusters describe <cluster-name>
  3. Verify bastion is in authorized network
  4. Check firewall rules allow bastion to master

kubectl Not Found on Bastion

Problem: kubectl: command not found

Solutions:

  1. The bastion should have kubectl pre-installed via startup script
  2. If missing, install manually:
    bash
    curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
    chmod +x kubectl
    sudo mv kubectl /usr/local/bin/

Permission Denied

Problem: kubectl returns permission errors

Solutions:

  1. Verify service account permissions
  2. Check IAM bindings: gcloud projects get-iam-policy <project-id>
  3. Ensure bastion service account has container.developer role

Alternative Access Methods

VPN/Interconnect

For production, consider:

  • Cloud VPN: Connect on-premises to VPC
  • Cloud Interconnect: Dedicated connection
  • Private Service Connect: Connect other GCP services

Cloud Shell

You can also use Cloud Shell with private cluster access:

bash
# Enable private cluster access in Cloud Shell
gcloud container clusters get-credentials <cluster-name> \
  --region <region> \
  --project <project-id> \
  --internal-ip

Note: This requires Cloud Shell to be in the same VPC or connected via VPN.

Production Considerations

High Availability

  • Deploy multiple bastion hosts in different zones
  • Use a managed instance group for auto-scaling
  • Implement health checks and auto-recovery

Monitoring

  • Monitor bastion CPU, memory, disk
  • Alert on failed SSH attempts
  • Track kubectl usage and commands

Backup

  • Regularly backup bastion configuration
  • Document all installed tools and versions
  • Keep bastion OS and tools updated

Additional Resources

Released under the MIT License.