Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: Failed to create Certificate Authority. #1025

Open
4 of 5 tasks
Lucenabo opened this issue Jun 21, 2024 · 0 comments
Open
4 of 5 tasks

[Bug]: Failed to create Certificate Authority. #1025

Lucenabo opened this issue Jun 21, 2024 · 0 comments
Assignees
Labels
bug triage Needs triaging

Comments

@Lucenabo
Copy link

Have you already looked into this bug?

(Optional) Error message

httpd_1 | [FAIL] Failed to create Certificate Authority. httpd_1 | [FAIL] ca-gen -v -c DE -s Berlin -l Berlin -o Devilbox -u Devilbox -n 'Devilbox Root CA' -e '[email protected]' "/ca/devilbox-ca.key" "/ca/devilbox-ca.crt" httpd_1 | ca-gen: [INFO] Create CA KEY file: /ca/devilbox-ca.key httpd_1 | ca-gen: [ERR] Command: openssl genrsa -out /ca/devilbox-ca.key 2048 httpd_1 | ca-gen: [ERR] Output: genrsa: Can't open "/ca/devilbox-ca.key" for writing, Permission denied devilbox_httpd_1 exited with code 1

What went wrong?

When I start the docker I get this error and can't see the localhost page

Expected behaviour

I should go to localhost and see the panel.

How can we reproduce the bug?

When I make "sudo docker-compose up" it happens.
Screenshot from 2024-06-21 11-57-48

Host Operating System

Linux

Host Platform (amd64, arm64, other)

amd64

(Linux only) Is SELinux enabled?

Yes, SELinux is enabled

Docker version

Docker version 24.0.5, build %{shortcommit_cli}

Docker Compose version

docker-compose version 1.29.2, build unknown

Devilbox version

986f046

Have you removed stopped containers before starting?

Yes

Have you pulled latest Docker images before starting?

Yes

Devilbox start command

sudo docker-compose up

Config: .env file

###
###  ---------------------------------------------------
###  D E V I L B O X   R U N - T I M E   S E T T I N G S
###  ---------------------------------------------------
###
###  All the following settings are applied during
###  $ docker-compose up
###
###  No need to rebuild any docker images!
###
###  IMPORTANT:
###  ----------
###  When changing any values ensure to stop, rm and restart:
###  $ docker-compose stop
###  $ docker-compose rm -f
###  $ docker-compose up
###
###  NOTE:
###  -----
###  For you own custom variables, scroll to the bottom
###

# The following line will disable any shellcheck warnings throughout this file
# shellcheck disable=SC2034,SC2125


###
### Show all executed commands in each
### docker image during docker-compose up?
###
### 0: Errors
### 1: Errors, Warnings
### 2: Errors, Warnings, Infos  (Recommended)
### 3: Errors, Warnings, Infos, Debug
### 4: Errors, Warnings, Infos, Debug, Trace
DEBUG_ENTRYPOINT=2


###
### Log to file or Docker logs.
###
### Logging to file means log files are available under log/
### on your host operating system.
### Logging to Docker logs means log files are streamed to
### stdout and stderr.
###
### 1: Log to Docker logs
### 0: Log to file
###
DOCKER_LOGS=0


###
### Relative or absolute path to the devilbox repository.
### (Used as a prefix for all mount paths)
### There is no need to change this.
###
### The only exception is for OSX users wanting to use NFS
### mounts instead of Filesystem mounts due to degraded performance
### on OSX.
###
### Note: When changing this variable you must re-create the container.
###       Simply remove it, it will be auto-created during the next start:
###
### $ docker-compose rm -f
###
DEVILBOX_PATH=.


###
### At what IP address should the docker services listen
### on the Host computer?
###
### The specified default should be fine for Linux and OSX (127.0.0.1:).
### If you are on windows, you will probably have to change
### it to the IP address of the docker machine.
###
### a.) Leave blank, to listen on all interfaces (no trailing colon ':')
###    LOCAL_LISTEN_ADDR=
### b.) If an IP is specified, note the trailing colon ':'
###    LOCAL_LISTEN_ADDR=127.0.0.1:
###
LOCAL_LISTEN_ADDR=


###
### This is the domain suffix your projects will be made available
### with mass-virtual-hosting.
### It is also required for the internal DNS server to be setup.
###
### Note: In addition to period or dot character ('.'), only ALPHA ([a-zA-Z]+) characters are supported.
###       Mac users should not use the .local TLD, as this will not resolve properly due Apple's
###       use of Multicast DNS.
###
### Note: If you use 'dvl.to' then there is no need for additional DNS as this always points
###       to 127.0.0.1 by default.
###
### Example:
###   TLD_SUFFIX=loc
### Makes your project available under <project>.loc
###
### Example:
###   TLD_SUFFIX=local
### Makes your project available under <project>.local
###
TLD_SUFFIX=dvl.to


###
### Optional DNS configuration
### Allows you to add extra DNS records (above the wildcard entry)
### Useful if your host computer run other Docker services that you want to connect to or reach
### from within the Devilbox network by a custom hostname.
###
### Format:
### -------
### Resolve any custom defined hostname to an IP address (useable inside container and host os)
###     EXTRA_HOSTS=<hostname>=<ip>[,<hostname>=<ip>]
###
### Resolve any custom defined hostname to whatever IP address a CNAME resolves to
### (Useable inside the container and host OS).
### Note: CNAME must be resolvable by Google DNS
###     EXTRA_HOSTS=<hostname>=<CNAME>[,<hostname>=<CNAME>]
###
### Examples:
### ---------
### EXTRA_HOSTS=hostname.loc=1.2.3.4
### EXTRA_HOSTS=host.loc=1.2.3.4,host.example.org=3.4.5.6
EXTRA_HOSTS=


###
### Set your user id and group id
###
### This should be changed to the value of your local
### users uid and gid
###
### Type `id` on the terminal to find out your values
###
NEW_UID=1000
NEW_GID=1000


###
### Timezone for PHP Docker container (system and php.ini)
###
TIMEZONE=UTC



################################################################################
###
### INTRANET SETTINGS
###
################################################################################

###
### TLD_SUFFIX domains are checked if they are set in the
### host computer /etc/hosts or available via attached DNS server.
### Timeout is done on vhosts.php (intranet) via ajax calls.
### In order to keep performance, set this to a low value.
### DNS checks might not succeed in time on slow machines.
### If DNS is valid, but timeout is expired, set this to a higher value.
###
### DNS_CHECK_TIMEOUT value is how many seconds to time out
### Default is to timeout after 1 second (DNS_CHECK_TIMEOUT=1)
###
DNS_CHECK_TIMEOUT=1


###
### Devilbox UI SSL Certificate generation
###
### When using SSL each certificate requires names for which it is responsible:
### Common Name as well as alternative names.
###
### Specify comma separated hostnames below by which you want to access the Devilbox.
### Those hostnames will be included in the SSL certificate for the Devilbox intranet.
### This has nothing to do for SSL certificates for projects, it is just for the intranet
### itself.
###
DEVILBOX_UI_SSL_CN=localhost,*.localhost,devilbox,*.devilbox,httpd


###
### Devilbox UI Password protection enable/disable (1/0)
###
### Set DEVILBOX_UI_PROTECT to 1 in order to password protect the
### intranet.
###
### Example:
###   DEVILBOX_UI_PROTECT=1
###   DEVILBOX_UI_PROTECT=0
###
DEVILBOX_UI_PROTECT=0


###
### Devilbox UI Password
###
### When DEVILBOX_UI_PROTECT=1, use the following password
### to log in. The password can always be changed.
### When changing the password, make sure to restart your
### PHP container.
###
### Example:
###   DEVILBOX_UI_PASSWORD=my-very-secure-password
###   DEVILBOX_UI_PASSWORD=Some pass with spaces
###
### The default username is 'devilbox'
###
DEVILBOX_UI_PASSWORD=password


###
### Enable the Devilbox Intranet?
###
### Example:
###   DEVILBOX_UI_ENABLE=1
###   DEVILBOX_UI_ENABLE=0
###
DEVILBOX_UI_ENABLE=1


###
### Automatically be logged in into phpMyAdmin
###
### Example:
###   DEVILBOX_VENDOR_PHPMYADMIN_AUTOLOGIN=1
###   DEVILBOX_VENDOR_PHPMYADMIN_AUTOLOGIN=0
###
DEVILBOX_VENDOR_PHPMYADMIN_AUTOLOGIN=1


###
### Automatically be logged in into phpPgAdmin
###
### Example:
###   DEVILBOX_VENDOR_PHPPGADMIN_AUTOLOGIN=1
###   DEVILBOX_VENDOR_PHPPGADMIN_AUTOLOGIN=0
###
DEVILBOX_VENDOR_PHPPGADMIN_AUTOLOGIN=1

