背景

KVM安装了虚拟机通过NAT方式访问外放,但是端口映射一直不成功,可能是iptables哪里配置有问题。

起因

CentOS学习KVM笔记-网络篇的时候需要映射虚拟机端口,按照网上的iptables介绍的端口映射规则,加了这三条规则,但是没有效果。

1
2
3
iptables -A INPUT -p tcp --dport 10122 -j ACCEPT
iptables -t nat -A PREROUTING -d 192.168.124.21 -p tcp -m tcp --dport 10122 -j DNAT --to-destination 192.168.122.101:22
iptables -t nat -A POSTROUTING -s 192.168.122.0/255.255.255.0 -d 192.168.122.101 -p tcp -m tcp --dport 22 -j SNAT --to-source 192.168.122.1

iptables介绍

iptables概念

按照正常的理解上,iptables是个防火墙,跟windows里面的防火墙规则类似。其实iptables不是真正的防火墙,我们可以理解成一个客户端代理工具,用户通过iptables这个代理,将用户的安全设定执行到对应的安全框架中,这个安全框架”才是直正的防火墙,这个框架的名字叫 netfilter。

netfilter才是防火墙真正的安全框架( framework), netfilter位于内核空间。
iptables其实是个命令行工具,位于用户空间,我们用这个工具操作真正的框架。

netfilter/ iptables(下文中简称为 iptables)组成 Linux平台下的包过滤防火墙,与大多数的 Linux软件一样,这个包过滤防火墙是兔费的,它可以代替昂贵的商业防火墙解决方案,完成封包过滤、封包重定向和网络地址转换(NAT)等功能。

Netfilter是 Linux操作系统核心层内部的—一个数据包处理模块,它具有如下功能:

网络地址转换( Network Address Translate)

数据包内容修改以及数据包过滤的防火墙功能。所以说,虽然我们使用 service iptables start启动 iptables”服务”,但是其实准确的来说, iptables并没有一个守护进程,所以并不能算是真正意义上的服务,而应该算是内核提供的功能。

iptables原理

我们知道iptables是按照规则来办事的,我们就来说说规则(rules),规则其实就是网络管理员预定义的条件,规则一般的定义为”如果数据包头符合这样的条件,就这样处理这个数据包”。规则存储在内核空间的信息包过滤表中,这些规则分别指定了源地址、目的地址、传输协议(如TCP、UDP、|CMP)和服务类型(如HTP、FP和SMTP)等。
数据包与规则匹配时,iptables就根据规则所定义的方法来处理这些数据包,如放行(accept)、拒绝(reject))和丢弃(drop)等。配置防火墙的主要工作就是添加、修改和删除这些规则。

这样说可能并不容易理解,我们来换个容易理解的角度,从头说起

当客户端访问服务器的web服务时,客户端发送报文到网卡,而tcp/ip协议栈是属于内核的一部分,所以,客户端的信息会通过内核的TCP协议传输到用户空间中的web服务中,而此时,客户端报文的目标终点为web服务所监听的套接字(P:Por)上,当web服务需要响应客户端请求时,web服务发出的响应报文的目标终点则为客户端,这个时候,web服务所监听的P与端囗反而变成了原点,我们说过, netfilter 才是真正的防火墙,它是内核的部分,所以,如果我们想要防火墙能够达到”防火”的目的,则需要在内核中设置关卡,所有进出的报文都要通过这些关卡,经过检查后,符合放行条件的才能放行,符合阻拦条件的则需要被阻止,于是,就出现了input关卡和 output关卡,而这些关卡在 iptables中不被称为关卡”而被称为链”。

其实我们上面描述的场景并不完善,因为客户端发来的报文访问的目标地址可能并不是本机,而是其他服务器,当本机的内核支持 IP FORWARD时,我们可以将报文转发给其他服务器,所以,这个时候,我们就会提到 iptables中的其他”关卡,也就是其他链”,他们就是”路由前”、“转发”、”路由后”,他们的英文名是PREROUTING、FORWARD、POSTROUTING也就是说,当我们启用了防火墙功能时,报文需要经过如下关卡,也就是说,根据实际情况的不同,报文经过链可能不同。如果报文需要转发,那么报文则不会经过Input链发往用户空间,而是直接在内核空间中经过forward链和postrouting链转发出去的。

iptables基础

iptables结构

