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.
|
||||
|
||||
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.
|
||||
|
||||
@@ -156,6 +156,7 @@ php artisan test --compact
|
||||
|
||||
## 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.
|
||||
|
||||
## License
|
||||
|
||||
@@ -48,11 +48,10 @@ class UpgradeCommand extends Command
|
||||
try {
|
||||
$this->configureGitSafeDirectory();
|
||||
$this->ensureGitRepository();
|
||||
// Fetch from remote without merging
|
||||
$this->executeCommandOrFail('git fetch origin main');
|
||||
$updateSource = $this->fetchUpdates();
|
||||
|
||||
// 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') {
|
||||
$this->info('Jabali Panel is up to date!');
|
||||
@@ -64,7 +63,7 @@ class UpgradeCommand extends Command
|
||||
|
||||
// Show recent commits
|
||||
$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 !== '') {
|
||||
$this->line($commits);
|
||||
}
|
||||
@@ -115,7 +114,7 @@ class UpgradeCommand extends Command
|
||||
// Step 2: Fetch updates
|
||||
$this->info('[2/9] Fetching updates from repository...');
|
||||
try {
|
||||
$this->executeCommandOrFail('git fetch origin main');
|
||||
$updateSource = $this->fetchUpdates();
|
||||
} catch (Exception $e) {
|
||||
$this->error('Failed to fetch updates: '.$e->getMessage());
|
||||
|
||||
@@ -123,7 +122,7 @@ class UpgradeCommand extends Command
|
||||
}
|
||||
|
||||
// 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')) {
|
||||
$this->info('Already up to date!');
|
||||
|
||||
@@ -134,7 +133,7 @@ class UpgradeCommand extends Command
|
||||
$oldHead = trim($this->executeCommandOrFail('git rev-parse HEAD'));
|
||||
$this->info('[3/9] Pulling latest changes...');
|
||||
try {
|
||||
$pullResult = $this->executeCommand('git pull --ff-only origin main');
|
||||
$pullResult = $this->executeCommand("git pull --ff-only {$updateSource['pullRemote']} main");
|
||||
if ($pullResult['exitCode'] !== 0) {
|
||||
throw new Exception($pullResult['output'] ?: 'Git pull failed.');
|
||||
}
|
||||
@@ -249,6 +248,76 @@ class UpgradeCommand extends Command
|
||||
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
|
||||
{
|
||||
if (! File::exists($this->versionFile)) {
|
||||
|
||||
@@ -2781,8 +2781,20 @@ function ensureJabaliNginxIncludeFiles(): void
|
||||
@mkdir(JABALI_NGINX_INCLUDES, 0755, true);
|
||||
}
|
||||
|
||||
$baseConfig = findWafBaseConfig();
|
||||
$shouldDisableWaf = $baseConfig === null;
|
||||
|
||||
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)) {
|
||||
@@ -2847,7 +2859,7 @@ function findWafBaseConfig(): ?string
|
||||
];
|
||||
|
||||
foreach ($paths as $path) {
|
||||
if (file_exists($path)) {
|
||||
if (file_exists($path) && isWafBaseConfigUsable($path)) {
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
@@ -2855,6 +2867,28 @@ function findWafBaseConfig(): ?string
|
||||
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
|
||||
{
|
||||
$enabled = !empty($params['enabled']);
|
||||
@@ -2870,6 +2904,7 @@ function wafApplySettings(array $params): array
|
||||
if ($enabled) {
|
||||
$baseConfig = findWafBaseConfig();
|
||||
if (!$baseConfig) {
|
||||
file_put_contents(JABALI_WAF_INCLUDE, "# Managed by Jabali\nmodsecurity off;\n");
|
||||
return ['success' => false, 'error' => 'ModSecurity base configuration not found'];
|
||||
}
|
||||
|
||||
|
||||
45
install.sh
45
install.sh
@@ -1083,7 +1083,10 @@ configure_nginx() {
|
||||
local jabali_includes="/etc/nginx/jabali/includes"
|
||||
mkdir -p "$jabali_includes"
|
||||
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
|
||||
if [[ ! -f "$jabali_includes/geo.conf" ]]; then
|
||||
echo "# Managed by Jabali" > "$jabali_includes/geo.conf"
|
||||
@@ -2041,36 +2044,62 @@ configure_security() {
|
||||
if [[ "$INSTALL_SECURITY" == "true" ]]; then
|
||||
info "Installing ModSecurity (optional WAF)..."
|
||||
local module_pkg=""
|
||||
if apt-cache show libnginx-mod-http-modsecurity2 &>/dev/null; then
|
||||
module_pkg="libnginx-mod-http-modsecurity2"
|
||||
elif apt-cache show libnginx-mod-http-modsecurity &>/dev/null; then
|
||||
if apt-cache show libnginx-mod-http-modsecurity &>/dev/null; then
|
||||
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
|
||||
module_pkg="nginx-extras"
|
||||
else
|
||||
warn "ModSecurity nginx module not available in apt repositories"
|
||||
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=""
|
||||
if apt-cache show modsecurity-crs &>/dev/null; then
|
||||
crs_pkg="modsecurity-crs"
|
||||
fi
|
||||
|
||||
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
|
||||
if [[ -f /etc/modsecurity/modsecurity.conf-recommended ]] && [[ ! -f /etc/modsecurity/modsecurity.conf ]]; then
|
||||
if [[ ! -f /etc/modsecurity/modsecurity.conf ]]; then
|
||||
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
|
||||
|
||||
# Create main include file for nginx if missing
|
||||
mkdir -p /etc/nginx/modsec
|
||||
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'
|
||||
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
|
||||
EOF
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user