###
### HTTPD Supvervisord management
###
DEVILBOX_HTTPD_MGMT_USER=supervisord
DEVILBOX_HTTPD_MGMT_PASS=mypassword



################################################################################
###
### 1. Choose Images (Version)
###
################################################################################

###
### You can choose any combination of httpd, mysql, postgresql or php.
### Each of them are fully compatible between one another.
###


###
### 1.1 Choose PHP Server Image
###
### Note: PHP 5.2 is not officially supported. Intranet won't work (due to lack of namespace support).
###       PHP 5.2 only works with Apache 2.4, Nginx stable and Nginx mainline.
###       Use at your own risk.
###
#PHP_SERVER=5.2
#PHP_SERVER=5.3
#PHP_SERVER=5.4
#PHP_SERVER=5.5
#PHP_SERVER=5.6
#PHP_SERVER=7.0
#PHP_SERVER=7.1
#PHP_SERVER=7.2
#PHP_SERVER=7.3
#PHP_SERVER=7.4
#PHP_SERVER=8.0
PHP_SERVER=8.1
#PHP_SERVER=8.2


###
### 1.2 Choose HTTPD Server Image
###
### Choose between 'debian' or 'alpine' flavour and then select the version
###
### Note: apache-2.2 has no arm64 support on 'alpine' flavour
###
HTTPD_FLAVOUR=alpine

#HTTPD_SERVER=apache-2.2
#HTTPD_SERVER=apache-2.4
HTTPD_SERVER=nginx-stable
#HTTPD_SERVER=nginx-mainline


###
### 1.3 Choose MySQL Server Image
###
#MYSQL_SERVER=mysql-5.5
#MYSQL_SERVER=mysql-5.6
#MYSQL_SERVER=mysql-5.7
#MYSQL_SERVER=mysql-8.0
#MYSQL_SERVER=percona-5.5
#MYSQL_SERVER=percona-5.6
#MYSQL_SERVER=percona-5.7
#MYSQL_SERVER=percona-8.0
#MYSQL_SERVER=mariadb-5.5
#MYSQL_SERVER=mariadb-10.0
#MYSQL_SERVER=mariadb-10.1
#MYSQL_SERVER=mariadb-10.2
#MYSQL_SERVER=mariadb-10.3
#MYSQL_SERVER=mariadb-10.4
#MYSQL_SERVER=mariadb-10.5
MYSQL_SERVER=mariadb-10.6
#MYSQL_SERVER=mariadb-10.7
#MYSQL_SERVER=mariadb-10.8
#MYSQL_SERVER=mariadb-10.9
#MYSQL_SERVER=mariadb-10.10


###
### 1.4 Choose PostgreSQL Server Image
###
### https://www.postgresql.org/support/versioning/
###
### IMPORTANT: Alpine based images might cause issues on Docker Toolbox or OSX
###            https://github.com/docker/toolbox/issues/510
###
#
# PostgreSQL without arm64 support
#
#PGSQL_SERVER=9.0
#PGSQL_SERVER=9.1
#PGSQL_SERVER=9.2-alpine
#
# PostgreSQL with arm64 support
#
#PGSQL_SERVER=9.2
#PGSQL_SERVER=9.3
#PGSQL_SERVER=9.3-alpine
#PGSQL_SERVER=9.4
#PGSQL_SERVER=9.4-alpine
#PGSQL_SERVER=9.5
#PGSQL_SERVER=9.5-alpine
#PGSQL_SERVER=9.6
#PGSQL_SERVER=9.6-alpine
#PGSQL_SERVER=10
#PGSQL_SERVER=10-alpine
#PGSQL_SERVER=11
#PGSQL_SERVER=11-alpine
#PGSQL_SERVER=12
#PGSQL_SERVER=12-alpine
#PGSQL_SERVER=13
#PGSQL_SERVER=13-alpine
#PGSQL_SERVER=14
PGSQL_SERVER=14-alpine
#PGSQL_SERVER=15
#PGSQL_SERVER=15-alpine
#PGSQL_SERVER=latest
#PGSQL_SERVER=alpine


###
### 1.5 Choose Redis Server Image
###
### IMPORTANT: Alpine based images might cause issues on Docker Toolbox or OSX
###            https://github.com/docker/toolbox/issues/510
###
#
# Redis without arm64 support
#
#REDIS_SERVER=2.8
#REDIS_SERVER=3.0
#REDIS_SERVER=3.0-alpine
#
# Redis with arm64 support
#
#REDIS_SERVER=3.2
#REDIS_SERVER=3.2-alpine
#REDIS_SERVER=4.0
#REDIS_SERVER=4.0-alpine
#REDIS_SERVER=5.0
#REDIS_SERVER=5.0-alpine
#REDIS_SERVER=6.0
#REDIS_SERVER=6.0-alpine
#REDIS_SERVER=6.2
REDIS_SERVER=6.2-alpine
#REDIS_SERVER=7.0
#REDIS_SERVER=7.0-alpine
#REDIS_SERVER=latest
#REDIS_SERVER=alpine


###
### 1.6 Choose Memcached Server Image
###
### IMPORTANT: Alpine based images might cause issues on Docker Toolbox or OSX
###            https://github.com/docker/toolbox/issues/510
###
#
# Memcached without arm64 support
#
#MEMCD_SERVER=1.4
#MEMCD_SERVER=1.4-alpine
#
# Memcached with arm64 support
#
#MEMCD_SERVER=1.5
#MEMCD_SERVER=1.5-alpine
#MEMCD_SERVER=1.6
MEMCD_SERVER=1.6-alpine
#MEMCD_SERVER=latest
#MEMCD_SERVER=alpine


###
### 1.7 Choose Mongo Server Image
###
### https://www.mongodb.com/evolved
###
#
# MongoDB without arm64 support
#
#MONGO_SERVER=2.8
#MONGO_SERVER=3.0
#MONGO_SERVER=3.2
#
# MongoDB with arm64 support
#
#MONGO_SERVER=3.4
#MONGO_SERVER=3.6
#MONGO_SERVER=4.0
#MONGO_SERVER=4.2
#MONGO_SERVER=4.4
MONGO_SERVER=5.0
#MONGO_SERVER=latest



################################################################################
###
### 2. Host Mounts (Your computer)
###
################################################################################

###
### Global mount options
###
### Note: When adding custom mount options, ensure to start with a
###       leading ',' (comma), as those options are prepended to already
###       existing mount options.
###
### Note: If no mount options are specified, leave this variable empty
###       and do not add a leading ',' (comma).
###
### MOUNT_OPTIONS=,cached
### MOUNT_OPTIONS=
###
### Example: Allow to share mounts accross container with SELINUX enabled
###
### MOUNT_OPTIONS=,z
###
MOUNT_OPTIONS=


###
### Local filesystem path to www projects.
###
### Note: When changing this variable you must re-create the container.
###       Simply remove it, it will be auto-created during the next start:
###
### $ docker-compose rm -f
###
HOST_PATH_HTTPD_DATADIR=./data/www


###
### Local filesystem path to where your backups are stored
###
### Note: When changing this variable you must re-create the container.
###       Simply remove it, it will be auto-created during the next start:
###
### $ docker-compose rm -f
###
HOST_PATH_BACKUPDIR=./backups


###
### The path on your host OS of the ssh directory to be mounted into the
### PHP container into /home/devilbox/.ssh.
###
### IMPORTANT: The path is mounted read-only to ensure you cannot accidentally
##             delete anything inside the php container.
###
HOST_PATH_SSH_DIR=~/.ssh



################################################################################
###
### 3. PHP Docker Settings
###
################################################################################

###
### Enable certain PHP modules which are not enabled by default
###
### Currently the only modules that can be enabled are 'ioncube' and 'blackfire'
### Also ensure to disable xdebug when using any of the above:
### https://xdebug.org/docs/install#compat
###
### PHP_MODULES_ENABLE=ioncube, blackfire
###
PHP_MODULES_ENABLE=


###
### Disable any PHP modules that you don't require
###
### Specify a comma separated list without spaces of modules to disable
###
### PHP_MODULES_DISABLE=xdebug,imagick,swoole
###
PHP_MODULES_DISABLE=oci8,PDO_OCI,pdo_sqlsrv,sqlsrv,rdkafka,swoole,psr,phalcon


###
### Postfix settings for email catch-all
###
### When set to '1' postfix is normally started and made available. However you still need
### to configure it to your needs yourself. For that you can use the autostart scripts
### and define a couple of 'postconf -e name=value' commands.
###
### When set to '2' (email catch-all), no mail will leave the Devilbox. It is automatically
### internally routed the the devilbox mail account and you can see each sent mail
### in the bundled intranet: https://localhost/mail.php
###
### Values:
### 0: Disable postfix (do not start it)
### 1: Enable/Start postfix
### 2: Enable/Start postfix and enable email catch-all
###
PHP_MAIL_CATCH_ALL=2


