@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.
Files changed (84) hide show
  1. package/changelog.md +40 -0
  2. package/dist_rust/mailer-bin_linux_amd64 +0 -0
  3. package/dist_rust/mailer-bin_linux_arm64 +0 -0
  4. package/dist_ts/00_commitinfo_data.js +1 -1
  5. package/dist_ts/functions.errors.d.ts +3 -0
  6. package/dist_ts/functions.errors.js +8 -0
  7. package/dist_ts/index.d.ts +3 -0
  8. package/dist_ts/index.js +4 -1
  9. package/dist_ts/mail/core/classes.bouncemanager.d.ts +16 -12
  10. package/dist_ts/mail/core/classes.bouncemanager.js +146 -129
  11. package/dist_ts/mail/core/classes.email.js +15 -13
  12. package/dist_ts/mail/core/classes.emailvalidator.d.ts +3 -3
  13. package/dist_ts/mail/core/classes.emailvalidator.js +7 -5
  14. package/dist_ts/mail/core/classes.templatemanager.d.ts +10 -6
  15. package/dist_ts/mail/core/classes.templatemanager.js +35 -51
  16. package/dist_ts/mail/delivery/classes.delivery.queue.d.ts +92 -22
  17. package/dist_ts/mail/delivery/classes.delivery.queue.js +738 -151
  18. package/dist_ts/mail/delivery/classes.delivery.system.d.ts +13 -7
  19. package/dist_ts/mail/delivery/classes.delivery.system.js +458 -145
  20. package/dist_ts/mail/delivery/classes.unified.rate.limiter.js +9 -8
  21. package/dist_ts/mail/delivery/functions.safe-observers.d.ts +10 -0
  22. package/dist_ts/mail/delivery/functions.safe-observers.js +37 -0
  23. package/dist_ts/mail/delivery/interfaces.d.ts +21 -0
  24. package/dist_ts/mail/delivery/interfaces.js +1 -1
  25. package/dist_ts/mail/interfaces.storage.d.ts +37 -6
  26. package/dist_ts/mail/interfaces.storage.js +33 -3
  27. package/dist_ts/mail/routing/classes.dkim.manager.d.ts +10 -6
  28. package/dist_ts/mail/routing/classes.dkim.manager.js +47 -31
  29. package/dist_ts/mail/routing/classes.dns.manager.d.ts +7 -5
  30. package/dist_ts/mail/routing/classes.dns.manager.js +22 -11
  31. package/dist_ts/mail/routing/classes.email.action.executor.d.ts +2 -1
  32. package/dist_ts/mail/routing/classes.email.action.executor.js +45 -16
  33. package/dist_ts/mail/routing/classes.email.router.d.ts +5 -2
  34. package/dist_ts/mail/routing/classes.email.router.js +17 -12
  35. package/dist_ts/mail/routing/classes.unified.email.server.d.ts +12 -6
  36. package/dist_ts/mail/routing/classes.unified.email.server.js +69 -78
  37. package/dist_ts/mail/routing/interfaces.d.ts +0 -2
  38. package/dist_ts/mail/security/classes.dkimcreator.d.ts +22 -45
  39. package/dist_ts/mail/security/classes.dkimcreator.js +93 -296
  40. package/dist_ts/mail/security/classes.spfverifier.js +5 -3
  41. package/dist_ts/paths.d.ts +0 -12
  42. package/dist_ts/paths.js +3 -36
  43. package/dist_ts/plugins.d.ts +2 -5
  44. package/dist_ts/plugins.js +3 -6
  45. package/dist_ts/security/classes.contentscanner.js +14 -12
  46. package/dist_ts/security/classes.ipreputationchecker.d.ts +9 -6
  47. package/dist_ts/security/classes.ipreputationchecker.js +42 -93
  48. package/dist_ts/security/classes.rustsecuritybridge.d.ts +52 -4
  49. package/dist_ts/security/classes.rustsecuritybridge.js +201 -4
  50. package/dist_ts/security/classes.securitylogger.js +7 -5
  51. package/dist_ts/security/index.d.ts +1 -1
  52. package/dist_ts/security/index.js +2 -2
  53. package/package.json +8 -9
  54. package/readme.hints.md +4 -3
  55. package/readme.md +50 -18
  56. package/readme.plan.md +6 -0
  57. package/ts/00_commitinfo_data.ts +1 -1
  58. package/ts/functions.errors.ts +8 -0
  59. package/ts/index.ts +3 -0
  60. package/ts/mail/core/classes.bouncemanager.ts +188 -155
  61. package/ts/mail/core/classes.email.ts +20 -14
  62. package/ts/mail/core/classes.emailvalidator.ts +9 -7
  63. package/ts/mail/core/classes.templatemanager.ts +42 -57
  64. package/ts/mail/delivery/classes.delivery.queue.ts +996 -185
  65. package/ts/mail/delivery/classes.delivery.system.ts +583 -170
  66. package/ts/mail/delivery/classes.unified.rate.limiter.ts +9 -8
  67. package/ts/mail/delivery/functions.safe-observers.ts +45 -0
  68. package/ts/mail/delivery/interfaces.ts +27 -1
  69. package/ts/mail/interfaces.storage.ts +64 -10
  70. package/ts/mail/routing/classes.dkim.manager.ts +65 -40
  71. package/ts/mail/routing/classes.dns.manager.ts +39 -16
  72. package/ts/mail/routing/classes.email.action.executor.ts +64 -17
  73. package/ts/mail/routing/classes.email.router.ts +20 -13
  74. package/ts/mail/routing/classes.unified.email.server.ts +107 -86
  75. package/ts/mail/routing/interfaces.ts +0 -2
  76. package/ts/mail/security/classes.dkimcreator.ts +150 -355
  77. package/ts/mail/security/classes.spfverifier.ts +4 -2
  78. package/ts/paths.ts +2 -41
  79. package/ts/plugins.ts +1 -6
  80. package/ts/security/classes.contentscanner.ts +14 -12
  81. package/ts/security/classes.ipreputationchecker.ts +46 -99
  82. package/ts/security/classes.rustsecuritybridge.ts +272 -6
  83. package/ts/security/classes.securitylogger.ts +6 -4
  84. package/ts/security/index.ts +5 -1
