This commit is contained in:
2025-11-26 19:38:37 +01:00
parent 254a685b05
commit e6cb0cbed9
12 changed files with 259 additions and 0 deletions

View File

@@ -118,6 +118,8 @@ defaults_networks:
subnet: 192.168.104.80/28 subnet: 192.168.104.80/28
web-svc-onlyoffice: web-svc-onlyoffice:
subnet: 192.168.104.96/28 subnet: 192.168.104.96/28
web-app-suitecrm:
subnet: 192.168.104.112/28
# /24 Networks / 254 Usable Clients # /24 Networks / 254 Usable Clients
web-app-bigbluebutton: web-app-bigbluebutton:

View File

@@ -83,6 +83,7 @@ ports:
web-app-mini-qr: 8059 web-app-mini-qr: 8059
web-app-shopware: 8060 web-app-shopware: 8060
web-svc-onlyoffice: 8061 web-svc-onlyoffice: 8061
web-app-suitecrm: 8062
web-app-bigbluebutton: 48087 # This port is predefined by bbb. @todo Try to change this to a 8XXX port web-app-bigbluebutton: 48087 # This port is predefined by bbb. @todo Try to change this to a 8XXX port
public: public:
# The following ports should be changed to 22 on the subdomain via stream mapping # The following ports should be changed to 22 on the subdomain via stream mapping

View File