###
### Configure everything else about PHP in
### * cfg/php-ini-X.X/*.ini
### * cfg/php-fpm-X.X/*.conf



################################################################################
###
### 4. HTTPD Docker Settings
###
################################################################################

###
### Expose HTTPD Port to Host
###
HOST_PORT_HTTPD=80
HOST_PORT_HTTPD_SSL=443


###
### Globally enable/disable HTTP/2 support
###
### This cannot be done on a per vhost level and must be enabled/disabled globally.
###
### Values:
###  * 0: HTTP/2 is disabled
###  * 1: HTTP/2 is enabled
###
HTTPD_HTTP2_ENABLE=1


###
### SSL (HTTP/HTTPS) settings for automated vhost generation
###
### By default each project will have two vhosts (one for HTTP and one for HTTPS).
### You can control the SSL settings for your projects via the below stated values.
###
### This is internally achieved via the '-m' argument of https://github.com/devilbox/vhost-gen
###
### Values:
###   * both:  Serve HTTP and HTTPS for all projects
###   * redir: HTTP always redirects to HTTPS
###   * ssl:   Only serve HTTPS
###   * plain: Only serve HTTP
###
HTTPD_VHOST_SSL_TYPE=both


###
### Document Root Subdirectory
###
### In your project directory, which subfolder should
### serve your files?
###
### When changing this value, restart the devilbox.
###
HTTPD_DOCROOT_DIR=htdocs


###
### Per vHost Config Subdirectory
###
### In your project directory, which subfolder should
### hold apache, nginx templates for a customized vhost?
###
### When changing this value, restart the devilbox.
###
HTTPD_TEMPLATE_DIR=.devilbox


###
### Remote (Upstream) Backend Timeout
###
### This setting specifies the Timeout for a remote Backend server,
### such as PHP-FPM or a Reverse Proxy.
###
### As for PHP, keep in mind that this value should be greater than
### PHP's max_execution_time,otherwise the php script could still
### run and the webserver will simply drop the connection before getting an answer by PHP.
###
HTTPD_BACKEND_TIMEOUT=180


###
### NGINX ONLY
###
### Set worker_processes and worker_connections
###
### https://nginx.org/en/docs/ngx_core_module.html#worker_processes
### https://nginx.org/en/docs/ngx_core_module.html#worker_connections
###
HTTPD_NGINX_WORKER_PROCESSES=auto
HTTPD_NGINX_WORKER_CONNECTIONS=1024



################################################################################
###
### 5. MySQL Docker Settings
###
################################################################################

###
### MySQL root user password
###
### The password is required for the initial creation of the MySQL database
### as well as the Devilbox intranet to display schema and configuration settings.
###
### If you change your MySQL root user password via mysql cli, phpMyAdmin or other tools
### after the database has been created, ensure to adjust the value here accordingly as well.
###
### If you only change this value here after the database has been created,
### the MySQL root user password will not actually be changed and the Devilbox intranet
### won't be able to connect to the MySQL service.
###
MYSQL_ROOT_PASSWORD=


###
### Expose MySQL Port to Host
###
HOST_PORT_MYSQL=3306



################################################################################
###
### 6. PostgreSQL Docker Settings
###
################################################################################

###
### PostgreSQL 'root' user name (usually postgres)
###
PGSQL_ROOT_USER=postgres


###
### PostgreSQL 'root' user password
###
### If you want to set a password, ensure to remove 'trust' from
### PGSQL_HOST_AUTH_METHOD below
###
PGSQL_ROOT_PASSWORD=


###
### In order to not use a password for PostgreSQL, keep this value at 'trust'
###
PGSQL_HOST_AUTH_METHOD=trust


###
### Expose PostgreSQL Port to Host
###
HOST_PORT_PGSQL=5432



################################################################################
###
### 7. Redis Docker Settings
###
################################################################################

###
### Expose Redis Port to Host
###
HOST_PORT_REDIS=6379


###
### Custom startup arguments
###
### Apply custom startup arguments to redis
###
### Example: Password protection
###   Add password protection to the Redis server by specifying it should
###   require a password.
###   Note: Do not add quotes or spaces to the password
###
###   REDIS_ARGS=--requirepass my-redis-root-password
###
### Example: Verbosity
###
###   REDIS_ARGS=--loglevel verbose
###
REDIS_ARGS=
#REDIS_ARGS=--loglevel verbose --requirepass my-redis-root-password



################################################################################
###
### 8. Memcached Docker Settings
###
################################################################################

###
### Expose Memcached Port to Host
###
HOST_PORT_MEMCD=11211



################################################################################
###
### 9. MongoDB Docker Settings
###
################################################################################

###
### Expose MongoDB Port to Host
###
HOST_PORT_MONGO=27017



################################################################################
###
### 10. Bind Docker Settings
###
################################################################################

###
### Expose Bind Port to Host
###
HOST_PORT_BIND=1053


###
### Add comma separated DNS server from which you want to receive DNS
### You can also add DNS servers from your LAN (if any are available)
###
BIND_DNS_RESOLVER=8.8.8.8,8.8.4.4


###
### Validate DNSSEC
###
### Values:
###  no:    DNSSEC validation is disabled
###  yes:   DNSSEC validation is enabled, but a trust anchor must be manually configured.
###  auto:  DNSSEC validation is enabled, and a default trust anchor for root zone is used.
###
BIND_DNSSEC_VALIDATE=no


###
### Bind timing options (time in seconds)
###
### Leave empty for defaults.
### Only change when you know what you are doing.
###
BIND_TTL_TIME=
BIND_REFRESH_TIME=
BIND_RETRY_TIME=
BIND_EXPIRY_TIME=
BIND_MAX_CACHE_TIME=


###
### Show DNS Queries in Docker logs output?
###
### 1: Yes
### 0: No
BIND_LOG_DNS_QUERIES=0



################################################################################
###
### 11. Custom variables
###
################################################################################

###
### Any variable defined in this file will be available
### as environment variables to your PHP/HHV Docker container.
###
### This might be useful to set application environment and retrieve
### them via: <?php getenv('MY_APPLICATION_ENV'); ?>
###


###
### Example:
### <?php echo getenv('Foo'); ?> would produce: 'some value'
###
#Foo=some value

Config: docker-compose.override.yml

# IMPORTANT: The version must match the version of docker-compose.yml
---
version: '2.3'

# The following override shows an example for the cockroachdb
services:
  # Your custom Docker image here:
  cockroach:
    image: cockroachdb/cockroach:latest
    command: start --insecure
    # Ensure the chosen ports are not occupied on the host system
    ports:
      - "${LOCAL_LISTEN_ADDR}26257:26257"
      - "${LOCAL_LISTEN_ADDR}8080:8080"
    networks:
      app_net:
        # Ensure to pick an IP address from docker-compose.yml network
        # that is not yet taken by other sevices
        ipv4_address: 172.16.238.200
    # (Optional) For ease of use always automatically start these:
    depends_on:
      - bind
      - php
      - httpd

Config: ./check-config.sh

#!/usr/bin/env bash

set -e
set -u
set -o pipefail


#--------------------------------------------------------------------------------------------------
# GLOBALS
#--------------------------------------------------------------------------------------------------
RET_CODE=0
MY_UID="$( id -u )"
MY_GID="$( id -g )"
DEBUG=0


#--------------------------------------------------------------------------------------------------
# Functions
#--------------------------------------------------------------------------------------------------

###
### Logger functions
###
log_err() {
	>&2 printf "\\e[1;31m[ERR]   %s\\e[0m\\n" "${1}"
}

log_note() {
	>&2 printf "\\e[1;33m[NOTE]  %s\\e[0m\\n" "${1}"
}

log_info() {
	printf "\\e[;34m[INFO]  %s\\e[0m\\n" "${1}"
}

log_ok() {
	printf "\\e[;32m[SUCC]  %s\\e[0m\\n" "${1}"
}
log_debug() {
	if [ "${DEBUG}" -eq "1" ]; then
		printf "[DEBUG] %s\\n" "${1}"
	fi
}

###
### Output functions
###
print_head_1() {
	printf "\\n# "
	printf "%0.s=" {1..78}
	printf "\\n"

	printf "# %s\\n" "${1}"

	printf "# "
	printf "%0.s=" {1..78}
	printf "\\n"
}

###
### File functions
###
file_get_uid() {
	if [ "$(uname)" = "Linux" ]; then
		stat --format '%u' "${1}"
	else
		stat -f '%u' "${1}"
	fi
}

file_get_gid() {
	if [ "$(uname)" = "Linux" ]; then
		stat --format '%g' "${1}"
	else
		stat -f '%g' "${1}"
	fi
}

