Implemented CLI installer draft and replaced OIDC by OAuth2 Proxy for SuiteCRM - See https://chatgpt.com/share/69274924-9dec-800f-8653-345aa2c25664

This commit is contained in:
2025-11-27 01:10:11 +01:00
parent d97d34a822
commit 6d6b0fdea6
9 changed files with 130 additions and 74 deletions

View File

@@ -18,6 +18,7 @@ ports:
web-app-fusiondirectory: 4187 web-app-fusiondirectory: 4187
web-app-gitea: 4188 web-app-gitea: 4188
web-app-snipe-it: 4189 web-app-snipe-it: 4189
web-app-suitecrm: 4190
ldap: ldap:
svc-db-openldap: 389 svc-db-openldap: 389
http: http:

View File

@@ -1,8 +1,8 @@
# Constants # Constants
CSS_FILES: ['default.css','bootstrap.css'] CSS_FILES: ['default.css','bootstrap.css']
CSS_BASE_COLOR: "{{ design.css.colors.base }}" CSS_BASE_COLOR: "{{ design.css.colors.base }}"
CSS_COUNT: 7 CSS_COUNT: 7
CSS_SHADES: 100 CSS_SHADES: 100
# Variables # Variables
css_app_dst: "{{ [cdn_paths_all.role.release.css, 'style.css'] | path_join }}" css_app_dst: "{{ [cdn_paths_all.role.release.css, 'style.css'] | path_join }}"

View File

@@ -1,7 +1,7 @@
# DOMAIN/URL # DOMAIN/URL
DOMAIN={{ SHOPWARE_DOMAIN }} DOMAIN={{ SHOPWARE_DOMAIN }}
APP_URL="{{ domains | get_url(application_id, WEB_PROTOCOL) }}" APP_URL="{{ domains | get_url(application_id, WEB_PROTOCOL) }}"
APP_DEBUG="{{ MODE_DEBUG | ternary(1, 0) }}" APP_DEBUG="{{ MODE_DEBUG | bool| ternary(1, 0) }}"
# Shopware # Shopware
APP_ENV={{ 'dev' if (ENVIRONMENT | lower) == 'development' else 'prod' }} APP_ENV={{ 'dev' if (ENVIRONMENT | lower) == 'development' else 'prod' }}

View File