iptables的结构:iptables -> Tables -> Chains -> Rules. 简单地讲,tables由chains组成,而chains又由rules组成。如下图所示。

iptables表与链

iptables具有Filter,Nat,Mangle,Raw四种内建表:

Filter表

Filter表示iptables的默认表,因此如果你没有自定义表,那么就默认使用filter表,它具有以下三种内建链:

  • INPUT
    • 处理来自外部的数据。
  • OUTPUT
    • 处理向外发送的数据。
  • FORWARD
    • 将数据转发到本机的其他网卡设备上。

Nat表

NAT表有三种内建链:

  • PREROUTING
    • 处理刚到达本机并在路由转发前的数据包。它会转换数据包中的目标IP地址(destination ip address),通常用于DNAT(destination NAT)。
  • POSTROUTING
    • 处理即将离开本机的数据包。它会转换数据包中的源IP地址(source ip address),通常用于SNAT(source NAT)。
  • OUTPUT
    • 处理本机产生的数据包。

Mangle表

Mangle表用于指定如何处理数据包。它能改变TCP头中的QoS位。Mangle表具有5个内建链:

  • PREROUTING
  • OUTPUT
  • FORWARD
  • INPUT
  • POSTROUTING

Raw表

Raw表用于处理异常、状态跟踪,它具有2个内建链:

  • PREROUTING
  • OUTPUT

下图展示了iptables的三个内建表:

iptables规则

规则(Rules)注意点

  • Rules包括一个条件和一个目标(target)
  • 如果满足条件,就执行目标(target)中的规则或者特定值。
  • 如果不满足条件,就判断下一条Rules。

目标值(Target Values)

  • ACCEPT
    • 允许防火墙接收数据包
  • DROP
    • 防火墙丢弃包
  • QUEUE
    • 防火墙将数据包移交到用户空间
  • RETURN
    • 防火墙停止执行当前链中的后续Rules,并返回到调用链(the calling chain)中。

iptables操作

选择iptables表

  • 语法

    • iptables -t table
      • -t - 选择表,有效值:filter,mangle,raw,nat
      • 不加这个参数默认选择filter,
  • 查看filter表:
    注意:如果不指定 -t选项,就只会显示默认的 filter表。因此,以下两种命令形式是一个意思:
    iptables -t filter --list
    或者
    iptables --list

  • 查看mangle表:
    iptables -t mangle --list

  • 查看nat表:
    iptables -t nat --list

  • 查看raw表:
    iptables -t raw --list


查看iptables规则

  • 语法
    • iptables -nvL chain
      • -n - 显示ip
      • -v - 显示流量信息
      • --line-number - 显示序号

显示列说明:

  • pkts : 对应规则匹配到的报文的个数。
  • bytes : 对应匹配到的报文包的大小总和。
  • in : 表示数据包由哪个接口(网卡)流入,我们可以设置通过哪块网卡流入的报文需要匹配当前规则。
  • target : 规则对应的target,往往表示规则对应的”动作”,即规则匹配成功后需要采取的措施。
  • prot : 表示规则对应的协议,是否只针对某些协议应用此规则。
  • opt : 表示规则对应的选项。
  • out : 表示数据包由哪个接口(网卡)流出,我们可以设置通过哪块网卡流出的报文需要匹配当前规则。
  • source : 表示规则对应的源头地址,可以是一个IP,也可以是一个网段。
  • destination : 表示规则对应的目标地址。可以是一个IP,也可以是一个网段。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@chobon ~]# iptables –list
Chain INPUT (policy ACCEPT)
num target prot opt source destination
1 RH-Firewall-1-INPUT all — 0.0.0.0/0 0.0.0.0/0
Chain FORWARD (policy ACCEPT)
num target prot opt source destination
1 RH-Firewall-1-INPUT all – 0.0.0.0/0 0.0.0.0/0

Chain OUTPUT (policy ACCEPT)
num target prot opt source destination

Chain RH-Firewall-1-INPUT (2 references)
num target prot opt source destination
1 ACCEPT all – 0.0.0.0/0 0.0.0.0/0
2 ACCEPT icmp – 0.0.0.0/0 0.0.0.0/0 icmp type 255
3 ACCEPT esp – 0.0.0.0/0 0.0.0.0/0
4 ACCEPT ah – 0.0.0.0/0 0.0.0.0/0
5 ACCEPT udp – 0.0.0.0/0 224.0.0.251 udp dpt:5353
6 ACCEPT udp – 0.0.0.0/0 0.0.0.0/0 udp dpt:631
7 ACCEPT tcp – 0.0.0.0/0 0.0.0.0/0 tcp dpt:631
8 ACCEPT all – 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
9 ACCEPT tcp – 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22
10 REJECT all – 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited

