mirror of
https://github.com/opensourcepos/opensourcepos.git
synced 2026-05-14 10:43:56 -04:00
feat: add ALLOWED_HOSTNAMES environment variable support for Docker/Compose (#4544)
Allow configuring allowed hostnames via ALLOWED_HOSTNAMES environment variable as an alternative to app.allowedHostnames in .env file. This is more convenient for Docker/Compose deployments where environment variables are set directly in compose files. The ALLOWED_HOSTNAMES variable takes precedence over app.allowedHostnames if both are set, allowing deployment-specific overrides. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Ollama <ollama@steganos.dev> Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -16,6 +16,9 @@ CI_ENVIRONMENT = production
|
||||
# Configure with comma-separated list of domains/subdomains:
|
||||
# app.allowedHostnames = 'yourdomain.com,www.yourdomain.com'
|
||||
#
|
||||
# Or via environment variable (useful for Docker/Compose):
|
||||
# ALLOWED_HOSTNAMES=yourdomain.com,www.yourdomain.com
|
||||
#
|
||||
# For local development:
|
||||
# app.allowedHostnames = 'localhost'
|
||||
#
|
||||
|
||||
@@ -58,9 +58,9 @@ class App extends BaseConfig
|
||||
* Allowed Hostnames in the Site URL other than the hostname in the baseURL.
|
||||
* If you want to accept multiple Hostnames, set this.
|
||||
*
|
||||
* E.g.,
|
||||
* When your site URL ($baseURL) is 'http://example.com/', and your site
|
||||
* also accepts 'http://media.example.com/' and 'http://accounts.example.com/':
|
||||
* Or via environment variable (useful for Docker/Compose):
|
||||
* ALLOWED_HOSTNAMES=example.com,www.example.com
|
||||
*
|
||||
* ['media.example.com', 'accounts.example.com']
|
||||
*
|
||||
* @var list<string>
|
||||
@@ -286,7 +286,11 @@ class App extends BaseConfig
|
||||
|
||||
// Solution for CodeIgniter 4 limitation: arrays cannot be set from .env
|
||||
// See: https://github.com/codeigniter4/CodeIgniter4/issues/7311
|
||||
$envAllowedHostnames = getenv('app.allowedHostnames');
|
||||
// Support both: app.allowedHostnames (from .env) and ALLOWED_HOSTNAMES (from environment/Docker)
|
||||
$envAllowedHostnames = getenv('ALLOWED_HOSTNAMES');
|
||||
if ($envAllowedHostnames === false || trim($envAllowedHostnames) === '') {
|
||||
$envAllowedHostnames = getenv('app.allowedHostnames');
|
||||
}
|
||||
if ($envAllowedHostnames !== false && trim($envAllowedHostnames) !== '') {
|
||||
$this->allowedHostnames = array_values(array_filter(
|
||||
array_map('trim', explode(',', $envAllowedHostnames)),
|
||||
@@ -327,7 +331,7 @@ class App extends BaseConfig
|
||||
$errorMessage =
|
||||
'Security: allowedHostnames is not configured. ' .
|
||||
'Host header injection protection is disabled. ' .
|
||||
'Set app.allowedHostnames in your .env file. ' .
|
||||
'Set app.allowedHostnames in your .env file or ALLOWED_HOSTNAMES environment variable. ' .
|
||||
'Example: app.allowedHostnames = "example.com,www.example.com" ' .
|
||||
'Received Host: ' . $httpHost;
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ services:
|
||||
- .:/app
|
||||
environment:
|
||||
- CI_ENVIRONMENT=development
|
||||
- ALLOWED_HOSTNAMES=localhost
|
||||
- MYSQL_USERNAME=admin
|
||||
- MYSQL_PASSWORD=pointofsale
|
||||
- MYSQL_DB_NAME=ospos
|
||||
|
||||
@@ -16,6 +16,7 @@ services:
|
||||
- logs:/app/writable/logs
|
||||
environment:
|
||||
- CI_ENVIRONMENT=production
|
||||
- ALLOWED_HOSTNAMES=localhost
|
||||
- FORCE_HTTPS=false
|
||||
- PHP_TIMEZONE=UTC
|
||||
- MYSQL_USERNAME=admin
|
||||
|
||||
@@ -18,6 +18,7 @@ class AppTest extends CIUnitTestCase
|
||||
// Clean up environment
|
||||
putenv('CI_ENVIRONMENT');
|
||||
putenv('app.allowedHostnames');
|
||||
putenv('ALLOWED_HOSTNAMES');
|
||||
unset($_SERVER['HTTP_HOST']);
|
||||
}
|
||||
|
||||
@@ -281,4 +282,106 @@ class AppTest extends CIUnitTestCase
|
||||
putenv('app.allowedHostnames');
|
||||
putenv('CI_ENVIRONMENT');
|
||||
}
|
||||
|
||||
public function testAllowedHostnamesEnvVarParsedAsCommaSeparated(): void
|
||||
{
|
||||
// Set ALLOWED_HOSTNAMES environment variable
|
||||
putenv('ALLOWED_HOSTNAMES=example.com,www.example.com,demo.example.com');
|
||||
|
||||
$_SERVER['HTTP_HOST'] = 'www.example.com';
|
||||
$_SERVER['SCRIPT_NAME'] = '/index.php';
|
||||
$_SERVER['HTTPS'] = null;
|
||||
|
||||
$app = new App();
|
||||
|
||||
// Constructor should parse comma-separated values
|
||||
$this->assertEquals(['example.com', 'www.example.com', 'demo.example.com'], $app->allowedHostnames);
|
||||
$this->assertStringContainsString('www.example.com', $app->baseURL);
|
||||
|
||||
// Clean up
|
||||
putenv('ALLOWED_HOSTNAMES');
|
||||
}
|
||||
|
||||
public function testAllowedHostnamesEnvVarTakesPrecedenceOverDotEnv(): void
|
||||
{
|
||||
// Set both environment variables
|
||||
putenv('ALLOWED_HOSTNAMES=allowed1.com,allowed2.com');
|
||||
putenv('app.allowedHostnames=dotenv1.com,dotenv2.com');
|
||||
|
||||
$_SERVER['HTTP_HOST'] = 'allowed1.com';
|
||||
$_SERVER['SCRIPT_NAME'] = '/index.php';
|
||||
$_SERVER['HTTPS'] = null;
|
||||
|
||||
$app = new App();
|
||||
|
||||
// ALLOWED_HOSTNAMES should take precedence
|
||||
$this->assertEquals(['allowed1.com', 'allowed2.com'], $app->allowedHostnames);
|
||||
$this->assertStringContainsString('allowed1.com', $app->baseURL);
|
||||
|
||||
// Clean up
|
||||
putenv('ALLOWED_HOSTNAMES');
|
||||
putenv('app.allowedHostnames');
|
||||
}
|
||||
|
||||
public function testAllowedHostnamesEnvVarFallsBackToDotEnv(): void
|
||||
{
|
||||
// Only set app.allowedHostnames, not ALLOWED_HOSTNAMES
|
||||
putenv('app.allowedHostnames=dotenv1.com,dotenv2.com');
|
||||
|
||||
$_SERVER['HTTP_HOST'] = 'dotenv1.com';
|
||||
$_SERVER['SCRIPT_NAME'] = '/index.php';
|
||||
$_SERVER['HTTPS'] = null;
|
||||
|
||||
$app = new App();
|
||||
|
||||
// Should fall back to app.allowedHostnames
|
||||
$this->assertEquals(['dotenv1.com', 'dotenv2.com'], $app->allowedHostnames);
|
||||
$this->assertStringContainsString('dotenv1.com', $app->baseURL);
|
||||
|
||||
// Clean up
|
||||
putenv('app.allowedHostnames');
|
||||
}
|
||||
|
||||
public function testAllowedHostnamesEnvVarTrimmedWhitespace(): void
|
||||
{
|
||||
// Set environment variable with whitespace
|
||||
putenv('ALLOWED_HOSTNAMES= example.com , www.example.com , demo.example.com ');
|
||||
|
||||
$_SERVER['HTTP_HOST'] = 'example.com';
|
||||
$_SERVER['SCRIPT_NAME'] = '/index.php';
|
||||
$_SERVER['HTTPS'] = null;
|
||||
|
||||
$app = new App();
|
||||
|
||||
// Values should be trimmed
|
||||
$this->assertEquals(['example.com', 'www.example.com', 'demo.example.com'], $app->allowedHostnames);
|
||||
|
||||
// Clean up
|
||||
putenv('ALLOWED_HOSTNAMES');
|
||||
}
|
||||
|
||||
public function testAllowedHostnamesEnvVarFiltersEmptyEntries(): void
|
||||
{
|
||||
// Trailing comma should not produce empty entry
|
||||
putenv('ALLOWED_HOSTNAMES=example.com,');
|
||||
$_SERVER['HTTP_HOST'] = 'example.com';
|
||||
$_SERVER['SCRIPT_NAME'] = '/index.php';
|
||||
$_SERVER['HTTPS'] = null;
|
||||
|
||||
$app = new App();
|
||||
$this->assertEquals(['example.com'], $app->allowedHostnames);
|
||||
|
||||
// Clean up
|
||||
putenv('ALLOWED_HOSTNAMES');
|
||||
|
||||
// Whitespace-only entry should be filtered
|
||||
putenv('ALLOWED_HOSTNAMES=example.com, ,www.example.com');
|
||||
$_SERVER['HTTP_HOST'] = 'example.com';
|
||||
|
||||
$app = new App();
|
||||
$this->assertEquals(['example.com', 'www.example.com'], $app->allowedHostnames);
|
||||
|
||||
// Clean up
|
||||
putenv('ALLOWED_HOSTNAMES');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user