post(cf-tunnel-nixos): HTTPS SNI

update title and links
This commit is contained in:
Ming Di Leom 2024-08-25 02:22:55 +00:00
parent f980c7bb95
commit fd96e36793
No known key found for this signature in database
GPG Key ID: 32D3E28E96A695E8
1 changed files with 50 additions and 32 deletions

View File

@ -1,44 +1,47 @@
--- ---
title: Setup Cloudflare Argo Tunnel in NixOS title: Setup Cloudflare Tunnel in NixOS
excerpt: No inbound port required excerpt: No open inbound port required
date: 2021-06-15 date: 2021-06-15
updated: 2024-08-25
tags: tags:
- server - server
- linux - linux
- caddy - caddy
- nixos - nixos
- cloudflare - cloudflare
--- ---
Cloudflare Argo Tunnel enables a web server to serve websites over the public internet without opening an inbound port. With its built-in NAT traversal, a web server can even operate behind a NAT without resorting to port forwarding. The NAT traversal feature reminds me of Tor onion and I2P Eepsite, though the underlying connection is totally different. Cloudflare Tunnel (formerly known as Cloudflare Argo Tunnel) enables a web server to serve websites over the public internet without opening an inbound port. With its built-in NAT traversal, a web server can even operate behind a NAT without resorting to port forwarding. The NAT traversal feature reminds me of Tor onion and I2P Eepsite, though the underlying connection is totally different.
It operates through a Cloudflare daemon ([cloudflared](https://github.com/cloudflare/cloudflared)) that a user installs in a server. The daemon creates outbound tunnel(s) to the CDN and forward incoming request to the local web server. It is available for free since April 2021. However, the latest NixOS at that time was 20.09 and it shipped an older version of the daemon that didn't support static tunnel. It operates through a Cloudflare daemon ([cloudflared](https://github.com/cloudflare/cloudflared)) that a user installs in a server. The daemon creates outbound tunnel(s) to the CDN and forward incoming request to the local web server.
[Static tunnel](https://blog.cloudflare.com/argo-tunnels-that-live-forever/) is a feature introduced in v2020.9.3 that associate a tunnel with a static subdomain (UUID.cfargotunnel.com) that a user can CNAME a website to; without this feature, cloudflared had to recreate DNS record every time a tunnel reconnects. [Static tunnel](https://blog.cloudflare.com/argo-tunnels-that-live-forever/) is a feature introduced in v2020.9.3 that associate a tunnel with a static subdomain (UUID.cfargotunnel.com) that a user can CNAME a website to; without this feature, cloudflared had to recreate DNS record every time a tunnel reconnects.
I can now use the newer daemon after my recent upgrade to {% post_link upgrade-note-nixos-21-05 'NixOS 21.05' %}.
## Setup ## Setup
Generate a new cert.pem from [dashboard](https://dash.cloudflare.com/argotunnel). This is only required to create a new tunnel. When creating a new tunnel, cloudflared also generate a credentials file (UUID.json) that you use to _run_ a tunnel, so you don't have upload the cert.pem to your server. Since tunnel can be created anywhere, you can do it from your workstation. Grab the cloudflared binary from [Cloudflare](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/) and make it executable without installing it to "/usr/bin".
Grab the cloudflared binary from [Cloudflare](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation) and make it executable without installing it to "/usr/bin". ```
./cloudflared tunnel login
```
cert.pem will be generated in the default [cloudflared directory](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/tunnel-useful-terms/#default-cloudflared-directory).
## Create a new tunnel ## Create a new tunnel
This step can be done on your local machine, the actual installation on a server comes later. Once cloudflared binary and cert.pem are downloaded, proceed to creating a new tunnel. This step can be done on your local machine, the actual installation on a server can [come later](#configure-nixos).
``` ```
./cloudflared tunnel --origincert cert.pem create mytunnel ./cloudflared tunnel create mytunnel
``` ```
A new _UUID_.json will be generated in the current folder. A new _UUID_.json will be generated in the default [cloudflared directory](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/tunnel-useful-terms/#default-cloudflared-directory).
## Configure the tunnel ## Configure the tunnel
Create a new yml file. Create a new yml file.
``` yml ```yml
tunnel: mytunnel tunnel: mytunnel
credentials-file: /var/lib/argoWeb/uuid.json credentials-file: /var/lib/argoWeb/uuid.json
@ -53,13 +56,32 @@ ingress:
- service: http_status:404 - service: http_status:404
``` ```
Alternatively, HTTPS can be reached directly if enabled.
```yml
tunnel: mytunnel
credentials-file: /var/lib/argoWeb/uuid.json
originRequest:
matchSNItoHost: true
# Optional
# loglevel: warn
ingress:
- hostname: mdleom.com
service: https://localhost:4430
- hostname: www.mdleom.com
service: https://localhost:4430
- service: http_status:404
```
The last entry is intentionally left without a `hostname` key as required by cloudflared. Usually, it's configured with `http_status:404` so cloudflared returns that status if there is no matching destination hostname for an incoming request. This can happen when say you have a foo.example.com DNS record that points to the daemon, so incoming request does reach the daemon, but either you forgot to configure the daemon to route the traffic to the actual foo.example.com web server or the web server is not running at all. In that case, the daemon will return a HTTP 404 status. The last entry is intentionally left without a `hostname` key as required by cloudflared. Usually, it's configured with `http_status:404` so cloudflared returns that status if there is no matching destination hostname for an incoming request. This can happen when say you have a foo.example.com DNS record that points to the daemon, so incoming request does reach the daemon, but either you forgot to configure the daemon to route the traffic to the actual foo.example.com web server or the web server is not running at all. In that case, the daemon will return a HTTP 404 status.
## Configure NixOS ## Configure NixOS
Create a new user and group named "argoWeb" in the server. Create a new user and group named "argoWeb" in the server.
``` nix ```nix
users = { users = {
users = { users = {
argoWeb = { argoWeb = {
@ -76,11 +98,11 @@ Create a new user and group named "argoWeb" in the server.
}; };
``` ```
Once `argoWeb` is created via nixos-rebuild, upload and move the json file to "/var/lib/argoWeb" folder. `chown argoWeb:argoWeb` and `chmod 600` that file. Once `argoWeb` is created via nixos-rebuild, upload and move the json file (located in the default [cloudflared directory](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/tunnel-useful-terms/#default-cloudflared-directory)) to "/var/lib/argoWeb" folder. `chown argoWeb:argoWeb` and `chmod 600` that file.
Then, Create a new nix file; in this case, I'm using "/etc/caddy/" folder (where I put other *.nix files): Then, Create a new nix file; in this case, I'm using "/etc/caddy/" folder (where I put other \*.nix files):
``` nix /etc/caddy/argoWeb.nix ```nix /etc/caddy/argoWeb.nix
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
with lib; with lib;
@ -89,7 +111,7 @@ let
cfg = config.services.argoWeb; cfg = config.services.argoWeb;
in { in {
options.services.argoWeb = { options.services.argoWeb = {
enable = mkEnableOption "Cloudflare Argo Tunnel"; enable = mkEnableOption "Cloudflare Tunnel";
config = mkOption { config = mkOption {
default = "/etc/caddy/argoWeb.yml"; default = "/etc/caddy/argoWeb.yml";
@ -115,7 +137,7 @@ in {
config = mkIf cfg.enable { config = mkIf cfg.enable {
systemd.services.argoWeb = { systemd.services.argoWeb = {
description = "Cloudflare Argo Tunnel"; description = "Cloudflare Tunnel";
after = [ "network-online.target" ]; after = [ "network-online.target" ];
wants = [ "network-online.target" ]; # systemd-networkd-wait-online.service wants = [ "network-online.target" ]; # systemd-networkd-wait-online.service
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
@ -144,18 +166,14 @@ Move the yml file to "/etc/caddy/" and set both yml and nix files to be `chown r
## Configure Caddy ## Configure Caddy
Bind the web server to localhost ("127.0.0.1" or "::1") and _optionally_ disable the tls. If Cloudflare's authenticated origin pull (client authentication) is configured, that should still work if you prefer to leave tls on, though I haven't test it. You don't have to bind it to localhost if you insist so, but it defeats the security purpose of Argo. Bind the web server to localhost and _optionally_ enable the tls. [`client_auth`](https://caddyserver.com/docs/caddyfile/directives/tls#client_auth) should not be configured as Cloudflare Tunnel [does not](https://community.cloudflare.com/t/authenticated-origin-pulls-while-using-the-zerotrust-tunnels/442702) support it and [does not](https://github.com/cloudflare/cloudflared/issues/407) offer security benefit for a localhost web server.
``` Caddyfile ```Caddyfile
mdleom.com:4430 www.mdleom.com:4430 { mdleom.com:4430 www.mdleom.com:4430 {
bind 127.0.0.1 bind 127.0.0.1 [::1]
tls /var/lib/caddyProxy/mdleom.com.pem /var/lib/caddyProxy/mdleom.com.key { tls /var/lib/caddyProxy/mdleom.com.pem /var/lib/caddyProxy/mdleom.com.key {
protocols tls1.3 protocols tls1.3
client_auth {
mode require_and_verify
trusted_ca_cert_file /var/lib/caddyProxy/origin-pull-ca.pem
}
} }
} }
``` ```
@ -164,11 +182,11 @@ Restart/reload Caddy for the changed config to take effect.
## Custom package (optional) ## Custom package (optional)
If your NixOS instance is IPv6-only, you may want to use a {% post_link custom-package-nixos-module 'custom package' %}. [`pkgs.cloudflared`](https://search.nixos.org/packages?channel=21.11&from=0&size=50&sort=relevance&type=packages&query=cloudflared) is installed by compiling the source from the [GitHub repo](https://github.com/cloudflare/cloudflared), instead of using a cached binary from Nix repo. cloudflared's license restricts the distribution of binary, hence the need of source compilation. However, GitHub doesn't support IPv6 yet, so we need to clone its repo to other Git repo that supports IPv6 and then download it from there. If your NixOS instance is IPv6-only, you may want to use a {% post_link custom-package-nixos-module 'custom package' %}. [`pkgs.cloudflared`](https://search.nixos.org/packages?channel=unstable&type=packages&query=cloudflared) is installed by compiling the source from the [GitHub repo](https://github.com/cloudflare/cloudflared), instead of using a cached binary from Nix repo. cloudflared's license restricts the distribution of binary, hence the need of source compilation. However, GitHub doesn't support IPv6 yet, so we need to clone its repo to other Git repo that supports IPv6 and then download it from there.
## Start cloudflared ## Start cloudflared
``` nix ```nix
require = [ require = [
/etc/caddy/argoWeb.nix /etc/caddy/argoWeb.nix
]; ];