以上输出包含下列字段:

  • num – 指定链中的规则编号
  • target – 前面提到的target的特殊值
  • prot – 协议:tcp, udp, icmp等
  • source – 数据包的源IP地址
  • destination – 数据包的目标IP地址

清空itables规则

  • 语法
    • iptables -F(--flush) chain
      • chain - 指定要追加规则的链

在配置iptables之前,你通常需要用iptables –list命令或者iptables-save命令查看有无现存规则,因为有时需要删除现有的iptables规则:
iptables -flush
或者
iptables -F
这两条命令是等效的。但是并非执行后就万事大吉了。你仍然需要检查规则是不是真的清空了,因为有的linux发行版上这个命令不会清除NAT表中的规则,此时只能手动清除:
iptables -t NAT -F


增加iptables规则

可以用 iptables -A 命令追加新规则,其中-A表示Append。因此,新的规则将追加到链尾。
一般而言,最后一条规则用于丢弃(DROP)所有数据包。如果你已经有这样的规则了,并且使用 -A参数添加新规则,那么就是无用功。

追加规则(-A,–append)

  • 语法
    • iptables -A chain firewall-rule
      • chain - 指定要追加规则的链
      • firewall-rule – 具体的规则参数

插入规则(-I,–insert)

  • 语法
    • iptables -I chain [rule-num] firewall-rule
      • chain - 指定要追加规则的链
      • [rule-num] - 可选插入的序号
      • firewall-rule – 具体的规则参数

删除iptables规则

删除规则有两种方法:

  • 方法一:根据规则的编号去删除规则

  • 方法二:根据具体的匹配条件与动作删除规则

  • 语法

    • iptables -D chain rule-num 或者 firewall-rule
      • chain - 指定要删除规则的链
      • rule-num - 规则的序号
      • firewall-rule - 具体的规则参数

先查看一下filter表中INPUT链中的规则

1
2
3
4
5
6
[root@chobon ~]# iptables --line -nvL INPUT
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
1 4 231 ACCEPT udp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:53
2 0 0 ACCEPT tcp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:53
3 0 0 ACCEPT udp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:67

结果出来是带num序号的,假如我们想删第1条。

1
2
3
4
5
6
7
[root@chobon ~]# iptables -t filter -D INPUT 1
[root@chobon ~]# iptables --line -nvL INPUT
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
1 0 0 ACCEPT tcp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:53
2 0 0 ACCEPT udp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:67
3 0 0 ACCEPT tcp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:67

根据具体的匹配条件与动作去删除规则,比如-A INPUT -i virbr0 -p tcp -m tcp --dport 53 -j ACCEPT

1
2
3
4
5
6
7
[root@chobon ~]# iptables -t filter -D INPUT -i virbr0 -p tcp -m tcp --dport 53 -j ACCEPT
[root@chobon ~]# iptables --line -nvL INPUT
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
1 0 0 ACCEPT udp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:67
2 0 0 ACCEPT tcp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:67
3 1178 85725 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED

修改iptables规则

修改规则(-R,–replace)

  1. 语法
    • iptables -R chain rule-num firewall-rule
      • chain - 指定要修改规则的链
      • rule-num - 规则的序号
      • firewall-rule - 具体的规则参数

先查看一下filter表中INPUT链中的规则

1
2
3
4
5
6
[root@chobon ~]# iptables --line -nvL INPUT
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
1 4 231 ACCEPT udp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:53
2 0 0 ACCEPT tcp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:53
3 0 0 ACCEPT udp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:67

如果要将第1条规则的动作修改为REJECT,-s和-d缺省的话会默认0.0.0.0/0,其他参数不指定的话也会改成默认值。

1
2
3
4
5
6
7
[root@chobon ~]# iptables -R INPUT 1 -i virbr0 -p udp -m udp --dport 53 -j REJECT
[root@chobon ~]# iptables --line -nvL INPUT
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
1 4 231 REJECT udp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:53
2 0 0 ACCEPT tcp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:53
3 0 0 ACCEPT udp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:67

