# Squid: Internet Forwarding to Remote VM

LLMS index: [llms.txt](/en/llms.txt)

---

Your VM in the cloud has no public IP or internet access is blocked via NAT, but the deployment needs wget/curl from inside. Squid on an intermediate host with a decent uplink solves this in ten minutes.

## Why This Is Needed

I forward internet through Squid when a VM sits in an isolated network segment. An intermediate host with a public IP and network access becomes the proxy server. The application on the remote machine routes traffic through the tunnel.

Real-world cases: test environments without external connectivity, CI/CD agents in a private subnet, temporary traffic routing for debugging.

## Installing and Basic Squid Setup

Install on the intermediate host (Ubuntu/Debian):

```bash
apt update && apt install -y squid
```

The default config lives in `/etc/squid/squid.conf`. Minimum working config:

```bash
# /etc/squid/squid.conf
http_port 3128

acl localnet src 10.0.0.0/8
acl localnet src 172.16.0.0/12
acl localnet src 192.168.0.0/16

http_access allow localnet
http_access deny all
```

Enable and start:

```bash
systemctl enable --now squid
```

Verify the port is listening:

```bash
ss -tlnp | grep 3128
```

## ACL and Port Configuration

If access should be only via SSH tunnel from a specific address, replace `localnet` with the exact IP:

```bash
# Proxy accessible only from this address
acl tunneled src 203.0.113.50

http_access allow tunneled
http_access deny all
```

To change the port:

```bash
http_port 8080
```

After config changes, reload without restarting:

```bash
squid -k reconfigure
```

> [!NOTE]
> If Squid fails to start after changes, check the log: `journalctl -u squid -n 50`.

## SSH Tunnel to VM

On the remote machine, establish a tunnel to the intermediate host:

```bash
ssh -N -L 3128:localhost:3128 user@proxy-host
```

`-N` — don't open a shell, forwarding only. `-L` binds local port 3128 to localhost:3128 on the remote host.

For background:

```bash
ssh -N -L 3128:localhost:3128 user@proxy-host &
```

Or via a systemd user service:

```bash
# ~/.config/systemd/user/proxy-tunnel.service
[Unit]
Description=SSH tunnel to proxy-host

[Service]
ExecStart=/usr/bin/ssh -N -L 3128:localhost:3128 user@proxy-host
Restart=always
RestartSec=10

[Install]
WantedBy=default.target
```

```bash
systemctl --user enable --now proxy-tunnel.service
```

## Client Proxy Setup

On the remote VM, set environment variables for applications that respect `http_proxy`:

```bash
export http_proxy=http://localhost:3128
export https_proxy=http://localhost:3128
```

Or permanently in `/etc/environment`:

```
http_proxy=http://localhost:3128
https_proxy=http://localhost:3128
```

For curl/wget, variables suffice. For apt — additionally:

```bash
echo 'Acquire::http::Proxy "http://localhost:3128";' | tee /etc/apt/apt.conf.d/99proxy
```

Test:

```bash
curl -s --max-time 10 https://ifconfig.me
```

If it returns the intermediate host IP — it's working.

## Verification and Logging

Squid access log:

```bash
tail -f /var/log/squid/access.log
```

Format: `time client/status code size method URL`

Sample entry:

```
1703123456.123  1024 192.168.1.100 TCP_MEM_HIT/200 5123 GET http://example.com/file.tar.gz
```

Response codes: `TCP_HIT` — served from cache, `TCP_MISS` — fetched from network, `TCP_DENIED` — access denied by ACL.

To clear the cache before testing:

```bash
squid -k shutdown && rm -rf /var/spool/squid/* && squid -z && systemctl start squid
```

| Flag | Description |
|------|-------------|
| `http_port` | Listening port |
| `acl name src IP/mask` | Access rule by IP |
| `http_access allow\|deny` | Permit or deny ACL |
| `-k reconfigure` | Reload config |
| `-k shutdown` | Graceful stop |
| `-z` | Initialize cache directories |

> [!WARNING]
> Squid caches responses by default. For debugging, disable caching: add `cache deny all` to the config, then run `squid -k reconfigure`.

Nine minutes of setup — and the isolated VM has internet via proxy. If you need HTTPS transparent mode with certificate substitution — that's a different story involving SSL-bump and CA generation.
