个人云服务器如何做安全防护
简述: 一台个人云服务器部署了个网站,该怎么做基础网络防护。
内容包含我当前正在使用中的一些配置,如主机配置,防火墙配置,nginx+lua策略,以及调用腾讯云API接口的示例等,年末了就整合到一起分享出来,欢迎大家给提意见。
背景
我很久前买了一个云主机,还买了个域名,当时可是雄心勃勃打算弄个人网站,做大做强,走向世界!
不过后来几年间几乎处于闲置状态,除了跑个几乎不更新的静态博客系统外,啥也没干。
PS:我的环境是1c2G的最低配置(活动价200元三年),每家云厂商几乎每个购物节都会推这样的活动;
现在眼看即将要到期了,也不打算续费,决定还是再折腾一下吧,免得太浪费;
想到很多人或许都有自己的云主机,所以打算就安全问题和大家分享讨论一下。
安全问题
云服务器有公网IP,开放后那攻击就是经常有的事情。
只要你启动了一个端口,那每天都是各种被扫描、探测、暴力破解等等。
恶意访问来源:
- 因为是对公网开放,所以全世界哪来都有可能;
- 感觉还是云厂商自己扫得比较多,毕竟你真被攻破了,还影响平台的声誉,提前发现了的话,就更方便给你推销安服产品啦。
我这台云主机虽然没啥数据也不重要,但我觉得还是要管一下,就像一群苍蝇围着牛叫,虽然牛被咬不死,但也得时不时用尾巴甩一下。
个人做安全防护的困难
对于个人的云主机来说,还是存在一些局限,比如:
- 缺乏能力,不像专业做安全的那么多手段,很多东西都不懂;
- 缺乏资金,不可能去买云厂商的安服,也不会买啥付费设备、软件;
- 缺乏精力,不可能一天到晚去盯着,10天半月不管不问是常事,也不想接收到太多误报告警干扰生活;
那么该怎么做防护呢?
我有位做安全的朋友(吴迪)多年前给我说了两个方法,直到现在我一遇到安全事件首先想到的就是这俩:解决或隔离
解决:有能力、有精力时,就去把问题和隐患给彻底修复了;
隔离:不能解决,就把问题和隐患给藏起来,让别人看不到摸不到。
PS: 我有位朋友目前正在隔离中,大过年的,真是惨啊!
本文接下来就这俩主题来说应该怎么办。
解决
只能是就事论事,没发生啥也不好说,所以只能简单说下通用方法了。
安全事件就两种情况: 未攻破前,被攻破后
未攻破前
一般是存在啥风险隐患可能被人利用,那就去修复,常见情况有:漏洞、权限、应用逻辑等情况。
-
软件有漏洞就升级
一是主要是关注一些新闻,看看最近是否爆出了啥漏洞;
二是时间长了,旧版本肯定会被发现一些漏洞出来,简单说就是 10年前的啥软件放到今天,你都可以认为它是不安全的。
解决办法:
- 针对某个被爆出漏洞的软件及时做升级
- 定期更新,每2-3月登上去全更新一遍, yum -y update
- 既然是个人使用,那么业务程序就紧跟最新版本就行
-
权限不合理
个人机器不可能去配置复杂的权限规则;
这个没啥好办法,分个主次就行,一个root账号一个普通账号就行,非必要不上 root 权限;
-
程序逻辑有缺陷
这个是特殊情况,需要具体分析,根据场景讨论吧。
被攻破后
已经被攻击成功了,或疑似被攻破了,该怎么事后处理;
- 评估损失,是否有什么重要资料泄漏,怎么避免二次伤害等;
- 删了实例重建(简单吧,重要资料本地有备份就能这么干)。
- 没备份可以尝试提工单找平台付费帮忙,或者自己去折腾。
隔离
简单说就是:就是通过各种方法将暴露面缩小,并增加恶意人员访问到你核心资源的路径长度,如原来一步就能访问到,现在要10几步。
本文主要说网络防护、主机系统防护、应用防护三部分。
网络防护
简单说就是允许哪些源IP访问哪些端口;
比如个人启动了个无密码的 redis,当然不想让所有人都能直接访问,只限自己或特定源IP才能访问。
方案一:充分利用云厂商的功能,比如安全组
基本规则:安全组上只开放固定的几个端口。
如果纯粹是私人用,那就限制只允许1个或多个源IP进行访问
如果有公开业务,就只对这1个端口添加 0.0.0.0/0 的源IP访问策略;
我是怎么配的安全组入策略的呢?
80端口对外全开(毕竟有个网站要开放)
ssh端口和其它端口就只允许1个源IP进行访问;
但我们经常更换网络环境,比如从公司到家里,难道每次要登录主机还得去云控制台改IP,太麻烦了;
解决办法: 我们可以通过云平台的API接口进行修改啊,一键更新;
文末给出了我目前正在用的这个方法,通过腾讯云的SDK调用API接口,获取本地电脑出网的公网IP后,再更新安全组内的ssh源IP规则,非常简单;
熟悉后还可以配合其它拦截策略调用API进行IP拦截,这样恶意流量就到不了主机侧。
方案二:利用主机系统的防护墙
如果觉得去折腾云API接口麻烦,那就折腾主机防火墙吧,这个稍微简单点,而且是通用技能,其它时候也能用得上。
原理: 先 ssh 登录主机,然后将当前源IP加入防火墙的可信区域内,且只允许可信区域的源IP对其它端口访问。
文末给出脚本方式添加访问IP到防火墙可信区域的示例
方案三:动态应用防火墙,根据认证情况放行
简单说就是需要先调认证接口,通过后才对此IP的后续请求放行。
文末给出了我当前正在使用的策略:使用 nginx 进行HTTP和TCP端口的代理,并采用lua进行简单认证的例子。
方案四:敲门性质的防火墙
一些公司采用的策略,原理也比较简单,个人要是有精力也可以尝试去鼓捣一下,我前段时间学习go的时候,做了一个半成品,后面可能会用起来。
访问流程:
客户端:先发一个特定的 UDP 包给服务器的 UDP 端口,进行敲门;
服务器:不会响应这个敲门的 UDP 请求,而是验证这个包是否符合基本逻辑,是的话就把源IP加入到TCP端口的临时白名单内;
客户端:等待一会后对服务器的 TCP 端口进行请求;
服务器:如果IP在白名单内,才允许TCP建立连接,进行二次认证;
优点:默认只开放UDP端口,而且不响应,外面怎么扫都不知道你开放了啥。
方案五:上 ipv6
目前国内的 ipv6 还没有多少人关注,但很多云服务商已经支持了;
v6的攻击来源要少很多的;
腾讯云和阿里云都是支持的,只是需要手动开启;
我后面打算将管理和测试类的应用端口都设置为仅 v6 网络可访问。
优点:有些云厂商分的是一个v6段,是一个段哇,可以随时换IP了。
主机系统层防护
关闭不需要的服务
netstat -anlp 看看有啥不需要的服务还开放着的;
用户账户防护
不建议整太复杂的策略,简洁有效就行,比如密码最短多少位、过期时间这些企业环境才有意义的配置就完全没必要搞,图增烦恼。
简单策略
- 禁用 root 远程登录或远程密码登录(仅允许key)
- 禁止普通用户密码登录,只允许key
- 修改 ssh 监听端口为高位随机端口
ssh 相关配置
/etc/ssh/sshd_config
Port 52222 # 高位随机端口
PermitRootLogin no # 禁止 root 用户远程登录
PermitRootLogin prohibit-password # 禁止 root 用户使用密码远程登录(允许key)
PasswordAuthentication no # 禁止所有用户密码登录
上面是最基础的几项内容了, PermitRootLogin 这个二选一;
关于密码: 最好是改一个非常复杂的密码,然后忘了它。
关于密钥:密钥key很安全,它并没有在网络中传输,传输的只是key加密后的数据;
需要注意key文件本身的安全,不要被盗走;
本地如果运行恶意程序盗取途径可能有:
读取 ~/.ssh/config 文件,看里面有没有指定 key,扫描本地的 putty 或 xshell 等的配置文件,找key路径。
目前好像还没有听说有啥恶意程序盗取私钥这种例子,或许是这个攻击需要专门针对特定人群,面太窄,于是没人这样做吧;
但还是得小心,防范和缓解办法有:
- 产生 key 的时候,给 key 设置密码
- 定期更换key
- 不同用户使用不同key
要是key丢了,就从云控制台上登录root去重置,真忘了root密码还可以提工单给客服进行重置
应用层防护
其它的服务我就不举了,这里只说一下我的这个 nginx;
而且一般场景中 nginx 都是做为业务入口,在这一层做防护就很有效果;
nginx 常规策略
- 黑白名单(ip, url, agent)
- 请求参数校验,过滤恶意关键字
- 对请求来源域名做限制(我这台没做)
- 访问频率限制,比如使用 lua 做异常访问次数锁定限制
- 流量控制
- 上https(我这快下线了,于是就不弄了,要是大家支持的话,我再续上)
文末给出一个我当前使用的一个例子:用 nginx+lua 对异常频率较大的IP请求进行限制;
关于异常请求应该响应什么
这个就是智者见智的事情了,我的理解是这样的;
200 可以设置跳转回首页,假装只有简单的防护策略
404 假装不知道被攻击
403 明确告诉他我有防护准备,收手吧(攻击者:或许是条大鱼,值得继续突破)
444 让攻击者误认为网络有问题
500 我快不行了,别打我了(攻击者:他快不行了,再加把劲)
应用防护的注意事项
- 有接口的情况必须要有鉴权;
- 最好不要将 java, php, python等通过框架开发的程序直接放出到公网,加层nginx很有必要;
- 如果有代理其它类型的应用程序最好就监听127.0.0.1就可以了。
文末案例
一、使用腾讯云SDK修改安全组策略
这个示例只是对已有安全组策略中的某一条策略进行修改,熟悉接口后配合应用程序可以做成针对某IP进行拦截。
详细规则可以去看官方的API文档
https://cloud.tencent.com/document/api
本例使用的是 python, 实际官方对主流编程语言都提供SDK,反正都是http请求,熟悉接口后就算使用 curl 也是可以的;
简单流程描述
-
登录控制台,获取 SecretId 和 SecretKey 信息,默认没有的需要手动启用。
-
在控制台找到你的安全组,记住 所在区域 和 安全组的ID
区域就是 ap-<地址位置拼音>,如 ap-shanghai,
安全组的ID,就在安全组信息的第一列,如 sg-ccms23e2,
记住你要修改的ID在哪一行,最后一行的 PolicyIndex 值是 0,往上+1;
-
安装 sdk, 我使用的是 python 的 SDK
pip install –upgrade tencentcloud-sdk-python
-
制作脚本,后面网络发生变化后,就运行一下这个脚本
我实际在用的python脚本(根据个人情况替换参数)
自动查询本机的公网出口IP地址,更新到安全组策略中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Email:
# DateTime: 2021-11-20 05:37:43
__author__ = 'chenxu'
import json
import re
from tencentcloud.common import credential
from tencentcloud.common.profile.client_profile import ClientProfile
from tencentcloud.common.profile.http_profile import HttpProfile
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
from tencentcloud.vpc.v20170312 import vpc_client, models
import requests
def getIntelenetIP():
'''获取当前电脑的出网公网地址'''
url = "http://txt.go.sohu.com/ip/soip"
try:
r = requests.get(url)
if r.status_code != 200:
print('get ip address error')
exit()
ip = re.findall(r'\d+.\d+.\d+.\d+',r.text)[0]
except Exception as err:
print(err)
exit()
return ip
def ReplaceSecurityGroupPolicy(ip):
SecretId = 'xxxxx' # 替换
SecretKey = 'xxxxx' # 替换
Region = 'ap-shanghai' # 更换为所在区域
SecurityGroupId = 'sg-ccms23e2' # 更换为安全组的ID
Port = '39021' # 要调整安全组规则的那个端口号
try:
cred = credential.Credential(SecretId, SecretKey)
httpProfile = HttpProfile()
httpProfile.endpoint = "vpc.tencentcloudapi.com"
clientProfile = ClientProfile()
clientProfile.httpProfile = httpProfile
client = vpc_client.VpcClient(cred, Region, clientProfile)
req = models.ReplaceSecurityGroupPolicyRequest()
params = {
"SecurityGroupId": SecurityGroupId,
"SecurityGroupPolicySet": {
"Ingress": [
{
"PolicyIndex": 0, # 就是安全组内已有的哪一条策略,最后一条是0,往上就+1
"Protocol": "tcp", # 什么协议
"Port": Port, # 变量,需要修改的端口
"CidrBlock": ip, # 变量,需要修改的IP
"Action": "ACCEPT", # 允许
"PolicyDescription": "test2" # 备注
}
]
}
}
req.from_json_string(json.dumps(params))
resp = client.ReplaceSecurityGroupPolicy(req)
print(resp.to_json_string())
print(f'add {ip} to SecurityGroup Success')
except TencentCloudSDKException as err:
print(err)
ip = getIntelenetIP()
ReplaceSecurityGroupPolicy(ip)
|
主机防火墙更新可信IP
简述:先配置基础的防火墙区域,然后再弄个脚本每次更新IP
区域描述:
设置有2个区域,不同的源IP会进入不同的区域;
将默认会进入的 public 区域的默认策略设置为拒绝,只对外开放1个端口;
将需要信任的源IP加入 trusted 可信任区域内,允许全部访问请求;
这样请求到主机时,防火墙会根据它的IP,选择进入不同的区域,匹配不同的规则;
root用户命令行执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
# 查看防火墙是否在运行,有可能没有开启
firewall-cmd --state
# 开启防火墙,并设为开机启动
systemctl stop firewalld
systemctl start firewalld
systemctl enable firewalld
# public 区域设置
firewall-cmd --zone=public --add-port=5000/tcp --permanent # 添加允许所有IP访问的1个ssh端口
firewall-cmd --permanent --zone=public --set-target=DROP # 设置默认策略为拒绝
# 移除区域内其它策略
firewall-cmd --permanent --zone=public --remove-service=ssh
firewall-cmd --permanent --zone=public --remove-service=dhcpv6-client
firewall-cmd --permanent --zone=public --remove-service=cockpit
# trusted 可信区域设置
firewall-cmd --permanent --zone=trusted --add-source=<当前登录云主机时的源IP>
# 加载配置
firewall-cmd --reload
# 查看当前规则
firewall-cmd --zone=public --list-all
firewall-cmd --zone=trusted --list-all
|
基础设定就完成了,下面来动态设定部分
流程:
先 ssh 普通用户登录主机后,执行一个这个脚本,脚本会获取当前登录使用的IP地址,并添加到可信区域内去
为了方便普通用户使用 sudo,为这个防火墙命令设置为不需要输入密码
/etc/sudoers 文件末尾添加
1
|
<普通用户名称> ALL = NOPASSWD: /usr/bin/firewall-cmd
|
脚本:
1
2
3
4
5
6
7
8
9
10
11
|
#!/bin/bash
# 脚本名称: addIPtoTrusted.sh
# 重新加载防火墙配置,会清理掉上一次添加的临时源IP
sudo firewall-cmd --reload
# 获取本次登录主机所使用的源IP地址
remoteIp=$(w | grep wait| head -n 1 | awk '{print $3}')
# 添加到可信区域内
sudo firewall-cmd --zone=work --add-source=${remoteIp}
|
nginx+lua 认证访问源
原理和流程:
- 悄悄提供一个隐蔽的 http 接口,需要先到这个 URL 进行鉴权;
- 通过鉴权后,将IP地址放入缓存和redis
- 对其它接口(http+tcp)的访问,每次都去缓存内查询IP是否可信
这里使用的是 nginx 的衍生版本 openresty
我的认证逻辑
1
|
curl -i -d "name=<占位内容>&key=$(($(date +%s)*9-3000))" https://xxxx.xxxx.cn/sakdwe/wefewoiefwe
|
这里我设置了一个非常简单的认证逻辑
值为 当前时间戳*N-3000,服务器那边需要还原出来,如果等于服务器当前的时间戳(误差5s内),就通过;
当然也可以做成其它更复杂适用的认证逻辑;
本实例中只有先通过了这个 http 的认证,才能访问代理的 http资源和tcp资源。
说明
- 当前设定只同时支持1个IP,当然可以改为 hash 类型, 保留多个可信任IP
- 我代理的TCP资源也包括 SSH,哈哈
nginx 配置文件 nginx.conf
针对 http 的设置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
http {
lua_shared_dict http_acl_zone 12k; # 设置一个共享内存
server {
listen 80;
# 秘密的认证接口和lua脚本
location = /sakdwe/wefewoiefwe {
content_by_lua_file /opt/app/openresty/nginx/conf/set_acl.lua;
}
location / {
# 每次访问都先到这一步进行鉴权
access_by_lua_file /opt/app/openresty/nginx/conf/http_acl.lua;
root html;
index index.html index.htm;
}
}
}
|
针对 TCP 端口的设置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
stream {
lua_shared_dict tcp_acl_zone 12k;
# 友好的日志格式
log_format proxy '$remote_addr [$time_local] '
'$protocol $status $bytes_sent $bytes_received $session_time "$upstream_addr" '
'"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"';
server {
access_log logs/tcp-ssh.log proxy;
listen 52222; # 需要代理的 TCP 端口
preread_by_lua_file /opt/app/openresty/nginx/conf/tcp_acl.lua; # 鉴权脚本
proxy_connect_timeout 5s;
proxy_timeout 120s;
proxy_pass 127.0.0.1:10022;
}
}
|
认证逻辑 set_acl.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
-- 描述:
-- 先校验: 是post请求,post参数内需要有一个 key,按设定的逻辑还原出来验证
-- 通过后将真实IP先写入内部共享缓存,再写入 redis 缓存
local function set_http_acl_zone()
local ip = ngx.var.remote_addr -- 获取用户真实IP地址
-- 先设置nginx内部共享变量
local http_acl_zone = ngx.shared.http_acl_zone
http_acl_zone:set("ops_ip",ip)
-- 再设置 redis key 值
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000) -- 连接超时时间设定 1 sec
local ok, err = red:connect("127.0.0.1", 26379)
if not ok then
return false
end
# 将用户IP写入 redis
ok, err = red:set("ops_ip", ip)
if not ok then
return false
else
ngx.log(ngx.ERR, "acl ip write ops_ip, ")
return true
end
end
local function verify_ip()
-- 校验请求参数是否合规
if ngx.var.request_method ~= "POST" then
ngx.exit(ngx.HTTP_FORBIDDEN)
end
ngx.req.read_body()
local post_args = ngx.req.get_post_args()
-- local name = post_args["name"]
local pw = post_args["key"]
if not pw then
return false
end
local rtime = os.time() - (pw+3000)/9
if math.abs(rtime) < 5 then
return true
else
return false
end
end
if(verify_ip() == true) then
set_http_acl_zone()
ngx.exit(ngx.HTTP_NOT_ALLOWED) -- 405, 校验成功
return
else
ngx.exit(ngx.HTTP_NOT_FOUND) -- 404
end
|
HTTP接口鉴权逻辑 http_acl.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
-- 校验当前IP是否与nginx共享内存中的IP地址一致
local function ops_acl()
local clientip = ngx.var.remote_addr
local http_acl_zone = ngx.shared.http_acl_zone
local acl_ip = http_acl_zone:get("ops_ip")
if (acl_ip ~= nil) then
-- 共享变量内有数据
if (clientip==acl_ip) then
return true
else
return false
end
else
return false
end
end
if(ops_acl() ~= true) then
ngx.log(ngx.ERR, "request ip not in ops_ip")
ngx.exit(ngx.HTTP_FORBIDDEN)
return
else
return
end
|
TCP代理端口鉴权逻辑 tcp_acl.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
-- 用于 TCP 代理验证用户IP是否在 共享内存 或 redis 中,如果不在则拒绝代理
local function ops_acl()
local clientip = ngx.var.remote_addr
local tcp_acl_zone = ngx.shared.tcp_acl_zone
local acl_ip = tcp_acl_zone:get("ops_ip")
-- 共享变量内有数据
if (acl_ip ~= nil) then
if (clientip==acl_ip) then
return true
end
end
-- 共享变量内无数据,或与限制IP不一致(因为http和tcp的变量不同步)
-- 则从redis中读一次
ngx.log(ngx.ERR, "acl link redis +1")
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000) -- 1 sec
local ok, err = red:connect("127.0.0.1", 26379)
if not ok then
return false
end
local res, err = red:get("ops_ip")
if not res then
return false -- redis 中无数据
end
if res == ngx.null then
return false -- redis 中无数据
end
if (clientip==res) then
-- 同时也设置一份到内存变量,避免以后再读redis
local tcp_acl_zone = ngx.shared.tcp_acl_zone
tcp_acl_zone:set("ops_ip",clientip)
return true
else
return false
end
end
if(ops_acl() ~= true) then
ngx.log(ngx.ERR, "tcp request ip != ops_ip, ")
ngx.exit(403)
return
else
return
end
|
nginx 过滤异常访问逻辑
弄一个专门的文件,然后 include 加载到主文件中去就行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
# 屏蔽指定的IP
if ($remote_addr ~* "1.2.3.4|1.2.3.5") {
return 403;
break;
}
# 屏蔽访问方法
if ($request_method !~ ^(GET|POST)$ ) {
return 403;
break;
}
# 屏蔽访问客户端类型
if ($http_user_agent ~* "python|sqlmap|Go-http-client|ELinks|Certificate|bingbot|libcurl") {
return 403;
break;
}
# 屏蔽奇怪的请求路径或参数
if ($request_uri ~* "java|.jps|.php|login|wls-wsat|admin|select|.zip|test") {
return 403;
break;
}
# 异常的referer来源,我实际没有使用,如果有防盗链需求,可以配置在需要的路径下
if ($invalid_referer ~* "xxxxx.com") {
return 403;
break;
}
|
nginx+lua 防护,针对异常请求计数
原理:
将每次异常请求的IP记录下来,这个IP每次异常则计数+1;
如果3分钟内多于3次异常就屏蔽此IP 3分钟(从第1次开始计的时);
如果3分钟内异常多于9次,则延长至10分钟;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# nginx.conf
http {
lua_shared_dict acl_ip_tables 1m; # 开一个共享内存区域用来计数
server {
# 请求时逻辑
access_by_lua_file /opt/app/nginx/conf/acl_req.lua;
# 响应时逻辑
header_filter_by_lua_file /opt/app/nginx/conf/acl_res.lua;
location {}
}
}
|
请求时逻辑 acl_req.lua
1
2
3
4
5
6
7
8
9
|
-- 如果这个IP已经存在异常计数,且大于3次就拒绝访问
local dict = ngx.shared.acl_ip_tables
local ip = ngx.var.remote_addr
num = dict:get(ip)
if ( num ~= nil ) and ( num > 2 ) then
ngx.log(ngx.ERR, "acl_num >2: ", num, ' DROP 403 ')
return ngx.exit(403)
end
|
响应时逻辑 acl_res.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
-- 本次请求的响应码,如果不是 200/304 或特定响应码,视为异常
local status_code = ngx.var.status
local res_status = 0
if ( status_code == '200' ) or ( status_code == '304' ) then
res_status = 1
elseif ( status_code == '999' ) then
res_status = 1
end
if ( res_status == 0 ) then
local ip = ngx.var.remote_addr
local dict = ngx.shared.acl_ip_tables
num = dict:get(ip)
if num == nil then
-- 第一次异常访问
ngx.log(ngx.ERR, "num: ",num, " status_code:", status_code," remote add: ", ngx.var.remote_addr)
dict:set(ip, 1, 180)
else
-- 存在异常时仍继续访问 +1
num = dict:incr(ip, 1)
ngx.log(ngx.ERR, "acl_num+1=", num)
-- 3分钟内异常大于9次,延长至10分钟
if ( num == 9 ) then
dict:set(ip, 9, 600)
ngx.log(ngx.ERR, "num: ",num, " set 10min: ", ip)
end
end
end
|
本来想一次写全一些,但我自己很多时候都有“太长不看”的习惯,所以今后还是尽量以短篇为主吧。
关于我的博客
- 用的是 hugo
- 主题使用的是 maupassant, 配置极为简单
- 写好 Markdown 的文件后,运行一下 hugo 命令即可生成静态页面,上传至 nginx 目录即可
下一篇计划年后再写写最近接触到的一些有意思的内容。
最后再祝大家新年快乐、恭喜发财、虎年大吉,虎虎生威!!