# Returns 4-digit format
file_get_perm() {
	local perm
	local len

	if [ "$(uname)" = "Linux" ]; then
		# If no special permissions are set (no sticky bit...), linux will
		# only output the 3 digit number
		perm="$( stat --format '%a' "${1}" )"
	else
		perm="$( stat -f '%OLp' "${1}" )"
	fi

	# For special cases check the length and add a leading 0
	len="$(echo "${perm}" | awk '{ print length() }')"
	if [ "${len}" = "3" ]; then
		perm="0${perm}"
	fi

	echo "${perm}"
}

# Get path with '~' replace with correct home path
get_path() {
	echo "${1/#\~/${HOME}}"
}

# Returns sub directories by one level
# Also returns symlinks if they point to a directory
get_sub_dirs_level_1() {
	local dir="${1}"
	dir="${dir#./}"   # Remove leading './' if it exists
	dir="${dir%/}"    # Remove trailing '/' if it exists
	# shellcheck disable=SC2016
	find "${dir}" \
		| grep -Ev "^${dir}\$" \
		| grep -Ev "^${dir}/.+/" \
		| xargs -n1 sh -c 'if [ -d "${1}" ]; then echo "${1}"; fi'  -- \
		| sort
}

# Returns sub directories by two level
# Also returns symlinks if they point to a directory
get_sub_dirs_level_2() {
	local dir="${1}"
	dir="${dir#./}"   # Remove leading './' if it exists
	dir="${dir%/}"    # Remove trailing '/' if it exists
	# shellcheck disable=SC2016
	find "${dir}" \
		| grep -Ev "^${dir}\$" \
		| grep -Ev "^${dir}/.+/.+/" \
		| xargs -n1 sh -c 'if [ -d "${1}" ]; then echo "${1}"; fi'  -- \
		| sort
}

# Returns the value of .env var
get_env_value() {
	local val
	val="$( grep -E "^${1}=" .env )"
	echo "${val#*=}"
}

# Validate a DNS record
validate_dns() {
	ping -c1 "${1}" >/dev/null 2>&1
}



#--------------------------------------------------------------------------------------------------
# Check git
#--------------------------------------------------------------------------------------------------
print_head_1 "Checking git"

GIT_STATUS="$( git status -s )"
if [ -z "${GIT_STATUS}" ]; then
	log_ok "git is clean"
else
	log_err "git is unclean"
	echo "${GIT_STATUS}"
	RET_CODE=$(( RET_CODE + 1))
fi


#--------------------------------------------------------------------------------------------------
# Check env file
#--------------------------------------------------------------------------------------------------
print_head_1 "Checking .env file"

if [ -f .env ]; then
	log_ok ".env file exists"
else
	log_err ".env file does not exist"
	RET_CODE=$(( RET_CODE + 1))
	exit 1
fi
if [ -r .env ]; then
	log_ok ".env file is readable"
else
	log_err ".env file is not readable"
	RET_CODE=$(( RET_CODE + 1))
	exit 1
fi

# Ensure all variables exist in .env file
ENV_VAR_MISSING=0
while read -r env_var; do
	if ! grep -E "^${env_var}=" .env >/dev/null; then
		log_err "Variable '${env_var}' missing in .env file"
		RET_CODE=$(( RET_CODE + 1))
		ENV_VAR_MISSING=1
	else
		log_debug "Variable '${env_var}' is present in '.env file"
	fi
done < <(grep -E '^[A-Z].+=' env-example  | awk -F'=' '{print $1}')
if [ "${ENV_VAR_MISSING}" = "0" ]; then
	log_ok "All variables are present in .env file"
fi

# Ensure variables are not duplicated in .env
ENV_VAR_DUPLICATED=0
while read -r env_var; do
	OCCURANCES="$( grep -Ec "^${env_var}=" .env )"
	if [ "${OCCURANCES}" != "1" ]; then
		log_err "Variable '${env_var}' should only be defined once. Occurances: ${OCCURANCES}"
		RET_CODE=$(( RET_CODE + 1))
		ENV_VAR_DUPLICATED=1
	else
		log_debug "Variable '${env_var}' is defined exactly once."
	fi
done < <(grep -E '^[A-Z].+=' env-example  | awk -F'=' '{print $1}')
if [ "${ENV_VAR_DUPLICATED}" = "0" ]; then
	log_ok "No variables is duplicated in .env file"
fi


#--------------------------------------------------------------------------------------------------
# Check env file values
#--------------------------------------------------------------------------------------------------
print_head_1 "Checking .env file values"

WRONG_ENV_FILES_VALUES=0

DEBUG_ENTRYPOINT="$( get_env_value "DEBUG_ENTRYPOINT" )"
if [ "${DEBUG_ENTRYPOINT}" != "0" ] && [ "${DEBUG_ENTRYPOINT}" != "1" ] && [ "${DEBUG_ENTRYPOINT}" != "2" ] && [ "${DEBUG_ENTRYPOINT}" != "3" ] && [ "${DEBUG_ENTRYPOINT}" != "4" ]; then
	log_err "Variable 'DEBUG_ENTRYPOINT' should be 0, 1, 2, 3 or 4. Has: ${DEBUG_ENTRYPOINT}"
	RET_CODE=$(( RET_CODE + 1))
	WRONG_ENV_FILES_VALUES=1
else
	log_debug "Variable 'DEBUG_ENTRYPOINT' has correct value: ${DEBUG_ENTRYPOINT}"
fi

DOCKER_LOGS="$( get_env_value "DOCKER_LOGS" )"
if [ "${DOCKER_LOGS}" != "0" ] && [ "${DOCKER_LOGS}" != "1" ]; then
	log_err "Variable 'DOCKER_LOGS' should be 0 or 1. Has: ${DOCKER_LOGS}"
	RET_CODE=$(( RET_CODE + 1))
	WRONG_ENV_FILES_VALUES=1
else
	log_debug "Variable 'DOCKER_LOGS' has correct value: ${DOCKER_LOGS}"
fi

DEVILBOX_PATH="$( get_env_value "DEVILBOX_PATH" )"
if [ ! -d "${DEVILBOX_PATH}" ]; then
	log_err "Variable 'DEVILBOX_PATH' directory does not exist: ${DEVILBOX_PATH}"
	RET_CODE=$(( RET_CODE + 1))
	WRONG_ENV_FILES_VALUES=1
else
	log_debug "Variable 'DEVILBOX_PATH' directory exists: ${DEVILBOX_PATH}"
fi

DEVILBOX_PATH_PERM="$( file_get_perm "${DEVILBOX_PATH}" )"
if [ "${DEVILBOX_PATH_PERM}" != "0755" ] && [ "${DEVILBOX_PATH_PERM}" != "0775" ] && [ "${DEVILBOX_PATH_PERM}" != "0777" ]; then
	log_err "Variable 'DEVILBOX_PATH' directory must be 0755, 0775 or 0777. Has: ${DEVILBOX_PATH_PERM}"
	RET_CODE=$(( RET_CODE + 1))
	WRONG_ENV_FILES_VALUES=1
else
	log_debug "Variable 'DEVILBOX_PATH' directory has correct permissions: ${DEVILBOX_PATH_PERM}"
fi

DEVILBOX_PATH_PERM="$( file_get_uid "${DEVILBOX_PATH}" )"
if [ "${DEVILBOX_PATH_PERM}" != "${MY_UID}" ]; then
	log_err "Variable 'DEVILBOX_PATH' directory uid must be ${MY_UID}. Has: ${DEVILBOX_PATH_PERM}"
	RET_CODE=$(( RET_CODE + 1))
	WRONG_ENV_FILES_VALUES=1
else
	log_debug "Variable 'DEVILBOX_PATH' diretory has correct uid: ${DEVILBOX_PATH_PERM}"
fi

DEVILBOX_PATH_PERM="$( file_get_gid "${DEVILBOX_PATH}" )"
if [ "${DEVILBOX_PATH_PERM}" != "${MY_GID}" ]; then
	log_err "Variable 'DEVILBOX_PATH' directory gid must be ${MY_GID}. Has: ${DEVILBOX_PATH_PERM}"
	RET_CODE=$(( RET_CODE + 1))
	WRONG_ENV_FILES_VALUES=1
else
	log_debug "Variable 'DEVILBOX_PATH' diretory has correct gid: ${DEVILBOX_PATH_PERM}"
fi

LOCAL_LISTEN_ADDR="$( get_env_value "LOCAL_LISTEN_ADDR" )"
if [ -n "${LOCAL_LISTEN_ADDR}" ]; then
	if ! echo "${LOCAL_LISTEN_ADDR}" | grep -E ':$' >/dev/null; then
		log_err "Variable 'LOCAL_LISTEN_ADDR' is not empty and missing trailing ':'"
		RET_CODE=$(( RET_CODE + 1))
		WRONG_ENV_FILES_VALUES=1
	elif ! echo "${LOCAL_LISTEN_ADDR}" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:$' >/dev/null; then
		log_err "Variable 'LOCAL_LISTEN_ADDR' has wrong value: '${LOCAL_LISTEN_ADDR}'"
		RET_CODE=$(( RET_CODE + 1))
		WRONG_ENV_FILES_VALUES=1
	else
		log_debug "Variable 'LOCAL_LISTEN_ADDR' has correct value: ${LOCAL_LISTEN_ADDR}"
	fi
