diff --git a/CLAUDE.md b/CLAUDE.md index c1254df..d28be23 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -95,6 +95,23 @@ The entrypoint script handles: | `SIMPLERISK_DB_USERNAME/PASSWORD/DATABASE` | DB credentials | | `SIMPLERISK_CRON_SETUP` | Enable/disable PHP cron (default: enabled) | | `SIMPLERISK_CSRF_SECRET` | Override auto-generated CSRF secret | +| `ADMIN_USERNAME` | Username for initial admin user (only during `DB_SETUP`; idempotent) | +| `ADMIN_PASSWORD` | Password for initial admin user | +| `ADMIN_EMAIL` | Email for initial admin user | +| `ADMIN_NAME` | Display name for initial admin user (default: `Administrator`) | +| `MAIL_TRANSPORT` | `smtp` or `sendmail` | +| `MAIL_FROM_EMAIL` | Sender address (validated against email regex) | +| `MAIL_FROM_NAME` | Sender display name | +| `MAIL_REPLYTO_EMAIL` | Reply-to address (same regex as from) | +| `MAIL_REPLYTO_NAME` | Reply-to display name | +| `MAIL_HOST` | SMTP hostname | +| `MAIL_PORT` | SMTP port (numeric) | +| `MAIL_ENCRYPTION` | `none`, `tls`, or `ssl` | +| `MAIL_SMTPAUTH` | `true` or `false` | +| `MAIL_SMTPAUTOTLS` | `true` or `false` | +| `MAIL_USERNAME` | SMTP auth username | +| `MAIL_PASSWORD` | SMTP auth password (not applied if empty) | +| `MAIL_PREPEND` | Subject-line prefix string | ### CI/CD diff --git a/simplerisk-minimal/README.md b/simplerisk-minimal/README.md index a8a3f1f..9ba6eaf 100644 --- a/simplerisk-minimal/README.md +++ b/simplerisk-minimal/README.md @@ -70,3 +70,28 @@ docker run -d --name simplerisk -e SIMPLERISK_DB_PASSWORD=pass -e SIMPLERISK_DB_ | `SIMPLERISK_DB_FOR_SESSIONS` | `true` | Indicator that the application will store all sessions on the configured database | | `SIMPLERISK_DB_SSL_CERT_PATH` | Empty string (`''`) | Path where SSL certificates, to contact the database, are located | | `SIMPLERISK_CRON_SETUP` | `enabled` | Install the cron job to run in this Docker container | +| `SIMPLERISK_CSRF_SECRET` | Auto-generated | Override the auto-generated CSRF secret | +| `ADMIN_USERNAME` | — | Username for an initial admin user created once during `DB_SETUP`. Requires `ADMIN_PASSWORD` and `ADMIN_EMAIL`. Skipped if the username already exists | +| `ADMIN_PASSWORD` | — | Password for the initial admin user | +| `ADMIN_EMAIL` | — | Email address for the initial admin user | +| `ADMIN_NAME` | `Administrator` | Display name for the initial admin user | + +## Mail settings + +The following environment variables configure the outbound mail settings stored in the SimpleRisk database (`settings` table). They are applied on every container start, so changing a value and restarting the container is all that is needed to update the configuration. Invalid values are silently skipped with a warning in the container logs. + +| Variable Name | Default Value | Purpose | +|:-------------:|:-------------:|:--------| +| `MAIL_TRANSPORT` | — | Transport to use: `smtp` or `sendmail` | +| `MAIL_FROM_EMAIL` | — | Sender email address (must be a valid email) | +| `MAIL_FROM_NAME` | — | Sender display name | +| `MAIL_REPLYTO_EMAIL` | — | Reply-to email address (must be a valid email) | +| `MAIL_REPLYTO_NAME` | — | Reply-to display name | +| `MAIL_HOST` | — | SMTP server hostname | +| `MAIL_PORT` | — | SMTP server port (must be numeric) | +| `MAIL_ENCRYPTION` | — | Encryption method: `none`, `tls`, or `ssl` | +| `MAIL_SMTPAUTH` | — | Whether SMTP authentication is required: `true` or `false` | +| `MAIL_SMTPAUTOTLS` | — | Whether to automatically use TLS: `true` or `false` | +| `MAIL_USERNAME` | — | SMTP authentication username | +| `MAIL_PASSWORD` | — | SMTP authentication password (not applied if empty) | +| `MAIL_PREPEND` | — | Prefix string prepended to all outbound email subjects | diff --git a/simplerisk-minimal/common/docker/configure-admin.php b/simplerisk-minimal/common/docker/configure-admin.php new file mode 100644 index 0000000..c2f39cc --- /dev/null +++ b/simplerisk-minimal/common/docker/configure-admin.php @@ -0,0 +1,57 @@ +prepare("SELECT COUNT(*) FROM user"); +$stmt->execute(); +$count = (int)$stmt->fetchColumn(); +db_close($db); + +if ($count > 0) { + echo "Database already has users, skipping admin user creation.\n"; + exit(0); +} + +if (get_id_by_user($username)) { + echo "User '{$username}' already exists, skipping.\n"; + exit(0); +} + +$salt = ''; +$values = array_merge(range(0, 9), range('a', 'z'), range('A', 'Z')); +for ($i = 0; $i < 20; $i++) { + $salt .= $values[array_rand($values)]; +} +set_time_limit(120); +$hash = crypt($password, '$2a$15$' . md5($salt)); + +$user_id = add_user( + 'simplerisk', + $username, + $email, + $name, + $salt, + $hash, + [], + 1, + 1, + 0, + 0, + 0, + get_possible_permission_ids() +); + +echo "Admin user '{$username}' created with ID {$user_id}.\n"; diff --git a/simplerisk-minimal/common/entrypoint.sh b/simplerisk-minimal/common/entrypoint.sh index a25acfc..36de9ba 100644 --- a/simplerisk-minimal/common/entrypoint.sh +++ b/simplerisk-minimal/common/entrypoint.sh @@ -104,6 +104,91 @@ set_cron(){ fi } +apply_mail_setting(){ + local db_key="$1" value="$2" + # Escape backslashes then single quotes for a MySQL single-quoted string literal + local escaped + escaped=$(printf '%s' "$value" | sed 's/\\/\\\\/g' | sed "s/'/\\\\'/g") + mysql -u "$SIMPLERISK_DB_USERNAME" \ + -p"$SIMPLERISK_DB_PASSWORD" \ + -h "$SIMPLERISK_DB_HOSTNAME" \ + -P "$SIMPLERISK_DB_PORT" \ + "$SIMPLERISK_DB_DATABASE" \ + -e "UPDATE settings SET value='${escaped}' WHERE name='${db_key}';" \ + || print_log "mail_settings:warn" "Failed to update ${db_key}" +} + +set_mail_settings(){ + local email_regex='^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$' + + # transport: smtp or sendmail + if [ -n "${MAIL_TRANSPORT:-}" ]; then + case "${MAIL_TRANSPORT}" in + smtp|sendmail) apply_mail_setting phpmailer_transport "$MAIL_TRANSPORT" ;; + *) print_log "mail_settings:warn" "MAIL_TRANSPORT='${MAIL_TRANSPORT}' must be 'smtp' or 'sendmail' — skipping" ;; + esac + fi + + # email addresses: validate regex + if [ -n "${MAIL_FROM_EMAIL:-}" ]; then + if [[ "${MAIL_FROM_EMAIL}" =~ $email_regex ]]; then + apply_mail_setting phpmailer_from_email "$MAIL_FROM_EMAIL" + else + print_log "mail_settings:warn" "MAIL_FROM_EMAIL is not a valid email address — skipping" + fi + fi + + if [ -n "${MAIL_REPLYTO_EMAIL:-}" ]; then + if [[ "${MAIL_REPLYTO_EMAIL}" =~ $email_regex ]]; then + apply_mail_setting phpmailer_replyto_email "$MAIL_REPLYTO_EMAIL" + else + print_log "mail_settings:warn" "MAIL_REPLYTO_EMAIL is not a valid email address — skipping" + fi + fi + + # free-form strings + [ -n "${MAIL_FROM_NAME:-}" ] && apply_mail_setting phpmailer_from_name "$MAIL_FROM_NAME" + [ -n "${MAIL_REPLYTO_NAME:-}" ] && apply_mail_setting phpmailer_replyto_name "$MAIL_REPLYTO_NAME" + [ -n "${MAIL_HOST:-}" ] && apply_mail_setting phpmailer_host "$MAIL_HOST" + [ -n "${MAIL_USERNAME:-}" ] && apply_mail_setting phpmailer_username "$MAIL_USERNAME" + [ -n "${MAIL_PREPEND:-}" ] && apply_mail_setting phpmailer_prepend "$MAIL_PREPEND" + + # booleans: true or false + if [ -n "${MAIL_SMTPAUTOTLS:-}" ]; then + case "${MAIL_SMTPAUTOTLS}" in + true|false) apply_mail_setting phpmailer_smtpautotls "$MAIL_SMTPAUTOTLS" ;; + *) print_log "mail_settings:warn" "MAIL_SMTPAUTOTLS must be 'true' or 'false' — skipping" ;; + esac + fi + + if [ -n "${MAIL_SMTPAUTH:-}" ]; then + case "${MAIL_SMTPAUTH}" in + true|false) apply_mail_setting phpmailer_smtpauth "$MAIL_SMTPAUTH" ;; + *) print_log "mail_settings:warn" "MAIL_SMTPAUTH must be 'true' or 'false' — skipping" ;; + esac + fi + + # smtpsecure: none, tls, or ssl + if [ -n "${MAIL_ENCRYPTION:-}" ]; then + case "${MAIL_ENCRYPTION}" in + none|tls|ssl) apply_mail_setting phpmailer_smtpsecure "$MAIL_ENCRYPTION" ;; + *) print_log "mail_settings:warn" "MAIL_ENCRYPTION must be 'none', 'tls', or 'ssl' — skipping" ;; + esac + fi + + # port: numeric only + if [ -n "${MAIL_PORT:-}" ]; then + if [[ "${MAIL_PORT}" =~ ^[0-9]+$ ]]; then + apply_mail_setting phpmailer_port "$MAIL_PORT" + else + print_log "mail_settings:warn" "MAIL_PORT must be numeric — skipping" + fi + fi + + # password: only applied when non-empty + [ -n "${MAIL_PASSWORD:-}" ] && apply_mail_setting phpmailer_password "$MAIL_PASSWORD" +} + delete_db(){ print_log "db_deletion: prepare" "Performing database deletion" @@ -158,6 +243,10 @@ EOSQL" "Was not able to apply settings on database. Check error above. Exiting." # Update the SIMPLERISK_INSTALLED value exec_cmd "sed -i \"s/\('SIMPLERISK_INSTALLED', \)'false'/\1'true'/g\" $CONFIG_PATH" + # Create admin user if ADMIN_USERNAME is provided (optional, non-fatal) + # shellcheck disable=SC2015 + [ -n "${ADMIN_USERNAME:-}" ] && exec_cmd_nobail "php /docker/configure-admin.php" || print_log "initial_setup:warn" "Admin user creation failed; check output above" + # shellcheck disable=SC2015 [ "${DB_SETUP:-}" = "automatic-only" ] && print_log "initial_setup:info" "Running setup only (automatic-only). Container will be discarded." && exit 0 || true } @@ -176,6 +265,23 @@ unset_variables() { unset SIMPLERISK_DB_SSL_CERT_PATH unset SIMPLERISK_CSRF_SECRET unset SIMPLERISK_CRON_SETUP + unset MAIL_TRANSPORT + unset MAIL_FROM_EMAIL + unset MAIL_FROM_NAME + unset MAIL_REPLYTO_EMAIL + unset MAIL_REPLYTO_NAME + unset MAIL_HOST + unset MAIL_SMTPAUTOTLS + unset MAIL_SMTPAUTH + unset MAIL_USERNAME + unset MAIL_PASSWORD + unset MAIL_ENCRYPTION + unset MAIL_PORT + unset MAIL_PREPEND + unset ADMIN_USERNAME + unset ADMIN_PASSWORD + unset ADMIN_EMAIL + unset ADMIN_NAME } _main() { @@ -193,6 +299,7 @@ _main() { [[ "${DB_SETUP:-}" == "delete" ]] && delete_db || true # shellcheck disable=SC2015 [[ "${DB_SETUP:-}" = automatic* ]] && db_setup || true + set_mail_settings unset_variables exec "$@" }