【场景】搭建了一台CentOS虚拟机,并在上面搭了DOCKER,然后再DOCKER中安装Mysql。但只要将网络端口映射到宿主机上,那么外部网络就可以直接访问该数据。为此,我们需要使用防火墙(暂且不考虑mysql本身的账号权限控制)。
说到防火墙,CentOS有FirewallD,Ubuntu有ufw 。它们的用法和语法不尽相同,但有一点却是一致的,那就是他们底层都使用了iptables。 所以为了在不同发行版的linux下都能安全管理我们的服务器,有必要学学这个iptables
由于FirewallD和ufw本质都是基于iptables的,那么它们都会在iptables中添加一些规则,甚至定义一些链,为了不跟往后我们自己定义的规则相冲突,第一件事,便是停止并卸掉FirewallD和ufw对应的服务。
sudo systemctl stop firewalld //停止FirewallD
sudo systemctl disable firewalld //让FirewallD 不要随系统启动而启动
sudo ufw disable //停止并在系统启动时不启动ufw
iptables是一个linux下的防火墙工具,它能帮助我们基于规则进行网络流量控制。它可以做到,但不限于以下功能:
- 允许/拒绝某种协议的链接建立,比如TCP,UDP
- 允许/拒绝 来自某个ip的访问
- 允许/拒绝某个端口被访问
- ......
例如:来自192.168.2.31的访问,就要将其拒绝。这即是一条规则。
往往我们的安全策略不只一条规则,除了 来自192.168.2.31的访问,就要将其拒绝 这条规则之外,我们还有其它规则,比如: 来自192.168.43.22的访问,也要将其拒绝
甚至,我们可能还有多个互斥的规则,这多个规则,哪个规则先执行? 这就涉及到链这个概念。简单来讲,链就是将多个规则从上大小串起来的一个集合单位。规则按从上倒下依次进行匹配。

链条可以有多个。将多个链条再规整在一起的集合,叫做表。

在iptables中,有四张表:
- filter:这里面的链条,规则,可以决定一个数据包是否可以到达目标进程端口
- mangle: 这里面的链条,规则,可以修改数据包的内容,比如ttl
- nat:这里面的链条,规则,可以修改源和目标的ip地址,从而进行包路由。
- raw:这里面的链条,规则,能基于数据包的状态进行规则设定
上述四张表中,会内置一些链,且每个链,都有默认包处理策略,默认策略一般在链中的所有规则都没匹配时生效。 filter表中的链有:
- INPUT:对路由策略分派过来的包到达目标进程端口之前进行匹配并处理,后续会讲到细节
- FORWARD:对路由策略分派过来的包进行路由转发,后续会讲到细节
- OUTPUT:判断,从本地的目标进程端口处理好的包如何返回/要不要返回给请求方
【mangle表】中的链有:
PREROUTING:包在到达网口时,进行规则匹配
INPUT:含义同filter
FORWARD: 含义同filter
OUTPUT: 含义同filter
POSTROUTING: 包离开网口的时候匹配
【nat表】中的链有:
PREROUTING:含义同mangle
OUTPUT:含义同filter
POSTROUTING:含义同mangle
【raw表】中的链有:
PREROUTING:含义同mangle
OUTPUT:含义同filter
注意,虽然不同的表中有同名的链,但他们并不是同一个链,并且一个链只能引用同一个表中的链,不能跨表引用。平时我们的防火墙策略配置,即是在上述各个表的各个链中配置具体的规则。
虽然一个链中的规则是从上到下依次匹配,但多个表中的多个链,甚至同名链的之间的匹配优先顺序是啥?这就要看下图了:

PREROUTING 链是最先生效的,当数据包到达网口时,即开始工作。同时由于其在raw, mangle, nat表中都存在,其执行的优先顺序是:raw(PREROUTING) ----> mangle(PREROUTING)----> mangle(nat)
PREROUTING 一般用作对包进行目标地址修改。比如将该包的目标地址,修改为非本机的另外的网络ip,一般通过DNAT规则进行修改。
决定一个包该走哪个链。如果上述PREROUTING 链对包进行了目标网络ip更改。那么决策会觉得这个是一个需要转发的数据包,于是会将该包转发给 FORWARD 链。
否则, 该包会走INPUT链
FORWARD在各表中生效的优先顺序是:mangle(FORWARD) ----> filter(FORWARD) 处理路由决策派发发过来的包,到这里的包一般目标网络地址在PREROUTING链被修改过。
其生效顺序是: mangle(INPUT) ----> filter(INPUT) 处理路由决策派发发过来的包,到这里的包一般目标网络地址在PREROUTING链没有被修改过。
在目标进程端口接收到输入数据包后,输出的数据包,将在这里进行规则应用。OUTPUT链在各表中生效的先后顺序是: raw(OUTPUT) ----> mangle(OUTPUT) ----> nat(OUTPUT) ----> filter(OUTPUT)
前面铺垫了那么多,主要讲解了链的复杂生效时机,毕竟如果包最终都到不了这个链,那其中的规则配置也就没有意义。接下来,我们需要讲解,链中具体规则的设置和使用。
一个规则一般分为两大部分:
- 匹配: 即哪些数据包会命中这个规则,比如一个指定的ip,即是一个匹配规则
- 动作: 匹配到规则之后,需要做什么动作,是放行,还是拒绝。
iptables -t nat -nvL --line-numbers
-t 表示想要查看哪个表,这里查看的是nat表。iptables的所有命令,如果不指定-t,或者不写默认是filter表。
-L 表示列出该表所有链和所有规则
-v 详细显示,会将规则匹配的进出网口也列出来
--line-numbers表示给规则进行编号处理。编号能方便我们后续对规则进行修改、删除等操作。

