v0.5.174: timeout IAM 5с, rate-limiters на login/CRUD/export
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ipwhitelist",
|
"name": "ipwhitelist",
|
||||||
"version": "0.5.173",
|
"version": "0.5.174",
|
||||||
"description": "IP WhiteList microservice for cloud provider",
|
"description": "IP WhiteList microservice for cloud provider",
|
||||||
"main": "server.js",
|
"main": "server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ async function start() {
|
|||||||
if (auth.jwksHandler) app.get('/.well-known/jwks.json', auth.jwksHandler);
|
if (auth.jwksHandler) app.get('/.well-known/jwks.json', auth.jwksHandler);
|
||||||
|
|
||||||
// ── OIDC-роуты (Keycloak) ───────────────────────────────────────────────
|
// ── OIDC-роуты (Keycloak) ───────────────────────────────────────────────
|
||||||
|
app.use('/login', authLimiter); // защита от брутфорса
|
||||||
const { createRouter: createOidcRouter } = require('./src/routes/oidc');
|
const { createRouter: createOidcRouter } = require('./src/routes/oidc');
|
||||||
app.use('/', createOidcRouter({ auth }));
|
app.use('/', createOidcRouter({ auth }));
|
||||||
|
|
||||||
|
|||||||
+8
-5
@@ -31,6 +31,7 @@ const { createUserRouter } = require('./src/user');
|
|||||||
const { createAdminRouter } = require('./src/admin');
|
const { createAdminRouter } = require('./src/admin');
|
||||||
const { pool } = require('./src/db');
|
const { pool } = require('./src/db');
|
||||||
const { ensureSchema } = require('./src/db/schema');
|
const { ensureSchema } = require('./src/db/schema');
|
||||||
|
const { mutationLimiter, exportLimiter } = require('../../src/middleware/rateLimit');
|
||||||
|
|
||||||
function createV2Router({ generateCsrfToken, doubleCsrfProtection } = {}) {
|
function createV2Router({ generateCsrfToken, doubleCsrfProtection } = {}) {
|
||||||
// Автосоздание таблиц v2_companies, v2_entries, v2_audit при старте
|
// Автосоздание таблиц v2_companies, v2_entries, v2_audit при старте
|
||||||
@@ -102,10 +103,10 @@ function createV2Router({ generateCsrfToken, doubleCsrfProtection } = {}) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// ── /v2/app — пользовательский CRUD (только с сессией) ──────────────
|
// ── /v2/app — пользовательский CRUD (только с сессией) ──────────────
|
||||||
router.use('/app', enhanceImpersonation, resolveContext, doubleCsrfProtection || ((req,res,next)=>next()), createUserRouter({ generateCsrfToken }));
|
router.use('/app', mutationLimiter, enhanceImpersonation, resolveContext, doubleCsrfProtection || ((req,res,next)=>next()), createUserRouter({ generateCsrfToken }));
|
||||||
|
|
||||||
// ── /v2/admin — админка: лимиты + аудит ──────────────────────────
|
// ── /v2/admin — админка: лимиты + аудит ──────────────────────────
|
||||||
router.use('/admin', enhanceImpersonation, resolveContext, doubleCsrfProtection || ((req,res,next)=>next()), createAdminRouter({ generateCsrfToken }));
|
router.use('/admin', mutationLimiter, enhanceImpersonation, resolveContext, doubleCsrfProtection || ((req,res,next)=>next()), createAdminRouter({ generateCsrfToken }));
|
||||||
|
|
||||||
// ── GET /v2/ — редирект на /v2/app ──────────────────────────────────
|
// ── GET /v2/ — редирект на /v2/app ──────────────────────────────────
|
||||||
router.get('/', (req, res) => res.redirect('/v2/app'));
|
router.get('/', (req, res) => res.redirect('/v2/app'));
|
||||||
@@ -121,7 +122,7 @@ function createV2Router({ generateCsrfToken, doubleCsrfProtection } = {}) {
|
|||||||
|
|
||||||
// ── /v2/export — выгрузка CIDR (только для админов) ─────────────
|
// ── /v2/export — выгрузка CIDR (только для админов) ─────────────
|
||||||
const { createExportRouter } = require('./src/export');
|
const { createExportRouter } = require('./src/export');
|
||||||
router.use('/export', resolveContext, (req, res, next) => {
|
router.use('/export', exportLimiter, resolveContext, (req, res, next) => {
|
||||||
if (!req.canAdmin) return res.status(403).send('Доступ запрещён — только для администраторов');
|
if (!req.canAdmin) return res.status(403).send('Доступ запрещён — только для администраторов');
|
||||||
next();
|
next();
|
||||||
}, createExportRouter());
|
}, createExportRouter());
|
||||||
@@ -172,7 +173,7 @@ async function iamFetch(token, url) {
|
|||||||
const https = require('https');
|
const https = require('https');
|
||||||
const u = new URL(url);
|
const u = new URL(url);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
https.get({
|
const req = https.get({
|
||||||
hostname: u.hostname, port: 443, path: u.pathname,
|
hostname: u.hostname, port: 443, path: u.pathname,
|
||||||
headers: { Authorization: 'Bearer ' + token, Accept: 'application/json' },
|
headers: { Authorization: 'Bearer ' + token, Accept: 'application/json' },
|
||||||
}, res => {
|
}, res => {
|
||||||
@@ -182,7 +183,9 @@ async function iamFetch(token, url) {
|
|||||||
if (res.statusCode !== 200) return reject(new Error(res.statusCode + ': ' + data.slice(0, 200)));
|
if (res.statusCode !== 200) return reject(new Error(res.statusCode + ': ' + data.slice(0, 200)));
|
||||||
try { resolve(JSON.parse(data)); } catch (e) { reject(e); }
|
try { resolve(JSON.parse(data)); } catch (e) { reject(e); }
|
||||||
});
|
});
|
||||||
}).on('error', reject);
|
});
|
||||||
|
req.on('error', reject);
|
||||||
|
req.setTimeout(5000, () => { req.destroy(); reject(new Error('timeout')); });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
// ═══════════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
version: '0.5.173',
|
version: '0.5.174',
|
||||||
|
|
||||||
// ── IAM ──────────────────────────────────────────────────────────────────
|
// ── IAM ──────────────────────────────────────────────────────────────────
|
||||||
iamUrl: process.env.V2_IAM_URL || 'https://auth-api.ngcloud.ru/api/v1/auth/user',
|
iamUrl: process.env.V2_IAM_URL || 'https://auth-api.ngcloud.ru/api/v1/auth/user',
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ async function checkImpersonation(token, req, u) {
|
|||||||
const url = config.iamApiBase + '/auth/user';
|
const url = config.iamApiBase + '/auth/user';
|
||||||
const uObj = new URL(url);
|
const uObj = new URL(url);
|
||||||
const data = await new Promise((resolve, reject) => {
|
const data = await new Promise((resolve, reject) => {
|
||||||
https.get({
|
const req = https.get({
|
||||||
hostname: uObj.hostname, port: 443, path: uObj.pathname,
|
hostname: uObj.hostname, port: 443, path: uObj.pathname,
|
||||||
headers: { Authorization: 'Bearer ' + token, Accept: 'application/json' },
|
headers: { Authorization: 'Bearer ' + token, Accept: 'application/json' },
|
||||||
}, res => {
|
}, res => {
|
||||||
@@ -46,7 +46,9 @@ async function checkImpersonation(token, req, u) {
|
|||||||
if (res.statusCode !== 200) return reject(new Error(res.statusCode + ': ' + d.slice(0, 200)));
|
if (res.statusCode !== 200) return reject(new Error(res.statusCode + ': ' + d.slice(0, 200)));
|
||||||
try { resolve(JSON.parse(d)); } catch (e) { reject(e); }
|
try { resolve(JSON.parse(d)); } catch (e) { reject(e); }
|
||||||
});
|
});
|
||||||
}).on('error', reject);
|
});
|
||||||
|
req.on('error', reject);
|
||||||
|
req.setTimeout(5000, () => { req.destroy(); reject(new Error('timeout')); });
|
||||||
});
|
});
|
||||||
|
|
||||||
const imp = data.impersonation || {};
|
const imp = data.impersonation || {};
|
||||||
|
|||||||
Reference in New Issue
Block a user