diff --git a/kasmvnc/README.md b/kasmvnc/README.md new file mode 100644 index 00000000..845cbc23 --- /dev/null +++ b/kasmvnc/README.md @@ -0,0 +1,23 @@ +--- +display_name: KasmVNC +description: A modern open source VNC server +icon: ../.icons/kasmvnc.svg +maintainer_github: coder +verified: true +tags: [helper, vnc, desktop] +--- + +# KasmVNC + +Automatically install [KasmVNC](https://kasmweb.com/kasmvnc) in a workspace, and create an app to access it via the dashboard. + +```tf +module "kasmvnc" { + source = "registry.coder.com/modules/kasmvnc/coder" + version = "1.0.21" + agent_id = coder_agent.example.id + desktop_environment = "xfce" +} +``` + +> **Note:** This module only works on workspaces with a pre-installed desktop environment. As an example base image you can use `codercom/enterprise-desktop` image. diff --git a/kasmvnc/main.test.ts b/kasmvnc/main.test.ts new file mode 100644 index 00000000..0116d053 --- /dev/null +++ b/kasmvnc/main.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, it } from "bun:test"; +import { + runTerraformApply, + runTerraformInit, + testRequiredVariables, +} from "../test"; + +const allowedDesktopEnvs = ["xfce", "kde", "gnome", "lxde", "lxqt"] as const; +type AllowedDesktopEnv = (typeof allowedDesktopEnvs)[number]; + +type TestVariables = Readonly<{ + agent_id: string; + desktop_environment: AllowedDesktopEnv; + port?: string; + kasm_version?: string; +}>; + +describe("Kasm VNC", async () => { + await runTerraformInit(import.meta.dir); + testRequiredVariables(import.meta.dir, { + agent_id: "foo", + desktop_environment: "gnome", + }); + + it("Successfully installs for all expected Kasm desktop versions", async () => { + for (const v of allowedDesktopEnvs) { + const applyWithEnv = () => { + runTerraformApply(import.meta.dir, { + agent_id: "foo", + desktop_environment: v, + }); + }; + + expect(applyWithEnv).not.toThrow(); + } + }); +}); diff --git a/kasmvnc/main.tf b/kasmvnc/main.tf new file mode 100644 index 00000000..3a730ff5 --- /dev/null +++ b/kasmvnc/main.tf @@ -0,0 +1,63 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + coder = { + source = "coder/coder" + version = ">= 0.12" + } + } +} + +variable "agent_id" { + type = string + description = "The ID of a Coder agent." +} + +variable "port" { + type = number + description = "The port to run KasmVNC on." + default = 6800 +} + +variable "kasm_version" { + type = string + description = "Version of KasmVNC to install." + default = "1.3.2" +} + +variable "desktop_environment" { + type = string + description = "Specifies the desktop environment of the workspace. This should be pre-installed on the workspace." + validation { + condition = contains(["xfce", "kde", "gnome", "lxde", "lxqt"], var.desktop_environment) + error_message = "Invalid desktop environment. Please specify a valid desktop environment." + } +} + +resource "coder_script" "kasm_vnc" { + agent_id = var.agent_id + display_name = "KasmVNC" + icon = "/icon/kasmvnc.svg" + script = templatefile("${path.module}/run.sh", { + PORT : var.port, + DESKTOP_ENVIRONMENT : var.desktop_environment, + VERSION : var.kasm_version + }) + run_on_start = true +} + +resource "coder_app" "kasm_vnc" { + agent_id = var.agent_id + slug = "kasm-vnc" + display_name = "kasmVNC" + url = "http://localhost:${var.port}" + icon = "/icon/kasmvnc.svg" + subdomain = true + share = "owner" + healthcheck { + url = "http://localhost:${var.port}/app" + interval = 5 + threshold = 5 + } +} diff --git a/kasmvnc/run.sh b/kasmvnc/run.sh new file mode 100644 index 00000000..0390ea10 --- /dev/null +++ b/kasmvnc/run.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env bash + +#!/bin/bash + +# Function to check if vncserver is already installed +check_installed() { + if command -v vncserver &> /dev/null; then + echo "vncserver is already installed." + return 0 # Don't exit, just indicate it's installed + else + return 1 # Indicates not installed + fi +} + +# Function to install kasmvncserver for debian-based distros +install_deb() { + local url=$1 + wget $url -O /tmp/kasmvncserver.deb + sudo apt-get install --yes --no-install-recommends --no-install-suggests /tmp/kasmvncserver.deb + sudo adduser $USER ssl-cert + rm /tmp/kasmvncserver.deb +} + +# Function to install kasmvncserver for Oracle 8 +install_rpm_oracle8() { + local url=$1 + wget $url -O /tmp/kasmvncserver.rpm + sudo dnf config-manager --set-enabled ol8_codeready_builder + sudo dnf install oracle-epel-release-el8 -y + sudo dnf localinstall /tmp/kasmvncserver.rpm -y + sudo usermod -aG kasmvnc-cert $USER + rm /tmp/kasmvncserver.rpm +} + +# Function to install kasmvncserver for CentOS 7 +install_rpm_centos7() { + local url=$1 + wget $url -O /tmp/kasmvncserver.rpm + sudo yum install epel-release -y + sudo yum install /tmp/kasmvncserver.rpm -y + sudo usermod -aG kasmvnc-cert $USER + rm /tmp/kasmvncserver.rpm +} + +# Function to install kasmvncserver for rpm-based distros +install_rpm() { + local url=$1 + wget $url -O /tmp/kasmvncserver.rpm + sudo rpm -i /tmp/kasmvncserver.rpm + rm /tmp/kasmvncserver.rpm +} + +# Function to install kasmvncserver for Alpine Linux +install_alpine() { + local url=$1 + wget $url -O /tmp/kasmvncserver.tgz + tar -xzf /tmp/kasmvncserver.tgz -C /usr/local/bin/ + rm /tmp/kasmvncserver.tgz +} + +# Detect system information +distro=$(grep "^ID=" /etc/os-release | awk -F= '{print $2}') +version=$(grep "^VERSION_ID=" /etc/os-release | awk -F= '{print $2}' | tr -d '"') +arch=$(uname -m) + +echo "Detected Distribution: $distro" +echo "Detected Version: $version" +echo "Detected Architecture: $arch" + +# Map arch to package arch +if [[ "$arch" == "x86_64" ]]; then + if [[ "$distro" == "ubuntu" || "$distro" == "debian" || "$distro" == "kali" ]]; then + arch="amd64" + else + arch="x86_64" + fi +elif [[ "$arch" == "aarch64" || "$arch" == "arm64" ]]; then + if [[ "$distro" == "ubuntu" || "$distro" == "debian" || "$distro" == "kali" ]]; then + arch="arm64" + else + arch="aarch64" + fi +else + echo "Unsupported architecture: $arch" + exit 1 +fi + +# Check if vncserver is installed, and install if not +if ! check_installed; then + case $distro in + ubuntu | debian | kali) + case $version in + "20.04") + install_deb "https://github.com/kasmtech/KasmVNC/releases/download/v${VERSION}/kasmvncserver_focal_${VERSION}_$${arch}.deb" + ;; + "22.04") + install_deb "https://github.com/kasmtech/KasmVNC/releases/download/v${VERSION}/kasmvncserver_jammy_${VERSION}_$${arch}.deb" + ;; + "24.04") + install_deb "https://github.com/kasmtech/KasmVNC/releases/download/v${VERSION}/kasmvncserver_noble_${VERSION}_$${arch}.deb" + ;; + *) + echo "Unsupported Ubuntu/Debian/Kali version: $${version}" + exit 1 + ;; + esac + ;; + oracle) + if [[ "$version" == "8" ]]; then + install_rpm_oracle8 "https://github.com/kasmtech/KasmVNC/releases/download/v${VERSION}/kasmvncserver_oracle_8_${VERSION}_$${arch}.rpm" + else + echo "Unsupported Oracle version: $${version}" + exit 1 + fi + ;; + centos) + if [[ "$version" == "7" ]]; then + install_rpm_centos7 "https://github.com/kasmtech/KasmVNC/releases/download/v${VERSION}/kasmvncserver_centos_core_${VERSION}_$${arch}.rpm" + else + install_rpm "https://github.com/kasmtech/KasmVNC/releases/download/v${VERSION}/kasmvncserver_centos_core_${VERSION}_$${arch}.rpm" + fi + ;; + alpine) + if [[ "$version" == "3.17" || "$version" == "3.18" || "$version" == "3.19" || "$version" == "3.20" ]]; then + install_alpine "https://github.com/kasmtech/KasmVNC/releases/download/v${VERSION}/kasmvnc.alpine_$${version}_$${arch}.tgz" + else + echo "Unsupported Alpine version: $${version}" + exit 1 + fi + ;; + fedora | opensuse) + install_rpm "https://github.com/kasmtech/KasmVNC/releases/download/v${VERSION}/kasmvncserver_$${distro}_$${version}_${VERSION}_$${arch}.rpm" + ;; + *) + echo "Unsupported distribution: $${distro}" + exit 1 + ;; + esac +else + echo "vncserver already installed. Skipping installation." +fi + +# Coder port-forwarding from dashboard only supports HTTP +sudo bash -c "cat > /etc/kasmvnc/kasmvnc.yaml < /tmp/kasmvncserver.log 2>&1 &