目录
5. 第五章 服务器端的局域网中有什么玄机
5.1 Web服务器的部署地点
5.1.1 在公司里部署Web服务器
网络包到底服务器根据服务器部署地点的不同而不同。最简单的是图1中(a)这种情况,服务器直接部署在公司网络上,可以从互联网直接访问。
但是现在主流方式是(b)这种加上防火墙。第一种方式几乎被淘汰了。一个原因是IPV4地址不足,如果按照第一种,那么公司要为公司的所有网络设备分配互联网的公有地址。第二个原因是安全问题。在这种方式中,从互联网传来的网络包会直接进入服务器,意味着服务器在攻击者看来处于“裸奔”状态。
所以现在一般都用第二种方式,部署防火墙。防火墙只允许发往指定服务器的指定应用程序的网络包通过,从而屏蔽其他不允许通过的包。
图1
5.1.2 将Web服务器部署在数据中心
还有种方式是部署在网络运营商管理的数据中心里,或者直接租用运营商提供的服务器。数据中心通过高速线路直接连接到互联网的核心部分,因此将服务器部署在这里可以获得很高的访问速度,当服务器访问量很大时这非常有效。而去,数据中心一般位于具有抗震结构的大楼内还具有自主发电设备,并实现24小时门禁管理。比一般放在公司里更具有安全性(但是现在一般大厂都会有自己的机房)。此外,数据中心不但提供安放服务器的场地,还提供各种附加服务,如服务器工作状态监控、防火墙的配置和运营,非法入侵监控等。
5.2 防火墙的结构和原理
5.2.1 主流的包过滤方式
防火墙的设计了多种方式(包过滤、应用层网关、电路层网关)实现防火墙的功能,但出于性能、价格、易用性等因素,现在最为普及的是包过滤方式。
5.2.2 如何设置包过滤的方式
其实防火墙的包过滤方式就是根据网络包中的头部(MAC头部、IP头部、TCP头部等)的字段来设置过滤规则。
图2
其实就像下图的表一样,防火墙里设置的规则就类似。比如第一行是允许互联网发到Web服务器的包通过,不管发送方是哪里发出的。再比如第二行第三行都是根据IP地址和TCP的控制位来判断的。
图3
5.2.3 通过端口号限定应用程序
我们还可以通过端口号来限制一台服务器的哪些应用程序可以与互联网通信。如果不设置端口号,相当于某台服务器的所有应用程序都可以与互联网通信,但这是不安全的,比如服务器上还有一个文件服务器程序在工作,那么这些文件可能会被非法访问从而造成信息泄漏。所以最好还是阻止除了必需服务以外的所有应用程序的包。就如上表第一行就是限定了端口号。
5.2.4 通过控制位判断连接方向
上面的规则我们已经可以做到只让某个具体的应用程序与互联网通信了,如果我们要阻止服务器访问互联网(比如有的公司内网访问不了互联网的某些网站)。当然我们不可能去阻止所有从服务器流向互联网的包,因为TCP是双向的,如果阻止全部包,那么互联网去访问我们的服务器也会出现问题访问不了。所以我们光判断包的流向还不行,还必须根据访问的方向来进行判断。这里就需要用到TCP中的控制位字段了。
我们知道TCP的三次握手,其中第一次握手的包的TCP控制位中SYN为1,而ACK为0。第二次和第三次中ACK都是为1的,且在第三个包中SYN为0的,所以根据这两个控制位就可以判断出一个网络包是否为建立连接的第一个包。如果第一个包是从Web服务器发往互联网的,那么防火墙就阻止它,这样设置之后,也肯定不会收到对方返回的第二个响应包,这样TCP三次握手就失败了。
通过接收方IP、发送方IP、接收方端口、发送方端口、TCP控制位等条件就可以判断出通信的起点和终点、应用程序种类、以及访问的方向,通过对这些条件的组合,就可以对包进行筛选了,以达到我们想要的包过滤条件。
但是其实存在无法将希望允许和阻止的访问完全区分开的情况,比如对DNS服务器的访问。DNS查询使用的是UDP协议,UDP没有连接操作,所以无法像TCP一样根据控制位来判断访问方向。所以无法设置一个规则只允许公司内部访问互联网的DNS服务器,而阻止互联网访问公司内部的DNS服务器。这一性质对于所有UDP协议的应用程序都是共通的。这种情况,要么冒一定的风险允许该应用程序的所有包通过,要么牺牲一定的便利性阻止该应用程序的所有包通过。
5.2.5 从公司内网访问公开区域的规则
像上面图3那样的网络结构中,需要设置互联网和公开区域之间的包过滤规则,还需要设置公司内网和互联网之间,或者公司内网与公开区域之间的包过滤规则。但是要注意不要让这些规则互相影响。比如为了让内网(DCN区域)能访问公开区域(DMZ区域),可以将接收方IP地址为公开区域的包通过,但是这样一来,互联网的包也可以全部访问到公开区域了,就有一定风险,所以设置防火墙的规则时一定要考虑周到。
5.2.6 从外部无法访问公司内网
包过滤方式的防火墙还具有地址转换功能。和包过滤一样,以起点和终点作为条件,根据需要设置是否需要进行地址转换。私有地址和公有地址之间的对应关系,以及端口好的对应关系都是自动管理的,因此只需要设置是否允许地址转换就可以。在现如今的网络架构中,不设置地址转换,其实互联网的包是无法直接进入内网的,所以不需要再单独设置一条包过滤规则来阻止从互联网访问内网的包了。
5.2.7 通过防火墙
当包通过防火墙,防火墙根据规则判断是否允许包通过,如果判读结果为阻止,那么这个包就会被丢弃并记录下来(如果是路由器,就不会被记录,因为路由器容量小)。记录下来是可以通过这些被阻止的包来分析其中的非法入侵的痕迹,帮助我们更好地防范非法入侵。
如果包被判断为允许通过,则该包会被转发出去,这个转发的过程和路由器是相同的。如果我们只关注判断是否允许包通过这一点,可能会觉得防火墙是一种特殊机制,而且市面上销售的防火墙大多是专用的硬件设备或者软件,这也加深了大家的这种印象。
5.2.8 防火墙无法抵御的攻击
防火墙可以根据起点终点判断是否允许包通过,但是防火墙无法识别包的内容,假如有入侵者通过某种方式正确地设置了起点终点,但是包的内容是恶意代码,就会导致服务器被攻击。这种只有检查包的内容才能识别这种风险,防火墙对此也无能为力。
应对这种情况有两种方法。一个问题的根源是服务区的BUG,攻击者的代码之所以能攻击成功,就是因为服务器有BUG,所以修复BUG防止宕机就是一种方法。开发者会很快发布修复了BUG的新版本,因此持续关注安全漏洞信息并更新软件的版本是非常重要的。
另一种方法是在防火墙之外部署用来检查包的内容并阻止有害包的设备和软件。但是其实这种方法和直接修复BUG也差不多,因为这种方法只能阻止已知的BUG,不能抵御未知的风险,所以最后还是得修复BUG。但是当服务器数量较多,更新软件版本需要花费一定时间时或者容易忘记更新软件,这时对包的内容进行检查就比较有效。
5.3 通过将请求平均分配给多台服务器来平衡负载
5.3.1 性能不足时需要负载均衡
这里其实就是现在企业都在用的负载均衡,分为ALB(应用负载均衡)、NLB(网络负载均衡),具体区别大家可以百度一下。
负载均衡就是采用多台服务器,这样就能减少每台服务器的访问量。要采用这样的方法,必须有一个机制将请求平均分配到每台服务器上。具体做法有很多种,其中一种是通过DNS服务器来分配,当访问服务器时,客户端需要先向DNS服务器查询WEB服务器的IP地址,如果在DNS服务器中填写多个名称相同的记录,则每次查询时DNS服务器都会按照顺序返回不同的IP地址。
比如www.csdn.com有三个IP:
192.0.2.60
192.0.2.70
192.0.2.80
那第一次查询域名时服务器返回:
192.0.2.60 192.0.2.70 192.0.2.80
第二次返回:
192.0.2.70 192.0.2.80 192.0.2.60
第三次返回:
192.0.2.80 192.0.2.60 192.0.2.70
第四次又返回第一次的结果。这种方式叫做轮询。
但这种方法存在缺点,假如有一台服务器故障,这时希望返回的IP地址能够跳过故障的服务器,然而普通的DNS服务器不能确认Web服务器是否正常工作,因此即便Web服务器即使宕机了,它依然会返回这台故障的服务器的IP。
5.3.2 使用负载均衡器分配访问
为了防止上面的问题,可以使用负载均衡器。首先要把负载均衡器的IP地址代替Web服务器的IP地址注册到DNS服务器上,然后当访问Web时,DNS服务器就返回负载均衡器的地址,负载均衡器再通过自己的算法来将判断将请求转发给哪台服务器。如下图
图4
负载均衡器可以定期采集Web服务器的CPU、内存使用率,根据这些数据判读服务器的负载状况。也可以不去查询这些数据,而是直接根据事先设置的服务器性能指数,按比例去分配请求。当然,有很多种算法,大家可以自行网上看一下。
5.4 使用缓存服务器分担负载
5.4.1 如何使用缓存服务器
缓存服务器是一台通过代理机制对数据进行缓存的服务器。代理介于Web服务器和客户端之间,具有对Web服务器访问进行中转的功能。当进行中转时,它可以将Web服务器返回的数据保存在磁盘中,并可以代替Web服务器将磁盘中的数据返回客户端。这种保存的数据称为缓存,缓存服务器就是指这样的功能。
5.4.2 缓存服务器通过更新时间管理内容
缓存服务器和负载均衡器一样,需要代替Web服务器注册到DNS服务器中。具体的工作过程就像下图这样。像平时我们见到的304状态码其实就是返回的缓存中的内容,具体其实还是有点复杂,大家可以网上搜一下Web缓存的工作过程,下面简单介绍一下。
图5
没命中缓存的情况:缓存服务器会以客户端的身份向目标Web服务器发送请求消息,于是缓存服务器会收到来自Web服务器的响应消息,而且缓存服务器会在响应消息中添加Via字段(但是这个字段不重要 其实大部分都不加的),它表示这个消息是经过缓存服务器转发的,然后缓存服务器会以Web服务器的身份向客户端发送响应消息。同时缓存服务器会将响应消息保存到缓存中,并记录保存的时间。
命中缓存的情况:当请求发到缓存服务器时,缓存服务器会添加一个If-Modified-Since头部字段并转发给Web服务器,询问Web服务器用户请求的数据是否发生变化。然后,然后Web服务器会根据If-Modified-Since的值与服务器上的页面数据最后更新时间进行比较,如果在指定时间内数据没有变化,就会返回304 Not Modified,然后将消息返回给客户端。
5.4.3 最原始的代理—正向代理
现在有两种代理,正向代理和反向代理。最开始正向代理就叫代理,后来出现了反向代理,才区分正向和反向。
正向代理刚出现时是为了缓存。现在还有一个目的是用来实现防火墙(包过滤之外的另一种方式)。防火墙是为了防止互联网的非法入侵,最可靠的办法就是阻止互联网和公司内网之间的所有包。不过,这样内外就无法访问互联网了,因此就可以利用代理。如下图所示,首先接收来自客户端的请求消息,然后转发到互联网中,这样就可以实现只允许通过必要的网络包了。同时,还可以利用代理的缓存,速度会更快。
图6
而且,代理在转发过程中还可以查看请求的内容,所以可以根据内容判断是否允许访问。包过滤方式无法做到这一点。
5.4.4 正向代理的改良版 — 反向代理
正向代理需要在浏览器中进行设置,如果是公司发的电脑,大家点击浏览器应该会看到,“本浏览器由贵公司管理”,这里我猜应该就是公司用代理去管理了我们的浏览器,所以才无法访问某些网站,比如我们公司的电脑就访问不了B站。
平时我们开发中在webpack、vite中的用的proxy就是反向代理。关于正向代理和反向代理的区别推荐看下这篇文章,简单易懂:详解正向代理与反向代理
5.4.5 透明代理
缓存服务器判断转发目标的方法还有一种就是查看请求消息的包头部。因为包的IP头部中包含接收方IP地址,只要知道了这个地址,就知道用户要访问哪台服务器了,这种方法称为透明代理。
但是书上也说了,现在其实大部分人不叫透明代理了,而是直接叫“缓存”。透明代理是放在请求消息从浏览器传输到Web服务器的路径中,当消息经过时拦截,然后转发到Web服务器,其实就和之前讲304缓存差不多,这节不重要,知道是缓存就可以了。
5.5 内容分发服务
5.5.1 利用内容分发服务分担负载
缓存服务器部署在客户端还是服务器端效果是不同的。如下图,当部署在服务器端(a)时,可以减轻Web服务器的负载,但无法减少互联网中的流量,而放在客户端(b)更有效,就可以减少访问服务器的时间,让网络流量更稳定,特别是当访问内容中含有大量图片或视频时效果更明显。现在一些APP中的H5页面就会使用到内容分发网络(CDN),这样可以让用户更快访问到H5。
图7
不过,客户端的缓存服务器是归客户端网络运营商管理者(三大运营商)所有的,Web服务器的运营者(公司)无法控制它。但是可以像上图C那样,将可以自己控制的缓存服务器放在客户端的运营商处。
这样一来,我们就可以将缓存服务器部署在距离用户很近的地方,同时Web服务器运营者还可以控制这些服务器,但也有问题。任何地方的人都可以访问这个缓存服务器,因为它部署在互联网中的。而且如果真的要实现这种方式,必须在所有运营商的POP中都部署缓存服务器才可以,这个数量太大,非常不现实。
所以可以筛选出一些主要的运营商,这样就可以减少缓存服务器的数量,部署一些主要节点,比如我们的服务器在广东,我在东北只在哈尔滨部署一个缓存服务器,这样也可以大幅提高访问速度。
但是即使减少了数量,作为一个Web服务器运营者,如果自己和这些运营商签约并部署缓存服务器,无论是费用还是精力都是吃不消的。所以有一些专门从事相关服务的厂商出现了,就是阿里云、腾讯云等厂商,他们都提供CDN服务。它们称为CDSP(内容分发服务运营商),它们会与主要的运营商签约,并部署很多台缓存服务器。
5.5.2 如何找到最近的缓存服务器
那CDN是如何实现找到最近的缓存服务器的呢?
有几种方法,第一个是像负载均衡一样用DNS服务器来分配访问。可以在DNS服务器返回Web服务器IP地址时,对返回的内容进行一些加工,使其能够返回距离客户最近的缓存服务求的IP地址。
那怎样判断客户端与缓存服务器的距离呢?
首先,需要事先从缓存服务器部署地点的路由器收集路由信息。例如下图一共有4台缓存服务器,在这4台服务器的部署地点又分别有4台路由器,则我们需要分别获取这4台路由器的路由表,并将4张路由表集中到DNS服务器上。
然后,当客户端的DNS服务器去查询本机的IP时。服务器端的DNS服务器(图中左侧)会拿事先设置好的4台缓存服务器的路由表去查询,通过互联网内部的路由表中的路由信息可以知道先通过运营商X,然后通过运营商Y,最后到达运营商Z这样的信息,通过这样的信息就可以大致估算出距离,然后依次查询所有路由器的路由表之后,就可以通过比较找出哪一台路由器距离客户端DNS服务器最近。而客户端DNS服务器一般都是和客户端在同一位置,所以就等于估算出来缓存服务器与客户端之间的距离了。
图8
5.5.3 通过重定向服务器分配访问目标
另一种找到离客户端最近缓存服务器的方法是通过HTTP中的头部字段来找,主要是Location字段。这个应该大家都知道,就是在重定向的时候用到。
当使用这种方式时,首先需要将重定向服务器注册到Web服务器端的DNS服务器上。这样,客户端就会将Http消息发到重定向服务器上。重定向服务器会像刚才那种方法一样收集了来自各个路由器的路由信息,然后根据这些信息找到最近的缓存服务器,然后将缓存服务器的地址放到Location字段中返回响应。这样,客户端就会重新去访问指定的缓存服务器了。工作流程如下图。
图9
这种方法的缺点是增加了HTTP的交互次数,相应的开销也比较大,但优点是精度比较高。因为重定向是根据客户端发送来的HTTP消息的发送方IP地址来估算距离的,而上一节的方法是估算客户端DNS服务器到缓存服务器之间的距离,精度比较差。
还可以使用除路由信息之外的其他信息来估算距离,进一步提高精度。重定向服务器不仅可以返回带有Location字段的HTTP消息,也可以返回一个通过网络包往返时间估算到缓存服务器的距离的脚本,通过在客户端运行脚本来找到最优的缓存服务器。这个脚本可以向不同的缓存服务器发送测试包并计算往返时间,然后将请求发送到往返时间最短的一台缓存服务器,这样就可以找到距离最短的缓存服务器。
5.5.4 缓存的更新方法会影响性能
缓存内容的更新方法也会影响缓存服务器的效率。也就是每次发送请求时都需要向原始服务器询问数据有没有变化(ps:现在浏览器的缓存机制就是这样的,但是这样其实也会比没有缓存好很多),而且对于第一次访问是没用的,第一次是没有缓存的。
要改善上面这一点,有一种方法是让Web服务器在原始数据发生更新时,立即通知缓存服务器,使得缓存服务器上的数据一直保持最新状态,这样就不需要每次确认原始数据是否变化,而且从第一次开始就能发挥缓存的作用。内容分发服务(CDN)就是这样的。
此外,由CGI程序生成的动态页面是不能保存在缓存服务器中的,这种情况下需要将其中的动态部分和静态部分区分开,只保存静态部分。
Web服务器前存在各种各样的服务器,如防火墙、代理服务器、缓存服务器等。请求消息最终会通过这些服务器,到达Web服务器,然后Web服务器会返回响应消息,这一部分就是最后一章第六章的内容了。
总结
本章的内容介绍的都是一些概念,但是其实这些概念挺重要的,比如CDN、防火墙、缓存、代理、NAT、ALB、NLB,这些都是在公司开发时实际需要用到的东西。我看这本书的目的就是搞懂整个网络流程,因为组里一个前辈曾经跟我说过,写的代码的bug可以根据debugger排查出来,但是如果是网络的问题,基本上就是你知道就知道,不知道就不知道。然后在今年8月初遇到的两个网关方面的问题后(大家可以看我8月发的两篇“记录一次网关方面的问题排查”文章),觉得学习网络方面的知识变得越发迫切了,就开始本次学习,大家一起学起来!