89 lines
3.6 KiB
PowerShell
89 lines
3.6 KiB
PowerShell
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$ApacheConfig,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$PhpIni,
|
|
|
|
[switch]$Apply,
|
|
[switch]$SkipApacheSyntaxCheck
|
|
)
|
|
|
|
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
function Resolve-ConfigFile([string]$Value, [string]$Label) {
|
|
$resolved = (Resolve-Path -LiteralPath $Value -ErrorAction Stop).Path
|
|
if (-not (Test-Path -LiteralPath $resolved -PathType Leaf)) {
|
|
throw "$Label is not a file: $resolved"
|
|
}
|
|
return $resolved
|
|
}
|
|
|
|
function Replace-ManagedBlock([string]$Text, [string]$Begin, [string]$End, [string]$Block) {
|
|
$normalized = $Text -replace "`r`n?", "`n"
|
|
$pattern = '(?ms)(?:^|\n)' + [regex]::Escape($Begin) + '.*?' + [regex]::Escape($End) + '(?:\n|$)'
|
|
$without = [regex]::Replace($normalized, $pattern, "`n").TrimEnd("`n")
|
|
if ($without.Length -eq 0) { return "$Block`n" }
|
|
return "$without`n`n$Block`n"
|
|
}
|
|
|
|
function Write-Utf8NoBom([string]$File, [string]$Text) {
|
|
[System.IO.File]::WriteAllText($File, $Text, [System.Text.UTF8Encoding]::new($false))
|
|
}
|
|
|
|
$apacheFile = Resolve-ConfigFile $ApacheConfig 'Apache configuration'
|
|
$phpFile = Resolve-ConfigFile $PhpIni 'PHP configuration'
|
|
$apacheBegin = '# BEGIN LINKFIELD HOST HARDENING'
|
|
$apacheEnd = '# END LINKFIELD HOST HARDENING'
|
|
$phpBegin = '; BEGIN LINKFIELD HOST HARDENING'
|
|
$phpEnd = '; END LINKFIELD HOST HARDENING'
|
|
$apacheBlock = "$apacheBegin`nServerTokens Prod`nServerSignature Off`n$apacheEnd"
|
|
$phpBlock = "$phpBegin`nexpose_php = Off`n$phpEnd"
|
|
$apacheOriginal = [System.IO.File]::ReadAllText($apacheFile)
|
|
$phpOriginal = [System.IO.File]::ReadAllText($phpFile)
|
|
$apacheUpdated = Replace-ManagedBlock $apacheOriginal $apacheBegin $apacheEnd $apacheBlock
|
|
$phpUpdated = Replace-ManagedBlock $phpOriginal $phpBegin $phpEnd $phpBlock
|
|
$apacheAlreadySafe = $apacheUpdated -eq ($apacheOriginal -replace "`r`n?", "`n")
|
|
$phpAlreadySafe = $phpUpdated -eq ($phpOriginal -replace "`r`n?", "`n")
|
|
|
|
Write-Output "Apache config: $apacheFile"
|
|
Write-Output "PHP config: $phpFile"
|
|
Write-Output "Apache ServerTokens/ServerSignature: $(if ($apacheAlreadySafe) {'already managed'} else {'change required'})"
|
|
Write-Output "PHP expose_php: $(if ($phpAlreadySafe) {'already managed'} else {'change required'})"
|
|
|
|
if (-not $Apply) {
|
|
Write-Output 'Dry run only. Re-run with -Apply to create backups and write the managed settings.'
|
|
exit 0
|
|
}
|
|
|
|
$stamp = Get-Date -Format 'yyyyMMdd-HHmmss'
|
|
$apacheBackup = "$apacheFile.linkfield-backup-$stamp"
|
|
$phpBackup = "$phpFile.linkfield-backup-$stamp"
|
|
Copy-Item -LiteralPath $apacheFile -Destination $apacheBackup -Force
|
|
Copy-Item -LiteralPath $phpFile -Destination $phpBackup -Force
|
|
|
|
try {
|
|
Write-Utf8NoBom $apacheFile $apacheUpdated
|
|
Write-Utf8NoBom $phpFile $phpUpdated
|
|
|
|
if (-not $SkipApacheSyntaxCheck) {
|
|
$apacheRoot = Split-Path -Parent (Split-Path -Parent $apacheFile)
|
|
$httpd = Join-Path $apacheRoot 'bin\httpd.exe'
|
|
if (-not (Test-Path -LiteralPath $httpd -PathType Leaf)) {
|
|
throw "Apache syntax checker was not found at $httpd. Re-run with -SkipApacheSyntaxCheck only after locating another way to run httpd -t."
|
|
}
|
|
& $httpd -t -f $apacheFile
|
|
if ($LASTEXITCODE -ne 0) { throw "Apache rejected the updated configuration (exit $LASTEXITCODE)." }
|
|
}
|
|
} catch {
|
|
Copy-Item -LiteralPath $apacheBackup -Destination $apacheFile -Force
|
|
Copy-Item -LiteralPath $phpBackup -Destination $phpFile -Force
|
|
throw "Host hardening failed; both original files were restored. $($_.Exception.Message)"
|
|
}
|
|
|
|
Write-Output "Applied LinkField host hardening. Backups:"
|
|
Write-Output " $apacheBackup"
|
|
Write-Output " $phpBackup"
|
|
Write-Output 'Restart Apache, then run: npm run smoke:public -- https://2012r2.nishi.boats/~333/link-field/'
|