From 34943f62b998a374e8f0d37e01602b065f9d4416 Mon Sep 17 00:00:00 2001 From: Weston Blieden <111699155+Mo3he@users.noreply.github.com> Date: Wed, 16 Apr 2025 18:35:38 +0200 Subject: [PATCH] Added custom version --- README.md | 6 + aarch64_custom/Dockerfile | 12 ++ aarch64_custom/README.md | 5 + aarch64_custom/app/LICENSE | 29 +++ aarch64_custom/app/Makefile | 29 +++ aarch64_custom/app/config_updater.c | 232 ++++++++++++++++++++++ aarch64_custom/app/html/index.html | 19 ++ aarch64_custom/app/lib/start_tailscale.sh | 64 ++++++ aarch64_custom/app/manifest.json | 29 +++ 9 files changed, 425 insertions(+) create mode 100644 aarch64_custom/Dockerfile create mode 100644 aarch64_custom/README.md create mode 100644 aarch64_custom/app/LICENSE create mode 100644 aarch64_custom/app/Makefile create mode 100644 aarch64_custom/app/config_updater.c create mode 100644 aarch64_custom/app/html/index.html create mode 100644 aarch64_custom/app/lib/start_tailscale.sh create mode 100644 aarch64_custom/app/manifest.json diff --git a/README.md b/README.md index 26bf5c5..9d5ecd2 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,12 @@ https://tailscale.com/changelog/ The "Auto Update" version has been removed since it no longer works given the files are not stored in the default location. +## Testers needed + +I have added a new "custom" version (Tailscale_VPN_Custom_1_82_0_aarch64.eap) which allows you to set a custom server and auth key. +Please give it a try and let me know if it works well or if you have issues. + + ## Good news, everyone! We have found a way to run the Tailscale ACAP without root privileges allowing it to run on Axis OS 12. diff --git a/aarch64_custom/Dockerfile b/aarch64_custom/Dockerfile new file mode 100644 index 0000000..517d44e --- /dev/null +++ b/aarch64_custom/Dockerfile @@ -0,0 +1,12 @@ +ARG ARCH=aarch64 +ARG VERSION=12.3.0 +ARG UBUNTU_VERSION=24.04 +ARG REPO=axisecp +ARG SDK=acap-native-sdk + +FROM ${REPO}/${SDK}:${VERSION}-${ARCH}-ubuntu${UBUNTU_VERSION} + +# Building the ACAP application +COPY ./app /opt/app/ +WORKDIR /opt/app +RUN . /opt/axis/acapsdk/environment-setup* && acap-build . \ No newline at end of file diff --git a/aarch64_custom/README.md b/aarch64_custom/README.md new file mode 100644 index 0000000..6e0fc02 --- /dev/null +++ b/aarch64_custom/README.md @@ -0,0 +1,5 @@ +To build, from main directory + +docker build --tag aarch64 . + +docker cp $(docker create aarch64):/opt/app ./build \ No newline at end of file diff --git a/aarch64_custom/app/LICENSE b/aarch64_custom/app/LICENSE new file mode 100644 index 0000000..9241b06 --- /dev/null +++ b/aarch64_custom/app/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2020 Tailscale & AUTHORS. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/aarch64_custom/app/Makefile b/aarch64_custom/app/Makefile new file mode 100644 index 0000000..393a400 --- /dev/null +++ b/aarch64_custom/app/Makefile @@ -0,0 +1,29 @@ +PROGS = serverconfig +SRCS = config_updater.c +OBJS = $(SRCS:.c=.o) + +PKGS = glib-2.0 gio-2.0 axparameter + +CFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) pkg-config --cflags $(PKGS)) +LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) pkg-config --libs $(PKGS)) + +CFLAGS += -Wall \ + -Wextra \ + -Wformat=2 \ + -Wpointer-arith \ + -Wbad-function-cast \ + -Wstrict-prototypes \ + -Wmissing-prototypes \ + -Winline \ + -Wdisabled-optimization \ + -Wfloat-equal \ + -W \ + -Werror + +all: $(PROGS) + +$(PROGS): $(OBJS) + $(CC) $(LDFLAGS) $^ $(LIBS) $(LDLIBS) -o $@ + +clean: + rm -f $(PROGS) *.o *.eap* *_LICENSE.txt package.conf* param.conf tmp* \ No newline at end of file diff --git a/aarch64_custom/app/config_updater.c b/aarch64_custom/app/config_updater.c new file mode 100644 index 0000000..e2401f4 --- /dev/null +++ b/aarch64_custom/app/config_updater.c @@ -0,0 +1,232 @@ +/** + * Simple file-based configuration updater for Tailscale + * This avoids the AXParameter system and just writes directly to a config file + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define APP_NAME "serverconfig" +#define CONFIG_FILE "/usr/local/packages/serverconfig/config.txt" +#define SCRIPT_PATH "/usr/local/packages/serverconfig/start_tailscale.sh" +#define SCRIPT_SOURCE "/usr/local/packages/serverconfig/lib/start_tailscale.sh" + +static gboolean signal_handler(gpointer loop) { + g_main_loop_quit((GMainLoop*)loop); + syslog(LOG_INFO, "Configuration updater stopping."); + return G_SOURCE_REMOVE; +} + +// Copy script from lib folder to main directory +static void copy_script_file(void) { + char buffer[4096]; + ssize_t bytes_read, bytes_written; + int source_fd, dest_fd; + + syslog(LOG_INFO, "Copying script from %s to %s", SCRIPT_SOURCE, SCRIPT_PATH); + + // Open source file + source_fd = open(SCRIPT_SOURCE, O_RDONLY); + if (source_fd < 0) { + syslog(LOG_ERR, "Failed to open source script: %s", strerror(errno)); + return; + } + + // Open destination file (create if doesn't exist, truncate if exists) + dest_fd = open(SCRIPT_PATH, O_WRONLY | O_CREAT | O_TRUNC, 0755); + if (dest_fd < 0) { + syslog(LOG_ERR, "Failed to open destination script: %s", strerror(errno)); + close(source_fd); + return; + } + + // Copy the file + while ((bytes_read = read(source_fd, buffer, sizeof(buffer))) > 0) { + bytes_written = write(dest_fd, buffer, bytes_read); + if (bytes_written != bytes_read) { + syslog(LOG_ERR, "Error writing to destination file: %s", strerror(errno)); + close(source_fd); + close(dest_fd); + return; + } + } + + // Close file descriptors + close(source_fd); + close(dest_fd); + + // Make the script executable + if (chmod(SCRIPT_PATH, 0755) != 0) { + syslog(LOG_ERR, "Failed to make script executable: %s", strerror(errno)); + return; + } + + syslog(LOG_INFO, "Script copied and made executable successfully"); +} + +// Execute the Tailscale script +static void start_tailscale(void) { + syslog(LOG_INFO, "Starting Tailscale VPN script"); + + // Check if script exists, if not, copy it + struct stat st; + if (stat(SCRIPT_PATH, &st) != 0) { + syslog(LOG_INFO, "Script not found at %s, copying from lib folder", SCRIPT_PATH); + copy_script_file(); + } + + // Fork and execute the script + pid_t pid = fork(); + if (pid < 0) { + syslog(LOG_ERR, "Failed to fork for Tailscale script: %s", strerror(errno)); + return; + } else if (pid == 0) { + // Child process - execute the script + execl(SCRIPT_PATH, "start_tailscale.sh", NULL); + + // If we get here, execl failed + syslog(LOG_ERR, "Failed to execute Tailscale script: %s", strerror(errno)); + _exit(1); + } + + syslog(LOG_INFO, "Tailscale script started with PID: %d", pid); +} + +// Update the configuration file with current parameter values +static void update_config_file(AXParameter* handle) { + GError* error = NULL; + gchar* server_value = NULL; + gchar* key_value = NULL; + FILE* file; + + // Get parameter values + if (!ax_parameter_get(handle, "CustomServer", &server_value, &error)) { + syslog(LOG_ERR, "Failed to get CustomServer: %s", + error ? error->message : "unknown error"); + if (error) g_error_free(error); + error = NULL; + server_value = g_strdup(""); + } + + if (!ax_parameter_get(handle, "AuthKey", &key_value, &error)) { + syslog(LOG_ERR, "Failed to get AuthKey: %s", + error ? error->message : "unknown error"); + if (error) g_error_free(error); + key_value = g_strdup(""); + } + + // Write to config file + file = fopen(CONFIG_FILE, "w"); + if (file) { + fprintf(file, "custom_server=%s\n", server_value ? server_value : ""); + fprintf(file, "auth_key=%s\n", key_value ? key_value : ""); + fclose(file); + + // Set permissions to ensure the file is readable + chmod(CONFIG_FILE, 0644); + + syslog(LOG_INFO, "Updated configuration file: custom_server=%s", + server_value ? server_value : ""); + syslog(LOG_INFO, "Updated configuration file: auth_key=%s", + key_value && strlen(key_value) > 0 ? "(set)" : "(empty)"); + } else { + syslog(LOG_ERR, "Failed to open config file for writing"); + } + + // Clean up + g_free(server_value); + g_free(key_value); +} + +// Handle parameter changes +static void parameter_changed(const gchar* name, const gchar* value, gpointer handle_void_ptr) { + AXParameter* handle = handle_void_ptr; + + // Extract simple parameter name from the fully qualified name + const char* simple_name = name; + const char* prefix = "root." APP_NAME "."; + if (strncmp(name, prefix, strlen(prefix)) == 0) { + simple_name = name + strlen(prefix); + } + + syslog(LOG_INFO, "Parameter changed: %s = %s", simple_name, value); + + // Update config file whenever any parameter changes + update_config_file(handle); + + // Restart Tailscale to apply the new settings + start_tailscale(); +} + +int main(void) { + GError* error = NULL; + GMainLoop* loop = NULL; + + // Open syslog for logging + openlog(APP_NAME, LOG_PID, LOG_USER); + syslog(LOG_INFO, "Config updater starting"); + + // Initialize parameter handling + AXParameter* handle = ax_parameter_new(APP_NAME, &error); + if (handle == NULL) { + syslog(LOG_ERR, "Failed to initialize parameters: %s", + error ? error->message : "unknown error"); + if (error) g_error_free(error); + exit(1); + } + + // Ensure script is copied from lib folder + copy_script_file(); + + // Create initial config file + update_config_file(handle); + + // Start Tailscale VPN script + start_tailscale(); + + // Register for parameter changes + if (!ax_parameter_register_callback(handle, "CustomServer", parameter_changed, handle, &error)) { + syslog(LOG_ERR, "Failed to register CustomServer callback: %s", + error ? error->message : "unknown error"); + if (error) g_error_free(error); + error = NULL; + } + + if (!ax_parameter_register_callback(handle, "AuthKey", parameter_changed, handle, &error)) { + syslog(LOG_ERR, "Failed to register AuthKey callback: %s", + error ? error->message : "unknown error"); + if (error) g_error_free(error); + } + + // Register for parameter changes with fully qualified names as fallback + if (!ax_parameter_register_callback(handle, "root." APP_NAME ".CustomServer", parameter_changed, handle, NULL)) { + syslog(LOG_INFO, "Fallback CustomServer registration failed (this may be normal)"); + } + if (!ax_parameter_register_callback(handle, "root." APP_NAME ".AuthKey", parameter_changed, handle, NULL)) { + syslog(LOG_INFO, "Fallback AuthKey registration failed (this may be normal)"); + } + + // Set up main loop + loop = g_main_loop_new(NULL, FALSE); + g_unix_signal_add(SIGTERM, signal_handler, loop); + g_unix_signal_add(SIGINT, signal_handler, loop); + + syslog(LOG_INFO, "Config updater running. Waiting for parameter changes..."); + g_main_loop_run(loop); + + // Clean up + g_main_loop_unref(loop); + ax_parameter_free(handle); + + return 0; +} \ No newline at end of file diff --git a/aarch64_custom/app/html/index.html b/aarch64_custom/app/html/index.html new file mode 100644 index 0000000..6672898 --- /dev/null +++ b/aarch64_custom/app/html/index.html @@ -0,0 +1,19 @@ + + + + +

