about summary refs log tree commit diff
path: root/nixpkgs/nixos/modules/services/misc/mollysocket.nix
blob: f40caa4a782ef68cc5298a22b205d3ff4e29a325 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
{ config, lib, pkgs, ... }:

let
  inherit (lib) getExe mkIf mkOption mkEnableOption optionals types;

  cfg = config.services.mollysocket;
  configuration = format.generate "mollysocket.conf" cfg.settings;
  format = pkgs.formats.toml { };
  package = pkgs.writeShellScriptBin "mollysocket" ''
    MOLLY_CONF=${configuration} exec ${getExe pkgs.mollysocket} "$@"
  '';
in {
  options.services.mollysocket = {
    enable = mkEnableOption ''
      [MollySocket](https://github.com/mollyim/mollysocket) for getting Signal
      notifications via UnifiedPush
    '';

    settings = mkOption {
      default = { };
      description = ''
        Configuration for MollySocket. Available options are listed
        [here](https://github.com/mollyim/mollysocket#configuration).
      '';
      type = types.submodule {
        freeformType = format.type;
        options = {
          host = mkOption {
            default = "127.0.0.1";
            description = "Listening address of the web server";
            type = types.str;
          };

          port = mkOption {
            default = 8020;
            description = "Listening port of the web server";
            type = types.port;
          };

          allowed_endpoints = mkOption {
            default = [ "*" ];
            description = "List of UnifiedPush servers";
            example = [ "https://ntfy.sh" ];
            type = with types; listOf str;
          };

          allowed_uuids = mkOption {
            default = [ "*" ];
            description = "UUIDs of Signal accounts that may use this server";
            example = [ "abcdef-12345-tuxyz-67890" ];
            type = with types; listOf str;
          };
        };
      };
    };

    environmentFile = mkOption {
      default = null;
      description = ''
        Environment file (see {manpage}`systemd.exec(5)` "EnvironmentFile="
        section for the syntax) passed to the service. This option can be
        used to safely include secrets in the configuration.
      '';
      example = "/run/secrets/mollysocket";
      type = with types; nullOr path;
    };

    logLevel = mkOption {
      default = "info";
      description = "Set the {env}`RUST_LOG` environment variable";
      example = "debug";
      type = types.str;
    };
  };

  config = mkIf cfg.enable {
    environment.systemPackages = [
      package
    ];

    # see https://github.com/mollyim/mollysocket/blob/main/mollysocket.service
    systemd.services.mollysocket = {
      description = "MollySocket";
      wantedBy = [ "multi-user.target" ];
      after = [ "network-online.target" ];
      wants = [ "network-online.target" ];
      environment.RUST_LOG = cfg.logLevel;
      serviceConfig = let
        capabilities = [ "" ] ++ optionals (cfg.settings.port < 1024) [ "CAP_NET_BIND_SERVICE" ];
      in {
        EnvironmentFile = cfg.environmentFile;
        ExecStart = "${getExe package} server";
        KillSignal = "SIGINT";
        Restart = "on-failure";
        StateDirectory = "mollysocket";
        TimeoutStopSec = 5;
        WorkingDirectory = "/var/lib/mollysocket";

        # hardening
        AmbientCapabilities = capabilities;
        CapabilityBoundingSet = capabilities;
        DevicePolicy = "closed";
        DynamicUser = true;
        LockPersonality = true;
        MemoryDenyWriteExecute = true;
        NoNewPrivileges = true;
        PrivateDevices = true;
        PrivateTmp = true;
        PrivateUsers = true;
        ProcSubset = "pid";
        ProtectClock = true;
        ProtectControlGroups = true;
        ProtectHome = true;
        ProtectHostname = true;
        ProtectKernelLogs = true;
        ProtectKernelModules = true;
        ProtectKernelTunables = true;
        ProtectProc = "invisible";
        ProtectSystem = "strict";
        RemoveIPC = true;
        RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
        RestrictNamespaces = true;
        RestrictRealtime = true;
        RestrictSUIDSGID = true;
        SystemCallArchitectures = "native";
        SystemCallFilter = [ "@system-service" "~@resources" "~@privileged" ];
        UMask = "0077";
      };
    };
  };

  meta.maintainers = with lib.maintainers; [ dotlambda ];
}