Bladeren bron

fix: verify dovecot imaps auth during deploy

Add a deployment-time IMAPS login probe so Dovecot Lua passdb failures are caught before traffic is reopened.

AI-Co-Authored-By: Codex
chendeben 1 maand geleden
bovenliggende
commit
30d31a791d
2 gewijzigde bestanden met toevoegingen van 52 en 1 verwijderingen
  1. 44 0
      scripts/deploy-remote.sh
  2. 8 1
      test/deploy-remote-script.test.js

+ 44 - 0
scripts/deploy-remote.sh

@@ -113,6 +113,50 @@ verify_mail_runtime() {
     return 1
   }
 
+  docker compose exec -T app </dev/null node -e '
+    const tls = require("node:tls");
+
+    const socket = tls.connect({
+      host: "dovecot",
+      port: 31993,
+      servername: process.env.MAIL_HOSTNAME || "mailhub.local",
+      rejectUnauthorized: false
+    });
+    socket.setEncoding("utf8");
+
+    let buffer = "";
+    let finished = false;
+    let loginSent = false;
+    const timer = setTimeout(() => finish(false), 5000);
+
+    function finish(ok) {
+      if (finished) return;
+      finished = true;
+      clearTimeout(timer);
+      socket.destroy();
+      process.exit(ok ? 0 : 1);
+    }
+
+    socket.on("data", (chunk) => {
+      buffer += chunk;
+      const lines = buffer.split(/\r?\n/).filter(Boolean);
+      if (!loginSent && lines.some((line) => /^\* OK\b/i.test(line))) {
+        loginSent = true;
+        socket.write("A1 LOGIN \"mailhub-healthcheck@invalid.invalid\" \"mailhub-healthcheck-invalid-password\"\r\n");
+      }
+
+      const tagged = lines.find((line) => /^A1\b/i.test(line));
+      if (!tagged) return;
+      if (/temporary authentication failure|unavailable/i.test(tagged)) return finish(false);
+      finish(/\bNO\b/i.test(tagged) && /auth/i.test(tagged));
+    });
+    socket.on("error", () => finish(false));
+    socket.on("end", () => finish(false));
+  ' >/dev/null 2>&1 || {
+    echo "Dovecot IMAPS authentication path check failed." >&2
+    return 1
+  }
+
   if ! docker compose exec -T --user 1000:1000 dovecot </dev/null sh -ec '
     test -r /run/secrets/dovecot_auth_secret
     test -s /run/secrets/dovecot_auth_secret

+ 8 - 1
test/deploy-remote-script.test.js

@@ -39,6 +39,13 @@ test('isolates runtime probes and setup scripts from the SSH heredoc stdin', ()
   );
 });
 
+test('checks the Dovecot IMAPS authentication path through Lua passdb', () => {
+  assert.match(scriptSource, /tls\.connect\(\{[\s\S]*host: "dovecot"[\s\S]*port: 31993/);
+  assert.match(scriptSource, /mailhub-healthcheck@invalid\.invalid/);
+  assert.match(scriptSource, /temporary authentication failure\|unavailable/);
+  assert.match(scriptSource, /Dovecot IMAPS authentication path check failed\./);
+});
+
 test('waits for app and postfix health before and after certificate synchronization', { skip: !canRun }, (t) => {
   const fixture = createFixture(t);
   const result = runDeploy(fixture);
@@ -58,7 +65,7 @@ test('waits for app and postfix health before and after certificate synchronizat
       'health:dovecot-container'
     ]
   );
-  assert.equal(events.filter((event) => event === 'runtime:app').length, 4);
+  assert.equal(events.filter((event) => event === 'runtime:app').length, 6);
   assert.equal(events.filter((event) => event === 'runtime:dovecot').length, 2);
 });