1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8let
9 cfg = config.services.kasmweb;
10in
11{
12 options.services.kasmweb = {
13 enable = lib.mkEnableOption "kasmweb";
14
15 networkSubnet = lib.mkOption {
16 default = "172.20.0.0/16";
17 type = lib.types.str;
18 description = ''
19 The network subnet to use for the containers.
20 '';
21 };
22
23 postgres = {
24 user = lib.mkOption {
25 default = "kasmweb";
26 type = lib.types.str;
27 description = ''
28 Username to use for the postgres database.
29 '';
30 };
31 password = lib.mkOption {
32 default = "kasmweb";
33 type = lib.types.str;
34 description = ''
35 password to use for the postgres database.
36 '';
37 };
38 };
39
40 redisPassword = lib.mkOption {
41 default = "kasmweb";
42 type = lib.types.str;
43 description = ''
44 password to use for the redis cache.
45 '';
46 };
47
48 defaultAdminPassword = lib.mkOption {
49 default = "kasmweb";
50 type = lib.types.str;
51 description = ''
52 default admin password to use.
53 '';
54 };
55
56 defaultUserPassword = lib.mkOption {
57 default = "kasmweb";
58 type = lib.types.str;
59 description = ''
60 default user password to use.
61 '';
62 };
63
64 defaultManagerToken = lib.mkOption {
65 default = "kasmweb";
66 type = lib.types.str;
67 description = ''
68 default manager token to use.
69 '';
70 };
71
72 defaultGuacToken = lib.mkOption {
73 default = "kasmweb";
74 type = lib.types.str;
75 description = ''
76 default guac token to use.
77 '';
78 };
79
80 defaultRegistrationToken = lib.mkOption {
81 default = "kasmweb";
82 type = lib.types.str;
83 description = ''
84 default registration token to use.
85 '';
86 };
87
88 datastorePath = lib.mkOption {
89 type = lib.types.str;
90 default = "/var/lib/kasmweb";
91 description = ''
92 The directory used to store all data for kasmweb.
93 '';
94 };
95
96 listenAddress = lib.mkOption {
97 type = lib.types.str;
98 default = "0.0.0.0";
99 description = ''
100 The address on which kasmweb should listen.
101 '';
102 };
103
104 listenPort = lib.mkOption {
105 type = lib.types.int;
106 default = 443;
107 description = ''
108 The port on which kasmweb should listen.
109 '';
110 };
111
112 sslCertificate = lib.mkOption {
113 type = lib.types.nullOr lib.types.path;
114 default = null;
115 description = ''
116 The SSL certificate to be used for kasmweb.
117 '';
118 };
119
120 sslCertificateKey = lib.mkOption {
121 type = lib.types.nullOr lib.types.path;
122 default = null;
123 description = ''
124 The SSL certificate's key to be used for kasmweb. Make sure to specify
125 this as a string and not a literal path, so that it is not accidentally
126 included in your nixstore.
127 '';
128 };
129 };
130
131 config = lib.mkIf cfg.enable {
132 systemd.services = {
133 "init-kasmweb" = {
134 wantedBy = [
135 "docker-kasm_db.service"
136 "podman-kasm_db.service"
137 ];
138 wants = [ "network-online.target" ];
139 after = [ "network-online.target" ];
140 serviceConfig = {
141 Type = "oneshot";
142 TimeoutStartSec = 300;
143 ExecStart = pkgs.replaceVarsWith {
144 src = ./initialize_kasmweb.sh;
145 isExecutable = true;
146 replacements = {
147 binPath = lib.makeBinPath [
148 pkgs.docker
149 pkgs.openssl
150 pkgs.gnused
151 pkgs.yq-go
152 ];
153 runtimeShell = pkgs.runtimeShell;
154 kasmweb = pkgs.kasmweb;
155 postgresUser = "postgres";
156 postgresPassword = "postgres";
157 inherit (cfg)
158 datastorePath
159 sslCertificate
160 sslCertificateKey
161 redisPassword
162 networkSubnet
163 defaultUserPassword
164 defaultAdminPassword
165 defaultManagerToken
166 defaultRegistrationToken
167 defaultGuacToken
168 ;
169 };
170 };
171 };
172 };
173 };
174
175 virtualisation = {
176 oci-containers.backend = "docker";
177 oci-containers.containers = {
178 kasm_db = {
179 image = "postgres:16-alpine";
180 autoStart = true;
181 environment = {
182 POSTGRES_PASSWORD = "postgres";
183 POSTGRES_USER = "postgres";
184 POSTGRES_DB = "kasm";
185 };
186 volumes = [
187 "${cfg.datastorePath}/conf/database/data.sql:/docker-entrypoint-initdb.d/data.sql"
188 "${cfg.datastorePath}/conf/database/:/tmp/"
189 "kasmweb_db:/var/lib/postgresql/data"
190 ];
191 extraOptions = [ "--network=kasm_default_network" ];
192 };
193 kasm_db_init = {
194 image = "kasmweb/api:${pkgs.kasmweb.version}";
195 user = "root:root";
196 autoStart = true;
197 volumes = [
198 "${cfg.datastorePath}/:/opt/kasm/current/"
199 "kasmweb_api_data:/tmp"
200 ];
201 dependsOn = [ "kasm_db" ];
202 entrypoint = "/bin/bash";
203 cmd = [ "/opt/kasm/current/init_seeds.sh" ];
204 extraOptions = [
205 "--network=kasm_default_network"
206 "--userns=host"
207 ];
208 };
209 kasm_redis = {
210 image = "redis:5-alpine";
211 entrypoint = "/bin/sh";
212 autoStart = true;
213 cmd = [
214 "-c"
215 "redis-server --requirepass ${cfg.redisPassword}"
216 ];
217 extraOptions = [
218 "--network=kasm_default_network"
219 "--userns=host"
220 ];
221 };
222 kasm_api = {
223 image = "kasmweb/api:${pkgs.kasmweb.version}";
224 autoStart = false;
225 user = "root:root";
226 volumes = [
227 "${cfg.datastorePath}/:/opt/kasm/current/"
228 "kasmweb_api_data:/tmp"
229 ];
230 dependsOn = [ "kasm_db_init" ];
231 extraOptions = [
232 "--network=kasm_default_network"
233 "--userns=host"
234 ];
235 };
236 kasm_manager = {
237 image = "kasmweb/manager:${pkgs.kasmweb.version}";
238 autoStart = false;
239 user = "root:root";
240 volumes = [
241 "${cfg.datastorePath}/:/opt/kasm/current/"
242 ];
243 dependsOn = [
244 "kasm_db_init"
245 "kasm_db"
246 "kasm_api"
247 ];
248 extraOptions = [
249 "--network=kasm_default_network"
250 "--userns=host"
251 "--read-only"
252 ];
253 };
254 kasm_agent = {
255 image = "kasmweb/agent:${pkgs.kasmweb.version}";
256 autoStart = false;
257 user = "root:root";
258 volumes = [
259 "${cfg.datastorePath}/:/opt/kasm/current/"
260 "/var/run/docker.sock:/var/run/docker.sock"
261 "${pkgs.docker}/bin/docker:/usr/bin/docker"
262 "${cfg.datastorePath}/conf/nginx:/etc/nginx/conf.d"
263 ];
264 dependsOn = [ "kasm_manager" ];
265 extraOptions = [
266 "--network=kasm_default_network"
267 "--userns=host"
268 "--read-only"
269 ];
270 };
271 kasm_share = {
272 image = "kasmweb/share:${pkgs.kasmweb.version}";
273 autoStart = false;
274 user = "root:root";
275 volumes = [
276 "${cfg.datastorePath}/:/opt/kasm/current/"
277 ];
278 dependsOn = [
279 "kasm_db_init"
280 "kasm_db"
281 "kasm_redis"
282 ];
283 extraOptions = [
284 "--network=kasm_default_network"
285 "--userns=host"
286 "--read-only"
287 ];
288 };
289 kasm_guac = {
290 image = "kasmweb/kasm-guac:${pkgs.kasmweb.version}";
291 autoStart = false;
292 user = "root:root";
293 volumes = [
294 "${cfg.datastorePath}/:/opt/kasm/current/"
295 ];
296 dependsOn = [
297 "kasm_db"
298 "kasm_redis"
299 ];
300 extraOptions = [
301 "--network=kasm_default_network"
302 "--userns=host"
303 "--read-only"
304 ];
305 };
306 kasm_proxy = {
307 image = "kasmweb/nginx:latest";
308 autoStart = false;
309 ports = [ "${cfg.listenAddress}:${toString cfg.listenPort}:443" ];
310 user = "root:root";
311 volumes = [
312 "${cfg.datastorePath}/conf/nginx:/etc/nginx/conf.d:ro"
313 "${cfg.datastorePath}/certs/kasm_nginx.key:/etc/ssl/private/kasm_nginx.key"
314 "${cfg.datastorePath}/certs/kasm_nginx.crt:/etc/ssl/certs/kasm_nginx.crt"
315 "${cfg.datastorePath}/www:/srv/www:ro"
316 "${cfg.datastorePath}/log/nginx:/var/log/external/nginx"
317 "${cfg.datastorePath}/log/logrotate:/var/log/external/logrotate"
318 ];
319 dependsOn = [
320 "kasm_manager"
321 "kasm_api"
322 "kasm_agent"
323 "kasm_share"
324 "kasm_guac"
325 ];
326 extraOptions = [
327 "--network=kasm_default_network"
328 "--userns=host"
329 "--network-alias=proxy"
330 ];
331 };
332 };
333 };
334 };
335}