-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
posts(bcy0349): finish 'better not mess around with iptables'
- Loading branch information
Showing
3 changed files
with
206 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
--- | ||
date: 2019-12-15 | ||
spot: 大冲新城花园 | ||
sort: Computer Science | ||
tags: | ||
- Network | ||
- iptables | ||
- NAT | ||
- TCP | ||
- MySQL | ||
- Unix domain socket | ||
--- | ||
|
||
# 慎用 iptables:误用规则引发的疑问 | ||
|
||
![BCY0349 Masquerade](./bcy0349.jpg "Permitted under the [Terms and conditions](https://www.dfo-mpo.gc.ca/terms-conditions-avis-eng.htm). ©️ [**Contributors**](https://www.dfo-mpo.gc.ca/species-especes/mammals-mammiferes/humpback-rorqual-a-bosse/photos/index-eng.html) on [*dfo-mpo.gc.ca*](https://www.dfo-mpo.gc.ca/species-especes/mammals-mammiferes/humpback-rorqual-a-bosse/photos/bcy-eng.html).") | ||
|
||
昨天去了一趟广州。在深圳安检排队时微信突然来了一串消息: | ||
|
||
> 有个 Web 服务突然被数据库拒绝访问。 | ||
事态比较紧急,我们组长先做了临时处理,之后通知了我们几个相关的人。由于我前阵子接手了这个项目,所以也就承担调查事故原因的任务。 | ||
|
||
## 背景 | ||
|
||
这里涉及一些业务层面的东西,需要脱敏,所以只提取出涉事技术因素: | ||
|
||
- Web 服务(下文以 `IDLE` 代称):一个重要而不繁忙的内部网站,只有工作时间会有人使用。 | ||
- 数据库(MySQL,下文以 `DB` 代称):一个重要且繁忙的数据库,`IDLE` 会对 `DB` 进行只读操作。 | ||
- 另一个 Web 服务(下文以 `BUSY` 代称):持续对 `DB` 进行高频读写操作。 | ||
- `IDLE` 和 `BUSY` 位于同一个 Linux 服务器。 | ||
|
||
### 具体故障 | ||
|
||
`IDLE` 被 `DB` 拒绝访问。 | ||
|
||
在这之前,`IDLE` 对 `DB` 的访问是完全正常的。作为当前唯一的维护人员,我上周根本就没有对它做任何变更,出了这个故障实属让我摸不着头脑。 | ||
|
||
### 初步调查及临时措施 | ||
|
||
组长做了初步调查及临时措施,并提出疑问: | ||
|
||
- Web 服务报错:`sqlMessage: "Host 'IP.IP.IP.IP' is not allowed to connect to this MySQL server"` | ||
- 临时措施: | ||
- `GRANT ALL PRIVILEGES ON *.* TO 'USER'@'IP.IP.IP.IP';` | ||
- 对 `DB` 做了此变更之后 `IDLE` 恢复正常 | ||
- 疑问: | ||
> 1. MySQL 拒绝 `IDLE` 访问,为什么昨天没有出现,而今天早晨出现了?周五晚上有人修改了 MySQL 配置吗? | ||
> 2. `IDLE` 服务连接数据库的配置中,`host = "localhost"`,为什么 MySQL 的报错会是 `"IP.IP.IP.IP"`? | ||
### 可能相关的运维变更 | ||
|
||
对于疑问 `1`,我并没有对服务器或服务做任何变更,所以应该是其他人做了相关操作。有位同事补充了周五晚上他对服务器做的变更,出于运维需要,给 iptables 加了如下规则: | ||
|
||
```sh | ||
sudo iptables -t nat -A POSTROUTING -j MASQUERADE | ||
``` | ||
|
||
之后,又使用 `iptables-restore` 将变更前备份的 iptables 规则表重新导入。 | ||
|
||
### 我的疑问 | ||
|
||
得到以上信息之后,我也产生了新的疑问:为什么在同一台服务器上面,且使用同样配置连接 `DB` 的 `BUSY` 服务不受影响? | ||
|
||
## 调查 | ||
|
||
同事对服务器 iptables 做了相关的配置,而刚好出了故障,从直觉上来说这两件事有关联的概率很大。但由于后续用 `iptables-restore` 导入了原来的配置,所以这个因素暂时就先搁置了。 | ||
|
||
对于疑问 `2`,按照正常情况,如果客户端使用 `localhost` 连接 `DB`,那么客户端自身的地址也会是 `localhost` [[1]],现在却变成 `IP.IP.IP.IP`,导致被 `DB` 拒绝连接。 | ||
|
||
会是 `localhost` 指向有问题吗? | ||
|
||
```sh | ||
$ ping localhost | ||
PING localhost (127.0.0.1) 56(84) bytes of data. | ||
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.026 ms | ||
64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.024 ms | ||
... | ||
``` | ||
|
||
显然,`localhost` 正常。 | ||
|
||
### MySQL client 的连接方式 | ||
|
||
如果用 `mysql` 的命令行客户端登录呢?也正常: | ||
|
||
```sh | ||
$ mysql -uUSER -p | ||
Enter password: | ||
Welcome to the MySQL monitor. Commands end with ; or \g | ||
... | ||
``` | ||
|
||
之后跟朋友 `@Shady` 吐槽了这个现象,他告诉我在 Linux 上 `mysql` 命令行客户端默认使用 Unix domain socket [[2], [3]] 而不是 TCP 协议。这个信息很重要: | ||
|
||
```sh | ||
$ mysql -uUSER -h 127.0.0.1 -p --protocol=TCP | ||
Enter password: | ||
ERROR 1130 (HY000): Host 'IP.IP.IP.IP' is not allowed to connect to this MySQL server | ||
``` | ||
|
||
可以看到,只要是通过 TCP 协议通过 `localhost` (`127.0.0.1`) 连接 `DB`,就会有问题。据此可以确定和 `IDLE` 服务本身无关。 | ||
|
||
这让我不得不怀疑起前面提到的 iptables 变更,增加的规则真的没留下任何影响吗? | ||
|
||
### iptables 规则 | ||
|
||
前文说到前一晚服务器做了这个变更: | ||
|
||
```sh | ||
sudo iptables -t nat -A POSTROUTING -j MASQUERADE | ||
``` | ||
|
||
这条命令的意思是: | ||
|
||
- 对 `nat` 表 (`-t TABLE`) 增加针对 `POSTROUTING` 链 (`-A CHAIN`) 的 `-j MASQUERADE` 规则 (`-j TARGET`) [[4]] | ||
- 翻译成人话就是:将服务器发出的所有网络数据包源 IP 修改为网络出口(即网卡)的 IP [[5]] | ||
|
||
这非常符合故障特征,`DB` 报错中的 `IP.IP.IP.IP` 就是网卡地址。我用 `sudo iptables -L -t nat` 一看,规则果然还在。所以我将条规则移除: | ||
|
||
```sh | ||
sudo iptables -t nat -D POSTROUTING -j MASQUERADE | ||
``` | ||
|
||
之后用 `mysql -uUSER -h 127.0.0.1 -p --protocol=TCP` 进行测试,结果正常了。我又将前面组长给 MySQL 权限做的临时措施撤销掉,`IDLE` 并没有报出原来的故障。说明确实就是这条 iptables 规则造成的故障。 | ||
|
||
#### iptables-restore | ||
|
||
新的疑问来了:同事说前面已经用 `iptables-restore` 导入备份,为什么新增的规则还在? | ||
|
||
我检查了他的 bash history [[6]],确实有过 `iptables-restore IPTABLES.bak` 的操作记录,备份文件 `IPTABLES.bak` 中确实也没有 `-A POSTROUTING -j MASQUERADE` 相关的内容。 | ||
|
||
直觉告诉我,大概率是因为 `iptables-restore` 不是预期中的完全按备份文件覆盖现有规则。在 `iptables-restore` 的 man page [[6]] 中, `-n, --noflush` 参数说: | ||
|
||
```sh | ||
-n, --noflush | ||
don't flush the previous contents of the table. If not | ||
specified, both commands flush (delete) all previous | ||
contents of the respective table. | ||
``` | ||
即 `iptables-restore` 只会覆盖相应 table 的内容,备份中没有 `nat` 表的规则,因此在 `nat` 表中新增的规则不会被 `iptables-restore IPTABLES.bak` 覆盖。 | ||
### 解开我的疑问 | ||
> 为什么在同一台服务器上面,且使用同样配置连接 `DB` 的 `BUSY` 服务不受影响? | ||
直觉上,大概率是因为新增的 `nat` 规则不影响现有的 TCP 连接。做了一番搜寻之后,确实如此 [[7], [8]]: | ||
> **NAT** | ||
> | ||
> This table is slightly different from the `filter' table, in that only the first packet of a new connection will traverse the table: the result of this traversal is then applied to all future packets in the same connection. | ||
|
||
其实想来也正常,因为 TCP 的 socket pair 是一个四元组 `SRC_IP:SRC_PORT - DST_IP:DST_PORT` [[9]] ,如果 `SRC_IP` 变了,对连接的另一端来说,这根本就是另一个连接。如果修改原有连接的数据包会摧毁整个连接。 | ||
|
||
> **Connections**: | ||
> | ||
> Each connection is uniquely specified by a pair of sockets | ||
identifying its two sides. | ||
|
||
另外,`DB` 的系统变量 `wait_timeout` 为默认的 8 小时 [[10]]。而 `BUSY` 服务高频访问 `DB`,连接池长时间保持活跃,没有连接被 `DB` 关闭,所以 `BUSY` 服务也就不受 iptables 影响。 | ||
|
||
--- | ||
|
||
:::details 封面图 | ||
|
||
封面图是一头大翅鲸 (Humpback whale,也叫座头鲸) [[11]] 的尾鳍。 | ||
|
||
- 图中这头大翅鲸的编号是 `BCY0349`,名字叫 Masquerade。 | ||
- 编号中的 `BC` 是 British Columbia 的首字母缩写,`Y` 是名录编号,目前有 `XYZ` 三个名录 [[12]]。 | ||
|
||
::: | ||
|
||
## References | ||
|
||
1. [What is the source IP address when we ping 127.0.0.1?][1]. *forum.networklessons.com*. | ||
2. [Unix domain socket][2]. *en.wikipedia.org*. | ||
3. [4.3.3 Connecting using Unix Sockets and Windows Named Pipes][3]. *dev.mysql.com*. | ||
4. [iptables(8) — Linux manual page][4]. *man7.org*. | ||
5. [Iptables Tutorial - Chapter 11. Iptables targets and jumps - 11.9. MASQUERADE target][5]. *frozentux.net*. | ||
6. [iptables-restore(8) — Linux manual page][6]. *man7.org*. | ||
7. [iptables - redirecting established connections - Answered by `@Khaled`][7]. *serverfault.com*. | ||
8. [3. Netfilter Architecture - 3.2 Packet Selection: IP Tables - NAT][8]. *netfilter.org*. | ||
9. [RFC 793 (TRANSMISSION CONTROL PROTOCOL) - 1.5. Operation][9]. *datatracker.ietf.org*. | ||
10. [MySQL 8.0 Reference Manual - 5.1.8 Server System Variables][10]. *dev.mysql.com*. | ||
11. [Humpback whale][11]. *wikipedia.org*. | ||
12. [Photographic Catalogue of Humpback Whales in British Columbia][12]. *dfo-mpo.gc.ca*. | ||
|
||
[1]: <https://forum.networklessons.com/t/what-is-the-source-ip-address-when-we-ping-127-0-0-1/3643> | ||
[2]: <https://en.wikipedia.org/wiki/Unix_domain_socket> | ||
[3]: <https://dev.mysql.com/doc/mysql-shell/8.2/en/mysql-shell-connection-socket.html> | ||
[4]: <https://man7.org/linux/man-pages/man8/iptables.8.html> | ||
[5]: <https://www.frozentux.net/iptables-tutorial/chunkyhtml/x4422.html> | ||
[6]: <https://man7.org/linux/man-pages/man8/iptables-restore.8.html> | ||
[7]: <https://serverfault.com/a/828705/553550> | ||
[8]: <https://www.netfilter.org/documentation/HOWTO//netfilter-hacking-HOWTO-3.html> | ||
[9]: <https://datatracker.ietf.org/doc/html/rfc793#section-1.5> | ||
[10]: <https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_wait_timeout> | ||
[11]: <https://en.wikipedia.org/wiki/Humpback_whale> | ||
[12]: <https://www.dfo-mpo.gc.ca/species-especes/mammals-mammiferes/humpback-rorqual-a-bosse/photos/index-eng.html> |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.