# Configure VPN

Both Virtual Private Network (VPN) types are configured in the host's YAML configuration. For the
difference between them and how to choose, see [What is a VPN on TurboStack?](what-is.md).

> [!IMPORTANT]
> VPN settings are only available in the **Source (YAML)** view of the host - there are no fields for
> them in the platform interface yet. Open the host, switch to the Source view, add the configuration
> below, then **Save and publish** to apply it. Plan the change with
> [Support](../../platform/support.md): the settings must match what the remote side uses, and a
> wrong value leaves the tunnel down.

## IPsec VPN (site-to-site)

An IPsec tunnel is defined as a list under `ipsecvpn_connections`. Each entry is one tunnel to one
remote gateway, and each tunnel carries one or more subnet pairs.

```yaml
ipsecvpn_connections:
  - name: cloud-to-office
    keyexchange: ikev2
    local_gw: 94.237.45.100          # public IP of this TurboStack host
    remote_gw: 84.198.149.130        # public IP of the remote gateway
    psk: "<shared secret>"
    phase1_proposal: "aes256-sha1-modp1536!"
    phase1_lifetime: 86400s
    phase2_proposal: "aes256-sha1"
    phase2_lifetime: 3600s
    dpd: hold
    dpd_delay: 30s
    dpd_timeout: 120s
    pfs: "no"
    subnets:
      - local: 192.168.205.0/24
        remote: 192.168.1.0/24
```

### Connection fields

| Field | Required | What it does |
| --- | --- | --- |
| `name` | yes | A short name for the tunnel. It identifies the connection in the configuration and in logs. |
| `keyexchange` | yes | The key-exchange protocol. Use `ikev2` unless the remote side only supports the older `ikev1`. |
| `local_gw` | yes | The public IP address of this host - the local end of the tunnel. |
| `remote_gw` | yes | The public IP address of the gateway at the other side. |
| `subnets` | yes | The subnet pairs allowed through the tunnel. Each entry has a `local` and a `remote` network in Classless Inter-Domain Routing (CIDR) notation. |
| `psk` | yes in practice | The pre-shared key: the shared secret both sides authenticate with. It must be identical on both ends. |
| `phase1_proposal` | yes in practice | The encryption, integrity and Diffie-Hellman group for the key exchange, for example `aes256-sha1-modp1536!`. A trailing `!` means "only this proposal, do not negotiate anything weaker". |
| `phase2_proposal` | yes in practice | The encryption and integrity for the data itself, for example `aes256-sha1`. |
| `phase1_lifetime` | yes in practice | How long the key-exchange session stays valid before it is renegotiated, for example `86400s` (24 hours). |
| `phase2_lifetime` | yes in practice | How long the data keys stay valid, for example `3600s` (1 hour). |
| `dpd` | no | Dead Peer Detection: what to do when the other side stops answering. `hold` keeps the tunnel definition and re-establishes on the next matching traffic. Leave it out, or use `none`, to switch the check off. |
| `dpd_delay` | with `dpd` | How often to check that the peer is still alive, for example `30s`. |
| `dpd_timeout` | with `dpd` | How long to wait before the peer counts as gone, for example `120s`. |
| `pfs` | no | Perfect Forward Secrecy. With `"yes"` the keys are renegotiated from scratch on rekey, so a compromised key cannot expose earlier traffic. Use `"yes"` when the remote side supports it. |
| `monitoring_endpoints` | no | A list of IP addresses at the remote side that TurboStack Monitoring checks through the tunnel, so you are alerted when the far end becomes unreachable. |

> [!WARNING]
> The `psk` is a shared secret. Treat it like a password: agree on it over a secure channel and do not
> reuse it between tunnels. Ask [Support](../../platform/support.md) if you prefer not to place it in
> the host configuration yourself.

### Matching both sides

An IPsec tunnel only comes up when both gateways propose the same parameters. `keyexchange`,
`phase1_proposal`, `phase2_proposal`, the lifetimes, `pfs` and the `psk` must be agreed with whoever
manages the remote gateway. The subnet pairs must mirror each other: what is `local` here is `remote`
there.

