ip_ranges.js 4.2 KB

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