Expose a service

Table of Contents

Service setup
SSL Certificate
LDAP group
Reverse Proxy with Forward Auth
ZFS support
Debugging
Backup

Let’s see how one can use most of the blocks provided by SelfHostBlocks to make a service accessible through a reverse proxy with LDAP and SSO integration as well as backing up this service and creating a ZFS dataset to store the service’s data.

We’ll use an hypothetical well made service found under services.awesome as our example. We’re purposely not using a real service to avoid needing to deal with uninteresting particularities.

Service setup

Let’s say our domain name is example.com, and we want to reach our service under the awesome subdomain:

let
  domain = "example.com";
  subdomain = "awesome";
  fqdn = "${subdomain}.${domain}";
  listenPort = 9000;
  dataDir = "/var/lib/awesome";
in

We then enable the service and explicitly set the listenPort and dataDir, assuming those options exist:

services.awesome = {
  enable = true;
  inherit dataDir listenPort;
};

SSL Certificate

Requesting an SSL certificate from Let’s Encrypt is done by adding an entry to the extraDomains option:

shb.certs.certs.letsencrypt.${domain}.extraDomains = [ fqdn ];

This assumes the shb.certs block has been configured:

shb.certs.certs.letsencrypt.${domain} = {
  inherit domain;
  group = "nginx";
  reloadServices = [ "nginx.service" ];
  adminEmail = "admin@${domain}";
};

LDAP group

We want only users of the group calibre_user to be able to access this subdomain. The following snippet creates the LDAP group:

shb.lldap.ensureGroups = {
  calibre_user = {};
};

Reverse Proxy with Forward Auth

If our service does not integrate with OIDC, we can still protect it with SSO with forward authentication by letting the reverse proxy handle authentication. This is done by adding an entry to shb.nginx.vhosts:

shb.nginx.vhosts = [
  {
    inherit subdomain domain;
    ssl = config.shb.certs.certs.letsencrypt.${domain};
    upstream = "http://127.0.0.1:${toString config.services.calibre-web.listen.port}";
    authEndpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}";
    autheliaRules = [{
      policy = "one_factor";
      subject = [ "group:${config.shb.lldap.ensureGroups.calibre_user.name}" ];
    }];
  }
];

ZFS support

If you use ZFS, you can use SelfHostBlocks to create a dataset for you:

shb.zfs.datasets."safe/awesome".path = config.services.awesome.dataDir;

Debugging

Usually, the log level of the service can be increased with some option they provide.

With SelfHostBlocks, you can also introspect any HTTP service by adding an mitmdump instance between the reverse proxy and the awesome service:

shb.mitmdump.awesome = {
  inherit listenPort;
  upstreamPort = listenPort + 1;
};
services.awesome.listenPort = lib.mkForce (listenPort + 1);

This creates a mitmdump-awesome.service systemd service which prints the requests’ and responses’ headers and bodies.

Backup

The following snippet uses the shb.restic block to backup the services.awesome.dataDir directory:

shb.restic.instances.awesome = {
  request.user = "awesome";
  request.sourceDirectories = [ dataDir ];
  settings.enable = true;
  settings.passphrase.result = config.shb.sops.secret.awesome.result;
  settings.repository.path = "/srv/backup/awesome";
};

shb.sops.secret."awesome" = {
  request = config.shb.restic.instances.awesome.settings.passphrase.request;
};