Skip to content

Instantly share code, notes, and snippets.

@managedkaos
Created April 14, 2025 18:47
Show Gist options
  • Save managedkaos/046f979cb0ea06120810667f48d29872 to your computer and use it in GitHub Desktop.
Save managedkaos/046f979cb0ea06120810667f48d29872 to your computer and use it in GitHub Desktop.
A tool for creating a temporary Cloudflare tunnel allowing HTTP access to servers in private subnets.
import argparse
import json
import os
import shutil
import signal
import subprocess
import sys
import boto3
def check_session_manager_plugin():
if shutil.which("session-manager-plugin") is None:
print("Error: AWS Session Manager plugin is not installed.")
print("\nPlease install the AWS Session Manager plugin:")
print("\nFor macOS (using Homebrew):")
print("brew install --cask session-manager-plugin")
print("\nFor Linux (using curl):")
print(
"curl 'https://s3.amazonaws.com/session-manager-downloads/plugin/latest/mac/sessionmanager-bundle.zip' -o 'sessionmanager-bundle.zip'"
)
print("unzip sessionmanager-bundle.zip")
print(
"sudo ./sessionmanager-bundle/install -i /usr/local/sessionmanagerplugin -b /usr/local/bin/session-manager-plugin"
)
print("\nFor Windows:")
print(
"Download and install from: https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html"
)
sys.exit(1)
def parse_arguments():
parser = argparse.ArgumentParser(
description="Start a Cloudflare tunnel to a server in a private subnet"
)
parser.add_argument(
"-t",
"--tag",
required=True,
help="Value of the Name tag to use for selecting a server to connect to",
)
# Add an optional argument for the port to use for the Cloudflare tunnel
parser.add_argument(
"-p",
"--port",
type=int,
default=8000,
help="Port to use for the Cloudflare tunnel (default: 8000)",
)
# Add an optional argument for the AWS region
parser.add_argument(
"-r",
"--region",
default=os.environ.get("AWS_REGION", "us-east-1"),
help="AWS region to use (default: us-east-1)",
)
return parser.parse_args()
def get_backend_instance(tag, region):
ec2 = boto3.client("ec2", region_name=region)
print(f"## Looking for server with Name tag: {tag}")
response = ec2.describe_instances(
Filters=[
{"Name": "tag:Name", "Values": [tag]},
{"Name": "instance-state-name", "Values": ["running"]},
]
)
instances = [
instance["InstanceId"]
for reservation in response["Reservations"]
for instance in reservation["Instances"]
]
if not instances:
print(f"\tNo running instances found with Name tag: {tag}")
sys.exit(1)
# Return the first instance found
return instances[0]
def main():
# Check if session-manager-plugin is installed
check_session_manager_plugin()
args = parse_arguments()
instance_id = get_backend_instance(args.tag, args.region)
print(f"## Starting Cloudflare tunnel session to instance: {instance_id}")
print("## Press CTRL+C to end the session")
print("## (If the session fails, please close the session and try again)")
# Set up signal handling to pass SIGINT (CTRL+C) to the subprocess
def signal_handler(signum, frame):
# Pass the signal to the subprocess
pass
signal.signal(signal.SIGINT, signal_handler)
ssm = boto3.client("ssm", region_name=args.region)
try:
# Start the SSM session with Cloudflare tunnel command
response = ssm.start_session(
Target=instance_id,
DocumentName="AWS-StartInteractiveCommand",
Parameters={"command": ["cloudflared tunnel --url http://localhost:" + str(args.port)]},
)
# Extract the session details
session_id = response["SessionId"]
token_value = response["TokenValue"]
stream_url = response["StreamUrl"]
# Start the session using the AWS CLI
subprocess.run(
[
"session-manager-plugin",
json.dumps(
{
"SessionId": session_id,
"TokenValue": token_value,
"StreamUrl": stream_url,
}
),
args.region,
"StartSession",
]
)
except Exception as e:
print(f"Error connecting to instance {instance_id}: {str(e)}")
sys.exit(1)
if __name__ == "__main__":
main()

Use Cloudflare Tunnel to connect to EC2 Instance in Private Subnet

A tool for creating a temporary Cloudflare tunnel allowing HTTP access to servers in private subnets.

1. Install the AWS Session Manager Plugin on the Local System

The AWS Session Manager Plugin is required to connect to the EC2 instance.

Follow this link to install the plugin:

2. Install Cloudflare Tunneler on the EC2 Instance

The target server needs to have the cloudflared binary installed.

Add these commands to your launch template or user data:

# NOTE: Install wget if not already installed
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 \
    -O /usr/local/bin/cloudflared
chmod +x /usr/local/bin/cloudflared
cloudflared --version

3. Start the Service on the EC2 Instance

Run the target service on the EC2 as needed.

4. Create a Cloudflare Tunnel

Use the cloudflare-tunnel.py script to create a tunnel.

The script expects these parameters:

  • --tag or -t: The value of the Name tag on at least one running instance.
  • --port or -p: The port to forward to tunnel to on the server's localhost. Default is 8000.
  • --region or -r: The region to create the tunnel in. Default is us-east-1.

For example:

python3 ./cloudflare-tunnel.py --tag backend-dev --port 3000

5. Access the Tunnel

If the tunnel is created successfully, you will see output similar to the following:

2025-04-14T18:15:19Z INF +--------------------------------------------------------------------------------------------+
2025-04-14T18:15:19Z INF |  Your quick Tunnel has been created! Visit it at (it may take some time to be reachable):  |
2025-04-14T18:15:19Z INF |  https://rocks-authorized-packages-employed.trycloudflare.com                              |
2025-04-14T18:15:19Z INF +--------------------------------------------------------------------------------------------+

You may need to wait a 1-2 minutes for the tunnel to be reachable.

Follow the link to access the tunnel.

If the tunnel fails to connect, type CTRL+C to stop the script and run it again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment