Caddy: Difference between revisions

imported>Malteneuss
m Add example how to open NixOS ports
Onny (talk | contribs)
No edit summary
 
(23 intermediate revisions by 10 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.


== Installation ==
== Setup ==


To try out Caddy add the following minimal example to your 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 = {
services.caddy = {
   enable = true;
   enable = true;
   virtualHosts."localhost:80".extraConfig = ''
   virtualHosts."localhost".extraConfig = ''
     respond "Hello, world!"
     respond "Hello, world!"
   '';
   '';
};
};</syntaxhighlight>
</syntaxhighlight>


The snippet above will run Caddy on http://localhost and respond with a dummy text "Hello world!".
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).


A similar example with serving a dummy "http://localhost/example.html" page is:
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">
<syntaxhighlight lang="nix">services.caddy = {
services.caddy = {
   enable = true;
   enable = true;
   virtualHosts."localhost".extraConfig = ''
   virtualHosts."example.org".extraConfig = ''
     encode gzip
     respond "Hello, world!"
    file_server
    root * ${
      pkgs.runCommand "testdir" {} ''
        mkdir "$out"
        echo hello world > "$out/example.html"
      ''
    }
   '';
   '';
};
};  
</syntaxhighlight>


== Configuration examples ==
networking.firewall.allowedTCPPorts = [ 80 443 ];</syntaxhighlight>


=== SSL ===
== Configuration ==


Caddy will automatically try to acquire SSL certificates for the specified domain, in this example <code>example.org</code>. This requires you to configure the DNS records of your domain correctly, which should point to the address of your Caddy server. The [[firewall]] ports <code>80</code> and <code>443</code> needs to be opened.
=== Plug-ins ===
 
{{Note|Parts of this module are not yet stable will be available with the upcoming NixOS release 25.05.}}
<syntaxhighlight lang="nix">
Following example is adding the plugin powerdns in version 1.0.1 to your Caddy binary<syntaxhighlight lang="nix">
services.caddy = {
services.caddy = {
   enable = true;
   enable = true;
   virtualHosts."example.org".extraConfig = ''
   package = pkgs.caddy.withPlugins {
    encode gzip
    plugins = [ "github.com/caddy-dns/powerdns@v1.0.1" ];
    file_server
    hash = "sha256-F/jqR4iEsklJFycTjSaW8B/V3iTGqqGOzwYBUXxRKrc=";
    root * ${
  };
      pkgs.runCommand "testdir" {} ''
};
        mkdir "$out"
        echo hello world > "$out/example.html"
      ''
    }
  '';
};
networking.firewall.allowedTCPPorts = [ 80 443];  
</syntaxhighlight>
</syntaxhighlight>


Line 67: Line 49:
   virtualHosts."example.org".extraConfig = ''
   virtualHosts."example.org".extraConfig = ''
     reverse_proxy http://10.25.40.6
     reverse_proxy http://10.25.40.6
  '';
  virtualHosts."another.example.org".extraConfig = ''
    reverse_proxy unix//run/gunicorn.sock
   '';
   '';
};
};
</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 = {
* [https://caddyserver.com/docs/quick-starts/reverse-proxy Caddy reverse proxy documentation]
  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 ===


Redirecting <code>example.org</code> and <code>old.example.org</code> to <code>www.example.org</code>
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 82: Line 74:
   virtualHosts."example.org" = {
   virtualHosts."example.org" = {
     extraConfig = ''
     extraConfig = ''
       redir https://www.example.org
       redir https://www.example.org{uri} permanent
   '';
   '';
     serverAlias = [ "old.example.org" ];
     serverAliases = [ "old.example.org" ];
};
};
</syntaxhighlight>
</syntaxhighlight>
Line 107: Line 99:
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>.


== Debugging ==
=== 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 netstat:
<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
$ netstat -tulpn
$ netstat -tulpn
Line 122: Line 131:
</syntaxhighlight>
</syntaxhighlight>
The tcp (ipv4) socket port 2019 is Caddy's management endpoint, for when you want manage its config via web REST calls instead of Nix (ignore).
The tcp (ipv4) socket port 2019 is Caddy's management endpoint, for when you want manage its config via web REST calls instead of Nix (ignore).
The tcp6 (an ipv6 socket that also listens on ipv4) socket on port 80 (HTTP) and 443 (HTTPS) indicate that a virtualhost config was used.
The tcp6 (an ipv6 socket that also listens on ipv4) socket on port 80 (HTTP) and 443 (HTTPS) indicate that our virtualhost config was used.
 
=== Check connections ===
 
You can also use curl to test http(s) calls. However, you must set the "Host" header correctly when testing locally:
<syntaxhighlight lang="bash">
$ curl localhost -H "Host: example.org"
</syntaxhighlight>


for an virtualhost config like  
=== 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  


<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
Line 141: Line 144:
};
};
</syntaxhighlight>
</syntaxhighlight>
you must send the request against "localhost" and manually override the host header to "example.org":
<syntaxhighlight lang="bash">
$ curl localhost -i -H "Host: example.org"
HTTP/1.1 308 Permanent Redirect
Connection: close
Location: https://example.org/
Server: Caddy
...
</syntaxhighlight>
Above you also see the redirect from http://localhost to https://example.org; Caddy always redirects from the unsecure to the secure port of your virtualhost.


If the response is empty, try setting a port number like 80 and/or try a local TLS security certificate instead of global LetsEncrypt:
If the response is empty, try setting a port number like 80 and/or try a local TLS security certificate instead of global LetsEncrypt:
Line 157: Line 173:


* [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 166: Line 181:


[[Category:Applications]]
[[Category:Applications]]
[[Category:Web Servers]]
[[Category:Server]]
[[Category:Networking]]