Skip to main content
https://catalogartifact.azureedge.net/publicartifacts/lynxroute.esprocrm-560cff75-e79f-4b2a-a898-f4222f60cfe1/image2_Azureready.png

EspoCRM - Hardened Self-Hosted CRM

by Lynxroute

EspoCRM - CIS Level 1 hardened self-hosted CRM on Ubuntu 24.04 LTS with SBOM and CIS Report.

What is EspoCRM

EspoCRM is a mature open-source CRM (12+ years on the market) covering the full revenue pipeline - leads, accounts, contacts, opportunities, calendar, email integration, document storage, and reporting. Built on PHP 8 with PHP-FPM and MySQL 8.0, served by Nginx. The community edition is feature-complete for small-to-mid sales teams: multi-currency, role-based access control, custom entities and fields, formula scripting, workflow automation, and IMAP-aware email-to-record assignment.

Why self-host EspoCRM

Self-hosting puts every lead record, account note, and email thread inside your own Azure subscription - no per-seat SaaS fees and no third-party data residency questions. Suited to organisations with strict data residency requirements (GDPR, SOC 2, internal compliance), regulated industries that need an auditable on-premises sales platform, and teams moving from subscription CRM services to a self-hosted equivalent. AGPL-3.0 source - fully auditable, no vendor lock-in.

What this VM image adds

Security hardening:

  • Admin password generated per instance - 20-character random password, stored in /root/espocrm-credentials.txt at first boot, never the same on two deployments
  • Database password generated per instance - rotated at first boot, internal only, never written to user-facing files
  • MySQL 8.0 listens on localhost only - no exposed database port; espocrm DB user limited to localhost
  • PHP-FPM tuned at first boot - pm.max_children and OPcache memory sized from instance RAM
  • Self-signed TLS at first boot - HTTPS on port 443 from launch; port 80 redirects to 443; replace with Let's Encrypt via Certbot for production
  • Install wizard locked after first boot - /var/www/espocrm/install set to mode 0000 so the web installer cannot be re-triggered
  • Trivy CVE scan - every image is scanned before release; CRITICAL/HIGH with available fix block the build
  • UFW firewall - only ports 22, 80, and 443 open
  • fail2ban - SSH brute-force protection
  • AppArmor - mandatory access control on system services

OS hardening (CIS Level 1):

  • CIS Level 1 hardened - CIS Ubuntu 24.04 LTS Level 1 Benchmark applied via ansible-lockdown
  • auditd - system call auditing for critical paths
  • SSH hardening - PasswordAuthentication disabled, key-only access, MaxAuthTries 4
  • Kernel hardening - SYN cookies, ASLR, rp_filter, TCP BBR
  • /tmp as tmpfs - nosuid, nodev, noexec
  • Azure IMDS endpoints - egress rules pre-configured (169.254.169.254, 168.63.129.16)

Compliance artifacts (inside the VM):

  • SBOM - CycloneDX 1.6 at /etc/lynxroute/sbom.json with SHA-256 hashes
  • CIS Conformance Report - OpenSCAP HTML at /etc/lynxroute/cis-report.html, 0 FAIL rules on the tailored profile
  • Tailored CIS profile - /usr/share/doc/lynxroute/CIS_TAILORED_PROFILE.md with documented exceptions
  • Server credentials file - /root/espocrm-credentials.txt with the public IP, the web UI URL, and the per-instance admin password

Quick Start

  1. Deploy VM from Azure Marketplace (Standard_D2s_v3 or larger recommended)
  2. Open NSG: TCP 443 from your client networks - SSH 22 from your management IPs only
  3. SSH: ssh -i key.pem <username>@<PUBLIC_IP> (username set during VM creation, default: azureuser)
  4. Wait 60-90 seconds for first-boot setup; the page at https://<PUBLIC_IP>/ shows a "Starting up" splash during this window
  5. Read admin password: sudo cat /root/espocrm-credentials.txt
  6. Open https://<PUBLIC_IP>/, accept the self-signed cert warning, log in as "admin" with the generated password
  7. Change the admin password immediately (User Profile - Change Password)
  8. Issue a trusted HTTPS certificate (recommended for production): sudo apt install certbot python3-certbot-nginx -y && sudo certbot --nginx -d your.domain.com