The tunnel is established on demand: as soon as traffic matches one of the configured subnet pairs,
the connection is set up. Only the listed subnet pairs are routed through the tunnel - traffic to any
other destination keeps its normal route.

### Several subnets or several tunnels

Add more entries under `subnets` to reach more networks over the same tunnel, and more entries under
`ipsecvpn_connections` to build tunnels to more locations:

```yaml
ipsecvpn_connections:
  - name: cloud-to-office
    keyexchange: ikev2
    local_gw: 94.237.45.100
    remote_gw: 84.198.149.130
    psk: "<shared secret>"
    phase1_proposal: "aes256-sha256-modp2048!"
    phase1_lifetime: 28800s
    phase2_proposal: "aes256-sha256-ecp521!"
    phase2_lifetime: 3600s
    dpd: hold
    dpd_delay: 30s
    dpd_timeout: 120s
    pfs: "yes"
    subnets:
      - local: 192.168.205.0/24
        remote: 192.168.1.0/24
      - local: 192.168.205.0/24
        remote: 192.168.254.0/24
  - name: cloud-to-datacenter
    keyexchange: ikev2
    local_gw: 94.237.45.100
    remote_gw: 203.0.113.10
    psk: "<other shared secret>"
    phase1_proposal: "aes256-sha1-modp1536!"
    phase1_lifetime: 86400s
    phase2_proposal: "aes256-sha1"
    phase2_lifetime: 3600s
    dpd: hold
    dpd_delay: 30s
    dpd_timeout: 120s
    pfs: "no"
    subnets:
      - local: 192.168.205.0/24
        remote: 10.20.0.0/16
```

## SSL VPN (remote access)

The Secure Sockets Layer (SSL) VPN is configured as a single `sslvpn` block. Only the values you set are overridden; the
rest keeps its platform default. A working setup needs three things: switch it on, give it a hostname,
and list the users.

```yaml
sslvpn:
  enabled: true
  hostname: vpn.example.com        # FQDN that points to this host
  users:
    - alice
    - bob
```

| Field | Required | What it does |
| --- | --- | --- |
| `enabled` | yes | Switches the SSL VPN service on. |
| `hostname` | yes | The Fully Qualified Domain Name (FQDN) users connect to. It is also the name on the Transport Layer Security (TLS) certificate, so it must resolve to this host before you publish. |
| `users` | yes | The list of user names that may connect. Each user gets a personal password, which Hosted Power stores in the platform's secret store - ask [Support](../../platform/support.md) to set or reset one. |
| `tcp_port` / `udp_port` | no | The port the service listens on. Both default to `4443`. The client uses the faster datagram path when possible and falls back to the TCP port when the network blocks it. |
| `dns_servers` | no | The name servers pushed to connected clients. Defaults to `8.8.8.8` and `1.1.1.1`. |
| `default_domain` | no | The search domain pushed to clients, so short names resolve. |
| `ipv4_network` / `ipv4_netmask` / `ipv4_network_cidr` | no | The address pool that connected clients receive an address from. Defaults to `172.30.30.0/24`. Change it when it overlaps with a network you route. |
| `max_clients` | no | Maximum number of connected clients. Defaults to `128`. |
| `max_same_clients` | no | How many sessions one user may have at the same time. `0` means no limit. |
| `predictable_ips` | no | Gives a user the same VPN address each time, which is useful when you allow-list those addresses somewhere. |

### Split tunnel or full tunnel

This is the most important choice for an SSL VPN: **which of the user's traffic goes through the
tunnel**. It is controlled by `split_tunnel` together with `networks`.

**Split tunnel** (`split_tunnel: true`) sends only the networks you list through the VPN. Everything
else - the user's browsing, video calls, cloud tools - keeps going out over their own internet
connection.

```yaml
sslvpn:
  enabled: true
  hostname: vpn.example.com
  users: [alice, bob]
  split_tunnel: true
  networks:
    - 10.0.0.0/8
    - 172.16.0.0/12
    - 192.168.0.0/16
```

The `networks` list is pushed to the client as routes, and it is also what the host allows between
the VPN pool and those networks. The default list covers the private address ranges, which is a safe
starting point; narrow it to the subnets you actually want reachable.

