Merge pull request #10484 from appwrite/feat-proxy-e2e-tests

Feat: Proxy verification tests
This commit is contained in:
Matej Bačo
2025-09-15 15:36:03 +02:00
committed by GitHub
9 changed files with 261 additions and 9 deletions
+3 -3
View File
@@ -21,13 +21,13 @@ _APP_OPTIONS_ROUTER_PROTECTION=disabled
_APP_OPTIONS_FORCE_HTTPS=disabled
_APP_OPTIONS_ROUTER_FORCE_HTTPS=disabled
_APP_OPENSSL_KEY_V1=your-secret-key
_APP_DNS=8.8.8.8,1.1.1.1
_APP_DNS=172.16.238.100 # CoreDNS
_APP_DOMAIN=traefik
_APP_CONSOLE_DOMAIN=localhost
_APP_DOMAIN_FUNCTIONS=functions.localhost
_APP_DOMAIN_SITES=sites.localhost
_APP_DOMAIN_TARGET_CNAME=test.l
_APP_DOMAIN_TARGET_A=127.0.0.1
_APP_DOMAIN_TARGET_CNAME=cname.localhost
_APP_DOMAIN_TARGET_A=203.0.0.1
_APP_DOMAIN_TARGET_AAAA=::1
_APP_DOMAIN_TARGET_CAA=digicert.com
_APP_RULES_FORMAT=md5
+23
View File
@@ -89,9 +89,12 @@ services:
- ./public:/usr/src/code/public
- ./src:/usr/src/code/src
- ./dev:/usr/src/code/dev
dns:
- 172.16.238.100
depends_on:
- mariadb
- redis
- coredns
# - clamav
entrypoint:
- php
@@ -524,9 +527,12 @@ services:
image: appwrite-dev
networks:
- appwrite
dns:
- 172.16.238.100
depends_on:
- redis
- mariadb
- coredns
volumes:
- appwrite-config:/storage/config:rw
- appwrite-certificates:/storage/certificates:rw
@@ -1068,6 +1074,20 @@ services:
- appwrite
volumes:
- appwrite-redis:/data:rw
coredns: # DNS server for testing purposes (Proxy APIs)
image: coredns/coredns:1.12.4
container_name: appwrite-coredns
restart: unless-stopped
<<: *x-logging
command: ["-conf", "/mnt/resources/Corefile"]
ports:
- "1053:1053"
networks:
appwrite:
ipv4_address: 172.16.238.100
volumes:
- ./tests/resources/coredns:/mnt/resources:ro
# Dev Tools Start ------------------------------------------------------------------------------------------
#
@@ -1146,6 +1166,9 @@ networks:
name: gateway
appwrite:
name: appwrite
ipam:
config:
- subnet: 172.16.238.0/24
runtimes:
name: runtimes
@@ -68,8 +68,17 @@ class Get extends Action
$certificate = $dbForPlatform->getDocument('certificates', $rule->getAttribute('certificateId', ''));
$rule->setAttribute('logs', $certificate->getAttribute('logs', ''));
$rule->setAttribute('renewAt', $certificate->getAttribute('renewDate', ''));
if (new \DateTime($certificate->getUpdatedAt()) > new \DateTime($rule->getUpdatedAt())) {
$rule->setAttribute('$updatedAt', $certificate->getUpdatedAt());
$certificateHasUpdatedAt = $certificate->getUpdatedAt() !== null;
$ruleHasUpdatedAt = $rule->getUpdatedAt() !== null;
if ($certificateHasUpdatedAt) {
if ($ruleHasUpdatedAt) {
if (new \DateTime($certificate->getUpdatedAt()) > new \DateTime($rule->getUpdatedAt())) {
$rule->setAttribute('$updatedAt', $certificate->getUpdatedAt());
}
} else {
$rule->setAttribute('$updatedAt', $certificate->getUpdatedAt());
}
}
$response->dynamic($rule, Response::MODEL_PROXY_RULE);
@@ -110,8 +110,17 @@ class Update extends Action
$certificate = $dbForPlatform->getDocument('certificates', $rule->getAttribute('certificateId', ''));
$rule->setAttribute('logs', $certificate->getAttribute('logs', ''));
$rule->setAttribute('renewAt', $certificate->getAttribute('renewDate', ''));
if (new \DateTime($certificate->getUpdatedAt()) > new \DateTime($rule->getUpdatedAt())) {
$rule->setAttribute('$updatedAt', $certificate->getUpdatedAt());
$certificateHasUpdatedAt = $certificate->getUpdatedAt() !== null;
$ruleHasUpdatedAt = $rule->getUpdatedAt() !== null;
if ($certificateHasUpdatedAt) {
if ($ruleHasUpdatedAt) {
if (new \DateTime($certificate->getUpdatedAt()) > new \DateTime($rule->getUpdatedAt())) {
$rule->setAttribute('$updatedAt', $certificate->getUpdatedAt());
}
} else {
$rule->setAttribute('$updatedAt', $certificate->getUpdatedAt());
}
}
$response->dynamic($rule, Response::MODEL_PROXY_RULE);
@@ -110,8 +110,17 @@ class XList extends Action
$certificate = $dbForPlatform->getDocument('certificates', $rule->getAttribute('certificateId', ''));
$rule->setAttribute('logs', $certificate->getAttribute('logs', ''));
$rule->setAttribute('renewAt', $certificate->getAttribute('renewDate', ''));
if (new \DateTime($certificate->getUpdatedAt()) > new \DateTime($rule->getUpdatedAt())) {
$rule->setAttribute('$updatedAt', $certificate->getUpdatedAt());
$certificateHasUpdatedAt = $certificate->getUpdatedAt() !== null;
$ruleHasUpdatedAt = $rule->getUpdatedAt() !== null;
if ($certificateHasUpdatedAt) {
if ($ruleHasUpdatedAt) {
if (new \DateTime($certificate->getUpdatedAt()) > new \DateTime($rule->getUpdatedAt())) {
$rule->setAttribute('$updatedAt', $certificate->getUpdatedAt());
}
} else {
$rule->setAttribute('$updatedAt', $certificate->getUpdatedAt());
}
}
}
@@ -15,6 +15,36 @@ class ProxyCustomServerTest extends Scope
use ProjectCustom;
use SideServer;
protected function tearDown(): void
{
// Cleanup for testRuleVerification test
// Required as it uses static domain name
$rules = $this->listRules([
'queries' => [
Query::endsWith('domain', 'webapp.com')->toString(),
Query::limit(1000)->toString(),
]
]);
$this->assertEquals(200, $rules['headers']['status-code']);
foreach ($rules['body']['rules'] as $rule) {
$ruleId = $rule['$id'];
$response = $this->deleteRule($ruleId);
$this->assertEquals(204, $response['headers']['status-code']);
}
if ($rules['body']['total'] > 0) {
$rules = $this->listRules([
'queries' => [
Query::endsWith('domain', 'webapp.com')->toString(),
Query::limit(1)
]
]);
$this->assertEquals(200, $rules['headers']['status-code']);
$this->assertEquals(0, count($rules['body']['rules']));
$this->assertEquals(0, $rules['body']['total']);
}
}
public function testCreateRule(): void
{
$domain = \uniqid() . '-api.myapp.com';
@@ -540,4 +570,130 @@ class ProxyCustomServerTest extends Scope
$this->assertEquals(0, $rules['body']['total']);
$this->assertCount(0, $rules['body']['rules']);
}
public function testRuleVerification(): void
{
// 1. Site rule can verify
$site = $this->setupSite();
$siteId = $site['siteId'];
$rule = $this->createSiteRule('stage-site.webapp.com', $siteId);
$this->assertEquals(201, $rule['headers']['status-code']);
$this->assertEquals('verifying', $rule['body']['status']);
$this->assertEmpty($rule['body']['verificationLogs']);
$this->cleanupRule($rule['body']['$id']);
$this->cleanupSite($siteId);
// 2. Function rule can verify
$function = $this->setupFunction();
$functionId = $function['functionId'];
$rule = $this->createFunctionRule('stage-function.webapp.com', $functionId);
$this->assertEquals(201, $rule['headers']['status-code']);
$this->assertEquals('verifying', $rule['body']['status']);
$this->assertEmpty($rule['body']['verificationLogs']);
$this->cleanupRule($rule['body']['$id']);
$rule = $this->createAPIRule('stage-site.webapp.com');
$this->assertEquals(201, $rule['headers']['status-code']);
$this->assertEquals('created', $rule['body']['status']);
$this->assertStringContainsString('has incorrect CNAME value', $rule['body']['verificationLogs']);
$this->cleanupRule($rule['body']['$id']);
$this->cleanupFunction($functionId);
// 3. Wrong A record fails to verify
$rule = $this->createAPIRule('wrong-a-webapp.com');
$this->assertEquals(201, $rule['headers']['status-code']);
$this->assertEquals('created', $rule['body']['status']);
$this->assertStringContainsString('is missing CNAME record', $rule['body']['verificationLogs']);
$ruleId = $rule['body']['$id'];
$rule = $this->updateRuleVerification($ruleId);
$this->assertEquals(400, $rule['headers']['status-code']);
$this->assertStringContainsString('is missing CNAME record', $rule['body']['message']);
$rule = $this->getRule($ruleId);
$this->assertEquals(200, $rule['headers']['status-code']);
$this->assertEquals('created', $rule['body']['status']);
$this->cleanupRule($ruleId);
// 4. Correct A record can verify
$rule = $this->createAPIRule('webapp.com');
$this->assertEquals(201, $rule['headers']['status-code']);
$this->assertEquals('verifying', $rule['body']['status']);
$this->assertEmpty($rule['body']['verificationLogs']);
$this->cleanupRule($rule['body']['$id']);
// 5. Correct CNAME record can verify (no CAA record)
$rule = $this->createAPIRule('stage.webapp.com');
$this->assertEquals(201, $rule['headers']['status-code']);
$this->assertEquals('verifying', $rule['body']['status']);
$this->assertEmpty($rule['body']['verificationLogs']);
$this->cleanupRule($rule['body']['$id']);
// 6. Missing CNAME record fails to verify
$rule = $this->createAPIRule('stage-missing-cname.webapp.com');
$this->assertEquals(201, $rule['headers']['status-code']);
$this->assertEquals('created', $rule['body']['status']);
$this->assertStringContainsString('is missing CNAME record', $rule['body']['verificationLogs']);
$ruleId = $rule['body']['$id'];
$rule = $this->updateRuleVerification($ruleId);
$this->assertEquals(400, $rule['headers']['status-code']);
$this->assertStringContainsString('is missing CNAME record', $rule['body']['message']);
$rule = $this->getRule($ruleId);
$this->assertEquals(200, $rule['headers']['status-code']);
$this->assertEquals('created', $rule['body']['status']);
$this->cleanupRule($ruleId);
// 7. Wrong CNAME record fails to verify
$rule = $this->createAPIRule('stage-wrong-cname.webapp.com');
$this->assertEquals(201, $rule['headers']['status-code']);
$this->assertEquals('created', $rule['body']['status']);
$this->assertStringContainsString('has incorrect CNAME value', $rule['body']['verificationLogs']);
$ruleId = $rule['body']['$id'];
$rule = $this->updateRuleVerification($ruleId);
$this->assertEquals(400, $rule['headers']['status-code']);
$this->assertStringContainsString('has incorrect CNAME value', $rule['body']['message']);
$rule = $this->getRule($ruleId);
$this->assertEquals(200, $rule['headers']['status-code']);
$this->assertEquals('created', $rule['body']['status']);
$this->cleanupRule($ruleId);
// 8. Wrong CAA record fails to verify
$rule = $this->createAPIRule('stage-wrong-caa.webapp.com');
$this->assertEquals(201, $rule['headers']['status-code']);
$this->assertEquals('created', $rule['body']['status']);
$this->assertStringContainsString('has incorrect CAA value', $rule['body']['verificationLogs']);
$ruleId = $rule['body']['$id'];
$rule = $this->updateRuleVerification($ruleId);
$this->assertEquals(400, $rule['headers']['status-code']);
$this->assertStringContainsString('has incorrect CAA value', $rule['body']['message']);
$rule = $this->getRule($ruleId);
$this->assertEquals(200, $rule['headers']['status-code']);
$this->assertEquals('created', $rule['body']['status']);
$this->cleanupRule($ruleId);
// 9. Correct CAA record can verify
$rule = $this->createAPIRule('stage-correct-caa.webapp.com');
$this->assertEquals(201, $rule['headers']['status-code']);
$this->assertEquals('verifying', $rule['body']['status']);
$this->assertEmpty($rule['body']['verificationLogs']);
$this->cleanupRule($rule['body']['$id']);
}
}
+15
View File
@@ -0,0 +1,15 @@
# Re-use public resolver to answer unknown queries
. {
forward . 1.1.1.1
}
# Zones configuration
webapp.com {
file /mnt/resources/webapp.com.zone
}
# Zones configuration
wrong-a-webapp.com {
file /mnt/resources/wrong-a-webapp.com.zone
}
+23
View File
@@ -0,0 +1,23 @@
; Nessessary setup
$ORIGIN webapp.com.
@ IN SOA ns1.webapp.com. hostmaster.webapp.com. (2025091201 7200 3600 1209600 3600)
@ IN NS ns1.webapp.com.
ns1 IN A 127.0.0.1
; Custom DNS records
@ IN A 203.0.0.1
stage-site IN CNAME sites.localhost.
stage-function IN CNAME functions.localhost.
stage IN CNAME cname.localhost.
; No CAA record intentionally
stage-wrong-cname IN CNAME cname-wrong.tests.appwrite.io.
stage-wrong-caa IN A 203.0.0.1
stage-wrong-caa IN CAA 0 issue "unknown-issuer.org"
stage-correct-caa IN CNAME cname.localhost.
stage-correct-caa IN CAA 0 issue "digicert.com"
@@ -0,0 +1,8 @@
; Nessessary setup
$ORIGIN wrong-a-webapp.com.zone.
@ IN SOA ns1.wrong-a-webapp.com.zone. hostmaster.wrong-a-webapp.com.zone. (2025091201 7200 3600 1209600 3600)
@ IN NS ns1.wrong-a-webapp.com.zone.
ns1 IN A 127.0.0.1
; Custom DNS records
@ IN A 203.0.0.5