Compare commits

...

23 Commits

Author SHA1 Message Date
1d4b720cee feat: ssh config builder & fetcher 2025-10-29 17:54:42 +01:00
c8640eb035 fix: cleanup stdout output 2025-10-27 14:13:53 +01:00
2a9559642b fix: simple warnings/handlers for ipv6 2025-10-27 14:13:51 +01:00
ac2fbfdef2 fix: add error for invalid port config 2025-10-27 14:13:36 +01:00
9cb5204fec add: qemu iptables hook 2025-10-24 18:05:16 +02:00
cd76f3fe6b wip: wireguard server extender 2025-05-10 12:46:50 +02:00
Kathrin Maurer
9634f35a1e feat: kubernetes ingress/alt http port support 2025-02-28 16:15:51 +01:00
73106f6d57 wip: skel for provisioning script 2025-01-18 13:18:40 +00:00
Kathrin Maurer
3222b4b437 fix: soft-fail on invalid backup config 2025-01-09 23:43:16 +01:00
Kathrin Maurer
e99e729a83 fix: allow targetportoverwrite as L4 param 2025-01-05 19:50:05 +01:00
Kathrin Maurer
84cb2f9fb2 fix: add master-address.txt to gitignore 2025-01-05 17:39:12 +01:00
Kathrin Maurer
f71269d14b fix: check for invalid entries 2025-01-05 17:38:45 +01:00
Kathrin Maurer
bfd1c20c2b fix: increase proxy buffers for large OIDC jwt tokens 2024-12-31 03:08:17 +01:00
a7abf4c5c1 fix: make master address configurable by file 2024-12-29 15:31:29 +00:00
f7e4d95ef9 fix: make async icinga curl optiona 2024-12-29 15:31:13 +00:00
581e741470 fix: move pull snapshots script to helper scripts 2024-12-29 15:19:28 +00:00
7f488a984b fix: don't crash on inaccessible host 2024-12-29 15:05:36 +00:00
7ff0cba340 fix: disable nginx vpn cert map by default 2024-12-29 13:13:30 +00:00
105ce44026 feat: configurate interfaces for listen directive 2024-12-28 15:32:14 +00:00
54c299c21e feat: dont pre-template nsca server conf pw 2024-12-22 13:38:28 +00:00
e5313bcf4e add: current iptables 2024-11-23 18:31:27 +00:00
79f36ac23e fix: update host exlusion list 2024-11-23 18:30:48 +00:00
da24ff9b1e fix: double config in extra content 2024-11-23 17:04:40 +00:00
21 changed files with 531 additions and 25 deletions

1
.gitignore vendored
View File

@@ -8,3 +8,4 @@ password.txt
ssh_config_for_clients
virsh_backup
.wireguard_keys
master-address.txt

View File

