From dbfe3bd2e70ba58935497cf32142981a6c82561e Mon Sep 17 00:00:00 2001 From: s1341 Date: Thu, 29 Dec 2022 08:05:41 +0200 Subject: [PATCH] kasmweb: init at 1.12.0 --- nixos/modules/module-list.nix | 1 + .../services/web-apps/kasmweb/default.nix | 275 ++++++++++++++++++ .../web-apps/kasmweb/initialize_kasmweb.sh | 114 ++++++++ pkgs/servers/web-apps/kasmweb/default.nix | 36 +++ pkgs/top-level/all-packages.nix | 2 + 5 files changed, 428 insertions(+) create mode 100644 nixos/modules/services/web-apps/kasmweb/default.nix create mode 100644 nixos/modules/services/web-apps/kasmweb/initialize_kasmweb.sh create mode 100644 pkgs/servers/web-apps/kasmweb/default.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 7903cd23a750..061d0ff1da4b 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1135,6 +1135,7 @@ ./services/web-apps/isso.nix ./services/web-apps/jirafeau.nix ./services/web-apps/jitsi-meet.nix + ./services/web-apps/kasmweb/default.nix ./services/web-apps/keycloak.nix ./services/web-apps/komga.nix ./services/web-apps/lemmy.nix diff --git a/nixos/modules/services/web-apps/kasmweb/default.nix b/nixos/modules/services/web-apps/kasmweb/default.nix new file mode 100644 index 000000000000..0d78025ecf0f --- /dev/null +++ b/nixos/modules/services/web-apps/kasmweb/default.nix @@ -0,0 +1,275 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.kasmweb; +in +{ + options.services.kasmweb = { + enable = lib.mkEnableOption (lib.mdDoc "kasmweb"); + + networkSubnet = lib.mkOption { + default = "172.20.0.0/16"; + type = lib.types.str; + description = lib.mdDoc '' + The network subnet to use for the containers. + ''; + }; + + postgres = { + user = lib.mkOption { + default = "kasmweb"; + type = lib.types.str; + description = lib.mdDoc '' + Username to use for the postgres database. + ''; + }; + password = lib.mkOption { + default = "kasmweb"; + type = lib.types.str; + description = lib.mdDoc '' + password to use for the postgres database. + ''; + }; + }; + + redisPassword = lib.mkOption { + default = "kasmweb"; + type = lib.types.str; + description = lib.mdDoc '' + password to use for the redis cache. + ''; + }; + + defaultAdminPassword = lib.mkOption { + default = "kasmweb"; + type = lib.types.str; + description = lib.mdDoc '' + default admin password to use. + ''; + }; + + defaultUserPassword = lib.mkOption { + default = "kasmweb"; + type = lib.types.str; + description = lib.mdDoc '' + default user password to use. + ''; + }; + + defaultManagerToken = lib.mkOption { + default = "kasmweb"; + type = lib.types.str; + description = lib.mdDoc '' + default manager token to use. + ''; + }; + + defaultGuacToken = lib.mkOption { + default = "kasmweb"; + type = lib.types.str; + description = lib.mdDoc '' + default guac token to use. + ''; + }; + + defaultRegistrationToken = lib.mkOption { + default = "kasmweb"; + type = lib.types.str; + description = lib.mdDoc '' + default registration token to use. + ''; + }; + + datastorePath = lib.mkOption { + type = lib.types.str; + default = "/var/lib/kasmweb"; + description = lib.mdDoc '' + The directory used to store all data for kasmweb. + ''; + }; + + listenAddress = lib.mkOption { + type = lib.types.str; + default = "0.0.0.0"; + description = lib.mdDoc '' + The address on which kasmweb should listen. + ''; + }; + + listenPort = lib.mkOption { + type = lib.types.int; + default = 443; + description = lib.mdDoc '' + The port on which kasmweb should listen. + ''; + }; + + sslCertificate = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + description = lib.mdDoc '' + The SSL certificate to be used for kasmweb. + ''; + }; + + sslCertificateKey = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + description = lib.mdDoc '' + The SSL certificate's key to be used for kasmweb. Make sure to specify + this as a string and not a literal path, so that it is not accidentally + included in your nixstore. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + + systemd.services = { + "init-kasmweb" = { + wantedBy = [ + "docker-kasm_db.service" + ]; + before = [ + "docker-kasm_db.service" + "docker-kasm_redis.service" + "docker-kasm_db_init.service" + "docker-kasm_api.service" + "docker-kasm_agent.service" + "docker-kasm_manager.service" + "docker-kasm_share.service" + "docker-kasm_guac.service" + "docker-kasm_proxy.service" + ]; + serviceConfig = { + Type = "oneshot"; + ExecStart = pkgs.substituteAll { + src = ./initialize_kasmweb.sh; + isExecutable = true; + binPath = lib.makeBinPath [ pkgs.docker pkgs.openssl pkgs.gnused ]; + runtimeShell = pkgs.runtimeShell; + kasmweb = pkgs.kasmweb; + postgresUser = cfg.postgres.user; + postgresPassword = cfg.postgres.password; + inherit (cfg) + datastorePath + sslCertificate + sslCertificateKey + redisPassword + defaultUserPassword + defaultAdminPassword + defaultManagerToken + defaultRegistrationToken + defaultGuacToken; + }; + }; + }; + }; + + virtualisation = { + oci-containers.containers = { + kasm_db = { + image = "postgres:12-alpine"; + environment = { + POSTGRES_PASSWORD = cfg.postgres.password; + POSTGRES_USER = cfg.postgres.user; + POSTGRES_DB = "kasm"; + }; + volumes = [ + "${cfg.datastorePath}/conf/database/data.sql:/docker-entrypoint-initdb.d/data.sql" + "${cfg.datastorePath}/conf/database/:/tmp/" + "kasmweb_db:/var/lib/postgresql/data" + ]; + extraOptions = [ "--network=kasm_default_network" ]; + }; + kasm_db_init = { + image = "kasmweb/api:${pkgs.kasmweb.version}"; + user = "root:root"; + volumes = [ + "${cfg.datastorePath}/:/opt/kasm/current/" + "kasmweb_api_data:/tmp" + ]; + dependsOn = [ "kasm_db" ]; + entrypoint = "/bin/bash"; + cmd = [ "/opt/kasm/current/init_seeds.sh" ]; + extraOptions = [ "--network=kasm_default_network" "--userns=host" ]; + }; + kasm_redis = { + image = "redis:5-alpine"; + entrypoint = "/bin/sh"; + cmd = [ + "-c" + "redis-server --requirepass ${cfg.redisPassword}" + ]; + extraOptions = [ "--network=kasm_default_network" "--userns=host" ]; + }; + kasm_api = { + image = "kasmweb/api:${pkgs.kasmweb.version}"; + user = "root:root"; + volumes = [ + "${cfg.datastorePath}/:/opt/kasm/current/" + "kasmweb_api_data:/tmp" + ]; + dependsOn = [ "kasm_db_init" ]; + extraOptions = [ "--network=kasm_default_network" "--userns=host" ]; + }; + kasm_manager = { + image = "kasmweb/manager:${pkgs.kasmweb.version}"; + user = "root:root"; + volumes = [ + "${cfg.datastorePath}/:/opt/kasm/current/" + ]; + dependsOn = [ "kasm_db" "kasm_api" ]; + extraOptions = [ "--network=kasm_default_network" "--userns=host" "--read-only"]; + }; + kasm_agent = { + image = "kasmweb/agent:${pkgs.kasmweb.version}"; + user = "root:root"; + volumes = [ + "${cfg.datastorePath}/:/opt/kasm/current/" + "/var/run/docker.sock:/var/run/docker.sock" + "${pkgs.docker}/bin/docker:/usr/bin/docker" + "${cfg.datastorePath}/conf/nginx:/etc/nginx/conf.d" + ]; + dependsOn = [ "kasm_manager" ]; + extraOptions = [ "--network=kasm_default_network" "--userns=host" "--read-only" ]; + }; + kasm_share = { + image = "kasmweb/share:${pkgs.kasmweb.version}"; + user = "root:root"; + volumes = [ + "${cfg.datastorePath}/:/opt/kasm/current/" + ]; + dependsOn = [ "kasm_db" "kasm_redis" ]; + extraOptions = [ "--network=kasm_default_network" "--userns=host" "--read-only" ]; + }; + kasm_guac = { + image = "kasmweb/kasm-guac:${pkgs.kasmweb.version}"; + user = "root:root"; + volumes = [ + "${cfg.datastorePath}/:/opt/kasm/current/" + ]; + dependsOn = [ "kasm_db" "kasm_redis" ]; + extraOptions = [ "--network=kasm_default_network" "--userns=host" "--read-only" ]; + }; + kasm_proxy = { + image = "kasmweb/nginx:latest"; + ports = [ "${cfg.listenAddress}:${toString cfg.listenPort}:443" ]; + user = "root:root"; + volumes = [ + "${cfg.datastorePath}/conf/nginx:/etc/nginx/conf.d:ro" + "${cfg.datastorePath}/certs/kasm_nginx.key:/etc/ssl/private/kasm_nginx.key" + "${cfg.datastorePath}/certs/kasm_nginx.crt:/etc/ssl/certs/kasm_nginx.crt" + "${cfg.datastorePath}/www:/srv/www:ro" + "${cfg.datastorePath}/log/nginx:/var/log/external/nginx" + "${cfg.datastorePath}/log/logrotate:/var/log/external/logrotate" + ]; + dependsOn = [ "kasm_manager" "kasm_api" "kasm_agent" "kasm_share" + "kasm_guac" ]; + extraOptions = [ "--network=kasm_default_network" "--userns=host" + "--network-alias=proxy"]; + }; + }; + }; + }; +} diff --git a/nixos/modules/services/web-apps/kasmweb/initialize_kasmweb.sh b/nixos/modules/services/web-apps/kasmweb/initialize_kasmweb.sh new file mode 100644 index 000000000000..dbf043b98693 --- /dev/null +++ b/nixos/modules/services/web-apps/kasmweb/initialize_kasmweb.sh @@ -0,0 +1,114 @@ +#! @runtimeShell@ +export PATH=@binPath@:$PATH + +mkdir -p @datastorePath@/log +chmod -R a+rw @datastorePath@ + +ln -sf @kasmweb@/bin @datastorePath@ +rm -r @datastorePath@/conf +cp -r @kasmweb@/conf @datastorePath@ +mkdir -p @datastorePath@/conf/nginx/containers.d +chmod -R a+rw @datastorePath@/conf +ln -sf @kasmweb@/www @datastorePath@ + + +docker network inspect kasm_default_network >/dev/null || docker network create kasm_default_network --subnet @networkSubnet@ +if docker volume inspect kasmweb_db >/dev/null; then + source @datastorePath@/ids.env + echo 'echo "skipping database init"' > @datastorePath@/init_seeds.sh + echo 'while true; do sleep 10 ; done' >> @datastorePath@/init_seeds.sh +else + API_SERVER_ID=$(cat /proc/sys/kernel/random/uuid) + MANAGER_ID=$(cat /proc/sys/kernel/random/uuid) + SHARE_ID=$(cat /proc/sys/kernel/random/uuid) + SERVER_ID=$(cat /proc/sys/kernel/random/uuid) + echo "export API_SERVER_ID=$API_SERVER_ID" > @datastorePath@/ids.env + echo "export MANAGER_ID=$MANAGER_ID" >> @datastorePath@/ids.env + echo "export SHARE_ID=$SHARE_ID" >> @datastorePath@/ids.env + echo "export SERVER_ID=$SERVER_ID" >> @datastorePath@/ids.env + + mkdir -p @datastorePath@/certs + openssl req -x509 -nodes -days 1825 -newkey rsa:2048 -keyout @datastorePath@/certs/kasm_nginx.key -out @datastorePath@/certs/kasm_nginx.crt -subj "/C=US/ST=VA/L=None/O=None/OU=DoFu/CN=$(hostname)/emailAddress=none@none.none" 2> /dev/null + + docker volume create kasmweb_db + rm @datastorePath@/.done_initing_data + cat >@datastorePath@/init_seeds.sh <&1 | grep -v UserWarning + /usr/bin/kasm_server.so --cfg \ + /opt/kasm/current/conf/app/api.app.config.yaml \ + --populate-production \ + --seed-file \ + /opt/kasm/current/conf/database/seed_data/default_agents.yaml \ + 2>&1 | grep -v UserWarning + /usr/bin/kasm_server.so --cfg \ + /opt/kasm/current/conf/app/api.app.config.yaml \ + --populate-production \ + --seed-file \ + /opt/kasm/current/conf/database/seed_data/default_connection_proxies.yaml \ + 2>&1 | grep -v UserWarning + /usr/bin/kasm_server.so --cfg \ + /opt/kasm/current/conf/app/api.app.config.yaml \ + --populate-production \ + --seed-file \ + /opt/kasm/current/conf/database/seed_data/default_images_amd64.yaml \ + 2>&1 | grep -v UserWarning + touch /opt/kasm/current/.done_initing_data + while true; do sleep 10 ; done +else + echo "skipping database init" + while true; do sleep 10 ; done +fi +EOF +fi + +chmod +x @datastorePath@/init_seeds.sh +chmod a+w @datastorePath@/init_seeds.sh + +if [ -e @sslCertificate@ ]; then + cp @sslCertificate@ @datastorePath@/certs/kasm_nginx.crt + cp @sslCertificateKey@ @datastorePath@/certs/kasm_nginx.key +fi + +sed -i -e "s/username.*/username: @postgresUser@/g" \ + -e "s/password.*/password: @postgresPassword@/g" \ + -e "s/host.*db/host: kasm_db/g" \ + -e "s/ssl: true/ssl: false/g" \ + -e "s/redisPassword.*/redisPassword: @redisPassword@/g" \ + -e "s/server_hostname.*/server_hostname: kasm_api/g" \ + -e "s/server_id.*/server_id: $API_SERVER_ID/g" \ + -e "s/manager_id.*/manager_id: $MANAGER_ID/g" \ + -e "s/share_id.*/share_id: $SHARE_ID/g" \ + @datastorePath@/conf/app/api.app.config.yaml + +sed -i -e "s/ token:.*/ token: \"@defaultManagerToken@\"/g" \ + -e "s/hostnames: \['proxy.*/hostnames: \['kasm_proxy'\]/g" \ + -e "s/server_id.*/server_id: $SERVER_ID/g" \ + @datastorePath@/conf/app/agent.app.config.yaml + + +sed -i -e "s/password: admin.*/password: \"@defaultAdminPassword@\"/g" \ + -e "s/password: user.*/password: \"@defaultUserPassword@\"/g" \ + -e "s/default-manager-token/@defaultManagerToken@/g" \ + -e "s/default-registration-token/@defaultRegistrationToken@/g" \ + -e "s/upstream_auth_address:.*/upstream_auth_address: 'proxy'/g" \ + @datastorePath@/conf/database/seed_data/default_properties.yaml + +sed -i -e "s/GUACTOKEN/@defaultGuacToken@/g" \ + -e "s/APIHOSTNAME/proxy/g" \ + @datastorePath@/conf/app/kasmguac.app.config.yaml + +sed -i -e "s/GUACTOKEN/@defaultGuacToken@/g" \ + -e "s/APIHOSTNAME/proxy/g" \ + @datastorePath@/conf/database/seed_data/default_connection_proxies.yaml + +sed -i "s/00000000-0000-0000-0000-000000000000/$SERVER_ID/g" \ + @datastorePath@/conf/database/seed_data/default_agents.yaml + diff --git a/pkgs/servers/web-apps/kasmweb/default.nix b/pkgs/servers/web-apps/kasmweb/default.nix new file mode 100644 index 000000000000..f83db6baddc5 --- /dev/null +++ b/pkgs/servers/web-apps/kasmweb/default.nix @@ -0,0 +1,36 @@ +{ stdenv +, lib +, fetchzip +}: + +stdenv.mkDerivation rec { + pname = "kasmweb"; + version = "1.12.0"; + build = "d4fd8a"; + + src = fetchzip { + url = "https://kasm-static-content.s3.amazonaws.com/kasm_release_${version}.${build}.tar.gz"; + sha256 = "sha256-dCjWmI8gYtoMiMHVNgTg2ZROHXvT4ulynNvnKfMxURo="; + }; + + dontConfigure = true; + dontBuild = true; + + installPhase = '' + runHook preInstall + + mkdir $out + rm bin/utils/yq* + cp -r bin conf www $out/ + + runHook postInstall + ''; + + + meta = with lib; { + homepage = "https://www.kasmweb.com/"; + description = "Streaming containerized apps and desktops to end-users"; + license = licenses.unfree; + maintainers = with maintainers; [ s1341 ]; + }; +} diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 5fd949664b11..360a2bbfa60e 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -35575,6 +35575,8 @@ with pkgs; autoconf = buildPackages.autoconf269; }; + kasmweb = callPackage ../servers/web-apps/kasmweb { }; + kssd = callPackage ../applications/science/biology/kssd { }; last = callPackage ../applications/science/biology/last { };