feat: convert WordOps from Nginx to OpenLiteSpeed + LSPHP + LSCache
Complete conversion of the WordOps stack from Nginx + PHP-FPM to OpenLiteSpeed + LSPHP + LSCache. This is a full rewrite across all 7 phases of the codebase: - Foundation: OLS paths, variables, services, removed pynginxconfig dep - Templates: 11 new OLS mustache templates, removed nginx-specific ones - Stack: stack_pref, stack, stack_services, stack_upgrade, stack_migrate - Site: site_functions, site, site_create, site_update - Plugins: debug, info, log, clean rewritten for OLS - SSL/ACME: acme.sh deploy uses lswsctrl, OLS vhssl blocks - Other: secure, backup, clone, install script Additional features: - Debian 13 (trixie) support - PHP 8.5 support - WP Fort Knox mu-plugin integration (wo secure --lockdown/--unlock) - --nginx CLI flag preserved for backward compatibility Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import getpass
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from cement.core.controller import CementBaseController, expose
|
||||
|
||||
@@ -40,6 +41,12 @@ class WOSecureController(CementBaseController):
|
||||
(['--allowpassword'], dict(
|
||||
help='allow password authentification '
|
||||
'when hardening ssh security', action='store_true')),
|
||||
(['--lockdown'], dict(
|
||||
help='enable WP Fort Knox lockdown on a site',
|
||||
action='store_true')),
|
||||
(['--unlock'], dict(
|
||||
help='disable WP Fort Knox lockdown on a site',
|
||||
action='store_true')),
|
||||
(['--force'],
|
||||
dict(help='force execution without being prompt',
|
||||
action='store_true')),
|
||||
@@ -62,12 +69,16 @@ class WOSecureController(CementBaseController):
|
||||
self.secure_ssh_port()
|
||||
if pargs.ssh:
|
||||
self.secure_ssh()
|
||||
if pargs.lockdown:
|
||||
self.secure_lockdown()
|
||||
if pargs.unlock:
|
||||
self.secure_unlock()
|
||||
|
||||
@expose(hide=True)
|
||||
def secure_auth(self):
|
||||
"""This function secures authentication"""
|
||||
WOGit.add(self, ["/etc/nginx"],
|
||||
msg="Add Nginx to into Git")
|
||||
WOGit.add(self, [WOVar.wo_ols_conf_dir],
|
||||
msg="Add OLS config to Git")
|
||||
pargs = self.app.pargs
|
||||
passwd = RANDOM.long(self)
|
||||
if not pargs.user_input:
|
||||
@@ -82,25 +93,21 @@ class WOSecureController(CementBaseController):
|
||||
pargs.user_pass = password
|
||||
if password == "":
|
||||
pargs.user_pass = passwd
|
||||
Log.debug(self, "printf username:"
|
||||
"$(openssl passwd --apr1 "
|
||||
"password 2> /dev/null)\n\""
|
||||
"> /etc/nginx/htpasswd-wo 2>/dev/null")
|
||||
WOShellExec.cmd_exec(self, "printf \"{username}:"
|
||||
"$(openssl passwd -apr1 "
|
||||
"{password} 2> /dev/null)\n\""
|
||||
"> /etc/nginx/htpasswd-wo 2>/dev/null"
|
||||
.format(username=pargs.user_input,
|
||||
password=pargs.user_pass),
|
||||
log=False)
|
||||
WOGit.add(self, ["/etc/nginx"],
|
||||
# Set OLS admin password using admpass.sh
|
||||
WOShellExec.cmd_exec(
|
||||
self, "/usr/local/lsws/admin/misc/admpass.sh "
|
||||
"{username} {password}"
|
||||
.format(username=pargs.user_input,
|
||||
password=pargs.user_pass),
|
||||
log=False)
|
||||
WOGit.add(self, [WOVar.wo_ols_conf_dir],
|
||||
msg="Adding changed secure auth into Git")
|
||||
|
||||
@expose(hide=True)
|
||||
def secure_port(self):
|
||||
"""This function Secures port"""
|
||||
WOGit.add(self, ["/etc/nginx"],
|
||||
msg="Add Nginx to into Git")
|
||||
WOGit.add(self, [WOVar.wo_ols_conf_dir],
|
||||
msg="Add OLS config to Git")
|
||||
pargs = self.app.pargs
|
||||
if pargs.user_input:
|
||||
while ((not pargs.user_input.isdigit()) and
|
||||
@@ -117,25 +124,27 @@ class WOSecureController(CementBaseController):
|
||||
Log.info(self, "Please Enter valid port number :")
|
||||
port = input("WordOps admin port [22222]:")
|
||||
pargs.user_input = port
|
||||
data = dict(release=WOVar.wo_version,
|
||||
port=pargs.user_input, webroot='/var/www/')
|
||||
WOTemplate.deploy(
|
||||
self, '/etc/nginx/sites-available/22222',
|
||||
'22222.mustache', data)
|
||||
WOGit.add(self, ["/etc/nginx"],
|
||||
# Update OLS backend listener port
|
||||
httpd_conf = '{0}/httpd_config.conf'.format(WOVar.wo_ols_conf_dir)
|
||||
if os.path.isfile(httpd_conf):
|
||||
WOFileUtils.searchreplace(
|
||||
self, httpd_conf,
|
||||
'address *:22222',
|
||||
'address *:{0}'.format(pargs.user_input))
|
||||
WOGit.add(self, [WOVar.wo_ols_conf_dir],
|
||||
msg="Adding changed secure port into Git")
|
||||
if not WOService.reload_service(self, 'nginx'):
|
||||
Log.error(self, "service nginx reload failed. "
|
||||
"check issues with `nginx -t` command")
|
||||
if not WOService.reload_service(self, 'lsws'):
|
||||
Log.error(self, "service lsws reload failed. "
|
||||
"check issues with `{0} -t` command"
|
||||
.format(WOVar.wo_ols_bin))
|
||||
Log.info(self, "Successfully port changed {port}"
|
||||
.format(port=pargs.user_input))
|
||||
|
||||
@expose(hide=True)
|
||||
def secure_ip(self):
|
||||
"""IP whitelisting"""
|
||||
if os.path.exists('/etc/nginx'):
|
||||
WOGit.add(self, ["/etc/nginx"],
|
||||
msg="Add Nginx to into Git")
|
||||
WOGit.add(self, [WOVar.wo_ols_conf_dir],
|
||||
msg="Add OLS config to Git")
|
||||
pargs = self.app.pargs
|
||||
if not pargs.user_input:
|
||||
ip = input("Enter the comma separated IP addresses "
|
||||
@@ -146,17 +155,98 @@ class WOSecureController(CementBaseController):
|
||||
except Exception as e:
|
||||
Log.debug(self, "{0}".format(e))
|
||||
user_ip = ['127.0.0.1']
|
||||
for ip_addr in user_ip:
|
||||
if not ("exist_ip_address " + ip_addr in open('/etc/nginx/common/'
|
||||
'acl.conf').read()):
|
||||
WOShellExec.cmd_exec(self, "sed -i "
|
||||
"\"/deny/i allow {whitelist_address}\;\""
|
||||
" /etc/nginx/common/acl.conf"
|
||||
.format(whitelist_address=ip_addr))
|
||||
WOGit.add(self, ["/etc/nginx"],
|
||||
# Update OLS ACL configuration
|
||||
acl_conf = '{0}/22222/vhconf.conf'.format(WOVar.wo_ols_vhost_dir)
|
||||
if os.path.isfile(acl_conf):
|
||||
for ip_addr in user_ip:
|
||||
ip_addr = ip_addr.strip()
|
||||
if not WOFileUtils.grepcheck(self, acl_conf, ip_addr):
|
||||
WOFileUtils.searchreplace(
|
||||
self, acl_conf,
|
||||
'allowList',
|
||||
'allowList\n {0}'.format(ip_addr))
|
||||
WOGit.add(self, [WOVar.wo_ols_conf_dir],
|
||||
msg="Adding changed secure ip into Git")
|
||||
Log.info(self, "Successfully added IP address in access control")
|
||||
|
||||
Log.info(self, "Successfully added IP address in acl.conf file")
|
||||
@expose(hide=True)
|
||||
def secure_lockdown(self):
|
||||
"""Enable WP Fort Knox lockdown on a WordPress site"""
|
||||
pargs = self.app.pargs
|
||||
if not pargs.user_input:
|
||||
site_name = input("Enter the site name to lockdown: ")
|
||||
pargs.user_input = site_name
|
||||
|
||||
site_name = pargs.user_input
|
||||
webroot = '{0}{1}'.format(WOVar.wo_webroot, site_name)
|
||||
mu_plugins_dir = '{0}/htdocs/wp-content/mu-plugins'.format(webroot)
|
||||
fort_knox_src = '/var/lib/wo/wp-fort-knox.php'
|
||||
|
||||
if not os.path.isdir(webroot):
|
||||
Log.error(self, "Site {0} not found".format(site_name))
|
||||
|
||||
# Check if it's a WordPress site
|
||||
if not os.path.isfile(
|
||||
'{0}/htdocs/wp-config.php'.format(webroot)):
|
||||
Log.error(self, "Site {0} is not a WordPress site"
|
||||
.format(site_name))
|
||||
|
||||
# Check if Fort Knox source exists
|
||||
if not os.path.isfile(fort_knox_src):
|
||||
Log.error(self, "WP Fort Knox plugin not found at {0}. "
|
||||
"Please reinstall WordOps.".format(fort_knox_src))
|
||||
|
||||
# Create mu-plugins directory if it doesn't exist
|
||||
if not os.path.isdir(mu_plugins_dir):
|
||||
WOFileUtils.mkdir(self, mu_plugins_dir)
|
||||
|
||||
fort_knox_dest = '{0}/wp-fort-knox.php'.format(mu_plugins_dir)
|
||||
|
||||
if os.path.isfile(fort_knox_dest):
|
||||
Log.info(self, "WP Fort Knox is already enabled for {0}"
|
||||
.format(site_name))
|
||||
return
|
||||
|
||||
Log.wait(self, "Enabling WP Fort Knox lockdown")
|
||||
shutil.copy2(fort_knox_src, fort_knox_dest)
|
||||
WOFileUtils.chown(
|
||||
self, fort_knox_dest,
|
||||
WOVar.wo_php_user, WOVar.wo_php_user)
|
||||
Log.valide(self, "Enabling WP Fort Knox lockdown")
|
||||
Log.info(self, "WP Fort Knox enabled for {0}\n"
|
||||
" File modifications and plugin management "
|
||||
"are now disabled in wp-admin.\n"
|
||||
" Use WP-CLI for all administrative tasks.\n"
|
||||
" To disable: wo secure --unlock {0}"
|
||||
.format(site_name))
|
||||
|
||||
@expose(hide=True)
|
||||
def secure_unlock(self):
|
||||
"""Disable WP Fort Knox lockdown on a WordPress site"""
|
||||
pargs = self.app.pargs
|
||||
if not pargs.user_input:
|
||||
site_name = input("Enter the site name to unlock: ")
|
||||
pargs.user_input = site_name
|
||||
|
||||
site_name = pargs.user_input
|
||||
webroot = '{0}{1}'.format(WOVar.wo_webroot, site_name)
|
||||
fort_knox_path = ('{0}/htdocs/wp-content/mu-plugins/'
|
||||
'wp-fort-knox.php'.format(webroot))
|
||||
|
||||
if not os.path.isdir(webroot):
|
||||
Log.error(self, "Site {0} not found".format(site_name))
|
||||
|
||||
if not os.path.isfile(fort_knox_path):
|
||||
Log.info(self, "WP Fort Knox is not enabled for {0}"
|
||||
.format(site_name))
|
||||
return
|
||||
|
||||
Log.wait(self, "Disabling WP Fort Knox lockdown")
|
||||
WOFileUtils.rm(self, fort_knox_path)
|
||||
Log.valide(self, "Disabling WP Fort Knox lockdown")
|
||||
Log.info(self, "WP Fort Knox disabled for {0}\n"
|
||||
" Plugin management is now available in wp-admin."
|
||||
.format(site_name))
|
||||
|
||||
@expose(hide=True)
|
||||
def secure_ssh(self):
|
||||
|
||||
Reference in New Issue
Block a user