Fix ModSecurity install/setup and bump version
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
A modern web hosting control panel for WordPress and general PHP hosting. Built with Laravel 12, Filament v5, Livewire 4, and Tailwind CSS v4.
|
A modern web hosting control panel for WordPress and general PHP hosting. Built with Laravel 12, Filament v5, Livewire 4, and Tailwind CSS v4.
|
||||||
|
|
||||||
Version: 0.9-rc23 (release candidate)
|
Version: 0.9-rc24 (release candidate)
|
||||||
|
|
||||||
This is a release candidate. Expect rapid iteration and breaking changes until 1.0.
|
This is a release candidate. Expect rapid iteration and breaking changes until 1.0.
|
||||||
|
|
||||||
@@ -156,6 +156,7 @@ php artisan test --compact
|
|||||||
|
|
||||||
## Initial Release
|
## Initial Release
|
||||||
|
|
||||||
|
- 0.9-rc24: WAF installer improvements and ModSecurity setup fixes.
|
||||||
- 0.9-rc: initial release candidate with core hosting, mail, DNS, SSL, backups, and migrations.
|
- 0.9-rc: initial release candidate with core hosting, mail, DNS, SSL, backups, and migrations.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|||||||
@@ -48,11 +48,10 @@ class UpgradeCommand extends Command
|
|||||||
try {
|
try {
|
||||||
$this->configureGitSafeDirectory();
|
$this->configureGitSafeDirectory();
|
||||||
$this->ensureGitRepository();
|
$this->ensureGitRepository();
|
||||||
// Fetch from remote without merging
|
$updateSource = $this->fetchUpdates();
|
||||||
$this->executeCommandOrFail('git fetch origin main');
|
|
||||||
|
|
||||||
// Check if there are updates
|
// Check if there are updates
|
||||||
$behindCount = trim($this->executeCommandOrFail('git rev-list HEAD..origin/main --count'));
|
$behindCount = trim($this->executeCommandOrFail("git rev-list HEAD..{$updateSource['remoteRef']} --count"));
|
||||||
|
|
||||||
if ($behindCount === '0') {
|
if ($behindCount === '0') {
|
||||||
$this->info('Jabali Panel is up to date!');
|
$this->info('Jabali Panel is up to date!');
|
||||||
@@ -64,7 +63,7 @@ class UpgradeCommand extends Command
|
|||||||
|
|
||||||
// Show recent commits
|
// Show recent commits
|
||||||
$this->line("\nRecent changes:");
|
$this->line("\nRecent changes:");
|
||||||
$commits = $this->executeCommandOrFail('git log HEAD..origin/main --oneline -10');
|
$commits = $this->executeCommandOrFail("git log HEAD..{$updateSource['remoteRef']} --oneline -10");
|
||||||
if ($commits !== '') {
|
if ($commits !== '') {
|
||||||
$this->line($commits);
|
$this->line($commits);
|
||||||
}
|
}
|
||||||
@@ -115,7 +114,7 @@ class UpgradeCommand extends Command
|
|||||||
// Step 2: Fetch updates
|
// Step 2: Fetch updates
|
||||||
$this->info('[2/9] Fetching updates from repository...');
|
$this->info('[2/9] Fetching updates from repository...');
|
||||||
try {
|
try {
|
||||||
$this->executeCommandOrFail('git fetch origin main');
|
$updateSource = $this->fetchUpdates();
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$this->error('Failed to fetch updates: '.$e->getMessage());
|
$this->error('Failed to fetch updates: '.$e->getMessage());
|
||||||
|
|
||||||
@@ -123,7 +122,7 @@ class UpgradeCommand extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: Check if updates available
|
// Step 3: Check if updates available
|
||||||
$behindCount = trim($this->executeCommandOrFail('git rev-list HEAD..origin/main --count'));
|
$behindCount = trim($this->executeCommandOrFail("git rev-list HEAD..{$updateSource['remoteRef']} --count"));
|
||||||
if ($behindCount === '0' && ! $this->option('force')) {
|
if ($behindCount === '0' && ! $this->option('force')) {
|
||||||
$this->info('Already up to date!');
|
$this->info('Already up to date!');
|
||||||
|
|
||||||
@@ -134,7 +133,7 @@ class UpgradeCommand extends Command
|
|||||||
$oldHead = trim($this->executeCommandOrFail('git rev-parse HEAD'));
|
$oldHead = trim($this->executeCommandOrFail('git rev-parse HEAD'));
|
||||||
$this->info('[3/9] Pulling latest changes...');
|
$this->info('[3/9] Pulling latest changes...');
|
||||||
try {
|
try {
|
||||||
$pullResult = $this->executeCommand('git pull --ff-only origin main');
|
$pullResult = $this->executeCommand("git pull --ff-only {$updateSource['pullRemote']} main");
|
||||||
if ($pullResult['exitCode'] !== 0) {
|
if ($pullResult['exitCode'] !== 0) {
|
||||||
throw new Exception($pullResult['output'] ?: 'Git pull failed.');
|
throw new Exception($pullResult['output'] ?: 'Git pull failed.');
|
||||||
}
|
}
|
||||||
@@ -249,6 +248,76 @@ class UpgradeCommand extends Command
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array{pullRemote: string, remoteRef: string}
|
||||||
|
*/
|
||||||
|
private function fetchUpdates(): array
|
||||||
|
{
|
||||||
|
$originUrl = trim($this->executeCommandOrFail('git remote get-url origin'));
|
||||||
|
$fetchAttempts = [
|
||||||
|
['remote' => 'origin', 'ref' => 'origin/main', 'type' => 'remote'],
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($this->hasRemote('gitea')) {
|
||||||
|
$fetchAttempts[] = ['remote' => 'gitea', 'ref' => 'gitea/main', 'type' => 'remote'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->isGithubSshUrl($originUrl)) {
|
||||||
|
$fetchAttempts[] = [
|
||||||
|
'remote' => $this->githubHttpsUrlFromSsh($originUrl),
|
||||||
|
'ref' => 'jabali-upgrade/main',
|
||||||
|
'type' => 'url',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$lastError = null;
|
||||||
|
foreach ($fetchAttempts as $attempt) {
|
||||||
|
$result = $attempt['type'] === 'url'
|
||||||
|
? $this->executeCommand("git fetch {$attempt['remote']} main:refs/remotes/{$attempt['ref']}")
|
||||||
|
: $this->executeCommand("git fetch {$attempt['remote']} main");
|
||||||
|
|
||||||
|
if (($result['exitCode'] ?? 1) === 0) {
|
||||||
|
return [
|
||||||
|
'pullRemote' => $attempt['remote'],
|
||||||
|
'remoteRef' => $attempt['ref'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$lastError = $result['output'] ?? 'Unknown error';
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception($lastError ?: 'Unable to fetch updates from any configured remote.');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function hasRemote(string $remote): bool
|
||||||
|
{
|
||||||
|
$result = $this->executeCommand("git remote get-url {$remote}");
|
||||||
|
|
||||||
|
return ($result['exitCode'] ?? 1) === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isGithubSshUrl(string $url): bool
|
||||||
|
{
|
||||||
|
return str_starts_with($url, 'git@github.com:') || str_starts_with($url, 'ssh://git@github.com/');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function githubHttpsUrlFromSsh(string $url): string
|
||||||
|
{
|
||||||
|
if (str_starts_with($url, 'git@github.com:')) {
|
||||||
|
$path = substr($url, strlen('git@github.com:'));
|
||||||
|
|
||||||
|
return 'https://github.com/'.$path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str_starts_with($url, 'ssh://git@github.com/')) {
|
||||||
|
$path = substr($url, strlen('ssh://git@github.com/'));
|
||||||
|
|
||||||
|
return 'https://github.com/'.$path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $url;
|
||||||
|
}
|
||||||
|
|
||||||
private function getCurrentVersion(): string
|
private function getCurrentVersion(): string
|
||||||
{
|
{
|
||||||
if (! File::exists($this->versionFile)) {
|
if (! File::exists($this->versionFile)) {
|
||||||
|
|||||||
@@ -2781,8 +2781,20 @@ function ensureJabaliNginxIncludeFiles(): void
|
|||||||
@mkdir(JABALI_NGINX_INCLUDES, 0755, true);
|
@mkdir(JABALI_NGINX_INCLUDES, 0755, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$baseConfig = findWafBaseConfig();
|
||||||
|
$shouldDisableWaf = $baseConfig === null;
|
||||||
|
|
||||||
if (!file_exists(JABALI_WAF_INCLUDE)) {
|
if (!file_exists(JABALI_WAF_INCLUDE)) {
|
||||||
file_put_contents(JABALI_WAF_INCLUDE, "# Managed by Jabali\n");
|
$content = "# Managed by Jabali\n";
|
||||||
|
if ($shouldDisableWaf) {
|
||||||
|
$content .= "modsecurity off;\n";
|
||||||
|
}
|
||||||
|
file_put_contents(JABALI_WAF_INCLUDE, $content);
|
||||||
|
} elseif ($shouldDisableWaf) {
|
||||||
|
$current = file_get_contents(JABALI_WAF_INCLUDE);
|
||||||
|
if ($current === false || strpos($current, 'modsecurity_rules_file') !== false || strpos($current, 'modsecurity on;') !== false) {
|
||||||
|
file_put_contents(JABALI_WAF_INCLUDE, "# Managed by Jabali\nmodsecurity off;\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file_exists(JABALI_GEO_INCLUDE)) {
|
if (!file_exists(JABALI_GEO_INCLUDE)) {
|
||||||
@@ -2847,7 +2859,7 @@ function findWafBaseConfig(): ?string
|
|||||||
];
|
];
|
||||||
|
|
||||||
foreach ($paths as $path) {
|
foreach ($paths as $path) {
|
||||||
if (file_exists($path)) {
|
if (file_exists($path) && isWafBaseConfigUsable($path)) {
|
||||||
return $path;
|
return $path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2855,6 +2867,28 @@ function findWafBaseConfig(): ?string
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isWafBaseConfigUsable(string $path): bool
|
||||||
|
{
|
||||||
|
if (!is_readable($path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = file_get_contents($path);
|
||||||
|
if ($content === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match_all('/^\s*Include\s+("?)([^"\s]+)\1/m', $content, $matches)) {
|
||||||
|
foreach ($matches[2] as $includePath) {
|
||||||
|
if ($includePath === '/etc/modsecurity/modsecurity.conf' && !file_exists($includePath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function wafApplySettings(array $params): array
|
function wafApplySettings(array $params): array
|
||||||
{
|
{
|
||||||
$enabled = !empty($params['enabled']);
|
$enabled = !empty($params['enabled']);
|
||||||
@@ -2870,6 +2904,7 @@ function wafApplySettings(array $params): array
|
|||||||
if ($enabled) {
|
if ($enabled) {
|
||||||
$baseConfig = findWafBaseConfig();
|
$baseConfig = findWafBaseConfig();
|
||||||
if (!$baseConfig) {
|
if (!$baseConfig) {
|
||||||
|
file_put_contents(JABALI_WAF_INCLUDE, "# Managed by Jabali\nmodsecurity off;\n");
|
||||||
return ['success' => false, 'error' => 'ModSecurity base configuration not found'];
|
return ['success' => false, 'error' => 'ModSecurity base configuration not found'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
47
install.sh
47
install.sh
@@ -1083,7 +1083,10 @@ configure_nginx() {
|
|||||||
local jabali_includes="/etc/nginx/jabali/includes"
|
local jabali_includes="/etc/nginx/jabali/includes"
|
||||||
mkdir -p "$jabali_includes"
|
mkdir -p "$jabali_includes"
|
||||||
if [[ ! -f "$jabali_includes/waf.conf" ]]; then
|
if [[ ! -f "$jabali_includes/waf.conf" ]]; then
|
||||||
echo "# Managed by Jabali" > "$jabali_includes/waf.conf"
|
cat > "$jabali_includes/waf.conf" <<'EOF'
|
||||||
|
# Managed by Jabali
|
||||||
|
modsecurity off;
|
||||||
|
EOF
|
||||||
fi
|
fi
|
||||||
if [[ ! -f "$jabali_includes/geo.conf" ]]; then
|
if [[ ! -f "$jabali_includes/geo.conf" ]]; then
|
||||||
echo "# Managed by Jabali" > "$jabali_includes/geo.conf"
|
echo "# Managed by Jabali" > "$jabali_includes/geo.conf"
|
||||||
@@ -2041,36 +2044,62 @@ configure_security() {
|
|||||||
if [[ "$INSTALL_SECURITY" == "true" ]]; then
|
if [[ "$INSTALL_SECURITY" == "true" ]]; then
|
||||||
info "Installing ModSecurity (optional WAF)..."
|
info "Installing ModSecurity (optional WAF)..."
|
||||||
local module_pkg=""
|
local module_pkg=""
|
||||||
if apt-cache show libnginx-mod-http-modsecurity2 &>/dev/null; then
|
if apt-cache show libnginx-mod-http-modsecurity &>/dev/null; then
|
||||||
module_pkg="libnginx-mod-http-modsecurity2"
|
|
||||||
elif apt-cache show libnginx-mod-http-modsecurity &>/dev/null; then
|
|
||||||
module_pkg="libnginx-mod-http-modsecurity"
|
module_pkg="libnginx-mod-http-modsecurity"
|
||||||
|
elif apt-cache show libnginx-mod-http-modsecurity2 &>/dev/null; then
|
||||||
|
module_pkg="libnginx-mod-http-modsecurity2"
|
||||||
elif apt-cache show nginx-extras &>/dev/null; then
|
elif apt-cache show nginx-extras &>/dev/null; then
|
||||||
module_pkg="nginx-extras"
|
module_pkg="nginx-extras"
|
||||||
else
|
else
|
||||||
warn "ModSecurity nginx module not available in apt repositories"
|
warn "ModSecurity nginx module not available in apt repositories"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
local modsec_lib=""
|
||||||
|
if apt-cache show libmodsecurity3t64 &>/dev/null; then
|
||||||
|
modsec_lib="libmodsecurity3t64"
|
||||||
|
elif apt-cache show libmodsecurity3 &>/dev/null; then
|
||||||
|
modsec_lib="libmodsecurity3"
|
||||||
|
fi
|
||||||
|
|
||||||
local crs_pkg=""
|
local crs_pkg=""
|
||||||
if apt-cache show modsecurity-crs &>/dev/null; then
|
if apt-cache show modsecurity-crs &>/dev/null; then
|
||||||
crs_pkg="modsecurity-crs"
|
crs_pkg="modsecurity-crs"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "$module_pkg" ]]; then
|
if [[ -n "$module_pkg" ]]; then
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq "$module_pkg" $crs_pkg 2>/dev/null || warn "ModSecurity install failed"
|
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq "$module_pkg" $modsec_lib $crs_pkg 2>/dev/null || warn "ModSecurity install failed"
|
||||||
|
|
||||||
# Ensure ModSecurity base config
|
# Ensure ModSecurity base config
|
||||||
if [[ -f /etc/modsecurity/modsecurity.conf-recommended ]] && [[ ! -f /etc/modsecurity/modsecurity.conf ]]; then
|
if [[ ! -f /etc/modsecurity/modsecurity.conf ]]; then
|
||||||
cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf
|
if [[ -f /etc/nginx/modsecurity.conf ]]; then
|
||||||
|
cp /etc/nginx/modsecurity.conf /etc/modsecurity/modsecurity.conf
|
||||||
|
elif [[ -f /etc/modsecurity/modsecurity.conf-recommended ]]; then
|
||||||
|
cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf
|
||||||
|
elif [[ -f /usr/share/modsecurity-crs/modsecurity.conf-recommended ]]; then
|
||||||
|
cp /usr/share/modsecurity-crs/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf
|
||||||
|
else
|
||||||
|
cat > /etc/modsecurity/modsecurity.conf <<'EOF'
|
||||||
|
SecRuleEngine DetectionOnly
|
||||||
|
SecRequestBodyAccess On
|
||||||
|
SecResponseBodyAccess Off
|
||||||
|
SecAuditEngine RelevantOnly
|
||||||
|
SecAuditLog /var/log/nginx/modsec_audit.log
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create main include file for nginx if missing
|
# Create main include file for nginx if missing
|
||||||
mkdir -p /etc/nginx/modsec
|
mkdir -p /etc/nginx/modsec
|
||||||
if [[ ! -f /etc/nginx/modsec/main.conf ]]; then
|
if [[ ! -f /etc/nginx/modsec/main.conf ]]; then
|
||||||
if [[ -f /usr/share/modsecurity-crs/crs-setup.conf ]]; then
|
if [[ -f /usr/share/modsecurity-crs/owasp-crs.load ]]; then
|
||||||
cat > /etc/nginx/modsec/main.conf <<'EOF'
|
cat > /etc/nginx/modsec/main.conf <<'EOF'
|
||||||
Include /etc/modsecurity/modsecurity.conf
|
Include /etc/modsecurity/modsecurity.conf
|
||||||
Include /usr/share/modsecurity-crs/crs-setup.conf
|
Include /usr/share/modsecurity-crs/owasp-crs.load
|
||||||
|
EOF
|
||||||
|
elif [[ -f /etc/modsecurity/crs/crs-setup.conf ]]; then
|
||||||
|
cat > /etc/nginx/modsec/main.conf <<'EOF'
|
||||||
|
Include /etc/modsecurity/modsecurity.conf
|
||||||
|
Include /etc/modsecurity/crs/crs-setup.conf
|
||||||
Include /usr/share/modsecurity-crs/rules/*.conf
|
Include /usr/share/modsecurity-crs/rules/*.conf
|
||||||
EOF
|
EOF
|
||||||
else
|
else
|
||||||
|
|||||||
Reference in New Issue
Block a user