diff --git a/Classes/Command/SetupCommandController.php b/Classes/Command/SetupCommandController.php
index 89b3535..32ecb83 100644
--- a/Classes/Command/SetupCommandController.php
+++ b/Classes/Command/SetupCommandController.php
@@ -20,11 +20,11 @@
use Neos\Setup\Domain\CliEnvironment;
use Neos\Setup\Domain\HealthcheckEnvironment;
use Neos\Setup\Infrastructure\HealthChecker;
-use Neos\Setup\RequestHandler\SetupCliRequestHandler;
use Neos\Utility\Arrays;
use Neos\Setup\Exception as SetupException;
use Neos\Setup\Infrastructure\Database\DatabaseConnectionService;
use Symfony\Component\Yaml\Yaml;
+use Neos\Utility\Files;
class SetupCommandController extends CommandController
{
@@ -185,6 +185,7 @@ private function writeSettings(string $filename, string $path, $settings): strin
$previousSettings = [];
}
$newSettings = Arrays::setValueByPath($previousSettings, $path, $settings);
+ Files::createDirectoryRecursively(dirname($filename));
file_put_contents($filename, YAML::dump($newSettings, 10, 2));
return YAML::dump(Arrays::setValueByPath([], $path, $settings), 10, 2);
}
diff --git a/Classes/Infrastructure/Healthcheck/TrustedProxiesHealthcheck.php b/Classes/Infrastructure/Healthcheck/TrustedProxiesHealthcheck.php
new file mode 100644
index 0000000..7138500
--- /dev/null
+++ b/Classes/Infrastructure/Healthcheck/TrustedProxiesHealthcheck.php
@@ -0,0 +1,224 @@
+ mappingKey (clientIp, host, port, proto) or null for detection-only headers
+ * Priority is determined by array order for headers with the same mappingKey
+ */
+ private const REVERSE_PROXY_HEADERS = [
+ // Standard reverse proxy headers with mapping
+ 'X-Forwarded-For' => 'clientIp',
+ 'X-Forwarded-Host' => 'host',
+ 'X-Forwarded-Port' => 'port',
+ 'X-Forwarded-Proto' => 'proto',
+ 'X-Real-IP' => 'clientIp',
+ 'Forwarded' => null, // RFC 7239 - detection only
+
+ // Additional clientIp headers (priority order)
+ 'True-Client-IP' => 'clientIp',
+ 'X-Client-IP' => 'clientIp',
+ 'Client-IP' => 'clientIp',
+
+ // Cloudflare
+ 'CF-Connecting-IP' => 'clientIp',
+ 'CF-Visitor' => null,
+ 'CF-RAY' => null,
+ 'CF-IPCountry' => null,
+
+ // AWS
+ 'X-Amzn-Trace-Id' => null,
+ 'X-Amz-Cf-Id' => null,
+ 'CloudFront-Viewer-Address' => 'clientIp',
+
+ // Google Cloud
+ 'X-Cloud-Trace-Context' => null,
+
+ // Azure
+ 'X-Azure-ClientIP' => 'clientIp',
+ 'X-ARR-ClientIP' => 'clientIp',
+
+ // Fastly / Other CDNs
+ 'Fastly-Client-IP' => 'clientIp',
+ 'X-Forwarded-Ssl' => null,
+ 'X-Original-Forwarded-For' => 'clientIp',
+ 'X-Original-Host' => 'host',
+ ];
+
+ public function __construct(
+ private ConfigurationManager $configurationManager,
+ ) {
+ }
+
+ public function getTitle(): string
+ {
+ return 'Trusted Proxies Configuration';
+ }
+
+ public function execute(HealthcheckEnvironment $environment): Health
+ {
+ if ($environment->executionEnvironment instanceof WebEnvironment) {
+ $trustedProxiesConfig = $this->configurationManager->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, 'Neos.Flow.http.trustedProxies');
+ $configuredHeaders = $trustedProxiesConfig['headers'] ?? [];
+ $configuredProxies = $trustedProxiesConfig['proxies'] ?? [];
+
+ if (is_string($configuredProxies)) {
+ $configuredProxies = array_map('trim', explode(',', $configuredProxies));
+ }
+
+ $remoteAddr = $_SERVER['REMOTE_ADDR'] ?? null;
+
+ // Check if any reverse proxy header is present
+ $detectedProxyHeaders = [];
+ foreach (self::REVERSE_PROXY_HEADERS as $header => $mappingKey) {
+ if (isset($_SERVER['HTTP_' . strtoupper(str_replace('-', '_', $header))])) {
+ $detectedProxyHeaders[] = $header;
+ }
+ }
+
+ if (count($detectedProxyHeaders) > 0) {
+ $message = "Reverse proxy headers detected: " . implode(', ', $detectedProxyHeaders) . "
";
+ } else {
+ $message = "No reverse proxy headers detected.
";
+ }
+
+
+ if (!empty($detectedProxyHeaders)) {
+ // Reverse proxy headers detected
+ // -> config is OK ($isRemoteAddrTrusted==true) if the current REMOTE_ADDR matches any configured trusted proxy
+ $isProxyConfigured = !empty($configuredProxies);
+ $isRemoteAddrTrusted = false;
+
+ if ($isProxyConfigured && $remoteAddr) {
+ $isRemoteAddrTrusted = self::matchesProxyPattern($remoteAddr, $configuredProxies);
+ }
+
+ if (!$isProxyConfigured || !$isRemoteAddrTrusted) {
+ // Trusted proxies not configured or don't match REMOTE_ADDR
+
+ if (!$isProxyConfigured) {
+ $message .= "Trusted proxies are not configured. ";
+ } else {
+ $message .= "The current REMOTE_ADDR {$remoteAddr} does not match any configured trusted proxies " . implode(',', $configuredProxies) . ". ";
+ }
+
+ $message .= "You need to configure trusted proxies to ensure URLs can be properly built.
";
+ $message .= "Configure via Settings.yaml:
";
+ $message .= "
Neos:\n";
+ $message .= " Flow:\n";
+ $message .= " http:\n";
+ $message .= " trustedProxies:\n";
+
+ if ($remoteAddr) {
+ $message .= " proxies: ['{$remoteAddr}']\n";
+ } else {
+ $message .= " proxies: ['']\n";
+ }
+
+ // Generate headers configuration mapping based on detected headers
+ $headersMapping = self::generateHeadersMapping($detectedProxyHeaders);
+ if (!empty($headersMapping)) {
+ $message .= " headers:\n";
+ foreach ($headersMapping as $key => $header) {
+ $message .= " {$key}: '{$header}'\n";
+ }
+ }
+
+ $message .= " \n";
+
+
+ $message .= "Alternatively, set the FLOW_HTTP_TRUSTED_PROXIES={$remoteAddr} environment variable.