Deployment guide

WBCE CMS 1.6.9

Overview

WBCE CMS (WebsiteBaker Community Edition) is a free and open source content management system written in PHP. It offers a tree of pages built from content sections (WYSIWYG text with CKEditor, news with images, contact forms, menu links, wrappers), droplets (small snippets you place in a page), output filters, a media manager and an administration area at /admin.

This guide covers the WBCE CMS 1.6.9 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud BookStack, CMS, Blogs and Wikis listing on AWS Marketplace. You pick it as the software version when you subscribe and launch.

What you get from the SecureAnyCloud image:

  • No reachable installer, no shared database. WBCE's browser installer creates the site and the first administrator for whoever opens it first. This image ships no database at all (the MariaDB data directory is empty), no configuration file and no installer in the web root. Your instance's first boot initializes its own database and runs WBCE's own installer before the web server starts. The installer runs on a temporary server on a random local port that answers only requests carrying a token generated for that boot. First boot then stops that server and removes the installer.
  • Secrets of your instance's own, never on a command line. The database password, the administrator password, the secret WBCE signs its form tokens with and the TLS key are generated on your instance, and nothing from the image build is reused. Without its own secret, WBCE signs forms with a constant string that is the same in every copy. The installer's form, which carries the passwords, is handed over in a file that only root can read, never as a command line argument, and its replies are never logged. The administrator's password is stored with bcrypt.
  • HTTPS from the first boot. nginx serves WBCE on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS.
  • Fail closed. nginx does not start until first boot has finished. If first boot cannot complete, the site stays offline rather than open.
  • Code read-only to PHP, page files read as data. The WBCE code belongs to root. PHP can write only media (your uploads), pages (the small page files WBCE writes for every page), temp and var. nginx never runs a PHP file from those folders. A root-owned page router reads the page files, takes only the page number out of a file in exactly the form WBCE writes, and renders the page with WBCE's own index.php.
  • Static-only uploads. media is static only. Every file gets nosniff and a sandboxing content security policy. Only passive types (images, PDF, fonts, audio and video) open in the browser, and anything else, SVG and HTML included, is sent as a download. temp, var, the framework and library code, language files and every text, SQL and template file are never served, and /x.php/<anything> is never a PHP request.
  • Forms and back-end links from other sites cannot act for you. Every request that changes something (every form submission, on any address of the site) and every link into the administration area and the modules only works when the request comes from your site itself (Step 9).
  • A rate-limited sign-in. On the administration and front-end forms, nginx allows 10 sign-in and password-reset attempts a minute from one address, after a burst of 10. It uses the address of the connection, never a header a client sends. WBCE's own failed-login blocking and CAPTCHA stay on.
  • The real host and client address. nginx hands WBCE the host name you configure and nothing a request claims, and WB_URL in the configuration is pinned to it, so every link and e-mail uses your host name. nginx removes X-Forwarded-For, Client-IP and the other forwarding headers, so WBCE's sign-in blocking and its users list record the address of the connection.
  • A PHP that cannot reach the shell. In the web server and on the command line alike, PHP cannot run shell commands, fork, change its environment, load extensions or use FFI. Because it cannot change its environment, the putenv + mail() trick that loads a planted library does not work. The web server is confined to the site folder.
  • No calls home. The dashboard's GitHub release check is off. The online updater, which downloads releases and overwrites the code, is not shipped, so you update from the shell instead (Step 12).
  • Hardened defaults. MariaDB has no network listener at all. WBCE talks to it over a local socket, and its root account signs in only through the socket as the operating system's root. WBCE writes its files with mode 0640 and its folders with 0750. nginx, PHP, MariaDB and the first-boot service are blocked from the instance metadata service, the AMI requires IMDSv2, and requests for a host name the instance does not serve are dropped.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

The image contains WBCE CMS 1.6.9 (the upstream release, checksum verified, without the online updater module), MariaDB 10.11, PHP 8.3 FPM, nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.

Default user: ubuntu. Architecture: x86_64. Recommended instance type: m5.large.