else
	log_debug "Variable 'LOCAL_LISTEN_ADDR' has correct value: ${LOCAL_LISTEN_ADDR}"
fi

HOST_PATH_HTTPD_DATADIR="$( get_path "$( get_env_value "HOST_PATH_HTTPD_DATADIR" )" )"
if [ ! -d "${HOST_PATH_HTTPD_DATADIR}" ]; then
	log_err "Variable 'HOST_PATH_HTTPD_DATADIR' directory does not exist: ${HOST_PATH_HTTPD_DATADIR}"
	RET_CODE=$(( RET_CODE + 1))
	WRONG_ENV_FILES_VALUES=1
else
	log_debug "Variable 'HOST_PATH_HTTPD_DATADIR' directory exists: ${HOST_PATH_HTTPD_DATADIR}"
fi

HOST_PATH_HTTPD_DATADIR_PERM="$( file_get_perm "${HOST_PATH_HTTPD_DATADIR}" )"
if [ "${HOST_PATH_HTTPD_DATADIR_PERM}" != "0755" ] && [ "${HOST_PATH_HTTPD_DATADIR_PERM}" != "0775" ] && [ "${HOST_PATH_HTTPD_DATADIR_PERM}" != "0777" ]; then
	log_err "Variable 'HOST_PATH_HTTPD_DATADIR' directory must be 0755, 0775 or 0777. Has: ${HOST_PATH_HTTPD_DATADIR_PERM}"
	RET_CODE=$(( RET_CODE + 1))
	WRONG_ENV_FILES_VALUES=1
else
	log_debug "Variable 'HOST_PATH_HTTPD_DATADIR' directory has correct permissions: ${HOST_PATH_HTTPD_DATADIR_PERM}"
fi

HOST_PATH_HTTPD_DATADIR_PERM="$( file_get_uid "${HOST_PATH_HTTPD_DATADIR}" )"
if [ "${HOST_PATH_HTTPD_DATADIR_PERM}" != "${MY_UID}" ]; then
	log_err "Variable 'HOST_PATH_HTTPD_DATADIR' directory uid must be ${MY_UID}. Has: ${HOST_PATH_HTTPD_DATADIR_PERM}"
	RET_CODE=$(( RET_CODE + 1))
	WRONG_ENV_FILES_VALUES=1
else
	log_debug "Variable 'HOST_PATH_HTTPD_DATADIR' directory has correct uid: ${HOST_PATH_HTTPD_DATADIR_PERM}"
fi

HOST_PATH_HTTPD_DATADIR_PERM="$( file_get_gid "${HOST_PATH_HTTPD_DATADIR}" )"
if [ "${HOST_PATH_HTTPD_DATADIR_PERM}" != "${MY_GID}" ]; then
	log_err "Variable 'HOST_PATH_HTTPD_DATADIR' directory gid must be ${MY_GID}. Has: ${HOST_PATH_HTTPD_DATADIR_PERM}"
	RET_CODE=$(( RET_CODE + 1))
	WRONG_ENV_FILES_VALUES=1
else
	log_debug "Variable 'HOST_PATH_HTTPD_DATADIR' directory has correct gid: ${HOST_PATH_HTTPD_DATADIR_PERM}"
fi

PHP_SERVER="$( get_env_value "PHP_SERVER" )"
if ! grep -E "^#?PHP_SERVER=${PHP_SERVER}\$" env-example >/dev/null; then
	log_err "Variable 'PHP_SERVER' has wrong value: ${PHP_SERVER}"
	RET_CODE=$(( RET_CODE + 1))
	WRONG_ENV_FILES_VALUES=1
else
	log_debug "Variable 'PHP_SERVER' has correct value: ${PHP_SERVER}"
fi

HTTPD_SERVER="$( get_env_value "HTTPD_SERVER" )"
if ! grep -E "^#?HTTPD_SERVER=${HTTPD_SERVER}\$" env-example >/dev/null; then
	log_err "Variable 'HTTPD_SERVER' has wrong value: ${HTTPD_SERVER}"
	RET_CODE=$(( RET_CODE + 1))
	WRONG_ENV_FILES_VALUES=1
else
	log_debug "Variable 'HTTPD_SERVER' has correct value: ${HTTPD_SERVER}"
fi

MYSQL_SERVER="$( get_env_value "MYSQL_SERVER" )"
if ! grep -E "^#?MYSQL_SERVER=${MYSQL_SERVER}\$" env-example >/dev/null; then
	log_err "Variable 'MYSQL_SERVER' has wrong value: ${MYSQL_SERVER}"
	RET_CODE=$(( RET_CODE + 1))
	WRONG_ENV_FILES_VALUES=1
else
	log_debug "Variable 'MYSQL_SERVER' has correct value: ${MYSQL_SERVER}"
fi

PGSQL_SERVER="$( get_env_value "PGSQL_SERVER" )"
if ! grep -E "^#?PGSQL_SERVER=${PGSQL_SERVER}\$" env-example >/dev/null; then
	log_err "Variable 'PGSQL_SERVER' has wrong value: ${PGSQL_SERVER}"
	RET_CODE=$(( RET_CODE + 1))
	WRONG_ENV_FILES_VALUES=1
else
	log_debug "Variable 'PGSQL_SERVER' has correct value: ${PGSQL_SERVER}"
fi

REDIS_SERVER="$( get_env_value "REDIS_SERVER" )"
if ! grep -E "^#?REDIS_SERVER=${REDIS_SERVER}\$" env-example >/dev/null; then
	log_err "Variable 'REDIS_SERVER' has wrong value: ${REDIS_SERVER}"
	RET_CODE=$(( RET_CODE + 1))
	WRONG_ENV_FILES_VALUES=1
else
	log_debug "Variable 'REDIS_SERVER' has correct value: ${REDIS_SERVER}"
fi

MEMCD_SERVER="$( get_env_value "MEMCD_SERVER" )"
if ! grep -E "^#?MEMCD_SERVER=${MEMCD_SERVER}\$" env-example >/dev/null; then
	log_err "Variable 'MEMCD_SERVER' has wrong value: ${MEMCD_SERVER}"
	RET_CODE=$(( RET_CODE + 1))
	WRONG_ENV_FILES_VALUES=1
else
	log_debug "Variable 'MEMCD_SERVER' has correct value: ${MEMCD_SERVER}"
fi

MONGO_SERVER="$( get_env_value "MONGO_SERVER" )"
if ! grep -E "^#?MONGO_SERVER=${MONGO_SERVER}\$" env-example >/dev/null; then
	log_err "Variable 'MONGO_SERVER' has wrong value: ${MONGO_SERVER}"
	RET_CODE=$(( RET_CODE + 1))
	WRONG_ENV_FILES_VALUES=1
else
	log_debug "Variable 'MONGO_SERVER' has correct value: ${MONGO_SERVER}"
fi

NEW_UID="$( get_env_value "NEW_UID" )"
if [ "${NEW_UID}" != "${MY_UID}" ]; then
	log_err "Variable 'NEW_UID' has wrong value: '${NEW_UID}'. Should have: ${MY_UID}"
	RET_CODE=$(( RET_CODE + 1))
	WRONG_ENV_FILES_VALUES=1
else
	log_debug "Variable 'NEW_UID' has correct value: '${NEW_UID}'"
fi

NEW_GID="$( get_env_value "NEW_GID" )"
if [ "${NEW_GID}" != "${MY_GID}" ]; then
	log_err "Variable 'NEW_GID' has wrong value: '${NEW_GID}'. Should have: ${MY_GID}"
	RET_CODE=$(( RET_CODE + 1))
	WRONG_ENV_FILES_VALUES=1
else
	log_debug "Variable 'NEW_GID' has correct value: '${NEW_GID}'"
fi

TLD_SUFFIX="$( get_env_value "TLD_SUFFIX" )"
TLD_SUFFIX_BLACKLIST="dev|com|org|net|int|edu|de"
if echo "${TLD_SUFFIX}" | grep -E "^(${TLD_SUFFIX_BLACKLIST})\$" >/dev/null; then
	log_err "Variable 'TLD_SUFFX' should not be set to '${TLD_SUFFIX}'. It is a real tld domain."
	log_err "All DNS requests will be intercepted to this tld domain and re-routed to the HTTP container."
	log_info "Consider using a subdomain value of e.g.: 'mydev.${TLD_SUFFIX}' instead."
	RET_CODE=$(( RET_CODE + 1))
	WRONG_ENV_FILES_VALUES=1
