You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

525 lines
22 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 42 | 软件部署实战IAM系统安全加固、水平扩缩容实战
你好,我是孔令飞。
这一讲和前面两讲,都是介绍如何基于物理机/虚拟机来部署IAM的。在前面两讲我们了解了如何部署一个高可用的 IAM 应用今天就再来看看IAM 应用安全和弹性伸缩能力的构建方式。在这一讲中我会带你加固IAM应用的安全性并介绍如何具体执行扩缩容步骤。
接下来我们先来看下如何加固IAM应用的安全性。
## IAM应用安全性加固
iam-apiserver、iam-authz-server、MariaDB、Redis和MongoDB这些服务都提供了绑定监听网卡的功能。我们可以将这些服务绑定到内网网卡上从而只接收来自于内网的请求通过这种方式可以加固我们的系统。
我们也可以通过iptables来实现类似的功能通过将安全问题统一收敛到iptables规则可以使我们更容易地维护安全类设置。
这门课通过iptables来加固系统使系统变得更加安全。下面我先来对iptables工具进行一些简单的介绍。
### iptables简介
iptables是Linux下最优秀的防火墙工具也是Linux内核中netfilter网络子系统用户态的工具。
netfilter提供了一系列的接口在一个到达本机的数据包或者经本机转发的数据包流程中添加了一些可供用户操作的点这些点被称为HOOK点。通过在HOOK点注册数据包处理函数可以实现数据包转发、数据包过滤、地址转换等功能。
用户通过iptables工具定义各种规则这些规则通过iptables传给内核中的netfilter。最终netfilter会根据规则对网络包进行过滤。Linux系统一般会默认安装iptables软件。防火墙根据iptables里的规则对收到的网络数据包进行处理。
iptables里的数据组织结构分为表、链、规则。
* **tables:**表可以提供特定的功能每个表里包含多个链。iptables里面一共有5个表分别是filter、nat、mangle、raw、security。这些表分别用来实现包过滤、网络地址转换、包重构、数据追踪处理和SELinux标记设置。
* **chains:**链是数据包传播的路径每一条链中可以有一个或多个规则。当一个数据包到达一个链时iptables会从链中第一条规则开始检查该数据包是否满足规则所定义的条件。如果满足就会根据该条规则所定义的方法处理该数据包。否则就继续检查下一条规则。如果该数据包不符合链中任一条规则iptables就会根据该链预先定义的默认策略来处理数据包。
* **规则rules**规则存储在内核空间的信息包过滤表中,用来描述“如果数据包满足所描述的条件,就按照要求处理这个数据包,如果不满足,就判断下一条规则”。
其中iptables中表和链的种类及其功能如下表所示
![](https://static001.geekbang.org/resource/image/11/0f/112df7eb9a1258dd61e3bd0e0b6b210f.png?wh=2248x1941)
上面的表格中五张表的处理是有顺序的。当数据包到达某一条链时会按照RAW、MANGLE、NAT、FILTER、SECURITY的顺序进行处理。
到这里我介绍了关于iptables的一些基础知识但这还远远不够。要想使用iptables来加固你的系统你还需要掌握iptables工具的使用方法。接下来我先来介绍下iptables是如何处理网络数据包的。
### 网络数据包处理流程
网络数据包的处理流程如下图所示:
![](https://static001.geekbang.org/resource/image/9e/bb/9ece7f3001c022790f1fd1a0yy1246bb.jpg?wh=2248x1414)
具体可以分为两个步骤。
第一步当数据包进入网卡后它首先进入PREROUTING链根据目的IP判断是否转发出去。
第二步分为两种情况如果数据包目的地是本机它会到达INPUT链。到达后任何进程都会收到它。本机上的程序可以发送数据包这些数据包会经过OUTPUT链然后经POSTROUTING链输出如果数据包是要转发出去并且内核允许转发那么数据包会经过FORWARD链最后从POSTROUTING链输出。
### iptables工具使用方式介绍
iptables的功能强大所以使用方法也非常多样。这里我来介绍下iptables工具的使用方式并给出一些使用示例。
1. 命令格式
iptables的语法格式为
```bash
iptables [-t 表名] 命令选项 [链名] [条件匹配] [-j 目标动作或跳转]
```
下面是一个iptables的使用示例
```bash
iptables -t nat -I PREROUTING -p tcp --dport 8080 -j DNAT --to 10.0.4.88
```
这里对上面涉及到的一些参数进行说明。
* 表名/链名指定iptables命令所操作的表/链。
* 命令选项指定处理iptables规则的方式例如插入、增加、删除、查看等。
* 条件匹配:指定对符合条件的数据包进行处理。
* 目标动作或跳转:防火墙处理数据包的方式。
iptables的命令选项又分为管理控制选项和通用选项。
管理控制选项如下:
![](https://static001.geekbang.org/resource/image/6d/b2/6d37f77b4cee31eea694cc588ayy3cb2.png?wh=2248x2323)
通用选项如下:
![](https://static001.geekbang.org/resource/image/0b/ae/0b38f3ba2d722ccf3274a0ae0a5f79ae.png?wh=2248x1498)
处理数据包的方式(目标动作或跳转)有多种,具体如下表所示:
![](https://static001.geekbang.org/resource/image/f7/dc/f796fc7905c88cf0461f4464cec8cddc.png?wh=2248x1625)
上面我介绍了iptables工具的使用方式。因为内容有点多你可能仍然不知道如何使用iptables工具。没关系接下来你可以结合我举的一些例子来看下。
2. 命令示例
下面的命令示例,默认使用了 `FILTER` 表,也即规则存放在 `FILTER` 表中相当于每一条iptables命令都添加了`-t filter` 参数。
1. 拒绝进入防火墙的所有ICMP协议数据包
```bash
$ iptables -I INPUT -p icmp -j REJECT
```
2. 允许防火墙转发除ICMP协议以外的所有数据包
```bash
$ iptables -A FORWARD -p ! icmp -j ACCEPT
```
3. 拒绝转发来自192.168.1.10主机的数据允许转发来自192.168.0.0/24网段的数据
```bash
$ iptables -A FORWARD -s 192.168.1.11 -j REJECT
$ iptables -A FORWARD -s 192.168.0.0/24 -j ACCEPT
```
4. 丢弃从外网接口eth1进入防火墙本机的源地址为私网地址的数据包
```bash
$ iptables -A INPUT -i eth1 -s 192.168.0.0/16 -j DROP
$ iptables -A INPUT -i eth1 -s 172.16.0.0/12 -j DROP
$ iptables -A INPUT -i eth1 -s 10.0.0.0/8 -j DROP
```
5. 只允许管理员从202.13.0.0/16网段使用SSH远程登录防火墙主机
```bash
$ iptables -A INPUT -p tcp --dport 22 -s 202.13.0.0/16 -j ACCEPT
$ iptables -A INPUT -p tcp --dport 22 -j DROP
```
6. 允许本机开放从TCP端口20-1024提供的应用服务
```bash
$ iptables -A INPUT -p tcp --dport 20:1024 -j ACCEPT
$ iptables -A OUTPUT -p tcp --sport 20:1024 -j ACCEPT
```
7. 允许转发来自192.168.0.0/24局域网段的DNS解析请求数据包
```bash
$ iptables -A FORWARD -s 192.168.0.0/24 -p udp --dport 53 -j ACCEPT
$ iptables -A FORWARD -d 192.168.0.0/24 -p udp --sport 53 -j ACCEPT
```
8. 禁止其他主机ping防火墙主机但是允许从防火墙上ping其他主机
```bash
$ iptables -I INPUT -p icmp --icmp-type Echo-Request -j DROP
$ iptables -I INPUT -p icmp --icmp-type Echo-Reply -j ACCEPT
$ iptables -I INPUT -p icmp --icmp-type destination-Unreachable -j ACCEPT
```
9. 禁止转发来自MAC地址为000C2927553F的数据包和主机的数据包
```bash
$ iptables -A FORWARD -m mac --mac-source 00:0c:29:27:55:3F -j DROP
```
10. 对外开放TCP端口20、21、25、110以及被动模式FTP端口1250-1280
```bash
$ iptables -A INPUT -p tcp -m multiport --dport 20,21,25,110,1250:1280 -j ACCEPT
```
11. 禁止转发源IP地址为192.168.1.20-192.168.1.99的TCP数据包
```bash
$ iptables -A FORWARD -p tcp -m iprange --src-range 192.168.1.20-192.168.1.99 -j DROP
```
12. 禁止转发与正常TCP连接无关的非syn请求数据包
```bash
$ iptables -A FORWARD -m state --state NEW -p tcp ! --syn -j DROP
```
13. 拒绝访问防火墙的新数据包,但允许响应连接或与已有连接相关的数据包:
```bash
$ iptables -A INPUT -p tcp -m state --state NEW -j DROP
$ iptables -A INPUT -p tcp -m state --state ESTABLISHED,RELATED -j ACCEPT
```
14. 只开放本机的web服务80、FTP(20、21、20450-20480),放行外部主机发往服务器其他端口的应答数据包,将其他入站数据包都进行丢弃处理:
```bash
$ iptables -I INPUT -p tcp -m multiport --dport 20,21,80 -j ACCEPT
$ iptables -I INPUT -p tcp --dport 20450:20480 -j ACCEPT
$ iptables -I INPUT -p tcp -m state --state ESTABLISHED -j ACCEPT
$ iptables -P INPUT DROP
```
到这里我们已经了解了iptables的功能下面来看看如何使用iptables来加固IAM应用。我把它分成内网不安全和内网安全两种情况。
### IAM安全加固内网不安全
在设置iptables规则之前我们需要先梳理系统的访问关系然后根据这些访问关系设置iptables规则。访问关系如下图所示
![](https://static001.geekbang.org/resource/image/9a/8d/9a9b8d4283410dc842505f258128d78d.jpg?wh=2248x1386)
你可以看到IAM系统服务互访关系分为下面这4种
* 允许公网客户端访问Nginx的80和443端口。
* Keepalived服务之间能够互发VRRP协议包。
* Nginx访问各节点上iam-apiserver、iam-authz-server和iam-pump组件开启的HTTP/HTTPS/GRPC服务。
* iam服务可以从各节点访问Redis、MariaDB、MongoDB数据库。
这里我们假定IAM系统部署在一个非常大的内网中该内网部署了很多其他团队的服务有很多其他团队的研发、测试等人员在内网中执行各种操作。也就是说我们处在一个不安全的内网中。这时候如果要加固我们的系统最安全的方式是屏蔽掉未知的来源IP。
内网不安全的情况下加固系统可以分为3大步骤每个步骤中又有一些小步骤。另外需要新增节点或者删除节点时也需要进行一些变更操作。下面我们来具体看下。
**第一步,设置防火墙规则。**
基于上面说到的几种互访关系我们可以在各个节点上设置iptables规则来加固系统。我将这些规则设置编写成了go工具用来自动生成设置这些规则的shell脚本。
具体设置的过程可以分为5步。
1. 进入iam项目源码根目录。
2. 配置accesss.yaml工具根据此配置自动生成iptables设置脚本内容如下位于[configs/access.yaml](https://github.com/marmotedu/iam/blob/v1.0.8/configs/access.yaml)文件):
```yaml
# 允许登录SSH节点的来源IP可以是固定IP(例如10.0.4.2)也可以是个网段0.0.0.0/0代表不限制来源IP
ssh-source: 10.0.4.0/24
# IAM应用节点列表来源IP
hosts:
- 10.0.4.20
- 10.0.4.21
# 来源IP可以访问的应用端口列表iam-apiserver, iam-authz-server, iam-pump对外暴露的的端口
ports:
- 8080
- 8443
- 9090
- 9443
- 7070
# 来源IP可以访问的数据库端口列表Redis, MariaDB, MongoDB
dbports:
- 3306
- 6379
- 27017
```
上面的配置中我们指定了允许登陆机器的子网、Nginx需要访问的端口列表和各节点需要访问的数据库端口列表。
3. 生成iptables初始化脚本
```bash
$ go run tools/geniptables/main.go -c access.yaml -t app -a -o firewall.sh
$ ls firewall.sh
firewall.sh
```
你可以打开firewall.sh文件查看该脚本设置的规则。
4\. 将firewall.sh脚本拷贝到10.0.4.20和10.0.4.21节点执行:
```bash
$ scp firewall.sh root@10.0.4.20:/tmp/
$ scp firewall.sh root@10.0.4.21:/tmp/
```
登陆10.0.4.20和10.0.4.21机器,执行`/tmp/firewall.sh`。
5. 在10.0.4.20数据库节点节点上设置iptables规则以允许各节点访问
因为数据库节点也位于10.0.4.20节点所以只需要添加新的rule并将`iptables -A INPUT -j DROP`规则放到最后执行即可。
```bash
$ go run tools/geniptables/main.go -c access.yaml -t db -o addrules.sh
```
然后将addrules.sh脚本拷贝到10.0.4.20节点执行。
注意因为iptables是按顺序进行规则过滤的所以需要将`iptables -A INPUT -j DROP`规则放在新设置规则的后面否则执行不到新设置的规则。你可以在设置完iptables规则之后执行下面的命令来将DROP放到最后
```bash
iptables -A INPUT -j LOG --log-level 7 --log-prefix "Default Deny"
iptables -A INPUT -j DROP
```
生成的addrules.sh脚本加入以上设置。
**第二步设置重启自动加载iptables规则。**
前面我们在各个节点设置了iptables规则但是这些规则在系统重启后会丢失。为了使系统重启后自动重新设置这些规则我们需要将当前的iptables规则保存起来让系统重启时自动加载。需要进行下面两个步骤。
1. 保存现有的规则:
```bash
$ sudo iptables-save > /etc/sysconfig/iptables
```
2. 添加下面的命令行到/etc/rc.d/rc.local文件中
```bash
$ iptables-restore < /etc/sysconfig/iptables
```
**第三步,自动化。**
在上面的步骤中我们自动生成了iptables规则并手动登陆到节点进行设置。你肯定也发现了整个流程手动操作过多容易出错效率还低。你可以参考设置过程将这些设置工作自动化比如编写脚本一键刷新所有节点的iptables规则。
另外我们再来看下在新增节点和删除节点两种场景下如何设置iptables规则。
**场景1新增节点**
如果我们要扩容一个节点,也需要在新节点设置防火墙规则,并在数据库节点设置防火墙规则允许来自新节点的访问。
假如我们新增一个10.0.4.22节点这里要设置防火墙规则需要下面的4个步骤。
1. 编辑access.yaml在hosts列表下新增10.0.4.22节点IP。编辑后内容如下
```yaml
# 允许登录SSH节点的来源IP可以是固定IP(例如10.0.4.2)也可以是个网段0.0.0.0/0代表不限制来源IP
ssh-source: 10.0.4.0/24
# IAM应用节点列表来源IP
hosts:
- 10.0.4.20
- 10.0.4.21
- 10.0.4.22
# 来源IP可以访问的应用端口列表iam-apiserver, iam-authz-server, iam-pump对外暴露的的端口
ports:
- 8080
- 8443
- 9090
- 9443
- 7070
# 来源IP可以访问的数据库端口列表Redis, MariaDB, MongoDB
dbports:
- 3306
- 6379
- 27017
```
2. 在10.0.4.22节点设置iptables规则
```bash
$ go run tools/geniptables/main.go -c access.yaml -t app -a -o firewall.sh
```
将firewall.sh脚本拷贝到10.0.4.22节点,并执行。
3. 在已有节点新增规则允许来自10.0.4.22的 Nginx服务的访问
```bash
$ go run tools/geniptables/main.go -c access.yaml -t app 10.0.4.22 -o addrules.sh
```
将addrules.sh脚本拷贝到存量节点并执行。
4. 在数据库节点新增iptables规则以允许来自新节点的访问
```bash
$ go run tools/geniptables/main.go -c access.yaml -t db 10.0.4.22 -o addrules.sh
```
将addrules.sh脚本拷贝到10.0.4.20节点执行即可。
**场景2删除节点。**
如果我们要删除一个节点需要在保留的节点和数据库节点中将该节点的访问权限删除。假如我们要删除10.0.4.22节点设置防火墙规则需要下面3个步骤。
1. 在保留节点删除10.0.4.22节点访问权限:
```bash
$ go run tools/geniptables/main.go -c access.yaml -t app --delete 10.0.4.22 -o delete.sh
```
将delete.sh脚本拷贝到保留节点10.0.4.2010.0.4.21),并执行。
2. 在数据库节点删除10.0.4.22节点访问权限:
```bash
$ go run tools/geniptables/main.go -c access.yaml -t db --delete 10.0.4.22 -o delete.sh
```
将delete.sh脚本拷贝到10.0.4.20节点执行即可。
3. 将下线的节点从access.yaml文件中的hosts部分删除。
### IAM安全加固内网安全
这里我们来看第二种情况假定我们系统部署在一个安全的内网环境中这时候加固系统就会变得异常简单只需要允许来源IP为内网IP的客户端访问我们提供的各类端口即可。在我们设置完iptables规则之后后续再新增或者删除节点就不需要再做变更了。
具体可以分为5个步骤。
**第一步进入iam项目源码根目录。**
**第二步配置accesss.yaml**工具根据此配置自动生成iptables设置脚本内容如下[configs/access.yaml](https://github.com/marmotedu/iam/blob/v1.0.8/configs/access.yaml)文件):
```yaml
# 允许登录SSH节点的来源IP可以是固定IP(例如10.0.4.2)也可以是个网段0.0.0.0/0代表不限制来源IP
ssh-source: 10.0.4.0/24
# 来源IP可以访问的应用端口列表iam-apiserver, iam-authz-server, iam-pump对外暴露的的端口
ports:
- 8080
- 8443
- 9090
- 9443
- 7070
# 来源IP可以访问的数据库端口列表Redis, MariaDB, MongoDB
dbports:
- 3306
- 6379
- 27017
```
上面配置中我们仅仅指定了IAM服务端口和数据库端口。
**第三步生成iptables初始化脚本**
```bash
$ go run tools/geniptables/main.go -c access.yaml -t app --cidr=10.0.4.0/24 -a -o firewall.sh
$ ls firewall.sh
firewall.sh
```
**第四步将firewall.sh脚本拷贝到10.0.4.20和10.0.4.21节点执行:**
```bash
$ scp firewall.sh root@10.0.4.20:/tmp/
$ scp firewall.sh root@10.0.4.21:/tmp/
```
登陆10.0.4.20和10.0.4.21机器执行 `/tmp/firewall.sh`
**第五步在10.0.4.20数据库节点节点上设置iptables规则以允许各节点访问。**
因为数据库节点也位于10.0.4.20节点所以只需要添加新的rule并将 `iptables -A INPUT -j DROP` 规则放到最后执行即可。
```bash
$ go run tools/geniptables/main.go -c access.yaml -t db --cidr=10.0.4.0/24 -o addrules.sh
```
然后,将 `addrules.sh` 脚本拷贝到10.0.4.20节点执行。
如果要增加节点你只需要重新执行第三步生成firewall.sh脚本并将firewall.sh脚本拷贝到新节点上执行即可。删除节点则不需要做任何操作。
接下来我们再来看下如何对IAM应用进行弹性伸缩操作。
## 弹性伸缩
弹性伸缩包括扩容和缩容。扩容是指当业务量越来越大时,能够很容易地增加计算节点,来分散工作负载,从而实现计算等能力的扩展。缩容是指当业务量变小时,能够很容易地减少计算节点,从而减小成本。
在系统上线初期,通常业务量不会很大,但是随着产品的迭代,用户量的增多,系统承载的请求量会越来越多,系统承载的压力也会越来越大。这时,就需要我们的系统架构有能力进行水平扩容,以满足业务需求,同时避免因为系统负载过高造成系统雪崩。
一些电商系统在双11这类促销活动之前会提前扩容计算节点以应对即将到来的流量高峰。但是活动过后流量会逐渐下降这时就需要我们的系统有能力进行缩容以减少计算节点从而节省成本。
一个可伸缩的系统架构是我们在进行系统设计时必须要保证的。如果系统不具有伸缩性那么当我们后期需要扩缩容时就需要对代码进行大改不仅会增加额外的工作量还会拖累产品的迭代速度。而且你想想改完之后还要测试发布之后还可能因为代码变更引入Bug。总之不具伸缩性的系统架构可以说是后患无穷。
IAM系统在设计之初就考虑到了系统的伸缩能力我们可以很容易地对系统进行扩缩容。下面我来分别介绍下如何对系统进行扩容和缩容。
### 系统扩容
系统扩容的步骤很简单你只需要进行下面这5步
1. 根据需要申请计算节点,如无特殊需求,计算节点的配置、操作系统等要跟已有的节点保持一致。
2. 在新的节点上部署iam-apiserver、iam-authz-server、iam-pump部署方式跟部署其他节点一样。
3. 在新节点部署Nginx并将新节点的IP加入到已有所有节点的Nginx upstream配置中重启Nginx。
4. 在新节点部署Keepalived并将新节点的IP加入到已有所有节点的unicast\_peer配置中重启Keepalived。
5. 修改iptables规则并刷新所有机器的iptables。
### 系统缩容
系统缩容是系统扩容的逆向操作也是5个步骤
1. 根据需要,确定要删除的节点。
2. 关闭待删除节点的iam-apiserver、iam-authz-server、iam-pump服务。
3. 从所有保留节点的Nginx upstream配置中删除待删除节点的IP地址, 重启Nginx。
4. 从所有保留节点的Keepalived unicast\_peer配置中删除待删除节点的IP地址, 重启Keepalived。
5. 修改iptables规则并刷新所有保留机器的iptables。
## 总结
安全对于应用软件来说至关重要,在部署应用时,也一定要评估应用的安全性,并采取一定的措施来保证安全性。
在进行软件部署时保证应用安全性最简单有效的方式是使用iptables规则来加固系统。实现思路也很简单就是使用iptables规则只允许特定来源的IP访问特定的端口。
在业务正式上线之后可能会遇到业务高峰期或低峰期。业务高峰期可能需要添加机器提高系统的吞吐量可以在新机器上安装需要扩容的服务组件并安装和配置好Nginx和Keepalived之后将该服务器添加到Nginx的upstream中。在业务低峰期时可以将服务器从Nginx的upstream列表中移除并关停IAM应用的服务。
## 课后练习
1. 请根据这一讲学习的内容,再增扩容一台机器。
2. 思考下,你在应用部署时,还有哪些比较好的应用安全加固方法,欢迎在留言区分享。
欢迎你在留言区与我交流讨论,我们下一讲见。