
暂时未有相关通用技术能力~
阿里云技能认证
详细说明本章序言云网络环境下,当问题出现时,往往是没有充足的时间来全栈分析问题出现的原因,此时,“抓包”这个手段就显得尤为重要,它可以在只需要付出少量的开销就可以获得观察到链路上通信数据包的能力,性价比尤为突出。本文主要从定制个性化的Wireshark开始,同时也介绍了Wireshark自带的一些命令行工具,希望可以为大家提供排查问题的一些思路。另外,本文不会探讨如何去使用Wireshark来抓包,而是重点讨论如何通过Wireshark及相关命令行工具去分析抓包,找到问题的结症。 从抓包说起不知何时开始,“抓包”一词在IT圈内已经变得人尽皆知,任何问题只要涉及到网络时,有很大的概率会使用抓包来排查。我对抓包的态度,也是又爱又恨:出现的频率如此之高,说明这个方法确实有它的效果,否则不会被这么多人提起,但究其本身来看,抓包对问题排查其实是一个投入产出比非常差的方案:它一定需要问题出现,而且成功的抓包了问题现场的包,才能有分析问题的前提条件。在历史案例处理中,特别是一些疑难案例,往往都需要好几次的问题现场抓包,最终才能定位问题原因。站在客户的角度上,得要非常配合的客户才会事无巨细的做好一些事情,比如部署抓包、复现问题、提供问题现场信息等等。所以每一次问题现场的抓包,都需要尽我们所能提供足够的捕获问题环境的手段、分析问题抓包的方法。千人千面Wireshark本身是一个跨平台的工具,可以轻松地在windows、MacOS、Linux三大主流平台上安装并运行。使用Wireshark打开一个网络包后,可以看到一个默认界面,我强烈建议大家对这个界面作一些定制化的修改,比如分析TCP协议时将sequence number、ack number、源端口、目标端口、TCP segment length放到数据列中分析ICMP协议时,可以将三层的TTL、identification放到数据列中以下展示了我个人比较喜欢的数据列:理由如下:工作中我经常碰到需要分析TCP数据包的情况,所以我需要一目了然的掌握每一个数据包的seq number和ack numberTTL是辨别短时间内来自相同的目标IP是否是同一个机器发出的,目前网络上存在非常多的防火墙会截获一些报文,然后生造类似的报文发回,这种情况下TTL字段就派上用场了。id(identification)值则是IP层一个不起眼的字段,但是却可以指示这个包的唯一性,机器每发一个包,这个值就自增一次,通常的用处是快速找到两个抓包中同一个IP包,或者是确认某一个包是重传包还是转发包(这里的重传包指的是上层应用的重传,前者id值会自增,后者不会)源目端口的fields值用了一个小技巧,我将tcp和udp的端口过滤条件合并在了一起,中间使用逻辑或来连接,这样这一列就可以同时支持显示TCP、UDP两个协议的端口,不需要来回切换TCP Seg Len字段显示了他的载荷字节大小,因为工作中经常会碰到MTU、MSS等类似的问题,可以通过这个字段快速的判断某一个TCP包中是否有载荷。 当然,这里展示的只是我在工作中常用的字段,大家所处的岗位不同,业务场景也不同,所需要的字段也不尽相同,大家可以根据需要手动添加自己需要的fields,Wireshark可以支持所有的过滤条件展示为数据列,当然它也支持直接在Packet Details中,选中一个字段,直接右击,选择Apply as Column即可,非常方便。Wireshark的伙伴们当你将Wireshark装进电脑时,在它的程序目录下,其实还附带了很多非常有用的程序,只是这些程序都没有图形界面,所以上手会比较困难,但也正是因为他们舍弃了图形界面,在某些场合下反而比Wireshark要更好用,甚至还能提供Wireshark都不具备的功能。tshark首先登场的是tshark,你可以简单理解为它是文本形式的Wireshark,一般会用作以下场景:一、抓包文件过大,需要对它进行一些操作,比如根据特定的过滤条件导出筛选后的包;二、提取抓包内特定的数据,作为其他程序的文本输入,实现一些自定义逻辑。比如我要分析某个包中来自特定IP的ICMP包的数量,那么可以结合tshark的显示过滤器,过滤出特定的抓包数据,然后作为输入传给wc。当然,你依然可以通过-h参数来打印出tshark支持的参数列表,这里提供几个比较有用的参数:-R -Y:这两个参数分别为读取过滤器和显示过滤器,他们的区别如名称所示,tshark在展示包时,会有两个阶段,先从pcap文件中读取数据,然后展示出来,那么这两个过滤器分别会应用在对对应的阶段。但一般-Y参数用的会更多一些,一方面是-R参数需要读取两次包(需要配合-2参数),时间较久;另外一方面-R参数会破坏包的序列号,不方便对比分析。两个参数支持的过滤条件,形如" tcp.flags.syn==1"-t:这个参数指定time显示格式,一般推荐-r(相对首包的时间)、-a/-ad(绝对时间)-e:指定tshark显示的列名,这里支持任何Wireshark的字段名,如tshark -e frame.number -e ip.addr -e udp其余还有很多参数,限于篇幅这里就不展开介绍了,若有兴趣可以从官方网站上查看到所有支持的参数用法说明:https://www.wireshark.org/docs/man-pages/tshark.html另外,虽然tshark也支持抓包,但我建议使用dumpcap来完成,毕竟从名字上看,dumpcap就是为抓包而生的。dumpcap曾几何时,linux上的tcpdump在问题排障方面大出风头,特别是“循环抓包”,简直是拿“空间换时间”的典范,设定一个足够大的存储空间,甚至可以稳定的回溯近24小时内的所有网络流量。而Windows上,循环抓包一直是一个头疼的问题,系统自带的netsh可以实现循环抓包,但是命令晦涩难懂,同时也不支持rotate,直到我发现了dumpcap这个命令,windows上循环抓包终于有简便的实现方式了。先上示例:dumpcap -f tcp -i 2 -w C:\tcp.pcap -b filesize:102400 -b files:10上面这个命令,指定了抓包的网卡index为2,只抓tcp协议的包,同时每个抓包文件大小上限为102400KB,最大存储10个抓包文件。这里唯一和tcpdump表现不同的是,tcpdump的循环,是固定10个文件,当文件个数占满后,覆盖最早的文件,重新开始写入文件;而dumpcap是删除最早的文件,继续创建新的序列号,比如tcp_00011_xxx.pcap来继续写入抓包数据。其余常用的参数依然可以参考以下文档:https://www.wireshark.org/docs/man-pages/dumpcap.htmlcapinfos在循环抓包结束后,通常会得到非常多的抓包文件,如果用户的业务量非常大,每个抓包文件覆盖的时间可能非常短(比如10秒),那么如何找到准确的一个时间点在哪个抓包里呢?这时候capinfos就派上用场了,它可以对一个包快速的做概览,给出包数量、抓包起止时间、包速率等等,而不需要大动干戈的使用Wireshark打开后再进行summary。输出样例:File name: randomicmp.pcapFile type: Wireshark/tcpdump/... - pcapFile encapsulation: EthernetFile timestamp precision: microseconds (6)Packet size limit: file hdr: 5000 bytesNumber of packets: 1,000File size: 2,531 kBData size: 2,515 kBCapture duration: 999.000000 secondsFirst packet time: 1970-01-01 08:00:00.000000Last packet time: 1970-01-01 08:16:39.000000Data byte rate: 2,517 bytes/sData bit rate: 20 kbpsAverage packet size: 2515.14 bytesAverage packet rate: 1 packets/sSHA256: 6c41b814c403f6c9f81686788a0d210feadb14707a3e3b8ceeb8c49663b322d7RIPEMD160: 9efc88ead1d1c083f96690e441e4090ebabf55e1SHA1: 837f7da2d96dc11e971d39e78756b6b5096e8fd9Strict time order: TrueNumber of interfaces in file: 1Interface #0 info:Encapsulation = Ethernet (1 - ether)Capture length = 5000Time precision = microseconds (6)Time ticks per second = 1000000Number of stat entries = 0Number of packets = 1000mergecap& editcap这两个程序均是对抓包做修改的命令,但前者功能更加纯粹一些,就是单纯的将若干个包合并为1个包,后者原则上是对一个包做修改,但是也可以曲线的实现拆包,即将一个包按照包数量、包流逝的时间拆成若干个包。mergecap这个工具使用比较简单,一般情况下也无需携带特别的参数,以下命令供参考:mergecap -w all.pcap input1.pcap input2.pcap input3.pcap editcap的参数就相对丰富一些:-s:可以将每个包的字节大小裁剪至指定的大小-c/-i:可以按照包数量/包消逝的时间来对一个很大的抓包进行切割,方便用Wireshark打开做图形化的分析 实战演练上文说了这么多,这里举一个例子,来说明上述工具分别可以怎么用。问题概述用户反馈客户端访问自建nginx上的网站会偶发出现失败的情况,由于问题出现的频率非常低,难以通过传统的日志来进行问题分析。计划在客户端和nginx上部署循环抓包进一步分析。解决步骤在客户端和nginx服务器上部署循环抓包,客户端是windows,使用dumpcap部署,nginx服务器是linux,使用tcpdump部署(部署方案这里不赘述,可以参考网上的文献)问题复现后,我们发现用户提供的抓包文件非常大,每个抓包200MB,一共有50个。由于用户的业务量非常大,每个抓包覆盖的时间点只有20秒。结合用户提供的日志,挑选了一个问题时间点,通过capinfo找到了问题的包。打开单个抓包后我们发现用户的业务是典型的长连接,单个包里看不出完整的TCP会话过程,我们先通过mergecap将所有的包合并在一起,然后再使用tshark,传入显示过滤器(可以用源端口进行过滤,因为客户端的源端口比较唯一,筛选效果会比较好),将筛选结果输出到文件。这样就得到了比较干净的一个问题现场TCP流的抓包了。
本章序言老实说,最开始排查网络问题,接触到的命令就是ping,当时也只是对ping有一个非常粗浅的理解,知道他可以探测本地到目标的链路是否是否正常,再往后就知道了traceroute和mtr,感叹网络的大佬们真的将三层、四层协议用的是淋漓尽致,很好的利用了ICMP、IP协议的各个字段,非常巧妙的实现了“窥一斑而知全豹”的能力,下面的内容,部分可能非常老套,但我建议大家可以一并参考一下,虽然这些工具都是老生常谈了,但每次碰到疑难问题去研究他们的实现时,都可以获得新的感悟。探测元老——ping说到ping,应该是无人不知无人不晓,最简单的方式,就是ping加上目标IP,在Windows下会连续发起4次ICMP request探测,类Linux下则会发起持续的ICMP request探测,直到Ctrl+C停止。但它其实还有很多高级功能,这里列举一二。Flood ping默认情况下,ping的探测逻辑是这样的:发出ICMP request-接收到ICMP reply-发送下一个ICMP request,每一个ICMP request都是串行的发送的,但有时候我们希望更快速的进行探测,这个时候就需要flood ping出马了,对应的参数为-f,加上后ping程序会并发的发出ICMP request,同时在接收远端回复的ICMP reply,每发出一个ICMP request,在屏幕上打印一个点,每收到一个ICMP reply,就打印一个退格,那么就可以很直观的知道到底丢了多少包,当然,这个方式下-i、-c参数依然可以使用,特别是-c参数,指定要发多少的ICMP包。更直观的打印探测结果默认情况下,ping程序只会打印出收到ICMP reply信息,如果其中丢了几个包,用肉眼很难在满屏的结果中找到,这个时候就需要让ping程序更直观的打印出每个sequence number的结果,对应参数-O,如果在规定的时间内没收到回复,会打印一行no answer yet for icmp_seq=xxx,这样可以非常直观的看到具体哪个包出现了丢包。另外,在使用这个参数时,一般还会合并使用-D参数,这个参数会在每一行输出结果前加上timestamp时间戳,可以用于一些持续性的网络监控。mtr相较于ping,mtr会更加实用一些,他利用了IP头字段里的TTL值,结合ICMP协议完成了整条网络链路的探测。默认情况下,他会使用ICMP type 0x00和0x08的包作为探测的报文,一般使用中我都会建议用户加上-n参数,禁止反解,这样可以最直观的看到每一跳的真实IP,另外,若需要将mtr的结果提供给第三方,建议可以使用-rc参数,r代表不使用交互界面,而是在最后给出一个探测结果报告;c参数指定需要作几次探测(一般建议是至少200个包,可以配合-i参数减少包间隔来加快得到结果的时间)。除了ICMP,mtr也可以进行TCP和UDP协议的探测,当然依然得结合ICMP和TTL的特性来探测,不过由于链路哈希的存在,这个探测的结果不太可靠(读者朋友可以思考一下为什么,下一节讲到traceroute会详细说明),一般建议还是直接使用ICMP协议进行探测。路由跟踪Traceroute上一讲到了mtr,但其实traceroute才是更加“历史悠久”的路由跟踪程序,从mtr的全称My TraceRoute就可以看出来了,mtr是受到traceroute的启发诞生的,虽然mtr基本可以做到traceroute的绝大多数功能,但在一些特定的环境下(比如没有安装mtr),还是得要使用traceroute来进行排查。traceroute的默认行为和mtr不同的是,traceroute默认使用UDP作为四层协议,下层还是依靠IP头的TTL来控制中间的节点返回ICMP差错报文,来获得中间节点的IP和延时。唯一的区别是,在达到目标节点时,若是ICMP协议,目标大概率是会回复ICMP reply;如果是UDP协议,按照RFC协议规定,系统是要回复ICMP 端口不可达的差错报文,虽然三大平台Windows/MacOS/Linux都实现了这个行为,但出于某些原因,这个包可能还是会在链路上被丢弃,导致路由跟踪的结果无法显示出最后一跳。所以建议在一般的情况下,traceroute命令可以加上-I参数,让程序使用ICMP协议来发送探测数据包。mtr的隐藏陷阱我曾经接触过一个case,用户反馈业务上部署的监控,会时不时的出现“丢包”问题。我们查看了公网的大盘监控,反复查阅都没有看到任何匹配的异常报警,没法子只能和客户沟通,从问题表现着手分析,直到我们发现客户使用的是mtr给出报告的方式来监控是否丢包(mtr的报告最后一行是否包含目标IP),才发现客户一不小心踩到了mtr的一个陷阱中去了。由于mtr本身存在一个缺陷,如果链路上有多跳都禁ICMP回显,那么MTR是大概率无法到达最终IP的(尽快实际测试,ping目标IP是通的)。这个问题在Linux上尤为突出,MacOS上的mtr没有这个问题,这个时候就需要traceroute出马了,加上-q参数,指定每一跳尝试发包的次数(最大10次),然后可以得到一个近似的mtr结果,若需要更多的结果,只能是不停的执行traceroute -q 10 -w 1 -n -I x.x.x.x ,来获取更准确的路由跟踪结果。站在这个角度,大家知道为什么mtr会诞生了吧:-)传输层的Ping工具——tcpping2上面说了很多基于ICMP的测试工具,但真实的业务下,多数都是基于TCP来实现的,比如业务上报Connection Timeout、Connection Reset By Peer等报错,对应到传输层面,往往是和建连、关闭连接相关,这个时候ping、mtr很难作为排查工具来辅助问题分析,同时当时市面上也未有比较方便的制造TCP建连、断连流量的工具,笔者利用空闲时间,基于Python写了一个tcpping2工具,可以实现一些高级功能,详细可以参考github的工具主页[1]基本功能连接指定的目标和端口(可指定本地源IP和源端口)支持以FIN和RESET方式断开连接默认情况下和linux下的ping类似,会持续的进行连接,可以通过-c参数来制定连接的次数可输出日志文件,方便关闭程序后查看历史连接情况还支持一些额外的参数,如-i可以调节连接的间隔,-D可以设置延迟发送FIN或RST包程序的基本逻辑以给定的参数构建五元组,使用TCP去连接目标记录连接时间,打印在标准输出上,同时记录到同名的日志文件中(若指定了-c参数)循环执行,或者在执行COUNT(给定参数)次退出在收到TERM或INT信号后强制结束,同时输出此次运行的统计结果命令示例连接指定8.8.8.8的53端口python tcpping2.py 8.8.8.8 53指定本地源地址和源端口10086去连接8.8.8.8的53端口python tcpping2.py -H 30.11.212.23 -P 10086 8.8.8.8 53指定连接10次目标地址,同时输出日志文件到当前目录下python tcpping2.py -c 10 -l 8.8.8.8 53以RESET断开连接python tcpping2.py -R 8.8.8.8 53间隔1秒进行一次探测python tcpping2.py -i 1 8.8.8.8 53最佳实践这个工具适合用于那些可以稳定复现的问题现场,根据我们的问题排查经验,绝大多数场景都是客户端和服务端建连出现了问题,比如SYN丢包、SYN_ACK丢包、特定五元组丢包等等,针对上面的场景,tcpping2提供了诸如-H、-P、-L等参数,可以非常灵活的决定使用什么源端口来进行连接。特别是“特定五元组丢包”这个场景,因为丢包的五元组不可知,使用这个工具传入-L参数后,tcpping2会快速的从指定的起始源端口开始,不停地自增源端口来连接,可以快速的发现不通的五元组,随后再使用-H和-P参数来固定这个不通的五元组,持续的打问题流量,以便在链路上通过抓包、流统来缩短问题链路,定位具体的问题模块。[1] https://github.com/huigher/tcpping2
业务系统性能压测的最佳实践 压测工具的选择 目前主流的压测工具有 ab Jmeter 阿里云PTS 如何来选择呢,我们建议如果是简单压测,可以直接使用ab来进行,它可以通过一条命令来快速的发起指定并发数的请求。但如果需要进行复杂的压测,建议使用后两者: Jmeter是开源的压测工具,可以实现非常复杂的压测需求,比如设定一个包含很多URL的场景、配置一个施压集群来发起压测测试等等,而阿里云PTS服务,相较于Jmeter增加了许多独特的功能,比如: 施压流量来自真实CDN节点,最大限度模拟真实流量的路径 纯SaaS,无需额外安装和部署 兼容Jmeter脚本,可以平滑的复用之前的jmx脚本文件 配置界面所见即所得,对于新手非常友好 所以,我们建议大家根据实际的压测需求来选择压测工具。另外,如果您对PTS感兴趣,可以前往PTS控制台进一步了解。 阿里云上业务压测流程上的注意事项 施压前 对业务系统容量有一个预估,比如QPS、并发用户量 确定好压测时间和压测环境,尽量不要直接对生产环境压测,避免影响业务 若链路上存在SLB,建议至少使用4台施压机来压测,施压机越多,SLB的转发会越均衡 合理安排施压机的压测能力,若单台施压机无法满足施压需求,可以构建Jmeter施压集群或者使用PTS进行压测 若链路上存在安全模块,如高防、Web应用防火墙等,建议对施压机的源IP添加白名单,避免施压机被安全模块拦截 施压中 密切关注被压测的模块的各项监控 当出现瓶颈时就可以考虑停止或延迟若干分钟停止压测,避免产生无效的压测流量 施压后 结合业务数据对施压结果进行分析,确定系统是否可以达到预期的目标 出现瓶颈后,需要及时优化业务程序 压测结果相关 如何判断目标系统是否出现瓶颈? 判断瓶颈的方法非常多,比较简单的方法之一是增加并发用户数量,查看目标系统的TPS是否同步上涨,如果没有出现增加,甚至出现了下降,说明业务系统处理每个请求的时间变长,可以近似理解为此时的业务系统就出现了瓶颈。 并发用户、RPS、TPS如何解读 名词的定义千差万别,但归根结底的形容都是类似的,以下可以作参考: 并发用户:施压机上同时去请求的用户数量,比如500个并发用户,配置的施压目标是串行的两个URL,那么施压过程中,就会有500个客户端,不停地去请求URL 1和URL 2,用户之间的请求互不影响,并发的请求,而每个用户是先请求URL 1,得到结果后在请求URL 2,得到结果后再循环请求URL 1,以此类推。 RPS:在PTS中,RPS被定义为施压机每秒发出的请求数 TPS:在PTS中,TPS被定义为压测目标每秒执行的事物数,若目标系统是基于HTTP(S),可以理解为每秒能够执行多少HTTP请求。 那么有的同学会问了,TPS和RPS有什么差别呢?在正常情况下,RPS的数值近似等同于TPS,TPS对于一个系统来说,是恒定的,但只要施压机性能还够,RPS还可以提升,只是此时目标系统已经无法处理,可能会出现连接失败、请求超时等异常的响应,那么此时,RPS是大于TPS的,需要及时停止施压并分析目标系统为何会出现异常
概述 近年来,随着云计算越来越普及,更多的人可以随时随地的使用云资源,享受着云计算带来的红利。越来越多的企业、网站也把云平台当做主要的业务环境,其中云网络和传统的IDC会有一些差别,如何更平滑的将IDC的资源迁移到云上,如何更好地利用云网络的特性服务业务,也是一个重点要探讨的问题点。 本文结构 本文先分析传统IDC的网络特点,然后重点介绍了云计算网络的特点,最后列举了几种网络上云的方案,让大家可以做详细对比,选择自己合适的上云方法。 从IDC说起 传统的IDC架构,无外乎就是“接入层”、“汇聚层”、“核心层”,每一层工作职责分明,便于管理和维护。IDC内部网络肯定是有冗余的,防止某个设备出现异常导致单点故障,同时对外的出口在一个数据中心往往有一个或多个,规模大一些的IDC会同时接入多家运营商,保证和对端的通信质量。 在传统的IDC内部署业务时,往往是在一个IDC内构建业务,若对业务可用性要求高一些,会在同一个城市的另外一个IDC部署一套相同的业务,作为主备IDC。但这样操作的灵活性较差,若是业务需要扩容机器,往往采购过程会长达几周甚至几个月,同时使用IDC的成本也会较高,不利于中小企业的发展。 云计算网络 云计算的网络,充分的贯彻了云化的概念,包括阿里云在内的几家云计算厂商,不约而同的提出了“专有网络VPC”的概念,相较于传统IDC的网络,他有着让人着迷的特性: 租户间网络隔离 弹性伸缩 自主分配IP地址 他克服了传统IDC的劣势,随时随地都可以新建主机,同时将IP网段划分的自主权完全交给了用户,用户可以根据自己的业务需求更好的来对生产网进行地址划分,同时还保证了租户间的网络隔离,进一步提高了安全性。 我们打个比方,在传统的IDC内,我们要规划三个环境:生产环境、预发环境、开发环境,这三个环境很有可能都是在同一个网段内,无法做到严格隔离,相互之间会受到干扰,但是在云上,我们可以创建三个专有网络,它们的网段甚至都可以配置成一致的172.16.0.0/12,保证网络环境的一致性,同时这三个专有网络又是隔离的,保证了整个运维环境的稳定性。 构建混合云 说了这么多云网络的好处,有些同学就会问了,我的业务在IDC已经跑了一段时间,没办法一下子就迁到云上,有什么好的办法可以平滑的迁移吗?答案当然有,就阿里云目前的上云服务,我们提供了多种网络上云的方案。 VPN网关 VPN网关是一款基于IPSec VPN和SSL VPN的产品,数据进行加密传输,保证数据安全可靠,可以在短时间内打通两个网络,构建一个虚拟的网络环境的产品。 下面这张图说明了VPN的一个典型应用:通过SSL VPN可以将某一台终端快速的接入VPC网络,通过IPSec VPN可以让一个网络和云上VPC网络快速内网打通,为迁云提供一个过渡环境。 智能接入网关SAG 前面提到的VPN网关,一般要求云下的网络内部必须要有VPN硬件设备,来和云上的VPN网关构建链接,但如果云下网络内没有相关硬件,要怎么办呢?阿里云目前提供了一站式快速上云的产品智能接入网关SAG,无论是小型分支或门店,还是集团总部网络,都可以选择不同规格的SAG进行快速上云,阿里云提供设备,加电就上阿里云。在SAG覆盖的网络内部,可以快速的、安全的访问云上VPC的资源,快速搭建一个混合云网络。 专线 前面说了两种构建混合云的方式,主要的思路是基于现有的公网链路进行数据传输,但公网链路难免会有抖动,对于一些敏感业务,我们推荐使用专线构建混合云,保证混合云的通路质量。 下面是一个典型的场景,VBR可以理解为传统IDC中的CPE接入交换机,通过专线连接了IDC的接入交换机和云上的虚拟边界路由器VBR,打通两侧的网络,构建稳定的混合云。 该选择什么方式上云? 说了这么多构建混合云的方法,大家可能问我该选择哪一种方式上云呢?其实没有绝对的答案,需要根据业务的实际情况来定。VPN网关、智能接入网关、专线,他们的成本依次增加,当然网络的健壮性、可靠性也是依次增加,得需要根据整个项目的成本、要求来综合考虑使用什么形态上云或构建混合云。 一般来说,VPN网关适合临时测试以及管控的备用线路;智能接入网关适合有许多门店的场景;而专线就是用来稳定的链接两个网络的终极方案。前两者跑在公网上,会受到公网链路的抖动影响,后者采用了专用链路,独享通信链路,避免了公网抖动给业务造成的影响。 终极形态 - 云企业网 最后有的同学可能会想在整个迁云过程中,需要多个网络使用多个上云的方案共存,是否可以更智能化、更简便的管理云上云下网络呢?答案就是云企业网。它可以非常方便快速的打通VPC-VPC、VPC-本地网络,通过自动路由分发及学习,提高网络的快速收敛和跨网络通信的质量和安全性,实现全网资源的互通。是目前来说比较理想的智能组网方式。同时支持上述三种上云方案,帮助大家平滑的上云。 下图展示了云企业网同时加载专线、VPN网关、智能接入网关三种产品,构建企业级混合云的拓扑,一些小的分支站点,可以使用VPN网关、智能接入网关快速上云,而一些已经部署业务的IDC短期内无法版迁到云上,则可以通过专线和云网络打通: 参考文献 IDC通过VPN网关上云方案 IDC双专线静态路由冗余上云方案 [IDC通过专线和智能接入网关主备方式上云方案](
概述 目前阿里云ECS部分机型可以配置多张网卡(一张原生网卡+若干张弹性网卡),用来实现业务上对于系统内多网卡的需求,详细可以参考 [【新功能】弹性公网IP绑定弹性网卡新功能及最佳实践 ](https://yq.aliyun.com/articles/629383?spm=a2c4e.11163080.searchblog.9.30812ec15GS8P5 )。在使用过程中,可能会遇到多个弹性公网IP绑定到一个ECS上的原生网卡、弹性网卡上后无法通信的问题,这个时候需要检查系统内的路由配置和策略(policy rules)。 从route路由表说起 默认情况下,linux系统中都会有一张路由表,使用route或ip route就可以进行查看,如 default via 172.16.127.253 dev eth0 169.254.0.0/16 dev eth0 scope link metric 1002 172.16.112.0/20 dev eth0 proto kernel scope link src 172.16.115.242 以上起到关键作用的是第一行路由,它声明了一条默认路由,从eth0网卡中出去,目标是网关172.16.127.253。在只有一张网卡的实例中,这样的配置是没有问题的,但如果有多张网卡的情况下,入流量会从不同的网卡中进入实例,但出流量都会从eth0网卡中出去(譬如从外部ping 绑定在弹性网卡上的弹性公网IP,ICMP request包会从实例的弹性网卡中进入实例,而实例回复的ICMP reply包会从eth0中流出),导致出入路径不一致,会造成一系列的问题,如单网卡流量瓶颈、负载不均衡等问题。 真实的路由表 在linux系统中,实际上是有255张路由表的,默认使用的是254这张路由表,使用route命令展示的即是这张表中的路由条目。除此之外的路由表我们可以对它进行适当的配置,以实现自定义或高级需求。 我们可以使用ip route list table [table index]来查看系统内的路由表条目,如 [root@xiaoling-hz-test ~]# ip route list table 254 default via 172.16.127.253 dev eth0 169.254.0.0/16 dev eth0 scope link metric 1002 172.16.112.0/20 dev eth0 proto kernel scope link src 172.16.115.242 以上条目可以看到系统内默认的254号路由表内的条目,另外,还可以将index置为0来查看系统内所有的路由条目,这在需要循环遍历系统内自定义路由表时非常有用。 PS. table==253这张路由表被系统默认为是default表,建议使用1-252号路由表来添加自定义的路由条目。被linux预先定义的路由表详情可以查看系统内/etc/iproute2/rt_tables,以下是一个示例,可以看到253 - 255已经被系统定义: # # reserved values # 255 local 254 main 253 default 0 unspec # # local # #1 inr.ruhep 策略路由 为什么会需要多张路由表呢?这不得不提到策略路由,目前我们需要实现的功能是同进同出,保证从eth0口进来的包从eth0口出去,eth1进从eth1出,但弹性公网IP需要有一条默认路由来让ECS可以正常访问公网,但却又不能直接在默认路由表里添加两个目标网段是0.0.0.0,分别从eth0、eth1中出去的路由,这个时候策略路由就派上用场了,我们需要根据源地址进行策略选择。 简单来说就是配置两张路由表,路由表中分别有从eth0、eth1中出去的默认路由,在系统需要发包时,根据包的源IP进行选择,eth0的源IP就从包含eth0的路由表出去,eth1的源IP就从eth1的路由表走,从而实现同进同出。 linux系统内关于策略的索引是一个无符号的32位整型数值,所以理论上策略索引的数值可以达到4294967295之多,当然通常我们只需要使用个位数的索引条目即可。 具体的一个配置 以上说的都是理论,可能会有一些虚,下面我们来实际进行一个配置。我们特意选取阿里云官方尚未支持自动配置的镜像Ubuntu 16.04 64位来进行配置,以下是这个场景中相关参数: ECS实例网关地址:172.16.127.253 ==原生网卡信息== 名称:eth0 IP地址:172.16.116.38 绑定的弹性公网IP:47.99.42.x ==弹性网卡信息== 名称:eth1 IP地址:172.16.116.39 绑定的弹性公网IP:116.62.163.x 如何获得ECS实例完整的网卡信息 阿里云ECS控制台对于实例的网卡信息展示的不是很完全,建议通过API方法 DescribeNetworkInterfaces 来获取,若没有API调试环境,可以使用阿里云的API Explorer来调试,非常方便,以下是一个示例 URL ,点击后可以直接进入在线调试页面。 检查系统的默认配置 依次查看网卡、默认路由,可以看到系统内的配置残缺不全: root@iZbp14bxrlofsqs3d5dw43Z:~# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:16:3e:0c:72:5d brd ff:ff:ff:ff:ff:ff inet 172.16.116.38/20 brd 172.16.127.255 scope global eth0 valid_lft forever preferred_lft forever 3: eth1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000 link/ether 00:16:3e:10:aa:7e brd ff:ff:ff:ff:ff:ff root@iZbp14bxrlofsqs3d5dw43Z:~# ip route list table 254 default via 172.16.127.253 dev eth0 172.16.112.0/20 dev eth0 proto kernel scope link src 172.16.116.38 首先编辑/etc/network/interfaces文件,在最后加上关于eth1的定义,新添加的两行分别告诉系统在启动时自动拉起eth1、eth1的参数配置使用DHCP协议,详细的配置说明可以参考man interfaces: auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp auto eth1 iface eth1 inet dhcp 保存后执行systemctl restart networking重启网络服务,可以看到eth1网卡已经配置上IP地址了: root@iZbp14bxrlofsqs3d5dw43Z:~# ip address 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:16:3e:0c:72:5d brd ff:ff:ff:ff:ff:ff inet 172.16.116.38/20 brd 172.16.127.255 scope global eth0 valid_lft forever preferred_lft forever 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:16:3e:10:aa:7e brd ff:ff:ff:ff:ff:ff inet 172.16.116.39/20 brd 172.16.127.255 scope global eth1 valid_lft forever preferred_lft forever 接下去就需要配置策略路由了,针对这个场景,我们定义了以下信息: ==原生网卡== 使用路由表100,策略路由优先级索引为200 添加路由的命令(网关地址根据实际情况而定):ip route add default via 172.16.127.253 dev eth0 tab 100 添加策略的命令(源IP根据实际情况而定):ip rule add from 172.16.116.38 tab 100 priority 200 ==弹性网卡== 使用路由表101,策略路由优先级索引为300 添加路由的命令(网关地址根据实际情况而定):ip route add default via 172.16.127.253 dev eth1 tab 101 添加策略的命令(源IP根据实际情况而定):ip rule add from 172.16.116.39 tab 101 priority 300 执行以上命令后,系统内策略路由就已经配好了,通过相关命令查看的结果如下(已经略去无关信息): root@iZbp14bxrlofsqs3d5dw43Z:~# ip route list table 0 default via 172.16.127.253 dev eth0 table 100 default via 172.16.127.253 dev eth1 table 101 default via 172.16.127.253 dev eth0 172.16.112.0/20 dev eth0 proto kernel scope link src 172.16.116.38 172.16.112.0/20 dev eth1 proto kernel scope link src 172.16.116.39 root@iZbp14bxrlofsqs3d5dw43Z:~# ip rule list 0: from all lookup local 200: from 172.16.116.38 lookup 100 300: from 172.16.116.39 lookup 101 32766: from all lookup main 32767: from all lookup default 可以看到系统中增加了table==100、101的两张路由表,增加了200、300两个优先级的策略,分别指定eth0、eth1出入的规则,这样就可以做到一张网卡同进同出了。 持久化配置 以上的配置,除了网卡的IP信息可以持久化,其他的配置如路由、策略都会在重启后失效,可以把以上命令写到系统的配置文件中。理论上更优雅的方法是在/etc/sysconfig/network-scripts/ (CentOS) 、 /etc/network/ (Ubuntu)目录下将命令写到特定的文件中来解决这个问题,但无奈无法测试所有的场景,我采取了比较“暴力”的方法来统一的解决:将命令写到/etc/rc.local文件中,可以无视发行版。 比如本文中的场景,就可以将四条ip route/ip rule命令添加到rc.local中: root@iZabcdeZ:~# vim /etc/rc.local ip route add default via 172.16.127.253 dev eth0 tab 100 ip rule add from 172.16.116.38 tab 100 priority 200 ip route add default via 172.16.127.253 dev eth1 tab 101 ip rule add from 172.16.116.39 tab 101 priority 300 阿里云原生的做法 查看阿里云官方关于配置 ECS 实例的弹性网卡的说明,以下几种镜像是可以直接支持不需要手工配置: CentOS 7.3 64 位 CentOS 6.8 64 位 Windows Server 2016 数据中心版 64 位 Windows Server 2012 R2 数据中心版 64 位 实际上CentOS系统内部也是采用了类似的策略来实现的,使用ip route和ip rule命令可以查看到响应的策略,只是路由表索引和策略路由的优先级序号有些许不同,各位可以实际验证一下。 参考文献 https://www.linuxjournal.com/article/7291
概述 作为非常古老的协议(1971年首次提出,1980年首次应用),FTP在目前依然占有一席之地,但是由于目前IT业发展的非常迅猛,它和现在的基础架构产生了一些水土不服,本文旨在通过日常常见的案例,来介绍如何在阿里云上搭建FTP Server和FTP Client。 本文结构 本文先从FTP的协议分析,重点阐述了其中控制通道的两种连接模式,进而说明在阿里云上搭建FTP服务器可能遇到的问题,如被动模式下服务器公网IP无法获取、主动模式下无法连接等情况。 FTP协议摘选 控制通道与数据通道 在FTP协议中,通常有两个通道:控制通道和数据通道,前者用来传输控制命令,如协议握手、获得当前目录等操作,而数据通道顾名思义,就是用来传输文件数据之用,另外ls命令的结果通常也会通过数据通道来传输。 被动模式与主动模式 而被动模式和主动模式是针对如何建立数据通道而言的,这里的主动、被动是站在服务器的角度上描述的,他们的差别简单来说就是数据通道的TCP连接建立是由谁发起的,主动模式下服务器主动向客户端建立数据通道,而被动模式下,服务器是等待客户端来连接的。 这两个模式大家听到比较多的应该是被动模式,通常网上的文章都会建议客户端使用被动模式来连接,下面简单来说明一下两者的差别。 主动模式 主动模式的详细操作可以参考我之前写的一篇文章,这里节选部分信息: 以下是一次完整的交互,客户端为47.x.x.224,服务端为112.x.x.7。 对他的分析可以参考之后的一张交互图,需要重点说明的是交互图中标红的一行,这一行可以理解为数据通道的实际体现(抓包中倒数第4个包,由于我只过滤了FTP协议的包,TCP的控制包没有显示出来,这个包实际上是建立了一个新的TCP流来传输的),在这之前PORT命令是由客户端发出的,这一条报文为 PORT 47,x.x,224,39,16 其中前四个数字为十进制的IP地址,在主动模式下为客户端的IP地址,最后两个数字为端口,由于端口需要两个字节表示,所以在这里的计算方式为39x256+16=10000,那么这一条报文的意思就是告诉服务器,“我计划使用10000端口作为监听的端口,我的IP地址是47.x.x.224,你可以来连接我了进行数据传输了”。 被动模式 而被动模式最显著地差别就是在于如何建立数据通道,主动模式下是服务端去连接客户端传输数据,而被动模式下,是客户端去发起连接,向服务器建立连接。 以下是一次被动模式下的交互,客户端为30.x.x.85,服务端为47.x.x.255。 可以看出最大的区别在于红框中的命令,这一次从PORT改为PASV,之后服务器回复一条被动模式的信息,其中同样是6个数字:IP地址+端口号(端口号的计算方法和主动模式中一样),只是这一次换做服务器告诉客户端,“你现在可以连接我的47.x.x.255的47185端口了”。 这里特别提示一下,从下面的抓包截图中可以看出服务器回复的PASV响应中IP地址并不是上面提到的47.x.x.255,而是一个私网地址172.x.x.241,原因我们会在# 专有网络ECS内部署FTP Server #这一节中提到。 被动模式出现的原因 为何会同时存在被动模式和主动模式呢?在互联网的初期,由于各个终端使用的都是标准的公网IP地址,彼此时间可以互相通信,互相可见,所以在发起TCP连接时,无所谓是哪一方先发起。但随着互联网的高速发展,人们发现IP地址已经不够用了,进而诞生了NAT技术,这就在通信的双方之间插入了一个中间人,在NAT设备之后的终端对于对方是不可见的,所以只能由它来发起连接。 所以为了迎合NAT技术,FTP协议里就出现了被动模式,而且由于目前大部分FTP客户端都是在NAT设备之后,所以绝大多数情况下只能使用被动模式(当然在一些特殊的架构里主动模式还是有一定的用武之处,本文最后一节将会提及) 专有网络ECS内部署FTP Server 阿里云ECS的网络 目前阿里云ECS的网络类型有两种:经典网络与专有网络。对于经典网络的ECS来说,系统内有两张网卡:eth0和eth1,eth0为内网网卡,eth1为外网网卡。在系统内内网和外网的IP地址是静态写在配置文件里的,系统内可以直接看到。而专有网络的ECS系统内只有一张网卡eth0,所有的流量都由这张网卡出入,系统内部无法看到ECS自身的公网IP/弹性公网IP。 这就直接导致了一个问题:由于目前的网络环境下大部分客户端都会使用被动模式来连接,但是被动模式下服务器需要告诉客户端连接的IP地址,而专有网络下的ECS无法直接看到外网IP,那么就导致FTP Server将自己的私网IP告诉了客户端,客户端收到了一个私网IP自然就无法进行连接。而list、文件传输都需要走FTP协议的数据通道,所以最终导致了专有网络下搭建的FTP Server无法让客户端正常的传输数据。 解决方案 解决办法也很简单:主动告知FTP Server程序系统的外网地址,由于市面上FTP Server众多,各家的设置方式不尽相同,以下说明几个常见的FTP服务器的设置方法: vsftpd 在vsftpd的配置文件中,加入以下两行,其中替换为ECS的弹性公网地址: listen_ipv6=NO pasv_address=<EIP> IIS 打开IIS,选择服务器(注意不要选择“网站”下的ftp站点),打开右侧的“防火墙支持”,在“防火墙的外部IP地址”其中填入ECS的弹性公网地址即可。 Serv-U(最新版本) 打开管理控制台,依次选择“域”-》[对应的域]-》“域详细信息”-》“监听器”-》21端口的监听器,点击“编辑”,将ECS的弹性公网地址填入弹出的对话框中 One more thing 在撰写本文时,我发现一些客户端似乎已经提供了智能化的规避方案,如lftp提供以下参数,当出现类似的问题时会尝试使用控制通道的服务器IP地址进行数据通道的连接。 ftp:fix-pasv-address (boolean) if true, lftp will try to correct address returned by server for PASV command in case when server address is in public network and PASV returns an address from a private network. In this case lftp would substitute server address instead of the one returned by PASV command, port number would not be changed. Default is true. 另外,FileZilla也默认开启了修正被动模式下数据通道IP地址的功能: FTP的端口与ECS的安全组 ECS的安全组 阿里云上的安全组相当于一个方向墙,类似iptables或Windows防火墙,可以根据协议、源/目的IP、源/目的端口来实现特定流量的放行、拦截。在# 被动模式和主动模式 #一节中我们已经提到了FTP除了21端口以外,根据不同的模式在数据通道下还会使用到特定的端口,所以需要在ECS的安全组中放行。 FTP端口的选择策略 关于FTP两种模式下的报文交互,前文已经做了阐述,但还未说明程序是如何选择端口的,下面简单说明一下: 被动模式 - 控制通道 数据通道 客户端 主动连接服务器,随机选择本地源端口 主动连接服务器,随机选择本地源端口 服务端 被动接受客户端连接,本地默认监听21端口 被动接受客户端连接,本地随机监听端口 注:被动模式下服务端数据通道监听的端口理论上可以通过FTP程序的配置文件修改 主动模式 - 控制通道 数据通道 客户端 主动连接服务器,随机选择本地源端口 等待服务器的链接,本地监听一个随机的端口(注) 服务端 被动接受客户端连接,本地默认监听21端口 主动链接客户端,随机选择本地源端口 注:主动模式下客户端数据通道监听的端口理论上可以通过FTP程序的配置文件修改 那么可以得出在ECS上部署FTP Server,若对于安全组有严格的进出限制,那么需要放行: 控制通道监听的端口,用来接收客户端的控制命令,默认为21端口 被动模式下建议指定一个端口范围,以便在安全组中可以放行;主动模式下由于安全组出方向是默认放行的,所以理论上不需要额外的配置 如何指定被动模式下服务器数据通道监听的端口 这里依然以常见的FTP程序为例: vsftpd在配置文件里添加以下信息,其中端口的起止范围根据实际需求修改,然后重启vsftpd pasv_enable=YES pasv_min_port=10000 pasv_max_port=10020 IIS同上文提到的类似,在“FTP防火墙支持”中填入数据通道的端口范围即可 Serv-U 打开左侧“全局”菜单,选择“限制与设置” 选择右侧“设置”选项卡,页面拉到下方,可以看到“网络设置”中有一项“PASV端口范围” 根据实际情况填写即可 主动模式适用的场景 前文提到在一些特定的场景下,确实只能使用主动模式,比如说IDC和银行侧相连,银行出于安全考虑仅允许21端口入方向的流量,其余的端口都是封禁的,那么就无法使用被动模式,只能退而求其次使用主动模式。那么此时对于客户端的要求可以总结为下列几点: 客户端必须有自己的公网IP,如ECS 弹性公网IP,或者可以使用阿里云NAT网关的DNAT功能,将公网IP的部分端口映射到ECS内部的端口上 客户端上强制使用主动模式(目前大部分客户端会优先使用被动模式) 由于主动模式下数据通道是由服务器发起连接,需要在ECS安全组中放行对应的端口 客户端系统内若无法直接看到公网IP(如专有网络下的ECS、DNAT模式下,依然需要对FTP客户端进行配置,告知客户端可以连接的公网IP地址)。 关于第二、三、四点,其实和被动模式下PASV命令的响应类似,都需要在程序中作相应的配置才行,但由于这个场景下这个需求确实比较小众,我只收集了少量的客户端配置方法,其他的客户端配置方法请和程序的提供商寻求技术支持。 配置主动模式的端口和公网IP地址 lftplftp程序非常友好,在man手册中就已经说明了配置方法,依次是被动模式开关、主动模式下的IP地址、主动模式下数据通道监听的端口范围: ftp:passive-mode (boolean) sets passive FTP mode. This can be useful if you are behind a firewall or a dumb masquerading router. In passive mode lftp uses PASV command, not the PORT command which is used in active mode. In passive mode lftp itself makes the data connection to the server; in active mode the server connects to lftp for data transfer. Passive mode is the default. ftp:port-ipv4 (ipv4 address) specifies an IPv4 address to send with PORT command. Default is empty which means to send the address of local end of control connection. ftp:port-range (from-to) allowed port range for active mode. Format is min-max, or `full' or `any' to indicate any port. Default is `full'. FileZilla 主动模式相关配置可以直接在菜单栏中的“编辑”-》“设置”中配置 客户端的连接行为可以在每个FTP会话中单独配置 参考 1、阿里云金融云下FTP主动模式的讨论
Signature的生成方法 将get中提交的参数(除了aks以外所有的参数)按照一定的组成规则拼成一个字符串,前面再加上GET&/&组成StringToSign,然后对StringToSign做 HMAC计算,以Access Key Secret+一个“&”号为HMAC计算的key,最终算出的字符串就是Signature。 排查思路 若碰到反馈Signature错误,可以排查以下几点: 在构造“StringToSign”时是否对key值做了A-Z的字典排序 是否将所有的参数都放入了StringToSign 计算前是否对StringToSign中的值做了urlencode,即将一些特殊的字符替换成类似%3D这样的字符串,如“=”需要替换成“%3D”,“/”要替换成“%2F”等,参见下述说明:a、 对于字符 A-Z、a-z、0-9 以及字符“-”、“_”、“.”、“~”不编码; b、 对于其他字符编码成 “%XY” 的格式,其中 XY 是字符对应 ASCII 码的 16 进制表示。比如英文的双引号(”)对应的编码就是 %22c、 对于扩展的 UTF-8 字符,编码成 “%XY%ZA…” 的格式;d、 需要说明的是英文空格( )要被编码是 %20,而不是加号(+)。 是否使用了指定的算法,目前Signature需要使用HMAC-SHA1算法,在Java中通过Mac mac = Mac.getInstance("HmacSHA1");来获得HMAC-SHA1算法的对象 检查HMAC-SHA1运算时是否是将Access Key Secret+&作为key(如Access Key Secret为abc,那么使用“abc&”作为运算的key),运算过程编码使用UTF-8编码 以下是一个例子 1、 比如说要查询可用的镜像,先加入除了Signature以外所有的参数(值被忽略): [ImageOwnerAlias, SignatureVersion, Action, Format, PageSize, SignatureNonce, Version, AccessKeyId, SignatureMethod, RegionId, Timestamp] 2、 对它们进行字典排序: [AccessKeyId, Action, Format, ImageOwnerAlias, PageSize, RegionId, SignatureMethod, SignatureNonce, SignatureVersion, Timestamp, Version] 3、 挨个将它们拼接在一起(注意这个时候Timestamp中的值就已经经过了urlencode了,将“:”替换为“%3A”): AccessKeyId=6olc8au16tjr574v222c923p&Action=DescribeImages&Format=XML&ImageOwnerAlias=system&PageSize=10&RegionId=cn-hangzhou&SignatureMethod=HMAC-SHA1&SignatureNonce=352f98b6-5fbe-489c-b8a4-5d484939a8d5&SignatureVersion=1.0&Timestamp=2015-09-12T07%3A45%3A58Z&Version=2014-05-26 4、 在前面加上GET&/&: GET&/&AccessKeyId=6olc8au16tjr574v222c923p&Action=DescribeImages&Format=XML&ImageOwnerAlias=system&PageSize=10&RegionId=cn-hangzhou&SignatureMethod=HMAC-SHA1&SignatureNonce=352f98b6-5fbe-489c-b8a4-5d484939a8d5&SignatureVersion=1.0&Timestamp=2015-09-12T07%3A45%3A58Z&Version=2014-05-26 5、 再对整个字符串做urlencode,可以看到其中的“/”、“=”和参数键值对中的“&”都已经被替换,注意最前面的GET之后的两个“&”并没有被替换: GET&%2F&AccessKeyId%3D6olc8au16tjr574v222c923p%26Action%3DDescribeImages%26Format%3DXML%26ImageOwnerAlias%3Dsystem%26PageSize%3D10%26RegionId%3Dcn-hangzhou%26SignatureMethod%3DHMAC-SHA1%26SignatureNonce%3D352f98b6-5fbe-489c-b8a4-5d484939a8d5%26SignatureVersion%3D1.0%26Timestamp%3D2015-09-12T07%253A45%253A58Z%26Version%3D2014-05-26 6、 然后构造HMAC运算的key,很简单,将Access Key Secret再加上一个“&”即可: IamAccessKeySecret& 7、 扔给电脑做HMAC-SHA1运算,然后算出来一个最终的字符串: 53wPekiWxh45TgPxVb4bkXrzZ6M= 8、 这个字符串就是最终的Signature值,然后将其加入到HTTP get请求中,组成最终的get参数字符串,特别要注意Signature加入后需要确认其中的特殊符号如“=”是否被转换为%xx的字符串。 ImageOwnerAlias=system&SignatureVersion=1.0&Action=DescribeImages&Format=XML&PageSize=10&SignatureNonce=352f98b6-5fbe-489c-b8a4-5d484939a8d5&Version=2014-05-26&AccessKeyId=6olc8au16tjr574v222c923p&Signature=53wPekiWxh45TgPxVb4bkXrzZ6M%3D&SignatureMethod=HMAC-SHA1&RegionId=cn-hangzhou&Timestamp=2015-09-12T07%3A45%3A58Z 9、 然后就可以向ECS的API服务器发送请求了 参考文档 1.官网的生成Signature文档:https://help.aliyun.com/document_detail/25492.html
PermitRootLogin的可选项 众所周知,sshd_config是sshd的配置文件,其中PermitRootLogin可以限定root用户通过ssh的登录方式,如禁止登陆、禁止密码登录、仅允许密钥登陆和开放登陆,以下是对可选项的概括: 参数类别 是否允许ssh登陆 登录方式 交互shell yes 允许 没有限制 没有限制 without-password 允许 除密码以外 没有限制 forced-commands-only 允许 仅允许使用密钥 仅允许已授权的命令 no 不允许 N/A N/A 以上选项中,yes和no的功能显而易见,只是很粗暴的允许、禁止root用户进行登陆。without-password在yes的基础上,禁止了root用户使用密码登陆。 forced-commands-only的功能 目前看就只有forced-commands-only这个参数还不明了,网上的参考资料仅有以下说明: If this option is set to “forced-commands-only”, root login with public key authentication will be allowed, but only if the command option has been specified (which may be useful for taking remote backups even if root login is normally not allowed). All other authentication methods are disabled for root. 大体上的意思是设置了forced-commands-only之后,root用户仅允许使用密钥登陆,然后只允许执行在command中允许的命令,这个模式通常用来供定期需要使用root用户登陆,但是只需要执行规定的脚本、命令的情形,如定期备份等周期性、固化的操作。但这其中的command到底是如何指定的,这里并没有说明,本着打破砂锅问到底的态度,翻遍了各种资料,终于找到了command这个选项该填到的地方:那就是/root/.ssh/目录下的authorized_keys文件。默认情况下authorized_keys的文件类似下面这样子: ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAt0BETg9J6hZb5Kqxy+yfNtKHfwxUELz7PqGtGiM5eNb8DHC8kj02SCFoql5rpaecMGybWRiSK8/k+EsK7TMgd4O+p6WkNyLD3WZrmVzUEPaxAdYf1eeCQooTJ+B1TKXDNlF9t8xTVsHd67HmPWYU6i3+kaDSX7cbrz2ds2zUGSozj1UQ8AJDJMbGOqpjs3nVh2EpSDgY7znqmUDnygVPiM4c3OfEzs5iCxVd4ggpPhH8d0bwy8RmPsooxJYUY4rE1C5iWCvB7P810yUFB0OilxiX9AfZa9shC3n5bqaX0ioY1eC44hFFPL602fJyKMj6w/zxN5aIeFO03Sl9+FU4YQ== root@iZ23wan41azZ 那么command就需要加载文件的开头: command="/bin/ps" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAt0BETg9J6hZb5Kqxy+yfNtKHfwxUELz7PqGtGiM5eNb8DHC8kj02SCFoql5rpaecMGybWRiSK8/k+EsK7TMgd4O+p6WkNyLD3WZrmVzUEPaxAdYf1eeCQooTJ+B1TKXDNlF9t8xTVsHd67HmPWYU6i3+kaDSX7cbrz2ds2zUGSozj1UQ8AJDJMbGOqpjs3nVh2EpSDgY7znqmUDnygVPiM4c3OfEzs5iCxVd4ggpPhH8d0bwy8RmPsooxJYUY4rE1C5iWCvB7P810yUFB0OilxiX9AfZa9shC3n5bqaX0ioY1eC44hFFPL602fJyKMj6w/zxN5aIeFO03Sl9+FU4YQ== root@iZ23wan41azZ 这样就完成了配置,使用这个公钥登陆的用户,只有执行/bin/ps的权限,而且没有交互的shell,只能一次性的执行完command中的命令。 那么问题来了 细心地朋友可能发现了,因为没有交互式shell,所以command中只能添加一条命令,如果想要执行多条命令应该怎么办呢?这个时候就是伟大的shell脚本登场的时候了,把该执行的命令,全部封装到shell脚本中即可,然后再command中写上脚本的绝对路径即可。譬如,例子脚本hi.sh: echo This is huigher speaking echo Now is `date` 那么执行的实际执行的效果就是(ssh连接时对ssh连接的目标做了alias): [root@test .ssh]# ssh huigher This is huigher speaking Now is Sat Oct 29 16:13:36 CST 2016 Connection to 121.40.xxx.xxx closed. 光说不练假把式 说了这么多,可能各位还是有点云里来雾里去,那么就实战一下吧 配置公钥与密钥 由于forced-commands-only模式,只允许密钥的方式登录,那么就需要先配置公钥和密钥。 在本地电脑上,使用命令ssh-keygen -t rsa来生成一对密钥和公钥,过程中需要选择密钥存放的目录(/YourPath/YourPrivateKey)和密码(本例中为空) 将生成的公钥放置到远端服务器上,将公钥附加到~/.ssh/authorized_keys中,如cat id_rsa.pub >> ~/.ssh/authorized_keys 确保~/.ssh/目录的权限700,~/.ssh/authorized_keys文件的权限是600 修改/etc/ssh/sshd_config文件,确保其中填写了以下几行 RSAAuthentication yes PubkeyAuthentication yes 设置forced-commands-only相关参数 修改/etc/ssh/sshd_config文件,添加以下一行: PermitRootLogin forced-commands-only 打开~/.ssh/authorized_keys,找到最后添加的公钥一行,在最前面加上command="/YourPath/YourFile",如: command="/usr/hi.sh" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAt0BETg9J6hZb5Kqxy+yfNtKHfwxUELz7PqGtGiM5eNb8DHC8kj02SCFoql5rpaecMGybWRiSK8/k+EsK7TMgd4O+p6WkNyLD3WZrmVzUEPaxAdYf1eeCQooTJ+B1TKXDNlF9t8xTVsHd67HmPWYU6i3+kaDSX7cbrz2ds2zUGSozj1UQ8AJDJMbGOqpjs3nVh2EpSDgY7znqmUDnygVPiM4c3OfEzs5iCxVd4ggpPhH8d0bwy8RmPsooxJYUY4rE1C5iWCvB7P810yUFB0OilxiX9AfZa9shC3n5bqaX0ioY1eC44hFFPL602fJyKMj6w/zxN5aIeFO03Sl9+FU4YQ== root@iZ23wan41azZ 收尾及测试 重启sshd进程 在客户端上使用ssh进行连接:ssh root@xx.xx.xx.xx -i /YourPath/YourPrivateKey,如果嫌麻烦,也可以直接在客户端的.ssh文件夹下新建config配置文件,填写ssh主机的alias,这样就可以直接使用ssh huigher来进行连接了: Host huigher HostName 121.40.xxx.xxx Port 22 User root IdentityFile /YourPath/YourPrivateKey 配置完毕后的效果如下: [root@test .ssh]# ssh huigher This is huigher speaking Now is Sat Oct 29 16:13:36 CST 2016 Connection to 121.40.xxx.xxx closed. 除了command以外的命令,是无法执行的,况且连交互式shell都没有,更别提执行命令了,所以安全性是比较高的。 最后 本文撰写仓促,难免有错误和不足之处,若各位看官发现,请评论指出,不胜感激! 参考资料 https://www.novell.com/support/kb/doc.php?id=7007565 http://askubuntu.com/questions/449364/what-does-without-password-mean-in-sshd-config-file
JCLI说明文档,详细说明了使用方法和使用范例
阿里云的金融云集群,针对金融用户做了很多主动式的防御手段,其中包含主动禁止外网连接,安全组默认是黑名单的形式,必须要显式的放行对应的端口或IP才可以向外通信。 问题的起点 大多数FTP客户端默认使用被动模式来进行连接,在这个模式下,客户端会去连接服务器的某个大于1024的随机端口,但在金融云下使用就不太方便,如之前所述,金融云端口需要显式放开,但又不可能为了FTP将所有端口放开,所以就需要使用FTP的主动模式。 FTP的主动模式工作流程 以下是使用lftp的一个典型的主动模式下的抓包与流程: 主动模式中,最关键的一句命令是PORT,这一句话告诉了服务器要连去连接客户端的哪一个端口,这个端口必须要在客户端ECS所在的安全组中开放,同时,和被动模式一样,在服务器ECS所在的安全组中,也要放开21端口,以便让客户端正常连入。 配置实战 初始配置 两台服务器,一台作客户端,一台做服务器,模拟金融云环境下默认黑名单,两台服务器的安全组入方向上除了22端口以外没有放行任何规则: 使用lftp连接,可以看到登陆正常进行,但是会卡在类似ls等需要数据传输的命令上: [root@iZ94tued97gZ ~]# lftp 120.76.118.xxx lftp 120.76.118.xxx:~> user anonymous Password: lftp anonymous@120.76.118.xxx:~> ls `ls' at 0 [Connecting...] 说明数据通道没有正常建立。 主动模式的配置 假设我让10000-10002这三个端口来进行数据传输,需要以下配置 在客户端的安全组入方向上放行10000-10002这三个端口的TCP连接 在客户端的ftp软件上,强制使用主动模式,并设置使用10000-10002这三个端口进行主动模式的数据传输 1、客户端ECS安全组配置 打开ECS所在安全组,在其中放行10000-10002端口、入方向、TCP协议的放行规则: 2、服务器ECS安全组的配置 因为客户端要先主动连接服务器的21端口,所以在服务器侧放行21端口、入方向、TCP协议的规则: 3、ftp客户端软件的配置 这里以lftp为例,lftp的配置文件在/etc/lftpd.conf下,找到set ftp:passive-mode off,将前面的注释去掉,然后写上set ftp:port-range 10000-10002,让软件使用这三个端口来进行数据通信: ## if default ftp passive mode does not work, try this: set ftp:passive-mode off set ftp:port-range 10000-10002 稍等片刻(1-2分钟,让ECS安全组生效),然后测试,发现已经可以正常使用了! [root@iZ94tued97gZ ~]# lftp 120.76.118.xxx lftp 120.76.118.xxx:~> user anonymous Password: lftp anonymous@120.76.118.xxx:~> ls drwxr-xr-x2 004096 May 11 06:17 pub lftp anonymous@120.76.118.xxx:/> Misson Complete! 深一度挖掘 21?20?连接端口众说纷纭 根据众多FTP的资料,都是说默认倩况下使用21作为控制命令的连接端口,20作为数据的传输端口,但从未说客户端的端口是如何设置的,这里针对主动模式说明一下:1、客户端选择一个随机的端口(>1024),去连接FTP服务器的21端口2、传输命令数据(如登陆、PWD等)3、若需要数据传输(如ls),服务器使用20端口去连接客户端之前在PORT命令中告知的端口(下文会详细说明PORT命令) 为何不需要放行客户端的随机端口和20端口? 可以看到以上两个端口,都是系统主动向外连接的端口,在安全组中,默认出方向都是全部放行的(公共云和金融云都是如此,没有任何规则时,出方向的安全组是accept all),所以并不需要显式放行以上两个端口。 客户端如何告知服务器数据通信的端口? 为了探究原因,在配置前,我只启用了set ftp:passive-mode off,没有指明端口,然后再客户端进行抓包,逐条分析,发现在第725个包的时候,我看到了这么一条信息: Ftp: Request from Port 50301,'PORT 120,xxx,xxx,xxx,218,62' Command: PORT, Data port CommandParameter: 120,xxx,xxx,xxx,218,62 谷歌了一下,发现这个就是问题的原因:FTP主动模式下客户端并不是一定使用20端口进行通信的! FTP的这个PORT命令,意在告诉服务器,我要使用哪一个端口让你连接进行数据传输,后面跟的六个数字,前四个是客户端的IP,后两个就是实际的端口号,以上面的为例,客户端告知的端口号是218*256+62=55870,计算方式就是第一个数字乘以256,加上第二个数字,这才是真正的主动模式下服务器连接客户端使用的端口。 lftp和ftp的默认模式 这两款软件都是比较常用的ftp客户端,他们默认情况下使用的模式为:lftp(Linux):被动模式 ftp(Linux):被动模式 ftp(Windows):主动模式
缘起 致敬AliyunCLI,不可否认它非常好用,可以直接发起API请求,但是它需要依赖具体的SDK,且无法识别SDK中未包含的命令,这就让我想做一个更自由一些的CLI工具。这样只要在API更新后,直接就可以使用,不需要等待SDK的迭代更新。 她的优势 不依赖SDK,完全使用用户传入的参数,忠心耿耿的拼接URL(只要API支持,就可以使用,不需依赖SDK是否更新)。 受惠于Java优秀的跨平台性,此程序在不同系统下均可顺利运行,拿着一个jar文件可以跑遍Windows、Mac和Linux(Solaris当然也没落下~) 命令使用绝大部分兼容AliyunCLI,会用AliyunCLI的同学只需花几分钟就可以上手JCLI 对中文字符的支持较好,实际测试在windows和linux下都可以正确显示中文字符 对于可以支持https的服务,优先使用https,提高安全性 她的不足 上面第一点的优点,也是它的缺点,由于不依赖SDK,她无法对传入的Action做校验,会出现即使输入一个错误的Action,还是会生成URL的情况。但俗话说的好,“鱼和熊掌不可兼得”,在得到巨大的自由度下,牺牲一点健壮性还是可以接受的。 一些未知的代码bug……(尽管我已经很努力地去设想可能的情况,并做了容错,但估计还是有一些坑 Orz) 运行要求 JDK(JRE)>=1.6 jar包中一种引入了所有依赖的非JRE的jar包,理论上不需要再引入别的依赖包了 发布说明 压缩包里包含了Windows和Linux下封装好的脚本、详细的程序说明文档和jar本体,由于限制,附件只能上传一个,zip包中包含了以上所有的文件。 支持的服务 由于JCLI没有使用SDK,所以支持的服务是一次添加,永久使用(前提是公共参数和请求地址没有修改),目前经过测试,JCLI支持以下服务(目前还是比较笨的写死在程序中的,后续会考虑用更灵活的方法实现): C:\>jcli list Available ServiceType: RDS | DOMAIN | ESS | SLB | CMS | VPC | EMR | CDN | ACTIONTRAILS | ECS | ALIDNS | DRDS | RAM 备注 在运行时,若JCLI发现无法parse的参数,会主动打印出,方便debug:
前阵子AliyunCLI开源,同时转变为社区维护软件,这让我第一次有了自己做一个CLI的想法,同时想改进一下CLI的内部实现方法:AliyunCLI为了判断产品的endpoint和Action名字,需要依赖对应产品的SDK,在日常的工单处理中,用户在安装SDK时出现的问题比较多,且由于对代码和SDK不甚了解,排查起来有些困难,所以就想做一个纯粹的CLI,他只负责两件事情:一是签名的计算;二是将参数拼接成URL,显示出来或者直接提交。由于该工具使用Java编写,暂定名为JCLI。 公共参数Timestamp 这里的时间,并不是通常指的那个流逝的“时间”,而是人们定义的“时间”规则,如UTC、GMT等等国际上标准的时间表示和记录方法。阿里云API中,绝大部分产品都有“公共参数”这么一说,而其中也都有Timestamp这个参数,除了标识请求的时间以外,还有一个原因是为了防止中间人攻击,提高签名被猜出的难度。 根据阿里云的官方定义,这个值是请求时的UTC时间,日期格式按照ISO8601标准表示,当时搜了一下网上获取UTC时间的方法,发现五花八门,同时反编译了阿里云官方SDK,发现其中使用了以下的代码来获取UTC时间: SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); sdf.setTimeZone(new SimpleTimeZone(0, "GMT")); return sdf.format(new Date()); 时区ID 其中setTimeZone这个方法,还需要传入SimpleTimeZone这个对象,研究了一下发现使用TimeZone这个类的getTimeZone(String ID)也可以实现类似的方法,省去了一些开销,决定使用TimeZone。于是乎,需要先了解TimeZone工厂方法:TimeZone.getTimeZone。研究之后发现其中传入的getTimeZone这个方法的ID这个参数,查阅了JDK API,发现这个参数竟然可以传三种类型的参数:常规的地区/城市的格式(如America/Los_Angeles)、字母的时区简写(如CTS)和标准化的时间计算方法的名称(如GMT),可以传三种格式的时区字符串呀,很黄很暴力。由于对后两者传入方式不熟悉,打算使用完整的城市名称。联想到UTC时间,就是英国伦敦当地时间嘛,然后就去翻java的ZoneInfo文件,想找一下java关于地区/城市名字的定义,然后就在openjdk的文件夹(jdkmakesunjavazictzdata)里找到了描述文件,翻到了伦敦的完整名称:“Europe/London”。 DaylightSaving 当我开心的在SimpleTimeZone中填入Europe/London作为构造参数后,启动发现服务器总是提示Timestamp不对,比对了一下之前使用new SimpleTimeZone(0, "GMT")这种方式传入的时间,总是晚1个小时: 通过百度查询发现,伦敦地区的时间确实就是6点54分呀,为什么要求的时区要提早一个小时呢?百思不得其解,浏览的各种时区网站后,发现了一个重要的问题:夏令时! 国外某些地区为了更好地利用白天的时间,会将时间延后1个小时或0.5个小时,当前伦敦正在处于夏令时状态,所以时间较UTC 0时区的时间会延后1个小时,而Timestamp这个参数只要求是UTC 0时区,忽略夏令时的特殊情况,那么直接使用TimeZone.getTimeZone("GMT+00")就可以拿到要求的UTC 0时区的时间了。
2022年03月
2019年11月
2019年07月