Initial commit on Forgejo
Some checks are pending
CI and deploy / Test (push) Waiting to run
CI and deploy / Build, push, and deploy (push) Blocked by required conditions

Fresh repository history for elektrine/elektrine-haraka hosted at
https://git.elektrine.com/elektrine/elektrine-haraka.
This commit is contained in:
Maxfield Luke 2026-07-29 04:43:42 -04:00
commit 67c1d41a0c
82 changed files with 8713 additions and 0 deletions

View file

@ -0,0 +1,86 @@
/**
* Elektrine Recipient Verification Plugin
*
* Verifies that recipients exist before accepting email.
* Prevents backscatter attacks from bouncing to spoofed senders.
*/
'use strict';
const constants = require('haraka-constants');
// Shared library modules
const { config, http: httpClient, domains } = require('../lib');
exports.register = function() {
const plugin = this;
// Load configuration
plugin.cfg = config.load();
// Log initial domains (may be updated dynamically from Phoenix)
const local_domains = domains.get_local_domains();
plugin.loginfo(`Recipient verification enabled for: ${local_domains.join(', ')}`);
// Log cache status periodically (every 10 minutes)
setInterval(() => {
const cache_status = domains.get_cache_status();
if (cache_status.cached) {
plugin.logdebug(`Domain cache: ${cache_status.domains.length} domains, age: ${Math.round(cache_status.age_ms / 1000)}s`);
}
}, 10 * 60 * 1000);
};
exports.hook_rcpt = function(next, connection, params) {
const plugin = this;
const rcpt = params[0];
if (!rcpt || !rcpt.host) {
return next();
}
const recipient_domain = rcpt.host.toLowerCase();
const recipient_email = rcpt.address().toLowerCase();
// Check if this is for a local domain
const is_local = domains.is_local_domain(recipient_domain);
if (!is_local) {
// External domain - only allow if authenticated (relay)
// Check if connection is authenticated (handled by auth/auth_proxy plugin)
if (connection.relaying) {
plugin.loginfo(`Authenticated relay to external domain: ${recipient_email}`);
return next();
}
// Not authenticated and not local domain - reject (prevent open relay)
plugin.logwarn(`Rejecting relay attempt to external domain: ${recipient_email}`);
return next(constants.DENY, `Relay not permitted for ${recipient_domain}`);
}
// Local domain - verify recipient exists in Phoenix
plugin.loginfo(`Verifying recipient exists: ${recipient_email}`);
// Verify with Phoenix app
httpClient.verify_recipient(plugin.cfg.verify_url, recipient_email, {
api_key: plugin.cfg.phoenix_api_key,
timeout: plugin.cfg.verify_timeout,
logger: (msg) => plugin.logdebug(msg)
})
.then((exists) => {
if (exists) {
plugin.loginfo(`Recipient verified: ${recipient_email}`);
return next(constants.OK);
} else {
plugin.logwarn(`Recipient does not exist: ${recipient_email}`);
return next(constants.DENY, `Recipient ${recipient_email} does not exist`);
}
})
.catch((err) => {
plugin.logerror(`Recipient verification failed: ${err.message}`);
// On error, defer (temp fail) so the sending server retries later
// This avoids accepting mail for potentially non-existent recipients
// which would cause backscatter bounces
return next(constants.DENYSOFT, 'Temporary recipient verification failure, please retry');
});
};