chore: Finalize install scripts.

This commit is contained in:
greysoh 2024-08-02 11:47:00 -04:00
parent 61ccbf61d6
commit 9006a8e002
7 changed files with 191 additions and 39 deletions

View file

@ -1,24 +1,37 @@
#!/usr/bin/env bash
SERVER_INSTALL_PATH="$1"
EXTERN_IP="$2"
HTTP_PORT="$((1024 + $RANDOM % 65535))"
TMPDIR="/tmp/server_http_$HTTP_PORT"
BASE_IPS="$(ip a | grep "inet" | grep "brd" | cut -d "/" -f 1 | cut -d " " -f 6)"
EXT_10_DOT_IPS="$(echo "$BASE_IPS" | grep "10.")"
EXT_192168_IPS="$(echo "$BASE_IPS" | grep "192.168.")"
EXT_172_16_IPS="$(echo "$BASE_IPS" | grep "172.16.")"
EXTERNAL_IP_FULL=$EXT_10_DOT_IPS$'\n'$EXT_192168_IPS$'\n'$EXT_172_16_IPS$'\n'
if [ "$SERVER_INSTALL_PATH" = "" ]; then
if [ "$SERVER_INSTALL_PATH" == "" ]; then
echo "You didn't pass in all the arguments! Usage:"
echo " ./install.sh \$INSTALL_KEY"
exit 1
fi
./merge.py "$SERVER_INSTALL_PATH"
if [ "$EXTERN_IP" == "" ]; then
BASE_IPS="$(ip a | grep "inet" | grep "brd" | cut -d "/" -f 1 | cut -d " " -f 6)"
EXT_10_DOT_IP="$(echo "$BASE_IPS" | grep "10." | cut -d $'\n' -f 1)"
EXT_172_16_IP="$(echo "$BASE_IPS" | grep "172.16." | cut -d $'\n' -f 1)"
EXT_192168_IP="$(echo "$BASE_IPS" | grep "192.168." | cut -d $'\n' -f 1)"
if [ "$EXT_10_DOT_IP" != "" ]; then
EXTERN_IP="$EXT_10_DOT_IP"
fi
if [ "$EXT_172_16_IP" != "" ]; then
EXTERN_IP="$EXT_172_16_IP"
fi
if [ "$EXT_192168_IP" != "" ]; then
EXTERN_IP="$EXT_192168_IP"
fi
fi
./merge.py "$SERVER_INSTALL_PATH" "http://$EXTERN_IP:$HTTP_PORT/api/installer_update_webhook"
echo "[x] initializing..."
mkdir $TMPDIR
@ -35,18 +48,12 @@ touch $TMPDIR/meta-data
touch $TMPDIR/vendor-data
echo "[x] starting HTTP server..."
echo " - Listening on port $HTTP_PORT."
echo " - Add one of these command line options for Ubuntu (guessed local IP):"
echo " - Going to listen on port $HTTP_PORT."
echo " - Unless you believe the install has gone wrong, do NOT manually kill the HTTP server,"
echo " - as it will close on its own."
echo " - Add these command line options to Ubuntu:"
echo " - autoinstall \"ds=nocloud-net;s=http://$EXTERN_IP:$HTTP_PORT/\""
while IFS= read -r IP; do
# I'm too lazy to do root causing of this shit.
if [ "$IP" != "" ]; then
echo " - autoinstall \"ds=nocloud-net;s=http://$IP:$HTTP_PORT/\""
fi
done <<< "$EXTERNAL_IP_FULL"
echo " - Choose the right IP."
echo
SERVE_SCRIPT="$PWD/serve.py"

19
serverinfra/k3s.yaml Normal file
View file

