@push.rocks/smartmta 6.5.2 → 8.0.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 +40 -0
- package/dist_rust/mailer-bin_linux_amd64 +0 -0
- package/dist_rust/mailer-bin_linux_arm64 +0 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/functions.errors.d.ts +3 -0
- package/dist_ts/functions.errors.js +8 -0
- package/dist_ts/index.d.ts +3 -0
- package/dist_ts/index.js +4 -1
- package/dist_ts/mail/core/classes.bouncemanager.d.ts +16 -12
- package/dist_ts/mail/core/classes.bouncemanager.js +146 -129
- package/dist_ts/mail/core/classes.email.js +15 -13
- package/dist_ts/mail/core/classes.emailvalidator.d.ts +3 -3
- package/dist_ts/mail/core/classes.emailvalidator.js +7 -5
- package/dist_ts/mail/core/classes.templatemanager.d.ts +10 -6
- package/dist_ts/mail/core/classes.templatemanager.js +35 -51
- package/dist_ts/mail/delivery/classes.delivery.queue.d.ts +92 -22
- package/dist_ts/mail/delivery/classes.delivery.queue.js +738 -151
- package/dist_ts/mail/delivery/classes.delivery.system.d.ts +13 -7
- package/dist_ts/mail/delivery/classes.delivery.system.js +458 -145
- package/dist_ts/mail/delivery/classes.unified.rate.limiter.js +9 -8
- package/dist_ts/mail/delivery/functions.safe-observers.d.ts +10 -0
- package/dist_ts/mail/delivery/functions.safe-observers.js +37 -0
- package/dist_ts/mail/delivery/interfaces.d.ts +21 -0
- package/dist_ts/mail/delivery/interfaces.js +1 -1
- package/dist_ts/mail/interfaces.storage.d.ts +37 -6
- package/dist_ts/mail/interfaces.storage.js +33 -3
- package/dist_ts/mail/routing/classes.dkim.manager.d.ts +10 -6
- package/dist_ts/mail/routing/classes.dkim.manager.js +47 -31
- package/dist_ts/mail/routing/classes.dns.manager.d.ts +7 -5
- package/dist_ts/mail/routing/classes.dns.manager.js +22 -11
- package/dist_ts/mail/routing/classes.email.action.executor.d.ts +2 -1
- package/dist_ts/mail/routing/classes.email.action.executor.js +45 -16
- package/dist_ts/mail/routing/classes.email.router.d.ts +5 -2
- package/dist_ts/mail/routing/classes.email.router.js +17 -12
- package/dist_ts/mail/routing/classes.unified.email.server.d.ts +12 -6
- package/dist_ts/mail/routing/classes.unified.email.server.js +69 -78
- package/dist_ts/mail/routing/interfaces.d.ts +0 -2
- package/dist_ts/mail/security/classes.dkimcreator.d.ts +22 -45
- package/dist_ts/mail/security/classes.dkimcreator.js +93 -296
- package/dist_ts/mail/security/classes.spfverifier.js +5 -3
- package/dist_ts/paths.d.ts +0 -12
- package/dist_ts/paths.js +3 -36
- package/dist_ts/plugins.d.ts +2 -5
- package/dist_ts/plugins.js +3 -6
- package/dist_ts/security/classes.contentscanner.js +14 -12
- package/dist_ts/security/classes.ipreputationchecker.d.ts +9 -6
- package/dist_ts/security/classes.ipreputationchecker.js +42 -93
- package/dist_ts/security/classes.rustsecuritybridge.d.ts +52 -4
- package/dist_ts/security/classes.rustsecuritybridge.js +201 -4
- package/dist_ts/security/classes.securitylogger.js +7 -5
- package/dist_ts/security/index.d.ts +1 -1
- package/dist_ts/security/index.js +2 -2
- package/package.json +8 -9
- package/readme.hints.md +4 -3
- package/readme.md +50 -18
- package/readme.plan.md +6 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/functions.errors.ts +8 -0
- package/ts/index.ts +3 -0
- package/ts/mail/core/classes.bouncemanager.ts +188 -155
- package/ts/mail/core/classes.email.ts +20 -14
- package/ts/mail/core/classes.emailvalidator.ts +9 -7
- package/ts/mail/core/classes.templatemanager.ts +42 -57
- package/ts/mail/delivery/classes.delivery.queue.ts +996 -185
- package/ts/mail/delivery/classes.delivery.system.ts +583 -170
- package/ts/mail/delivery/classes.unified.rate.limiter.ts +9 -8
- package/ts/mail/delivery/functions.safe-observers.ts +45 -0
- package/ts/mail/delivery/interfaces.ts +27 -1
- package/ts/mail/interfaces.storage.ts +64 -10
- package/ts/mail/routing/classes.dkim.manager.ts +65 -40
- package/ts/mail/routing/classes.dns.manager.ts +39 -16
- package/ts/mail/routing/classes.email.action.executor.ts +64 -17
- package/ts/mail/routing/classes.email.router.ts +20 -13
- package/ts/mail/routing/classes.unified.email.server.ts +107 -86
- package/ts/mail/routing/interfaces.ts +0 -2
- package/ts/mail/security/classes.dkimcreator.ts +150 -355
- package/ts/mail/security/classes.spfverifier.ts +4 -2
- package/ts/paths.ts +2 -41
- package/ts/plugins.ts +1 -6
- package/ts/security/classes.contentscanner.ts +14 -12
- package/ts/security/classes.ipreputationchecker.ts +46 -99
- package/ts/security/classes.rustsecuritybridge.ts +272 -6
- package/ts/security/classes.securitylogger.ts +6 -4
- package/ts/security/index.ts +5 -1
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import * as plugins from '../../plugins.js';
|
|
2
|
-
import {
|
|
2
|
+
import type { IStorageManager } from '../interfaces.storage.js';
|
|
3
3
|
import { Email } from '../core/classes.email.js';
|
|
4
|
-
export interface IKeyPaths {
|
|
5
|
-
privateKeyPath: string;
|
|
6
|
-
publicKeyPath: string;
|
|
7
|
-
}
|
|
8
4
|
export interface IDkimKeyMetadata {
|
|
9
5
|
domain: string;
|
|
10
6
|
selector: string;
|
|
@@ -13,21 +9,31 @@ export interface IDkimKeyMetadata {
|
|
|
13
9
|
previousSelector?: string;
|
|
14
10
|
keySize: number;
|
|
15
11
|
}
|
|
12
|
+
export type TDkimKeyType = 'rsa' | 'ed25519';
|
|
13
|
+
export interface IDkimSigningMaterial {
|
|
14
|
+
privateKey: string;
|
|
15
|
+
publicKey: string;
|
|
16
|
+
keyType: TDkimKeyType;
|
|
17
|
+
}
|
|
16
18
|
export declare class DKIMCreator {
|
|
17
|
-
private
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
private
|
|
21
|
-
private
|
|
22
|
-
private
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
handleDKIMKeysForSelector(domainArg: string, selector?: string, keySize?: number): Promise<void>;
|
|
19
|
+
private readonly storageManager;
|
|
20
|
+
constructor(storageManager: IStorageManager);
|
|
21
|
+
private legacyKey;
|
|
22
|
+
private selectorKey;
|
|
23
|
+
private metadataKey;
|
|
24
|
+
private storeKeyPair;
|
|
25
|
+
handleDKIMKeysForDomain(domain: string): Promise<void>;
|
|
26
|
+
handleDKIMKeysForSelector(domain: string, selector?: string, keySize?: number): Promise<void>;
|
|
26
27
|
handleDKIMKeysForEmail(email: Email): Promise<void>;
|
|
27
|
-
readDKIMKeys(
|
|
28
|
+
readDKIMKeys(domain: string): Promise<{
|
|
29
|
+
privateKey: string;
|
|
30
|
+
publicKey: string;
|
|
31
|
+
}>;
|
|
32
|
+
readDKIMKeysForSelector(domain: string, selector: string): Promise<{
|
|
28
33
|
privateKey: string;
|
|
29
34
|
publicKey: string;
|
|
30
35
|
}>;
|
|
36
|
+
readValidatedDKIMKeysForSelector(domain: string, selector: string): Promise<IDkimSigningMaterial>;
|
|
31
37
|
createDKIMKeys(keySize?: number): Promise<{
|
|
32
38
|
privateKey: string;
|
|
33
39
|
publicKey: string;
|
|
@@ -38,40 +44,11 @@ export declare class DKIMCreator {
|
|
|
38
44
|
}>;
|
|
39
45
|
createAndStoreDKIMKeys(domain: string, keySize?: number): Promise<void>;
|
|
40
46
|
createAndStoreDKIMKeysForSelector(domain: string, selector: string, keySize?: number): Promise<void>;
|
|
41
|
-
getDNSRecordForDomain(
|
|
42
|
-
/**
|
|
43
|
-
* Get DKIM key metadata for a domain
|
|
44
|
-
*/
|
|
47
|
+
getDNSRecordForDomain(domain: string, selector?: string): Promise<plugins.tsclass.network.IDnsRecord>;
|
|
45
48
|
private getKeyMetadata;
|
|
46
|
-
/**
|
|
47
|
-
* Save DKIM key metadata
|
|
48
|
-
*/
|
|
49
49
|
private saveKeyMetadata;
|
|
50
|
-
/**
|
|
51
|
-
* Check if DKIM keys need rotation
|
|
52
|
-
*/
|
|
53
50
|
needsRotation(domain: string, selector?: string, rotationIntervalDays?: number): Promise<boolean>;
|
|
54
|
-
/**
|
|
55
|
-
* Rotate DKIM keys for a domain
|
|
56
|
-
*/
|
|
57
51
|
rotateDkimKeys(domain: string, currentSelector?: string, keySize?: number): Promise<string>;
|
|
58
|
-
/**
|
|
59
|
-
* Get key paths for a specific selector
|
|
60
|
-
*/
|
|
61
|
-
getKeyPathsForSelector(domain: string, selector: string): Promise<IKeyPaths>;
|
|
62
|
-
/**
|
|
63
|
-
* Read DKIM keys for a specific selector
|
|
64
|
-
*/
|
|
65
|
-
readDKIMKeysForSelector(domain: string, selector: string): Promise<{
|
|
66
|
-
privateKey: string;
|
|
67
|
-
publicKey: string;
|
|
68
|
-
}>;
|
|
69
|
-
/**
|
|
70
|
-
* Get DNS record for a specific selector
|
|
71
|
-
*/
|
|
72
52
|
getDNSRecordForSelector(domain: string, selector: string): Promise<plugins.tsclass.network.IDnsRecord>;
|
|
73
|
-
/**
|
|
74
|
-
* Clean up old DKIM keys after grace period
|
|
75
|
-
*/
|
|
76
53
|
cleanupOldKeys(domain: string, gracePeriodDays?: number): Promise<void>;
|
|
77
54
|
}
|
|
@@ -1,127 +1,84 @@
|
|
|
1
1
|
import * as plugins from '../../plugins.js';
|
|
2
|
-
import * as paths from '../../paths.js';
|
|
3
|
-
import { hasStorageManagerMethods } from '../interfaces.storage.js';
|
|
4
2
|
import { Email } from '../core/classes.email.js';
|
|
5
|
-
// MtaService reference removed
|
|
6
|
-
const readFile = plugins.util.promisify(plugins.fs.readFile);
|
|
7
|
-
const writeFile = plugins.util.promisify(plugins.fs.writeFile);
|
|
8
3
|
const generateKeyPair = plugins.util.promisify(plugins.crypto.generateKeyPair);
|
|
9
4
|
export class DKIMCreator {
|
|
10
|
-
keysDir;
|
|
11
5
|
storageManager;
|
|
12
|
-
constructor(
|
|
13
|
-
this.keysDir = keysDir;
|
|
6
|
+
constructor(storageManager) {
|
|
14
7
|
this.storageManager = storageManager;
|
|
15
8
|
}
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
legacyKey(domain, filename) {
|
|
10
|
+
return `/email/dkim/${domain}/${filename}`;
|
|
18
11
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
await Promise.all([
|
|
24
|
-
this.storageManager.set(`/email/dkim/${domain}/private.key`, privateKey),
|
|
25
|
-
this.storageManager.set(`/email/dkim/${domain}/public.key`, publicKey),
|
|
26
|
-
]);
|
|
12
|
+
selectorKey(domain, selector, filename) {
|
|
13
|
+
return selector === 'default'
|
|
14
|
+
? this.legacyKey(domain, filename)
|
|
15
|
+
: `/email/dkim/${domain}/${selector}/${filename}`;
|
|
27
16
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
17
|
+
metadataKey(domain, selector) {
|
|
18
|
+
return `/email/dkim/${domain}/${selector}/metadata`;
|
|
19
|
+
}
|
|
20
|
+
async storeKeyPair(domain, selector, privateKey, publicKey) {
|
|
32
21
|
await Promise.all([
|
|
33
|
-
this.storageManager.set(
|
|
34
|
-
this.storageManager.set(
|
|
22
|
+
this.storageManager.set(this.selectorKey(domain, selector, 'private.key'), privateKey),
|
|
23
|
+
this.storageManager.set(this.selectorKey(domain, selector, 'public.key'), publicKey),
|
|
35
24
|
]);
|
|
36
25
|
}
|
|
37
|
-
async
|
|
38
|
-
|
|
39
|
-
privateKeyPath: plugins.path.join(this.keysDir, `${domainArg}-private.pem`),
|
|
40
|
-
publicKeyPath: plugins.path.join(this.keysDir, `${domainArg}-public.pem`),
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
// Check if a DKIM key is present and creates one and stores it to disk otherwise
|
|
44
|
-
async handleDKIMKeysForDomain(domainArg) {
|
|
45
|
-
try {
|
|
46
|
-
await this.readDKIMKeys(domainArg);
|
|
47
|
-
}
|
|
48
|
-
catch (error) {
|
|
49
|
-
console.log(`No DKIM keys found for ${domainArg}. Generating...`);
|
|
50
|
-
await this.createAndStoreDKIMKeys(domainArg);
|
|
51
|
-
const dnsValue = await this.getDNSRecordForDomain(domainArg);
|
|
52
|
-
await plugins.smartfs.directory(paths.dnsRecordsDir).recursive().create();
|
|
53
|
-
await plugins.smartfs.file(plugins.path.join(paths.dnsRecordsDir, `${domainArg}.dkimrecord.json`)).write(JSON.stringify(dnsValue, null, 2));
|
|
54
|
-
}
|
|
26
|
+
async handleDKIMKeysForDomain(domain) {
|
|
27
|
+
await this.handleDKIMKeysForSelector(domain, 'default');
|
|
55
28
|
}
|
|
56
|
-
async handleDKIMKeysForSelector(
|
|
57
|
-
if (selector === 'default') {
|
|
58
|
-
await this.handleDKIMKeysForDomain(domainArg);
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
29
|
+
async handleDKIMKeysForSelector(domain, selector = 'default', keySize = 2048) {
|
|
61
30
|
try {
|
|
62
|
-
await this.readDKIMKeysForSelector(
|
|
31
|
+
await this.readDKIMKeysForSelector(domain, selector);
|
|
63
32
|
}
|
|
64
33
|
catch {
|
|
65
|
-
|
|
66
|
-
await this.createAndStoreDKIMKeysForSelector(domainArg, selector, keySize);
|
|
34
|
+
await this.createAndStoreDKIMKeysForSelector(domain, selector, keySize);
|
|
67
35
|
}
|
|
68
36
|
}
|
|
69
37
|
async handleDKIMKeysForEmail(email) {
|
|
70
38
|
const domain = email.from.split('@')[1];
|
|
71
39
|
await this.handleDKIMKeysForDomain(domain);
|
|
72
40
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return { privateKey, publicKey };
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
catch (error) {
|
|
87
|
-
// Fall through to migration check
|
|
88
|
-
}
|
|
89
|
-
// Check if keys exist in filesystem and migrate them to storage manager
|
|
90
|
-
const keyPaths = await this.getKeyPathsForDomain(domainArg);
|
|
91
|
-
try {
|
|
92
|
-
const [privateKeyBuffer, publicKeyBuffer] = await Promise.all([
|
|
93
|
-
readFile(keyPaths.privateKeyPath),
|
|
94
|
-
readFile(keyPaths.publicKeyPath),
|
|
95
|
-
]);
|
|
96
|
-
// Convert the buffers to strings
|
|
97
|
-
const privateKey = privateKeyBuffer.toString();
|
|
98
|
-
const publicKey = publicKeyBuffer.toString();
|
|
99
|
-
// Migrate to storage manager
|
|
100
|
-
console.log(`Migrating DKIM keys for ${domainArg} from filesystem to StorageManager`);
|
|
101
|
-
await this.storeLegacyKeysToStorage(domainArg, privateKey, publicKey);
|
|
102
|
-
return { privateKey, publicKey };
|
|
103
|
-
}
|
|
104
|
-
catch (error) {
|
|
105
|
-
if (error.code === 'ENOENT') {
|
|
106
|
-
// Keys don't exist anywhere
|
|
107
|
-
throw new Error(`DKIM keys not found for domain ${domainArg}`);
|
|
108
|
-
}
|
|
109
|
-
throw error;
|
|
110
|
-
}
|
|
41
|
+
async readDKIMKeys(domain) {
|
|
42
|
+
return this.readDKIMKeysForSelector(domain, 'default');
|
|
43
|
+
}
|
|
44
|
+
async readDKIMKeysForSelector(domain, selector) {
|
|
45
|
+
const [privateKey, publicKey] = await Promise.all([
|
|
46
|
+
this.storageManager.get(this.selectorKey(domain, selector, 'private.key')),
|
|
47
|
+
this.storageManager.get(this.selectorKey(domain, selector, 'public.key')),
|
|
48
|
+
]);
|
|
49
|
+
if (!privateKey || !publicKey) {
|
|
50
|
+
throw new Error(`DKIM keys not found for domain ${domain} with selector ${selector}`);
|
|
111
51
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
52
|
+
return { privateKey, publicKey };
|
|
53
|
+
}
|
|
54
|
+
async readValidatedDKIMKeysForSelector(domain, selector) {
|
|
55
|
+
const keys = await this.readDKIMKeysForSelector(domain, selector);
|
|
56
|
+
let privateKeyObject;
|
|
57
|
+
let publicKeyObject;
|
|
58
|
+
try {
|
|
59
|
+
privateKeyObject = plugins.crypto.createPrivateKey(keys.privateKey);
|
|
60
|
+
publicKeyObject = plugins.crypto.createPublicKey(keys.publicKey);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
throw new Error(`Invalid DKIM key material for domain ${domain} with selector ${selector}: ${error.message}`);
|
|
64
|
+
}
|
|
65
|
+
const keyType = privateKeyObject.asymmetricKeyType;
|
|
66
|
+
if (keyType !== 'rsa' && keyType !== 'ed25519') {
|
|
67
|
+
throw new Error(`Unsupported DKIM key type for domain ${domain} with selector ${selector}: ${keyType || 'unknown'}`);
|
|
68
|
+
}
|
|
69
|
+
if (publicKeyObject.asymmetricKeyType !== keyType) {
|
|
70
|
+
throw new Error(`DKIM public/private key type mismatch for domain ${domain} with selector ${selector}`);
|
|
71
|
+
}
|
|
72
|
+
const derivedPublicKey = plugins.crypto.createPublicKey(keys.privateKey).export({
|
|
73
|
+
type: 'spki',
|
|
74
|
+
format: 'der',
|
|
75
|
+
});
|
|
76
|
+
const suppliedPublicKey = publicKeyObject.export({ type: 'spki', format: 'der' });
|
|
77
|
+
if (!derivedPublicKey.equals(suppliedPublicKey)) {
|
|
78
|
+
throw new Error(`DKIM public/private key mismatch for domain ${domain} with selector ${selector}`);
|
|
122
79
|
}
|
|
80
|
+
return { ...keys, keyType };
|
|
123
81
|
}
|
|
124
|
-
// Create an RSA DKIM key pair - changed to public for API access
|
|
125
82
|
async createDKIMKeys(keySize = 2048) {
|
|
126
83
|
const { privateKey, publicKey } = await generateKeyPair('rsa', {
|
|
127
84
|
modulusLength: keySize,
|
|
@@ -130,7 +87,6 @@ export class DKIMCreator {
|
|
|
130
87
|
});
|
|
131
88
|
return { privateKey, publicKey };
|
|
132
89
|
}
|
|
133
|
-
// Create an Ed25519 DKIM key pair (RFC 8463)
|
|
134
90
|
async createEd25519Keys() {
|
|
135
91
|
const { privateKey, publicKey } = await generateKeyPair('ed25519', {
|
|
136
92
|
publicKeyEncoding: { type: 'spki', format: 'pem' },
|
|
@@ -138,244 +94,85 @@ export class DKIMCreator {
|
|
|
138
94
|
});
|
|
139
95
|
return { privateKey, publicKey };
|
|
140
96
|
}
|
|
141
|
-
// Create a DKIM key pair and store it to disk - changed to public for API access
|
|
142
97
|
async createAndStoreDKIMKeys(domain, keySize = 2048) {
|
|
143
|
-
|
|
144
|
-
const keyPaths = await this.getKeyPathsForDomain(domain);
|
|
145
|
-
await this.storeLegacyKeysToStorage(domain, privateKey, publicKey);
|
|
146
|
-
await this.writeKeyPairToFilesystem(keyPaths.privateKeyPath, keyPaths.publicKeyPath, privateKey, publicKey);
|
|
147
|
-
await this.saveKeyMetadata({
|
|
148
|
-
domain,
|
|
149
|
-
selector: 'default',
|
|
150
|
-
createdAt: Date.now(),
|
|
151
|
-
keySize,
|
|
152
|
-
});
|
|
153
|
-
console.log(`DKIM keys for ${domain} created and stored.`);
|
|
98
|
+
await this.createAndStoreDKIMKeysForSelector(domain, 'default', keySize);
|
|
154
99
|
}
|
|
155
100
|
async createAndStoreDKIMKeysForSelector(domain, selector, keySize = 2048) {
|
|
156
|
-
if (selector === 'default') {
|
|
157
|
-
await this.createAndStoreDKIMKeys(domain, keySize);
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
101
|
const { privateKey, publicKey } = await this.createDKIMKeys(keySize);
|
|
161
|
-
|
|
162
|
-
await this.storeSelectorKeysToStorage(domain, selector, privateKey, publicKey);
|
|
163
|
-
await this.writeKeyPairToFilesystem(keyPaths.privateKeyPath, keyPaths.publicKeyPath, privateKey, publicKey);
|
|
102
|
+
await this.storeKeyPair(domain, selector, privateKey, publicKey);
|
|
164
103
|
await this.saveKeyMetadata({
|
|
165
104
|
domain,
|
|
166
105
|
selector,
|
|
167
106
|
createdAt: Date.now(),
|
|
168
107
|
keySize,
|
|
169
108
|
});
|
|
170
|
-
console.log(`DKIM keys for ${domain}/${selector} created and stored.`);
|
|
171
109
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
return this.getDNSRecordForSelector(domainArg, selector);
|
|
110
|
+
async getDNSRecordForDomain(domain, selector = 'default') {
|
|
111
|
+
await this.handleDKIMKeysForSelector(domain, selector);
|
|
112
|
+
return this.getDNSRecordForSelector(domain, selector);
|
|
176
113
|
}
|
|
177
|
-
/**
|
|
178
|
-
* Get DKIM key metadata for a domain
|
|
179
|
-
*/
|
|
180
114
|
async getKeyMetadata(domain, selector = 'default') {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
const metadataKey = `/email/dkim/${domain}/${selector}/metadata`;
|
|
185
|
-
const metadataStr = await this.storageManager.get(metadataKey);
|
|
186
|
-
if (!metadataStr) {
|
|
187
|
-
return null;
|
|
188
|
-
}
|
|
189
|
-
return JSON.parse(metadataStr);
|
|
115
|
+
const metadata = await this.storageManager.get(this.metadataKey(domain, selector));
|
|
116
|
+
return metadata ? JSON.parse(metadata) : null;
|
|
190
117
|
}
|
|
191
|
-
/**
|
|
192
|
-
* Save DKIM key metadata
|
|
193
|
-
*/
|
|
194
118
|
async saveKeyMetadata(metadata) {
|
|
195
|
-
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
const metadataKey = `/email/dkim/${metadata.domain}/${metadata.selector}/metadata`;
|
|
199
|
-
await this.storageManager.set(metadataKey, JSON.stringify(metadata));
|
|
119
|
+
await this.storageManager.set(this.metadataKey(metadata.domain, metadata.selector), JSON.stringify(metadata));
|
|
200
120
|
}
|
|
201
|
-
/**
|
|
202
|
-
* Check if DKIM keys need rotation
|
|
203
|
-
*/
|
|
204
121
|
async needsRotation(domain, selector = 'default', rotationIntervalDays = 90) {
|
|
205
122
|
const metadata = await this.getKeyMetadata(domain, selector);
|
|
206
|
-
if (!metadata)
|
|
207
|
-
// No metadata means old keys, should rotate
|
|
123
|
+
if (!metadata)
|
|
208
124
|
return true;
|
|
209
|
-
|
|
210
|
-
const now = Date.now();
|
|
211
|
-
const keyAgeMs = now - metadata.createdAt;
|
|
212
|
-
const keyAgeDays = keyAgeMs / (1000 * 60 * 60 * 24);
|
|
213
|
-
return keyAgeDays >= rotationIntervalDays;
|
|
125
|
+
return (Date.now() - metadata.createdAt) / (1000 * 60 * 60 * 24) >= rotationIntervalDays;
|
|
214
126
|
}
|
|
215
|
-
/**
|
|
216
|
-
* Rotate DKIM keys for a domain
|
|
217
|
-
*/
|
|
218
127
|
async rotateDkimKeys(domain, currentSelector = 'default', keySize = 2048) {
|
|
219
|
-
console.log(`Rotating DKIM keys for ${domain}...`);
|
|
220
|
-
// Generate new selector based on date
|
|
221
128
|
const now = new Date();
|
|
222
129
|
const newSelector = `key${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}`;
|
|
223
|
-
// Create new keys with custom key size
|
|
224
130
|
const { privateKey, publicKey } = await this.createDKIMKeys(keySize);
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
// Store in storage manager if available
|
|
228
|
-
await this.storeSelectorKeysToStorage(domain, newSelector, privateKey, publicKey);
|
|
229
|
-
// Also store to filesystem
|
|
230
|
-
await this.writeKeyPairToFilesystem(newKeyPaths.privateKeyPath, newKeyPaths.publicKeyPath, privateKey, publicKey);
|
|
231
|
-
// Save metadata for new keys
|
|
232
|
-
const metadata = {
|
|
131
|
+
await this.storeKeyPair(domain, newSelector, privateKey, publicKey);
|
|
132
|
+
await this.saveKeyMetadata({
|
|
233
133
|
domain,
|
|
234
134
|
selector: newSelector,
|
|
235
135
|
createdAt: Date.now(),
|
|
236
136
|
previousSelector: currentSelector,
|
|
237
|
-
keySize
|
|
238
|
-
};
|
|
239
|
-
await this.saveKeyMetadata(metadata);
|
|
240
|
-
// Update metadata for old keys
|
|
137
|
+
keySize,
|
|
138
|
+
});
|
|
241
139
|
const oldMetadata = await this.getKeyMetadata(domain, currentSelector);
|
|
242
140
|
if (oldMetadata) {
|
|
243
141
|
oldMetadata.rotatedAt = Date.now();
|
|
244
142
|
await this.saveKeyMetadata(oldMetadata);
|
|
245
143
|
}
|
|
246
|
-
console.log(`DKIM keys rotated for ${domain}. New selector: ${newSelector}`);
|
|
247
144
|
return newSelector;
|
|
248
145
|
}
|
|
249
|
-
/**
|
|
250
|
-
* Get key paths for a specific selector
|
|
251
|
-
*/
|
|
252
|
-
async getKeyPathsForSelector(domain, selector) {
|
|
253
|
-
return {
|
|
254
|
-
privateKeyPath: plugins.path.join(this.keysDir, `${domain}-${selector}-private.pem`),
|
|
255
|
-
publicKeyPath: plugins.path.join(this.keysDir, `${domain}-${selector}-public.pem`),
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* Read DKIM keys for a specific selector
|
|
260
|
-
*/
|
|
261
|
-
async readDKIMKeysForSelector(domain, selector) {
|
|
262
|
-
// Try to read from storage manager first
|
|
263
|
-
if (hasStorageManagerMethods(this.storageManager, ['get', 'set'])) {
|
|
264
|
-
try {
|
|
265
|
-
const [privateKey, publicKey] = await Promise.all([
|
|
266
|
-
this.storageManager.get(`/email/dkim/${domain}/${selector}/private.key`),
|
|
267
|
-
this.storageManager.get(`/email/dkim/${domain}/${selector}/public.key`)
|
|
268
|
-
]);
|
|
269
|
-
if (privateKey && publicKey) {
|
|
270
|
-
return { privateKey, publicKey };
|
|
271
|
-
}
|
|
272
|
-
if (selector === 'default') {
|
|
273
|
-
return await this.readDKIMKeys(domain);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
catch (error) {
|
|
277
|
-
// Fall through to migration check
|
|
278
|
-
}
|
|
279
|
-
// Check if keys exist in filesystem and migrate them to storage manager
|
|
280
|
-
const keyPaths = await this.getKeyPathsForSelector(domain, selector);
|
|
281
|
-
try {
|
|
282
|
-
const [privateKeyBuffer, publicKeyBuffer] = await Promise.all([
|
|
283
|
-
readFile(keyPaths.privateKeyPath),
|
|
284
|
-
readFile(keyPaths.publicKeyPath),
|
|
285
|
-
]);
|
|
286
|
-
const privateKey = privateKeyBuffer.toString();
|
|
287
|
-
const publicKey = publicKeyBuffer.toString();
|
|
288
|
-
// Migrate to storage manager
|
|
289
|
-
console.log(`Migrating DKIM keys for ${domain}/${selector} from filesystem to StorageManager`);
|
|
290
|
-
await this.storeSelectorKeysToStorage(domain, selector, privateKey, publicKey);
|
|
291
|
-
return { privateKey, publicKey };
|
|
292
|
-
}
|
|
293
|
-
catch (error) {
|
|
294
|
-
if (error.code === 'ENOENT') {
|
|
295
|
-
throw new Error(`DKIM keys not found for domain ${domain} with selector ${selector}`);
|
|
296
|
-
}
|
|
297
|
-
throw error;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
else {
|
|
301
|
-
// No storage manager, use filesystem directly
|
|
302
|
-
if (selector === 'default') {
|
|
303
|
-
return this.readDKIMKeys(domain);
|
|
304
|
-
}
|
|
305
|
-
const keyPaths = await this.getKeyPathsForSelector(domain, selector);
|
|
306
|
-
const [privateKeyBuffer, publicKeyBuffer] = await Promise.all([
|
|
307
|
-
readFile(keyPaths.privateKeyPath),
|
|
308
|
-
readFile(keyPaths.publicKeyPath),
|
|
309
|
-
]);
|
|
310
|
-
const privateKey = privateKeyBuffer.toString();
|
|
311
|
-
const publicKey = publicKeyBuffer.toString();
|
|
312
|
-
return { privateKey, publicKey };
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
/**
|
|
316
|
-
* Get DNS record for a specific selector
|
|
317
|
-
*/
|
|
318
146
|
async getDNSRecordForSelector(domain, selector) {
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
.replace(pemHeader, '')
|
|
325
|
-
.replace(pemFooter, '')
|
|
326
|
-
.replace(/\n/g, '');
|
|
327
|
-
// Detect key type from PEM header
|
|
328
|
-
const keyAlgo = keys.privateKey.includes('ED25519') || keys.publicKey.length < 200 ? 'ed25519' : 'rsa';
|
|
329
|
-
// Generate the DKIM DNS TXT record
|
|
330
|
-
const dnsRecordValue = `v=DKIM1; h=sha256; k=${keyAlgo}; p=${keyContents}`;
|
|
147
|
+
const material = await this.readValidatedDKIMKeysForSelector(domain, selector);
|
|
148
|
+
const keyContents = material.publicKey
|
|
149
|
+
.replace('-----BEGIN PUBLIC KEY-----', '')
|
|
150
|
+
.replace('-----END PUBLIC KEY-----', '')
|
|
151
|
+
.replace(/\s/g, '');
|
|
331
152
|
return {
|
|
332
153
|
name: `${selector}._domainkey.${domain}`,
|
|
333
154
|
type: 'TXT',
|
|
334
|
-
dnsSecEnabled:
|
|
335
|
-
value:
|
|
155
|
+
dnsSecEnabled: false,
|
|
156
|
+
value: `v=DKIM1; h=sha256; k=${material.keyType}; p=${keyContents}`,
|
|
336
157
|
};
|
|
337
158
|
}
|
|
338
|
-
/**
|
|
339
|
-
* Clean up old DKIM keys after grace period
|
|
340
|
-
*/
|
|
341
159
|
async cleanupOldKeys(domain, gracePeriodDays = 30) {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
if (now - metadata.rotatedAt > gracePeriodMs) {
|
|
358
|
-
console.log(`Cleaning up old DKIM keys for ${domain} selector ${metadata.selector}`);
|
|
359
|
-
// Delete key files
|
|
360
|
-
const keyPaths = await this.getKeyPathsForSelector(domain, metadata.selector);
|
|
361
|
-
try {
|
|
362
|
-
await plugins.fs.promises.unlink(keyPaths.privateKeyPath);
|
|
363
|
-
await plugins.fs.promises.unlink(keyPaths.publicKeyPath);
|
|
364
|
-
}
|
|
365
|
-
catch (error) {
|
|
366
|
-
console.warn(`Failed to delete old key files: ${error.message}`);
|
|
367
|
-
}
|
|
368
|
-
// Delete selector-specific storage keys and metadata
|
|
369
|
-
await Promise.all([
|
|
370
|
-
this.storageManager.delete(`/email/dkim/${domain}/${metadata.selector}/private.key`),
|
|
371
|
-
this.storageManager.delete(`/email/dkim/${domain}/${metadata.selector}/public.key`),
|
|
372
|
-
]);
|
|
373
|
-
await this.storageManager.delete(key);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
}
|
|
160
|
+
const cutoff = Date.now() - gracePeriodDays * 24 * 60 * 60 * 1000;
|
|
161
|
+
const keys = await this.storageManager.list(`/email/dkim/${domain}/`);
|
|
162
|
+
const metadataKeys = keys.filter((key) => key.endsWith('/metadata'));
|
|
163
|
+
for (const metadataKey of metadataKeys) {
|
|
164
|
+
const serialized = await this.storageManager.get(metadataKey);
|
|
165
|
+
if (!serialized)
|
|
166
|
+
continue;
|
|
167
|
+
const metadata = JSON.parse(serialized);
|
|
168
|
+
if (!metadata.rotatedAt || metadata.rotatedAt >= cutoff)
|
|
169
|
+
continue;
|
|
170
|
+
await Promise.all([
|
|
171
|
+
this.storageManager.delete(this.selectorKey(domain, metadata.selector, 'private.key')),
|
|
172
|
+
this.storageManager.delete(this.selectorKey(domain, metadata.selector, 'public.key')),
|
|
173
|
+
this.storageManager.delete(metadataKey),
|
|
174
|
+
]);
|
|
378
175
|
}
|
|
379
176
|
}
|
|
380
177
|
}
|
|
381
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5ka2ltY3JlYXRvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL21haWwvc2VjdXJpdHkvY2xhc3Nlcy5ka2ltY3JlYXRvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBQzVDLE9BQU8sS0FBSyxLQUFLLE1BQU0sZ0JBQWdCLENBQUM7QUFDeEMsT0FBTyxFQUE0Qix3QkFBd0IsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBRTlGLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUNqRCwrQkFBK0I7QUFFL0IsTUFBTSxRQUFRLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxRQUFRLENBQUMsQ0FBQztBQUM3RCxNQUFNLFNBQVMsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLFNBQVMsQ0FBQyxDQUFDO0FBQy9ELE1BQU0sZUFBZSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsZUFBZSxDQUFDLENBQUM7QUFnQi9FLE1BQU0sT0FBTyxXQUFXO0lBQ2QsT0FBTyxDQUFTO0lBQ2hCLGNBQWMsQ0FBdUI7SUFFN0MsWUFBWSxPQUFPLEdBQUcsS0FBSyxDQUFDLE9BQU8sRUFBRSxjQUFvQztRQUN2RSxJQUFJLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztRQUN2QixJQUFJLENBQUMsY0FBYyxHQUFHLGNBQWMsQ0FBQztJQUN2QyxDQUFDO0lBRU8sS0FBSyxDQUFDLHdCQUF3QixDQUNwQyxjQUFzQixFQUN0QixhQUFxQixFQUNyQixVQUFrQixFQUNsQixTQUFpQjtRQUVqQixNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxTQUFTLENBQUMsY0FBYyxFQUFFLFVBQVUsQ0FB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
|
|
178
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5ka2ltY3JlYXRvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL21haWwvc2VjdXJpdHkvY2xhc3Nlcy5ka2ltY3JlYXRvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBRzVDLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUVqRCxNQUFNLGVBQWUsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLGVBQWUsQ0FBQyxDQUFDO0FBbUIvRSxNQUFNLE9BQU8sV0FBVztJQUNPO0lBQTdCLFlBQTZCLGNBQStCO1FBQS9CLG1CQUFjLEdBQWQsY0FBYyxDQUFpQjtJQUFHLENBQUM7SUFFeEQsU0FBUyxDQUFDLE1BQWMsRUFBRSxRQUFzQztRQUN0RSxPQUFPLGVBQWUsTUFBTSxJQUFJLFFBQVEsRUFBRSxDQUFDO0lBQzdDLENBQUM7SUFFTyxXQUFXLENBQ2pCLE1BQWMsRUFDZCxRQUFnQixFQUNoQixRQUFzQztRQUV0QyxPQUFPLFFBQVEsS0FBSyxTQUFTO1lBQzNCLENBQUMsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sRUFBRSxRQUFRLENBQUM7WUFDbEMsQ0FBQyxDQUFDLGVBQWUsTUFBTSxJQUFJLFFBQVEsSUFBSSxRQUFRLEVBQUUsQ0FBQztJQUN0RCxDQUFDO0lBRU8sV0FBVyxDQUFDLE1BQWMsRUFBRSxRQUFnQjtRQUNsRCxPQUFPLGVBQWUsTUFBTSxJQUFJLFFBQVEsV0FBVyxDQUFDO0lBQ3RELENBQUM7SUFFTyxLQUFLLENBQUMsWUFBWSxDQUN4QixNQUFjLEVBQ2QsUUFBZ0IsRUFDaEIsVUFBa0IsRUFDbEIsU0FBaUI7UUFFakIsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDO1lBQ2hCLElBQUksQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxX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
|