@@ -0,0 +1,48 @@
# SuiteCRM
## 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. 🚀💼
## Overview
This Ansible role deploys SuiteCRM using Docker and the Infinito.Nexus shared stack. It handles:
- MariaDB database provisioning via the `sys-svc-rdbms` role
- Nginx domain and reverse-proxy configuration
- Environment variable management through Jinja2 templates
- Docker Compose orchestration for the **SuiteCRM** application container
- Native **LDAP** authentication via Symfonys LDAP configuration
- OIDC-ready wiring for integration with Keycloak or other OIDC providers (via reverse proxy or plugin)
With this role, you get a production-ready CRM environment that plugs into your existing IAM stack.
## Features
- **Sales & Service CRM:** Accounts, Contacts, Leads, Opportunities, Cases, Campaigns and more 📊
- **Workflow Engine:** Automate business processes and notifications 🛠️
- **LDAP Authentication:** Centralize user authentication against OpenLDAP 🔐
- **OIDC-Ready SSO:** Preconfigured OIDC environment variables for use with plugins or an OIDC reverse proxy 🌐
- **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 📈
- **Modular Role Composition:** Uses shared roles for DB, proxy and monitoring to keep your stack consistent 🔄
## Further Resources
- [SuiteCRM Official Website](https://suitecrm.com/) 🌍
- [SuiteCRM Documentation](https://docs.suitecrm.com/) 📖
- [Infinito.Nexus Project Repository](https://s.infinito.nexus/code) 🔗
## OIDC & LDAP Notes
- **LDAP** is configured using Symfonys environment variables (`AUTH_TYPE=ldap`, `LDAP_*`) so SuiteCRM 8+ can authenticate directly against your OpenLDAP service.
- **OIDC** is provided at the platform level (e.g. Keycloak + oauth2-proxy or a SuiteCRM OIDC plugin).
This role exposes OIDC client, issuer and endpoint settings as environment variables, so plugins or
sidecar components can consume them without duplicating configuration.
## Credits
Developed and maintained by **Kevin Veen-Birkenbach**.
Consulting & Coaching Solutions: [veen.world](https://www.veen.world) 🌟
Part of the [Infinito.Nexus Project](https://s.infinito.nexus/code) 📂
License: [Infinito.Nexus NonCommercial License](https://s.infinito.nexus/license) ⚖️

View File

@@ -0,0 +1,40 @@
features:
matomo: true
css: true
desktop: true
ldap: true
oidc: true # OIDC via Keycloak + reverse proxy / plugin
central_database: true
logout: true
server:
csp:
flags: {}
whitelist:
# Allow data URIs for icons etc.
connect-src:
- "data:"
domains:
aliases: []
canonical:
- suite.crm.{{ PRIMARY_DOMAIN }}
email:
from_name: "Customer Relationship Management (SuiteCRM, {{ PRIMARY_DOMAIN }})"
docker:
services:
database:
enabled: true
suitecrm:
image: "bitnami/suitecrm"
version: "8"
name: "suitecrm"
cpus: 1.5
mem_reservation: 1.2g
mem_limit: 2g
pids_limit: 768
volumes:
data: suitecrm_data
maintenance_mode: false

View File

@@ -0,0 +1,23 @@
galaxy_info:
author: "Kevin Veen-Birkenbach"
description: >
Power your sales and service processes with SuiteCRM.
license: "Infinito.Nexus NonCommercial License"
license_url: "https://s.infinito.nexus/license"
company: |
Kevin Veen-Birkenbach
Consulting & Coaching Solutions
https://www.veen.world
galaxy_tags:
- suitecrm
- crm
- sales
repository: "https://s.infinito.nexus/code"
issue_tracker_url: "https://s.infinito.nexus/issues"
documentation: "https://docs.infinito.nexus"
logo:
class: "fa-solid fa-phone"
run_after:
- web-app-keycloak
- web-app-mailu
- web-app-mastodon

View File

View File

@@ -0,0 +1,6 @@
---
- name: "Load docker, db and proxy for {{ application_id }}"
include_role:
name: sys-stk-full-stateful
vars:
docker_compose_flush_handlers: true

View File

@@ -0,0 +1 @@
FROM "{{ SUITECRM_IMAGE }}:{{ SUITECRM_VERSION }}"

View File

@@ -0,0 +1,24 @@
{% include 'roles/docker-compose/templates/base.yml.j2' %}
{% set service_name = SUITECRM_SERVICE %}
{{ service_name }}:
{{ lookup('template', 'roles/docker-container/templates/build.yml.j2') | indent(4) }}
container_name: {{ SUITECRM_CONTAINER }}
image: "{{ SUITECRM_CUSTOM_IMAGE }}"
init: true
stop_signal: SIGTERM
stop_grace_period: 30s
{% include 'roles/docker-container/templates/base.yml.j2' %}
{% include 'roles/docker-container/templates/healthcheck/curl.yml.j2' %}
ports:
- "127.0.0.1:{{ ports.localhost.http[application_id] }}:80"
{% include 'roles/docker-container/templates/depends_on/dmbs_excl.yml.j2' %}
{% include 'roles/docker-container/templates/networks.yml.j2' %}
volumes:
- data:/bitnami/suitecrm
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
data:
name: {{ SUITECRM_DATA_VOLUME }}
{% include 'roles/docker-compose/templates/networks.yml.j2' %}

View File

@@ -0,0 +1,83 @@
#############################################
# SuiteCRM Docker Environment (.env) ENGLISH
# Based on bitnami/suitecrm environment variables
#############################################
# ------------------------------------------------
# Database connection (Bitnami-style)
# ------------------------------------------------
MARIADB_HOST={{ database_host }}
MARIADB_PORT={{ database_port }}
MARIADB_USER={{ database_username }}
MARIADB_PASSWORD={{ database_password }}
# ------------------------------------------------
# Initial admin account
# ------------------------------------------------
SUITECRM_USERNAME={{ applications | get_app_conf(application_id, 'users.administrator.username') }}
SUITECRM_PASSWORD={{ applications | get_app_conf(application_id, 'credentials.administrator_password') }}
SUITECRM_EMAIL={{ users['contact'].email }}
SUITECRM_HOST={{ domains | get_domain(application_id) }}
# ------------------------------------------------
# Logger / basic options
# (SuiteCRM/Bitnami will log to stdout/stderr by default)
# ------------------------------------------------
# ------------------------------------------------
# System SMTP settings
# ------------------------------------------------
SUITECRM_SMTP_HOST={{ SYSTEM_EMAIL.HOST }}
SUITECRM_SMTP_PORT={{ SYSTEM_EMAIL.PORT }}
SUITECRM_SMTP_USER={{ users['contact'].email }}
SUITECRM_SMTP_PASSWORD={{ users['contact'].mailu_token }}
SUITECRM_SMTP_PROTOCOL={{ "TLS" if SYSTEM_EMAIL.START_TLS else "SSL" }}
# ------------------------------------------------
# LDAP settings (native SuiteCRM 8+ via Symfony)
# Applied only if the feature flag is true
# ------------------------------------------------
{% if applications | get_app_conf(application_id, 'features.ldap') %}
AUTH_TYPE=ldap
# Base LDAP connection
LDAP_HOST={{ LDAP.SERVER.DOMAIN }}
LDAP_PORT={{ LDAP.SERVER.PORT }}
# Allowed values: none, tls, ssl (see SuiteCRM docs)
LDAP_ENCRYPTION={{ LDAP.SERVER.SECURITY | lower if LDAP.SERVER.SECURITY else "none" }}
LDAP_PROTOCOL_VERSION=3
LDAP_REFERRALS=false
# DN / search configuration
LDAP_DN_STRING='{{ LDAP.USER.ATTRIBUTES.ID }}={username},{{ LDAP.DN.OU.USERS }}'
LDAP_QUERY_STRING=''
LDAP_SEARCH_DN={{ LDAP.DN.ADMINISTRATOR.DATA }}
LDAP_SEARCH_PASSWORD={{ LDAP.BIND_CREDENTIAL }}
# Auto-create SuiteCRM users from LDAP
LDAP_AUTO_CREATE=enabled
LDAP_PROVIDER_BASE_DN={{ LDAP.DN.OU.USERS }}
LDAP_PROVIDER_SEARCH_DN={{ LDAP.DN.ADMINISTRATOR.DATA }}
LDAP_PROVIDER_SEARCH_PASSWORD={{ LDAP.BIND_CREDENTIAL }}
LDAP_PROVIDER_DEFAULT_ROLES=ROLE_USER
LDAP_PROVIDER_UID_KEY={{ LDAP.USER.ATTRIBUTES.ID }}
LDAP_PROVIDER_FILTER='({{ LDAP.USER.ATTRIBUTES.ID }}={username})'
{% endif %}
# ------------------------------------------------
# OpenID Connect settings (platform / plugin level)
# Applied only if the feature flag is true
# ------------------------------------------------
{% if SUITECRM_OIDC_ENABLED | bool %}
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_ENDPOINT={{ OIDC.CLIENT.AUTHORIZE_URL }}
SUITECRM_OIDC_TOKEN_ENDPOINT={{ OIDC.CLIENT.TOKEN_URL }}
SUITECRM_OIDC_USER_INFO_ENDPOINT={{ OIDC.CLIENT.USER_INFO_URL }}
SUITECRM_OIDC_JWKS_ENDPOINT={{ OIDC.CLIENT.CERTS }}
SUITECRM_OIDC_REDIRECT_URI={{ SUITECRM_URL }}/oidc/callback
SUITECRM_OIDC_SCOPES=openid,profile,email
{% endif %}

View File

@@ -0,0 +1,7 @@
users:
administrator:
username: "administrator"
contact:
username: "contact"
roles:
- mail-bot

View File

@@ -0,0 +1,24 @@
# General
application_id: "web-app-suitecrm"
entity_name: "{{ application_id | get_entity_name }}"
# Database
database_type: "mariadb"
# Webserver
client_max_body_size: "100m"
vhost_flavour: "generic"
# SuiteCRM container settings
SUITECRM_VERSION: "{{ applications | get_app_conf(application_id, 'docker.services.' ~ entity_name ~ '.version') }}"
SUITECRM_IMAGE: "{{ applications | get_app_conf(application_id, 'docker.services.' ~ entity_name ~ '.image') }}"
SUITECRM_CUSTOM_IMAGE: "custom_suitecrm"
SUITECRM_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.' ~ entity_name ~ '.name') }}"
SUITECRM_SERVICE: "{{ entity_name }}"
# Volumes
SUITECRM_DATA_VOLUME: "{{ applications | get_app_conf(application_id, 'docker.volumes.data') }}"
# URLs & features
SUITECRM_URL: "{{ domains | get_url(application_id, WEB_PROTOCOL) }}"
SUITECRM_OIDC_ENABLED: "{{ applications | get_app_conf(application_id, 'features.oidc') }}"