@@ -2,7 +2,7 @@
## Description ## Description
Manage your customer relationships with SuiteCRM, a powerful open-source CRM platform extending SugarCRM with advanced modules, workflows, and integrations. This role integrates SuiteCRM into the Infinito.Nexus ecosystem with centralized database, mail, LDAP and OIDC-ready SSO support. 🚀💼 Manage your customer relationships with SuiteCRM, a powerful open-source CRM platform extending SugarCRM with advanced modules, workflows, and integrations. This role integrates SuiteCRM into the Infinito.Nexus ecosystem with centralized database, mail and LDAP-ready single sign-on integration. 🚀💼
## Overview ## Overview
@@ -13,7 +13,7 @@ This Ansible role deploys SuiteCRM using Docker and the Infinito.Nexus shared st
- Environment variable management through Jinja2 templates - Environment variable management through Jinja2 templates
- Docker Compose orchestration for the **SuiteCRM** application container - Docker Compose orchestration for the **SuiteCRM** application container
- Native **LDAP** authentication via Symfonys LDAP configuration - Native **LDAP** authentication via Symfonys LDAP configuration
- OIDC-ready wiring for integration with Keycloak or other OIDC providers (via reverse proxy or plugin) - SSO integration via SAML / OAuth2 configured inside SuiteCRMs Administration Panel
With this role, you get a production-ready CRM environment that plugs into your existing IAM stack. With this role, you get a production-ready CRM environment that plugs into your existing IAM stack.
@@ -22,23 +22,27 @@ With this role, you get a production-ready CRM environment that plugs into your
- **Sales & Service CRM:** Accounts, Contacts, Leads, Opportunities, Cases, Campaigns and more 📊 - **Sales & Service CRM:** Accounts, Contacts, Leads, Opportunities, Cases, Campaigns and more 📊
- **Workflow Engine:** Automate business processes and notifications 🛠️ - **Workflow Engine:** Automate business processes and notifications 🛠️
- **LDAP Authentication:** Centralize user authentication against OpenLDAP 🔐 - **LDAP Authentication:** Centralize user authentication against OpenLDAP 🔐
- **OIDC-Ready SSO:** Preconfigured OIDC environment variables for use with plugins or an OIDC reverse proxy 🌐 - **SSO-Ready:** Integrates with SAML / OAuth2 providers (e.g. Keycloak as IdP) via SuiteCRMs admin UI 🌐
- **Config via Templates:** Fully customizable `.env` and `docker-compose.yml` rendered via Jinja2 ⚙️ - **Config via Templates:** Fully customizable `.env` and `docker-compose.yml` rendered via Jinja2 ⚙️
- **Health Checks & Logging:** Integrates with Infinito.Nexus health checking and journald logging 📈 - **Health Checks & Logging:** Integrates with Infinito.Nexus health checking and journald logging 📈
- **Modular Role Composition:** Uses shared roles for DB, proxy and monitoring to keep your stack consistent 🔄 - **Modular Role Composition:** Uses shared roles for DB, proxy and monitoring to keep your stack consistent 🔄
## Further Resources ## Further Resources
- [SuiteCRM Official Website](https://suitecrm.com/) 🌍 - [SuiteCRM Official Website](https://suitecrm.com/) 🌍
- [SuiteCRM Documentation](https://docs.suitecrm.com/) 📖 - [SuiteCRM Documentation](https://docs.suitecrm.com/) 📖
- [Infinito.Nexus Project Repository](https://s.infinito.nexus/code) 🔗 - [Infinito.Nexus Project Repository](https://s.infinito.nexus/code) 🔗
## OIDC & LDAP Notes ## LDAP & SSO Notes
- **LDAP** is configured using Symfonys environment variables (`AUTH_TYPE=ldap`, `LDAP_*`) so SuiteCRM 8+ can authenticate directly against your OpenLDAP service. - **LDAP** is configured via environment variables (`AUTH_TYPE=ldap`, `LDAP_*`).
- **OIDC** is provided at the platform level (e.g. Keycloak + oauth2-proxy or a SuiteCRM OIDC plugin). The role writes a `config_override.php` so SuiteCRMs legacy backend
This role exposes OIDC client, issuer and endpoint settings as environment variables, so plugins or uses LDAP for authentication against your OpenLDAP service.
sidecar components can consume them without duplicating configuration.
- **SSO** in SuiteCRM 8 is handled via **SAML** (e.g. with Keycloak as IdP) and
**OAuth providers** configured in the Administration panel (for outbound email and API access).
This role does not implement full OIDC login flows; instead, you configure SAML/OAuth inside SuiteCRMs admin UI.
## Credits ## Credits

View File

@@ -3,9 +3,10 @@ features:
css: true css: true
desktop: true desktop: true
ldap: true ldap: true
oidc: true # OIDC via Keycloak + reverse proxy / plugin oidc: false # OIDC isn't available, just SAML for SSO
central_database: true central_database: true
logout: true logout: true
oauth2: true
server: server:
csp: csp:
@@ -29,13 +30,17 @@ docker:
suitecrm: suitecrm:
image: "php" image: "php"
version: "8.2-apache" version: "8.2-apache"
# Git tag from https://github.com/SuiteCRM/SuiteCRM # Git tag from https://github.com/SuiteCRM/SuiteCRM-Core/releases
app_version: "7.14.8" app_version: "8.9.1"
name: "suitecrm" name: "suitecrm"
cpus: 1.5 cpus: 1.5
mem_reservation: 1.2g mem_reservation: 1.2g
mem_limit: 2g mem_limit: 2g
pids_limit: 768 pids_limit: 768
oauth2_proxy:
origin:
host: "suitecrm"
port: "80"
volumes: volumes:
data: suitecrm_data data: suitecrm_data

View File

@@ -1,49 +1,93 @@
#!/bin/sh #!/bin/sh
# Minimal SuiteCRM entrypoint for Infinito.Nexus
set -eu set -eu
APP_DIR="/var/www/html" APP_DIR="/var/www/html"
WEB_USER="www-data" WEB_USER="www-data"
WEB_GROUP="www-data" WEB_GROUP="www-data"
INSTALL_FLAG="${APP_DIR}/public/installed.flag"
log() { printf '%s %s\n' "[suitecrm-entrypoint]" "$*" >&2; } log() { printf '%s %s\n' "[suitecrm-entrypoint]" "$*" >&2; }
# Ensure application directory exists ############################################
# 1) Sanity Checks
############################################
if [ ! -d "$APP_DIR" ]; then if [ ! -d "$APP_DIR" ]; then
log "ERROR: Application directory '$APP_DIR' does not exist." log "ERROR: Application directory '$APP_DIR' does not exist."
exit 1 exit 1
fi fi
# Fix permissions (best-effort, idempotent enough for small instances) ############################################
log "Adjusting permissions on ${APP_DIR} (this may take some time on first run)..." # 2) Permissions
############################################
log "Adjusting file permissions..."
chown -R "$WEB_USER:$WEB_GROUP" "$APP_DIR" chown -R "$WEB_USER:$WEB_GROUP" "$APP_DIR"
find "$APP_DIR" -type d -exec chmod 755 {} \; find "$APP_DIR" -type d -exec chmod 755 {} \;
find "$APP_DIR" -type f -exec chmod 644 {} \; find "$APP_DIR" -type f -exec chmod 644 {} \;
# Writable directories for d in cache public/upload public/legacy/upload public/legacy/cache; do
for d in cache custom modules themes upload; do
if [ -d "${APP_DIR}/${d}" ]; then if [ -d "${APP_DIR}/${d}" ]; then
chmod -R 775 "${APP_DIR}/${d}" chmod -R 775 "${APP_DIR}/${d}"
chown -R "$WEB_USER:$WEB_GROUP" "${APP_DIR}/${d}"
fi fi
done done
# Add a simple healthcheck file ############################################
echo "OK" > "${APP_DIR}/healthcheck.html" # 3) Auto-Install SuiteCRM (only if not yet installed)
chown "$WEB_USER:$WEB_GROUP" "${APP_DIR}/healthcheck.html" ############################################
if [ ! -f "$INSTALL_FLAG" ]; then
log "SuiteCRM 8 is not installed — performing automated installation..."
# (Optional) place for future auto-config (DB, LDAP, OIDC) by editing config.php # CLI installer (SuiteCRM 8)
# Ref: ./bin/console suitecrm:app:install -u "admin" -p "pass" -U "db_user" -P "db_pass" -H "db_host" -N "db_name" -S "https://crm.example.com" -d "yes"
php bin/console suitecrm:app:install \
-u "$SUITECRM_ADMIN_USERNAME" \
-p "$SUITECRM_ADMIN_PASSWORD" \
-U "$SUITECRM_DB_USER" \
-P "$SUITECRM_DB_PASSWORD" \
-H "$SUITECRM_DB_HOST" \
-N "$SUITECRM_DB_NAME" \
-S "$SUITECRM_URL" \
-d "no"
# Hand off to CMD (Apache in foreground by default) # Mark as installed
if [ "$#" -gt 0 ]; then echo "installed" > "$INSTALL_FLAG"
log "Executing CMD: $*" chown "$WEB_USER:$WEB_GROUP" "$INSTALL_FLAG"
exec "$@"
log "SuiteCRM installation completed successfully."
else
log "SuiteCRM already installed — skipping installer."
fi fi
# Default: start Apache HTTPD in foreground ############################################
if command -v apache2-foreground >/dev/null 2>&1; then # 4) LDAP Auto-Configuration (legacy backend)
log "Starting apache2-foreground..." ############################################
exec apache2-foreground if [ "${AUTH_TYPE:-disabled}" = "ldap" ]; then
log "Writing LDAP configuration to config_override.php"
cat > "${APP_DIR}/public/legacy/config_override.php" <<PHP
<?php
\$sugar_config['authenticationClass'] = 'LdapAuthenticate';
\$sugar_config['ldap_hostname'] = '${LDAP_HOST}';
\$sugar_config['ldap_port'] = '${LDAP_PORT}';
\$sugar_config['ldap_encrypt'] = '${LDAP_ENCRYPTION}';
\$sugar_config['ldap_base_dn'] = '${LDAP_BASE_DN}';
\$sugar_config['ldap_bind_attr'] = '${LDAP_UID_KEY}';
\$sugar_config['ldap_login_filter'] = "(${LDAP_UID_KEY}=%s)";
\$sugar_config['ldap_bind_dn'] = '${LDAP_BIND_DN}';
\$sugar_config['ldap_bind_password'] = '${LDAP_BIND_PASSWORD}';
PHP
chown "$WEB_USER:$WEB_GROUP" "${APP_DIR}/public/legacy/config_override.php"
fi fi
log "No known server command found; keeping container alive." ############################################
exec tail -f /dev/null # 5) Healthcheck file
############################################
echo "OK" > "${APP_DIR}/public/healthcheck.html"
chown "$WEB_USER:$WEB_GROUP" "${APP_DIR}/public/healthcheck.html"
############################################
# 6) Start Apache
############################################
log "Starting apache2-foreground..."
exec apache2-foreground

View File

@@ -10,6 +10,8 @@ RUN apt-get update && apt-get install -y \
libicu-dev \ libicu-dev \
libonig-dev \ libonig-dev \
libxml2-dev \ libxml2-dev \
libldap2-dev \
&& docker-php-ext-configure ldap --with-libdir=lib/x86_64-linux-gnu \
&& docker-php-ext-install \ && docker-php-ext-install \
mysqli \ mysqli \
gd \ gd \
@@ -18,6 +20,7 @@ RUN apt-get update && apt-get install -y \
mbstring \ mbstring \
soap \ soap \
opcache \ opcache \
ldap \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# Install Apache modules # Install Apache modules
@@ -25,15 +28,22 @@ RUN a2enmod rewrite headers
WORKDIR /var/www/html WORKDIR /var/www/html
# Adjust Apache DocumentRoot for SuiteCRM 8 (Symfony public/ dir)
RUN sed -ri 's!/var/www/html!/var/www/html/public!g' \
/etc/apache2/sites-available/000-default.conf \
/etc/apache2/apache2.conf
# Install Composer # Install Composer
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
# Download SuiteCRM source # Download SuiteCRM 8 source from GitHub
RUN set -eux; \ RUN set -eux; \
SUITECRM_TAG="v{{ SUITECRM_APP_VERSION }}"; \ SUITECRM_TAG="v{{ SUITECRM_APP_VERSION }}"; \
wget -qO /tmp/suitecrm.tar.gz "https://github.com/SuiteCRM/SuiteCRM/archive/refs/tags/${SUITECRM_TAG}.tar.gz"; \ wget -qO /tmp/suitecrm.tar.gz \
"https://github.com/SuiteCRM/SuiteCRM-Core/archive/refs/tags/${SUITECRM_TAG}.tar.gz"; \
tar --strip-components=1 -xzf /tmp/suitecrm.tar.gz -C /var/www/html; \ tar --strip-components=1 -xzf /tmp/suitecrm.tar.gz -C /var/www/html; \
rm /tmp/suitecrm.tar.gz rm /tmp/suitecrm.tar.gz; \
chmod +x bin/console
# Install PHP dependencies via Composer (critical!) # Install PHP dependencies via Composer (critical!)
RUN set -eux; \ RUN set -eux; \
@@ -41,7 +51,8 @@ RUN set -eux; \
--no-dev \ --no-dev \
--prefer-dist \ --prefer-dist \
--no-interaction \ --no-interaction \
--optimize-autoloader --optimize-autoloader \
--no-scripts
# Copy entrypoint # Copy entrypoint
COPY {{ SUITECRM_ENTRYPOINT_SCRIPT_HOST_REL }} {{ SUITECRM_ENTRYPOINT_SCRIPT_DOCKER }} COPY {{ SUITECRM_ENTRYPOINT_SCRIPT_HOST_REL }} {{ SUITECRM_ENTRYPOINT_SCRIPT_DOCKER }}

View File

@@ -4,8 +4,19 @@
############################################# #############################################
# ------------------------------------------------ # ------------------------------------------------
# Database # Core Symfony / SuiteCRM 8 settings
# ------------------------------------------------ # ------------------------------------------------
APP_ENV={{ 'dev' if (ENVIRONMENT | lower) == 'development' else 'prod' }}
APP_DEBUG="{{ MODE_DEBUG | bool| ternary(1, 0) }}"
# ------------------------------------------------
# Database (Symfony-style)
# ------------------------------------------------
DATABASE_URL=mysql://{{ database_username }}:{{ database_password | urlencode }}@{{ database_host }}:{{ database_port }}/{{ database_name }}
DATABASE_DRIVER=pdo_mysql
DATABASE_SERVER_VERSION=10.11
# Keep legacy-style vars for external tools or debugging
SUITECRM_DB_HOST={{ database_host }} SUITECRM_DB_HOST={{ database_host }}
SUITECRM_DB_PORT={{ database_port }} SUITECRM_DB_PORT={{ database_port }}
SUITECRM_DB_NAME={{ database_name }} SUITECRM_DB_NAME={{ database_name }}
@@ -13,12 +24,11 @@ SUITECRM_DB_USER={{ database_username }}
SUITECRM_DB_PASSWORD={{ database_password }} SUITECRM_DB_PASSWORD={{ database_password }}
# ------------------------------------------------ # ------------------------------------------------
# Initial admin account # Initial admin account (for your own tooling)
# (SuiteCRM installer will use this; can also be set in config_override.php)
# ------------------------------------------------ # ------------------------------------------------
SUITECRM_ADMIN_USERNAME={{ applications | get_app_conf(application_id, 'users.administrator.username') }} SUITECRM_ADMIN_USERNAME={{ applications | get_app_conf(application_id, 'users.administrator.username') }}
SUITECRM_ADMIN_PASSWORD={{ applications | get_app_conf(application_id, 'credentials.administrator_password') }} SUITECRM_ADMIN_PASSWORD={{ applications | get_app_conf(application_id, 'credentials.administrator_password') }}
SUITECRM_ADMIN_EMAIL={{ users['contact'].email }} SUITECRM_ADMIN_EMAIL={{ users['administrator'].email }}
# Public base URL of the SuiteCRM instance # Public base URL of the SuiteCRM instance
SUITECRM_URL={{ SUITECRM_URL }} SUITECRM_URL={{ SUITECRM_URL }}
@@ -34,37 +44,19 @@ SUITECRM_SMTP_PROTOCOL={{ "TLS" if SYSTEM_EMAIL.START_TLS else "SSL" }}
SUITECRM_EMAIL_FROM_NAME={{ applications | get_app_conf(application_id, 'email.from_name') }} SUITECRM_EMAIL_FROM_NAME={{ applications | get_app_conf(application_id, 'email.from_name') }}
# ------------------------------------------------ # ------------------------------------------------
# LDAP settings (optional) # LDAP settings (native SuiteCRM 8 / Symfony)
# ------------------------------------------------ # ------------------------------------------------
{% if SUITECRM_LDAP_ENABLED | bool %} {% if SUITECRM_LDAP_ENABLED | bool %}
SUITECRM_LDAP_ENABLED=true AUTH_TYPE=ldap
SUITECRM_LDAP_HOST={{ LDAP.SERVER.DOMAIN }} LDAP_HOST={{ LDAP.SERVER.DOMAIN }}
SUITECRM_LDAP_PORT={{ LDAP.SERVER.PORT }} LDAP_PORT={{ LDAP.SERVER.PORT }}
SUITECRM_LDAP_ENCRYPTION={{ LDAP.SERVER.SECURITY | lower if LDAP.SERVER.SECURITY else "none" }} LDAP_ENCRYPTION={{ LDAP.SERVER.SECURITY | lower if LDAP.SERVER.SECURITY else "none" }} # none|ssl|tls
SUITECRM_LDAP_BASE_DN={{ LDAP.DN.OU.USERS }} LDAP_BASE_DN={{ LDAP.DN.OU.USERS }}
SUITECRM_LDAP_BIND_DN={{ LDAP.DN.ADMINISTRATOR.DATA }} LDAP_BIND_DN={{ LDAP.DN.ADMINISTRATOR.DATA }}
SUITECRM_LDAP_BIND_PASSWORD={{ LDAP.BIND_CREDENTIAL }} LDAP_BIND_PASSWORD={{ LDAP.BIND_CREDENTIAL }}
SUITECRM_LDAP_UID_ATTR={{ LDAP.USER.ATTRIBUTES.ID }} LDAP_UID_KEY={{ LDAP.USER.ATTRIBUTES.ID }} # e.g. uid or mail
{% else %} {% else %}
SUITECRM_LDAP_ENABLED=false AUTH_TYPE=disabled
{% endif %}
# ------------------------------------------------
# OpenID Connect settings (optional)
# ------------------------------------------------
{% if SUITECRM_OIDC_ENABLED | bool %}
SUITECRM_OIDC_ENABLED=true
SUITECRM_OIDC_CLIENT_ID={{ OIDC.CLIENT.ID }}
SUITECRM_OIDC_CLIENT_SECRET={{ OIDC.CLIENT.SECRET }}
SUITECRM_OIDC_ISSUER_URL={{ OIDC.CLIENT.ISSUER_URL }}
SUITECRM_OIDC_AUTHORIZATION_URL={{ OIDC.CLIENT.AUTHORIZE_URL }}
SUITECRM_OIDC_TOKEN_URL={{ OIDC.CLIENT.TOKEN_URL }}
SUITECRM_OIDC_USERINFO_URL={{ OIDC.CLIENT.USER_INFO_URL }}
SUITECRM_OIDC_JWKS_URL={{ OIDC.CLIENT.CERTS }}
SUITECRM_OIDC_REDIRECT_URI={{ SUITECRM_URL }}/oidc/callback
SUITECRM_OIDC_SCOPES=openid,profile,email
{% else %}
SUITECRM_OIDC_ENABLED=false
{% endif %} {% endif %}
# ------------------------------------------------ # ------------------------------------------------

View File

@@ -24,7 +24,6 @@ SUITECRM_DATA_VOLUME: "{{ applications | get_app_conf(application_id
# URLs & feature flags # URLs & feature flags
SUITECRM_URL: "{{ domains | get_url(application_id, WEB_PROTOCOL) }}" SUITECRM_URL: "{{ domains | get_url(application_id, WEB_PROTOCOL) }}"
SUITECRM_OIDC_ENABLED: "{{ applications | get_app_conf(application_id, 'features.oidc') }}"
SUITECRM_LDAP_ENABLED: "{{ applications | get_app_conf(application_id, 'features.ldap') }}" SUITECRM_LDAP_ENABLED: "{{ applications | get_app_conf(application_id, 'features.ldap') }}"
# Simple maintenance toggle (for later extensions) # Simple maintenance toggle (for later extensions)