@push.rocks/smartmta 5.1.2 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.md +14 -0
- package/dist_ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/index.d.ts +3 -0
- package/dist_ts/index.js +4 -0
- package/dist_ts/logger.d.ts +17 -0
- package/dist_ts/logger.js +76 -0
- package/dist_ts/mail/core/classes.bouncemanager.d.ts +185 -0
- package/dist_ts/mail/core/classes.bouncemanager.js +569 -0
- package/dist_ts/mail/core/classes.email.d.ts +291 -0
- package/dist_ts/mail/core/classes.email.js +802 -0
- package/dist_ts/mail/core/classes.emailvalidator.d.ts +61 -0
- package/dist_ts/mail/core/classes.emailvalidator.js +184 -0
- package/dist_ts/mail/core/classes.templatemanager.d.ts +95 -0
- package/dist_ts/mail/core/classes.templatemanager.js +240 -0
- package/dist_ts/mail/core/index.d.ts +4 -0
- package/dist_ts/mail/core/index.js +6 -0
- package/dist_ts/mail/delivery/classes.delivery.queue.d.ts +163 -0
- package/dist_ts/mail/delivery/classes.delivery.queue.js +488 -0
- package/dist_ts/mail/delivery/classes.delivery.system.d.ts +160 -0
- package/dist_ts/mail/delivery/classes.delivery.system.js +630 -0
- package/dist_ts/mail/delivery/classes.unified.rate.limiter.d.ts +200 -0
- package/dist_ts/mail/delivery/classes.unified.rate.limiter.js +820 -0
- package/dist_ts/mail/delivery/index.d.ts +4 -0
- package/dist_ts/mail/delivery/index.js +6 -0
- package/dist_ts/mail/delivery/interfaces.d.ts +140 -0
- package/dist_ts/mail/delivery/interfaces.js +17 -0
- package/dist_ts/mail/index.d.ts +7 -0
- package/dist_ts/mail/index.js +12 -0
- package/dist_ts/mail/routing/classes.dkim.manager.d.ts +25 -0
- package/dist_ts/mail/routing/classes.dkim.manager.js +127 -0
- package/dist_ts/mail/routing/classes.dns.manager.d.ts +79 -0
- package/dist_ts/mail/routing/classes.dns.manager.js +415 -0
- package/dist_ts/mail/routing/classes.domain.registry.d.ts +54 -0
- package/dist_ts/mail/routing/classes.domain.registry.js +119 -0
- package/dist_ts/mail/routing/classes.email.action.executor.d.ts +33 -0
- package/dist_ts/mail/routing/classes.email.action.executor.js +137 -0
- package/dist_ts/mail/routing/classes.email.router.d.ts +171 -0
- package/dist_ts/mail/routing/classes.email.router.js +494 -0
- package/dist_ts/mail/routing/classes.unified.email.server.d.ts +241 -0
- package/dist_ts/mail/routing/classes.unified.email.server.js +935 -0
- package/dist_ts/mail/routing/index.d.ts +7 -0
- package/dist_ts/mail/routing/index.js +9 -0
- package/dist_ts/mail/routing/interfaces.d.ts +187 -0
- package/dist_ts/mail/routing/interfaces.js +2 -0
- package/dist_ts/mail/security/classes.dkimcreator.d.ts +72 -0
- package/dist_ts/mail/security/classes.dkimcreator.js +360 -0
- package/dist_ts/mail/security/classes.spfverifier.d.ts +62 -0
- package/dist_ts/mail/security/classes.spfverifier.js +87 -0
- package/dist_ts/mail/security/index.d.ts +2 -0
- package/dist_ts/mail/security/index.js +4 -0
- package/dist_ts/paths.d.ts +14 -0
- package/dist_ts/paths.js +39 -0
- package/dist_ts/plugins.d.ts +24 -0
- package/dist_ts/plugins.js +28 -0
- package/dist_ts/security/classes.contentscanner.d.ts +130 -0
- package/dist_ts/security/classes.contentscanner.js +338 -0
- package/dist_ts/security/classes.ipreputationchecker.d.ts +73 -0
- package/dist_ts/security/classes.ipreputationchecker.js +263 -0
- package/dist_ts/security/classes.rustsecuritybridge.d.ts +398 -0
- package/dist_ts/security/classes.rustsecuritybridge.js +484 -0
- package/dist_ts/security/classes.securitylogger.d.ts +140 -0
- package/dist_ts/security/classes.securitylogger.js +235 -0
- package/dist_ts/security/index.d.ts +4 -0
- package/dist_ts/security/index.js +5 -0
- package/package.json +6 -1
- package/readme.md +52 -9
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/index.ts +3 -0
- package/ts/logger.ts +91 -0
- package/ts/mail/core/classes.bouncemanager.ts +731 -0
- package/ts/mail/core/classes.email.ts +942 -0
- package/ts/mail/core/classes.emailvalidator.ts +239 -0
- package/ts/mail/core/classes.templatemanager.ts +320 -0
- package/ts/mail/core/index.ts +5 -0
- package/ts/mail/delivery/classes.delivery.queue.ts +645 -0
- package/ts/mail/delivery/classes.delivery.system.ts +816 -0
- package/ts/mail/delivery/classes.unified.rate.limiter.ts +1053 -0
- package/ts/mail/delivery/index.ts +5 -0
- package/ts/mail/delivery/interfaces.ts +167 -0
- package/ts/mail/index.ts +17 -0
- package/ts/mail/routing/classes.dkim.manager.ts +157 -0
- package/ts/mail/routing/classes.dns.manager.ts +573 -0
- package/ts/mail/routing/classes.domain.registry.ts +139 -0
- package/ts/mail/routing/classes.email.action.executor.ts +175 -0
- package/ts/mail/routing/classes.email.router.ts +575 -0
- package/ts/mail/routing/classes.unified.email.server.ts +1207 -0
- package/ts/mail/routing/index.ts +9 -0
- package/ts/mail/routing/interfaces.ts +202 -0
- package/ts/mail/security/classes.dkimcreator.ts +447 -0
- package/ts/mail/security/classes.spfverifier.ts +126 -0
- package/ts/mail/security/index.ts +3 -0
- package/ts/paths.ts +48 -0
- package/ts/plugins.ts +53 -0
- package/ts/security/classes.contentscanner.ts +400 -0
- package/ts/security/classes.ipreputationchecker.ts +315 -0
- package/ts/security/classes.rustsecuritybridge.ts +943 -0
- package/ts/security/classes.securitylogger.ts +299 -0
- package/ts/security/index.ts +40 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMTP and email delivery interface definitions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* SMTP session state enumeration
|
|
7
|
+
*/
|
|
8
|
+
export enum SmtpState {
|
|
9
|
+
GREETING = 'GREETING',
|
|
10
|
+
AFTER_EHLO = 'AFTER_EHLO',
|
|
11
|
+
MAIL_FROM = 'MAIL_FROM',
|
|
12
|
+
RCPT_TO = 'RCPT_TO',
|
|
13
|
+
DATA = 'DATA',
|
|
14
|
+
DATA_RECEIVING = 'DATA_RECEIVING',
|
|
15
|
+
FINISHED = 'FINISHED'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Email processing mode type
|
|
20
|
+
*/
|
|
21
|
+
export type EmailProcessingMode = 'forward' | 'mta' | 'process';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Envelope recipient information
|
|
25
|
+
*/
|
|
26
|
+
export interface IEnvelopeRecipient {
|
|
27
|
+
/**
|
|
28
|
+
* Email address of the recipient
|
|
29
|
+
*/
|
|
30
|
+
address: string;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Additional SMTP command arguments
|
|
34
|
+
*/
|
|
35
|
+
args: Record<string, string>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* SMTP session envelope information
|
|
40
|
+
*/
|
|
41
|
+
export interface ISmtpEnvelope {
|
|
42
|
+
/**
|
|
43
|
+
* Envelope sender (MAIL FROM) information
|
|
44
|
+
*/
|
|
45
|
+
mailFrom: {
|
|
46
|
+
/**
|
|
47
|
+
* Email address of the sender
|
|
48
|
+
*/
|
|
49
|
+
address: string;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Additional SMTP command arguments
|
|
53
|
+
*/
|
|
54
|
+
args: Record<string, string>;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Envelope recipients (RCPT TO) information
|
|
59
|
+
*/
|
|
60
|
+
rcptTo: IEnvelopeRecipient[];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* SMTP Session interface - represents an active SMTP connection
|
|
65
|
+
*/
|
|
66
|
+
export interface ISmtpSession {
|
|
67
|
+
/**
|
|
68
|
+
* Unique session identifier
|
|
69
|
+
*/
|
|
70
|
+
id: string;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Current session state in the SMTP conversation
|
|
74
|
+
*/
|
|
75
|
+
state: SmtpState;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Hostname provided by the client in EHLO/HELO command
|
|
79
|
+
*/
|
|
80
|
+
clientHostname: string;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* MAIL FROM email address (legacy format)
|
|
84
|
+
*/
|
|
85
|
+
mailFrom: string;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* RCPT TO email addresses (legacy format)
|
|
89
|
+
*/
|
|
90
|
+
rcptTo: string[];
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Raw email data being received
|
|
94
|
+
*/
|
|
95
|
+
emailData: string;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Chunks of email data for more efficient buffer management
|
|
99
|
+
*/
|
|
100
|
+
emailDataChunks?: string[];
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Whether the connection is using TLS
|
|
104
|
+
*/
|
|
105
|
+
useTLS: boolean;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Whether the connection has ended
|
|
109
|
+
*/
|
|
110
|
+
connectionEnded: boolean;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Remote IP address of the client
|
|
114
|
+
*/
|
|
115
|
+
remoteAddress: string;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Whether the connection is secure (TLS)
|
|
119
|
+
*/
|
|
120
|
+
secure: boolean;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Whether the client has been authenticated
|
|
124
|
+
*/
|
|
125
|
+
authenticated: boolean;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* SMTP envelope information (structured format)
|
|
129
|
+
*/
|
|
130
|
+
envelope: ISmtpEnvelope;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Email processing mode to use for this session
|
|
134
|
+
*/
|
|
135
|
+
processingMode?: EmailProcessingMode;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Timestamp of last activity for session timeout tracking
|
|
139
|
+
*/
|
|
140
|
+
lastActivity?: number;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Timeout ID for DATA command timeout
|
|
144
|
+
*/
|
|
145
|
+
dataTimeoutId?: NodeJS.Timeout;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* SMTP authentication data
|
|
150
|
+
*/
|
|
151
|
+
export interface ISmtpAuth {
|
|
152
|
+
/**
|
|
153
|
+
* Authentication method used
|
|
154
|
+
*/
|
|
155
|
+
method: 'PLAIN' | 'LOGIN' | 'OAUTH2' | string;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Username for authentication
|
|
159
|
+
*/
|
|
160
|
+
username: string;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Password or token for authentication
|
|
164
|
+
*/
|
|
165
|
+
password: string;
|
|
166
|
+
}
|
|
167
|
+
|
package/ts/mail/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Export all mail modules for simplified imports
|
|
2
|
+
export * from './routing/index.js';
|
|
3
|
+
export * from './security/index.js';
|
|
4
|
+
|
|
5
|
+
// Make the core and delivery modules accessible
|
|
6
|
+
import * as Core from './core/index.js';
|
|
7
|
+
import * as Delivery from './delivery/index.js';
|
|
8
|
+
|
|
9
|
+
export { Core, Delivery };
|
|
10
|
+
|
|
11
|
+
// For direct imports
|
|
12
|
+
import { Email } from './core/classes.email.js';
|
|
13
|
+
|
|
14
|
+
// Re-export commonly used classes
|
|
15
|
+
export {
|
|
16
|
+
Email,
|
|
17
|
+
};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { logger } from '../../logger.js';
|
|
2
|
+
import { DKIMCreator } from '../security/classes.dkimcreator.js';
|
|
3
|
+
import { DomainRegistry } from './classes.domain.registry.js';
|
|
4
|
+
import { RustSecurityBridge } from '../../security/classes.rustsecuritybridge.js';
|
|
5
|
+
import { Email } from '../core/classes.email.js';
|
|
6
|
+
|
|
7
|
+
/** External DcRouter interface shape used by DkimManager */
|
|
8
|
+
interface DcRouter {
|
|
9
|
+
storageManager: any;
|
|
10
|
+
dnsServer?: any;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Manages DKIM key setup, rotation, and signing for all configured domains
|
|
15
|
+
*/
|
|
16
|
+
export class DkimManager {
|
|
17
|
+
private dkimKeys: Map<string, string> = new Map();
|
|
18
|
+
|
|
19
|
+
constructor(
|
|
20
|
+
private dkimCreator: DKIMCreator,
|
|
21
|
+
private domainRegistry: DomainRegistry,
|
|
22
|
+
private dcRouter: DcRouter,
|
|
23
|
+
private rustBridge: RustSecurityBridge,
|
|
24
|
+
) {}
|
|
25
|
+
|
|
26
|
+
async setupDkimForDomains(): Promise<void> {
|
|
27
|
+
const domainConfigs = this.domainRegistry.getAllConfigs();
|
|
28
|
+
|
|
29
|
+
if (domainConfigs.length === 0) {
|
|
30
|
+
logger.log('warn', 'No domains configured for DKIM');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
for (const domainConfig of domainConfigs) {
|
|
35
|
+
const domain = domainConfig.domain;
|
|
36
|
+
const selector = domainConfig.dkim?.selector || 'default';
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
let keyPair: { privateKey: string; publicKey: string };
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
keyPair = await this.dkimCreator.readDKIMKeys(domain);
|
|
43
|
+
logger.log('info', `Using existing DKIM keys for domain: ${domain}`);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
keyPair = await this.dkimCreator.createDKIMKeys();
|
|
46
|
+
await this.dkimCreator.createAndStoreDKIMKeys(domain);
|
|
47
|
+
logger.log('info', `Generated new DKIM keys for domain: ${domain}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
this.dkimKeys.set(domain, keyPair.privateKey);
|
|
51
|
+
logger.log('info', `DKIM keys loaded for domain: ${domain} with selector: ${selector}`);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
logger.log('error', `Failed to set up DKIM for domain ${domain}: ${error.message}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async checkAndRotateDkimKeys(): Promise<void> {
|
|
59
|
+
const domainConfigs = this.domainRegistry.getAllConfigs();
|
|
60
|
+
|
|
61
|
+
for (const domainConfig of domainConfigs) {
|
|
62
|
+
const domain = domainConfig.domain;
|
|
63
|
+
const selector = domainConfig.dkim?.selector || 'default';
|
|
64
|
+
const rotateKeys = domainConfig.dkim?.rotateKeys || false;
|
|
65
|
+
const rotationInterval = domainConfig.dkim?.rotationInterval || 90;
|
|
66
|
+
const keySize = domainConfig.dkim?.keySize || 2048;
|
|
67
|
+
|
|
68
|
+
if (!rotateKeys) {
|
|
69
|
+
logger.log('debug', `DKIM key rotation disabled for ${domain}`);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const needsRotation = await this.dkimCreator.needsRotation(domain, selector, rotationInterval);
|
|
75
|
+
|
|
76
|
+
if (needsRotation) {
|
|
77
|
+
logger.log('info', `DKIM keys need rotation for ${domain} (selector: ${selector})`);
|
|
78
|
+
|
|
79
|
+
const newSelector = await this.dkimCreator.rotateDkimKeys(domain, selector, keySize);
|
|
80
|
+
|
|
81
|
+
domainConfig.dkim = {
|
|
82
|
+
...domainConfig.dkim,
|
|
83
|
+
selector: newSelector
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
if (domainConfig.dnsMode === 'internal-dns' && this.dcRouter.dnsServer) {
|
|
87
|
+
const keyPair = await this.dkimCreator.readDKIMKeysForSelector(domain, newSelector);
|
|
88
|
+
const publicKeyBase64 = keyPair.publicKey
|
|
89
|
+
.replace(/-----BEGIN PUBLIC KEY-----/g, '')
|
|
90
|
+
.replace(/-----END PUBLIC KEY-----/g, '')
|
|
91
|
+
.replace(/\s/g, '');
|
|
92
|
+
|
|
93
|
+
const ttl = domainConfig.dns?.internal?.ttl || 3600;
|
|
94
|
+
|
|
95
|
+
this.dcRouter.dnsServer.registerHandler(
|
|
96
|
+
`${newSelector}._domainkey.${domain}`,
|
|
97
|
+
['TXT'],
|
|
98
|
+
() => ({
|
|
99
|
+
name: `${newSelector}._domainkey.${domain}`,
|
|
100
|
+
type: 'TXT',
|
|
101
|
+
class: 'IN',
|
|
102
|
+
ttl: ttl,
|
|
103
|
+
data: `v=DKIM1; k=rsa; p=${publicKeyBase64}`
|
|
104
|
+
})
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
logger.log('info', `DKIM DNS handler registered for new selector: ${newSelector}._domainkey.${domain}`);
|
|
108
|
+
|
|
109
|
+
await this.dcRouter.storageManager.set(
|
|
110
|
+
`/email/dkim/${domain}/public.key`,
|
|
111
|
+
keyPair.publicKey
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
this.dkimCreator.cleanupOldKeys(domain, 30).catch(error => {
|
|
116
|
+
logger.log('warn', `Failed to cleanup old DKIM keys for ${domain}: ${error.message}`);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
} else {
|
|
120
|
+
logger.log('debug', `DKIM keys for ${domain} are up to date`);
|
|
121
|
+
}
|
|
122
|
+
} catch (error) {
|
|
123
|
+
logger.log('error', `Failed to check/rotate DKIM keys for ${domain}: ${error.message}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async handleDkimSigning(email: Email, domain: string, selector: string): Promise<void> {
|
|
129
|
+
try {
|
|
130
|
+
await this.dkimCreator.handleDKIMKeysForDomain(domain);
|
|
131
|
+
const { privateKey } = await this.dkimCreator.readDKIMKeys(domain);
|
|
132
|
+
const rawEmail = email.toRFC822String();
|
|
133
|
+
|
|
134
|
+
// Detect key type from PEM header
|
|
135
|
+
const keyType = privateKey.includes('ED25519') ? 'ed25519' : 'rsa';
|
|
136
|
+
|
|
137
|
+
const signResult = await this.rustBridge.signDkim({
|
|
138
|
+
rawMessage: rawEmail,
|
|
139
|
+
domain,
|
|
140
|
+
selector,
|
|
141
|
+
privateKey,
|
|
142
|
+
keyType,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
if (signResult.header) {
|
|
146
|
+
email.addHeader('DKIM-Signature', signResult.header);
|
|
147
|
+
logger.log('info', `Successfully added DKIM signature for ${domain}`);
|
|
148
|
+
}
|
|
149
|
+
} catch (error) {
|
|
150
|
+
logger.log('error', `Failed to sign email with DKIM: ${error.message}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
getDkimKey(domain: string): string | undefined {
|
|
155
|
+
return this.dkimKeys.get(domain);
|
|
156
|
+
}
|
|
157
|
+
}
|