@@ -1,10 +1,6 @@
1
1
  import * as plugins from '../../plugins.js';
2
- import { type IStorageManagerLike } from '../interfaces.storage.js';
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 keysDir;
18
- private storageManager?;
19
- constructor(keysDir?: string, storageManager?: IStorageManagerLike);
20
- private writeKeyPairToFilesystem;
21
- private storeLegacyKeysToStorage;
22
- private storeSelectorKeysToStorage;
23
- getKeyPathsForDomain(domainArg: string): Promise<IKeyPaths>;
24
- handleDKIMKeysForDomain(domainArg: string): Promise<void>;
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(domainArg: string): Promise<{
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(domainArg: string, selector?: string): Promise<plugins.tsclass.network.IDnsRecord>;
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(keysDir = paths.keysDir, storageManager) {
13
- this.keysDir = keysDir;
6
+ constructor(storageManager) {
14
7
  this.storageManager = storageManager;
15
8
  }
16
- async writeKeyPairToFilesystem(privateKeyPath, publicKeyPath, privateKey, publicKey) {
17
- await Promise.all([writeFile(privateKeyPath, privateKey), writeFile(publicKeyPath, publicKey)]);
9
+ legacyKey(domain, filename) {
10
+ return `/email/dkim/${domain}/${filename}`;
18
11
  }
19
- async storeLegacyKeysToStorage(domain, privateKey, publicKey) {
20
- if (!hasStorageManagerMethods(this.storageManager, ['set'])) {
21
- return;
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
- async storeSelectorKeysToStorage(domain, selector, privateKey, publicKey) {
29
- if (!hasStorageManagerMethods(this.storageManager, ['set'])) {
30
- return;
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(`/email/dkim/${domain}/${selector}/private.key`, privateKey),
34
- this.storageManager.set(`/email/dkim/${domain}/${selector}/public.key`, publicKey),
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 getKeyPathsForDomain(domainArg) {
38
- return {
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(domainArg, selector = 'default', keySize = 2048) {
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(domainArg, selector);
31
+ await this.readDKIMKeysForSelector(domain, selector);
63
32
  }
64
33
  catch {
65
- console.log(`No DKIM keys found for ${domainArg}/${selector}. Generating...`);
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
- // Read DKIM keys - always use storage manager, migrate from filesystem if needed
74
- async readDKIMKeys(domainArg) {
75
- // Try to read from storage manager first
76
- if (hasStorageManagerMethods(this.storageManager, ['get', 'set'])) {
77
- try {
78
- const [privateKey, publicKey] = await Promise.all([
79
- this.storageManager.get(`/email/dkim/${domainArg}/private.key`),
80
- this.storageManager.get(`/email/dkim/${domainArg}/public.key`)
81
- ]);
82
- if (privateKey && publicKey) {
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
- else {
113
- // No storage manager, use filesystem directly
114
- const keyPaths = await this.getKeyPathsForDomain(domainArg);
115
- const [privateKeyBuffer, publicKeyBuffer] = await Promise.all([
116
- readFile(keyPaths.privateKeyPath),
117
- readFile(keyPaths.publicKeyPath),
118
- ]);
119
- const privateKey = privateKeyBuffer.toString();
120
- const publicKey = publicKeyBuffer.toString();
121
- return { privateKey, publicKey };
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
- const { privateKey, publicKey } = await this.createDKIMKeys(keySize);
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
- const keyPaths = await this.getKeyPathsForSelector(domain, selector);
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
- // Changed to public for API access
173
- async getDNSRecordForDomain(domainArg, selector = 'default') {
174
- await this.handleDKIMKeysForSelector(domainArg, selector);
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
- if (!hasStorageManagerMethods(this.storageManager, ['get'])) {
182
- return null;
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
- if (!hasStorageManagerMethods(this.storageManager, ['set'])) {
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
- // Store new keys with new selector
226
- const newKeyPaths = await this.getKeyPathsForSelector(domain, newSelector);
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 keys = await this.readDKIMKeysForSelector(domain, selector);
320
- // Remove the PEM header and footer and newlines
321
- const pemHeader = '-----BEGIN PUBLIC KEY-----';
322
- const pemFooter = '-----END PUBLIC KEY-----';
323
- const keyContents = keys.publicKey
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: null,
335
- value: dnsRecordValue,
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
- if (!hasStorageManagerMethods(this.storageManager, ['get', 'list', 'delete'])) {
343
- console.log(`StorageManager for ${domain} does not support list/delete. Skipping DKIM cleanup.`);
344
- return;
345
- }
346
- // List all selectors for the domain
347
- const metadataKeys = await this.storageManager.list(`/email/dkim/${domain}/`);
348
- for (const key of metadataKeys) {
349
- if (key.endsWith('/metadata')) {
350
- const metadataStr = await this.storageManager.get(key);
351
- if (metadataStr) {
352
- const metadata = JSON.parse(metadataStr);
353
- // Check if key is rotated and past grace period
354
- if (metadata.rotatedAt) {
355
- const gracePeriodMs = gracePeriodDays * 24 * 60 * 60 * 1000;
356
- const now = Date.now();
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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