File uyuni-configure.py of Package proxy-httpd-image

#!/usr/bin/python3
# pylint: disable=invalid-name,inconsistent-quotes
"""Configure script for Uyuni proxy httpd container."""

import logging
import os
import subprocess
import re
import yaml
import socket
import sys

from pathlib import Path
from typing import Tuple

config_path = "/etc/uyuni/"


def get_ips(fqdn: str) -> Tuple[str, str]:
    addrinfo = socket.getaddrinfo(fqdn, None)
    ipv4s = set(
        map(lambda r: r[4][0], filter(lambda f: f[0] == socket.AF_INET, addrinfo))
    )
    ipv6s = set(
        map(lambda r: r[4][0], filter(lambda f: f[0] == socket.AF_INET6, addrinfo))
    )
    ipv4, ipv6 = "", ""

    if len(ipv4s) == 0 and len(ipv6s) == 0:
        logging.critical("Cannot determine proxy IPv4 nor IPv6 from FQDN %s", fqdn)
        sys.exit(1)

    try:
        ipv4 = ipv4s.pop()
        if len(ipv4s) > 0:
            logging.warning(
                "Cannot determine unique IPv4 address for the proxy. TFTP sync may not work. Using IPv4 %s",
                ipv4,
            )
    except KeyError:
        logging.warning(
            "No IPv4 address detected for proxy. If this is single stack IPv6 setup this warning can be ignored"
        )

    try:
        ipv6 = ipv6s.pop()
        if len(ipv6s) > 0:
            logging.debug("Multiple IPv6 addresses resolved, using IPv6 %s", ipv6)
    except KeyError:
        logging.warning(
            "No IPv6 address detected for proxy. If this is single stack IPv4 setup this warning can be ignored"
        )

    logging.debug("Detected ips '%s', '%s' for fqdn %s", ipv4, ipv6, fqdn)
    return (ipv4, ipv6)


# read from files
with open(config_path + "config.yaml", encoding="utf-8") as source:
    config = yaml.safe_load(source)

    # log_level is the value for rhn.conf and should be a positive integer
    log_level = logging.WARNING if config.get("log_level") == 1 else logging.DEBUG
    logging.getLogger().setLevel(log_level)

