1{
2 config,
3 options,
4 lib,
5 pkgs,
6 ...
7}:
8let
9 inherit (lib) types;
10
11 cfg = config.services.orthanc;
12 opt = options.services.orthanc;
13
14 settingsFormat = pkgs.formats.json { };
15in
16{
17 options = {
18 services.orthanc = {
19 enable = lib.mkEnableOption "Orthanc server";
20 package = lib.mkPackageOption pkgs "orthanc" { };
21
22 stateDir = lib.mkOption {
23 type = types.path;
24 default = "/var/lib/orthanc";
25 example = "/home/foo";
26 description = "State directory of Orthanc.";
27 };
28
29 environment = lib.mkOption {
30 type = types.attrsOf types.str;
31 default = {
32 };
33 example = ''
34 {
35 ORTHANC_NAME = "Orthanc server";
36 }
37 '';
38 description = ''
39 Extra environment variables
40 For more details see <https://orthanc.uclouvain.be/book/users/configuration.html>
41 '';
42 };
43
44 environmentFile = lib.mkOption {
45 description = ''
46 Environment file to be passed to the systemd service.
47 Useful for passing secrets to the service to prevent them from being
48 world-readable in the Nix store.
49 '';
50 type = lib.types.nullOr lib.types.path;
51 default = null;
52 example = "/var/lib/secrets/orthancSecrets";
53 };
54
55 settings = lib.mkOption {
56 type = lib.types.submodule {
57 freeformType = settingsFormat.type;
58 };
59 default = {
60 HttpPort = lib.mkDefault 8042;
61 IndexDirectory = lib.mkDefault "/var/lib/orthanc/";
62 StorageDirectory = lib.mkDefault "/var/lib/orthanc/";
63 };
64 example = {
65 Name = "My Orthanc Server";
66 HttpPort = 12345;
67 };
68 description = ''
69 Configuration written to a json file that is read by orthanc.
70 See <https://orthanc.uclouvain.be/book/index.html> for more.
71 '';
72 };
73
74 openFirewall = lib.mkOption {
75 type = types.bool;
76 default = false;
77 description = ''
78 Whether to open the firewall for Orthanc.
79 This adds `services.orthanc.settings.HttpPort` to `networking.firewall.allowedTCPPorts`.
80 '';
81 };
82 };
83 };
84
85 config = lib.mkIf cfg.enable {
86 services.orthanc.settings = opt.settings.default;
87
88 systemd.services.orthanc = {
89 description = "Orthanc is a lightweight, RESTful DICOM server for healthcare and medical research";
90 wantedBy = [ "multi-user.target" ];
91 after = [ "network.target" ];
92
93 environment = cfg.environment;
94
95 serviceConfig =
96 let
97 config-json = settingsFormat.generate "orthanc-config.json" (cfg.settings);
98 in
99 {
100 ExecStart = "${lib.getExe cfg.package} ${config-json}";
101 EnvironmentFile = lib.optional (cfg.environmentFile != null) cfg.environmentFile;
102 WorkingDirectory = cfg.stateDir;
103 BindReadOnlyPaths = [
104 "-/etc/localtime"
105 ];
106 StateDirectory = "orthanc";
107 RuntimeDirectory = "orthanc";
108 RuntimeDirectoryMode = "0755";
109 PrivateTmp = true;
110 DynamicUser = true;
111 DevicePolicy = "closed";
112 LockPersonality = true;
113 PrivateUsers = true;
114 ProtectHome = true;
115 ProtectHostname = true;
116 ProtectKernelLogs = true;
117 ProtectKernelModules = true;
118 ProtectKernelTunables = true;
119 ProtectControlGroups = true;
120 RestrictNamespaces = true;
121 RestrictRealtime = true;
122 SystemCallArchitectures = "native";
123 UMask = "0077";
124 };
125 };
126
127 networking.firewall = lib.mkIf cfg.openFirewall { allowedTCPPorts = [ cfg.settings.HttpPort ]; };
128
129 # Orthanc requires /etc/localtime to be present
130 time.timeZone = lib.mkDefault "UTC";
131 };
132
133 meta.maintainers = with lib.maintainers; [ drupol ];
134}