Prerequisites

  • An AWS account subscribed to the listing, an EC2 key pair, and a VPC subnet with a route to the internet (or a private route you use for management).
  • A security group that allows TCP 22 (SSH), TCP 443 (WBCE CMS) and TCP 80 (redirects to HTTPS) from the networks that should reach them. Keep 22 to your own networks. Open 443 to your visitors for a public site, or only to your team while you build it.
1

Launch the instance

In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick WBCE CMS 1.6.9 on Ubuntu 24.04 as the software version, select your Region, and choose Launch through EC2. Choose m5.large, your key pair and a security group as described above.

To launch with the AWS CLI instead, replace the values in angle brackets:

aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
  --instance-type m5.large --key-name <your-key-pair> \
  --security-group-ids <your-security-group> --subnet-id <your-subnet> \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=wbce-cms}]'
2

Connect to your instance

Connect over SSH as the ubuntu user with the key pair you launched with.

ssh -i <your-key.pem> ubuntu@<public-ip>
3

Check the services

First boot takes under a minute, because it creates the database and runs WBCE's installer. Afterwards, nginx, PHP, MariaDB and the first-boot unit are active.

systemctl is-active nginx php8.3-fpm mariadb wbce-firstboot
active active active active

nginx serves ports 80 and 443. PHP and MariaDB listen on local sockets only, and the installer's temporary server is gone, so the only open ports are 22, 80 and 443.

sudo ss -tlnp | awk 'NR==1 || /nginx|sshd|php|maria/' | awk '{print $1, $4, $6}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g'
State Local Peer LISTEN 0.0.0.0:443 users:(("nginx"),("nginx"),("nginx")) LISTEN 0.0.0.0:80 users:(("nginx"),("nginx"),("nginx")) LISTEN 0.0.0.0:22 users:(("sshd"),("systemd")) LISTEN [::]:443 users:(("nginx"),("nginx"),("nginx")) LISTEN [::]:80 users:(("nginx"),("nginx"),("nginx")) LISTEN [::]:22 users:(("sshd"),("systemd"))

Check the WBCE release, the editor, the PHP and MariaDB versions, and how the download was verified.

grep -E "define\('NEW_WBCE_(VERSION|TAG)'" /var/www/wbce/admin/interface/version.php
grep -o 'version:"[0-9.]*"' /var/www/wbce/modules/ckeditor/ckeditor/ckeditor.js | head -1 php -v | head -1 mariadb --version cat /usr/share/doc/wbce/RELEASE
define('NEW_WBCE_VERSION', '1.6.9'); // NEW_WBCE_VERSION define('NEW_WBCE_TAG', '1.6.9'); // NEW_WBCE_TAG version:"4.22.1" PHP 8.3.6 (cli) (built: Oct 19 2026 15:16:58) (NTS) mariadb Ver 15.1 Distrib 10.11.14-MariaDB, for debian-linux-gnu (x86_64) using EditLine wrapper WBCE CMS 1.6.9 source https://codeload.github.com/WBCE/WBCE_CMS/tar.gz/refs/tags/1.6.9 commit 4bd2a809d7a955f65e2907a1256cc3294216b9ea sha256 920640d940573a120b0af30e45b0badb8d7e537817fbd84d82a35086104b53d6 not shipped: modules/wbce_updater (online self-updater; the code tree is root-owned) miniform defaults moved to templates/ at build (its installer step)
4

Get your sign-in details

