mirror of
https://github.com/Mo3he/Axis_Cam_Tailscale.git
synced 2026-10-01 03:55:40 +00:00
Added custom version
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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 .
|
||||
@@ -0,0 +1,5 @@
|
||||
To build, from main directory
|
||||
|
||||
docker build --tag aarch64 .
|
||||
|
||||
docker cp $(docker create aarch64):/opt/app ./build
|
||||
@@ -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.
|
||||
@@ -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*
|
||||
@@ -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 <axsdk/axparameter.h>
|
||||
#include <glib-unix.h>
|
||||
#include <stdbool.h>
|
||||
#include <syslog.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<head>
|
||||
<h1 style="color:red;">Scroll to the bottom to find authentication URL</h1>
|
||||
<script>
|
||||
function refreshIFrame() {
|
||||
var x = document.getElementById("*Your_iframe_id*");
|
||||
x.contentWindow.location.reload();
|
||||
var t = setTimeout(refreshIFrame, 5000);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="refreshIFrame()">
|
||||
<iframe id="*Your_iframe_id*" src= "/axis-cgi/admin/systemlog.cgi?appname=serverconfig" style="height:600px;width:100%;border: none;" ></iframe>
|
||||
</body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -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
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user