如图所示,表头有以下信息:
总结来看,其实一个数据包本身就有源、目标的一些信息,而规则就是基于数据包本身属性的特点进行规则设定。
iptables -t filter -A INPUT -s 59.45.175.62 -j REJECT
-A 表示Append,其后紧跟的是链的名称,表示该条规则要被添加到哪个链中。
-s 表示包的来源ip即source。除了指定固定的ip外,我们还可以指定ip范围,比如59.45.175.0/24
-j 表示jump 也即是我们最终的动作,这里的动作是拒绝。
链尾的规则匹配优先级最低,如果前面有规则被匹配后,并将数据包进行了终态处理(比如:ACCEPT, DROP, REJECT),那么链尾的规则将永远不会被使用。
如果我们想要该规则优先匹配,可以选择将其放入链首,使用-I参数,表示insert。举例:
iptables -t filter -I INPUT -s 59.45.175.62 -j REJECT
想要删除已配置的规则,可以使用-D参数。举例:
iptables -t filter -D INPUT -s 59.45.175.62 -j REJECT
这种删法,要我们明确知道当初添加进去的规则是怎么写的。如果忘了,我们可以通过规则编号进行删除。在查看规则时使用参数 --line-numbers(例如:iptables -nvL --line-numbers),可以对规则进行编号,然后基于编号进行删除。

iptables -t filter -D FOWARD 1 //表示删除filter表中的FORWARD链的第一条规则
iptables -t filter -F INPUT
iptables -A OUTPUT -d 31.13.78.35 -j DROP
-d 表示destination,即所有返回给ip 31.13.78.35的数据包都直接丢掉,不回应。
iptables -A INPUT -p tcp -j DROP
-p表示protocol
iptables -A INPUT -p tcp -m tcp --dport 22 -s 59.45.175.0/24 -j DROP
由于要对端口进行精准匹配,所以先-m tcp 进行tcp module加载。
iptables -A INPUT -p tcp -m multiport --dports 22,5901 -s 59.45.175.0/24 -j DROP
--dport 是某个端口
--dports 是逗号分隔多个端口
链接状态有以下几种:
- NEW:新创建的连接
- ESTABLISHED 已经建立的连接
- RELATED:跟已经创建的连接相关的连接
- INVALID:非正常状态
- DNAT:如果一个连接其目标地址被nat表PREROUTING链中的规则修改了,即是这个状态
- SNAT:如果一个连接其源地址被nat表中的规则修改了,即是这个状态
示例:iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 即只对已经建立的连接和由此产生的相关连接进行放行。
有些版本的linux,对应的module不是conntrack,而是state。 对应指定状态的参数不是ctstate 而是--state。所以,上述写法在有些linux版本中需要替换成:
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
一般来讲,这些规则不可能单独出现,如果都不允许任何NEW状态连接建立,那哪来的已建立连接和相关连接?所以正确的做法一般是:
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT //这条规则允许已经建立的连接和相关连接
iptables -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT //新建链接如果是访问22号端口,则允许访问
一般每个链都有默认规则,即该链没有任何规则或者没有任何一条规则被匹配的情况下,对数据的放行策略是怎么样的。
Chain INPUT (policy ACCEPT)
...
Chain FORWARD (policy ACCEPT)
...
Chain OUTPUT (policy ACCEPT)
...
以filter表的三个链为例,默认是ACCEPT。 但是我们可以改变这个模型规则,比如默认规则就是DROP:
iptables -t filter -P INPUT DROP
iptables -t filter -A INPUT -i lo -j ACCEPT //在本地网络通信的所有包,都放行
-i 表示input 输入网口。lo表示本地的网络接口。
这里没有指定 -s地址(来源) 或 -d地址(目标),表示在回环网络上通信的所有端口都放行。这样我们本机的web service,访问本机的mysql数据库才不会有问题。当然一般INPUT的默认规则是ACCEPT,你不用配置上述的规则,只要没有其它规则去限制,那么本机回环地址之间的端口通信也是放行的,除非你对INPUT链默认开启了拒绝策略。
iptables -A OUTPUT -o wlan0 -d 121.18.238.0/29 -j DROP
上述配置含义:所有发给目标网口是wlan0 且 目标ip是121.18.238.0/29 地址的包,都会被丢弃。 -o 表示 数据包的目标网口。
在linux命令行中,使用 ifconfig ,就能看见当前已连接的所有网络接口。
上述规则配置,一般都是满足某某条件,做什么动作。除此之外,我们还可以配置,如果不满足某某条件,则做某个动作。
iptables -A INPUT -p tcp -m multiport ! --dports 22,80,443 -j DROP
这个不满足则的取动作,是通过感叹号来实现的。 上述命令的含义是:非22,80,443的端口,我们直接丢弃。
当然这条命令之前,应该要配置一条规则:
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
因为通过22或80建立的连接,可能会衍生出一些RELATED的连接,他们的端口可能不是22或80,那样也就被拒掉了。会导致通信出问题。
上述命令对iptable的操作,并不是永久生效的,机器重启后,对应配置会丢失。如果需要持久化,则需要以下命令进行保存。
【centos系统】
iptables-save > /etc/iptables-myrules.conf 这一步,每次修改后都要做iptables-restore < /etc/iptables-myrules.conf。从此之后,每次重启,系统会自动从 /etc/iptables-myrules.conf 恢复对应的iptables配置。这一步只需要做一次判断启动级别,命令为 runlevel ,是否为3或5。可以使用命令“init 3”修改启动级别为3.
运行命令ls /etc/rc.d/rc3.d -l 判断,是否有S99local -> /etc/rc.d/rc.local 的软链接,文件名可能不一样,但要有 /etc/rc.d/rc.local的软链接,有的是 /etc/rc.local,反正就是你要运行的rc.local的文件。
如果没有则建立软链接,ln -s /etc/rc.d/rc.local /etc/rc.d/rc3.d/S99local
如果有 /etc/rc.d/rc.local链接,则判断权限是否为可执行,如果不是,可添加权限chmod +x /etc/rc.d/rc.local
如果还没有执行,可能是你的rc.local代码本身就有问题。

