Published on

Exposing resources securely with Argo Tunnel

5 min read


In this article, we'll see how to leverage Cloudflare's argo tunnels without publicly exposing our services. In this setup we'll use EC2 as an example, but this is works with Fargate as well.

Dev Env CF & EC2

What is an Argo Tunnel?

Argo Tunnel provides a secure way to connect your origin to Cloudflare without a publicly routable IP address. With Argo Tunnel, you do not expose an external IP from your infrastructure to the Internet. Instead, a lightweight daemon runs in your infrastructure and creates outbound-only connections to Cloudflare's edge.

There are tons of use cases like using exposing internal applications, replacing VPN setup with Cloudflare Access possibilities are endless!

Currently, I use this setup for developing remotely on EC2's securely.


What is Cloudflare Access?

If we want to only expose this to your team internally, we can configure a zero trust access with Cloudflare access

no trust access

We'll use Terraform to define our cloud resources

Let's define the providers aws and cloudflare which we'll be using:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 2.7.0"
    cloudflare = {
      source  = "cloudflare/cloudflare"
      version = "~> 2.26.0"
  required_version = ">= 0.13"

provider "cloudflare" {
  api_token = var.cloudflare_api_token

We can define our api keys and secrets as variables in

variable "region" {
  type    = string
  default = "us-west-2"

variable "cloudflare_api_token" {
  type        = string
  description = "Cloudflare api token"
  default     = "<YOUR_API_TOKEN>"

variable "cloudflare_account_id" {
  type        = string
  description = "Cloudflare account id"
  default     = "<YOUR_ACCOUNT_ID>"

variable "cloudflare_zone_id" {
  type        = string
  description = "Cloudflare zone id"
  default     = "<YOUR_ZONE_ID>"

variable "subdomain" {
  type        = string
  description = "your subdomain"
  default     = "pretty-cool"

In our file, we'll define resources. We'll first create an argo tunnel and add a cloudflare proxied record CNAME for it so that we can use in our user_data init script.

resource "random_id" "argo_secret" {
  byte_length = 35

resource "cloudflare_argo_tunnel" "argo_tunnel" {
  account_id = var.cloudflare_account_id
  name       = "${var.subdomain}-tunnel"
  secret     = random_id.argo_secret.b64_std

resource "cloudflare_record" "http_app" {
  zone_id = var.cloudflare_zone_id
  name    = var.subdomain
  value   = "${}"
  type    = "CNAME"
  proxied = true
data "template_file" "init" {
  template = file("my-project/init.tpl")

  vars = {
    subdomain   = var.subdomain
    domain      = var.cloudflare_zone,
    account     = var.cloudflare_account_id,
    tunnel_id   =,
    tunnel_name =,
    secret      = random_id.argo_secret.b64_std

resource "aws_instance" "api" {
  ami           = "ami-03d5c68bab01f3496"
  instance_type = "t3.large"
  user_data = data.template_file.init.rendered

In the init.tpl we'll simply install cloudflared and create a config.ymland then run it in background as our EC2 starts.


# Logs are availabe at /var/logs/cloud-init-output.log

# Install cloudflared
sudo dpkg -i cloudflared-stable-linux-amd64.deb

# Create required dirs
mkdir ~/.cloudflared
touch ~/.cloudflared/cert.json
touch ~/.cloudflared/config.yml

# Create cert.json
cat > ~/.cloudflared/cert.json << "EOF"
    "AccountTag"   : "${account}",
    "TunnelID"     : "${tunnel_id}",
    "TunnelName"   : "${tunnel_name}",
    "TunnelSecret" : "${secret}"

# Add a config file
cat > ~/.cloudflared/config.yml << "EOF"
tunnel: ${tunnel_id}
credentials-file: /etc/cloudflared/cert.json
logfile: /var/log/cloudflared.log
loglevel: info

  - hostname: ${subdomain}.${domain}
    service: http://localhost:80
  - service: http_status:404

# Start Cloudflared service
sudo cloudflared service install
sudo cp -via ~/.cloudflared/cert.json /etc/cloudflared/
sudo service cloudflared start

Once our EC2 starts, we'll be able to access it via and all without allowing any ingress in the security group!

Hope this was helpful!

© 2024 Karan Pratap Singh