Caddy: Difference between revisions
mNo edit summary |
Cartwatson (talk | contribs) mNo edit summary |
||
(16 intermediate revisions by 6 users not shown) | |||
Line 2: | Line 2: | ||
It can also be a reverse proxy to serve multiple web services under one server. Its main features are its simple config setup and automatic HTTPS: It will automatically request and renew a LetsEncrypt certificate so that users of your service get a Browser-trusted and secure connection. | It can also be a reverse proxy to serve multiple web services under one server. Its main features are its simple config setup and automatic HTTPS: It will automatically request and renew a LetsEncrypt certificate so that users of your service get a Browser-trusted and secure connection. | ||
== | == Setup == | ||
To try out Caddy add the following minimal example to your [[NixOS modules | NixOS module]]: | To try out Caddy add the following minimal example to your [[NixOS modules | NixOS module]]: | ||
<syntaxhighlight lang="nix"> | <syntaxhighlight lang="nix">services.caddy = { | ||
enable = true; | |||
virtualHosts."localhost".extraConfig = '' | |||
respond "Hello, world!" | |||
''; | |||
};</syntaxhighlight> | |||
</syntaxhighlight> | |||
This snippet will let Caddy respond on <code>http://localhost</code> and <code>https://localhost</code> with a dummy text "Hello world!". When no port is mentioned on virtualhost like just <code>localhost</code> instead of <code>localhost:8080</code>, Caddy listens on <code>80</code> and <code>443</code> by default and redirects requests from port 80 (unsecured) to 443 (secured). | This snippet will let Caddy respond on <code>http://localhost</code> and <code>https://localhost</code> with a dummy text "Hello world!". When no port is mentioned on virtualhost like just <code>localhost</code> instead of <code>localhost:8080</code>, Caddy listens on <code>80</code> and <code>443</code> by default and redirects requests from port 80 (unsecured) to 443 (secured). | ||
Use <code>curl -iLk localhost</code> to verify the configuration. | |||
For SSL to work, just supply a public domain and ensure HTTP and HTTPS ports are accessible. Caddy will automatically configure TLS: | |||
<syntaxhighlight lang="nix">services.caddy = { | |||
<syntaxhighlight lang="nix"> | |||
services.caddy = { | |||
enable = true; | enable = true; | ||
virtualHosts."example.org".extraConfig = '' | virtualHosts."example.org".extraConfig = '' | ||
respond "Hello, world!" | |||
''; | ''; | ||
}; | }; | ||
networking.firewall.allowedTCPPorts = [ 80 443]; | |||
</syntaxhighlight> | networking.firewall.allowedTCPPorts = [ 80 443 ];</syntaxhighlight> | ||
== Configuration == | |||
=== Reverse proxy === | === Reverse proxy === | ||
Line 100: | Line 44: | ||
''; | ''; | ||
}; | }; | ||
</syntaxhighlight> | </syntaxhighlight>In case you would like to forward the real client IP of the request to the backend, add following headers<syntaxhighlight lang="nix"> | ||
services.caddy = { | |||
virtualHosts."example.org".extraConfig = '' | |||
reverse_proxy http://10.25.40.6 { | |||
header_down X-Real-IP {http.request.remote} | |||
header_down X-Forwarded-For {http.request.remote} | |||
} | |||
''; | |||
}; | |||
</syntaxhighlight>Fur further reverse proxy configuration, see [https://caddyserver.com/docs/quick-starts/reverse-proxy upstream documentation]. | |||
=== Redirect === | === Redirect === | ||
Permanent redirect of <code>example.org</code> and <code>old.example.org</code> to <code>www.example.org</code> | |||
<syntaxhighlight lang="nix"> | <syntaxhighlight lang="nix"> | ||
Line 113: | Line 64: | ||
virtualHosts."example.org" = { | virtualHosts."example.org" = { | ||
extraConfig = '' | extraConfig = '' | ||
redir https://www.example.org{uri} | redir https://www.example.org{uri} permanent | ||
''; | ''; | ||
serverAliases = [ "old.example.org" ]; | |||
}; | }; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 138: | Line 89: | ||
You'll need a [[Phpfpm|PHP-FPM]] socket listening on Unix socket path <code>/var/run/phpfpm/localhost.sock</code>. | You'll need a [[Phpfpm|PHP-FPM]] socket listening on Unix socket path <code>/var/run/phpfpm/localhost.sock</code>. | ||
== | === Plug-ins === | ||
Following example is adding the plugin powerdns in version 1.0.1 to your Caddy binary | |||
<syntaxhighlight lang="nix"> | |||
services.caddy = { | |||
enable = true; | |||
package = pkgs.caddy.withPlugins { | |||
plugins = [ "github.com/caddy-dns/powerdns@v1.0.1" ]; | |||
hash = "sha256-F/jqR4iEsklJFycTjSaW8B/V3iTGqqGOzwYBUXxRKrc="; | |||
}; | |||
}; | |||
</syntaxhighlight> | |||
Get the correct hash by leaving the string empty at first and after rebuild, insert the hash which the build process calculated. | |||
In case a plugin has no version tag, you'll have to query it first. In this example we'll do this for the plugin caddy-webdav | |||
<syntaxhighlight lang="sh"> | |||
$ go mod init temp | |||
$ go get github.com/mholt/caddy-webdav | |||
$ grep 'caddy-webdav' go.mod | |||
github.com/mholt/caddy-webdav v0.0.0-20241008162340-42168ba04c9d // indirect | |||
</syntaxhighlight> | |||
Add this version string to your final config | |||
<syntaxhighlight lang="nix"> | |||
services.caddy = { | |||
enable = true; | |||
package = pkgs.caddy.withPlugins { | |||
plugins = [ "github.com/caddy-dns/caddy-webdav@v0.0.0-20241008162340-42168ba04c9d" ]; | |||
hash = "sha256-F/jqR4iEsklJFycTjSaW8B/V3iTGqqGOzwYBUXxRKrc="; | |||
}; | |||
}; | |||
</syntaxhighlight> | |||
=== uWSGI apps === | |||
Serving uWSGI apps with Caddy also requires a plugin, in this example we'll use [https://github.com/wxh06/caddy-uwsgi-transport caddy-uwsgi-transport]. See section above on how to fetch and update plugins.<syntaxhighlight lang="nix"> | |||
services.caddy = { | |||
package = pkgs.caddy.withPlugins { | |||
plugins = [ "github.com/BadAimWeeb/caddy-uwsgi-transport@v0.0.0-20240317192154-74a1008b9763" ]; | |||
hash = "sha256-aEdletYtVFnQMlWL6YW4gUgrrTBatoCIuugA/yvMGmI="; | |||
}; | |||
virtualHosts = { | |||
"myapp.example.org" = { | |||
extraConfig = '' | |||
reverse_proxy unix/${config.services.uwsgi.runDir}/myapp.sock { | |||
transport uwsgi | |||
} | |||
''; | |||
}; | |||
}; | |||
</syntaxhighlight>This example will serve a [[uWSGI]] app, provided by a unix socket file, on the host <code>myapp.example.org</code>. | |||
=== Passing environment variable secrets/configuring acme_dns === | |||
To prevent any secrets from being put in the nix store (any NixOS setting that writes a config in the Nix store will expose any secret in it), you can use the following setting<syntaxhighlight lang="nixos"> | |||
services.caddy = { | |||
enable = true; | |||
globalConfig = '' | |||
acme_dns PROVIDER { | |||
api_key {$APIKEY} | |||
api_secret_key {$APISECRETKEY} | |||
} | |||
''; | |||
}; | |||
systemd.services.caddy.serviceConfig.EnvironmentFile = ["/path/to/envfile"]; | |||
</syntaxhighlight>And then at '''/path/to/envfile''':<syntaxhighlight> | |||
APIKEY=YOURKEY | |||
APISECRETKEY=OTHERKEY | |||
</syntaxhighlight> | |||
== Troubleshooting == | |||
=== Check used ports === | === Check used ports === | ||
To check if Caddy is running and listening as configured you can run <code>netstat</code>: | To check if Caddy is running and listening as configured you can run <code>netstat</code>: | ||
Line 157: | Line 181: | ||
=== Virtualhost and real host not identical === | === Virtualhost and real host not identical === | ||
When you connect to Caddy must ensure that the "Host" header matches the virtualhost entry of Caddy. For example, when testing locally a config like | When you connect to Caddy must ensure that the "Host" header matches the virtualhost entry of Caddy. For example, when testing locally a config like | ||
Line 197: | Line 220: | ||
* [https://caddyserver.com/docs/caddyfile/directives/tls Caddy TLS settings documentation] | * [https://caddyserver.com/docs/caddyfile/directives/tls Caddy TLS settings documentation] | ||
== See also == | == See also == | ||
Line 207: | Line 229: | ||
[[Category:Applications]] | [[Category:Applications]] | ||
[[Category:Server]] | [[Category:Server]] | ||
[[Category:Networking]] |