@abtnode/router-provider 1.16.44-beta-20250527-130401-1b9ae926 → 1.16.44-beta-20250529-004636-15e80e20

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 (2) hide show
  1. package/lib/nginx/index.js +104 -3
  2. package/package.json +7 -6
@@ -26,8 +26,9 @@ const {
26
26
  GATEWAY_RATE_LIMIT_GLOBAL,
27
27
  GATEWAY_RATE_LIMIT,
28
28
  } = require('@abtnode/constant');
29
-
29
+ const { toHex } = require('@ocap/util');
30
30
  const promiseRetry = require('promise-retry');
31
+ const difference = require('lodash/difference');
31
32
 
32
33
  const logger = require('@abtnode/logger')('router:nginx:controller');
33
34
 
@@ -174,6 +175,7 @@ class NginxProvider extends BaseProvider {
174
175
  proxyPolicy,
175
176
  wafPolicy,
176
177
  cacheEnabled,
178
+ wafDisabledBlocklets = [],
177
179
  } = {}) {
178
180
  if (!Array.isArray(routingTable)) {
179
181
  throw new Error('routingTable must be an array');
@@ -228,7 +230,7 @@ class NginxProvider extends BaseProvider {
228
230
 
229
231
  this.ensureUpstreamServers(allRules);
230
232
 
231
- this._addModSecurity(conf, wafPolicy);
233
+ this._addModSecurity(conf, wafPolicy, wafDisabledBlocklets);
232
234
 
233
235
  // eslint-disable-next-line no-restricted-syntax
234
236
  for (const site of sites) {
@@ -940,7 +942,7 @@ class NginxProvider extends BaseProvider {
940
942
  return httpsServerUnit;
941
943
  }
942
944
 
943
- _addModSecurity(conf, wafPolicy = {}) {
945
+ _addModSecurity(conf, wafPolicy = {}, wafDisabledBlocklets = []) {
944
946
  if (!wafPolicy.enabled) {
945
947
  return;
946
948
  }
@@ -966,6 +968,8 @@ class NginxProvider extends BaseProvider {
966
968
  fs.writeFileSync(modSecurityConfPath, getModSecurityConf(variables));
967
969
  fs.writeFileSync(coreRuleSetConfPath, getCoreRuleSetConf(variables));
968
970
 
971
+ this.syncCustomCRSFiles({ wafDisabledBlocklets });
972
+
969
973
  conf.nginx.http._add('modsecurity', 'on');
970
974
  conf.nginx.http._add('modsecurity_transaction_id', '$request_id');
971
975
  conf.nginx.http._add('modsecurity_rules_file', modSecurityConfPath);
@@ -1076,6 +1080,103 @@ class NginxProvider extends BaseProvider {
1076
1080
  };
1077
1081
  }
1078
1082
 
1083
+ _didToNumber(did) {
1084
+ return parseInt(toHex(Buffer.from(did)).slice(-8), 16);
1085
+ }
1086
+
1087
+ _getBlockletWAFTemplateConf({ domainAliases, wafPolicy, defaultWAF = 'On', did = '' }) {
1088
+ if (!Array.isArray(domainAliases) || domainAliases.length === 0) return '';
1089
+
1090
+ const headerComment = [
1091
+ '# ------------------------------------------------------------------------',
1092
+ '# Blocklet WAF Exclusion Rules',
1093
+ '#',
1094
+ `# This file is generated for Blocklet DID: ${did || '[unknown]'} .`,
1095
+ '#',
1096
+ '# The rules below control ModSecurity WAF behavior for specific domains associated with this DID.',
1097
+ '# Each rule disables or enables the WAF engine for requests matching the Host header.',
1098
+ '#',
1099
+ '# DO NOT EDIT THIS FILE MANUALLY.',
1100
+ '# ------------------------------------------------------------------------',
1101
+ '',
1102
+ ].join('\n');
1103
+
1104
+ const baseId = 9000000 + (this._didToNumber(did) % 10000);
1105
+
1106
+ const rules = domainAliases
1107
+ .map((domain, idx) => {
1108
+ let rule;
1109
+ if (domain.includes(SLOT_FOR_IP_DNS_SITE)) {
1110
+ // 只匹配 did- 前缀
1111
+ const didPrefix = `${domain.split('-')[0]}-`;
1112
+ rule = `SecRule REQUEST_HEADERS:Host "@contains ${didPrefix}" \\`;
1113
+ } else {
1114
+ rule = `SecRule REQUEST_HEADERS:Host "@streq ${domain}" \\`;
1115
+ }
1116
+
1117
+ return `${rule}
1118
+ "id:${baseId + idx},\\
1119
+ phase:1,\\
1120
+ pass,\\
1121
+ nolog,\\
1122
+ tag:'SERVER_WAF',\\
1123
+ tag:'did:${did}',\\
1124
+ ctl:ruleEngine=${wafPolicy?.enabled ? defaultWAF : 'Off'}"`;
1125
+ })
1126
+ .join('\n\n');
1127
+ return `${headerComment}${rules}`;
1128
+ }
1129
+
1130
+ addCustomWAFConf({ did, domainAliases, wafPolicy, defaultWAF }) {
1131
+ if (!Array.isArray(domainAliases) || domainAliases.length === 0) return;
1132
+
1133
+ const customDIDConfigPath = path.join(this.includesDir, `security/crs4/rules/REQUEST-900-CUSTOM-RULES-${did}.conf`);
1134
+
1135
+ const confContent = this._getBlockletWAFTemplateConf({
1136
+ domainAliases: domainAliases.map((x) => x.value),
1137
+ defaultWAF,
1138
+ wafPolicy,
1139
+ did,
1140
+ });
1141
+ fs.writeFileSync(customDIDConfigPath, confContent);
1142
+ }
1143
+
1144
+ syncCustomCRSFiles({ wafDisabledBlocklets = [] }) {
1145
+ const wafDisabledBlockletsDIDs = wafDisabledBlocklets.map((x) => x.did);
1146
+ const rulesDir = path.join(this.includesDir, 'security/crs4/rules');
1147
+ const prefix = 'REQUEST-900-CUSTOM-RULES-';
1148
+ const files = fs.readdirSync(rulesDir).filter((f) => f.startsWith(prefix));
1149
+ const localWafBlockletsDIDs = files.map((x) => x.replace(prefix, '').replace('.conf', ''));
1150
+
1151
+ const removeList = difference(localWafBlockletsDIDs, wafDisabledBlockletsDIDs);
1152
+ const addList = difference(wafDisabledBlockletsDIDs, localWafBlockletsDIDs);
1153
+
1154
+ logger.info('syncCustomCRSFiles', { removeList, addList });
1155
+
1156
+ if (removeList.length) {
1157
+ const removeFiles = removeList.map((x) => path.join(rulesDir, `${prefix}${x}.conf`));
1158
+ for (const file of removeFiles) {
1159
+ try {
1160
+ fs.rmSync(file, { recursive: true, force: true });
1161
+ } catch (err) {
1162
+ logger.error('Failed to remove custom CRS file', { file, error: err });
1163
+ }
1164
+ }
1165
+ }
1166
+
1167
+ if (addList.length) {
1168
+ for (const did of addList) {
1169
+ try {
1170
+ const blocklet = wafDisabledBlocklets.find((x) => x.did === did);
1171
+ const domainAliases = blocklet?.site?.domainAliases || [];
1172
+ this.addCustomWAFConf({ did, domainAliases, wafPolicy: { enabled: false }, defaultWAF: 'On' });
1173
+ } catch (error) {
1174
+ logger.error('Failed to add custom CRS file', { did, error });
1175
+ }
1176
+ }
1177
+ }
1178
+ }
1179
+
1079
1180
  getLogDir() {
1080
1181
  return this.logDir;
1081
1182
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abtnode/router-provider",
3
- "version": "1.16.44-beta-20250527-130401-1b9ae926",
3
+ "version": "1.16.44-beta-20250529-004636-15e80e20",
4
4
  "description": "Routing engine implementations for abt node",
5
5
  "author": "polunzh <polunzh@gmail.com>",
6
6
  "homepage": "https://github.com/ArcBlock/blocklet-server#readme",
@@ -32,12 +32,13 @@
32
32
  "url": "https://github.com/ArcBlock/blocklet-server/issues"
33
33
  },
34
34
  "dependencies": {
35
- "@abtnode/constant": "1.16.44-beta-20250527-130401-1b9ae926",
36
- "@abtnode/logger": "1.16.44-beta-20250527-130401-1b9ae926",
37
- "@abtnode/router-templates": "1.16.44-beta-20250527-130401-1b9ae926",
38
- "@abtnode/util": "1.16.44-beta-20250527-130401-1b9ae926",
35
+ "@abtnode/constant": "1.16.44-beta-20250529-004636-15e80e20",
36
+ "@abtnode/logger": "1.16.44-beta-20250529-004636-15e80e20",
37
+ "@abtnode/router-templates": "1.16.44-beta-20250529-004636-15e80e20",
38
+ "@abtnode/util": "1.16.44-beta-20250529-004636-15e80e20",
39
39
  "@arcblock/http-proxy": "^1.19.1",
40
40
  "@arcblock/is-valid-domain": "^1.0.5",
41
+ "@ocap/util": "^1.20.11",
41
42
  "axios": "^1.7.9",
42
43
  "debug": "^4.3.7",
43
44
  "fast-glob": "^3.3.2",
@@ -61,5 +62,5 @@
61
62
  "bluebird": "^3.7.2",
62
63
  "fs-extra": "^11.2.0"
63
64
  },
64
- "gitHead": "dc9dbcc0940b331408fd2469147bbbf0c473376b"
65
+ "gitHead": "93f38f56824d2dd2afcd6950b73a67510d9cd18c"
65
66
  }