node-opcua-server-configuration 2.163.1 → 2.165.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/dist/clientTools/certificate_types.d.ts +15 -0
- package/dist/clientTools/certificate_types.js +19 -0
- package/dist/clientTools/certificate_types.js.map +1 -0
- package/dist/clientTools/get_certificate_key_type.d.ts +6 -0
- package/dist/clientTools/get_certificate_key_type.js +55 -0
- package/dist/clientTools/get_certificate_key_type.js.map +1 -0
- package/dist/clientTools/index.d.ts +2 -1
- package/dist/clientTools/index.js +2 -17
- package/dist/clientTools/index.js.map +1 -1
- package/dist/clientTools/push_certificate_management_client.d.ts +10 -10
- package/dist/clientTools/push_certificate_management_client.js +85 -89
- package/dist/clientTools/push_certificate_management_client.js.map +1 -1
- package/dist/index.d.ts +9 -7
- package/dist/index.js +9 -23
- package/dist/index.js.map +1 -1
- package/dist/push_certificate_manager.d.ts +6 -5
- package/dist/push_certificate_manager.js +1 -2
- package/dist/server/certificate_validation.d.ts +15 -0
- package/dist/server/certificate_validation.js +76 -0
- package/dist/server/certificate_validation.js.map +1 -0
- package/dist/server/file_transaction_manager.d.ts +30 -0
- package/dist/server/file_transaction_manager.js +223 -0
- package/dist/server/file_transaction_manager.js.map +1 -0
- package/dist/server/install_certificate_file_watcher.d.ts +1 -1
- package/dist/server/install_certificate_file_watcher.js +8 -14
- package/dist/server/install_certificate_file_watcher.js.map +1 -1
- package/dist/server/install_push_certitifate_management.d.ts +6 -6
- package/dist/server/install_push_certitifate_management.js +59 -81
- package/dist/server/install_push_certitifate_management.js.map +1 -1
- package/dist/server/promote_trust_list.d.ts +1 -1
- package/dist/server/promote_trust_list.js +348 -82
- package/dist/server/promote_trust_list.js.map +1 -1
- package/dist/server/push_certificate_manager/apply_changes.d.ts +4 -0
- package/dist/server/push_certificate_manager/apply_changes.js +65 -0
- package/dist/server/push_certificate_manager/apply_changes.js.map +1 -0
- package/dist/server/push_certificate_manager/create_signing_request.d.ts +5 -0
- package/dist/server/push_certificate_manager/create_signing_request.js +108 -0
- package/dist/server/push_certificate_manager/create_signing_request.js.map +1 -0
- package/dist/server/push_certificate_manager/get_rejected_list.d.ts +3 -0
- package/dist/server/push_certificate_manager/get_rejected_list.js +46 -0
- package/dist/server/push_certificate_manager/get_rejected_list.js.map +1 -0
- package/dist/server/push_certificate_manager/internal_context.d.ts +35 -0
- package/dist/server/push_certificate_manager/internal_context.js +45 -0
- package/dist/server/push_certificate_manager/internal_context.js.map +1 -0
- package/dist/server/push_certificate_manager/subject_to_string.d.ts +3 -0
- package/dist/server/push_certificate_manager/subject_to_string.js +27 -0
- package/dist/server/push_certificate_manager/subject_to_string.js.map +1 -0
- package/dist/server/push_certificate_manager/update_certificate.d.ts +5 -0
- package/dist/server/push_certificate_manager/update_certificate.js +134 -0
- package/dist/server/push_certificate_manager/update_certificate.js.map +1 -0
- package/dist/server/push_certificate_manager/util.d.ts +29 -0
- package/dist/server/push_certificate_manager/util.js +117 -0
- package/dist/server/push_certificate_manager/util.js.map +1 -0
- package/dist/server/push_certificate_manager_helpers.d.ts +5 -2
- package/dist/server/push_certificate_manager_helpers.js +110 -113
- package/dist/server/push_certificate_manager_helpers.js.map +1 -1
- package/dist/server/push_certificate_manager_server_impl.d.ts +37 -30
- package/dist/server/push_certificate_manager_server_impl.js +58 -438
- package/dist/server/push_certificate_manager_server_impl.js.map +1 -1
- package/dist/server/roles_and_permissions.d.ts +1 -1
- package/dist/server/roles_and_permissions.js +24 -27
- package/dist/server/roles_and_permissions.js.map +1 -1
- package/dist/server/tools.d.ts +1 -1
- package/dist/server/tools.js +7 -13
- package/dist/server/tools.js.map +1 -1
- package/dist/server/trust_list_server.d.ts +2 -2
- package/dist/server/trust_list_server.js +40 -29
- package/dist/server/trust_list_server.js.map +1 -1
- package/dist/standard_certificate_types.js +6 -9
- package/dist/standard_certificate_types.js.map +1 -1
- package/dist/trust_list.d.ts +2 -2
- package/dist/trust_list.js +1 -2
- package/dist/trust_list_impl.js +1 -2
- package/dist/trust_list_impl.js.map +1 -1
- package/package.json +30 -30
- package/source/clientTools/certificate_types.ts +21 -0
- package/source/clientTools/get_certificate_key_type.ts +73 -0
- package/source/clientTools/index.ts +2 -1
- package/source/clientTools/push_certificate_management_client.ts +49 -44
- package/source/index.ts +9 -7
- package/source/push_certificate_manager.ts +17 -18
- package/source/server/certificate_validation.ts +103 -0
- package/source/server/file_transaction_manager.ts +253 -0
- package/source/server/install_certificate_file_watcher.ts +15 -11
- package/source/server/install_push_certitifate_management.ts +52 -68
- package/source/server/promote_trust_list.ts +392 -73
- package/source/server/push_certificate_manager/apply_changes.ts +73 -0
- package/source/server/push_certificate_manager/create_signing_request.ts +137 -0
- package/source/server/push_certificate_manager/get_rejected_list.ts +63 -0
- package/source/server/push_certificate_manager/internal_context.ts +63 -0
- package/source/server/push_certificate_manager/subject_to_string.ts +25 -0
- package/source/server/push_certificate_manager/update_certificate.ts +203 -0
- package/source/server/push_certificate_manager/util.ts +145 -0
- package/source/server/push_certificate_manager_helpers.ts +62 -52
- package/source/server/push_certificate_manager_server_impl.ts +133 -552
- package/source/server/roles_and_permissions.ts +7 -8
- package/source/server/tools.ts +2 -5
- package/source/server/trust_list_server.ts +24 -9
- package/source/standard_certificate_types.ts +2 -3
- package/source/trust_list.ts +26 -33
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module node-opcua-server-configuration-server
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import crypto from "node:crypto";
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import os from "node:os";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
|
|
10
|
+
|
|
11
|
+
const debugLog = make_debugLog("ServerConfiguration");
|
|
12
|
+
const errorLog = make_errorLog("ServerConfiguration");
|
|
13
|
+
const warningLog = make_warningLog("ServerConfiguration");
|
|
14
|
+
|
|
15
|
+
type Functor = () => Promise<void>;
|
|
16
|
+
|
|
17
|
+
async function _copyFile(source: string, dest: string): Promise<void> {
|
|
18
|
+
try {
|
|
19
|
+
debugLog("copying file \n source ", source, "\n =>\n dest ", dest);
|
|
20
|
+
const sourceExist = fs.existsSync(source);
|
|
21
|
+
if (sourceExist) {
|
|
22
|
+
await fs.promises.copyFile(source, dest);
|
|
23
|
+
}
|
|
24
|
+
} catch (err) {
|
|
25
|
+
errorLog(err);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function _deleteFile(file: string): Promise<void> {
|
|
30
|
+
try {
|
|
31
|
+
const exists = fs.existsSync(file);
|
|
32
|
+
if (exists) {
|
|
33
|
+
debugLog("deleting file ", file);
|
|
34
|
+
await fs.promises.unlink(file);
|
|
35
|
+
}
|
|
36
|
+
} catch (err) {
|
|
37
|
+
errorLog(err);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function _moveFile(source: string, dest: string): Promise<void> {
|
|
42
|
+
debugLog("moving file file \n source ", source, "\n =>\n dest ", dest);
|
|
43
|
+
try {
|
|
44
|
+
await _copyFile(source, dest);
|
|
45
|
+
await _deleteFile(source);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
errorLog(err);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function _moveFileWithBackup(source: string, dest: string, backupPath: string): Promise<void> {
|
|
52
|
+
// let make a copy of the destination file
|
|
53
|
+
debugLog("moveFileWithBackup file \n source ", source, "\n =>\n dest ", dest);
|
|
54
|
+
await _copyFile(dest, backupPath);
|
|
55
|
+
await _moveFile(source, dest);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export class FileTransactionManager {
|
|
59
|
+
readonly #pendingFileOps: Functor[] = [];
|
|
60
|
+
readonly #cleanupTasks: Functor[] = [];
|
|
61
|
+
readonly #backupFiles: Map<string, string> = new Map();
|
|
62
|
+
#tmpdir?: string;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Gets or initializes the underlying temporary directory for the transaction.
|
|
66
|
+
*/
|
|
67
|
+
public async getTmpDir(): Promise<string> {
|
|
68
|
+
if (!this.#tmpdir) {
|
|
69
|
+
const tempBase = path.join(os.tmpdir(), "node-opcua-tx-");
|
|
70
|
+
this.#tmpdir = await fs.promises.mkdtemp(tempBase);
|
|
71
|
+
}
|
|
72
|
+
return this.#tmpdir;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Stages a file for writing during the transaction.
|
|
77
|
+
* Writes the content to a temporary location and registers
|
|
78
|
+
* a move operation to atomically place it at destinationPath upon applyFileOps().
|
|
79
|
+
*/
|
|
80
|
+
public async stageFile(destinationPath: string, content: Buffer | string, encoding?: BufferEncoding): Promise<void> {
|
|
81
|
+
// ensure tmpdir exists
|
|
82
|
+
const tmpDir = await this.getTmpDir();
|
|
83
|
+
|
|
84
|
+
const uniqueFileName = `${crypto.randomBytes(16).toString("hex")}.tmp`;
|
|
85
|
+
const tempFilePath = path.join(tmpDir, uniqueFileName);
|
|
86
|
+
|
|
87
|
+
if (encoding) {
|
|
88
|
+
await fs.promises.writeFile(tempFilePath, content as string, encoding);
|
|
89
|
+
} else {
|
|
90
|
+
await fs.promises.writeFile(tempFilePath, content);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
this.addFileOp(() => this.#moveFileWithBackupTracked(tempFilePath, destinationPath));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public addFileOp(functor: Functor): void {
|
|
97
|
+
this.#pendingFileOps.push(functor);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
public addCleanupTask(functor: Functor): void {
|
|
101
|
+
this.#cleanupTasks.push(functor);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public get pendingTasksCount(): number {
|
|
105
|
+
return this.#pendingFileOps.length;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Abort the current transaction by clearing pending file operations
|
|
110
|
+
* and deleting the temporary staging folder.
|
|
111
|
+
*/
|
|
112
|
+
public async abortTransaction(): Promise<void> {
|
|
113
|
+
this.#pendingFileOps.length = 0;
|
|
114
|
+
await this.#executeCleanupTasks();
|
|
115
|
+
await this.#cleanupTempFolder();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Move file with backup and track the backup for potential rollback.
|
|
120
|
+
* This method creates a backup of the destination file and tracks it
|
|
121
|
+
* so it can be restored if the transaction fails.
|
|
122
|
+
*/
|
|
123
|
+
async #moveFileWithBackupTracked(source: string, dest: string): Promise<void> {
|
|
124
|
+
const tmpDir = await this.getTmpDir();
|
|
125
|
+
const uniqueFileName = `${crypto.randomBytes(16).toString("hex")}_backup.tmp`;
|
|
126
|
+
const backupPath = path.join(tmpDir, uniqueFileName);
|
|
127
|
+
|
|
128
|
+
// Track the backup before creating it
|
|
129
|
+
this.#backupFiles.set(dest, backupPath);
|
|
130
|
+
|
|
131
|
+
// Perform the actual move with backup
|
|
132
|
+
await _moveFileWithBackup(source, dest, backupPath);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Commit the transaction by executing all pending file operations.
|
|
137
|
+
*/
|
|
138
|
+
public async applyFileOps(): Promise<void> {
|
|
139
|
+
debugLog("start applyFileOps");
|
|
140
|
+
const fileOperation = this.#pendingFileOps.splice(0);
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
while (fileOperation.length) {
|
|
144
|
+
const op = fileOperation.shift();
|
|
145
|
+
await op?.();
|
|
146
|
+
}
|
|
147
|
+
debugLog("end applyFileOps");
|
|
148
|
+
|
|
149
|
+
// Transaction successful - clean up backup files
|
|
150
|
+
await this.#cleanupBackupFiles();
|
|
151
|
+
await this.#executeCleanupTasks();
|
|
152
|
+
await this.#cleanupTempFolder();
|
|
153
|
+
} catch (err) {
|
|
154
|
+
errorLog("Error during applyFileOps:", (err as Error).message);
|
|
155
|
+
errorLog("Rolling back transaction to restore previous certificate state");
|
|
156
|
+
|
|
157
|
+
// Rollback: restore all backup files to their original locations
|
|
158
|
+
try {
|
|
159
|
+
await this.#rollbackTransaction();
|
|
160
|
+
debugLog("Transaction rollback successful");
|
|
161
|
+
} catch (rollbackErr) {
|
|
162
|
+
errorLog("Critical: Rollback failed:", (rollbackErr as Error).message);
|
|
163
|
+
errorLog("Certificate state may be inconsistent - manual intervention required");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Clear backup tracking after rollback
|
|
167
|
+
this.#backupFiles.clear();
|
|
168
|
+
await this.#executeCleanupTasks();
|
|
169
|
+
await this.#cleanupTempFolder();
|
|
170
|
+
|
|
171
|
+
throw err;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Rollback the transaction by restoring all backup files.
|
|
177
|
+
* This restores files from their *_old backups to recover the previous state.
|
|
178
|
+
*/
|
|
179
|
+
async #rollbackTransaction(): Promise<void> {
|
|
180
|
+
debugLog("Rolling back transaction, restoring", this.#backupFiles.size, "backup files");
|
|
181
|
+
|
|
182
|
+
const rollbackPromises: Promise<void>[] = [];
|
|
183
|
+
|
|
184
|
+
for (const [dest, backupPath] of this.#backupFiles.entries()) {
|
|
185
|
+
rollbackPromises.push(
|
|
186
|
+
(async () => {
|
|
187
|
+
try {
|
|
188
|
+
// Check if backup exists before trying to restore
|
|
189
|
+
if (fs.existsSync(backupPath)) {
|
|
190
|
+
debugLog("Restoring backup:", backupPath, "to", dest);
|
|
191
|
+
await _copyFile(backupPath, dest);
|
|
192
|
+
// Delete backup immediately after restoration
|
|
193
|
+
await _deleteFile(backupPath);
|
|
194
|
+
}
|
|
195
|
+
} catch (err) {
|
|
196
|
+
errorLog("Error restoring backup file", backupPath, "to", dest, ":", (err as Error).message);
|
|
197
|
+
}
|
|
198
|
+
})()
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
await Promise.all(rollbackPromises);
|
|
203
|
+
debugLog("Transaction rollback completed");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Clean up backup files after successful transaction.
|
|
208
|
+
* Removes all *_old backup files that were created during the transaction.
|
|
209
|
+
*/
|
|
210
|
+
async #cleanupBackupFiles(): Promise<void> {
|
|
211
|
+
debugLog("Cleaning up", this.#backupFiles.size, "backup files");
|
|
212
|
+
|
|
213
|
+
const cleanupPromises: Promise<void>[] = [];
|
|
214
|
+
|
|
215
|
+
for (const backupPath of this.#backupFiles.values()) {
|
|
216
|
+
cleanupPromises.push(
|
|
217
|
+
_deleteFile(backupPath).catch((err) => {
|
|
218
|
+
warningLog("Failed to delete backup file", backupPath, ":", err);
|
|
219
|
+
})
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
await Promise.all(cleanupPromises);
|
|
224
|
+
this.#backupFiles.clear();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Clean up the temporary transaction folder.
|
|
229
|
+
*/
|
|
230
|
+
async #cleanupTempFolder(): Promise<void> {
|
|
231
|
+
if (this.#tmpdir) {
|
|
232
|
+
try {
|
|
233
|
+
await fs.promises.rm(this.#tmpdir, { recursive: true, force: true });
|
|
234
|
+
} catch (err) {
|
|
235
|
+
warningLog("Failed to delete temporary transaction folder", this.#tmpdir, ":", err);
|
|
236
|
+
} finally {
|
|
237
|
+
this.#tmpdir = undefined;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async #executeCleanupTasks(): Promise<void> {
|
|
243
|
+
debugLog("Executing cleanup tasks");
|
|
244
|
+
const tasks = this.#cleanupTasks.splice(0);
|
|
245
|
+
for (const task of tasks) {
|
|
246
|
+
try {
|
|
247
|
+
await task();
|
|
248
|
+
} catch (err) {
|
|
249
|
+
errorLog("Error during cleanup task:", (err as Error).message);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import { UAObject } from "node-opcua-address-space-base";
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import type { UAObject } from "node-opcua-address-space-base";
|
|
4
4
|
import { make_debugLog } from "node-opcua-debug";
|
|
5
5
|
|
|
6
6
|
const debugLog = make_debugLog("ServerConfiguration");
|
|
@@ -10,15 +10,19 @@ export interface ChangeDetector {
|
|
|
10
10
|
}
|
|
11
11
|
export function installCertificateFileWatcher(node: UAObject, certificateFile: string): ChangeDetector {
|
|
12
12
|
const fileToWatch = path.basename(certificateFile);
|
|
13
|
-
const fsWatcher = fs.watch(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
const fsWatcher = fs.watch(
|
|
14
|
+
path.dirname(certificateFile),
|
|
15
|
+
{ persistent: false },
|
|
16
|
+
(_eventType: "rename" | "change", filename) => {
|
|
17
|
+
/** */
|
|
18
|
+
if (filename === fileToWatch) {
|
|
19
|
+
debugLog("filename changed = ", filename, fileToWatch);
|
|
20
|
+
node.emit("certificateChange");
|
|
21
|
+
}
|
|
18
22
|
}
|
|
19
|
-
|
|
20
|
-
const addressSpace = node.addressSpace
|
|
21
|
-
addressSpace
|
|
23
|
+
);
|
|
24
|
+
const addressSpace = node.addressSpace;
|
|
25
|
+
addressSpace?.registerShutdownTask(() => {
|
|
22
26
|
fsWatcher.close();
|
|
23
27
|
});
|
|
24
28
|
return node as unknown as ChangeDetector;
|
|
@@ -1,31 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module node-opcua-server-configuration-server
|
|
3
3
|
*/
|
|
4
|
-
import fs from "fs";
|
|
5
|
-
import
|
|
6
|
-
import path from "path";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
7
6
|
|
|
8
|
-
import { types } from "util";
|
|
9
7
|
import chalk from "chalk";
|
|
10
8
|
|
|
11
|
-
import {
|
|
9
|
+
import type { AddressSpace, UAServerConfiguration } from "node-opcua-address-space";
|
|
12
10
|
import { assert } from "node-opcua-assert";
|
|
13
|
-
import { OPCUACertificateManager } from "node-opcua-certificate-manager";
|
|
14
|
-
import {
|
|
15
|
-
Certificate,
|
|
16
|
-
convertPEMtoDER,
|
|
17
|
-
PrivateKey,
|
|
18
|
-
split_der
|
|
19
|
-
} from "node-opcua-crypto/web";
|
|
11
|
+
import type { OPCUACertificateManager } from "node-opcua-certificate-manager";
|
|
12
|
+
import type { ICertificateKeyPairProviderPriv } from "node-opcua-common";
|
|
20
13
|
import { readPrivateKey } from "node-opcua-crypto";
|
|
14
|
+
import { type Certificate, convertPEMtoDER, type PrivateKey, split_der } from "node-opcua-crypto/web";
|
|
21
15
|
import { checkDebugFlag, make_debugLog, make_errorLog } from "node-opcua-debug";
|
|
22
|
-
import { getFullyQualifiedDomainName } from "node-opcua-hostname";
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import { ApplicationDescriptionOptions } from "node-opcua-types";
|
|
16
|
+
import { getFullyQualifiedDomainName, getIpAddresses } from "node-opcua-hostname";
|
|
17
|
+
import type { OPCUAServer, OPCUAServerEndPoint } from "node-opcua-server";
|
|
18
|
+
import type { ApplicationDescriptionOptions } from "node-opcua-types";
|
|
26
19
|
|
|
27
|
-
import { installPushCertificateManagement } from "./push_certificate_manager_helpers";
|
|
28
|
-
import { ActionQueue, PushCertificateManagerServerImpl } from "./push_certificate_manager_server_impl";
|
|
20
|
+
import { installPushCertificateManagement } from "./push_certificate_manager_helpers.js";
|
|
21
|
+
import type { ActionQueue, PushCertificateManagerServerImpl } from "./push_certificate_manager_server_impl.js";
|
|
29
22
|
|
|
30
23
|
// node 14 onward : import { readFile } from "fs/promises";
|
|
31
24
|
const { readFile } = fs.promises;
|
|
@@ -33,7 +26,6 @@ const { readFile } = fs.promises;
|
|
|
33
26
|
const debugLog = make_debugLog("ServerConfiguration");
|
|
34
27
|
const errorLog = make_errorLog("ServerConfiguration");
|
|
35
28
|
const doDebug = checkDebugFlag("ServerConfiguration");
|
|
36
|
-
doDebug;
|
|
37
29
|
|
|
38
30
|
export interface OPCUAServerPartial extends ICertificateKeyPairProviderPriv {
|
|
39
31
|
serverInfo?: ApplicationDescriptionOptions;
|
|
@@ -51,7 +43,7 @@ function getCertificate(this: OPCUAServerPartial): Certificate {
|
|
|
51
43
|
const certificateChain = getCertificateChain.call(this);
|
|
52
44
|
this.$$certificate = split_der(certificateChain)[0];
|
|
53
45
|
}
|
|
54
|
-
return this.$$certificate
|
|
46
|
+
return this.$$certificate;
|
|
55
47
|
}
|
|
56
48
|
|
|
57
49
|
function getCertificateChain(this: OPCUAServerPartial): Certificate {
|
|
@@ -62,41 +54,29 @@ function getCertificateChain(this: OPCUAServerPartial): Certificate {
|
|
|
62
54
|
}
|
|
63
55
|
|
|
64
56
|
function getPrivateKey(this: OPCUAServerPartial): PrivateKey {
|
|
65
|
-
//
|
|
57
|
+
// c8 ignore next
|
|
66
58
|
if (!this.$$privateKey) {
|
|
67
59
|
throw new Error("internal Error. cannot find $$privateKey");
|
|
68
60
|
}
|
|
69
61
|
return this.$$privateKey;
|
|
70
62
|
}
|
|
71
63
|
|
|
72
|
-
|
|
73
|
-
const ipAddresses: string[] = [];
|
|
74
|
-
const netInterfaces = os.networkInterfaces();
|
|
75
|
-
for (const interfaceName of Object.keys(netInterfaces)) {
|
|
76
|
-
if (!netInterfaces[interfaceName]) {
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
for (const interFace of netInterfaces[interfaceName]!) {
|
|
80
|
-
if ("IPv4" !== interFace.family || interFace.internal !== false) {
|
|
81
|
-
// skip over internal (i.e. 127.0.0.1) and non-ipv4 addresses
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
ipAddresses.push(interFace.address);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return ipAddresses;
|
|
88
|
-
}
|
|
64
|
+
|
|
89
65
|
|
|
90
66
|
/**
|
|
91
67
|
*
|
|
92
68
|
*/
|
|
93
69
|
async function install(this: OPCUAServerPartial): Promise<void> {
|
|
94
|
-
debugLog("install push certificate management", this.serverCertificateManager.rootDir);
|
|
70
|
+
doDebug && debugLog("install push certificate management", this.serverCertificateManager.rootDir);
|
|
95
71
|
|
|
96
|
-
(this
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
);
|
|
72
|
+
Object.defineProperty(this, "privateKeyFile", {
|
|
73
|
+
get: () => this.serverCertificateManager.privateKey,
|
|
74
|
+
configurable: true
|
|
75
|
+
});
|
|
76
|
+
Object.defineProperty(this, "certificateFile", {
|
|
77
|
+
get: () => path.join(this.serverCertificateManager.rootDir, "own/certs/certificate.pem"),
|
|
78
|
+
configurable: true
|
|
79
|
+
});
|
|
100
80
|
|
|
101
81
|
if (!this.$$privateKey) {
|
|
102
82
|
this.$$privateKey = readPrivateKey(this.serverCertificateManager.privateKey);
|
|
@@ -112,7 +92,7 @@ async function install(this: OPCUAServerPartial): Promise<void> {
|
|
|
112
92
|
const fqdn = await getFullyQualifiedDomainName();
|
|
113
93
|
const ipAddresses = await getIpAddresses();
|
|
114
94
|
|
|
115
|
-
const applicationUri = (this.serverInfo ? this.serverInfo
|
|
95
|
+
const applicationUri = (this.serverInfo ? this.serverInfo.applicationUri : null) || "uri:MISSING";
|
|
116
96
|
|
|
117
97
|
const options = {
|
|
118
98
|
applicationUri,
|
|
@@ -120,7 +100,7 @@ async function install(this: OPCUAServerPartial): Promise<void> {
|
|
|
120
100
|
dns: [fqdn],
|
|
121
101
|
ip: ipAddresses,
|
|
122
102
|
|
|
123
|
-
subject:
|
|
103
|
+
subject: `/CN=${applicationUri};/L=Paris`,
|
|
124
104
|
|
|
125
105
|
startDate: new Date(),
|
|
126
106
|
|
|
@@ -130,7 +110,7 @@ async function install(this: OPCUAServerPartial): Promise<void> {
|
|
|
130
110
|
outputFile: certificateFile
|
|
131
111
|
};
|
|
132
112
|
|
|
133
|
-
debugLog("creating self signed certificate", options);
|
|
113
|
+
doDebug && debugLog("creating self signed certificate", options);
|
|
134
114
|
await this.serverCertificateManager.createSelfSignedCertificate(options);
|
|
135
115
|
}
|
|
136
116
|
const certificatePEM = await readFile(certificateFile, "utf8");
|
|
@@ -154,9 +134,9 @@ function getPrivateKeyEP(this: OPCUAServerEndPoint): PrivateKey {
|
|
|
154
134
|
}
|
|
155
135
|
|
|
156
136
|
async function onCertificateAboutToChange(server: OPCUAServer) {
|
|
157
|
-
debugLog(chalk.yellow(" onCertificateAboutToChange => Suspending End points"));
|
|
137
|
+
doDebug && debugLog(chalk.yellow(" onCertificateAboutToChange => Suspending End points"));
|
|
158
138
|
await server.suspendEndPoints();
|
|
159
|
-
debugLog(chalk.yellow(" onCertificateAboutToChange => End points suspended"));
|
|
139
|
+
doDebug && debugLog(chalk.yellow(" onCertificateAboutToChange => End points suspended"));
|
|
160
140
|
}
|
|
161
141
|
|
|
162
142
|
/**
|
|
@@ -169,9 +149,9 @@ async function onCertificateAboutToChange(server: OPCUAServer) {
|
|
|
169
149
|
* @param server
|
|
170
150
|
*/
|
|
171
151
|
async function onCertificateChange(server: OPCUAServer) {
|
|
172
|
-
debugLog("on CertificateChanged");
|
|
152
|
+
doDebug && debugLog("on CertificateChanged");
|
|
173
153
|
|
|
174
|
-
const _server = server as
|
|
154
|
+
const _server = server as unknown as OPCUAServerPartial;
|
|
175
155
|
|
|
176
156
|
_server.$$privateKey = readPrivateKey(server.serverCertificateManager.privateKey);
|
|
177
157
|
const certificateFile = path.join(server.serverCertificateManager.rootDir, "own/certs/certificate.pem");
|
|
@@ -188,19 +168,17 @@ async function onCertificateChange(server: OPCUAServer) {
|
|
|
188
168
|
|
|
189
169
|
setTimeout(async () => {
|
|
190
170
|
try {
|
|
191
|
-
debugLog(chalk.yellow(" onCertificateChange => shutting down channels"));
|
|
171
|
+
doDebug && debugLog(chalk.yellow(" onCertificateChange => shutting down channels"));
|
|
192
172
|
await server.shutdownChannels();
|
|
193
|
-
debugLog(chalk.yellow(" onCertificateChange => channels shut down"));
|
|
173
|
+
doDebug && debugLog(chalk.yellow(" onCertificateChange => channels shut down"));
|
|
194
174
|
|
|
195
|
-
debugLog(chalk.yellow(" onCertificateChange => resuming end points"));
|
|
175
|
+
doDebug && debugLog(chalk.yellow(" onCertificateChange => resuming end points"));
|
|
196
176
|
await server.resumeEndPoints();
|
|
197
|
-
debugLog(chalk.yellow(" onCertificateChange => end points resumed"));
|
|
177
|
+
doDebug && debugLog(chalk.yellow(" onCertificateChange => end points resumed"));
|
|
198
178
|
|
|
199
179
|
debugLog(chalk.yellow("channels have been closed -> client should reconnect "));
|
|
200
180
|
} catch (err) {
|
|
201
|
-
|
|
202
|
-
errorLog("Error in CertificateChanged handler ", err.message);
|
|
203
|
-
}
|
|
181
|
+
errorLog("Error in CertificateChanged handler ", (err as Error).message);
|
|
204
182
|
debugLog("err = ", err);
|
|
205
183
|
}
|
|
206
184
|
}, 2000);
|
|
@@ -209,6 +187,12 @@ async function onCertificateChange(server: OPCUAServer) {
|
|
|
209
187
|
interface UAServerConfigurationEx extends UAServerConfiguration {
|
|
210
188
|
$pushCertificateManager: PushCertificateManagerServerImpl;
|
|
211
189
|
}
|
|
190
|
+
|
|
191
|
+
type OPCUAServerEndPointEx = typeof OPCUAServerEndPoint & {
|
|
192
|
+
_certificateChain: Buffer | null;
|
|
193
|
+
_privateKey: PrivateKey | null;
|
|
194
|
+
};
|
|
195
|
+
|
|
212
196
|
export async function installPushCertificateManagementOnServer(server: OPCUAServer): Promise<void> {
|
|
213
197
|
if (!server.engine || !server.engine.addressSpace) {
|
|
214
198
|
throw new Error(
|
|
@@ -216,14 +200,14 @@ export async function installPushCertificateManagementOnServer(server: OPCUAServ
|
|
|
216
200
|
"you need to call installPushCertificateManagementOnServer after server has been initialized"
|
|
217
201
|
);
|
|
218
202
|
}
|
|
219
|
-
await install.call(server as
|
|
203
|
+
await install.call(server as unknown as OPCUAServerPartial);
|
|
220
204
|
|
|
221
205
|
server.getCertificate = getCertificate;
|
|
222
206
|
server.getCertificateChain = getCertificateChain;
|
|
223
207
|
server.getPrivateKey = getPrivateKey;
|
|
224
208
|
|
|
225
209
|
for (const endpoint of server.endpoints) {
|
|
226
|
-
const endpointPriv = endpoint as
|
|
210
|
+
const endpointPriv: OPCUAServerEndPointEx = endpoint as unknown as OPCUAServerEndPointEx;
|
|
227
211
|
endpointPriv._certificateChain = null;
|
|
228
212
|
endpointPriv._privateKey = null;
|
|
229
213
|
|
|
@@ -231,9 +215,9 @@ export async function installPushCertificateManagementOnServer(server: OPCUAServ
|
|
|
231
215
|
endpoint.getPrivateKey = getPrivateKeyEP;
|
|
232
216
|
|
|
233
217
|
for (const e of endpoint.endpointDescriptions()) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
218
|
+
Object.defineProperty(e, "serverCertificate", {
|
|
219
|
+
get: () => endpoint.getCertificate(),
|
|
220
|
+
configurable: true
|
|
237
221
|
});
|
|
238
222
|
}
|
|
239
223
|
}
|
|
@@ -242,25 +226,25 @@ export async function installPushCertificateManagementOnServer(server: OPCUAServ
|
|
|
242
226
|
applicationGroup: server.serverCertificateManager,
|
|
243
227
|
userTokenGroup: server.userCertificateManager,
|
|
244
228
|
|
|
245
|
-
applicationUri: server.serverInfo.applicationUri
|
|
229
|
+
applicationUri: server.serverInfo.applicationUri || "InvalidURI"
|
|
246
230
|
});
|
|
247
231
|
|
|
248
|
-
const serverConfiguration = server.engine.addressSpace.rootFolder.objects.server.getChildByName("ServerConfiguration")
|
|
232
|
+
const serverConfiguration = server.engine.addressSpace.rootFolder.objects.server.getChildByName("ServerConfiguration");
|
|
249
233
|
const serverConfigurationPriv = serverConfiguration as UAServerConfigurationEx;
|
|
250
234
|
assert(serverConfigurationPriv.$pushCertificateManager);
|
|
251
235
|
|
|
252
236
|
serverConfigurationPriv.$pushCertificateManager.on("CertificateAboutToChange", (actionQueue: ActionQueue) => {
|
|
253
237
|
actionQueue.push(async (): Promise<void> => {
|
|
254
|
-
debugLog("CertificateAboutToChange Event received");
|
|
238
|
+
doDebug && debugLog("CertificateAboutToChange Event received");
|
|
255
239
|
await onCertificateAboutToChange(server);
|
|
256
|
-
debugLog("CertificateAboutToChange Event processed");
|
|
240
|
+
doDebug && debugLog("CertificateAboutToChange Event processed");
|
|
257
241
|
});
|
|
258
242
|
});
|
|
259
243
|
serverConfigurationPriv.$pushCertificateManager.on("CertificateChanged", (actionQueue: ActionQueue) => {
|
|
260
244
|
actionQueue.push(async (): Promise<void> => {
|
|
261
|
-
debugLog("CertificateChanged Event received");
|
|
245
|
+
doDebug && debugLog("CertificateChanged Event received");
|
|
262
246
|
await onCertificateChange(server);
|
|
263
|
-
debugLog("CertificateChanged Event processed");
|
|
247
|
+
doDebug && debugLog("CertificateChanged Event processed");
|
|
264
248
|
});
|
|
265
249
|
});
|
|
266
250
|
}
|