**Full tunnel** (`split_tunnel: false`) sends *all* of the user's traffic through the VPN, including
their normal internet traffic. The host then also needs an outbound interface for that traffic, which
is set with `nat_interface` (the platform fills in the host's main interface when you leave it empty).

```yaml
sslvpn:
  enabled: true
  hostname: vpn.example.com
  users: [alice, bob]
  split_tunnel: false
```

How to choose:

| | Split tunnel | Full tunnel |
| --- | --- | --- |
| **Traffic through the VPN** | Only the listed networks | Everything the user sends |
| **Speed and bandwidth** | Better - normal internet traffic takes the direct route | All traffic makes a detour over the host |
| **The user's public IP** | Their own internet connection | The host's IP address |
| **IP allow-listing** | Does not help - the user keeps their own changing IP | Works - everyone leaves from the same known IP |
| **Privacy for the user** | Their private browsing stays off your network | Their full internet traffic passes your server |
| **Best for** | Reaching a few internal services with the least impact | Enforcing that people leave from one trusted address |

> [!TIP]
> The usual reason to pick a full tunnel is IP allow-listing: everyone connected leaves the internet
> from the host's address, so you can allow that single address on the
> [Firewall](../firewall/configure.md) or in a third-party service, instead of chasing the changing
> home addresses of every user. If you do not need that, a split tunnel is lighter and faster.

> [!WARNING]
> With a full tunnel, all of the user's internet traffic runs over your server. Check that this is
> acceptable for your users and that the host has the bandwidth for it, and be aware that a VPN
> outage then takes the user fully offline instead of only losing access to internal services.

### Timeouts, limits and protection

These have sensible defaults. Change them only for a specific reason.

| Field | Default | What it does |
| --- | --- | --- |
| `keepalive` | `300` | Seconds between keepalive messages that hold the session open. |
| `dpd` / `mobile_dpd` | `60` / `300` | Seconds before an unresponsive client is considered gone. Mobile clients get more time because they change networks. |
| `idle_timeout` / `mobile_idle_timeout` | `1200` / `1800` | Seconds of inactivity before a session is closed. |
| `auth_timeout` | `240` | Seconds a user has to finish logging in. |
| `min_reauth_time` | `300` | Seconds a user must wait before retrying after a failed login. |
| `max_ban_score` / `ban_reset_time` | `80` / `300` | Brute-force protection: failed attempts add to a score, and the source is blocked when it passes the maximum. The score resets after the reset time. |
| `cookie_timeout` | `300` | Seconds a session may be resumed after a short network interruption. |
| `deny_roaming` | `false` | When `true`, a session may not continue from a different IP address. |
| `rekey_time` | `172800` | Seconds before the session keys are renewed. |

### Certificate

By default the platform requests and renews a Let's Encrypt certificate for `hostname`
(`tls_manage: true`). For this to work, `hostname` must point to the host and a web server must be
running on it, because the certificate is validated over HTTP.

To use your own certificate instead, switch off the automatic handling and point to the files:

```yaml
sslvpn:
  enabled: true
  hostname: vpn.example.com
  users: [alice]
  tls_manage: false
  server_cert: /etc/ssl/certs/vpn.example.com.crt
  server_key: /etc/ssl/private/vpn.example.com.key
```

### Connecting

Users connect to `https://<hostname>:4443` with a VPN client that supports the OpenConnect protocol,
using their user name and the password Hosted Power set for them. Clients are available for Windows,
macOS, Linux, Android and iOS.

## Apply the changes

VPN settings are applied when the host is published. Save the YAML, then **Save and publish** the
host - see [Publishing](../../platform/hosts/publishing.md). Ask
[Support](../../platform/support.md) to confirm the tunnel is up, or to check the far side when it
is not.

## Related

- [What is a VPN on TurboStack?](what-is.md)
- [Firewall](../firewall/configure.md)
- [SSH access](../ssh/what-is.md)
- [Networking](../../concepts/networking.md)
- [Publishing](../../platform/hosts/publishing.md)
- [Support](../../platform/support.md)
