Permalink
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
240 lines (187 sloc) 7.72 KB

nixcloud.reverse-proxy

The nixcloud.reverse-proxy acts as an intermediary to allow multiple webservices to run from the same IP address(es) using the same port (80/443), and mapping these to domain names. nixcloud.reverse-proxy is a part of nixcloud-webservices and is based on https://www.nginx.com/.

nixcloud.webservices layout

nixcloud.reverse-proxy fully implements security.acme certificate handling and transparently handles TLS contexts for the inner webservices.

Most important: additional certificates found in security.acme.certs, which were not defined by nixcloud.TLS, will also be handled by nixcloud.reverse-proxy.

The suffixes are set for security.acme.certs.<name> but the domains might be the same. This is required because of https://github.com/NixOS/nixpkgs/issues/34422

See also ../README.md.

Usage

To enable the reverse-proxy simply put this into your configuration.nix:

nixcloud.reverse-proxy = {
  enable = true;
};

The reverse-proxy can be used explicitly using extraMappings or implicitly by using nixcloud.webservices.

The TLS field in the following example has a special meaning:

nixcloud.webservices.mediawiki.test1 = {
  enable = true;
  proxyOptions = {
    TLS = "myidentifier";
    https.mode = "on";
    port   = 40001;
    path   = "/wiki";
    domain = "example.org";
  };
};

The TLS field was set to the identifier "myidentifier" for which you need a:

nixcloud.TLS.certs."myidentifier" = {
  domain = "example.org";
  mode = "selfsigned";
  reload = [ "postfix.service" "myservice.service" ];
};

By default the proxyOptions.TLS option is set to the proxyOptions.domain and nixcloud.TLS will then have "ACME" as the default so it will use security.acme as backend.

See nixcloud.TLS.md for more information.

extraMappings example(s)

Motivations to use `nixcloud.reverse-proxy.extraMappings:

  • Mix your legacy webservice, which was not built using nixcloud.webservices with services from nixcloud.webservices

  • Outsource TLS certificate management and TLS handling so you don't have to do this in you webservice

  • Use nixcloud.reverse-proxy features:

    • TLS configuration (permitted, prohibited protocol families and so on)
    • connection compression
    • basicAuth
  • Manage URL redirects for domains/resources

  • Manage websocket redirects

Example 1: Proxying a legacy webservice (not using nixcloud.webservices)

The code below has a few properties:

  • redirect all http requests to https

  • the reverse-proxy only serves data after basicAuth has been used to authentificate the user

  • it will serve documents from http://127.0.0.1:8081/.

      nixcloud.reverse-proxy = {
        enable = true;
        extendEtcHosts = true;
        extraMappings = [
          {  
            domain = "example.com";
            path = "/";
            https = {
              mode = "on";
              basicAuth."joachim" = "foo";
              record = ''
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_pass http://127.0.0.1:8081/;
              '';
            };
          }
        ];
      };
    
    • nginx example

      services.nginx = {
        enable = true;
        virtualHosts.nixcloud-backend = {
          default = true;
          listen = [ { addr = "127.0.0.1"; port = 8081; } ];
          serverName = "example.com";
          enableACME = false;
          forceSSL = false;
          locations = {
            "/" = {
              root = /www;
              extraConfig = ''
                try_files $uri $uri/ /index.html;
              '';
            };
          };
        };
      };
      
    • httpd example

      services.httpd = {
        enable = true;
        adminAddr = "js@lastlog.de";
        listen = [{port = 8081;}];
        virtualHosts =
        [
          {
            hostName = "nixdoc.io";
            serverAliases = [ "nixdoc.io" ];
            enableSSL = false;
            listen = [{port = 8081;}];
      
            extraConfig = ''
              DocumentRoot = "/www"
              <Directory /www>
                      Options Indexes FollowSymLinks
                      AllowOverride None
                      Require all granted
              </Directory>
            '';
          }
        ];
      };
      

Example 2: URL rewriting

The code below will create a mapping: when you visit http(s)://example.com it will redirect to https://example.org but if you access the https://example.com it will prompt for a password before it gives you the redirect. The username is 'joachim' and the password is 'foo'.

nixcloud.reverse-proxy = {
  enable = true;
  extendEtcHosts = true;
  extraMappings = [
    {  
      domain = "example.com";
      path = "/";
      http = {
        mode = "on";
        record = ''
          rewrite ^(.*)$ https://example.org permanent;
        '';
      };
      https = {
        mode = "on";
        basicAuth."joachim" = "foo";
        record = ''
          rewrite ^(.*)$ https://example.org permanent;
          add_header Strict-Transport-Security max-age=63646566;
          add_header Content-Security-Policy "default-src 'none';";
        '';
      };
    }
  ];
};

Example 3: Exposing only websocket(s)

This example disables all http/https mappings but adds two websocket mappings:

nixcloud.reverse-proxy = {
  enable = true;
  extraMappings = [
    {
      domain     = "example.com";
      port       = 3003;
      path       = "/backend";
      http.mode  = "off";
      https.mode = "off";
      websockets = {
        ws = {
          https.mode = "on";
          subpath = "/";
        };
        ws2 = {
          https.mode = "on";
          subpath = "/leaps/ws";
        };
      };
    }
  ];
};

From the internet one could connect to: wss://example.com/backend/ or wss://example.com/backend/leaps/ws

The implementation is options.nix and the option is called websockets = mkOption.

nixcloud.webservices example(s)

nixcloud.reverse-proxy.enable = true;

...

nixcloud.webservices.mediawiki.test1 = {
  enable = true;
  proxyOptions = {
    http.mode = "on";
    https.mode = "on";
    port   = 40001;
    path   = "/wiki";
    domain = "example.org";
  };
};

When using nixcloud.webservices the nixcloud.reverse-proxy will automatically collect all the proxyOptions and generate reverse-proxy mappings on Nix evaluation time.

The leaps webservice sets the websocket binding from the service itself:

# inject the leaps websocket for cooperative document opening/editing into proxyOptions
proxyOptions.websockets = {
  ws = {
    subpath = "/leaps/ws";
  };
};

See implementation.

Tests

See also the unit tests at ../modules/services/reverse-proxy/test.nix which tests various scenarios and is a good example to get into the reverse proxy configuration.