@ -0,0 +1,19 @@
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkakNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUzTWpJMk1USXpOelV3SGhjTk1qUXdPREF5TVRVeU5qRTFXaGNOTXpRd056TXhNVFV5TmpFMQpXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUzTWpJMk1USXpOelV3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFTcGhGejc1VUVxdHFlb3RKSUhWN0U4TkRuMVJXTHlrUXQ5UHdKTkdNT2kKS2h3WHRkbkM3aHRpRHFXcDJneEI5OStJSHlvdlc4VlJDcFkxdnpPcEtKdERvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVXdlNzhWbk5hRkpVeENVQlZKa0xOCkhRLzd3Tm93Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnWndmbjBYSWFoQ3VIa1JuVTMwMmVkd3ZxU1BiL2ZKR24KM0t4QXdVb0p4cG9DSUExZlRjMU1VbkFHdHVxd0RDR3FGdndndmVqOXNBUnJZekVtcitZelRzelMKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
server: https://127.0.0.1:6443
name: default
contexts:
- context:
cluster: default
user: default
name: default
current-context: default
kind: Config
preferences: {}
users:
- name: default
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJrVENDQVRlZ0F3SUJBZ0lJQlpxNlZMVm91R0l3Q2dZSUtvWkl6ajBFQXdJd0l6RWhNQjhHQTFVRUF3d1kKYXpOekxXTnNhV1Z1ZEMxallVQXhOekl5TmpFeU16YzFNQjRYRFRJME1EZ3dNakUxTWpZeE5Wb1hEVEkxTURndwpNakUxTWpZeE5Wb3dNREVYTUJVR0ExVUVDaE1PYzNsemRHVnRPbTFoYzNSbGNuTXhGVEFUQmdOVkJBTVRESE41CmMzUmxiVHBoWkcxcGJqQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJFS25jd2Jmc3NYdGxXVFMKb05LYTcyOHpFSDRZTkVoVUIzU0hRNXhzb2lMYzdEVmVpRjNpd0hER1FxNlpuWnFyRXI4U1c3Q1ZzZ2N1di83TwpLSUcrRWVhalNEQkdNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUtCZ2dyQmdFRkJRY0RBakFmCkJnTlZIU01FR0RBV2dCUTMrRmM5TVFXTnA5TXN2czNIUDdoa1phVXJLVEFLQmdncWhrak9QUVFEQWdOSUFEQkYKQWlBWGtiYVVLQnBNdkM2S1ZTVDJsYlFjcTJ2WmRSMU44SWIyL05SMWZHdUFRZ0loQUtsNkczci9pbDU1dW00UQphcjEvNThWd0Nac3lVUDBBVlZZZFlBSjhxbmRxCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkekNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdFkyeHAKWlc1MExXTmhRREUzTWpJMk1USXpOelV3SGhjTk1qUXdPREF5TVRVeU5qRTFXaGNOTXpRd056TXhNVFV5TmpFMQpXakFqTVNFd0h3WURWUVFEREJock0zTXRZMnhwWlc1MExXTmhRREUzTWpJMk1USXpOelV3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFUOE45RFRodkZmazl2QU83WlMxWW1MVVUxYWhGVzJHcTUwZFZIOXlFNHcKc3hrbGZxemdmWXprc2dES2dmZVlLOFZIak5jaHA2VTh6cERFNm9wczRNaTdvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVU4vaFhQVEVGamFmVExMN054eis0ClpHV2xLeWt3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnUWQ1bVI3QzFZazdpaUpIYS9vUlQ4Q0R4MGpQZ2NITEkKaVBCZU9GL1RKTDBDSVFDdW5ic1B5dS9KWUNJQlhVMy9mdXhRcjg5MDJoeXBUT0NocDJvVENqU291Zz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
client-key-data: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxIcnA5Rmh2a2ZJM2VJQTJiOVIxbTk4THh0RTBhaXRRcSt4REVVODhWRWJvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFUXFkekJ0K3l4ZTJWWk5LZzBwcnZiek1RZmhnMFNGUUhkSWREbkd5aUl0enNOVjZJWGVMQQpjTVpDcnBtZG1xc1N2eEpic0pXeUJ5Ni8vczRvZ2I0UjVnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=

View file

