1{ config, lib, pkgs, buildEnv, ... }:
2
3with lib;
4
5let
6 cfg = config.services.netbox;
7 staticDir = cfg.dataDir + "/static";
8 configFile = pkgs.writeTextFile {
9 name = "configuration.py";
10 text = ''
11 STATIC_ROOT = '${staticDir}'
12 ALLOWED_HOSTS = ['*']
13 DATABASE = {
14 'NAME': 'netbox',
15 'USER': 'netbox',
16 'HOST': '/run/postgresql',
17 }
18
19 # Redis database settings. Redis is used for caching and for queuing background tasks such as webhook events. A separate
20 # configuration exists for each. Full connection details are required in both sections, and it is strongly recommended
21 # to use two separate database IDs.
22 REDIS = {
23 'tasks': {
24 'URL': 'unix://${config.services.redis.servers.netbox.unixSocket}?db=0',
25 'SSL': False,
26 },
27 'caching': {
28 'URL': 'unix://${config.services.redis.servers.netbox.unixSocket}?db=1',
29 'SSL': False,
30 }
31 }
32
33 with open("${cfg.secretKeyFile}", "r") as file:
34 SECRET_KEY = file.readline()
35
36 ${optionalString cfg.enableLdap "REMOTE_AUTH_BACKEND = 'netbox.authentication.LDAPBackend'"}
37
38 ${cfg.extraConfig}
39 '';
40 };
41 pkg = (pkgs.netbox.overrideAttrs (old: {
42 installPhase = old.installPhase + ''
43 ln -s ${configFile} $out/opt/netbox/netbox/netbox/configuration.py
44 '' + optionalString cfg.enableLdap ''
45 ln -s ${ldapConfigPath} $out/opt/netbox/netbox/netbox/ldap_config.py
46 '';
47 })).override {
48 plugins = ps: ((cfg.plugins ps)
49 ++ optionals cfg.enableLdap [ ps.django-auth-ldap ]);
50 };
51 netboxManageScript = with pkgs; (writeScriptBin "netbox-manage" ''
52 #!${stdenv.shell}
53 export PYTHONPATH=${pkg.pythonPath}
54 sudo -u netbox ${pkg}/bin/netbox "$@"
55 '');
56
57in {
58 options.services.netbox = {
59 enable = mkOption {
60 type = lib.types.bool;
61 default = false;
62 description = lib.mdDoc ''
63 Enable Netbox.
64
65 This module requires a reverse proxy that serves `/static` separately.
66 See this [example](https://github.com/netbox-community/netbox/blob/develop/contrib/nginx.conf/) on how to configure this.
67 '';
68 };
69
70 listenAddress = mkOption {
71 type = types.str;
72 default = "[::1]";
73 description = lib.mdDoc ''
74 Address the server will listen on.
75 '';
76 };
77
78 port = mkOption {
79 type = types.port;
80 default = 8001;
81 description = lib.mdDoc ''
82 Port the server will listen on.
83 '';
84 };
85
86 plugins = mkOption {
87 type = types.functionTo (types.listOf types.package);
88 default = _: [];
89 defaultText = literalExpression ''
90 python3Packages: with python3Packages; [];
91 '';
92 description = lib.mdDoc ''
93 List of plugin packages to install.
94 '';
95 };
96
97 dataDir = mkOption {
98 type = types.str;
99 default = "/var/lib/netbox";
100 description = lib.mdDoc ''
101 Storage path of netbox.
102 '';
103 };
104
105 secretKeyFile = mkOption {
106 type = types.path;
107 description = lib.mdDoc ''
108 Path to a file containing the secret key.
109 '';
110 };
111
112 extraConfig = mkOption {
113 type = types.lines;
114 default = "";
115 description = lib.mdDoc ''
116 Additional lines of configuration appended to the `configuration.py`.
117 See the [documentation](https://netbox.readthedocs.io/en/stable/configuration/optional-settings/) for more possible options.
118 '';
119 };
120
121 enableLdap = mkOption {
122 type = types.bool;
123 default = false;
124 description = lib.mdDoc ''
125 Enable LDAP-Authentication for Netbox.
126
127 This requires a configuration file being pass through `ldapConfigPath`.
128 '';
129 };
130
131 ldapConfigPath = mkOption {
132 type = types.path;
133 default = "";
134 description = lib.mdDoc ''
135 Path to the Configuration-File for LDAP-Authentification, will be loaded as `ldap_config.py`.
136 See the [documentation](https://netbox.readthedocs.io/en/stable/installation/6-ldap/#configuration) for possible options.
137 '';
138 };
139 };
140
141 config = mkIf cfg.enable {
142 services.redis.servers.netbox.enable = true;
143
144 services.postgresql = {
145 enable = true;
146 ensureDatabases = [ "netbox" ];
147 ensureUsers = [
148 {
149 name = "netbox";
150 ensurePermissions = {
151 "DATABASE netbox" = "ALL PRIVILEGES";
152 };
153 }
154 ];
155 };
156
157 environment.systemPackages = [ netboxManageScript ];
158
159 systemd.targets.netbox = {
160 description = "Target for all NetBox services";
161 wantedBy = [ "multi-user.target" ];
162 after = [ "network-online.target" "redis-netbox.service" ];
163 };
164
165 systemd.services = let
166 defaultServiceConfig = {
167 WorkingDirectory = "${cfg.dataDir}";
168 User = "netbox";
169 Group = "netbox";
170 StateDirectory = "netbox";
171 StateDirectoryMode = "0750";
172 Restart = "on-failure";
173 };
174 in {
175 netbox-migration = {
176 description = "NetBox migrations";
177 wantedBy = [ "netbox.target" ];
178
179 environment = {
180 PYTHONPATH = pkg.pythonPath;
181 };
182
183 serviceConfig = defaultServiceConfig // {
184 Type = "oneshot";
185 ExecStart = ''
186 ${pkg}/bin/netbox migrate
187 '';
188 };
189 };
190
191 netbox = {
192 description = "NetBox WSGI Service";
193 wantedBy = [ "netbox.target" ];
194 after = [ "netbox-migration.service" ];
195
196 preStart = ''
197 ${pkg}/bin/netbox trace_paths --no-input
198 ${pkg}/bin/netbox collectstatic --no-input
199 ${pkg}/bin/netbox remove_stale_contenttypes --no-input
200 '';
201
202 environment = {
203 PYTHONPATH = pkg.pythonPath;
204 };
205
206 serviceConfig = defaultServiceConfig // {
207 ExecStart = ''
208 ${pkgs.python3Packages.gunicorn}/bin/gunicorn netbox.wsgi \
209 --bind ${cfg.listenAddress}:${toString cfg.port} \
210 --pythonpath ${pkg}/opt/netbox/netbox
211 '';
212 };
213 };
214
215 netbox-rq = {
216 description = "NetBox Request Queue Worker";
217 wantedBy = [ "netbox.target" ];
218 after = [ "netbox.service" ];
219
220 environment = {
221 PYTHONPATH = pkg.pythonPath;
222 };
223
224 serviceConfig = defaultServiceConfig // {
225 ExecStart = ''
226 ${pkg}/bin/netbox rqworker high default low
227 '';
228 };
229 };
230
231 netbox-housekeeping = {
232 description = "NetBox housekeeping job";
233 after = [ "netbox.service" ];
234
235 environment = {
236 PYTHONPATH = pkg.pythonPath;
237 };
238
239 serviceConfig = defaultServiceConfig // {
240 Type = "oneshot";
241 ExecStart = ''
242 ${pkg}/bin/netbox housekeeping
243 '';
244 };
245 };
246 };
247
248 systemd.timers.netbox-housekeeping = {
249 description = "Run NetBox housekeeping job";
250 wantedBy = [ "timers.target" ];
251
252 timerConfig = {
253 OnCalendar = "daily";
254 };
255 };
256
257 users.users.netbox = {
258 home = "${cfg.dataDir}";
259 isSystemUser = true;
260 group = "netbox";
261 };
262 users.groups.netbox = {};
263 users.groups."${config.services.redis.servers.netbox.user}".members = [ "netbox" ];
264 };
265}