ip_ranges.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import fs from "node:fs";
  2. import https from "node:https";
  3. import { dirname } from "node:path";
  4. import { fileURLToPath } from "node:url";
  5. import { ProxyAgent } from "proxy-agent";
  6. import errs from "../lib/error.js";
  7. import utils from "../lib/utils.js";
  8. import { ipRanges as logger } from "../logger.js";
  9. import internalNginx from "./nginx.js";
  10. const __filename = fileURLToPath(import.meta.url);
  11. const __dirname = dirname(__filename);
  12. const CLOUDFRONT_URL = "https://ip-ranges.amazonaws.com/ip-ranges.json";
  13. const CLOUDFARE_V4_URL = "https://www.cloudflare.com/ips-v4";
  14. const CLOUDFARE_V6_URL = "https://www.cloudflare.com/ips-v6";
  15. const regIpV4 = /^(\d+\.?){4}\/\d+/;
  16. const regIpV6 = /^(([\da-fA-F]+)?:)+\/\d+/;
  17. const internalIpRanges = {
  18. interval_timeout: 1000 * 60 * 60 * 6, // 6 hours
  19. interval: null,
  20. interval_processing: false,
  21. iteration_count: 0,
  22. initTimer: () => {
  23. logger.info("IP Ranges Renewal Timer initialized");
  24. internalIpRanges.interval = setInterval(internalIpRanges.fetch, internalIpRanges.interval_timeout);
  25. },
  26. fetchUrl: (url) => {
  27. const agent = new ProxyAgent();
  28. return new Promise((resolve, reject) => {
  29. logger.info(`Fetching ${url}`);
  30. return https
  31. .get(url, { agent }, (res) => {
  32. res.setEncoding("utf8");
  33. let raw_data = "";
  34. res.on("data", (chunk) => {
  35. raw_data += chunk;
  36. });
  37. res.on("end", () => {
  38. resolve(raw_data);
  39. });
  40. })
  41. .on("error", (err) => {
  42. reject(err);
  43. });
  44. });
  45. },
  46. /**
  47. * Triggered at startup and then later by a timer, this will fetch the ip ranges from services and apply them to nginx.
  48. */
  49. fetch: () => {
  50. if (!internalIpRanges.interval_processing) {
  51. internalIpRanges.interval_processing = true;
  52. logger.info("Fetching IP Ranges from online services...");
  53. let ip_ranges = [];
  54. return internalIpRanges
  55. .fetchUrl(CLOUDFRONT_URL)
  56. .then((cloudfront_data) => {
  57. const data = JSON.parse(cloudfront_data);
  58. if (data && typeof data.prefixes !== "undefined") {
  59. data.prefixes.map((item) => {
  60. if (item.service === "CLOUDFRONT") {
  61. ip_ranges.push(item.ip_prefix);
  62. }
  63. return true;
  64. });
  65. }
  66. if (data && typeof data.ipv6_prefixes !== "undefined") {
  67. data.ipv6_prefixes.map((item) => {
  68. if (item.service === "CLOUDFRONT") {
  69. ip_ranges.push(item.ipv6_prefix);
  70. }
  71. return true;
  72. });
  73. }
  74. })
  75. .then(() => {
  76. return internalIpRanges.fetchUrl(CLOUDFARE_V4_URL);
  77. })
  78. .then((cloudfare_data) => {
  79. const items = cloudfare_data.split("\n").filter((line) => regIpV4.test(line));
  80. ip_ranges = [...ip_ranges, ...items];
  81. })
  82. .then(() => {
  83. return internalIpRanges.fetchUrl(CLOUDFARE_V6_URL);
  84. })
  85. .then((cloudfare_data) => {
  86. const items = cloudfare_data.split("\n").filter((line) => regIpV6.test(line));
  87. ip_ranges = [...ip_ranges, ...items];
  88. })
  89. .then(() => {
  90. const clean_ip_ranges = [];
  91. ip_ranges.map((range) => {
  92. if (range) {
  93. clean_ip_ranges.push(range);
  94. }
  95. return true;
  96. });
  97. return internalIpRanges.generateConfig(clean_ip_ranges).then(() => {
  98. if (internalIpRanges.iteration_count) {
  99. // Reload nginx
  100. return internalNginx.reload();
  101. }
  102. });
  103. })
  104. .then(() => {
  105. internalIpRanges.interval_processing = false;
  106. internalIpRanges.iteration_count++;
  107. })
  108. .catch((err) => {
  109. logger.fatal(err.message);
  110. internalIpRanges.interval_processing = false;
  111. });
  112. }
  113. },
  114. /**
  115. * @param {Array} ip_ranges
  116. * @returns {Promise}
  117. */
  118. generateConfig: (ip_ranges) => {
  119. const renderEngine = utils.getRenderEngine();
  120. return new Promise((resolve, reject) => {
  121. let template = null;
  122. const filename = "/etc/nginx/conf.d/include/ip_ranges.conf";
  123. try {
  124. template = fs.readFileSync(`${__dirname}/../templates/ip_ranges.conf`, { encoding: "utf8" });
  125. } catch (err) {
  126. reject(new errs.ConfigurationError(err.message));
  127. return;
  128. }
  129. renderEngine
  130. .parseAndRender(template, { ip_ranges: ip_ranges })
  131. .then((config_text) => {
  132. fs.writeFileSync(filename, config_text, { encoding: "utf8" });
  133. resolve(true);
  134. })
  135. .catch((err) => {
  136. logger.warn(`Could not write ${filename}: ${err.message}`);
  137. reject(new errs.ConfigurationError(err.message));
  138. });
  139. });
  140. },
  141. };
  142. export default internalIpRanges;