elif [ "${TLD_SUFFIX}" = "localhost" ]; then
	log_err "Variable 'TLD_SUFFX' should not be set to '${TLD_SUFFIX}'. It is a loopback address."
	log_info "See: https://tools.ietf.org/html/draft-west-let-localhost-be-localhost-06"
	RET_CODE=$(( RET_CODE + 1))
	WRONG_ENV_FILES_VALUES=1
else
	log_debug "Variable 'TLD_SUFFIX' has correct value: '${TLD_SUFFIX}'"
fi

if [ "${WRONG_ENV_FILES_VALUES}" = "0" ]; then
	log_ok "All .env file variables have correct values"
fi


#--------------------------------------------------------------------------------------------------
# Ensure cfg/ and log/ directories exist
#--------------------------------------------------------------------------------------------------
print_head_1 "Checking required Devilbox core directories exist"

# /cfg/php-fpm-VERSION
DIR_MISSING=0
while read -r php_version; do
	if [ ! -d "cfg/php-fpm-${php_version}" ]; then
		log_err "Directory 'cfg/php-fpm-${php_version}' is missing"
		RET_CODE=$(( RET_CODE + 1))
		DIR_MISSING=1
	else
		log_debug "Directory 'cfg/php-fpm-${php_version}' is present"
	fi
done < <(grep -E '^#?PHP_SERVER=' env-example  | awk -F'=' '{print $2}')
if [ "${DIR_MISSING}" = "0" ]; then
	log_ok "All PHP cfg/ sub directories are present"
fi

# /log/php-fpm-VERSION
DIR_MISSING=0
while read -r php_version; do
	if [ ! -d "log/php-fpm-${php_version}" ]; then
		log_err "Directory 'log/php-fpm-${php_version}' is missing"
		RET_CODE=$(( RET_CODE + 1))
		DIR_MISSING=1
	else
		log_debug "Directory 'log/php-fpm-${php_version}' is present"
	fi
done < <(grep -E '^#?PHP_SERVER=' env-example  | awk -F'=' '{print $2}')
if [ "${DIR_MISSING}" = "0" ]; then
	log_ok "All PHP log/ sub directories are present"
fi

# /cfg/apache|nginx-VERSION
DIR_MISSING=0
while read -r httpd_version; do
	if [ ! -d "cfg/${httpd_version}" ]; then
		log_err "Directory 'cfg/${httpd_version}' is missing"
		RET_CODE=$(( RET_CODE + 1))
		DIR_MISSING=1
	else
		log_debug "Directory 'cfg/${httpd_version}' is present"
	fi
done < <(grep -E '^#?HTTPD_SERVER=' env-example  | awk -F'=' '{print $2}')
if [ "${DIR_MISSING}" = "0" ]; then
	log_ok "All HTTPD cfg/ sub directories are present"
fi

# /log/apache|nginx-VERSION
DIR_MISSING=0
while read -r httpd_version; do
	if [ ! -d "log/${httpd_version}" ]; then
		log_err "Directory 'log/${httpd_version}' is missing"
		RET_CODE=$(( RET_CODE + 1))
		DIR_MISSING=1
	else
		log_debug "Directory 'log/${httpd_version}' is present"
	fi
done < <(grep -E '^#?HTTPD_SERVER=' env-example  | awk -F'=' '{print $2}')
if [ "${DIR_MISSING}" = "0" ]; then
	log_ok "All HTTPD log/ sub directories are present"
fi


#--------------------------------------------------------------------------------------------------
# Devilbox Directory permissions
#--------------------------------------------------------------------------------------------------
print_head_1 "Checking devilbox core directory permissions"

DEVILBOX_DIRS=(
	"autostart"
	"bash"
	"ca"
	"cfg"
	"compose"
	"log"
	"supervisor"
)

# Check allowed directory permissions: 0755 0775 0777
DEVILBOX_DIR_PERM_WRONG=0
for search_dir in "${DEVILBOX_DIRS[@]}"; do
	while read -r my_dir; do
		PERM="$( file_get_perm "${my_dir}" )"
		if [ "${PERM}" != "0755" ] && [ "${PERM}" != "0775" ] && [ "${PERM}" != "0777" ]; then
			log_err "Directory '${my_dir}' should have 0755, 0775 or 0777 permissions. Has: ${PERM} permissions"
			RET_CODE=$(( RET_CODE + 1))
			DEVILBOX_DIR_PERM_WRONG=1
		else
			log_debug "Directory '${my_dir}' has correct permissions: ${PERM}"
		fi
	done < <(find "${search_dir}" -type d)
done
if [ "${DEVILBOX_DIR_PERM_WRONG}" = "0" ]; then
	log_ok "All devilbox directories have correct permissions"
fi

# Check allowed uid
DEVILBOX_DIR_PERM_WRONG=0
for search_dir in "${DEVILBOX_DIRS[@]}"; do
	while read -r my_dir; do
		PERM="$( file_get_uid "${my_dir}" )"
		if [ "${PERM}" != "${MY_UID}" ]; then
			log_err "Directory '${my_dir}' should have uid '${MY_UID}' Has: '${PERM}'"
			RET_CODE=$(( RET_CODE + 1))
			DEVILBOX_DIR_PERM_WRONG=1
		else
			log_debug "Directory '${my_dir}' has correct uid: ${PERM}"
		fi
	done < <(find "${search_dir}" -type d)
done
if [ "${DEVILBOX_DIR_PERM_WRONG}" = "0" ]; then
	log_ok "All devilbox directories have correct uid"
fi

# Check allowed gid
DEVILBOX_DIR_PERM_WRONG=0
for search_dir in "${DEVILBOX_DIRS[@]}"; do
	while read -r my_dir; do
		PERM="$( file_get_gid "${my_dir}" )"
		if [ "${PERM}" != "${MY_GID}" ]; then
			log_err "Directory '${my_dir}' should have gid '${MY_GID}' Has: '${PERM}'"
			RET_CODE=$(( RET_CODE + 1))
			DEVILBOX_DIR_PERM_WRONG=1
		else
			log_debug "Directory '${my_dir}' has correct gid: ${PERM}"
		fi
	done < <(find "${search_dir}" -type d)
done
if [ "${DEVILBOX_DIR_PERM_WRONG}" = "0" ]; then
	log_ok "All devilbox directories have correct gid"
fi


#--------------------------------------------------------------------------------------------------
# Devilbox File permissions
#--------------------------------------------------------------------------------------------------
print_head_1 "Checking devilbox core file permissions"

DEVILBOX_DIRS=(
	"autostart"
	"ca"
	"cfg"
	"compose"
	"supervisor"
)

# Check allowed directory permissions: 0644 0664 0666
DEVILBOX_DIR_PERM_WRONG=0
for search_file in "${DEVILBOX_DIRS[@]}"; do
	while read -r my_file; do
		PERM="$( file_get_perm "${my_file}" )"
		# Private CA file
		if [ "${my_file}" = "ca/devilbox-ca.key" ]; then
			if [ "${PERM}" != "0600" ]; then
				log_err "File '${my_file}' should have 0600 permissions. Has: ${PERM} permissions"
				RET_CODE=$(( RET_CODE + 1))
				DEVILBOX_DIR_PERM_WRONG=1
			else
				log_debug "File '${my_file}' has correct permissions: ${PERM}"
			fi
		# Executable files
		elif echo "${my_file}" | grep -E '.+\.sh(-example)?$' >/dev/null; then
			if [ "${PERM}" != "0755" ] && [ "${PERM}" != "0775" ] && [ "${PERM}" != "0777" ]; then
				log_err "File '${my_file}' should have 0755, 0775 or 0777 permissions. Has: ${PERM} permissions"
				RET_CODE=$(( RET_CODE + 1))
				DEVILBOX_DIR_PERM_WRONG=1
			else
				log_debug "File '${my_file}' has correct permissions: ${PERM}"
			fi
		# All other files
		else
			if [ "${PERM}" != "0644" ] && [ "${PERM}" != "0664" ] && [ "${PERM}" != "0666" ]; then
				log_err "File '${my_file}' should have 0644, 0664 or 0666 permissions. Has: ${PERM} permissions"
				RET_CODE=$(( RET_CODE + 1))
				DEVILBOX_DIR_PERM_WRONG=1
			else
				log_debug "File '${my_file}' has correct permissions: ${PERM}"
			fi
		fi
	done < <(find "${search_file}" -type f)
done
if [ "${DEVILBOX_DIR_PERM_WRONG}" = "0" ]; then
	log_ok "All devilbox files have correct permissions"
fi

