前言 事情的起因还得追溯到给博客加了 全站加速 。
依靠全站加速,还减轻了一次网站攻击,详见:记一次网站被 DDoS(CC)攻击 的过程和应对方案
在设置了全站加速之后,通过域名默认会走阿里云的 DCDN 节点,只有在未缓存的时候会访问源站。
此时,源站就会遇到一个需求:最好仅允许阿里云的 DCDN 节点进行访问。
也就是说,需要获取到阿里云的 DCDN 节点的 IP 列表,以添加到白名单中。
就此,开始了折腾之路。
开始 由于之前已经用过阿里云 OpenAPI 进行 CDN 的管理(如何通过阿里云 OpenAPI 管理 CDN ),所以这一次也采用了阿里云 OpenAP,从而实现自动化设置。
翻阅全站加速相关文档可以看到一个接口:DescribeDcdnL2Ips - 查询 L2 节点 IP 地址
这正是我需要的。
由于文档里没有说前置要求,所以我想当然的认为只要提交工单就可以获得权限。
中道崩阻 结果:
不是,大哥,你在 DCDN 的文档中又没有说明前置要求,没写就是没要求才对。
再者,CDN 节点的 IP 是什么需要藏着掖着的东西吗?你不公开出来我怎么设置白名单?
隔壁 Cloudflare 就是把 IP 地址范围全部公布出来的,例如 IP 地址范围 。
所以什么要设置个门槛?不是很能理解。
峰回路转 但很快啊,我又发现另一个接口:DescribeDcdnIpInfo - 验证 IP 节点
虽然没法直接获取 DCDN 节点的 IP ,但是可以验证一个 IP 是不是阿里云 DCDN 节点。
解决问题 所以接下来解决方案就很简单了。
我手动把 IP 地址验证一遍不就完事了?
当然了,考虑到 IPv4 一共有 42 亿个 IP,显然是不可能穷举一遍的。
那么我就得找个地方获取访问 IP。
获取 IP 地址 而 NGINX 的日志,就是一个获取 IP 的好地方。
以下是一个从日志中获取 IP 地址的 TypeScript 函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 async function extractUniqueIps (logFilePath : string ) { const uniqueIps = new Set <string >() const readStream = fs.createReadStream (logFilePath, { encoding : 'utf-8' }) const rl = createInterface ({ input : readStream }) rl.on ('line' , (line ) => { const ipPattern = /\b([0-9]{1,3}\.){3}[0-9]{1,3}\b/ const match = line.match (ipPattern) if (match) { uniqueIps.add (match[0 ]) } }) return new Promise <string []>((resolve ) => { rl.on ('close' , () => { resolve ([...uniqueIps]) }) }) }
采用流式读取是因为日志文件可能会有点大,全部读取进来有点占内存。
经过这一通折腾,我拿到了一个约 28000 个 IP 地址。
有一说一,这个量还是有点大,50 次/秒的话要跑 560 秒,约 9.3 分钟,这个时间就有点久了。
所以,可以采用 IP 属地进行初次过滤。
获取 IP 属地 通过 lionsoul2014/ip2region 这个项目可以拿到所有 IP 的属地。
虽然数据上可能有点过时,但用来初步筛选已经够了。
以下是一个使用 JavaScript 实现的查询脚本(参考:ip2region nodejs 查询客户端实现 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const Searcher = require ('.' )const dbPath = 'ip2region.xdb file path' try { const searcher = Searcher .newWithFileOnly (dbPath) const data = await searcher.search ('218.4.167.70' ) } catch (e) { console .log (e) }
然后从中筛选出 region 为 阿里云
或 阿里巴巴
的 IP。
接下来,再判断是否为阿里云 DCDN 节点。
判断 DCDN 节点 接下来就是调用 DescribeDcdnIpInfo
接口来验证是否为阿里云 DCDN 节点了。
以下是一个用 TypeScript 实现的 Client 封装类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 import dcdn20180115, * as $dcdn20180115 from '@alicloud/dcdn20180115' import OpenApi , * as $OpenApi from '@alicloud/openapi-client' import Util , * as $Util from '@alicloud/tea-util' export default class Client { static createClient (): dcdn20180115 { const config = new $OpenApi.Config ({ accessKeyId : process.env ['ALIBABA_CLOUD_ACCESS_KEY_ID' ], accessKeySecret : process.env ['ALIBABA_CLOUD_ACCESS_KEY_SECRET' ], }) config.endpoint = 'dcdn.aliyuncs.com' return new (dcdn20180115 as any ).default (config) } static async DescribeDcdnIpInfoRequest (IP : string ) { const client = Client .createClient () const describeDcdnIpInfoRequest = new $dcdn20180115.DescribeDcdnIpInfoRequest ({ IP , }) const runtime = new $Util.RuntimeOptions ({}) try { return (await client.describeDcdnIpInfoWithOptions (describeDcdnIpInfoRequest, runtime)).body } catch (error) { console .log (error.message ) console .log (error.data ['Recommend' ]) } } }
最后,把是 阿里云 DCDN 的 IP 写入到文件。
至此,就大功告成了!
生成网段 不过,单纯的用 IP 的话实际上容易误判,原因是阿里云 DCDN 的 IP 还是挺多的,靠从 NGINX 日志获取的方法效率低不说,还很滞后。
所以,将 IP 改为网段更加合理一些。
以下是一个从 IP 地址列表生成 IP 网段的 TypeScript 脚本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 function getSubnetCounts (ips : string [], netMask = 24 ) { const subnetCounts = new Map <string , number >() for (const ip of ips) { const segments = ip.split ('.' ) const subnetSegments = segments.slice (0 , netMask / 8 ) const subnet = `${subnetSegments.join('.' )} ${'.0' .repeat(4 - subnetSegments.length)} /${netMask} ` if (!subnetCounts.has (subnet)) { subnetCounts.set (subnet, 0 ) } subnetCounts.set (subnet, subnetCounts.get (subnet) + 1 ) } return [...subnetCounts].map (([subnet, count] ) => ({ subnet, count })) }
然后就可以生成 NGINX 的白名单了,生成的结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 allow 101.133.199.0 /24 ; allow 101.200.20.0 /24 ; allow 101.37.183.0 /24 ; allow 106.11.37.0 /24 ; allow 112.124.132.0 /24 ; allow 114.215.0.0 /16 ; allow 114.215.72.0 /24 ; allow 118.190.214.0 /24 ; allow 118.190.218.0 /24 ; allow 119.23.123.0 /24 ; allow 119.23.91.0 /24 ; allow 120.27.78.0 /24 ; allow 121.199.80.0 /24 ; allow 121.89.252.0 /24 ; allow 139.129.78.0 /24 ; allow 139.196.128.0 /24 ; allow 163.181.0.0 /16 ; allow 163.181.1.0 /24 ; allow 163.181.126.0 /24 ; allow 163.181.128.0 /24 ; allow 163.181.130.0 /24 ; allow 163.181.131.0 /24 ; allow 163.181.132.0 /24 ; allow 163.181.140.0 /24 ; allow 163.181.143.0 /24 ; allow 163.181.145.0 /24 ; allow 163.181.146.0 /24 ; allow 163.181.154.0 /24 ; allow 163.181.160.0 /24 ; allow 163.181.164.0 /24 ; allow 163.181.166.0 /24 ; allow 163.181.18.0 /24 ; allow 163.181.199.0 /24 ; allow 163.181.201.0 /24 ; allow 163.181.22.0 /24 ; allow 163.181.23.0 /24 ; allow 163.181.35.0 /24 ; allow 163.181.37.0 /24 ; allow 163.181.38.0 /24 ; allow 163.181.39.0 /24 ; allow 163.181.42.0 /24 ; allow 163.181.49.0 /24 ; allow 163.181.50.0 /24 ; allow 163.181.52.0 /24 ; allow 163.181.57.0 /24 ; allow 163.181.66.0 /24 ; allow 163.181.67.0 /24 ; allow 163.181.70.0 /24 ; allow 163.181.77.0 /24 ; allow 163.181.78.0 /24 ; allow 163.181.79.0 /24 ; allow 163.181.81.0 /24 ; allow 163.181.82.0 /24 ; allow 163.181.85.0 /24 ; allow 163.181.88.0 /24 ; allow 163.181.89.0 /24 ; allow 163.181.90.0 /24 ; allow 163.181.92.0 /24 ; allow 163.181.94.0 /24 ; allow 39.100.171.0 /24 ; allow 39.103.26.0 /24 ; allow 39.108.196.0 /24 ; allow 39.96.152.0 /24 ; allow 47.102.25.0 /24 ; allow 47.104.50.0 /24 ; allow 47.117.201.0 /24 ; allow 47.118.223.0 /24 ; allow 47.120.0.0 /16 ; allow 47.120.230.0 /24 ; allow 47.120.92.0 /24 ; allow 47.123.117.0 /24 ; allow 47.246.0.0 /16 ; allow 47.246.2.0 /24 ; allow 47.246.20.0 /24 ; allow 47.246.22.0 /24 ; allow 47.246.23.0 /24 ; allow 47.246.24.0 /24 ; allow 47.246.36.0 /24 ; allow 47.246.37.0 /24 ; allow 47.246.38.0 /24 ; allow 47.246.4.0 /24 ; allow 47.246.42.0 /24 ; allow 47.246.44.0 /24 ; allow 47.246.45.0 /24 ; allow 47.246.46.0 /24 ; allow 47.246.48.0 /24 ; allow 47.246.49.0 /24 ; allow 47.246.50.0 /24 ; allow 8.132.0.0 /16 ; allow 8.132.18.0 /24 ; allow 8.132.19.0 /24 ; allow 8.141.176.0 /24 ;
注释里面的 count 是该网段有多少个阿里云 DCDN IP。
以上结果仅供参考,如有需要,可以参考这些 IP 网段。
可以看到 NGINX 的白名单中有部分还采用了 16 位子网掩码,将 IP 范围进一步扩大到 65536 个。
总结 以上就是如何通过阿里云 OpenAPI 获取 DCDN 节点 IP 白名单的具体方法。
如果你达到了调用 DescribeDcdnL2Ips - 查询L2节点IP地址
接口的要求,那么实际上直接调用这个接口会更加方便。
如果你没达到要求的话,可以参考本文的实现方案,虽然不够优雅,但应该够用了。
喜欢的话就点个关注吧~
欢迎在评论区评论~
本文作者:草梅友仁 本文地址: https://blog.cmyr.ltd/archives/8c61e292.html 版权声明:转载请注明出处!