diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..0da35fb --- /dev/null +++ b/Dockerfile @@ -0,0 +1,64 @@ +FROM debian:bookworm-slim +LABEL maintainer="docker@ao-it.net" + +## prepare requirements +RUN ["bash", "-exo", "pipefail", "-c", "\ + export DEBIAN_FRONTEND=noninteractive ; \ + apt update ; \ + apt install -y wget gnupg python3 python3-mysqldb; \ + wget -O - https://packages.icinga.com/icinga.key | \ + gpg --dearmor -o /usr/share/keyrings/icinga-archive-keyring.gpg ; \ + source /etc/os-release ; \ + echo \"deb [signed-by=/usr/share/keyrings/icinga-archive-keyring.gpg] https://packages.icinga.com/debian icinga-${VERSION_CODENAME} main\" > /etc/apt/sources.list.d/${VERSION_CODENAME}-icinga.list ; \ + echo \"deb-src [signed-by=/usr/share/keyrings/icinga-archive-keyring.gpg] https://packages.icinga.com/debian icinga-${VERSION_CODENAME} main\" >> /etc/apt/sources.list.d/${VERSION_CODENAME}-icinga.list ; \ + apt clean all ; \ + rm -vrf /var/lib/apt/lists/* "] + +## install webserver +RUN ["bash", "-exo", "pipefail", "-c", "\ + export DEBIAN_FRONTEND=noninteractive ; \ + apt update ; \ + apt install -y --no-install-{recommends,suggests} apache2 ca-certificates libapache2-mod-php8.2 libldap-common locales-all php-{imagick,redis} php8.2-{bcmath,bz2,common,curl,dba,enchant,gd,gmp,imap,interbase,intl,ldap,mbstring,mysql,odbc,opcache,pgsql,pspell,readline,snmp,soap,sqlite3,sybase,tidy,xml,xmlrpc,xsl,zip} ; \ + apt clean all ; \ + rm -vrf /var/lib/apt/lists/* "] + + +## install icingaweb2 +RUN ["bash", "-exo", "pipefail", "-c", "\ + export DEBIAN_FRONTEND=noninteractive ; \ + apt update ; \ + apt install -y icingaweb2 icingadb-web icingacli ; \ + ln -vs /usr/share/icingaweb2/bin/icingacli /usr/local/bin/ ; \ + rm -f /etc/apache2/conf-enabled/icingaweb2.conf ; \ + icingacli setup config webserver apache --path=/ --file=/etc/apache2/conf-enabled/icingaweb2.conf ; \ + echo 'SetEnvIf X-REMOTE-USER \"(.*)\" REMOTE_USER=$0' > /etc/apache2/conf-enabled/allow-remote-user.conf ; \ + apt clean all ; \ + rm -vrf /var/lib/apt/lists/* "] + +## create persistend data store +RUN ["bash", "-exo", "pipefail", "-c", "\ + mkdir -p /data ; \ + mkdir -p /data-init/etc/ ; \ + mkdir -p /data-init/usr/ ; \ + mv /etc/icingaweb2 /data-init/etc/ ; \ + mv /usr/share/icingaweb2 /data-init/usr/ ; \ + ln -vs /data/etc/icingaweb2 /etc/icingaweb2 ; \ + ln -vs /data/usr/icingaweb2 /usr/share/icingaweb2 ; \ +"] + +## redirect log +RUN ["bash", "-exo", "pipefail", "-c", "\ + ln -vsf /dev/stdout /var/log/apache2/access.log ; \ + ln -vsf /dev/stderr /var/log/apache2/error.log ; \ + ln -vsf /dev/stdout /var/log/apache2/other_vhosts_access.log "] + +EXPOSE 8080 + +COPY init.sh /root/init.sh +COPY conf_icingaweb2.py /root/conf_icingaweb2.py + +VOLUME ["/data"] +WORKDIR /data +USER root +ENTRYPOINT ["/bin/bash", "/root/init.sh"] +CMD ["/usr/sbin/apache2", "-DFOREGROUND"] \ No newline at end of file diff --git a/conf_icigaweb2.py b/conf_icigaweb2.py new file mode 100644 index 0000000..50323a7 --- /dev/null +++ b/conf_icigaweb2.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +import configparser +import os +import MySQLdb +import subprocess +from time import sleep + +class IniFile: + def __init__(self, file: str, prefix: str): + """class for edit a ini file + + Args: + file (str): path to ini file to edit + prefix (str): prefix of env vars for this file + """ + self.file = file + self.prefix = prefix + self.config = configparser.ConfigParser() + + if os.path.exists(self.file): + self.config.read(self.file) + + self.get_env_vars_with_prefix() + self.save_changes() + + def get_env_vars_with_prefix(self): + """read the environment and get all env start with prefix""" + env_vars = {key: value for key, value in os.environ.items() if key.startswith(self.prefix)} + for env_var, value in env_vars.items(): + tmp_list = env_var.split('.') + if len(tmp_list) == 3: + _, section, key = tmp_list + self.edit_ini(section, key, value) + + def edit_ini(self, section: str, key: str, value: str): + """edit a single key / value of a section in ini file + + Args: + section (str): section to edit or create + key (str): key to edit or create in section + value (str): value to set in key + """ + if section not in self.config: + self.config[section] = {} + + self.config[section][key] = value + + def save_changes(self): + """write the changes in file""" + with open(self.file, 'w') as configfile: + self.config.write(configfile) + + +class InitDB: + def __init__(self): + ## check if all needes env vars a set + required_env_vars = [ 'INIT_DB', 'ICINGAWEB2_DB_RESOURCE_NAME', + 'ICINGAWEB2_DEFAULT_ADMIN_USER', 'ICINGAWEB2_DEFAULT_ADMIN_PASS' ] + if all(key in os.environ.keys() for key in required_env_vars): + prefix = 'ICINGAWEB2_RESOURCES.' + os.environ['ICINGAWEB2_DB_RESOURCE_NAME'] + self.host = os.environ[prefix + '.host'] # not check if exists + self.user = os.environ[prefix + '.username'] # not check if exists + self.passwd = os.environ[prefix + '.password'] # not check if exists + self.dbname = os.environ[prefix + '.dbname'] # not check if exists + self.default_admin_user = os.environ['ICINGAWEB2_DEFAULT_ADMIN_USER'] + self.default_admin_pass = os.environ['ICINGAWEB2_DEFAULT_ADMIN_PASS'] + + self.conn = self.db_conn() + self.init_db() + self.create_inital_admin() + self.conn.close() + + + def db_conn(self): + max_trys = 7 + trys = 0 + while trys < max_trys: + trys += 1 + try: + conn = MySQLdb.connect( + host = self.host, db = self.dbname, + user = self.user, passwd = self.passwd ) + except MySQLdb._exceptions.OperationalError: + print(f'can not connect the database {trys=} of {max_trys=}') + sleep(5) + else: + print('connect to db successfully') + return conn + else: + print('no connection to database, exit!') + exit() + + def init_db(self): + with open('/usr/share/icingaweb2/schema/mysql.schema.sql', "r", encoding="utf-8") as f: + sql_script = f.read() + with self.conn.cursor() as cursor: + for statement in sql_script.split(";"): + statement = statement.strip() + if statement: # Leere Statements überspringen + cursor.execute(statement) + self.conn.commit() + + def create_inital_admin(self): + php_command = f'php -r \'echo password_hash("{self.default_admin_pass}", PASSWORD_DEFAULT);\'' + result = subprocess.run(php_command, shell=True, capture_output=True, text=True) + hashed_pass = result.stdout.strip() + with self.conn.cursor() as cursor: + cursor.execute(""" + INSERT INTO icingaweb_user (name, active, password_hash) VALUES (%s, '1', %s); + """, (self.default_admin_user, hashed_pass)) + self.conn.commit() + +def main(): + ## icingaweb2 config + ICINGAWEB_CONFDIR = '/etc/icingaweb2/' + ### config.ini : https://icinga.com/docs/icinga-web/latest/doc/03-Configuration/#configuration-general + IniFile(ICINGAWEB_CONFDIR + 'config.ini', 'ICINGAWEB2_CONF.') + ### resources.ini : https://icinga.com/docs/icinga-web/latest/doc/04-Resources/#resources + IniFile(ICINGAWEB_CONFDIR + 'resources.ini', 'ICINGAWEB2_RESOURCES.') + ### authentication.ini : https://icinga.com/docs/icinga-web/latest/doc/05-Authentication/#authentication + IniFile(ICINGAWEB_CONFDIR + 'authentication.ini', 'ICINGAWEB2_AUTH.') + ### groups.ini : https://icinga.com/docs/icinga-web/latest/doc/05-Authentication/#groups + IniFile(ICINGAWEB_CONFDIR + 'groups.ini', 'ICINGAWEB2_GROUPS.') + ### roles.ini : https://icinga.com/docs/icinga-web/latest/doc/06-Security/#roles + IniFile(ICINGAWEB_CONFDIR + 'roles.ini', 'ICINGAWEB2_ROLES.') + + ## icingaweb2 modul: icingadb web config + ICINGADB_WEB_CONFDIR = ICINGAWEB_CONFDIR + 'modules/icingadb/' + ### config.ini : https://icinga.com/docs/icinga-db-web/latest/doc/03-Configuration/#general-configuration + IniFile(ICINGADB_WEB_CONFDIR + 'config.ini', 'ICINGAWEB2_ICINGADB_CONF.') + ### redis.ini : https://icinga.com/docs/icinga-db-web/latest/doc/03-Configuration/#redis-configuration + IniFile(ICINGADB_WEB_CONFDIR + 'redis.ini', 'ICINGAWEB2_ICINGADB_REDIS.') + ### commandtransports.ini : https://icinga.com/docs/icinga-db-web/latest/doc/03-Configuration/#command-transport-configuration + IniFile(ICINGADB_WEB_CONFDIR + 'commandtransports.ini', 'ICINGAWEB2_ICINGADB_API.') + + ## run db init + InitDB() + +if __name__ == '__main__': + main() diff --git a/init.sh b/init.sh new file mode 100644 index 0000000..7bf61c8 --- /dev/null +++ b/init.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +ICINGAWEB2_FILE_APACHE_DEFAULT=/etc/apache2/sites-available/000-default.conf +ICINGAWEB2_FILE_APACHE_PORTS=/etc/apache2/ports.conf +ICINGAWEB2_PORT=8080 + +## init icinga config files if need +if [ -z "$(ls -A /data)" ]; then + ICINGAWEB2_CONF_ONCE=false + export INIT_DB=true + + ## move init files + echo init icingaweb2 config + cp -ax /data-init/* /data + rm -rf /data-init + + ## enable modules + icingacli module enable icingadb + mkdir -p /etc/icingaweb2/modules/icingadb/ +fi + +## apache setup +if [ ! -z "$ICINGAWEB2_PORT" ]; then + echo set icingaweb2 apache ports + sed -i "s|^Listen .*|Listen $ICINGAWEB2_PORT|" $ICINGAWEB2_FILE_APACHE_PORTS + sed -i "s|VirtualHost \*:.*>|VirtualHost \*:$ICINGAWEB2_PORT>|g" $ICINGAWEB2_FILE_APACHE_DEFAULT +fi + +## edit icingaweb2 config only on first setup or is enabled +if [ ! $ICINGAWEB2_CONF_ONCE ] || [ "$ICINGAWEB2_CONF_ONCE" = false ]; then + /usr/bin/python3 /root/conf_icingaweb2.py +fi + +## init apache +. /etc/apache2/envvars + +# run CMD +exec "$@" \ No newline at end of file