# Check allowed uid
DEVILBOX_DIR_PERM_WRONG=0
for search_file in "${DEVILBOX_DIRS[@]}"; do
	while read -r my_file; do
		PERM="$( file_get_uid "${my_file}" )"
		if [ "${PERM}" != "${MY_UID}" ]; then
			log_err "File '${my_file}' should have uid '${MY_UID}' Has: '${PERM}'"
			RET_CODE=$(( RET_CODE + 1))
			DEVILBOX_DIR_PERM_WRONG=1
		else
			log_debug "File '${my_file}' has correct uid: ${PERM}"
		fi
	done < <(find "${search_file}" -type f)
done
if [ "${DEVILBOX_DIR_PERM_WRONG}" = "0" ]; then
	log_ok "All devilbox files have correct uid"
fi

# Check allowed gid
DEVILBOX_DIR_PERM_WRONG=0
for search_file in "${DEVILBOX_DIRS[@]}"; do
	while read -r my_file; do
		PERM="$( file_get_gid "${my_file}" )"
		if [ "${PERM}" != "${MY_GID}" ]; then
			log_err "File '${my_file}' should have gid '${MY_GID}' Has: '${PERM}'"
			RET_CODE=$(( RET_CODE + 1))
			DEVILBOX_DIR_PERM_WRONG=1
		else
			log_debug "File '${my_file}' has correct gid: ${PERM}"
		fi
	done < <(find "${search_file}" -type f)
done
if [ "${DEVILBOX_DIR_PERM_WRONG}" = "0" ]; then
	log_ok "All devilbox files have correct gid"
fi


#--------------------------------------------------------------------------------------------------
# Check projects permissions
#--------------------------------------------------------------------------------------------------
print_head_1 "Checking projects permissions"

HOST_PATH_HTTPD_DATADIR="$( get_path "$( get_env_value "HOST_PATH_HTTPD_DATADIR" )" )"

DATA_DIR_PERM_WRONG=0
while read -r project; do
	PERM="$( file_get_perm "${project}" )"
	if [ "${PERM}" != "0755" ] && [ "${PERM}" != "0775" ] && [ "${PERM}" != "0777" ]; then
		log_err "Directory '${project}' should have 0755, 0775 or 0777 permissions. Has: ${PERM} permissions"
		RET_CODE=$(( RET_CODE + 1))
		DATA_DIR_PERM_WRONG=1
	else
		log_debug "Directory '${project}' has correct permissions: ${PERM}"
	fi
done < <(get_sub_dirs_level_1 "${HOST_PATH_HTTPD_DATADIR}")
if [ "${DATA_DIR_PERM_WRONG}" = "0" ]; then
	log_ok "All project dirs have correct permissions"
fi

DATA_DIR_PERM_WRONG=0
while read -r project; do
	PERM="$( file_get_uid "${project}" )"
	if [ "${PERM}" != "${MY_UID}" ]; then
		log_err "Directory '${project}' should have uid '${MY_UID}' Has: '${PERM}'"
		RET_CODE=$(( RET_CODE + 1))
		DATA_DIR_PERM_WRONG=1
	else
		log_debug "Directory '${project}' has correct uid: ${PERM}"
	fi
done < <(get_sub_dirs_level_1 "${HOST_PATH_HTTPD_DATADIR}")
if [ "${DATA_DIR_PERM_WRONG}" = "0" ]; then
	log_ok "All project dirs have correct uid"
fi

DATA_DIR_PERM_WRONG=0
while read -r project; do
	PERM="$( file_get_gid "${project}" )"
	if [ "${PERM}" != "${MY_GID}" ]; then
		log_err "Directory '${project}' should have gid '${MY_GID}' Has: '${PERM}'"
		RET_CODE=$(( RET_CODE + 1))
		DATA_DIR_PERM_WRONG=1
	else
		log_debug "Directory '${project}' has correct gid: ${PERM}"
	fi
done < <(get_sub_dirs_level_1 "${HOST_PATH_HTTPD_DATADIR}")
if [ "${DATA_DIR_PERM_WRONG}" = "0" ]; then
	log_ok "All project dirs have correct gid"
fi


#--------------------------------------------------------------------------------------------------
# Check projects settings
#--------------------------------------------------------------------------------------------------
print_head_1 "Checking projects settings"

HOST_PATH_HTTPD_DATADIR="$( get_path "$( get_env_value "HOST_PATH_HTTPD_DATADIR" )" )"

TLD_SUFFIX="$( get_env_value "TLD_SUFFIX" )"
DNS_RECORD_WRONG=0
while read -r project; do
	VHOST="$( basename "${project}" ).${TLD_SUFFIX}"
	if ! validate_dns "${VHOST}"; then
		log_err "Project '${VHOST}' has no valid DNS record"
		RET_CODE=$(( RET_CODE + 1))
		DNS_RECORD_WRONG=1
	else
		log_debug "Project '${VHOST}' has valid DNS record"
	fi
done < <(get_sub_dirs_level_1 "${HOST_PATH_HTTPD_DATADIR}")
if [ "${DNS_RECORD_WRONG}" = "0" ]; then
	log_ok "All projects have valid DNS records"
fi

HTTPD_DOCROOT_DIR="$( get_env_value "HTTPD_DOCROOT_DIR" )"
DOCROOT_WRONG=0
while read -r project; do
	if [ ! -d "${project}/${HTTPD_DOCROOT_DIR}" ]; then
		log_err "Missing HTTPD_DOCROOT_DIR '${HTTPD_DOCROOT_DIR}' in: ${project}"
		RET_CODE=$(( RET_CODE + 1))
		DOCROOT_WRONG=1
	else
		log_debug "HTTPD_DOCROOT_DIR '${HTTPD_DOCROOT_DIR}' present in: ${project}"
	fi
done < <(get_sub_dirs_level_1 "${HOST_PATH_HTTPD_DATADIR}")
if [ "${DOCROOT_WRONG}" = "0" ]; then
	log_ok "All projects have valid HTTPD_DOCROOT_DIR"
fi


#--------------------------------------------------------------------------------------------------
# Check Customizations
#--------------------------------------------------------------------------------------------------
print_head_1 "Checking customizations"

CUSTOMIZATIONS=0

# vhost-gen
HOST_PATH_HTTPD_DATADIR="$( get_path "$( get_env_value "HOST_PATH_HTTPD_DATADIR" )" )"
HTTPD_TEMPLATE_DIR="$( get_env_value "HTTPD_TEMPLATE_DIR" )"
while read -r project; do
	if [ -f "${project}/${HTTPD_TEMPLATE_DIR}/apache22.yml" ]; then
		log_note "[vhost-gen]  Custom Apache 2.2 vhost-gen config present in: ${project}/"
		CUSTOMIZATIONS=$(( CUSTOMIZATIONS + 1 ))
	elif [ -f "${project}/${HTTPD_TEMPLATE_DIR}/apache24.yml" ]; then
		log_note "[vhost-gen]  Custom Apache 2.4 vhost-gen config present in: ${project}/"
		CUSTOMIZATIONS=$(( CUSTOMIZATIONS + 1 ))
	elif [ -f "${project}/${HTTPD_TEMPLATE_DIR}/nginx.yml" ]; then
		log_note "[vhost-gen]  Custom Nginx vhost-gen config present in: ${project}/"
		CUSTOMIZATIONS=$(( CUSTOMIZATIONS + 1 ))
	else
		log_debug "[vhost-gen]  No custom configuration for: ${project}/"
	fi
done < <(get_sub_dirs_level_1 "${HOST_PATH_HTTPD_DATADIR}")

# docker-compose.override.yml
if [ -f "docker-compose.override.yml" ]; then
	log_note "[docker]     Custom docker-compose.override.yml present"
	CUSTOMIZATIONS=$(( CUSTOMIZATIONS + 1 ))
else
	log_debug "[docker]     No custom docker-compose.override.yml present"
fi

# cfg/HTTPD/
while read -r httpd; do
	if find "cfg/${httpd}" | grep -E '\.conf$' >/dev/null; then
		log_note "[httpd]      Custom config present in cfg/${httpd}/"
		CUSTOMIZATIONS=$(( CUSTOMIZATIONS + 1 ))
	else
		log_debug "[httpd]      No custom config present in cfg/${httpd}/"
	fi
done < <(grep -E '^#?HTTPD_SERVER=' env-example  | awk -F'=' '{print $2}')

# cfg/php-ini-${version}/
while read -r php_version; do
	if find "cfg/php-ini-${php_version}" | grep -E '\.ini$' >/dev/null; then
		log_note "[php.ini]    Custom config present in cfg/php-ini-${php_version}/"
		CUSTOMIZATIONS=$(( CUSTOMIZATIONS + 1 ))
	else
		log_debug "[php.ini]    No custom config present in cfg/php-ini-${php_version}/"
	fi
done < <(grep -E '^#?PHP_SERVER=' env-example  | awk -F'=' '{print $2}')