@ -11,11 +11,12 @@ for item in ["K3S_TOKEN", "SETUP_USERNAME", "SETUP_PASSWORD"]:
print(f"ERROR: .env failed to load! (missing environment variable '{item}')")
exit(1)
if len(argv) < 2:
print("ERROR: Missing the server name")
if len(argv) < 3:
print("ERROR: Missing the server name or the webhook URL")
exit(1)
server_name = argv[1]
server_webhook_url = argv[2]
server_infra_contents = ""
@ -93,6 +94,8 @@ yaml_install_script["autoinstall"]["identity"]["hostname"] = infra_server["hostn
yaml_install_script["autoinstall"]["identity"]["username"] = environ["SETUP_USERNAME"]
yaml_install_script["autoinstall"]["identity"]["password"] = environ["SETUP_PASSWORD"]
yaml_install_script["autoinstall"]["reporting"]["hook"]["endpoint"] = server_webhook_url
ubuntu_install_contents = yaml.dump(yaml_install_script, Dumper=yaml.CDumper)
with open("/tmp/script.yml", "w") as new_install_script:

View file

@ -1,28 +1,146 @@
# TODO:
# Install logging over HTTP *could* be implemented (see autoinstall documentation), however
# it is not implemented here.
from termcolor import colored
from datetime import datetime, timezone
from os import getcwd, environ
from pathlib import Path
import socketserver
import http.server
import socket
import json
import sys
requests = set()
def json_to_bytes(str: str) -> bytearray:
return bytearray(json.dumps(str), "utf-8")
class HTTPHandler(http.server.SimpleHTTPRequestHandler):
# Who needs Flask, anyways?
class HTTPHandler(http.server.BaseHTTPRequestHandler):
def send_headers(self):
self.send_header("Content-Type", "application/json")
self.end_headers()
def do_POST(self):
if self.path == "/api/installer_update_webhook":
content_length = 0
try:
content_length = int(self.headers.get('Content-Length'))
except ValueError:
self.send_response(400)
self.send_headers()
self.wfile.write(json_to_bytes({
"success": False,
"error": "Failed to decode Content-Length to read body",
}))
return
resp_data = self.rfile.read(content_length).decode("utf-8")
resp_decoded_data: dict = {}
try:
resp_decoded_data = json.loads(resp_data)
if type(resp_decoded_data) is not dict:
self.send_response(400)
self.send_headers()
self.wfile.write(json_to_bytes({
"success": False,
"error": "Recieved invalid type for JSON",
}))
return
except json.JSONDecodeError:
self.send_response(400)
self.send_headers()
self.wfile.write(json_to_bytes({
"success": False,
"error": "Failed to decode JSON",
}))
return
date_time = datetime.fromtimestamp(resp_decoded_data["timestamp"], timezone.utc)
str_formatted_time = date_time.strftime("%H:%M:%S")
result_is_safe = resp_decoded_data["result"] == "SUCCESS" if "result" in resp_decoded_data else True
output_file = sys.stdout if result_is_safe else sys.stderr
output_coloring = "light_blue"
if "result" in resp_decoded_data:
res = resp_decoded_data["result"]
if res == "SUCCESS":
output_coloring = "light_green"
elif res == "WARN":
output_coloring = "light_yellow"
elif res == "FAIL":
output_coloring = "light_red"
result_text_component = f" {resp_decoded_data["result"]} " if "result" in resp_decoded_data else " "
final_output_text = f"{str_formatted_time} {resp_decoded_data["event_type"].upper()} {resp_decoded_data["level"]}:{result_text_component}{resp_decoded_data["name"]} ({resp_decoded_data["description"]})"
print(colored(final_output_text, output_coloring), file=output_file)
self.send_response(200)
self.send_headers()
self.wfile.write(json_to_bytes({
"success": True,
}))
if resp_decoded_data["event_type"] == "finish" and resp_decoded_data["name"] == "subiquity/Shutdown/shutdown":
print("\nSuccessfully finished installing!")
exit(0)
else:
self.send_response(404)
self.send_headers()
self.wfile.write(json_to_bytes({
"success": False,
"error": "Unknown route"
}))
def do_GET(self):
http.server.SimpleHTTPRequestHandler.do_GET(self)
requests.add(self.path)
resolved_path = str(Path(self.path).resolve())
file_path = getcwd() + resolved_path
found_meta_data = "/meta-data" in requests
found_user_data = "/user-data" in requests
found_vendor_data = "/vendor-data" in requests
try:
self.send_response(200)
self.end_headers()
with open(file_path, "rb") as file:
self.wfile.write(file.read())
except (FileNotFoundError, IsADirectoryError):
self.send_response(404)
self.send_headers()
self.wfile.write(json_to_bytes({
"success": False,
"error": "file not found"
}))
except () as exception:
exception.print_exception()
def log_message(self, format: str, *args):
status_code = 0
try:
status_code = int(args[1])
except ValueError:
pass
# Disable logging for the /api/ endpoint for POST requests unless the error code > 400
if len(args) >= 1 and args[0].startswith("POST") and self.path.startswith("/api/") and status_code < 400:
return
if found_meta_data and found_user_data and found_vendor_data:
print("[x] sent all our data, exiting...")
sys.exit(0)
super().log_message(format, *args)
server = socketserver.TCPServer(("", int(sys.argv[1])), HTTPHandler)
port = int(sys.argv[1]) if "SERVE_DEVELOP" not in environ else 10240
server = socketserver.TCPServer(("", port), HTTPHandler)
server.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
print("[x] started HTTP server.")

View file

@ -14,8 +14,9 @@ if [ ! -f "conifg/.env" ]; then
fi
echo "Installation usage:"
echo " - ./install.sh \$IP:"
echo " Installs Ubuntu Server on \$IP. You will find the correct password in Help > Help on SSH access"
echo " - ./install.sh \$CONFIG \$OPTIONAL_IP:"
echo " Installs Ubuntu Server using configuration \$CONFIG."
echo " \$OPTIONAL_IP is the optional IP address of your computer, if it guesses your IP address wrong."
echo
echo "Have fun!"

View file

@ -6,6 +6,7 @@
# Packages
python312Packages.pyyaml
python312Packages.termcolor
];
shellHook = ''

View file

@ -31,6 +31,9 @@ autoinstall:
install: false
drivers:
install: false
reporting:
hook:
type: webhook
kernel:
package: linux-generic
keyboard: