diff --git a/Dockerfile b/Dockerfile index d20514a..fb3b601 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,65 +7,55 @@ COPY prebuildfs / SHELL ["/bin/bash", "-o", "pipefail", "-c"] RUN install_packages ca-certificates git supervisor gettext-base nginx -RUN mkdir -p mkdir /etc/privacyidea/data/keys \ +# Create directories and user for PrivacyIdea and set ownership +RUN mkdir -p /data/privacyidea/keys \ /var/log/privacyidea && \ - adduser --gecos "PrivacyIdea User" --disabled-password --home /home/privacyidea privacyidea --uid 1001 && \ + adduser --gecos "PrivacyIdea User" \ + --disabled-password \ + --home /home/privacyidea \ + --uid 1001 \ + privacyidea && \ addgroup privacyidea privacyidea && \ usermod -g 1001 privacyidea && \ - chown -R privacyidea:privacyidea /etc/privacyidea /var/log/privacyidea - -COPY rootfs / - -# Which uWSGI .ini file should be used, to make it customizable -ENV UWSGI_INI /etc/uwsgi/uwsgi.ini - -# By default, run 2 processes -ENV UWSGI_CHEAPER 2 - -# By default, when on demand, run up to 16 processes -ENV UWSGI_PROCESSES 16 - -# By default, allow unlimited file sizes, modify it to limit the file sizes -# To have a maximum of 1 MB (Nginx's default) change the line to: -# ENV NGINX_MAX_UPLOAD 1m -ENV NGINX_MAX_UPLOAD 1m - -# By default, Nginx will run a single worker process, setting it to auto -# will create a worker for each CPU core -ENV NGINX_WORKER_PROCESSES 1 - -# By default, NGINX show NGINX version on error page and HTTP header -ENV NGINX_SERVER_TOKENS 'off' - -ENV NGINX_WORKER_CONNECTIONS 1024 - -# By default, Nginx listens on port 80. -# To modify this, change LISTEN_PORT environment variable. -# (in a Dockerfile or with an option for `docker run`) -ENV NGINX_LISTEN_PORT 80 -ENV NGINX_LISTEN_SSL_PORT 443 - -ENV NGINX_SSL_ENABLED true - -ENV PI_SKIP_BOOTSTRAP=false \ + chown -R privacyidea:privacyidea /var/log/privacyidea /data/privacyidea + +# Set environment variables for uWSGI and Nginx +ENV UWSGI_INI=/etc/uwsgi/uwsgi.ini \ + UWSGI_CHEAPER=2 \ + UWSGI_PROCESSES=16 \ + NGINX_MAX_UPLOAD=1m \ + NGINX_WORKER_PROCESSES=auto \ + NGINX_SERVER_TOKENS=off \ + NGINX_WORKER_CONNECTIONS=1024 \ + NGINX_LISTEN_PORT=80 \ + NGINX_LISTEN_SSL_PORT=443 \ + NGINX_SSL_ENABLED=true \ + PI_SKIP_BOOTSTRAP=false \ DB_VENDOR=sqlite \ PI_HOME=/opt/privacyidea \ VIRTUAL_ENV=/opt/privacyidea -RUN python3 -m venv $VIRTUAL_ENV - +# Set environment variables for Python ENV PATH="$VIRTUAL_ENV/bin:$PATH" -ARG PI_VERSION=3.8 +# Set the PrivacyIdea version to install +ARG PI_VERSION=3.8.1 -RUN pip3 install wheel && \ +# Create a virtual environment for PrivacyIdea and install its dependencies +RUN python3 -m venv $VIRTUAL_ENV && \ + pip3 install wheel && \ pip3 install uwsgi pymysql-sa PyMySQL psycopg2-binary && \ pip3 install -r https://raw.githubusercontent.com/privacyidea/privacyidea/v${PI_VERSION}/requirements.txt && \ pip3 install git+https://github.com/privacyidea/privacyidea.git@v${PI_VERSION} +# Copy the rootfs directory to the root of the container filesystem +COPY rootfs / + +# Expose ports 80 and 443 EXPOSE 80/tcp EXPOSE 443/tcp +# Set the entrypoint to the privacyidea_entrypoint.sh script ENTRYPOINT ["/usr/local/bin/privacyidea_entrypoint.sh"] WORKDIR /opt/privacyidea diff --git a/Makefile b/Makefile index 3b7e8bd..598f744 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,11 @@ -info: - @echo "build build the privacyidea image" - @echo "push push the image to the docker hub" - LOCAL_DATA_VOLUME=/tmp/privacyidea-data +help: ## Show this help message + @echo "Usage: make [target]" + @echo "" + @echo "Targets:" + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " %-30s %s\n", $$1, $$2}' | sort + build: ## Build image docker build -t khalibre/privacyidea:dev . @@ -25,9 +27,11 @@ pipepper: @echo PI_PEPPER=$(shell cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) > pipepper cleanup: - docker stop privacyidea-dev || true - docker rm privacyidea-dev || true - sudo rm -rf $(LOCAL_DATA_VOLUME) + @if docker ps -a | grep -q privacyidea-dev; then docker stop privacyidea-dev || true; fi + @if docker ps -a | grep -q privacyidea-dev; then docker rm privacyidea-dev || true; fi + @if [ -d $(LOCAL_DATA_VOLUME) ]; then sudo rm -rf $(LOCAL_DATA_VOLUME); fi test: container-structure-test test --image khalibre/privacyidea:dev --config structure-tests.yaml + +.DEFAULT_GOAL := help diff --git a/README.md b/README.md index efa17df..332a8fd 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ The Khalibre privacyIDEA container can create a default admin user by setting th The Khalibre privacyIDEA requires a database to work. This is configured with the following environment variables: - - `DB_VENDOR`: Database vendor (support mysql, mariadb or posgresql) No defaults. + - `DB_VENDOR`: Database vendor (support mysql, mariadb or posgresql) Default **sqlite**. - `DB_USER`: Database user. No defaults. - `DB_PASSWORD`: Database. No defaults. - `DB_NAME`: Database name. No defaults. @@ -50,12 +50,12 @@ The Khalibre privacyIDEA requires a database to work. This is configured with th ### NGINX configuration - - `NGINX_LISTEN_PORT`: Get the listen port for Nginx, default to 80 - - `NGINX_LISTEN_SSL_PORT`: Get the secured listen port for Nginx, default to 443 - - `NGINX_MAX_UPLOAD`: Get the maximum upload file size for Nginx, default to 100Mb - - `NGINX_SERVER_TOKENS`: Hide Nginx server version on error pages and in the “Server HTTP” response header field - - `NGINX_SSL_CERT`: Path to SSL certificate, default to **/etc/nginx/certs/pi-server-cert.pem** - - `NGINX_SSL_ENABLED`: Set to true to enable SSL, default **false** + - `NGINX_LISTEN_PORT`: Get the listen port for Nginx, default to **80** + - `NGINX_LISTEN_SSL_PORT`: Get the secured listen port for Nginx, default to **443** + - `NGINX_MAX_UPLOAD`: Get the maximum upload file size for Nginx, default to **100Mb** + - `NGINX_SERVER_TOKENS`: Hide Nginx server version on error pages and in the “Server HTTP” response header field. Default **off** + - `NGINX_SSL_CERT`: Path to SSL certificate. Default to **/etc/nginx/certs/pi-server-cert.pem** + - `NGINX_SSL_ENABLED`: Set to true to enable SSL, Default **false** - `NGINX_SSL_KEY`: Path to SSL key, default **/etc/nginx/certs/pi-server-key.pem** - `NGINX_WORKER_CONNECTIONS`: Set the max number of connections per worker for Nginx, if requested. - `NGINX_WORKER_PROCESSES`: Get the number of workers for Nginx, default to 1 @@ -64,8 +64,9 @@ The Khalibre privacyIDEA requires a database to work. This is configured with th - `CACHE_TYPE`: privacyIDEA cache type. Default simple. - `PI_PEPPER`: This is used to encrypt the admin passwords. No defaults. - - `PI_AUDIT_KEY_PRIVATE`: This is used to sign the audit log - - `PI_AUDIT_KEY_PUBLIC`: This is used to sign the audit log + - `PI_AUDIT_NO_SIGN`: If you by any reason want to avoid signing audit entries set it **true**. + - `PI_AUDIT_KEY_PRIVATE_PATH`: This is used to sign the audit log + - `PI_AUDIT_KEY_PUBLIC_PATH`: This is used to sign the audit log - `PI_ENCFILE`: This is used to encrypt the token data and token passwords. No defaults. - `PI_HSM`: privacyIDEA HSM. Default **default** - `PI_LOGFILE`: privacyIDEA log file location. Default **/var/log/privacyidea/privacyidea.log** diff --git a/prebuildfs/usr/sbin/install_packages b/prebuildfs/usr/sbin/install_packages index c957764..89d4cd8 100755 --- a/prebuildfs/usr/sbin/install_packages +++ b/prebuildfs/usr/sbin/install_packages @@ -1,24 +1,34 @@ #!/bin/sh + +# Install packages with retry logic set -e set -u export DEBIAN_FRONTEND=noninteractive + +# Update package index +apt-get update -qq + +# Install packages with retry logic n=0 max=2 until [ $n -gt $max ]; do - set +e - ( - apt-get update -qq && - apt-get install -y --no-install-recommends "$@" - ) - CODE=$? - set -e - if [ $CODE -eq 0 ]; then - break - fi - if [ $n -eq $max ]; then - exit $CODE - fi - echo "apt failed, retrying" - n=$(($n + 1)) + set +e + ( + apt-get install -y --no-install-recommends "$@" + ) + CODE=$? + set -e + if [ $CODE -eq 0 ]; then + break + fi + if [ $n -eq $max ]; then + exit $CODE + fi + echo "apt failed, retrying in 10 seconds..." + sleep 10 + n=$(($n + 1)) done + +# Clean up after installation +apt-get autoremove -y rm -r /var/lib/apt/lists /var/cache/apt/archives diff --git a/rootfs/opt/templates/nginx.conf.template b/rootfs/opt/templates/nginx.conf.template index d0978bb..ef09110 100644 --- a/rootfs/opt/templates/nginx.conf.template +++ b/rootfs/opt/templates/nginx.conf.template @@ -1,4 +1,3 @@ -user www-data; worker_processes $NGINX_WORKER_PROCESSES; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; diff --git a/rootfs/opt/templates/pi-config.template b/rootfs/opt/templates/pi-config.template index 314f1c7..2c3e540 100644 --- a/rootfs/opt/templates/pi-config.template +++ b/rootfs/opt/templates/pi-config.template @@ -14,11 +14,14 @@ if PI_PEPPER is None: # The realm, where users are allowed to login as administrators SUPERUSER_REALM = os.environ.get('SUPERUSER_REALM', ['administrator']) SQLALCHEMY_DATABASE_URI = "$SQLALCHEMY_DATABASE_URI" -PI_ENCFILE = os.environ.get("PI_ENCFILE", "/data/privacyidea/encfile") +PI_ENCFILE = os.environ.get("PI_ENCFILE", "/data/privacyidea/keys/encfile") PI_HSM = os.environ.get("PI_HSM", "default") +PI_AUDIT_NO_SIGN = os.environ.get("PI_AUDIT_NO_SIGN", "False").lower() == "true" PI_AUDIT_MODULE = os.environ.get("PI_AUDIT_MODULE", "privacyidea.lib.auditmodules.sqlaudit") -PI_AUDIT_KEY_PRIVATE = os.environ.get("PI_AUDIT_KEY_PRIVATE", "/etc/privacyidea/data/keys/private.pem") -PI_AUDIT_KEY_PUBLIC = os.environ.get("PI_AUDIT_KEY_PUBLIC", "/etc/privacyidea/data/keys/public.pem") +# PI_AUDIT_KEY_PRIVATE will be used only when PI_AUDIT_NO_SIGN is True +PI_AUDIT_KEY_PRIVATE = os.environ.get("PI_AUDIT_KEY_PRIVATE_PATH", "/data/privacyidea/keys/private.pem") +# PI_AUDIT_KEY_PUBLIC will be used only when PI_AUDIT_NO_SIGN is True +PI_AUDIT_KEY_PUBLIC = os.environ.get("PI_AUDIT_KEY_PUBLIC_PATH", "/data/privacyidea/keys/public.pem") PI_LOGFILE = os.environ.get("PI_LOGFILE", "/var/log/privacyidea/privacyidea.log") PI_LOGLEVEL = logging.getLevelName(os.environ.get("PI_LOGLEVEL", "INFO")) PI_NODE = os.environ.get("HOSTNAME", "localnode") diff --git a/rootfs/usr/local/bin/_privacyidea_common.sh b/rootfs/usr/local/bin/_privacyidea_common.sh index 79d8a8e..cb75832 100755 --- a/rootfs/usr/local/bin/_privacyidea_common.sh +++ b/rootfs/usr/local/bin/_privacyidea_common.sh @@ -1,18 +1,19 @@ #!/bin/bash -function execute_scripts { - if [ -e "${1}" ] && [[ $(find "${1}" -maxdepth 1 -name "*.sh" -printf "%f\n") ]] - then - echo "[PrivacyIDEA] Executing scripts in ${1}:" +execute_scripts() { + local script_dir="$1" + local script_names - for SCRIPT_NAME in $(find "${1}" -maxdepth 1 -name "*.sh" -printf "%f\n" | sort) - do - echo "" - echo "[PrivacyIDEA] Executing ${SCRIPT_NAME}." + if [[ -d "$script_dir" ]] && script_names=("$script_dir"/*.sh); (( ${#script_names[@]} )); then + echo "[PrivacyIDEA] Executing scripts in $script_dir:" - source "${1}/${SCRIPT_NAME}" - done + for script_path in "${script_names[@]}"; do + local script_name=$(basename "$script_path") + echo "" + echo "[PrivacyIDEA] Executing $script_name." + source "$script_path" || { echo "[PrivacyIDEA] Error: Failed to execute $script_name."; return 1; } + done - echo "" - fi + echo "" + fi } diff --git a/rootfs/usr/local/bin/configure_nginx.sh b/rootfs/usr/local/bin/configure_nginx.sh index e783b97..d86fe39 100755 --- a/rootfs/usr/local/bin/configure_nginx.sh +++ b/rootfs/usr/local/bin/configure_nginx.sh @@ -2,96 +2,82 @@ set -e -function main { +function generate_cert { - envsubst < /opt/templates/nginx.conf.template > /etc/nginx/nginx.conf - envsubst < /opt/templates/nginx-pi.conf.template > /etc/nginx/conf.d/pi.conf - if [ "$NGINX_SSL_ENABLED" = true ]; then - if [ -z "$NGINX_SSL_CERT" ] && [ -z "$NGINX_SSL_CERT" ]; - then - echo "SSL enabled but NGINX_SSL_CERT and NGINX_SSL_KEY are not defined, using generated certifiacate" - echo "Generate self signed certificate" - generate_cert - echo "" - echo "Finished generate certificates" + # Create certificate directory + mkdir -p /etc/nginx/certs - export NGINX_SSL_CERT=/etc/nginx/certs/pi-server-cert.pem - export NGINX_SSL_KEY=/etc/nginx/certs/pi-server-key.pem - fi - envsubst < /opt/templates/nginx-pi-ssl.conf.template > /etc/nginx/conf.d/pi-ssl.conf - fi + # [ global parameters ] + # certificate configuration + readonly CERT_DAYS=36500 + readonly RSA_STR_LEN=4096 + readonly PREFIX=pi- + readonly CERT_DIR=/etc/nginx/certs + readonly KEY_DIR=/etc/nginx/certs + # certificate content definition + readonly ADDRESS_COUNTRY_CODE=KH + readonly ADDRESS_PREFECTURE=PI + readonly ADDRESS_CITY='Phnom Penh' + readonly COMPANY_NAME=Khalibre + readonly COMPANY_SECTION=DevOps + readonly CERT_PASSWORD= # no password + # - server + readonly SERVER_DOMAIN=localhost + readonly SERVER_EMAIL=server@email.address -} + # [ functions ] + echo_cert_params() { + local company_domain="$1" + local company_email="$2" -function generate_cert { - - # Create certificate directory - mkdir -p /etc/nginx/certs + echo $ADDRESS_COUNTRY_CODE + echo $ADDRESS_PREFECTURE + echo $ADDRESS_CITY + echo $COMPANY_NAME + echo $COMPANY_SECTION + echo $company_domain + echo $company_email + echo $CERT_PASSWORD # password + echo $CERT_PASSWORD # password (again) + } + echo_server_cert_params() { + echo_cert_params "$SERVER_DOMAIN" "$SERVER_EMAIL" + } - # [ global parameters ] - # certificate configuration - readonly CERT_DAYS=36500 - readonly RSA_STR_LEN=4096 - readonly PREFIX=pi- - readonly CERT_DIR=/etc/nginx/certs - readonly KEY_DIR=/etc/nginx/certs - # certificate content definition - readonly ADDRESS_COUNTRY_CODE=KH - readonly ADDRESS_PREFECTURE=PI - readonly ADDRESS_CITY='Phnom Penh' - readonly COMPANY_NAME=Khalibre - readonly COMPANY_SECTION=DevOps - readonly CERT_PASSWORD= # no password - # - ca - readonly CA_DOMAIN='Khalibre DevOps' - readonly CA_EMAIL=ca@email.address + # [ main ] + # generate certificates # - server - readonly SERVER_DOMAIN=localhost - readonly SERVER_EMAIL=server@email.address - - # [ functions ] - echo_cert_params() { - local company_domain="$1" - local company_email="$2" + echo_server_cert_params | \ + openssl req -newkey rsa:$RSA_STR_LEN -days $CERT_DAYS -nodes -keyout $KEY_DIR/${PREFIX}server-key.pem -out $CERT_DIR/${PREFIX}server-req.pem > /dev/null + openssl rsa -in $KEY_DIR/${PREFIX}server-key.pem -out $KEY_DIR/${PREFIX}server-key.pem > /dev/null + openssl x509 -req -in $CERT_DIR/${PREFIX}server-req.pem -days $CERT_DAYS -signkey $KEY_DIR/${PREFIX}server-key.pem -out $CERT_DIR/${PREFIX}server-cert.pem > /dev/null - echo $ADDRESS_COUNTRY_CODE - echo $ADDRESS_PREFECTURE - echo $ADDRESS_CITY - echo $COMPANY_NAME - echo $COMPANY_SECTION - echo $company_domain - echo $company_email - echo $CERT_PASSWORD # password - echo $CERT_PASSWORD # password (again) - } - echo_ca_cert_params() { - echo_cert_params "$CA_DOMAIN" "$CA_EMAIL" - } - echo_server_cert_params() { - echo_cert_params "$SERVER_DOMAIN" "$SERVER_EMAIL" - } + # clean up (before permission changed) + rm $CERT_DIR/${PREFIX}server-req.pem > /dev/null - # [ main ] - # generate certificates - # - ca - openssl genrsa $RSA_STR_LEN > $KEY_DIR/${PREFIX}ca-key.pem - echo_ca_cert_params | \ - openssl req -new -x509 -nodes -days $CERT_DAYS -key $KEY_DIR/${PREFIX}ca-key.pem -out $CERT_DIR/${PREFIX}ca-cert.pem - # - server - echo_server_cert_params | \ - openssl req -newkey rsa:$RSA_STR_LEN -days $CERT_DAYS -nodes -keyout $KEY_DIR/${PREFIX}server-key.pem -out $CERT_DIR/${PREFIX}server-req.pem - openssl rsa -in $KEY_DIR/${PREFIX}server-key.pem -out $KEY_DIR/${PREFIX}server-key.pem - openssl x509 -req -in $CERT_DIR/${PREFIX}server-req.pem -days $CERT_DAYS -CA $CERT_DIR/${PREFIX}ca-cert.pem -CAkey $KEY_DIR/${PREFIX}ca-key.pem -set_serial 01 -out $CERT_DIR/${PREFIX}server-cert.pem + # validate permission + chmod 400 $KEY_DIR/${PREFIX}server-key.pem > /dev/null - # clean up (before permission changed) - rm $KEY_DIR/${PREFIX}ca-key.pem - rm $CERT_DIR/${PREFIX}server-req.pem + # verify certificate + openssl x509 -in $CERT_DIR/${PREFIX}server-cert.pem -noout -text > /dev/null +} - # validate permission - chmod 400 $KEY_DIR/${PREFIX}server-key.pem +function main { + # Generate certificate if SSL is enabled and no certificate/key paths are provided + if [ "$NGINX_SSL_ENABLED" = true ]; then + if [ -z "$NGINX_SSL_CERT" ] && [ -z "$NGINX_SSL_KEY" ]; + then + echo "SSL enabled but NGINX_SSL_CERT and NGINX_SSL_KEY are not defined, using generated certificate" + generate_cert + export NGINX_SSL_CERT=/etc/nginx/certs/pi-server-cert.pem + export NGINX_SSL_KEY=/etc/nginx/certs/pi-server-key.pem + fi + envsubst < /opt/templates/nginx-pi-ssl.conf.template > /etc/nginx/conf.d/pi-ssl.conf + fi - # verify relationship among certificates - openssl verify -CAfile $CERT_DIR/${PREFIX}ca-cert.pem $CERT_DIR/${PREFIX}server-cert.pem - } + # Substitute environment variables in nginx configuration files + envsubst < /opt/templates/nginx.conf.template > /etc/nginx/nginx.conf + envsubst < /opt/templates/nginx-pi.conf.template > /etc/nginx/conf.d/pi.conf +} main diff --git a/rootfs/usr/local/bin/privacyidea_entrypoint.sh b/rootfs/usr/local/bin/privacyidea_entrypoint.sh index 45c3cc2..c0d1cc5 100755 --- a/rootfs/usr/local/bin/privacyidea_entrypoint.sh +++ b/rootfs/usr/local/bin/privacyidea_entrypoint.sh @@ -1,33 +1,44 @@ #!/bin/bash +# This script sets up and starts PrivacyIDEA within a Docker container. + +# Source the common functions used by PrivacyIDEA scripts. source /usr/local/bin/_privacyidea_common.sh function main { + # Print a message indicating how to SSH into the container. echo "[PrivacyIDEA] To SSH into this container, run: \"docker exec -it ${HOSTNAME} /bin/bash\"." echo "" + # Set the mount directory for configuration files. if [ -d /etc/privacyidea/mount ] then PI_MOUNT_DIR=/etc/privacyidea/mount else PI_MOUNT_DIR=/mnt/privacyidea fi - export PI_MOUNT_DIR + # Execute any pre-configuration scripts. execute_scripts /usr/local/privacyidea/scripts/pre-configure + # Configure Nginx. . configure_nginx.sh + # Execute any pre-startup scripts. execute_scripts /usr/local/privacyidea/scripts/pre-startup + # Start PrivacyIDEA. start_privacyidea + # Execute any post-shutdown scripts. execute_scripts /usr/local/privacyidea/scripts/post-shutdown } +# Define the function to start PrivacyIDEA. function start_privacyidea { . start_privacyidea.sh } +# Call the main function. main diff --git a/rootfs/usr/local/bin/start_privacyidea.sh b/rootfs/usr/local/bin/start_privacyidea.sh index 8d0624c..b1144dd 100755 --- a/rootfs/usr/local/bin/start_privacyidea.sh +++ b/rootfs/usr/local/bin/start_privacyidea.sh @@ -15,97 +15,126 @@ function main { function generate_pi_config { - if { [ "${DB_VENDOR}" = "mariadb" ] || [ "${DB_VENDOR}" = "mysql" ]; } then + # Check the selected database vendor + if [ "${DB_VENDOR}" = "mariadb" ] || [ "${DB_VENDOR}" = "mysql" ]; then echo "Using $DB_VENDOR..." + + # Ensure that the necessary variables are defined [ -z "$DB_HOST" ] && echo "DB_HOST should be defined" && return 1 [ -z "$DB_USER" ] && echo "DB_USER should be defined" && return 1 [ -z "$DB_PASSWORD" ] && echo "DB_PASSWORD should be defined" && return 1 [ -z "$DB_NAME" ] && echo "DB_NAME should be defined" && return 1 + + # Set the default port if it is not defined if [ -z "$DB_PORT" ]; then echo DB_PORT is not defined using default port export DB_PORT=3306 fi + + # Define the SQLAlchemy database URI using the necessary variables export SQLALCHEMY_DATABASE_URI=pymysql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME} - elif { [ "${DB_VENDOR}" = "postgresql" ]; } then + + elif [ "${DB_VENDOR}" = "postgresql" ]; then + echo "Using $DB_VENDOR..." + + # Ensure that the necessary variables are defined [ -z "$DB_HOST" ] && echo "DB_HOST should be defined" && return 1 [ -z "$DB_USER" ] && echo "DB_USER should be defined" && return 1 [ -z "$DB_PASSWORD" ] && echo "DB_PASSWORD should be defined" && return 1 [ -z "$DB_NAME" ] && echo "DB_NAME should be defined" && return 1 + + # Set the default port if it is not defined if [ -z "$DB_PORT" ]; then echo DB_PORT is not defined using default port export DB_PORT=5432 fi + + # Define the SQLAlchemy database URI using the necessary variables export SQLALCHEMY_DATABASE_URI=postgresql+psycopg2://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME} + else - echo "DB_VENDOR enviroment varaible is not set. Using default SQLite..." + echo "DB_VENDOR environment variable is not set. Using default SQLite..." echo "" - export SQLALCHEMY_DATABASE_URI=sqlite:////etc/privacyidea/data/privacyidea.db + + # Define the SQLAlchemy database URI for SQLite + export SQLALCHEMY_DATABASE_URI=sqlite://///data/privacyidea/privacyidea.db fi - if [ ! -f /etc/privacyidea/pi.cfg ]; - then - if [ -z "$SQLALCHEMY_DATABASE_URI" ]; - then - echo "SQLALCHEMY_DATABASE_URI is undefieded" + # Check if the configuration file already exists + if [ ! -f /data/privacyidea/pi.cfg ]; then + + # Check if SQLALCHEMY_DATABASE_URI is defined + if [ -z "$SQLALCHEMY_DATABASE_URI" ]; then + echo "SQLALCHEMY_DATABASE_URI is undefined" else - envsubst < /opt/templates/pi-config.template > /etc/privacyidea/pi.cfg + # Use the pi-config.template file as a template and substitute the necessary variables + envsubst < /opt/templates/pi-config.template > /data/privacyidea/pi.cfg fi fi + } function prestart_privacyidea { - if [ -d "${PI_MOUNT_DIR}"/files ] - then - if [[ $(ls -A "${PI_MOUNT_DIR}"/files) ]] - then - echo "[privacyIDEA] Copying files from ${PI_MOUNT_DIR}/files:" - echo "" - - tree --noreport "${PI_MOUNT_DIR}"/files - - echo "" - echo "[privacyIDEA] ... into ${PI_HOME}." - - cp -r "${PI_MOUNT_DIR}"/files/* "${PI_HOME}" - - echo "" - fi + # Copy files from mounted directory to PI_HOME + if [ -d "${PI_MOUNT_DIR}/files" ] && [ "$(ls -A "${PI_MOUNT_DIR}/files")" ]; then + echo "[privacyIDEA] Copying files from ${PI_MOUNT_DIR}/files:" + echo "" + tree --noreport "${PI_MOUNT_DIR}/files" + echo "" + echo "[privacyIDEA] ... into ${PI_HOME}." + cp -r "${PI_MOUNT_DIR}/files"/* "${PI_HOME}" + echo "" else - echo "[privacyIDEA] The directory /mnt/privacyidea/files does not exist. Create the directory \$(pwd)/xyz123/files on the host operating system to create the directory ${PI_MOUNT_DIR}/files on the container. Files in ${PI_MOUNT_DIR}/files will be copied to ${PI_HOME} before privacyIDEA starts." + echo "[privacyIDEA] The directory ${PI_MOUNT_DIR}/files does not exist or is empty. Copy any files to this directory to have them copied to ${PI_HOME} before privacyIDEA starts." echo "" fi - if [ -d "${PI_MOUNT_DIR}"/scripts ] - then - execute_scripts "${PI_MOUNT_DIR}"/scripts - else - echo "[privacyIDEA] The directory /mnt/privacyidea/scripts does not exist. Create the directory \$(pwd)/xyz123/scripts on the host operating system to create the directory ${PI_MOUNT_DIR}/scripts on the container. Files in ${PI_MOUNT_DIR}/scripts will be executed, in alphabetical order, before privacyIDEA starts." + # Execute scripts from mounted directory + if [ -d "${PI_MOUNT_DIR}/scripts" ]; then + execute_scripts "${PI_MOUNT_DIR}/scripts" + else + echo "[privacyIDEA] The directory ${PI_MOUNT_DIR}/scripts does not exist. Copy any scripts to this directory to have them executed, in alphabetical order, before privacyIDEA starts." echo "" fi + # Generate keys, create tables, and admin user if [ "${PI_SKIP_BOOTSTRAP}" = false ]; then - ls -l /data - ls -l /data/privacyidea - if [ ! -f /etc/privacyidea/encfile ]; then - pi-manage create_enckey + + # Create keys directory if not exists + if [ ! -d /data/privacyidea/keys ]; then + echo "Creating keys directory..." + mkdir /data/privacyidea/keys fi - if [ ! -d /etc/privacyidea/keys ]; then - mkdir /etc/privacyidea/keys + + # Create encryption key file if not exists + if [ ! -f /data/privacyidea/keys/encfile ]; then + echo "Encryption key file not found, creating a new one..." + pi-manage create_enckey fi - if [ ! -f /etc/privacyidea/keys/private.pem ]; then + + # Create audit keys if not exists + if [ ! -f /data/privacyidea/keys/private.pem ]; then + echo "Creating audit keys..." pi-manage create_audit_keys fi - pi-manage createdb - pi-manage db stamp head -d /opt/privacyidea/lib/privacyidea/migrations/ - if { [ "${PI_SKIP_BOOTSTRAP}" = false ] && [ -z ${PI_ADMIN_USER} ] && [ -z ${PI_ADMIN_PASSWORD} ]; } then - echo "Create deafult admin user. Not recommented in production. Please set PI_ADMIN_USER and PI_ADMIN_PASSWORD in production enviroment." + + # Create database tables + echo "Generating privacyIDEA database tables..." + pi-manage create_tables + + # Create admin user if not specified through environment variables + if [ -z "${PI_ADMIN_USER}" ] || [ -z "${PI_ADMIN_PASSWORD}" ]; then + echo "Creating default admin user. WARNING: This is not recommended for production environments. Please set PI_ADMIN_USER and PI_ADMIN_PASSWORD environment variables to specify the admin user in production." pi-manage admin add admin -p privacyidea else - echo "Create admin user from definded enviroment variables." - pi-manage admin add ${PI_ADMIN_USER} -p ${PI_ADMIN_PASSWORD} + echo "Creating admin user from specified environment variables..." + pi-manage admin add "${PI_ADMIN_USER}" -p "${PI_ADMIN_PASSWORD}" fi + else + echo "Skipping key generation, table creation, and admin user creation." fi } + main diff --git a/structure-tests.yaml b/structure-tests.yaml index da48a20..3baf1ea 100644 --- a/structure-tests.yaml +++ b/structure-tests.yaml @@ -11,7 +11,7 @@ metadataTest: - key: NGINX_MAX_UPLOAD value: "1m" - key: NGINX_WORKER_PROCESSES - value: "1" + value: "auto" - key: NGINX_SERVER_TOKENS value: "off" - key: NGINX_WORKER_CONNECTIONS @@ -41,6 +41,6 @@ fileExistenceTests: - name: 'UWSGI config' path: '/etc/uwsgi/uwsgi.ini' shouldExist: true -- name: 'Tempalte directory' +- name: 'Template directory' path: '/opt/templates' shouldExist: true