【ubuntu系统】
sudo apt install iptables-persistent
所以,每次我们对iptables进行了任何改动,使用下面的命令,将当前生效的iptables配置,导出到/etc/iptables/rules.v4 和 /etc/iptables/rules.v6即可
sudo iptables-save > /etc/iptables/rules.v4 //如果添加了ipv4 规则,执行这步
sudo ip6tables-save > /etc/iptables/rules.v6 //如果添加了ipv6规则,执行这步
除了在现有的链中添加规则,我们也可以自定义链,自定义链可以帮助我们将一组规则收纳在一起,方便我们管理。比如:
iptables -t filter -N ssh-rules
iptables -t filter -A ssh-rules -s 18.130.0.0/16 -j ACCEPT
iptables -t filter -A ssh-rules -s 18.11.0.0/16 -j ACCEPT
iptables -t filter -A ssh-rules -j DROP
iptables -A INPUT -p tcp -m tcp --dport 22 -j ssh-rules
以上含义就是在Input链中添加一个规则,所有22号端口的访问,都会导向ssh-rules 再次强调: 只要不指定具体使用的表,默认都是filter表
当我们想要删除自定义链时,使用命令:iptables -X ssh-rules
一般我们会在filter中的input链中,配置对某个端口的限制。但是在装有docker的linux服务器上,docker暴露的任何端口,我们却无法通过在filter表中的input链的规则进行限制,这是为什么呢? 我们通过上文的对整个iptables的工作机制,来拆解下原因。
比如,我们在docker 中启动一个mysql,暴露端口是3306。 docker宿主机所在ip: 192.168.31.102。docker 服务启动的虚拟网段:172.17.0.1/16 , 启动的mysql在该虚拟网络的ip是: 172.17.0.2

服务器网口信息:

该机器真正的网口是enp0s3。 docker 启动的虚拟网口是docker0
docker服务本身会在iptables中插入很多规则,甚至定义许多自定义化的链。
当我们我们在192.68.31.23 这台机器上访问192.168.31.102的3306端口时:

该链中的规则会被命中,同时将数据包导向nat表的DOCKER 链。

tcp dpt:3306 to:172.17.0.2:3306,即将目标网络端口改成docker网段下的172.17.0.2:3306
由于prerouting对包进行了目标地址的修改,于是路由决策会将该包路由到forward链。所有表中的input 链将直接忽略。
说白了,由于数据包被更改了目标地址,于是路由策略将该包导向了FORWARD链。所以我们在INPUT链中再怎么定义规则,都无法限制外网对docker服务的访问。
那解决办法很简单,既然包导向了FORWARD链,那么在FORWARD链中添加拦路虎,不就得了嘛。DOCKER官方给的建议便是如此,比如,针对本文中的例子,我们可以添加如下规则,即可实现所有外部网络都无法访问docker中的服务:
iptables -I DOCKER-USER -i enp0s3 -j DROP
规则含义是:所有从外部网络进入的数据包,直接被丢弃。 DOCKER-USER链是上述FORWARD链中第一个规则匹配的到的链。 外部访问的数据包,其输入网口,肯定是enp0s3,因为在本例中,它是对外通信的网口。 当然我们也可以在此,插入只允许某个网络访问,或某个网络不能访问的规则,不再赘述。
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t