The credentials file holds the address WBCE is served as (your instance's public DNS name by default), the administration address, the administrator's user name, e-mail and password, and the database credentials. All of them are generated on this instance. Show the file with:

sudo cat /root/wbce-credentials.txt
WBCE_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ WBCE_ADMIN_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/admin/ WBCE_ADMIN_USER=admin WBCE_ADMIN_PASSWORD=<24 letters and digits> WBCE_ADMIN_EMAIL=admin@example.com WBCE_DB_NAME=wbce WBCE_DB_USER=wbce WBCE_DB_PASSWORD=<48 hexadecimal characters>

The administrator starts with the e-mail admin@example.com. Change it to your own address under Preferences before you rely on WBCE's password e-mails.

5

Visit the site and sign in

Open WBCE_URL in your browser. The certificate is self-signed and generated on your instance, so your browser warns once. Compare the certificate's SHA-256 fingerprint with the one on the instance (sudo openssl x509 -in /etc/nginx/ssl/wbce.crt -noout -fingerprint -sha256) and continue. Alternatively, install a certificate from your own certificate authority (Step 11).

A fresh site shows one Home page in WBCE's wbcezon template. WBCE's installer creates no page, so first boot adds this one and the site answers at once.

Open WBCE_ADMIN_URL and sign in with WBCE_ADMIN_USER and WBCE_ADMIN_PASSWORD. The administration area opens on its dashboard, with the menu on the left.

Move around the administration area with its menu and links. Back-end pages only open from your site itself (Step 9). A back-end address typed into the address bar, bookmarked or opened from an e-mail is refused, except the dashboard (/admin/) and the sign-in page, so start there.

The administrator can do everything, including things that run code on the server: install add-ons, and write droplets (PHP snippets) and output filters. Keep this account to yourself. For editors, create accounts under Access > Users in a group with only the page and media permissions they need.

6

Pages, sections and the editor

Pages live in the page tree under Pages. A page is made of sections, each of one type: WYSIWYG text, news with images, a mini form, a menu link, a wrapper and more. Add pages and sections from here.

A WYSIWYG section is edited with CKEditor 4.22.1. Save publishes the section. Every page has an address under /pages/, and the Home page is /pages/home.php.

Media opens the elFinder file manager for your uploads in /var/www/wbce/media. The editor's image and link dialogs use it too.

WBCE writes a small file in /var/www/wbce/pages for every page, and the file only names the page's number. nginx never runs those files. The page router reads the number out of a file in exactly the form WBCE writes and shows the page with WBCE's own index.php. Anything else in that folder, a planted script included, is answered with 404.

The optional "intro page" (a free-form pages/intro.php under Settings) is therefore not served on this image.

7

The closed-by-default settings

The image closes these settings by default. Each entry shows the image default and where to change it.

  • Front-end sign-up and sign-in: off, as WBCE ships. Change under Settings (Show advanced options).
  • Dashboard release check (SHOW_UPDATE_INFO): off, so there is no call to GitHub. Change in config.php (see Step 12).
  • Online updater: not shipped. Update from the shell (Step 12).
  • File and folder modes WBCE writes: 0640 and 0750. Change under Settings (Show advanced options).
  • Form-signing secret: generated on your instance. Change under Admin-Tools > SecureForm Switcher.
  • Site address (WB_URL): https://<your host>. Change with sudo wbce-set-hostname (Step 11).

Confirm the configuration and the database settings:

sudo php /usr/local/lib/sac/wbce/write-config.php parse /var/www/wbce/config.php | grep -E '^(WB_URL|SHOW_UPDATE_INFO|DB_NAME|DB_USERNAME|TABLE_PREFIX)=' | sed -E 's#https://[^/]+#https://<your host>#'
sudo mysql -N -B --raw wbce -e "SELECT name, value FROM wbce_settings WHERE name IN ('frontend_signup','frontend_login','string_file_mode','string_dir_mode') ORDER BY name"
DB_NAME=wbce DB_USERNAME=wbce TABLE_PREFIX=wbce_ WB_URL=https://<your host> SHOW_UPDATE_INFO=false frontend_login false frontend_signup false string_dir_mode 0750 string_file_mode 0640

Every PHP page also carries a content security policy that lets it connect only to your own server.

H=$(sed -n 's/^set \$wbce_host "\(.*\)";$/\1/p' /etc/nginx/wbce-host.conf)
curl -sk -D - -o /dev/null --resolve "$H:443:127.0.0.1" "https://$H/admin/login/index.php" | tr -d '\r' | grep -i '^content-security-policy'
content-security-policy: connect-src 'self'; frame-ancestors 'self'; object-src 'none'; base-uri 'self'
8

Your data

Everything that makes your site lives in the wbce database in MariaDB and in the uploads under /var/www/wbce/media. WBCE rewrites the page files in /var/www/wbce/pages whenever a page is saved, and temp and var hold caches and logs. Keep config.php with your backup, because it holds this site's database password.

Back up the database to a file that only root can read. MariaDB's root signs in over the local socket without a password, so no password appears on the command line.

sudo sh -c 'umask 077; mysqldump --single-transaction wbce > /root/wbce-$(date +%F).sql'
sudo tar -C /var/www/wbce -czf /root/wbce-files-$(date +%F).tgz config.php media pages

For a whole-instance copy, snapshot the EBS volume.

9

Security checks

Port 80 sends browsers to HTTPS, and the health endpoint answers for load balancer checks. The installer, the configuration, the framework code, temp, var, planted PHP in the writable folders, a path after a PHP file name and the removed updater are not reachable.

H=$(sed -n 's/^set \$wbce_host "\(.*\)";$/\1/p' /etc/nginx/wbce-host.conf)
curl -s -o /dev/null -w 'port 80: %{http_code} -> %{redirect_url}\n' --resolve "$H:80:127.0.0.1" "http://$H/admin/" | sed -E 's#https://[^/]+#https://<your host>#'
curl -sk --resolve "$H:443:127.0.0.1" "https://$H/healthz" for p in /install/ /config.php /framework/initialize.php /temp/ /var/ /media/x.php /pages/x.php /index.php/admin /modules/wbce_updater/info.php; do
curl -sk -o /dev/null -w "$p: %{http_code}\n" -H 'Sec-Fetch-Site: same-origin' --resolve "$H:443:127.0.0.1" "https://$H$p" done
curl -s -o /dev/null -w 'unknown host name: %{http_code}\n' -H 'Host: example.com' http://127.0.0.1/ || true
port 80: 301 -> https://<your host>/admin/ ok /install/: 404 /config.php: 404 /framework/initialize.php: 404 /temp/: 404 /var/: 404 /media/x.php: 404 /pages/x.php: 404 /index.php/admin: 404 /modules/wbce_updater/info.php: 404 unknown host name: 000

The status 000 means nginx closed the connection without an answer.

The credentials file is readable by root only, and the configuration file by root and the web server. The code belongs to root, and only media, pages, temp and var belong to the web server.

sudo stat -c '%a %U:%G %n' /root/wbce-credentials.txt /var/www/wbce/config.php /var/www/wbce /var/www/wbce/media /var/www/wbce/pages /var/www/wbce/temp /var/www/wbce/var /var/www/wbce/index.php /var/www/wbce/pages/home.php
600 root:root /root/wbce-credentials.txt 640 root:www-data /var/www/wbce/config.php 755 root:root /var/www/wbce 750 www-data:www-data /var/www/wbce/media 750 www-data:www-data /var/www/wbce/pages 750 www-data:www-data /var/www/wbce/temp 750 www-data:www-data /var/www/wbce/var 644 root:root /var/www/wbce/index.php 640 www-data:www-data /var/www/wbce/pages/home.php

This is the Home page's page file, as WBCE writes it. The router takes only $page_id from it.

sudo cat /var/www/wbce/pages/home.php
<?php // *** This file is created by WBCE v1.6.9 // *** Creation date: <first boot time> // *** Do not modify this file manually // *** WBCE will rebuild this file from time to time!! // ************************************************* $page_id = 1; require('../index.php'); // *************************************************

An uploaded HTML file is sent as a download with nosniff and the sandboxing policy. It is never rendered as a page of your site.

H=$(sed -n 's/^set \$wbce_host "\(.*\)";$/\1/p' /etc/nginx/wbce-host.conf)
echo '<b>demo</b>' | sudo -u www-data tee /var/www/wbce/media/guide-demo.html >/dev/null curl -sk -D - -o /dev/null --resolve "$H:443:127.0.0.1" "https://$H/media/guide-demo.html" | tr -d '\r' | grep -iE '^(HTTP|content-disposition|content-security-policy|x-content-type)'
sudo -u www-data rm -f /var/www/wbce/media/guide-demo.html
HTTP/2 200 x-content-type-options: nosniff content-security-policy: default-src 'none'; img-src 'self' data:; style-src 'unsafe-inline'; sandbox content-disposition: attachment

The certificate was generated on this instance, names its own addresses and can only vouch for itself. It is not a certificate authority.

H=$(sed -n 's/^set \$wbce_host "\(.*\)";$/\1/p' /etc/nginx/wbce-host.conf)
echo | openssl s_client -connect 127.0.0.1:443 -servername "$H" 2>/dev/null | openssl x509 -noout -ext basicConstraints,extendedKeyUsage
X509v3 Basic Constraints: critical CA:FALSE X509v3 Extended Key Usage: TLS Web Server Authentication

The next commands confirm the following:

  • nginx, PHP, MariaDB and the first-boot service cannot reach the instance metadata service. The installer's temporary server ran inside the first-boot service, so it could not either.
  • PHP cannot run shell commands, fork, change its environment or use FFI, and it is confined to the site folder.
  • nginx hands PHP the configured host name, removes forwarding headers and any Authorization header a client sends, never passes a path after a PHP file name, and rate-limits requests.
for u in nginx php8.3-fpm mariadb wbce-firstboot; do systemctl show $u -p IPAddressDeny; done php-fpm8.3 -i 2>/dev/null | grep -E '^(open_basedir|disable_functions|expose_php|ffi.enable) ' | cut -d' ' -f1-3 grep -E 'fastcgi_param (HTTP_HOST|SERVER_NAME|HTTP_X_FORWARDED_FOR|HTTP_CLIENT_IP|HTTP_X_FORWARDED_HOST|HTTP_AUTHORIZATION|PATH_INFO) ' /etc/nginx/snippets/sac-wbce-fastcgi-file.conf grep -h 'limit_req_zone\|limit_req_status' /etc/nginx/conf.d/sac-wbce-guard.conf
IPAddressDeny=168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128 IPAddressDeny=168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128 IPAddressDeny=168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128 IPAddressDeny=168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128 disable_functions => exec,passthru,shell_exec,system,proc_open,popen,pcntl_exec,pcntl_fork,putenv,dl,imap_open expose_php => Off open_basedir => /var/www/wbce/:/usr/local/lib/sac/wbce-web/:/var/lib/php/sessions/:/tmp/ ffi.enable => Off fastcgi_param PATH_INFO ""; fastcgi_param HTTP_HOST $wbce_host; fastcgi_param SERVER_NAME $wbce_host; fastcgi_param HTTP_X_FORWARDED_HOST ""; fastcgi_param HTTP_X_FORWARDED_FOR ""; fastcgi_param HTTP_CLIENT_IP ""; fastcgi_param HTTP_AUTHORIZATION ""; limit_req_zone $wbce_login_key zone=wbce_login:10m rate=10r/m; limit_req_zone $wbce_post_key zone=wbce_post:10m rate=60r/m; limit_req_zone $binary_remote_addr zone=wbce_php:10m rate=600r/m; limit_req_status 429;

The three request limits work as follows:

  • wbce_login counts sign-in, password-reset and sign-up attempts from one address: 10 a minute after a burst of 10.
  • wbce_post counts every request that sends data: 60 a minute after a burst of 60.
  • wbce_php counts every page: 600 a minute.

If your site needs higher limits, raise them in /etc/nginx/conf.d/sac-wbce-guard.conf and /etc/nginx/snippets/sac-wbce-fastcgi-*.conf, then run sudo nginx -t && sudo systemctl reload nginx. The same PHP settings apply on the command line (/etc/php/8.3/cli/conf.d/99-sac-wbce.ini).

sudo wbce-cred-roundtrip checks the whole sign-in path on the instance:

  • The redirect to HTTPS works.
  • No installer is offered, and the configuration is refused.
  • A sign-in through the real sign-in form works with the password from the credentials file.
  • A wrong password and a common password are both refused.
sudo wbce-cred-roundtrip
OK https 301 + no installer + config.php 404 + administrator sign-in + wrong and default passwords refused

Requests from another site. Every request that sends data (every form, on any address), every back-end page under /admin/<area>/ and every module address under /modules/ only works when the request comes from your site itself. nginx decides this from the browser's Sec-Fetch-Site, Origin and Referer headers, and a request with none of them is refused too. Another site therefore cannot make you act through a link or a hidden form.

The site's pages, the search, the sign-in pages, the dashboard at /admin/ and the news feed stay linkable. Scripts that call the back end must send Sec-Fetch-Site: same-origin.

H=$(sed -n 's/^set \$wbce_host "\(.*\)";$/\1/p' /etc/nginx/wbce-host.conf)
curl -sk -o /dev/null -w 'page tree link from another site: %{http_code}\n' -H 'Sec-Fetch-Site: cross-site' --resolve "$H:443:127.0.0.1" "https://$H/admin/pages/index.php"
curl -sk -o /dev/null -w 'form post from another site: %{http_code}\n' -H 'Sec-Fetch-Site: cross-site' -H 'Origin: https://example.com' --data 'x=1' --resolve "$H:443:127.0.0.1" "https://$H/modules/wysiwyg/save.php"
curl -sk -o /dev/null -w 'sign-in page from another site: %{http_code}\n' -H 'Sec-Fetch-Site: cross-site' --resolve "$H:443:127.0.0.1" "https://$H/admin/login/index.php"
curl -sk -o /dev/null -w 'home page from another site: %{http_code}\n' -H 'Sec-Fetch-Site: cross-site' --resolve "$H:443:127.0.0.1" "https://$H/pages/home.php"
page tree link from another site: 403 form post from another site: 403 sign-in page from another site: 200 home page from another site: 200

The rules are in /etc/nginx/conf.d/sac-wbce-guard.conf and /etc/nginx/conf.d/sac-xsite-guard.conf. wbce-set-hostname pins the latter to your host names.

10

Behind a proxy or load balancer

nginx removes X-Forwarded-For, X-Real-IP, Client-IP, X-Forwarded, Forwarded-For and Forwarded from every request, so WBCE's failed-login blocking and the users' last sign-in address use the address of the connection.

Behind an AWS load balancer, that is the load balancer's address. All visitors then share one failed-login counter, and nginx's own access log (/var/log/nginx/access.log) records the address the same way.

To record visitors' addresses, use nginx's real_ip module with set_real_ip_from set to your load balancer's subnet only.

11

Your own DNS name and certificate

Point a DNS record at the instance, then set the name WBCE is served as. The command below does the following:

  • Updates WB_URL in config.php, which WBCE builds every absolute link from.
  • Mints a new self-signed certificate for the name.
  • Updates nginx, which hands the name to WBCE, and the cross-site rules.
  • Clears WBCE's caches.
  • Updates the addresses in /root/wbce-credentials.txt.
  • Keeps the name across reboots.
sudo wbce-set-hostname <your-domain>

To use a certificate from your own certificate authority instead, copy it over the generated pair and reload nginx. Running wbce-set-hostname again replaces it with a self-signed one.

sudo install -m 0644 <your-domain>.crt /etc/nginx/ssl/wbce.crt
sudo install -m 0600 <your-domain>.key /etc/nginx/ssl/wbce.key
sudo nginx -t && sudo systemctl reload nginx

Once browsers trust the certificate you can also add an HSTS header (add_header Strict-Transport-Security "max-age=31536000" always;) to /etc/nginx/snippets/sac-wbce-headers.conf. The image does not send one, because browsers ignore HSTS from a site whose certificate they do not trust.

12

Passwords, settings, e-mail, add-ons and upgrades

wbce-set-password gives the administrator a new random password with WBCE's own password hashing (bcrypt), clears WBCE's failed-login blocking, signs out every session and records the password in /root/wbce-credentials.txt.

Run it without arguments, or pass your own password on stdin. A password must be 24 to 30 letters and digits, because WBCE's sign-in form accepts up to 30 characters. Users change their own password under Preferences.

sudo wbce-set-password printf '%s\n' '<your-new-password>' | sudo wbce-set-password --stdin

The configuration file is read-only to PHP. config.php is executed on every request, so the image does not let the web server rewrite it. Otherwise a compromised PHP process could change the site permanently. The SecureAnyCloud tools never execute it either: they read it as data and refuse it unless it belongs to root, is mode 0640 and holds plain define(...) values only.

WBCE keeps its settings in the database and saves them normally. To turn the dashboard's release check back on, change define('SHOW_UPDATE_INFO', false); to true in /var/www/wbce/config.php as root.

E-mail. By default, WBCE sends password and form e-mails with PHP's mail function. The image has no mail server, so set an SMTP server under Settings (Show advanced options, Mailer) before you rely on e-mail.

Add-ons, templates and module files. The code folders belong to root, so the administration area cannot install add-ons or templates, or edit module and template files. Install an add-on from the shell. As root, unpack it into /var/www/wbce/modules/<name> (or templates/<name>), then register it under Add-ons (Advanced, reload the add-on lists). Install only add-ons you trust, because an add-on is PHP that runs with your site's rights.

Upgrades. WBCE releases are published at https://github.com/WBCE/WBCE_CMS/releases. Take a snapshot first. Then, as root, copy the new release's wbce folder over /var/www/wbce. Keep config.php, media, pages, temp and var, and leave out install and modules/wbce_updater.

If the release has an upgrade script, copy it in, open it once from the site, then delete it. Finally, check that the code belongs to root again and that config.php is 0640 root:www-data.

13

Server components

Component Path

WBCE code (root owned) /var/www/wbce

Configuration (this instance's database password) /var/www/wbce/config.php

Uploads /var/www/wbce/media

Page files (read by the page router) /var/www/wbce/pages

Caches and logs /var/www/wbce/temp, /var/www/wbce/var

Page router /usr/local/lib/sac/wbce-web/page-router.php

Database (MariaDB datadir) /var/lib/mysql

nginx site, request limits and cross-site rules /etc/nginx/sites-available/sac-wbce, /etc/nginx/conf.d/sac-wbce-guard.conf, /etc/nginx/conf.d/sac-xsite-guard.conf, /etc/nginx/snippets/sac-wbce-*.conf

PHP settings /etc/php/8.3/fpm/conf.d/99-sac-wbce.ini, /etc/php/8.3/cli/conf.d/99-sac-wbce.ini

MariaDB settings /etc/mysql/mariadb.conf.d/99-sac-wbce.cnf

Certificate and key /etc/nginx/ssl/

First boot script, installer copy and helpers /usr/local/sbin/wbce-firstboot.sh, /usr/local/lib/sac/wbce/

Helpers /usr/local/sbin/wbce-set-hostname, wbce-set-password, wbce-cred-roundtrip

Licence, release and image notes /usr/share/doc/wbce/

Credentials /root/wbce-credentials.txt (mode 0600)

14

Security recommendations

  • Allow port 22 in the security group from your own networks only.
  • Change the administrator e-mail to your own (Step 4), keep its password secret and rotate it if it may have been shared (Step 12). The administrator can run code on the server (add-ons, droplets, output filters): give editors their own accounts with narrower groups.
  • Keep config.php and your database backups secret (Step 8).
  • Use your own DNS name and a certificate your browsers trust (Step 11).
  • Keep WBCE on the newest release (Step 12) and install only add-ons you trust.
  • Keep the operating system patched; unattended security upgrades are on, and sudo apt-get update && sudo apt-get upgrade applies everything else. MariaDB, PHP-FPM and PHP's zip extension come from Ubuntu's universe repository; sudo pro security-status --esm-apps shows whether Ubuntu Pro (ESM Apps) has security updates for them (none were pending when the image was built).

Support and licensing

WBCE CMS is free software under the GNU General Public License, version 2 (a copy is at /usr/share/doc/wbce/LICENSE); its bundled modules, templates and libraries (CKEditor 4.22.1, elFinder, jQuery and others) carry their own open source licences. There is no per user or per CPU licence fee and no licence key. This image installs the upstream release without its online updater module, plus the SecureAnyCloud first boot, settings and helpers described above. The AMI price covers SecureAnyCloud's hardening, first boot setup and support.

  • Email: sales@secureanycloud.com
  • Website: https://secureanycloud.com
  • Support: 24/7, with a 24 hour response SLA

Deploy on AWS

Launch WBCE CMS 1.6.9 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on AWS