flask 定时监控阿里云 ECS 安全组授权对象为公网的规则并进行钉钉报警

目录结构

创建 conf.py 文件,配置区分生产环境和开发环境

# 生产环境
class ProductionConfig:
    IGNORE_PORTS_LIST = ['80/80', '443/443', '22/22', '8080/8080', '8899/8899', '43999/43999', '8011/8011', '3333/3333']                # 定义排除的端口
    DINGTALK_URL = 'https://oapi.dingtalk.com/robot/send?access_token=8888888888888888888888888888888888888888888888888888888888888888' # 运维组


# 开发环境
class DevelopmentConfig:
    IGNORE_PORTS_LIST = ['80/80', '443/443', '22/22']
    DINGTALK_URL = 'https://oapi.dingtalk.com/robot/send?access_token=8888888888888888888888888888888888888888888888888888888888888888' # 运维组

在 operate 包下,创建 dingtalk.py 文件,用来发送钉钉告警信息

import json
import requests
# 定义环境
from conf import ProductionConfig as Config

url = Config.DINGTALK_URL


def send_msg(msg):
    parameter = {
        "msgtype": "text",
        "text": {
            "content": "告警 -- 阿里云正式环境 安全组 有对公网开放的端口,详情为:%s ,请及时查看~" % msg
        },
    }
    headers = {
        'Content-Type': 'application/json'
    }
    requests.post(url, data=json.dumps(parameter), headers=headers)

在 operate 包下,创建 check_securegroup.py 文件,用来检查阿里云ECS安全组的规则

# __author__: "klvchen"
# date: 2020/10/12

from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.acs_exception.exceptions import ClientException
from aliyunsdkcore.acs_exception.exceptions import ServerException
from aliyunsdkecs.request.v20140526.DescribeSecurityGroupAttributeRequest import DescribeSecurityGroupAttributeRequest
import json
from  operate import  dingtalk
# 定义环境
from conf import ProductionConfig as Config

# 定义忽略的端口号
ignore_ports_list = Config.IGNORE_PORTS_LIST

client = AcsClient('xxxxxxxxxxxxxx', 'xxxxxxxxxxxxxxx', 'cn-shenzhen')

request = DescribeSecurityGroupAttributeRequest()
request.set_accept_format('json')

request.set_SecurityGroupId("sg-xxxxxxxxxxxxxx")

def check_ports():
    response = client.do_action_with_exception(request)

    result_json = json.loads((str(response, encoding='utf-8')))

    # 定义可能有问题的端口
    ports_list = []

    for p in range(len(result_json['Permissions']['Permission'])):
        # 获取对全部公网开放的端口
        if result_json['Permissions']['Permission'][p]['SourceCidrIp'] == '0.0.0.0/0' and result_json['Permissions']['Permission'][p]["PortRange"] not in ignore_ports_list:
            # 排除 ICMP 协议
            if result_json['Permissions']['Permission'][p]["IpProtocol"] != 'ICMP':
                ports_list.append(result_json['Permissions']['Permission'][p]["PortRange"])

    return ports_list


# 控制报警次数
alarm_num = 1


# 重设报警次数
def reset_alarm_num():
    global alarm_num
    alarm_num = 1


def main():
    result = check_ports()

    # 使用全局变量 alarm_num
    global alarm_num

    if result and alarm_num > 0:
        # 发送钉钉报警
        dingtalk.send_msg(result)
        alarm_num = alarm_num - 1

编写 app.py 文件

from flask import Flask
from flask_apscheduler import APScheduler
from operate import check_securegroup
import logging

app = Flask(__name__)


@app.route('/')
def hello_world():
    app.logger.warning('warning log: It works!')         # 访问输出 warning 日志
    return 'Hello World!'


class SchedulerConfig(object):
    JOBS = [
        {
            'id': 'check_mysql1',                        # 任务id
            'func': '__main__:check_securegroup.main',   # 任务执行程序
            'args': None,                                # 执行程序参数
            'trigger': 'interval',                       # 任务执行类型,定时器
            'seconds': 300                               # 任务执行时间,单位秒 (每5分钟调用一次)
        },
        {
            'id': 'reset_alarm_num',
            'func': '__main__:check_securegroup.reset_alarm_num',  # 重设 alarm_num
            'args': None,
            'trigger': 'interval',
            'hours': 6                                   # 每 6 个小时重设一次报警次数
        }
    ]


if __name__ == '__main__':
    handler = logging.FileHandler('flask.log', encoding='UTF-8')   # 设置日志字符集和存储路径名字
    logging_format = logging.Formatter(                            # 设置日志格式
        '%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(lineno)s - %(message)s')
    handler.setFormatter(logging_format)
    app.logger.addHandler(handler)

    app.config.from_object(SchedulerConfig())            # 为实例化的flask引入定时任务配置
    scheduler = APScheduler()                            # 实例化APScheduler
    scheduler.init_app(app)                              # 把任务列表载入实例flask
    scheduler.start()                                    # 启动任务计划

    app.run()

运行, 进入 app.py 所在的目录

python app.py

附上依赖

aliyun-python-sdk-acs==0.0.3
aliyun-python-sdk-core==2.13.26
aliyun-python-sdk-core-v3==2.13.11
aliyun-python-sdk-ecs==4.19.12
APScheduler==3.6.3
certifi==2020.6.20
chardet==3.0.4
click==7.1.2
Flask==1.1.2
Flask-APScheduler==1.11.0
idna==2.10
itsdangerous==1.1.0
Jinja2==2.11.2
jmespath==0.10.0
MarkupSafe==1.1.1
python-dateutil==2.8.1
pytz==2020.1
requests==2.24.0
six==1.15.0
tzlocal==2.1
urllib3==1.25.10
Werkzeug==1.0.1

参考:https://help.aliyun.com/document_detail/25555.html?spm=a2c4g.11186623.6.1374.23ad27afN2xh8s

posted @ 2020-10-13 16:28  klvchen  阅读(257)  评论(0)    收藏  举报