diff --git a/.env.sample b/.env.sample index a7b4525..37581d1 100644 --- a/.env.sample +++ b/.env.sample @@ -4,6 +4,7 @@ COLOR="blue-grey" HS_SERVER=http://localhost:8080 KEY="GenerateYourOwnRandomKey" SCRIPT_NAME=/admin +DATA_DIRECTORY=/data DOMAIN_NAME=http://localhost:8080 AUTH_TYPE="Basic" LOG_LEVEL="Debug" diff --git a/SETUP.md b/SETUP.md index 24bc876..d57a127 100644 --- a/SETUP.md +++ b/SETUP.md @@ -37,6 +37,7 @@ That's it. Cheers. * `COLOR` Set this to your preferred color scheme. See the [MaterializeCSS docs](https://materializecss.github.io/materialize/color.html#palette) for examples. Only set the "base" color -- ie, instead of `blue-gray darken-1`, just use `blue-gray`. * `HS_SERVER` is the URL for your Headscale control server. * `SCRIPT_NAME` is your "Base Path" for hosting. For example, if you want to host on http://localhost/admin, set this to `/admin`, otherwise remove this variable entirely. + * `DATA_DIRECTORY` is your "Data Path". This is there the application will create and store data. Only applicable for bare metal installations. * `KEY` is your encryption key. Set this to a random value generated from `openssl rand -base64 32` * `AUTH_TYPE` can be set to `Basic` or `OIDC`. See the [Authentication](#Authentication) section below for more information. * `LOG_LEVEL` can be one of `Debug`, `Info`, `Warning`, `Error`, or `Critical` for decreasing verbosity. Default is `Info` if removed from your Environment. diff --git a/headscale.py b/headscale.py index d2dcc37..df678cd 100644 --- a/headscale.py +++ b/headscale.py @@ -9,6 +9,7 @@ from dotenv import load_dotenv load_dotenv() LOG_LEVEL = os.environ["LOG_LEVEL"].replace('"', '').upper() +DATA_DIRECTORY = os.environ["DATA_DIRECTORY"].replace('"', '') if os.environ["DATA_DIRECTORY"] else "/data" # Initiate the Flask application and logging: app = Flask(__name__, static_url_path="/static") match LOG_LEVEL: @@ -41,7 +42,7 @@ def set_api_key(api_key): # User-set encryption key encryption_key = os.environ['KEY'] # Key file on the filesystem for persistent storage - key_file = open("/data/key.txt", "wb+") + key_file = open(os.path.join(DATA_DIRECTORY, "key.txt"), "wb+") # Preparing the Fernet class with the key fernet = Fernet(encryption_key) # Encrypting the key @@ -50,11 +51,11 @@ def set_api_key(api_key): return True if key_file.write(encrypted_key) else False def get_api_key(): - if not os.path.exists("/data/key.txt"): return False + if not os.path.exists(os.path.join(DATA_DIRECTORY, "key.txt")): return False # User-set encryption key encryption_key = os.environ['KEY'] # Key file on the filesystem for persistent storage - key_file = open("/data/key.txt", "rb+") + key_file = open(os.path.join(DATA_DIRECTORY, "key.txt"), "rb+") # The encrypted key read from the file enc_api_key = key_file.read() if enc_api_key == b'': return "NULL" diff --git a/helper.py b/helper.py index 100a623..82a4c3c 100644 --- a/helper.py +++ b/helper.py @@ -4,6 +4,7 @@ import os, headscale, requests, logging from flask import Flask LOG_LEVEL = os.environ["LOG_LEVEL"].replace('"', '').upper() +DATA_DIRECTORY = os.environ["DATA_DIRECTORY"].replace('"', '') if os.environ["DATA_DIRECTORY"] else "/data" # Initiate the Flask application and logging: app = Flask(__name__, static_url_path="/static") match LOG_LEVEL: @@ -45,7 +46,7 @@ def text_color_duration(duration): if days > 5: return "deep-orange-text text-lighten-1" if days > 1: return "deep-orange-text text-lighten-1" if hours > 12: return "orange-text " - if hours > 1: return "orange-text text-lighten-2" + if hours > 1: return "orange-text text-lighten-2" if hours == 1: return "yellow-text " if mins > 15: return "yellow-text text-lighten-2" if mins > 5: return "green-text text-lighten-3" @@ -57,11 +58,11 @@ def key_check(): api_key = headscale.get_api_key() url = headscale.get_url() - # Test the API key. If the test fails, return a failure. + # Test the API key. If the test fails, return a failure. # AKA, if headscale returns Unauthorized, fail: app.logger.info("Testing API key validity.") status = headscale.test_api_key(url, api_key) - if status != 200: + if status != 200: app.logger.info("Got a non-200 response from Headscale. Test failed (Response: %i)", status) return False else: @@ -128,7 +129,7 @@ def format_message(error_type, title, message):
Your headscale server is either unreachable or not properly configured. +
Your headscale server is either unreachable or not properly configured. Please ensure your configuration is correct (Check for 200 status on """+url+"""/api/v1 failed. Response: """+str(response.status_code)+""".)
""" @@ -237,58 +238,58 @@ def access_checks(): message_html += format_message("Error", "/etc/headscale/config.yaml not readable", message) if not data_writable: - app.logger.critical("/data folder is not writable") - message = """ -/data is not writable. Please ensure your - permissions are correct. /data mount should be writable + app.logger.critical(f"{DATA_DIRECTORY} folder is not writable") + message = f""" +
{DATA_DIRECTORY} is not writable. Please ensure your + permissions are correct. {DATA_DIRECTORY} mount should be writable by UID/GID 1000:1000.
""" - message_html += format_message("Error", "/data not writable", message) + message_html += format_message("Error", f"{DATA_DIRECTORY} not writable", message) if not data_readable: - app.logger.critical("/data folder is not readable") - message = """ -/data is not readable. Please ensure your - permissions are correct. /data mount should be readable + app.logger.critical(f"{DATA_DIRECTORY} folder is not readable") + message = f""" +
{DATA_DIRECTORY} is not readable. Please ensure your + permissions are correct. {DATA_DIRECTORY} mount should be readable by UID/GID 1000:1000.
""" - message_html += format_message("Error", "/data not readable", message) + message_html += format_message("Error", f"{DATA_DIRECTORY} not readable", message) if not data_executable: - app.logger.critical("/data folder is not readable") - message = """ -/data is not executable. Please ensure your - permissions are correct. /data mount should be readable + app.logger.critical(f"{DATA_DIRECTORY} folder is not readable") + message = f""" +
{DATA_DIRECTORY} is not executable. Please ensure your + permissions are correct. {DATA_DIRECTORY} mount should be readable by UID/GID 1000:1000. (chown 1000:1000 /path/to/data && chmod -R 755 /path/to/data)
""" - message_html += format_message("Error", "/data not executable", message) + message_html += format_message("Error", f"{DATA_DIRECTORY} not executable", message) if file_exists: # If it doesn't exist, we assume the user hasn't created it yet. # Just redirect to the settings page to enter an API Key if not file_writable: - app.logger.critical("/data/key.txt is not writable") - message = """ -/data/key.txt is not writable. Please ensure your - permissions are correct. /data mount should be writable + app.logger.critical(f"{os.path.join(DATA_DIRECTORY, 'key.txt')} is not writable") + message = f""" +
{os.path.join(DATA_DIRECTORY, 'key.txt')} is not writable. Please ensure your + permissions are correct. {DATA_DIRECTORY} mount should be writable by UID/GID 1000:1000.
""" - message_html += format_message("Error", "/data/key.txt not writable", message) + message_html += format_message("Error", f"{os.path.join(DATA_DIRECTORY, 'key.txt')} not writable", message) if not file_readable: - app.logger.critical("/data/key.txt is not readable") - message = """ -/data/key.txt is not readable. Please ensure your - permissions are correct. /data mount should be readable + app.logger.critical(f"{os.path.join(DATA_DIRECTORY, 'key.txt')} is not readable") + message = f""" +
{os.path.join(DATA_DIRECTORY, 'key.txt')} is not readable. Please ensure your + permissions are correct. {DATA_DIRECTORY} mount should be readable by UID/GID 1000:1000.
""" - message_html += format_message("Error", "/data/key.txt not readable", message) + message_html += format_message("Error", f"{os.path.join(DATA_DIRECTORY, 'key.txt')} not readable", message) return message_html diff --git a/server.py b/server.py index fc49a08..85c0b79 100644 --- a/server.py +++ b/server.py @@ -7,9 +7,7 @@ from flask import Flask, escape, Markup, redirect, rende from dateutil import parser from flask_executor import Executor from werkzeug.middleware.proxy_fix import ProxyFix -from dotenv import load_dotenv -load_dotenv() # Global vars # Colors: https://materializecss.com/color.html COLOR = os.environ["COLOR"].replace('"', '').lower()