@@ -3,10 +3,11 @@ import functools
import os
import subprocess
import json
import sys
environment = jinja2.Environment(loader=jinja2.FileSystemLoader(searchpath="./templates"))
def createBackupScriptStructure(backupList, baseDomain="", icingaOnly=False):
def createBackupScriptStructure(backupList, baseDomain="", icingaOnly=False, backup_no_async_icinga=True):
backupPath = "./build/backup/"
@@ -20,6 +21,15 @@ def createBackupScriptStructure(backupList, baseDomain="", icingaOnly=False):
asyncIcingaConf = {}
for backup in backupList:
if not backup:
print("Warning: Empty backup mapping in List", file=sys.stderr)
continue
if type(backup) == str:
print(f"Warning: Backup Entry is a stirng instead of a dict-object ({backup})", file=sys.stderr)
continue
if backup.get("disabled"):
continue
@@ -107,7 +117,8 @@ def createBackupScriptStructure(backupList, baseDomain="", icingaOnly=False):
rsyncScript = rsyncScriptTemplate.render(hostname=hostname, token=icingaToken,
hostname_base=hostnameBase,
size_change_commands=sizeChangeNotifyCommands)
size_change_commands=sizeChangeNotifyCommands,
backup_no_async_icinga=backup_no_async_icinga)
# build all filter #
rsyncFilterAll = rsyncFilterTemplate.render(paths=pathsAll)
@@ -185,12 +196,20 @@ def sizeChanged(hostname, pathsToOptions, path):
return True
# check server #
cmd = ["ssh", hostname, "-t", "/opt/check_dir_size_for_backup.py", path ]
cmd = [
"ssh", hostname,
"-o", "PasswordAuthentication=no",
"-o", "ConnectTimeout=3",
"-t", "/opt/check_dir_size_for_backup.py",
path
]
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, encoding="utf-8")
stdout, stderr = p.communicate()
if p.wait() != 0:
raise OSError("ssh commmand for backup size info failed '{}' - '{}' Host: {}".format(
print("Warning: ssh commmand for backup size info failed '{}' - '{}' Host: {}".format(
stderr, stdout, hostname))
return []
# parse response #
result = json.loads(stdout)

View File

@@ -0,0 +1,45 @@
#!/usr/bin/python
import sys
import subprocess
import os
import paramiko
HOSTS = [
"root@atlantishq.de",
"root@katzencluster.atlantishq.de",
"root@atlantis-helsinki.atlantishq.de"
]
BASE_FILE = "~/.ssh/base_config"
MAIN_CONFIG = "~/.ssh/config"
if __name__ == "__main__":
contents = ""
for target in HOSTS:
RUN_CMD = ["ssh", "-t", target , "cd /root/athq-vm-management/; python3 main.py"]
COPY_CMD = ["ssh", "-t", target, "cat /root/athq-vm-management/ssh_config_for_clients"]
print("Doing", target, file=sys.stderr)
out = subprocess.run(RUN_CMD, capture_output=True, universal_newlines=True)
if out.returncode != 0:
print("failed (run command)!")
print(out.stderr)
sys.exit(1)
out = subprocess.run(COPY_CMD, capture_output=True, universal_newlines=True)
if out.returncode != 0:
print("failed (cat command)!")
print(out.stderr)
sys.exit(1)
contents += out.stdout
contents += "\n"
with open(os.path.expanduser(BASE_FILE)) as f:
with open(os.path.expanduser(MAIN_CONFIG), "w") as fout:
fout.write(f.read())
fout.write("\n")
fout.write(contents)

View File

@@ -0,0 +1,12 @@
#!/bin/bash
if [[ "$2" == "started" ]]; then
/usr/sbin/iptables -I LIBVIRT_FWI 1 -o virbr0 -i atlantishq -d 192.168.123.0/24 -j ACCEPT
/usr/sbin/iptables -I LIBVIRT_FWI 1 -o virbr0 -i at_helsinki -d 192.168.123.0/24 -j ACCEPT
/usr/sbin/iptables -I LIBVIRT_FWI 1 -o virbr0 -i hc_worker_1 -d 192.168.123.0/24 -j ACCEPT
/usr/sbin/iptables -I LIBVIRT_FWO 1 -i virbr0 -s 192.168.123.0/24 -o atlantishq -j ACCEPT
/usr/sbin/iptables -I LIBVIRT_FWO 1 -i virbr0 -s 192.168.123.0/24 -o hc_worker_1 -j ACCEPT
/usr/sbin/iptables -I LIBVIRT_FWO 1 -i virbr0 -s 192.168.123.0/24 -o at_helsinki -j ACCEPT
fi

View File

@@ -85,14 +85,22 @@ if __name__ == "__main__":
# shut down VM #
print("Next:", vm.name())
vm_skip_list = ["harbor-registry", "backup", "irc-new", #"kube1",
"kube2", "mail", "monitoring", "paperless",
"prometheus", "signal", "steam-master", "zabbix",
"git", "kathi", "usermanagement", "vpn", "ths", "nextcloud-athq"]
vm_skip_list = ["harbor-registry", "backup", #"irc-new", #"kube1",
"kube2",
"kube1",
"mail",
"opensearch",
"monitoring",
"paperless",
"prometheus", "signal",
"steam-master", "zabbix",
"git",
"kathi", "usermanagement", "vpn", "ths", "nextcloud-athq"
]
if vm.name() in vm_skip_list:
continue
vm_white_list = ["kube1"]
vm_white_list = ["kathi"]
if vm_white_list:
if not vm.name() in vm_white_list:
continue

View File

@@ -0,0 +1,107 @@
import subprocess
import json
import os
from pathlib import Path
import configparser
import ipaddress
LINK_BASE = f"10.0.{len(EXISTING_HOSTS)+10}.1/32"
LINK_BASE_PEER = f"10.0.{len(EXISTING_HOSTS)+10}.2/32"
PORT_BASE = 51820 + len(EXISTING_HOSTS)
MASTER_CONFIG = """[Interface]
PrivateKey = {local_private}
Address = {local_address}
[Peer]
Endpoint = {peer_endpoint}
PublicKey = {peer_public}
AllowedIPs = {", ".join(peer_allowed_ips)}
"""
PEER_CONFIG = f"""[Interface]
PrivateKey = {peer_private}
Address = {peer_address}
[Peer]
PublicKey = {local_public}
AllowedIPs = {", ".join(peer_allowed_ips)}
"""
def create_wireguard_config_pair(
new_host: str,
old_host: str,
new_host_allowed_ips: list[str],
old_host_allowed_ips,
old_host_public_key,
old_host_private_key,
output_dir: str = "./tmp"
):
# Validate input subnet
try:
ipaddress.IPv4Interface(local_address)
except ValueError as e:
raise ValueError(f"Invalid IP address: {local_address}") from e
Path(output_dir).mkdir(parents=True, exist_ok=True)
# Generate key pairs
local_private, local_public = generate_keypair()
peer_private, peer_public = generate_keypair()
new_host = MASTER_CONFIG.format(
local_private = local_private,
local_address = LINK_BASE,
local_public = local_public,
peer_endpoint = old_host,
peer_public = old_host_public_key,
peer_allowed_ips = [LINK_BASE_PEER, old_host_allowed_ips],
)
old_host = PEER_CONFIG.format(
peer_private = old_host_private_key,
peer_address = LINK_BASE_PEER,
local_public= local_public,
peer_allowed_ips = [LINK_BASE, new_host_subnet]
)
old_host_file = Path(output_dir) / f"{new_host}_local.conf"
new_host_file = Path(output_dir) / f"{new_host}_peer.conf"
new_host_file.write_text(new_host)
old_host_file.write_text(old_host)
return str(local_file), str(peer_file)
def generate_wireguard_keys():
private_key = subprocess.check_output(["wg", "genkey"]).strip()
public_key = subprocess.check_output(["wg", "pubkey"], input=private_key).strip()
return private_key.decode(), public_key.decode()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="WireGuard utility script")
parser.add_argument("--new-host", help="Hostname or IP of the new host")
parser.add_argument("--new-host-subnet", type=ipaddress.IPv4Network, help="Subnet of new host")
parser.add_argument("--write", action=argparse.BooleanOptionalAction, default=False)
parser.add_argument("--allow-overwrite", action=argparse.BooleanOptionalAction, default=False)
args = parser.parse_args()
configs = {}
for host in EXISTING_HOSTS:
fetch_conf_files(host)
configs |= load_all_confs(host)
# output current state #
print(json.dumps(configs, indent=2))
if args.new_host and args.new_host_subnet:
# TODO get keys from old host
create_wireguard_config_pair(LINK_BASE, new_host) #TODO

View File

@@ -0,0 +1,159 @@
import subprocess
import json
import os
from pathlib import Path
import configparser
import ipaddress
# FIXME/WARNING: dont change this order without checking the output
EXISTING_HOSTS = [
"atlantishq.de",
"katzencluster.atlantishq.de",
"atlantis-helsinki.atlantishq.de",
]
USER = "root"
REMOTE_DIR = "/etc/wireguard"
LOCAL_TMP_DIR = "./tmp"
LINK_BASE = f"10.0.{len(EXISTING_HOSTS)+10}.1/32"
LINK_BASE_PEER = f"10.0.{len(EXISTING_HOSTS)+10}.2/32"
PORT_BASE = 51820 + len(EXISTING_HOSTS)
def fetch_conf_files(host):
print(f"Doing {host}\n")
os.makedirs(LOCAL_TMP_DIR, exist_ok=True)
remote_path = f"{USER}@{host}:{REMOTE_DIR}/*.conf"
subprocess.run(["scp", remote_path, LOCAL_TMP_DIR], check=True)
print(f"\n{host} retrieved successfully.")
def parse_conf_file(filepath, host):
config = {}
current_section = "Interface"
config[current_section] = {}
with open(filepath) as f:
for line in f:
line = line.strip()
if not line or line.startswith("#"):
continue
if line.startswith("[") and line.endswith("]"):
current_section = line.strip("[]")
config[current_section] = {}
else:
if "=" in line:
key, val = map(str.strip, line.split("=", 1))
config[current_section][key] = val
return config
def load_all_confs(host):
all_configs = {}
for conf_file in Path(LOCAL_TMP_DIR).glob("*.conf"):
conf_name = conf_file.stem
all_configs[conf_name] = parse_conf_file(conf_file, host)
os.remove(conf_file)
return { host : all_configs }
def create_wireguard_config_pair(
new_host: str,
old_host: str,
new_host_allowed_ips: list[str],
old_host_allowed_ips,
old_host_public_key,
old_host_private_key,
output_dir: str = "./tmp"
):
# Validate input subnet
try:
ipaddress.IPv4Interface(local_address)
except ValueError as e:
raise ValueError(f"Invalid IP address: {local_address}") from e
Path(output_dir).mkdir(parents=True, exist_ok=True)
# Generate key pairs
local_private, local_public = generate_keypair()
peer_private, peer_public = generate_keypair()
MASTER_CONFIG = """[Interface]
PrivateKey = {local_private}
Address = {local_address}
[Peer]
Endpoint = {peer_endpoint}
PublicKey = {peer_public}
AllowedIPs = {", ".join(peer_allowed_ips)}
"""
PEER_CONFIG = f"""[Interface]
PrivateKey = {peer_private}
Address = {peer_address}
[Peer]
PublicKey = {local_public}
AllowedIPs = {", ".join(peer_allowed_ips)}
"""
new_host = MASTER_CONFIG.format(
local_private = local_private,
local_address = LINK_BASE,
local_public = local_public,
peer_endpoint = old_host,
peer_public = old_host_public_key,
peer_allowed_ips = [LINK_BASE_PEER, old_host_allowed_ips],
)
old_host = PEER_CONFIG.format(
peer_private = old_host_private_key,
peer_address = LINK_BASE_PEER,
local_public= local_public,
peer_allowed_ips = [LINK_BASE, new_host_subnet]
)
old_host_file = Path(output_dir) / f"{new_host}_local.conf"
new_host_file = Path(output_dir) / f"{new_host}_peer.conf"
new_host_file.write_text(new_host)
old_host_file.write_text(old_host)
return str(local_file), str(peer_file)
def generate_wireguard_keys():
private_key = subprocess.check_output(["wg", "genkey"]).strip()
public_key = subprocess.check_output(["wg", "pubkey"], input=private_key).strip()
return private_key.decode(), public_key.decode()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="WireGuard utility script")
parser.add_argument("--new-host", help="Hostname or IP of the new host")
parser.add_argument("--new-host-subnet", type=ipaddress.IPv4Network, help="Subnet of new host")
parser.add_argument("--write", action=argparse.BooleanOptionalAction, default=False)
parser.add_argument("--allow-overwrite", action=argparse.BooleanOptionalAction, default=False)
args = parser.parse_args()
configs = {}
for host in EXISTING_HOSTS:
fetch_conf_files(host)
configs |= load_all_confs(host)
# output current state #
print(json.dumps(configs, indent=2))
if args.new_host and args.new_host_subnet:
pub, priv = generate_wireguard_keys()
create_wireguard_config_pair(LINK_BASE, new_host

18
iptables/rules.v4 Normal file
View File

@@ -0,0 +1,18 @@
*filter
-A INPUT -p tcp -m tcp --dport 10050 -j DROP
-A FORWARD -d 159.69.136.222 -p tcp -m multiport --dports 26000:27000 -j ACCEPT
-A FORWARD -d 159.69.136.222 -p udp -m multiport --dports 26000:27000 -j ACCEPT
-A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A OUTPUT ! -s 159.69.136.222 -o eno1 -j REJECT --reject-with icmp-host-prohibited
-A INPUT -i eno1 -p tcp -m multiport --dports 5044,9200:9210,9300:9310 -j REJECT --reject-with icmp-host-prohibited
COMMIT
*nat
-A PREROUTING -i eno1 -p tcp -m multiport --dports 26000:27000 -j DNAT --to-destination 192.168.122.102
-A PREROUTING -i eno1 -p udp -m multiport --dports 26000:27000 -j DNAT --to-destination 192.168.122.102
-A POSTROUTING ! -o eno1 -p tcp -m multiport --dports 26000:27000 -d 192.168.122.102 -j SNAT --to-source 192.168.122.1
-A POSTROUTING ! -o eno1 -p tcp -m multiport --dports 26000:27000 -d 192.168.122.102 -j SNAT --to-source 192.168.122.1
COMMIT

4
iptables/rules.v6 Normal file
View File

@@ -0,0 +1,4 @@
*filter
-A INPUT -p tcp -m tcp --dport 10050 -j DROP
-A INPUT -i eno1 -p tcp -m multiport --dports 5044,9200:9210,9300:9310 -j REJECT
COMMIT

21
main.py
View File

@@ -21,6 +21,8 @@ if __name__ == "__main__":
parser.add_argument("--skip-nginx", action="store_const", default=True, const=False)
parser.add_argument("--skip-icinga", action="store_const", default=True, const=False)
parser.add_argument("--skip-ssh-config", action="store_const", default=True, const=False)
parser.add_argument("--do-nginx-map-cert-manager", action="store_const", default=False, const=True)
parser.add_argument("--backup-no-async-icinga", action="store_const", default=False, const=True)
args = parser.parse_args()
FILE = "./config/vms.json"
@@ -28,6 +30,12 @@ if __name__ == "__main__":
skipVirsh = not any([args.skip_ansible, args.skip_nginx,
args.skip_icinga, args.skip_ssh_config])
# set master address #
OPTIONAL_MASTER_ADDRESS_FILE="master-address.txt"
if os.path.isfile(OPTIONAL_MASTER_ADDRESS_FILE):
with open(OPTIONAL_MASTER_ADDRESS_FILE) as f:
MASTER_ADDRESS = f.read().strip()
with open(FILE) as f:
jsonList = json.load(f)
for obj in jsonList:
@@ -60,10 +68,15 @@ if __name__ == "__main__":
f.write("\n")
# backup #
with open("./config/backup.json") as f:
backup.createBackupScriptStructure(json.load(f), baseDomain=MASTER_ADDRESS,
icingaOnly=not args.backup)
try:
with open("./config/backup.json") as f:
backup.createBackupScriptStructure(json.load(f), baseDomain=MASTER_ADDRESS,
icingaOnly=not args.backup,
backup_no_async_icinga=args.backup_no_async_icinga)
except json.decoder.JSONDecodeError as e:
print("WARNING: Failed loading backup.json - either empty or invalid json!", file=sys.stderr)
print(e, file=sys.stderr)
# copy nginx maps #
if not args.backup:
if not args.backup and args.do_nginx_map_cert_manager:
os.system("rsync templates/nginx_maps.j2 root@192.168.122.104:/data/certificate-manager/")

View File

@@ -32,14 +32,15 @@ def dump_config(vmList, masterAddress):
for vmo in vmList:
relevant_subdomains = filter(lambda x: x.get("no-terminate-ssl"), vmo.subdomains)
for s in relevant_subdomains:
print(s)
# print(s, "ssl_target_port", s.get("ssl_target_port"))
# build the map contents #
if s.get("include-subdomains"):
match = "~.*{}".format(s.get("name"))
else:
match = s.get("name")
ssl_passthrough_map.append("{} {}:443;".format(match, vmo.ip))
ssl_target_port = s.get("ssl_target_port") or 443
ssl_passthrough_map.append("{} {}:{};".format(match, vmo.ip, ssl_target_port))
environment = jinja2.Environment(loader=jinja2.FileSystemLoader(searchpath="./templates"))
template = environment.get_template("nginx_stream_ssl_map.conf.j2")
@@ -65,7 +66,7 @@ def dump_config(vmList, masterAddress):
for vmo in vmList:
for subdomain in vmo.subdomains:
if vmo.noTerminateACME:
print("Not terminating ACME for: {}".format(subdomain))
print("Not terminating ACME for: {}".format(subdomain.get("name")))
continue
if type(subdomain) == dict:
domains.append(subdomain["name"])
@@ -94,10 +95,10 @@ def dump_config(vmList, masterAddress):
f.write(content)
def check_transparent_proxy_loader():
retcode = os.system("systemctl is-enabled nginx-iptables.service")
retcode = os.system("systemctl -q is-enabled nginx-iptables.service")
if retcode != 0:
print("############################ WARNING ###############################")
print("+++ You may have transparent proxy rules but the service to load +++")
print("+++ them is not enabled or missing, a restart WILL break your +++")
print("+++ setup! Add see nginx-iptables.service in the project root +++")
print("+++ setup! Look at nginx-iptables.service in the project root +++")
print("############################ WARNING ###############################")

40
provisioning/mac.py Normal file
View File

@@ -0,0 +1,40 @@
import libvirt
import xml.etree.ElementTree as ET
def get_mac_address(domain):
try:
# Connect to the libvirt daemon
conn = libvirt.open()
if conn is None:
raise RuntimeError("Failed to open connection to the hypervisor.")
# Lookup the domain by name
vm = conn.lookupByName(domain)
if vm is None:
raise ValueError(f"Domain '{domain}' not found.")
# Get the XML description of the domain
xml_desc = vm.XMLDesc()
# Parse the XML to extract the MAC address
root = ET.fromstring(xml_desc)
mac_element = root.find(".//mac")
if mac_element is not None:
mac_address = mac_element.attrib.get('address')
return mac_address
else:
raise ValueError("MAC address not found in XML.")
except libvirt.libvirtError as e:
return f"A libvirt error occurred: {e}"
except Exception as e:
return f"An error occurred: {e}"
finally:
if conn:
conn.close()
# Replace 'debian' with your domain name
domain_name = "debian"
mac_address = get_mac_address(domain_name)
print(f"MAC Address: {mac_address}")

26
provisioning/master.py Normal file
View File

@@ -0,0 +1,26 @@
# get new domain name
# create a VM and clone disk
# set CPU & RAM
# get mac
virsh_vm.get_mac(domain)
# change network
network.set_ip_for_mac_domain(domain, mac)
# net destory net start ?
# start vm
# change hostname
# add vm subdomains
# run python script
# run cert script
# reload nginx

View File

@@ -16,7 +16,7 @@ def dump_config(vmList):
f.write("\n")
# dump ansible
with open("./ansible/files/nsca_server.conf", "w") as f:
with open("./ansible/templates/nsca_server.conf", "w") as f:
env = jinja2.Environment(loader=jinja2.FileSystemLoader(searchpath="./templates"))
template = env.get_template("nsca_server.conf.j2")
f.write(template.render(vmList=sorted(list(set(filter(lambda x: x.ansible, vmList)))),

View File

@@ -44,6 +44,10 @@ http {
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# Increase the buffer size for headers
proxy_buffer_size 16k;
proxy_buffers 4 16k;
proxy_busy_buffers_size 16k;
client_header_buffer_size 5120k;
large_client_header_buffers 16 5120k;

View File

@@ -74,7 +74,11 @@ server{
{% else %}
location / {
{{ proxy_pass_blob }}
{% if http_target_port %}
proxy_pass http://{{ targetip }}:{{ http_target_port }};
{% else %}
proxy_pass http://{{ targetip }}:80;
{% endif %}
}
{% endif %}

View File

@@ -1,11 +1,31 @@
server {
# {{ comment }}
{% if port_interfaces %}
{% if "ipv4-all" in port_interfaces %}
listen {{ portstring }} {% if udp %} udp {% endif %}{% if ssl %} ssl {% endif %};
{% endif %}
{% if "ipv6-all" in port_interfaces %}
listen [::]:{{ portstring }} {% if udp %} udp {% endif %}{% if ssl %} ssl {% endif %};
{% endif %}
{% for pi in port_interfaces %}
{% if not pi.startswith("ipv") %}
listen {{ pi }}:{{ portstring }} {% if udp %} udp {% endif %}{% if ssl %} ssl {% endif %};
{% endif %}
{% endfor %}
{% else %}
listen {{ portstring }} {% if udp %} udp {% endif %}{% if ssl %} ssl {% endif %};
listen [::]:{{ portstring }} {% if udp %} udp {% endif %}{% if ssl %} ssl {% endif %};
{% endif %}
{% if not extra_content or not "proxy_timeout" in extra_content %}
proxy_timeout {{ proxy_timeout }};
proxy_responses 1;
{% endif %}
{% if targetportoverwrite %}
proxy_pass {{ targetip }}:{{ targetportoverwrite }};

View File

@@ -4,7 +4,7 @@ log_level = 2
{% for vmo in vmList %}
authorize "{{ vmo.hostname }}" {
password = "{{ password }}"
password = "{% raw %}{{ nsca_server_password }}{% endraw %}"
hosts = "{{ vmo.hostname }}"
services = {
"*",

View File

@@ -45,8 +45,10 @@ SERVICE="backup_{{ hostname_base }}"
TOKEN="{{ token }}"
if [ $RSYNC_SUCCESS -eq 0 ]; then
{% if not backup_no_async_icinga %}
curl -H "${CONTENT_TYPE}" -X POST "${ASYNC_ICINGA_ADDRESS}" -d \
"{\"service\": \"${SERVICE}\", \"token\": \"${TOKEN}\", \"status\": \"OK\", \"info\": \"\"}"
{% endif %}
# if size changed was copied save new size #
{% if not size_change_commands %}
@@ -56,8 +58,10 @@ if [ $RSYNC_SUCCESS -eq 0 ]; then
{{ cmd }}
{% endfor %}
else
{% if not backup_no_async_icinga %}
curl -H "${CONTENT_TYPE}" -X POST "${ASYNC_ICINGA_ADDRESS}" -d \
"{\"service\": \"${SERVICE}\", \"token\": \"${TOKEN}\", \"status\": \"CRITICAL\", \"info\": \"\"}"
{% endif %}
fi
cd ..

27
vm.py
View File

@@ -1,4 +1,5 @@
import libvirt
import json
import jinja2
class VM:
@@ -36,6 +37,8 @@ class VM:
network = con.networkLookupByName(self.network)
leases = network.DHCPLeases()
for l in leases:
if not l.get("type") == 0: # FIXME: only ipv4 for now
continue
if l.get("hostname") == self.hostname:
return l
@@ -62,11 +65,14 @@ class VM:
name = str(portStruct.get("name")).replace(" ", "")
portstring = str(portStruct.get("port")).replace(" ", "")
port_interfaces = portStruct.get("interfaces")
assert(port_interfaces is None or type(port_interfaces) == list)
transparent = portStruct.get("transparent")
proto = portStruct.get("proto") or "tcp"
isUDP = proto == "udp"
proxy_timeout = portStruct.get("proxy_timeout") or "10s"
extra_content = portStruct.get("extra-content")
targetportoverwrite = portStruct.get("targetportoverwrite")
compositeName = "-".join((self.hostname, name, portstring, proto))
@@ -75,7 +81,10 @@ class VM:
component = template.render(targetip=self.ip, udp=isUDP, portstring=portstring,
transparent=transparent, proxy_timeout=proxy_timeout,
comment=compositeName, extra_content=extra_content)
comment=compositeName, extra_content=extra_content,
targetportoverwrite=targetportoverwrite,
port_interfaces=port_interfaces)
components.append(component)
return components
@@ -85,7 +94,14 @@ class VM:
components = []
template = self.environment.get_template("nginx_stream_block.conf.j2")
if not self.isExternal:
self.sshOutsidePort = 7000 + int(self.ip.split(".")[-1])
try:
self.sshOutsidePort = 7000 + int(self.ip.split(".")[-1])
except ValueError as e:
print(f"Warning: {self.hostname} Invalid IP (IPv6 is not supported) {e}",
file=sys.stderr)
return []
component = template.render(targetip=self.ip, udp=False,
portstring=self.sshOutsidePort,
targetportoverwrite=7000,
@@ -130,7 +146,7 @@ class VM:
for subdomain in self.subdomains:
if subdomain.get("no-terminate-ssl"):
print("Not terminating TLS for: {}".format(subdomain))
print("Not terminating TLS for: {}".format(subdomain.get("name")))
if type(subdomain) != dict:
raise ValueError("Subdomain must be object containing 'name' ")
@@ -152,6 +168,10 @@ class VM:
if subdomain.get("include-subdomains") and not subdomain.get("no-terminate-ssl"):
raise ValueError("Wildcard Subdomain not supported with SSL Termination")
if "port" in subdomain and "no-terminate-ssl" in subdomain:
print(json.dumps(subdomain, indent=2))
raise ValueError("'port' is not allowed with no-terminate-ssl subdomain, use http_target_port and ssl_target_port")
component = template.render(targetip=self.ip, targetport=targetport,
servernames=[subdomain["name"]], comment=compositeName,
proxy_pass_blob=self.proxy_pass_blob,
@@ -162,6 +182,7 @@ class VM:
include_subdomains=subdomain.get("include-subdomains"),
cert_optional=cert_optional,
cert_non_optional=cert_non_optional,
http_target_port=subdomain.get("http_target_port"),
cert_header_line=header_line)
components.append(component)