# cfg/php-fpm-${version}/
while read -r php_version; do
	if find "cfg/php-fpm-${php_version}" | grep -E '\.conf$' >/dev/null; then
		log_note "[php-fpm]    Custom config present in cfg/php-fpm-${php_version}/"
		CUSTOMIZATIONS=$(( CUSTOMIZATIONS + 1 ))
	else
		log_debug "[php-fpm]    No custom config present in cfg/php-fpm-${php_version}/"
	fi
done < <(grep -E '^#?PHP_SERVER=' env-example  | awk -F'=' '{print $2}')

# cfg/MYSQL/
while read -r mysql; do
	if find "cfg/${mysql}" | grep -E '\.cnf$' >/dev/null; then
		log_note "[mysql]      Custom config present in cfg/${mysql}/"
		CUSTOMIZATIONS=$(( CUSTOMIZATIONS + 1 ))
	else
		log_debug "[mysql]      No custom config present in cfg/${mysql}/"
	fi
done < <(grep -E '^#?MYSQL_SERVER=' env-example  | awk -F'=' '{print $2}')

# cfg/php-startup-${version}/
while read -r php_version; do
	if find "cfg/php-startup-${php_version}" | grep -E '\.sh$' >/dev/null; then
		log_note "[startup]    Custom script present in cfg/php-startup-${php_version}/"
		CUSTOMIZATIONS=$(( CUSTOMIZATIONS + 1 ))
	else
		log_debug "[startup]    No custom script present in cfg/php-startup-${php_version}/"
	fi
done < <(grep -E '^#?PHP_SERVER=' env-example  | awk -F'=' '{print $2}')

# autostart/
if find "autostart" | grep -E '\.sh$' >/dev/null; then
	log_note "[startup]    Custom script present in autostart/"
	CUSTOMIZATIONS=$(( CUSTOMIZATIONS + 1 ))
else
	log_debug "[startup]    No custom script present in autostart/"
fi

# supervisor/
if find "supervisor" | grep -E '\.conf$' >/dev/null; then
	log_note "[supervisor] Custom config present in supervisor/"
	CUSTOMIZATIONS=$(( CUSTOMIZATIONS + 1 ))
else
	log_debug "[supervisor] No custom config present in supervisor/"
fi

# bash/
if find "bash" | grep -E '\.sh$' >/dev/null; then
	log_note "[bash]      Custom script present in bash/"
	CUSTOMIZATIONS=$(( CUSTOMIZATIONS + 1 ))
else
	log_debug "[bash]      No custom script present in bash/"
fi

# Total?
if [ "${CUSTOMIZATIONS}" = "0" ]; then
	log_info "No custom configurations applied"
fi


#--------------------------------------------------------------------------------------------------
# Summary
#--------------------------------------------------------------------------------------------------
print_head_1 "SUMMARY"

if [ "${RET_CODE}" -gt "0" ]; then
	log_err "Found ${RET_CODE} error(s)"
	log_err "Devilbox might not work properly"
	log_err "Fix the issues before submitting a bug report"
	if [ "${CUSTOMIZATIONS}" -gt "0" ]; then
		log_note "${CUSTOMIZATIONS} custom configurations applied. If you encounter issues, reset them first."
	else
		log_info "No custom configurations applied"
	fi
	log_info "Ensure to run 'docker-compose stop; docker-compose rm -f' on .env changes or custom configs"
	exit 1
else
	log_ok "Found no errors"
	if [ "${CUSTOMIZATIONS}" -gt "0" ]; then
		log_note "${CUSTOMIZATIONS} custom configurations applied. If you encounter issues, reset them first."
	else
		log_info "No custom configurations applied"
	fi
	log_info "Ensure to run 'docker-compose stop; docker-compose rm -f' on .env changes or custom configs"
	exit 0
fi

Log: docker-compose logs

Traceback (most recent call last):
  File "/usr/lib/python3.12/site-packages/urllib3/connectionpool.py", line 715, in urlopen
    httplib_response = self._make_request(
                       ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/urllib3/connectionpool.py", line 416, in _make_request
    conn.request(method, url, **httplib_request_kw)
  File "/usr/lib/python3.12/site-packages/urllib3/connection.py", line 244, in request
    super(HTTPConnection, self).request(method, url, body=body, headers=headers)
  File "/usr/lib64/python3.12/http/client.py", line 1336, in request
    self._send_request(method, url, body, headers, encode_chunked)
  File "/usr/lib64/python3.12/http/client.py", line 1382, in _send_request
    self.endheaders(body, encode_chunked=encode_chunked)
  File "/usr/lib64/python3.12/http/client.py", line 1331, in endheaders
    self._send_output(message_body, encode_chunked=encode_chunked)
  File "/usr/lib64/python3.12/http/client.py", line 1091, in _send_output
    self.send(msg)
  File "/usr/lib64/python3.12/http/client.py", line 1035, in send
    self.connect()
  File "/usr/lib/python3.12/site-packages/docker/transport/unixconn.py", line 27, in connect
    sock.connect(self.unix_socket)
PermissionError: [Errno 13] Permission denied

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.12/site-packages/requests/adapters.py", line 486, in send
    resp = conn.urlopen(
           ^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/urllib3/connectionpool.py", line 799, in urlopen
    retries = retries.increment(
              ^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/urllib3/util/retry.py", line 550, in increment
    raise six.reraise(type(error), error, _stacktrace)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/urllib3/packages/six.py", line 769, in reraise
    raise value.with_traceback(tb)
  File "/usr/lib/python3.12/site-packages/urllib3/connectionpool.py", line 715, in urlopen
    httplib_response = self._make_request(
                       ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/urllib3/connectionpool.py", line 416, in _make_request
    conn.request(method, url, **httplib_request_kw)
  File "/usr/lib/python3.12/site-packages/urllib3/connection.py", line 244, in request
    super(HTTPConnection, self).request(method, url, body=body, headers=headers)
  File "/usr/lib64/python3.12/http/client.py", line 1336, in request
    self._send_request(method, url, body, headers, encode_chunked)
  File "/usr/lib64/python3.12/http/client.py", line 1382, in _send_request
    self.endheaders(body, encode_chunked=encode_chunked)
  File "/usr/lib64/python3.12/http/client.py", line 1331, in endheaders
    self._send_output(message_body, encode_chunked=encode_chunked)
  File "/usr/lib64/python3.12/http/client.py", line 1091, in _send_output
    self.send(msg)
  File "/usr/lib64/python3.12/http/client.py", line 1035, in send
    self.connect()
  File "/usr/lib/python3.12/site-packages/docker/transport/unixconn.py", line 27, in connect
    sock.connect(self.unix_socket)
urllib3.exceptions.ProtocolError: ('Connection aborted.', PermissionError(13, 'Permission denied'))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.12/site-packages/docker/api/client.py", line 214, in _retrieve_server_version
    return self.version(api_version=False)["ApiVersion"]
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/docker/api/daemon.py", line 181, in version
    return self._result(self._get(url), json=True)
                        ^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/docker/utils/decorators.py", line 46, in inner
    return f(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/docker/api/client.py", line 237, in _get
    return self.get(url, **self._set_request_timeout(kwargs))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/requests/sessions.py", line 602, in get
    return self.request("GET", url, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/requests/sessions.py", line 589, in request
    resp = self.send(prep, **send_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/requests/sessions.py", line 703, in send
    r = adapter.send(request, **kwargs)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/requests/adapters.py", line 501, in send
    raise ConnectionError(err, request=request)
requests.exceptions.ConnectionError: ('Connection aborted.', PermissionError(13, 'Permission denied'))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/bin/docker-compose", line 33, in <module>
    sys.exit(load_entry_point('docker-compose==1.29.2', 'console_scripts', 'docker-compose')())
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/compose/cli/main.py", line 81, in main
    command_func()
  File "/usr/lib/python3.12/site-packages/compose/cli/main.py", line 200, in perform_command
    project = project_from_options('.', options)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/compose/cli/command.py", line 60, in project_from_options
    return get_project(
           ^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/compose/cli/command.py", line 152, in get_project
    client = get_client(
             ^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/compose/cli/docker_client.py", line 41, in get_client
    client = docker_client(
             ^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/compose/cli/docker_client.py", line 170, in docker_client
    client = APIClient(use_ssh_client=not use_paramiko_ssh, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/docker/api/client.py", line 197, in __init__
    self._version = self._retrieve_server_version()
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/docker/api/client.py", line 221, in _retrieve_server_version
    raise DockerException(
docker.errors.DockerException: Error while fetching server API version: ('Connection aborted.', PermissionError(13, 'Permission denied'))

(Optional) Additional information

No response

@Lucenabo Lucenabo added bug triage Needs triaging labels Jun 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug triage Needs triaging
Projects
None yet
Development

No branches or pull requests

2 participants