Scroll to the bottom to find authentication URL

+ + + + + + + + \ No newline at end of file diff --git a/aarch64_custom/app/lib/start_tailscale.sh b/aarch64_custom/app/lib/start_tailscale.sh new file mode 100644 index 0000000..d48257d --- /dev/null +++ b/aarch64_custom/app/lib/start_tailscale.sh @@ -0,0 +1,64 @@ +#!/bin/sh +# Make sure this script terminates any existing Tailscale processes before starting new ones + +# Kill any existing tailscaled processes +pkill -f tailscaled || true + +# Simple script to start Tailscale with custom configuration +CONFIG_FILE="/usr/local/packages/serverconfig/config.txt" +TAILSCALED_PATH="/usr/local/packages/serverconfig/lib/tailscaled" +TAILSCALE_PATH="/usr/local/packages/serverconfig/lib/tailscale" +SOCKET_PATH="/usr/local/packages/serverconfig/tailscaled.sock" + +# Log to syslog +logger -t "tailscale_script" "Starting Tailscale VPN service" + +# Set execute permissions +chmod 755 $TAILSCALED_PATH +chmod 755 $TAILSCALE_PATH + +# Read configuration (if exists) +CUSTOM_SERVER="" +AUTH_KEY="" +if [ -f "$CONFIG_FILE" ]; then + logger -t "tailscale_script" "Reading configuration from $CONFIG_FILE" + # Read values from config file + while IFS='=' read -r key value; do + case "$key" in + "custom_server") CUSTOM_SERVER="$value" ;; + "auth_key") AUTH_KEY="$value" ;; + esac + done < "$CONFIG_FILE" +fi + +# Start tailscaled +logger -t "tailscale_script" "Starting tailscaled daemon" +$TAILSCALED_PATH --tun=userspace-networking --socket=$SOCKET_PATH & +TAILSCALED_PID=$! + +# Wait for tailscaled to initialize +sleep 2 + +# Build up the command based on available parameters +TAILSCALE_CMD="$TAILSCALE_PATH --socket=$SOCKET_PATH up" + +if [ -n "$CUSTOM_SERVER" ]; then + logger -t "tailscale_script" "Using custom server: $CUSTOM_SERVER" + TAILSCALE_CMD="$TAILSCALE_CMD --login-server $CUSTOM_SERVER" +fi + +if [ -n "$AUTH_KEY" ]; then + logger -t "tailscale_script" "Using authentication key" + TAILSCALE_CMD="$TAILSCALE_CMD --authkey $AUTH_KEY" +fi + +# Connect to Tailscale network +logger -t "tailscale_script" "Running: $TAILSCALE_CMD" +eval $TAILSCALE_CMD + +# Keep the script running to maintain the tailscaled process +logger -t "tailscale_script" "Tailscale VPN is running" +logger -t "tailscale_script" "To change settings, modify parameters in ACAP web interface" + +# Wait for tailscaled process to exit +wait $TAILSCALED_PID \ No newline at end of file diff --git a/aarch64_custom/app/manifest.json b/aarch64_custom/app/manifest.json new file mode 100644 index 0000000..1118d20 --- /dev/null +++ b/aarch64_custom/app/manifest.json @@ -0,0 +1,29 @@ +{ + "schemaVersion": "1.7.3", + "acapPackageConf": { + "setup": { + "friendlyName": "Tailscale VPN", + "appName": "serverconfig", + "vendor": "Tailscale - Packaged by Mo3he", + "embeddedSdkVersion": "3.0", + "vendorUrl": "https://www.tailscale.com", + "runMode": "respawn", + "version": "1.82.0" + }, + "configuration": { + "settingPage": "index.html", + "paramConfig": [ + { + "name": "CustomServer", + "default": "", + "type": "string" + }, + { + "name": "AuthKey", + "default": "", + "type": "string" + } + ] + } + } +} \ No newline at end of file