2023-04-03 12:11:45 +00:00
|
|
|
import json
|
|
|
|
|
import logging
|
|
|
|
|
import os
|
|
|
|
|
from datetime import date, timedelta
|
2023-02-20 17:23:05 +09:00
|
|
|
|
2023-04-03 12:11:45 +00:00
|
|
|
import requests
|
|
|
|
|
import yaml
|
2023-02-06 04:58:09 +00:00
|
|
|
from cryptography.fernet import Fernet
|
2023-04-03 12:11:45 +00:00
|
|
|
from dateutil import parser
|
|
|
|
|
from flask import Flask
|
2023-02-17 22:41:41 +09:00
|
|
|
|
2023-04-03 12:11:45 +00:00
|
|
|
LOG_LEVEL = os.environ["LOG_LEVEL"].replace('"', "").upper()
|
2023-02-28 14:24:46 +09:00
|
|
|
# Initiate the Flask application and logging:
|
2023-02-27 22:21:51 +09:00
|
|
|
app = Flask(__name__, static_url_path="/static")
|
2023-02-28 14:24:46 +09:00
|
|
|
match LOG_LEVEL:
|
2023-04-03 12:11:45 +00:00
|
|
|
case "DEBUG":
|
|
|
|
|
app.logger.setLevel(logging.DEBUG)
|
|
|
|
|
case "INFO":
|
|
|
|
|
app.logger.setLevel(logging.INFO)
|
|
|
|
|
case "WARNING":
|
|
|
|
|
app.logger.setLevel(logging.WARNING)
|
|
|
|
|
case "ERROR":
|
|
|
|
|
app.logger.setLevel(logging.ERROR)
|
|
|
|
|
case "CRITICAL":
|
|
|
|
|
app.logger.setLevel(logging.CRITICAL)
|
|
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
|
|
|
|
|
##################################################################
|
|
|
|
|
# Functions related to HEADSCALE and API KEYS
|
|
|
|
|
##################################################################
|
2023-03-21 14:23:02 +09:00
|
|
|
def get_url(inpage=False):
|
2023-04-03 12:11:45 +00:00
|
|
|
if not inpage:
|
|
|
|
|
return os.environ["HS_SERVER"]
|
2023-03-21 14:14:33 +09:00
|
|
|
config_file = ""
|
|
|
|
|
try:
|
2023-04-03 12:11:45 +00:00
|
|
|
config_file = open("/etc/headscale/config.yml", "r")
|
2023-03-21 14:14:33 +09:00
|
|
|
app.logger.info("Opening /etc/headscale/config.yml")
|
2023-04-03 12:11:45 +00:00
|
|
|
except:
|
2023-03-21 14:14:33 +09:00
|
|
|
config_file = open("/etc/headscale/config.yaml", "r")
|
|
|
|
|
app.logger.info("Opening /etc/headscale/config.yaml")
|
|
|
|
|
config_yaml = yaml.safe_load(config_file)
|
2023-04-03 12:11:45 +00:00
|
|
|
if "server_url" in config_yaml:
|
2023-03-21 14:14:33 +09:00
|
|
|
return str(config_yaml["server_url"])
|
2023-04-03 12:11:45 +00:00
|
|
|
app.logger.warning(
|
|
|
|
|
"Failed to find server_url in the config. Falling back to ENV variable"
|
|
|
|
|
)
|
|
|
|
|
return os.environ["HS_SERVER"]
|
|
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
|
|
|
|
|
def set_api_key(api_key):
|
2023-02-20 17:23:05 +09:00
|
|
|
# User-set encryption key
|
2023-04-03 12:11:45 +00:00
|
|
|
encryption_key = os.environ["KEY"]
|
2023-02-20 17:23:05 +09:00
|
|
|
# Key file on the filesystem for persistent storage
|
2023-04-03 12:11:45 +00:00
|
|
|
key_file = open("/data/key.txt", "wb+")
|
2023-02-20 17:23:05 +09:00
|
|
|
# Preparing the Fernet class with the key
|
2023-04-03 12:11:45 +00:00
|
|
|
fernet = Fernet(encryption_key)
|
2023-02-20 17:23:05 +09:00
|
|
|
# Encrypting the key
|
2023-04-03 12:11:45 +00:00
|
|
|
encrypted_key = fernet.encrypt(api_key.encode())
|
2023-02-20 17:23:05 +09:00
|
|
|
# Return true if the file wrote correctly
|
2023-04-03 12:11:45 +00:00
|
|
|
return True if key_file.write(encrypted_key) else False
|
|
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
|
|
|
|
|
def get_api_key():
|
2023-04-03 12:11:45 +00:00
|
|
|
if not os.path.exists("/data/key.txt"):
|
|
|
|
|
return False
|
2023-02-20 17:23:05 +09:00
|
|
|
# User-set encryption key
|
2023-04-03 12:11:45 +00:00
|
|
|
encryption_key = os.environ["KEY"]
|
2023-02-20 17:23:05 +09:00
|
|
|
# Key file on the filesystem for persistent storage
|
2023-04-03 12:11:45 +00:00
|
|
|
key_file = open("/data/key.txt", "rb+")
|
2023-02-20 17:23:05 +09:00
|
|
|
# The encrypted key read from the file
|
2023-04-03 12:11:45 +00:00
|
|
|
enc_api_key = key_file.read()
|
|
|
|
|
if enc_api_key == b"":
|
|
|
|
|
return "NULL"
|
2023-02-06 04:58:09 +00:00
|
|
|
|
2023-02-27 20:15:44 +09:00
|
|
|
# Preparing the Fernet class with the key
|
2023-04-03 12:11:45 +00:00
|
|
|
fernet = Fernet(encryption_key)
|
2023-02-20 17:23:05 +09:00
|
|
|
# Decrypting the key
|
2023-04-03 12:11:45 +00:00
|
|
|
decrypted_key = fernet.decrypt(enc_api_key).decode()
|
2023-02-06 04:58:09 +00:00
|
|
|
|
|
|
|
|
return decrypted_key
|
|
|
|
|
|
2023-04-03 12:11:45 +00:00
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
def test_api_key(url, api_key):
|
|
|
|
|
response = requests.get(
|
2023-04-03 12:11:45 +00:00
|
|
|
str(url) + "/api/v1/apikey",
|
2023-02-06 04:58:09 +00:00
|
|
|
headers={
|
2023-04-03 12:11:45 +00:00
|
|
|
"Accept": "application/json",
|
|
|
|
|
"Authorization": "Bearer " + str(api_key),
|
|
|
|
|
},
|
2023-02-06 04:58:09 +00:00
|
|
|
)
|
|
|
|
|
return response.status_code
|
|
|
|
|
|
2023-04-03 12:11:45 +00:00
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
# Expires an API key
|
|
|
|
|
def expire_key(url, api_key):
|
2023-04-03 12:11:45 +00:00
|
|
|
payload = {"prefix": str(api_key[0:10])}
|
|
|
|
|
json_payload = json.dumps(payload)
|
|
|
|
|
app.logger.debug(
|
|
|
|
|
"Sending the payload '" + str(json_payload) + "' to the headscale server"
|
|
|
|
|
)
|
2023-02-06 04:58:09 +00:00
|
|
|
|
2023-02-20 17:23:05 +09:00
|
|
|
response = requests.post(
|
2023-04-03 12:11:45 +00:00
|
|
|
str(url) + "/api/v1/apikey/expire",
|
2023-02-20 17:23:05 +09:00
|
|
|
data=json_payload,
|
|
|
|
|
headers={
|
2023-04-03 12:11:45 +00:00
|
|
|
"Accept": "application/json",
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
"Authorization": "Bearer " + str(api_key),
|
|
|
|
|
},
|
2023-02-20 17:23:05 +09:00
|
|
|
)
|
|
|
|
|
return response.status_code
|
2023-02-06 04:58:09 +00:00
|
|
|
|
2023-04-03 12:11:45 +00:00
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
# Checks if the key needs to be renewed
|
|
|
|
|
# If it does, renews the key, then expires the old key
|
|
|
|
|
def renew_api_key(url, api_key):
|
|
|
|
|
# 0 = Key has been updated or key is not in need of an update
|
2023-04-03 12:11:45 +00:00
|
|
|
# 1 = Key has failed validity check or has failed to write the API key
|
2023-02-06 04:58:09 +00:00
|
|
|
# Check when the key expires and compare it to todays date:
|
2023-04-03 12:11:45 +00:00
|
|
|
key_info = get_api_key_info(url, api_key)
|
|
|
|
|
expiration_time = key_info["expiration"]
|
|
|
|
|
today_date = date.today()
|
|
|
|
|
expire = parser.parse(expiration_time)
|
|
|
|
|
expire_fmt = (
|
|
|
|
|
str(expire.year)
|
|
|
|
|
+ "-"
|
|
|
|
|
+ str(expire.month).zfill(2)
|
|
|
|
|
+ "-"
|
|
|
|
|
+ str(expire.day).zfill(2)
|
|
|
|
|
)
|
|
|
|
|
expire_date = date.fromisoformat(expire_fmt)
|
|
|
|
|
delta = expire_date - today_date
|
|
|
|
|
tmp = today_date + timedelta(days=90)
|
|
|
|
|
new_expiration_date = str(tmp) + "T00:00:00.000000Z"
|
2023-02-06 04:58:09 +00:00
|
|
|
|
|
|
|
|
# If the delta is less than 5 days, renew the key:
|
|
|
|
|
if delta < timedelta(days=5):
|
2023-04-03 12:11:45 +00:00
|
|
|
app.logger.warning("Key is about to expire. Delta is " + str(delta))
|
|
|
|
|
payload = {"expiration": str(new_expiration_date)}
|
|
|
|
|
json_payload = json.dumps(payload)
|
|
|
|
|
app.logger.debug(
|
|
|
|
|
"Sending the payload '" + str(json_payload) + "' to the headscale server"
|
|
|
|
|
)
|
2023-02-06 04:58:09 +00:00
|
|
|
|
|
|
|
|
response = requests.post(
|
2023-04-03 12:11:45 +00:00
|
|
|
str(url) + "/api/v1/apikey",
|
2023-02-06 04:58:09 +00:00
|
|
|
data=json_payload,
|
|
|
|
|
headers={
|
2023-04-03 12:11:45 +00:00
|
|
|
"Accept": "application/json",
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
"Authorization": "Bearer " + str(api_key),
|
|
|
|
|
},
|
2023-02-06 04:58:09 +00:00
|
|
|
)
|
|
|
|
|
new_key = response.json()
|
2023-04-03 12:11:45 +00:00
|
|
|
app.logger.debug("JSON: " + json.dumps(new_key))
|
|
|
|
|
app.logger.debug("New Key is: " + new_key["apiKey"])
|
2023-02-06 04:58:09 +00:00
|
|
|
api_key_test = test_api_key(url, new_key["apiKey"])
|
2023-04-03 12:11:45 +00:00
|
|
|
app.logger.debug("Testing the key: " + str(api_key_test))
|
2023-02-06 04:58:09 +00:00
|
|
|
# Test if the new key works:
|
|
|
|
|
if api_key_test == 200:
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("The new key is valid and we are writing it to the file")
|
2023-02-06 04:58:09 +00:00
|
|
|
if not set_api_key(new_key["apiKey"]):
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.error("We failed writing the new key!")
|
2023-04-03 12:11:45 +00:00
|
|
|
return False # Key write failed
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("Key validated and written. Moving to expire the key.")
|
2023-02-06 04:58:09 +00:00
|
|
|
expire_key(url, api_key)
|
2023-04-03 12:11:45 +00:00
|
|
|
return True # Key updated and validated
|
|
|
|
|
else:
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.error("Testing the API key failed.")
|
2023-02-27 20:15:44 +09:00
|
|
|
return False # The API Key test failed
|
2023-04-03 12:11:45 +00:00
|
|
|
else:
|
|
|
|
|
return True # No work is required
|
|
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
|
|
|
|
|
# Gets information about the current API key
|
|
|
|
|
def get_api_key_info(url, api_key):
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("Getting API key information")
|
2023-02-06 04:58:09 +00:00
|
|
|
response = requests.get(
|
2023-04-03 12:11:45 +00:00
|
|
|
str(url) + "/api/v1/apikey",
|
2023-02-06 04:58:09 +00:00
|
|
|
headers={
|
2023-04-03 12:11:45 +00:00
|
|
|
"Accept": "application/json",
|
|
|
|
|
"Authorization": "Bearer " + str(api_key),
|
|
|
|
|
},
|
2023-02-06 04:58:09 +00:00
|
|
|
)
|
|
|
|
|
json_response = response.json()
|
2023-04-03 12:11:45 +00:00
|
|
|
# Find the current key in the array:
|
2023-02-06 04:58:09 +00:00
|
|
|
key_prefix = str(api_key[0:10])
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("Looking for valid API Key...")
|
2023-02-06 04:58:09 +00:00
|
|
|
for key in json_response["apiKeys"]:
|
|
|
|
|
if key_prefix == key["prefix"]:
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("Key found.")
|
2023-02-06 04:58:09 +00:00
|
|
|
return key
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.error("Could not find a valid key in Headscale. Need a new API key.")
|
2023-02-06 04:58:09 +00:00
|
|
|
return "Key not found"
|
|
|
|
|
|
2023-04-03 12:11:45 +00:00
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
##################################################################
|
|
|
|
|
# Functions related to MACHINES
|
|
|
|
|
##################################################################
|
|
|
|
|
|
2023-04-03 12:11:45 +00:00
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
# register a new machine
|
|
|
|
|
def register_machine(url, api_key, machine_key, user):
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("Registering machine %s to user %s", str(machine_key), str(user))
|
2023-02-06 04:58:09 +00:00
|
|
|
response = requests.post(
|
2023-04-03 12:11:45 +00:00
|
|
|
str(url)
|
|
|
|
|
+ "/api/v1/machine/register?user="
|
|
|
|
|
+ str(user)
|
|
|
|
|
+ "&key="
|
|
|
|
|
+ str(machine_key),
|
2023-02-06 04:58:09 +00:00
|
|
|
headers={
|
2023-04-03 12:11:45 +00:00
|
|
|
"Accept": "application/json",
|
|
|
|
|
"Authorization": "Bearer " + str(api_key),
|
|
|
|
|
},
|
2023-02-06 04:58:09 +00:00
|
|
|
)
|
|
|
|
|
return response.json()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Sets the machines tags
|
|
|
|
|
def set_machine_tags(url, api_key, machine_id, tags_list):
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("Setting machine_id %s tag %s", str(machine_id), str(tags_list))
|
2023-02-06 04:58:09 +00:00
|
|
|
response = requests.post(
|
2023-04-03 12:11:45 +00:00
|
|
|
str(url) + "/api/v1/machine/" + str(machine_id) + "/tags",
|
2023-02-06 04:58:09 +00:00
|
|
|
data=tags_list,
|
|
|
|
|
headers={
|
2023-04-03 12:11:45 +00:00
|
|
|
"Accept": "application/json",
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
"Authorization": "Bearer " + str(api_key),
|
|
|
|
|
},
|
2023-02-06 04:58:09 +00:00
|
|
|
)
|
|
|
|
|
return response.json()
|
|
|
|
|
|
2023-04-03 12:11:45 +00:00
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
# Moves machine_id to user "new_user"
|
|
|
|
|
def move_user(url, api_key, machine_id, new_user):
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("Moving machine_id %s to user %s", str(machine_id), str(new_user))
|
2023-02-06 04:58:09 +00:00
|
|
|
response = requests.post(
|
2023-04-03 12:11:45 +00:00
|
|
|
str(url) + "/api/v1/machine/" + str(machine_id) + "/user?user=" + str(new_user),
|
2023-02-06 04:58:09 +00:00
|
|
|
headers={
|
2023-04-03 12:11:45 +00:00
|
|
|
"Accept": "application/json",
|
|
|
|
|
"Authorization": "Bearer " + str(api_key),
|
|
|
|
|
},
|
2023-02-06 04:58:09 +00:00
|
|
|
)
|
|
|
|
|
return response.json()
|
|
|
|
|
|
2023-04-03 12:11:45 +00:00
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
def update_route(url, api_key, route_id, current_state):
|
2023-03-29 13:59:34 +09:00
|
|
|
action = "disable" if current_state == "True" else "enable"
|
|
|
|
|
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("Updating Route %s: Action: %s", str(route_id), str(action))
|
2023-02-06 04:58:09 +00:00
|
|
|
|
|
|
|
|
# Debug
|
2023-04-03 12:11:45 +00:00
|
|
|
app.logger.debug("URL: " + str(url))
|
|
|
|
|
app.logger.debug("Route ID: " + str(route_id))
|
|
|
|
|
app.logger.debug("Current State: " + str(current_state))
|
|
|
|
|
app.logger.debug("Action to take: " + str(action))
|
2023-02-06 04:58:09 +00:00
|
|
|
|
|
|
|
|
response = requests.post(
|
2023-04-03 12:11:45 +00:00
|
|
|
str(url) + "/api/v1/routes/" + str(route_id) + "/" + str(action),
|
2023-02-06 04:58:09 +00:00
|
|
|
headers={
|
2023-04-03 12:11:45 +00:00
|
|
|
"Accept": "application/json",
|
|
|
|
|
"Authorization": "Bearer " + str(api_key),
|
|
|
|
|
},
|
2023-02-06 04:58:09 +00:00
|
|
|
)
|
|
|
|
|
return response.json()
|
|
|
|
|
|
2023-04-03 12:11:45 +00:00
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
# Get all machines on the Headscale network
|
|
|
|
|
def get_machines(url, api_key):
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("Getting machine information")
|
2023-02-06 04:58:09 +00:00
|
|
|
response = requests.get(
|
2023-04-03 12:11:45 +00:00
|
|
|
str(url) + "/api/v1/machine",
|
2023-02-06 04:58:09 +00:00
|
|
|
headers={
|
2023-04-03 12:11:45 +00:00
|
|
|
"Accept": "application/json",
|
|
|
|
|
"Authorization": "Bearer " + str(api_key),
|
|
|
|
|
},
|
2023-02-06 04:58:09 +00:00
|
|
|
)
|
|
|
|
|
return response.json()
|
|
|
|
|
|
2023-04-03 12:11:45 +00:00
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
# Get machine with "machine_id" on the Headscale network
|
|
|
|
|
def get_machine_info(url, api_key, machine_id):
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("Getting information for machine ID %s", str(machine_id))
|
2023-02-06 04:58:09 +00:00
|
|
|
response = requests.get(
|
2023-04-03 12:11:45 +00:00
|
|
|
str(url) + "/api/v1/machine/" + str(machine_id),
|
2023-02-06 04:58:09 +00:00
|
|
|
headers={
|
2023-04-03 12:11:45 +00:00
|
|
|
"Accept": "application/json",
|
|
|
|
|
"Authorization": "Bearer " + str(api_key),
|
|
|
|
|
},
|
2023-02-06 04:58:09 +00:00
|
|
|
)
|
|
|
|
|
return response.json()
|
|
|
|
|
|
2023-04-03 12:11:45 +00:00
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
# Delete a machine from Headscale
|
|
|
|
|
def delete_machine(url, api_key, machine_id):
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("Deleting machine %s", str(machine_id))
|
2023-02-06 04:58:09 +00:00
|
|
|
response = requests.delete(
|
2023-04-03 12:11:45 +00:00
|
|
|
str(url) + "/api/v1/machine/" + str(machine_id),
|
2023-02-06 04:58:09 +00:00
|
|
|
headers={
|
2023-04-03 12:11:45 +00:00
|
|
|
"Accept": "application/json",
|
|
|
|
|
"Authorization": "Bearer " + str(api_key),
|
|
|
|
|
},
|
2023-02-06 04:58:09 +00:00
|
|
|
)
|
|
|
|
|
status = "True" if response.status_code == 200 else "False"
|
2023-02-27 20:15:44 +09:00
|
|
|
if response.status_code == 200:
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("Machine deleted.")
|
2023-02-27 20:15:44 +09:00
|
|
|
else:
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.error("Deleting machine failed! %s", str(response.json()))
|
2023-02-06 04:58:09 +00:00
|
|
|
return {"status": status, "body": response.json()}
|
|
|
|
|
|
2023-04-03 12:11:45 +00:00
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
# Rename "machine_id" with name "new_name"
|
|
|
|
|
def rename_machine(url, api_key, machine_id, new_name):
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("Renaming machine %s", str(machine_id))
|
2023-02-06 04:58:09 +00:00
|
|
|
response = requests.post(
|
2023-04-03 12:11:45 +00:00
|
|
|
str(url) + "/api/v1/machine/" + str(machine_id) + "/rename/" + str(new_name),
|
2023-02-06 04:58:09 +00:00
|
|
|
headers={
|
2023-04-03 12:11:45 +00:00
|
|
|
"Accept": "application/json",
|
|
|
|
|
"Authorization": "Bearer " + str(api_key),
|
|
|
|
|
},
|
2023-02-06 04:58:09 +00:00
|
|
|
)
|
|
|
|
|
status = "True" if response.status_code == 200 else "False"
|
2023-02-27 20:15:44 +09:00
|
|
|
if response.status_code == 200:
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("Machine renamed")
|
2023-02-27 20:15:44 +09:00
|
|
|
else:
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.error("Machine rename failed! %s", str(response.json()))
|
2023-02-06 04:58:09 +00:00
|
|
|
return {"status": status, "body": response.json()}
|
|
|
|
|
|
2023-04-03 12:11:45 +00:00
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
# Gets routes for the passed machine_id
|
|
|
|
|
def get_machine_routes(url, api_key, machine_id):
|
2023-02-27 22:57:38 +09:00
|
|
|
app.logger.info("Getting routes for machine %s", str(machine_id))
|
2023-02-06 04:58:09 +00:00
|
|
|
response = requests.get(
|
2023-04-03 12:11:45 +00:00
|
|
|
str(url) + "/api/v1/machine/" + str(machine_id) + "/routes",
|
2023-02-06 04:58:09 +00:00
|
|
|
headers={
|
2023-04-03 12:11:45 +00:00
|
|
|
"Accept": "application/json",
|
|
|
|
|
"Authorization": "Bearer " + str(api_key),
|
|
|
|
|
},
|
2023-02-06 04:58:09 +00:00
|
|
|
)
|
2023-02-27 20:15:44 +09:00
|
|
|
if response.status_code == 200:
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("Routes obtained")
|
2023-02-27 20:15:44 +09:00
|
|
|
else:
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.error("Failed to get routes: %s", str(response.json()))
|
2023-02-06 04:58:09 +00:00
|
|
|
return response.json()
|
2023-02-27 20:15:44 +09:00
|
|
|
|
2023-04-03 12:11:45 +00:00
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
# Gets routes for the entire tailnet
|
|
|
|
|
def get_routes(url, api_key):
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("Getting routes")
|
2023-02-06 04:58:09 +00:00
|
|
|
response = requests.get(
|
2023-04-03 12:11:45 +00:00
|
|
|
str(url) + "/api/v1/routes",
|
2023-02-06 04:58:09 +00:00
|
|
|
headers={
|
2023-04-03 12:11:45 +00:00
|
|
|
"Accept": "application/json",
|
|
|
|
|
"Authorization": "Bearer " + str(api_key),
|
|
|
|
|
},
|
2023-02-06 04:58:09 +00:00
|
|
|
)
|
|
|
|
|
return response.json()
|
2023-04-03 12:11:45 +00:00
|
|
|
|
|
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
##################################################################
|
2023-03-29 13:59:34 +09:00
|
|
|
# Functions related to USERS
|
2023-02-06 04:58:09 +00:00
|
|
|
##################################################################
|
|
|
|
|
|
2023-04-03 12:11:45 +00:00
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
# Get all users in use
|
|
|
|
|
def get_users(url, api_key):
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("Getting Users")
|
2023-02-06 04:58:09 +00:00
|
|
|
response = requests.get(
|
2023-04-03 12:11:45 +00:00
|
|
|
str(url) + "/api/v1/user",
|
2023-02-06 04:58:09 +00:00
|
|
|
headers={
|
2023-04-03 12:11:45 +00:00
|
|
|
"Accept": "application/json",
|
|
|
|
|
"Authorization": "Bearer " + str(api_key),
|
|
|
|
|
},
|
2023-02-06 04:58:09 +00:00
|
|
|
)
|
|
|
|
|
return response.json()
|
|
|
|
|
|
2023-04-03 12:11:45 +00:00
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
# Rename "old_name" with name "new_name"
|
|
|
|
|
def rename_user(url, api_key, old_name, new_name):
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("Renaming user %s to %s.", str(old_name), str(new_name))
|
2023-02-06 04:58:09 +00:00
|
|
|
response = requests.post(
|
2023-04-03 12:11:45 +00:00
|
|
|
str(url) + "/api/v1/user/" + str(old_name) + "/rename/" + str(new_name),
|
2023-02-06 04:58:09 +00:00
|
|
|
headers={
|
2023-04-03 12:11:45 +00:00
|
|
|
"Accept": "application/json",
|
|
|
|
|
"Authorization": "Bearer " + str(api_key),
|
|
|
|
|
},
|
2023-02-06 04:58:09 +00:00
|
|
|
)
|
|
|
|
|
status = "True" if response.status_code == 200 else "False"
|
2023-02-27 20:15:44 +09:00
|
|
|
if response.status_code == 200:
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("User renamed.")
|
2023-02-27 20:15:44 +09:00
|
|
|
else:
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.error("Renaming User failed!")
|
2023-02-06 04:58:09 +00:00
|
|
|
return {"status": status, "body": response.json()}
|
|
|
|
|
|
2023-04-03 12:11:45 +00:00
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
# Delete a user from Headscale
|
|
|
|
|
def delete_user(url, api_key, user_name):
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("Deleting a User: %s", str(user_name))
|
2023-02-06 04:58:09 +00:00
|
|
|
response = requests.delete(
|
2023-04-03 12:11:45 +00:00
|
|
|
str(url) + "/api/v1/user/" + str(user_name),
|
2023-02-06 04:58:09 +00:00
|
|
|
headers={
|
2023-04-03 12:11:45 +00:00
|
|
|
"Accept": "application/json",
|
|
|
|
|
"Authorization": "Bearer " + str(api_key),
|
|
|
|
|
},
|
2023-02-06 04:58:09 +00:00
|
|
|
)
|
|
|
|
|
status = "True" if response.status_code == 200 else "False"
|
2023-02-27 20:15:44 +09:00
|
|
|
if response.status_code == 200:
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("User deleted.")
|
2023-02-27 20:15:44 +09:00
|
|
|
else:
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.error("Deleting User failed!")
|
2023-02-06 04:58:09 +00:00
|
|
|
return {"status": status, "body": response.json()}
|
|
|
|
|
|
2023-04-03 12:11:45 +00:00
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
# Add a user from Headscale
|
|
|
|
|
def add_user(url, api_key, data):
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("Adding user: %s", str(data))
|
2023-02-06 04:58:09 +00:00
|
|
|
response = requests.post(
|
2023-04-03 12:11:45 +00:00
|
|
|
str(url) + "/api/v1/user",
|
2023-02-06 04:58:09 +00:00
|
|
|
data=data,
|
|
|
|
|
headers={
|
2023-04-03 12:11:45 +00:00
|
|
|
"Accept": "application/json",
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
"Authorization": "Bearer " + str(api_key),
|
|
|
|
|
},
|
2023-02-06 04:58:09 +00:00
|
|
|
)
|
|
|
|
|
status = "True" if response.status_code == 200 else "False"
|
2023-02-27 20:15:44 +09:00
|
|
|
if response.status_code == 200:
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("User added.")
|
2023-02-27 20:15:44 +09:00
|
|
|
else:
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.error("Adding User failed!")
|
2023-02-06 04:58:09 +00:00
|
|
|
return {"status": status, "body": response.json()}
|
|
|
|
|
|
2023-04-03 12:11:45 +00:00
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
##################################################################
|
2023-03-29 13:59:34 +09:00
|
|
|
# Functions related to PREAUTH KEYS in USERS
|
2023-02-06 04:58:09 +00:00
|
|
|
##################################################################
|
|
|
|
|
|
2023-04-03 12:11:45 +00:00
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
# Get all PreAuth keys associated with a user "user_name"
|
|
|
|
|
def get_preauth_keys(url, api_key, user_name):
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("Getting PreAuth Keys in User %s", str(user_name))
|
2023-02-06 04:58:09 +00:00
|
|
|
response = requests.get(
|
2023-04-03 12:11:45 +00:00
|
|
|
str(url) + "/api/v1/preauthkey?user=" + str(user_name),
|
2023-02-06 04:58:09 +00:00
|
|
|
headers={
|
2023-04-03 12:11:45 +00:00
|
|
|
"Accept": "application/json",
|
|
|
|
|
"Authorization": "Bearer " + str(api_key),
|
|
|
|
|
},
|
2023-02-06 04:58:09 +00:00
|
|
|
)
|
|
|
|
|
return response.json()
|
|
|
|
|
|
2023-04-03 12:11:45 +00:00
|
|
|
|
|
|
|
|
# Add a preauth key to the user "user_name" given the booleans "ephemeral"
|
2023-02-20 17:23:05 +09:00
|
|
|
# and "reusable" with the expiration date "date" contained in the JSON payload "data"
|
2023-02-06 04:58:09 +00:00
|
|
|
def add_preauth_key(url, api_key, data):
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("Adding PreAuth Key: %s", str(data))
|
2023-02-06 04:58:09 +00:00
|
|
|
response = requests.post(
|
2023-04-03 12:11:45 +00:00
|
|
|
str(url) + "/api/v1/preauthkey",
|
2023-02-06 04:58:09 +00:00
|
|
|
data=data,
|
|
|
|
|
headers={
|
2023-04-03 12:11:45 +00:00
|
|
|
"Accept": "application/json",
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
"Authorization": "Bearer " + str(api_key),
|
|
|
|
|
},
|
2023-02-06 04:58:09 +00:00
|
|
|
)
|
|
|
|
|
status = "True" if response.status_code == 200 else "False"
|
2023-02-27 20:15:44 +09:00
|
|
|
if response.status_code == 200:
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("PreAuth Key added.")
|
2023-02-27 20:15:44 +09:00
|
|
|
else:
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.error("Adding PreAuth Key failed!")
|
2023-02-06 04:58:09 +00:00
|
|
|
return {"status": status, "body": response.json()}
|
|
|
|
|
|
2023-04-03 12:11:45 +00:00
|
|
|
|
2023-02-06 04:58:09 +00:00
|
|
|
# Expire a pre-auth key. data is {"user": "string", "key": "string"}
|
|
|
|
|
def expire_preauth_key(url, api_key, data):
|
2023-02-27 22:45:12 +09:00
|
|
|
app.logger.info("Expiring PreAuth Key...")
|
2023-02-06 04:58:09 +00:00
|
|
|
response = requests.post(
|
2023-04-03 12:11:45 +00:00
|
|
|
str(url) + "/api/v1/preauthkey/expire",
|
2023-02-06 04:58:09 +00:00
|
|
|
data=data,
|
|
|
|
|
headers={
|
2023-04-03 12:11:45 +00:00
|
|
|
"Accept": "application/json",
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
"Authorization": "Bearer " + str(api_key),
|
|
|
|
|
},
|
2023-02-06 04:58:09 +00:00
|
|
|
)
|
|
|
|
|
status = "True" if response.status_code == 200 else "False"
|
2023-04-03 12:11:45 +00:00
|
|
|
app.logger.debug("expire_preauth_key - Return: " + str(response.json()))
|
|
|
|
|
app.logger.debug("expire_preauth_key - Status: " + str(status))
|
2023-02-06 04:58:09 +00:00
|
|
|
return {"status": status, "body": response.json()}
|