修改规则链(-P,–policy)

  • 语法
    • iptables -P chain target
      • chain - 指定要修改规则的链
      • target - 指定默认处理动作,DROP和ACCEPT

每张表的每条链中,都有自己的默认策略,我们也可以理解为默认”动作”。

1
2
3
4
5
6
7
[root@chobon ~]# iptables -P INPUT DROP
[root@chobon ~]# iptables --line -nvL INPUT
Chain INPUT (policy DROP 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
1 4 231 ACCEPT udp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:53
2 0 0 ACCEPT tcp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:53
3 0 0 ACCEPT udp -- virbr0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:67

规则基本参数

  • -p | 协议(protocol)
    • 指定规则的协议,如tcp, udp, icmp等,可以使用all来指定所有协议。
    • 如果不指定-p参数,则默认是all值。这并不明智,请总是明确指定协议名称。
    • 可以使用协议名(如tcp),或者是协议值(比如6代表tcp)来指定协议。映射关系请查看/etc/protocols
    • 还可以使用–protocol参数代替-p参数
  • -s | 源地址
    • 指定数据包的源地址
    • 参数可以使IP地址、网络地址、主机名
    • 例如:-s 192.168.1.101指定IP地址
    • 例如:-s 192.168.1.10/24指定网络地址
    • 如果不指定-s参数,就代表所有地址
    • 还可以使用–src或者–source
  • -d | 目的地址(destination)
    • 指定目的地址
    • 参数和-s相同
    • 还可以使用–dst或者–destination
  • -j | 执行目标(jump to target)
    • -j代表 “jump to target”
    • -j指定了当与规则(Rule)匹配时如何处理数据包
    • 可能的值是 ACCEPT, DROP, QUEUE, RETURN, MASQUERADE
    • 还可以指定其他链(Chain)作为目标
    • 注:MASQUERADE,地址伪装,算是snat中的一种特例,可以实现自动化的snat
  • -i | 输入接口(input interface)
    • -i代表输入接口(input interface)
    • -i指定了要处理来自哪个接口的数据包
    • 这些数据包即将进入INPUT, FORWARD, PREROUTE链
    • 例如:-i eth0指定了要处理经由eth0进入的数据包
    • 如果不指定-i参数,那么将处理进入所有接口的数据包
    • 如果出现! -i eth0,那么将处理所有经由eth0以外的接口进入的数据包
    • 如果出现-i eth+,那么将处理所有经由eth开头的接口进入的数据包
    • 还可以使用–in-interface参数
  • -o | 输出(out interface)
    • -o代表 “output interface”
    • -o指定了数据包由哪个接口输出
    • 这些数据包即将进入FORWARD, OUTPUT, POSTROUTING链
    • 如果不指定-o选项,那么系统上的所有接口都可以作为输出接口
    • 如果出现! -o eth0,那么将从eth0以外的接口输出
    • 如果出现-i eth+,那么将仅从eth开头的接口输出
    • 还可以使用–out-interface参数
  • -m | 匹配模块(match)
    • -m表示指定的模块
    • 与前面-p选项对应的协议名称相同的模块可以忽略

描述规则的扩展参数

对规则有了一个基本描述之后,有时候我们还希望指定端口、TCP标志、ICMP类型等内容。

  • –sport | 源端口(source port)
    • 针对 -p tcp 或者 -p udp
    • 缺省情况下,将匹配所有端口
    • 可以指定端口号或者端口名称,例如 “–sport 22” 与 “–sport ssh” 。
    • /etc/services文件描述了上述映射关系。
    • 从性能上讲,使用端口号更好
    • 使用冒号可以匹配端口范围,如 “–sport 22:100”
    • 还可以使用 “–source-port”
  • –-dport | 目的端口(destination port)
    • 针对-p tcp 或者 -p udp
    • 参数和–sport类似
    • 还可以使用 “–destination-port”
  • -–tcp-flags | TCP标志
    • 针对-p tcp
    • 可以指定由逗号分隔的多个参数
    • 有效值可以是:SYN, ACK, FIN, RST, URG, PSH
    • 可以使用ALL或者NONE
  • -–icmp-type | ICMP类型
    • 针对-p icmp
    • –icmp-type 0 表示Echo Reply
    • –icmp-type 8 表示Echo
  • --state | STATE状态
    • 针对state
    • 链接报文状态
    • 有五种状态:
    • NEW: 连接的第一包
    • ESTABLISHED:NEW状态包后面的包状态理解,表示连接已建立
    • RELATED:数据进程连接–数据连接<如FTP中的数据连接>
    • INVALID:包没有办法被识别,或者空上包没有任何状态,可以主动屏蔽INVALID的报文
    • UNTRACKED:未被追踪的报文 ,当报文的状态为Untracked时通常表示无法找到相关的连接

创建自定义链

  • 语法

    • iptables -t table -N new-chain
      • new-chain - 新增自定义链名
  • 在mangle表增加一条名叫NEW_CHAIN的自定义链:
    iptables -t mangle -N NEW_CHAIN

引用链

  • 语法

    • iptables -t table firewall-rule new-chain
      • firewall-rule – 具体的规则参数
      • new-chain - 需要引用的链
  • 在filter表增加一条规则target引用叫NEW_CHAIN的自定义链:
    iptables -t filter -I INPUT -p tcp --dport 80 -j NEW_CHAIN

重命名链

  • 语法

    • iptables -t table -E old-chain new-chain
      • old-chain – 需要被重命名的链
      • new-chain - 重命名后的链
  • 在filter表重命名一条名叫NEW_CHAIN的自定义链为NEW_PREROUTING_CHAIN:
    iptables -t filter -E NEW_CHAIN NEW_PREROUTING_CHAIN

删除链

  • 语法

    • iptables -t table -X chain
      • chain – 需要被删除的链
  • 在filter表删除一条名叫NEW_CHAIN的自定义链:
    iptables -t filter -X NEW_CHAIN

永久生效

当你删除、添加规则后,这些更改并不能永久生效,这些规则很有可能在系统重启后恢复原样。为了让配置永久生效,根据平台的不同,具体操作也不同。下面进行简单介绍:

  1. Ubuntu
    首先,保存现有的规则:
    iptables-save > /etc/iptables.rules
    然后新建一个bash脚本,并保存到 /etc/network/if-pre-up.d/目录下:

    1
    2
    #!/bin/bash
    iptables-restore < /etc/iptables.rules

    这样,每次系统重启后iptables规则都会被自动加载。
    !注意:不要尝试在.bashrc或者.profile中执行以上命令,因为用户通常不是root,而且这只能在登录时加载iptables规则。

  2. CentOS, RedHat

    保存iptables规则

    service iptables save

    重启iptables服务

    service iptables stop
    service iptables start

    查看当前规则:

    cat /etc/sysconfig/iptables

学习总结

刚开始觉得挺复杂,深入后发现没那么难,一切都是按照规则来,有迹可循,只要掌握关键要领就可以按图索骥。

回顾到开头那三条规则,其实是正确的,只是KVM初始得虚拟网卡virbr0有条默认规则做了限制

iptables查看filter表FORWARD链,有条规则里面没有包含NEW,由于ssh请求第一个包是NEW,所以如果不加上,就连不上。

1
2
3
4
5
6
7
8
[root@chobon ~]# iptables -nvL FORWARD
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
1 40 ACCEPT all -- virbr0 * 192.168.122.0/24 0.0.0.0/0
2 80 ACCEPT all -- * virbr0 0.0.0.0/0 192.168.122.0/24 ctstate RELATED,ESTABLISHED
20 1040 REJECT all -- * virbr0 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
0 0 REJECT all -- virbr0 * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable

注意:这里后面两条规则REJECT顺序,如果在前面就得删除或者把ACCEPT移到前面

1
2
3
4
5
6
[root@chobon ~]# iptables -R FORWARD 2 -o virbr0 -d 192.168.122.0/24 -m conntrack --ctstate NEW,RELATED,ESTABLISHED -j ACCEPT
[root@chobon ~]# service iptables save
iptables: Saving firewall rules to /etc/sysconfig/iptables:[ 确定 ]
[root@chobon ~]# service iptables restart
Redirecting to /bin/systemctl restart iptables.service
[root@chobon ~]#

注意:-m state –state和-m conntrack –ctstate差不多,有些地方说一样的,有些地方说state是被conntrack代替了

现在就可以通过ssh -p 10122 [email protected]命令来连接虚拟机了。