# Exposing to the Internet

## Why do we want to expose things?

We want to expose services to the internet for people to use! For example, a gitlab instance such as <https://git.yadunut.dev/> should be publicly available!

However, Yadunand might not want to expose his `TopSecretService` running on port `8080` to the internet, so we have to be careful of how we are exposing these services.

## Cloudflare Tunnels

### How does it work?

<figure><img src="https://2807223923-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTUqAJOgHs57S8lmqdxRV%2Fuploads%2F90PdTTbGwiOg48i7aNf4%2Fimage.png?alt=media&#x26;token=dd3320fd-939d-4997-afac-58f81431328b" alt=""><figcaption></figcaption></figure>

Cloudflare Tunnel uses an outbound-only connection model to enable bidirectional communication. When you install and run `cloudflared`, `cloudflared` initiates an outbound connection through your firewall from the origin to the Cloudflare global network.

Most firewalls (and ours!) allow outbound traffic. `cloudflared` takes advantage of this standard by connecting out to the Cloudflare network from the server you installed `cloudflared` on. You can then configure your firewall to allow only these outbound connections and block all inbound traffic, effectively blocking access to your origin from anything other than Cloudflare.&#x20;

All traffic will then be securely routed through the tunnel.

***

#### We will not be setting up via the Cloudflare Dashboard

For some reason, it requires us to add a card, even though it is a free service :<&#x20;

But no fear, we can do it on the cli for free! WAHOOo

{% stepper %}
{% step %}

### Installation On Ubuntu 24.04&#x20;

For other Linux Distros: <https://pkg.cloudflare.com/index.html>

```zsh
# Add cloudflare gpg key
sudo mkdir -p --mode=0755 /usr/share/keyrings
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null

# Add this repo to your apt repositories
# Stable
echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared noble main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
# Nightly
echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://next.pkg.cloudflare.com/cloudflared noble main' | sudo tee /etc/apt/sources.list.d/cloudflared.list

# install cloudflared
sudo apt-get update && sudo apt-get install cloudflared
```

{% endstep %}

{% step %}

### For the rest of the setup, we are following:

{% embed url="<https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/create-local-tunnel/>" %}

If you did not manage to register a domain, you can skip the next few steps, instead just expose your services now with [Cloudflare Temporary Tunnels](#quickly-exposing-services-to-the-internet)
{% endstep %}

{% step %}

### Login

```bash
sudo su 
cloudflared tunnel login
```

It will then launch a window on cloudflare dashboard, where you can select a domain to authorize.

<figure><img src="https://2807223923-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTUqAJOgHs57S8lmqdxRV%2Fuploads%2F298fAqfWmW63odAEljFl%2FCleanShot%202026-03-18%20at%2000.18.16%402x.png?alt=media&#x26;token=9335b42e-c14b-4906-95e6-167c725cb51e" alt="" width="375"><figcaption></figcaption></figure>
{% endstep %}

{% step %}

### Create a Tunnel

```bash
cloudflared tunnel create <NAME>
```

This will create a tunnel with a unique UUID, and generate a tunnel credential file.

**Take note of this UUID, as we will be using it later.**

You can view your existing tunnels using: `cloudflared tunnel list`&#x20;

<details>

<summary>If you need to delete a tunnel</summary>

```bash
cloudflared tunnel cleanup <UUID>
cloudflared tunnel delete <UUID>
```

If the tunnel is still running, you need to run these to kill the process:

```bash
systemctl stop cloudflared
pkill -f "cloudflared tunnel"
```

</details>
{% endstep %}

{% step %}

### Creating Tunnel Configuration file

The configuration file should be created at `/etc/cloudflared/config.yml`

This is what the file can look like:

```bash
sudo mkdir -p /etc/cloudflared
sudo vim /etc/cloudflared/config.yml
```

{% code title="config.yml" %}

```yaml
### THIS IS AN EXAMPLE CONFIG FILE ###

tunnel: 6ff42ae2-765d-4adf-8112-31c55c1551ef
credentials-file: /root/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json
origincert: /root/.cloudflared/cert.pem

ingress:
  # Rules map traffic from a hostname to a local service:
  - hostname: example.com
    service: http://localhost:8000
  # Rules can match the request's path to a regular expression:
  - hostname: static.example.com
    path: \.(jpg|png|css|js)$
    service: http://localhost:8001
  # Rules can match the request's hostname to a wildcard character:
  - hostname: "*.example.com"
    service: http://localhost:8002
  # An example of a catch-all rule:
  - service: http://localhost:8003
```

{% endcode %}

In our case, let's set up a config which points the `docker` subdomain to `https://localhost:9443`

{% code title="config.yml" %}

```yaml
tunnel: TUNNEL-UUID
credentials-file: /root/.cloudflared/<TUNNEL-UUID>.json
origincert: /root/.cloudflared/cert.pem

ingress:
  # Let's map the subdomain docker here.
  - hostname: docker.<domain-name>.<tld>
    service: https://localhost:9443
    originRequest:
      noTLSVerify: true
  
    
  # Catch all everything else to 404
  - service: http_status:404
```

{% endcode %}

Note that we had to add an additional config of noTLSVerify. This is because&#x20;

&#x20;[Full Configuration File Docs (Cloudflare)](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/configuration-file/)
{% endstep %}

{% step %}

### Configure DNS

In our example, since we have configured cloudflared to tunnel traffic from: `docker.<domain-name>.tld -> https://localhost:9443` , we need to configure out cloudflare DNS to also match this

{% code title="" %}

```bash
sudo cloudflared tunnel route dns <TUNNEL-NAME OR TUNNEL-UUID> docker.<domain-name>.<tld>
```

{% endcode %}
{% endstep %}

{% step %}

### Start running Cloudflared

#### Install

{% code title="" %}

```bash
sudo cloudflared service install
```

{% endcode %}

Start service

{% code title="" %}

```bash
sudo systemctl start cloudflared
```

{% endcode %}

Check service

{% code title="" %}

```bash
sudo systemctl status cloudflared
```

{% endcode %}
{% endstep %}

{% step %}

### How to add new routes&#x20;

1\) Edit the config and add the new domain and which localhost its pointed to

