feat(mikrotikBackupRoutes): add encryption and decryption for backup content to enhance security
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m42s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m42s
This commit is contained in:
@@ -16,9 +16,24 @@ const { sendError, sendOk } = require('../middleware/errorHandler');
|
|||||||
const { writeS3TextObject, readS3TextObject, listS3Objects } = require('../services/s3Service');
|
const { writeS3TextObject, readS3TextObject, listS3Objects } = require('../services/s3Service');
|
||||||
const { readServersFromS3 } = require('./serversRoutes');
|
const { readServersFromS3 } = require('./serversRoutes');
|
||||||
const { createRosClient, fetchExportViaFile } = require('../services/mikrotikApplyService');
|
const { createRosClient, fetchExportViaFile } = require('../services/mikrotikApplyService');
|
||||||
const { decrypt } = require('../utils/encryption');
|
const { encrypt, decrypt } = require('../utils/encryption');
|
||||||
|
|
||||||
const BACKUP_PREFIX = 'backups/mikrotik';
|
const BACKUP_PREFIX = 'backups/mikrotik';
|
||||||
|
const ENCRYPTED_PREFIX = 'ENC:';
|
||||||
|
|
||||||
|
/** Расшифровать содержимое бэкапа (поддерживает старые незашифрованные) */
|
||||||
|
function decryptBackupContent(raw) {
|
||||||
|
if (!raw || typeof raw !== 'string') return '';
|
||||||
|
if (raw.startsWith(ENCRYPTED_PREFIX)) {
|
||||||
|
try {
|
||||||
|
return decrypt(raw.slice(ENCRYPTED_PREFIX.length));
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Backup decryption failed:', e?.message);
|
||||||
|
throw new Error(`Failed to decrypt backup: ${e?.message || 'decryption error'}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
||||||
function buildBackupKey(serverId, createdAt, suffix) {
|
function buildBackupKey(serverId, createdAt, suffix) {
|
||||||
const safeId = String(serverId || 'unknown').replace(/[^a-zA-Z0-9._-]/g, '_');
|
const safeId = String(serverId || 'unknown').replace(/[^a-zA-Z0-9._-]/g, '_');
|
||||||
@@ -44,7 +59,17 @@ async function saveBackupForServer(serverId, config, { source, comment } = {}) {
|
|||||||
'',
|
'',
|
||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
|
|
||||||
const content = `${headerLines.join('\n')}\n${String(config || '').replace(/^\uFEFF/, '')}`;
|
let content = `${headerLines.join('\n')}\n${String(config || '').replace(/^\uFEFF/, '')}`;
|
||||||
|
|
||||||
|
// Шифруем содержимое на S3, если задан ENCRYPTION_KEY
|
||||||
|
if (process.env.ENCRYPTION_KEY) {
|
||||||
|
try {
|
||||||
|
content = ENCRYPTED_PREFIX + encrypt(content);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Backup encryption failed:', e?.message);
|
||||||
|
throw new Error(`Failed to encrypt backup: ${e?.message || 'encryption error'}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const meta = await writeS3TextObject(key, content, 'text/plain');
|
const meta = await writeS3TextObject(key, content, 'text/plain');
|
||||||
return { key, createdAt: now.toISOString(), s3: meta };
|
return { key, createdAt: now.toISOString(), s3: meta };
|
||||||
@@ -132,11 +157,12 @@ async function getBackup(req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const data = await readS3TextObject(key);
|
const data = await readS3TextObject(key);
|
||||||
|
const config = decryptBackupContent(data.body || '');
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
ok: true,
|
ok: true,
|
||||||
key,
|
key,
|
||||||
config: data.body || '',
|
config,
|
||||||
etag: data.etag,
|
etag: data.etag,
|
||||||
lastModified: data.lastModified,
|
lastModified: data.lastModified,
|
||||||
contentLength: data.contentLength,
|
contentLength: data.contentLength,
|
||||||
@@ -169,8 +195,8 @@ async function diffBackups(req, res) {
|
|||||||
readS3TextObject(keyB),
|
readS3TextObject(keyB),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const bodyA = a.body || '';
|
const bodyA = decryptBackupContent(a.body || '');
|
||||||
const bodyB = b.body || '';
|
const bodyB = decryptBackupContent(b.body || '');
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
ok: true,
|
ok: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user