with open(config_path + "httpd.yaml", encoding="utf-8") as httpdSource:
    httpdConfig = yaml.safe_load(httpdSource).get("httpd")

    server_version = config.get("server_version")
    # Only check version for SUSE Multi-Linux Manager, not Uyuni
    matcher = re.fullmatch(r"([0-9]+\.)[0-9]+\.[0-9]+", server_version)
    if matcher:
        major_version = matcher.group(1)
        container_version = subprocess.run(
            ["rpm", "-q", "--queryformat", "%{version}", "spacewalk-proxy-common"],
            stdout=subprocess.PIPE,
            universal_newlines=True,
            check=False,
        ).stdout
        if not container_version.startswith(major_version):
            logging.critical(
                "Proxy container image version (%s) doesn't match server major version (%s)",
                container_version,
                major_version,
            )
            sys.exit(1)

    # store the systemid content, but only if it does not exist already
    if not os.path.exists("/etc/sysconfig/rhn/systemid"):
        with open("/etc/sysconfig/rhn/systemid", "w", encoding="utf-8") as file:
            file.write(httpdConfig.get("system_id"))

    # store SSL CA certificate, if it does not exist already
    if not os.path.exists("/etc/pki/trust/anchors/RHN-ORG-TRUSTED-SSL-CERT"):
        with open(
            "/etc/pki/trust/anchors/RHN-ORG-TRUSTED-SSL-CERT", "w", encoding="utf-8"
        ) as file:
            file.write(config.get("ca_crt"))
    # however always prepare link
    os.symlink(
        "/etc/pki/trust/anchors/RHN-ORG-TRUSTED-SSL-CERT",
        "/usr/share/rhn/RHN-ORG-TRUSTED-SSL-CERT",
    )
    os.system("/usr/sbin/update-ca-certificates")

    # store server certificate files, if cert does not exist already
    if not os.path.exists("/etc/apache2/ssl.crt/server.crt"):
        with open("/etc/apache2/ssl.crt/server.crt", "w", encoding="utf-8") as file:
            file.write(httpdConfig.get("server_crt"))
        with open("/etc/apache2/ssl.key/server.key", "w", encoding="utf-8") as file:
            file.write(httpdConfig.get("server_key"))

    with open("/etc/apache2/httpd.conf", "r+", encoding="utf-8") as file:
        file_content = file.read()
        # make sure to send logs to stdout/stderr instead to file
        file_content = re.sub(r"ErrorLog .*", "ErrorLog /proc/self/fd/2", file_content)
        # writing back the content
        file.seek(0, 0)
        file.write(file_content)
        file.truncate()

    # resolve needed IP addresses
    server_ipv4, server_ipv6 = get_ips(config["server"])
    proxy_ipv4, proxy_ipv6 = get_ips(config["proxy_fqdn"])

    # Create conf file
    with open("/etc/rhn/rhn.conf", "w", encoding="utf-8") as file:
        file.write(
            f"""# Automatically generated Uyuni Proxy Server configuration file.
        # -------------------------------------------------------------------------
        
        # Debug log level
        debug = {config.get("log_level", 1)}
        
        # Logs redirect
        proxy.broker.log_file = stdout
        proxy.redirect.log_file = stdout

        # SSL CA certificate location
        proxy.ca_chain = /etc/pki/trust/anchors/RHN-ORG-TRUSTED-SSL-CERT
        
        # Corporate HTTP proxy, format: corp_gateway.example.com:8080
        proxy.http_proxy = 
        
        # Username for that corporate HTTP proxy
        proxy.http_proxy_username = 
        
        # Password for that corporate HTTP proxy
        proxy.http_proxy_password = 
        
        # Location of locally built, custom packages
        proxy.pkg_dir = /var/spool/rhn-proxy
        
        # Hostname of Uyuni, SUSE Multi-Linux Manager Server or another proxy
        proxy.rhn_parent = {config['server']}
        proxy.proxy_fqdn = {config['proxy_fqdn']}
        
        # Destination of all tracebacks, etc.
        traceback_mail = {config['email']}

        # Tftp sync configuration
        tftpsync.server_fqdn = {config['server']}
        tftpsync.server_ip = {server_ipv4}
        tftpsync.server_ip6 = {server_ipv6}
        tftpsync.proxy_ip = {proxy_ipv4}
        tftpsync.proxy_ip6 = {proxy_ipv6}
        tftpsync.proxy_fqdn = {config['proxy_fqdn']}
        tftpsync.tftpboot = /srv/tftpboot"""
        )

    with open(
        "/etc/apache2/conf.d/smlm-proxy-forwards.conf", "r+", encoding="utf-8"
    ) as smlm_conf:
        file_content = smlm_conf.read()
        file_content = re.sub(r"{{ SERVER }}", config["server"], file_content)
        smlm_conf.seek(0, 0)
        smlm_conf.write(file_content)
        smlm_conf.truncate()

    with open(
        "/etc/apache2/conf.d/susemanager-tftpsync-recv.conf", "w", encoding="utf-8"
    ) as file:
        require_ipv4 = ""
        require_ipv6 = ""
        if len(server_ipv4) > 0:
            require_ipv4 = f"Require ip {server_ipv4}"
        if len(server_ipv6) > 0:
            require_ipv6 = f"Require ip {server_ipv6}"
        file.write(
            f"""<Directory "/srv/www/tftpsync">
    <RequireAny>
        {require_ipv4}
        {require_ipv6}
    </RequireAny>
</Directory>

WSGIScriptAlias /tftpsync/add /srv/www/tftpsync/add
WSGIScriptAlias /tftpsync/delete /srv/www/tftpsync/delete"""
        )

    with open("/etc/apache2/vhosts.d/ssl.conf", "w", encoding="utf-8") as file:
        file.write(
            f"""
<IfDefine SSL>
<IfDefine !NOSSL>
<VirtualHost _default_:443>

	DocumentRoot "/srv/www/htdocs"
	ServerName {config['proxy_fqdn']}

	ErrorLog /proc/self/fd/2
	TransferLog /proc/self/fd/1
	CustomLog /proc/self/fd/1   ssl_combined

	SSLEngine on
	SSLUseStapling  on

    SSLCertificateFile /etc/apache2/ssl.crt/server.crt
    SSLCertificateKeyFile /etc/apache2/ssl.key/server.key

    SSLProtocol all -SSLv2 -SSLv3
    RewriteEngine on
    RewriteOptions inherit
    SSLProxyEngine on
</VirtualHost>
</IfDefine>
</IfDefine>
"""
        )


# Make sure permissions are set as desired
os.system("/usr/bin/chown root:www /etc/rhn/rhn.conf")
os.system("/usr/bin/chmod 640 /etc/rhn/rhn.conf")

os.system("/usr/bin/chown -R wwwrun:www /var/spool/rhn-proxy")
os.system("/usr/bin/chmod -R 750 /var/spool/rhn-proxy")
if not os.path.exists("/var/cache/rhn/proxy-auth"):
    os.makedirs("/var/cache/rhn/proxy-auth")
os.system("/usr/bin/chown -R wwwrun:root /var/cache/rhn/proxy-auth")
os.system("/usr/bin/chown -R wwwrun:root /srv/tftpboot")
os.system("/usr/bin/chmod 755 /srv/tftpboot")

# Invalidate (remove) possible old proxy auth cache files, based on sha1
# after migration to sha256 proxy auth cache files.
#
# The old sha1 based cache files are like (filename length = 51):
#    p10000100040c488b45d72291a0da497f5101d47e274c6b63ac
#
# The new sha256 based cache files are like (filename length = 75):
#    p1000010004e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
#
for cache_file in Path("/var/cache/rhn/proxy-auth").iterdir():
    if cache_file.is_file() and len(cache_file.name) == 51:
        cache_file.unlink()
openSUSE Build Service is sponsored by