{% code title="" %}

```bash
sudo vim /etc/cloudflared/config.yml
```

{% endcode %}

2\) Add new DNS route

{% code title="" %}

```bash
sudo cloudflared tunnel route dns <TUNNEL-UUID or TUNNEL-NAME> docker.<domain-name>.<tld>
```

{% endcode %}

3\) Reload cloudflared

{% code title="" %}

```bash
sudo systemctl restart cloudflared
```

{% endcode %}

4\) Check the status after

{% code title="" %}

```bash
sudo systemctl status cloudflared
```

{% endcode %}
{% endstep %}
{% endstepper %}

### We should be able to view portainer now!

Nothing shows up? This might be the Portainer instance timed out for security purposes, to re-enable your Portainer instance, you will need to restart Portainer.

<details>

<summary>To do this, we can run: </summary>

{% code title="" %}

```bash
docker restart portainer
```

{% endcode %}

This might not work if the container is not named portainer. In that case, you should run:

{% code title="" %}

```bash
docker ps
docker logs <container-id>
docker restart <container-id>
```

{% endcode %}

</details>

We can also troubleshoot other problems on the Cloudflare Dashboard. Common culprits are DNS as well. (Are you sure you set that up?)

***

### Quickly exposing services to the internet

Cloudflare provides a service called [Quick Tunnels](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/trycloudflare/). This is usually meant for development environments, so it is not a good permanent solution!

It will generate a domain that you can use, which is pointed towards your ip address:

You will however, probably need to run these in separate tmux windows.. (It really isn't ideal!)&#x20;

```bash
sudo cloudflared tunnel  --config /dev/null --url http://127.0.0.1:<port-number>
```

To tunnel to portainer specifically, you can run:

```bash
sudo cloudflared tunnel --config /dev/null --no-tls-verify --url https://127.0.0.1:9443 
```

:warning:: The `--config /dev/null`  flag is needed because cloudflared will sometimes use the signed in credentials rather than creating a quick tunnel.

### Additional Docs:

{% embed url="<https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/create-local-tunnel/>" %}

{% embed url="<https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/configuration-file/>" %}

{% embed url="<https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/tunnel-useful-commands/>" %}

## What are some other ways to do this?

This will not be covered in the scope of this workshop, but there are many other ways to do this as well.&#x20;

### VPNs: Wireguard/ Tailscale

Usually, VPNs are used when we only want our services exposed to authenticated users with a VPN client. This is the same principle that SoC uses, for us to connect into SoC internal network via SoC VPN.&#x20;

<figure><img src="https://2807223923-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTUqAJOgHs57S8lmqdxRV%2Fuploads%2FblYLNVfNv8Xgk7duhhNn%2FCleanShot%202026-03-16%20at%2021.19.38%402x.png?alt=media&#x26;token=6f4e4330-a7a9-4c6d-b3eb-0353659d5a88" alt=""><figcaption></figcaption></figure>

{% embed url="<https://tailscale.com/blog/how-tailscale-works>" %}

### Reverse Proxies: Traefik/ Caddy

<figure><img src="https://2807223923-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTUqAJOgHs57S8lmqdxRV%2Fuploads%2FYQOJj6JNEmHHF89fLr0v%2Funknown.png?alt=media&#x26;token=3f9e86fb-98c8-4862-92b2-0d2f727554e8" alt=""><figcaption></figcaption></figure>

{% embed url="<https://doc.traefik.io/traefik/>" %}

## Additional Readings
