Writing a service for protonmail-bridge

I am currently trying to write my first NixOS service for the protonmail-bridge package. Because the program is quite monolithic, I am having trouble deciding how to approach it.

This is what I have so far

{ config, lib, pkgs, ... }:

with lib;
let
  cfg = config.services.protonmail-bridge;
in
{
  ##### interface
  options = {
    services.protonmail-bridge = {
      enable = mkOption {
        type = types.bool;
        default = false;
        description = "Whether to enable the Bridge.";
      };

      user = mkOption {
        type = types.str;
        description = "The user which should run the bridge";
      };

      nonInteractive = mkOption {
        type = types.bool;
        default = false;
        description = "Start Bridge entirely noninteractively";
      };

      logLevel = mkOption {
        type = types.enum [ "panic" "fatal" "error" "warn" "info" "debug" "debug-client" "debug-server" ];
        default = "info";
        description = "The log level";
      };

      trustCertificate = mkOption {
        type = types.bool;
        default = true;
        description = "Trust the self-signed certificate generated by the Bridge";
      };

    };
  };

  ##### implementation
  config = mkIf cfg.enable {

    environment.systemPackages = [ pkgs.protonmail-bridge ];
    
    systemd.services.protonmail-bridge = {
      description = "Protonmail Bridge";
      after = [ "network.target" ];
      wantedBy = [ "default.target" ];
      path = [ pkgs.pass ];
      serviceConfig = {
        User = "${cfg.user}";
        Restart = "always";
        ExecStart = "${pkgs.protonmail-bridge}/bin/protonmail-bridge --no-window --log-level ${cfg.logLevel}" + optionalString (cfg.nonInteractive) " --noninteractive";
      };
    };
  };
}

There are several challenges I am facing:

  1. The protonmail bridge stores credentials using pass or gnome-keyring. Because these are usually stored in the user’s personal keyring, how do we express this in the service description?
  2. How do you deal with the fact that the protonmail bridge generates a self-signed certificate that must be trusted by the system for it to be useable in MUAs. I currently copy the certificate manually after it is generated trust it by adding it to security.pki.certificates.

protonmail-bridge is probably better off as a home-manager module, or a NixOS module that generates a systemd user level service.

Running protonmail-bridge as a system level systemd service makes running more than a single instance impossible and doesn’t let you configure it as nice as home-manager would, IMO.

To answer your questions, though:

  1. Run as a user level systemd service as I mentioned above and I believe this problem is solved.
  2. Can you provide self signed certificates for protonmail-bridge to use instead of generating? This would be a convenient way to solve that problem.
1 Like

Thanks for the insight. I switched to a user service and wrote a module for home-manager (see below). The keyring/password store can now be accessed without any problems and the service works as expected. I looked into it some more and it should be possible to provide a certificate yourself for the bridge. Knowing this, how could one trust the certificate without manually needing to add it to security.pki.certificates

Also is there a way to add a “dependency” to the module to make sure that the user either has pass or gnome-keyring installed before they are able to enable this service?

{ config, lib, pkgs, ... }:

with lib;
let
  cfg = config.services.protonmail-bridge;
  #Still need to integrate more closely with the email management capabilities of home-manager
in
{
  ##### interface
  options = {
    services.protonmail-bridge = {
      enable = mkOption {
        type = types.bool;
        default = false;
        description = "Whether to enable the Bridge.";
      };

      nonInteractive = mkOption {
        type = types.bool;
        default = false;
        description = "Start Bridge entirely noninteractively";
      };

      logLevel = mkOption {
        type = types.enum [ "panic" "fatal" "error" "warn" "info" "debug" "debug-client" "debug-server" ];
        default = "info";
        description = "The log level";
      };

    };
  };

  ##### implementation
  config = mkIf cfg.enable {

    home.packages = [ pkgs.protonmail-bridge ];
    
    systemd.user.services.protonmail-bridge = {
      Unit = {
        Description = "Protonmail Bridge";
        After = [ "network.target" ];
      };

      Service = {
        Restart = "always";
        ExecStart = "${pkgs.protonmail-bridge}/bin/protonmail-bridge --no-window --log-level ${cfg.logLevel}" + optionalString (cfg.nonInteractive) " --noninteractive";
      };

      Install = {
        WantedBy = [ "default.target" ];
      };
    };
  };
}

2 Likes