diff --git a/apps-gx10/fc-php/configmap-php-web-config.json b/apps-gx10/fc-php/configmap-php-web-config.json index 4b7b73a..48cc56d 100644 --- a/apps-gx10/fc-php/configmap-php-web-config.json +++ b/apps-gx10/fc-php/configmap-php-web-config.json @@ -1,11 +1,11 @@ -{ - "apiVersion": "v1", - "data": { - "appsettings.Production.json": "{\n \"PhpManager\": {\n \"Namespace\": \"fc-php\",\n \"Slowlog\": {\n \"Path\": \"/var/log/apache2/php-fpm-slow.log\",\n \"Sidecar\": {\n \"Enabled\": true,\n \"Image\": \"\"\n }\n },\n \"PoolConfig\": {\n \"StartServers\": null,\n \"MinSpareServers\": null,\n \"MaxSpareServers\": null,\n \"ProcessIdleTimeoutSeconds\": 10,\n \"RequestTerminateTimeoutSeconds\": 30\n },\n \"Certificates\": {\n \"TlsInspector\": {\n \"LogGracefulDegradeWarnings\": false\n }\n },\n \"Backups\": {\n \"StoragePath\": \"/data/backups\"\n }\n },\n \"ApplicationArchives\": {\n \"WordPressCoreUrl\": \"http://php-web.fc-php.svc.cluster.local.:5400/api/v1/application-archives/wordpress/latest.tar.gz\",\n \"WordPressProxySourceUrl\": \"https://wordpress.org/latest.tar.gz\",\n \"WordPressLocalArchivePath\": \"/data/application-archives/latest.tar.gz\",\n \"MyBbCoreUrl\": \"http://php-web.fc-php.svc.cluster.local.:5400/api/v1/application-archives/mybb/latest.zip\",\n \"MyBbProxySourceUrl\": \"https://mybb.com/download/\",\n \"MyBbLocalArchivePath\": \"/data/application-archives/mybb-latest.zip\",\n \"MediaWikiCoreUrl\": \"http://php-web.fc-php.svc.cluster.local.:5400/api/v1/application-archives/mediawiki/latest.tar.gz\",\n \"MediaWikiProxySourceUrl\": \"https://releases.wikimedia.org/mediawiki/1.45/mediawiki-1.45.3.tar.gz\",\n \"MediaWikiLocalArchivePath\": \"/data/application-archives/mediawiki-latest.tar.gz\",\n \"DrupalCoreUrl\": \"http://php-web.fc-php.svc.cluster.local.:5400/api/v1/application-archives/drupal/latest.tar.gz\",\n \"DrupalProxySourceUrl\": \"https://ftp.drupal.org/files/projects/drupal-11.3.8.tar.gz\",\n \"DrupalLocalArchivePath\": \"/data/application-archives/drupal-latest.tar.gz\",\n \"BypassUpstreamTls\": true\n },\n \"ContainerBackend\": {\n \"Default\": \"Kubernetes\"\n },\n \"FlowerCore\": {\n \"Auth\": {\n \"Provider\": \"Oidc\",\n \"Enabled\": false,\n \"Oidc\": {\n \"Enabled\": true,\n \"Authority\": \"https://id.iamworkin.lan/application/o/php/\",\n \"Audience\": \"php\",\n \"ClientId\": \"php\",\n \"ClientSecret\": \"\"\n },\n \"Impersonation\": {\n \"Enabled\": false,\n \"DebugMode\": false\n }\n },\n \"Tenant\": {\n \"StrictMode\": false,\n \"JwtClaimsEnabled\": false,\n \"TenantClaimType\": \"fc:tenant\",\n \"ActorIdClaimType\": \"flowercore_actor_id\"\n },\n \"Account\": {\n \"AppId\": \"php\",\n \"DefaultTenantId\": \"default\",\n \"Impersonation\": {\n \"Enabled\": false,\n \"StrictMode\": false,\n \"TechSupportRoles\": [ \"tech-support\" ],\n \"Targets\": []\n }\n },\n \"Hosting\": {\n \"AutoDns\": {\n \"Enabled\": true,\n \"DnsManagerBaseUrl\": \"https://dns.iamworkin.lan/\",\n \"ZoneName\": \"iamworkin.lan\",\n \"RecordType\": \"A\",\n \"TargetAddress\": \"10.0.56.200\",\n \"Ttl\": 300,\n \"BypassTls\": true\n }\n },\n \"Database\": {\n \"Provider\": \"Sqlite\",\n \"ConnectionStrings\": {\n \"Sqlite\": \"Data Source=/data/php-manager.db\"\n }\n }\n }\n}\n" - }, - "kind": "ConfigMap", - "metadata": { - "name": "php-web-config", - "namespace": "fc-php" - } -} +{ + "apiVersion": "v1", + "data": { + "appsettings.Production.json": "{\"PhpManager\":{\"Namespace\":\"fc-php\",\"Slowlog\":{\"Path\":\"/var/log/apache2/php-fpm-slow.log\",\"Sidecar\":{\"Enabled\":true,\"Image\":\"\"}},\"PoolConfig\":{\"StartServers\":null,\"MinSpareServers\":null,\"MaxSpareServers\":null,\"ProcessIdleTimeoutSeconds\":10,\"RequestTerminateTimeoutSeconds\":30},\"Certificates\":{\"TlsInspector\":{\"LogGracefulDegradeWarnings\":false}},\"Backups\":{\"StoragePath\":\"/data/backups\"},\"Ingress\":{\"DefaultMiddlewares\":[{\"Name\":\"php-tenant-rate-limit\",\"Namespace\":\"fc-php\"},{\"Name\":\"php-tenant-secure-headers\",\"Namespace\":\"fc-php\"}],\"TlsOption\":{\"Name\":\"php-tenant-tls13\",\"Namespace\":\"fc-php\"}}},\"ApplicationArchives\":{\"WordPressCoreUrl\":\"http://php-web.fc-php.svc.cluster.local.:5400/api/v1/application-archives/wordpress/latest.tar.gz\",\"WordPressProxySourceUrl\":\"https://wordpress.org/latest.tar.gz\",\"WordPressLocalArchivePath\":\"/data/application-archives/latest.tar.gz\",\"MyBbCoreUrl\":\"http://php-web.fc-php.svc.cluster.local.:5400/api/v1/application-archives/mybb/latest.zip\",\"MyBbProxySourceUrl\":\"https://mybb.com/download/\",\"MyBbLocalArchivePath\":\"/data/application-archives/mybb-latest.zip\",\"MediaWikiCoreUrl\":\"http://php-web.fc-php.svc.cluster.local.:5400/api/v1/application-archives/mediawiki/latest.tar.gz\",\"MediaWikiProxySourceUrl\":\"https://releases.wikimedia.org/mediawiki/1.45/mediawiki-1.45.3.tar.gz\",\"MediaWikiLocalArchivePath\":\"/data/application-archives/mediawiki-latest.tar.gz\",\"DrupalCoreUrl\":\"http://php-web.fc-php.svc.cluster.local.:5400/api/v1/application-archives/drupal/latest.tar.gz\",\"DrupalProxySourceUrl\":\"https://ftp.drupal.org/files/projects/drupal-11.3.8.tar.gz\",\"DrupalLocalArchivePath\":\"/data/application-archives/drupal-latest.tar.gz\",\"BypassUpstreamTls\":true},\"ContainerBackend\":{\"Default\":\"Kubernetes\"},\"FlowerCore\":{\"Auth\":{\"Provider\":\"Oidc\",\"Enabled\":false,\"Oidc\":{\"Enabled\":true,\"Authority\":\"https://id.iamworkin.lan/application/o/php/\",\"Audience\":\"php\",\"ClientId\":\"php\",\"ClientSecret\":\"\"},\"Impersonation\":{\"Enabled\":false,\"DebugMode\":false}},\"Tenant\":{\"StrictMode\":false,\"JwtClaimsEnabled\":false,\"TenantClaimType\":\"fc:tenant\",\"ActorIdClaimType\":\"flowercore_actor_id\"},\"Account\":{\"AppId\":\"php\",\"DefaultTenantId\":\"default\",\"Impersonation\":{\"Enabled\":false,\"StrictMode\":false,\"TechSupportRoles\":[\"tech-support\"],\"Targets\":[]}},\"Hosting\":{\"AutoDns\":{\"Enabled\":true,\"DnsManagerBaseUrl\":\"https://dns.iamworkin.lan/\",\"ZoneName\":\"iamworkin.lan\",\"RecordType\":\"A\",\"TargetAddress\":\"10.0.56.200\",\"Ttl\":300,\"BypassTls\":true}},\"Database\":{\"Provider\":\"Sqlite\",\"ConnectionStrings\":{\"Sqlite\":\"Data Source=/data/php-manager.db\"}}}}" + }, + "kind": "ConfigMap", + "metadata": { + "name": "php-web-config", + "namespace": "fc-php" + } +} diff --git a/apps-gx10/fc-php/deployment-php-web.json b/apps-gx10/fc-php/deployment-php-web.json index 6e6dcf4..40f59c4 100644 --- a/apps-gx10/fc-php/deployment-php-web.json +++ b/apps-gx10/fc-php/deployment-php-web.json @@ -86,7 +86,7 @@ "value": "php" } ], - "image": "localhost/fc-php-web:v20260617-sec4-storage-8b19394", + "image": "localhost/fc-php-web:v20260617-whc4-edge-638b3b3", "imagePullPolicy": "Never", "livenessProbe": { "failureThreshold": 3, diff --git a/apps-gx10/fc-php/middleware-php-tenant-rate-limit.json b/apps-gx10/fc-php/middleware-php-tenant-rate-limit.json new file mode 100644 index 0000000..3f6e4ee --- /dev/null +++ b/apps-gx10/fc-php/middleware-php-tenant-rate-limit.json @@ -0,0 +1,15 @@ +{ + "apiVersion": "traefik.io/v1alpha1", + "kind": "Middleware", + "metadata": { + "name": "php-tenant-rate-limit", + "namespace": "fc-php" + }, + "spec": { + "rateLimit": { + "average": 120, + "burst": 240, + "period": "1m" + } + } +} diff --git a/apps-gx10/fc-php/middleware-php-tenant-secure-headers.json b/apps-gx10/fc-php/middleware-php-tenant-secure-headers.json new file mode 100644 index 0000000..11468da --- /dev/null +++ b/apps-gx10/fc-php/middleware-php-tenant-secure-headers.json @@ -0,0 +1,18 @@ +{ + "apiVersion": "traefik.io/v1alpha1", + "kind": "Middleware", + "metadata": { + "name": "php-tenant-secure-headers", + "namespace": "fc-php" + }, + "spec": { + "headers": { + "contentTypeNosniff": true, + "browserXssFilter": true, + "referrerPolicy": "strict-origin-when-cross-origin", + "stsSeconds": 31536000, + "stsIncludeSubdomains": true, + "stsPreload": false + } + } +} diff --git a/apps-gx10/fc-php/tlsoption-php-tenant-tls13.json b/apps-gx10/fc-php/tlsoption-php-tenant-tls13.json new file mode 100644 index 0000000..a40fbc9 --- /dev/null +++ b/apps-gx10/fc-php/tlsoption-php-tenant-tls13.json @@ -0,0 +1,11 @@ +{ + "apiVersion": "traefik.io/v1alpha1", + "kind": "TLSOption", + "metadata": { + "name": "php-tenant-tls13", + "namespace": "fc-php" + }, + "spec": { + "minVersion": "VersionTLS13" + } +} diff --git a/tests/bluejay-infra-lint/FleetManifestLintTests.cs b/tests/bluejay-infra-lint/FleetManifestLintTests.cs index 4e62284..f333221 100644 --- a/tests/bluejay-infra-lint/FleetManifestLintTests.cs +++ b/tests/bluejay-infra-lint/FleetManifestLintTests.cs @@ -981,6 +981,39 @@ public sealed class FleetManifestLintTests gatewayManifest.Should().Contain("port: 5400"); } + [Fact] + public void Gx10PhpTenantRoutes_HaveEdgeControlSubstrate() + { + var appRoot = Path.Combine(Inventory.BluejayRoot, "apps-gx10", "fc-php"); + using var configMap = JsonDocument.Parse(File.ReadAllText(Path.Combine(appRoot, "configmap-php-web-config.json"))); + var settingsJson = configMap.RootElement + .GetProperty("data") + .GetProperty("appsettings.Production.json") + .GetString(); + settingsJson.Should().NotBeNullOrWhiteSpace(); + + using var settings = JsonDocument.Parse(settingsJson!); + var ingress = settings.RootElement.GetProperty("PhpManager").GetProperty("Ingress"); + ingress.GetProperty("DefaultMiddlewares") + .EnumerateArray() + .Select(item => item.GetProperty("Name").GetString()) + .Should() + .Equal("php-tenant-rate-limit", "php-tenant-secure-headers"); + ingress.GetProperty("TlsOption").GetProperty("Name").GetString().Should().Be("php-tenant-tls13"); + + using var rateLimit = JsonDocument.Parse(File.ReadAllText(Path.Combine(appRoot, "middleware-php-tenant-rate-limit.json"))); + rateLimit.RootElement.GetProperty("kind").GetString().Should().Be("Middleware"); + rateLimit.RootElement.GetProperty("spec").GetProperty("rateLimit").GetProperty("average").GetInt32().Should().Be(120); + + using var headers = JsonDocument.Parse(File.ReadAllText(Path.Combine(appRoot, "middleware-php-tenant-secure-headers.json"))); + headers.RootElement.GetProperty("kind").GetString().Should().Be("Middleware"); + headers.RootElement.GetProperty("spec").GetProperty("headers").GetProperty("contentTypeNosniff").GetBoolean().Should().BeTrue(); + + using var tlsOption = JsonDocument.Parse(File.ReadAllText(Path.Combine(appRoot, "tlsoption-php-tenant-tls13.json"))); + tlsOption.RootElement.GetProperty("kind").GetString().Should().Be("TLSOption"); + tlsOption.RootElement.GetProperty("spec").GetProperty("minVersion").GetString().Should().Be("VersionTLS13"); + } + [Fact] public void Gx10HostingManagers_ProvisioningCrdsAndRbacMustBeGitOpsOwned() {