批量修改AlibabaCloud阿里雲service訪問控制和安全組白名單IP設置Python腳本

更新一

下文腳本已經重寫並分享到GitHub,而且新的腳本同時集成了亞馬遜AWS的相關操作實現,參考https://github.com/Bilery-Zoo/Cloud_Platform_Maintenance/tree/master/CloudPlatform_WhitelistIP_Switcher。而亞馬遜AWS單獨實現的腳本,也可以參考之後的博文https://blog.csdn.net/sweeper_freedoman/article/details/100868645


阿里雲 / AlibabaCloud的若干服務(service)出於安全考慮都有訪問控制(Access control)或者叫安全組(Security Group)的設計,類似白名單黑名單的功能,用於控制哪些網絡 / 網絡段可以訪問或者不能訪問該服務實例,是一種重要的安全策略。例如負載均衡SLB、內容分發網絡CDN和雲關係型數據庫RDS等。

生產環境一般都會在這些服務的白名單裏面配置本地的公網IP(公司或個人公網IP),以使得只有自己的本地網絡纔可以訪問相關服務實例。但是如果本地公網IP發生變更,一個個地手動去修改雲上配置,一方面工作比較繁複,另一方面也不安全。所以,用腳本去做是最理想的事情。

以下Python腳本即做了SLB、CDN和RDS三個service的白名單IP變更。過程分爲兩步:

1.向配置了舊的公網IP的白名單裏面添加新的公網IP

2.從添加了新的公網IP的白名單裏面刪除舊的公網IP

這樣可以實現平穩切換。

腳本文件定義如下:

├── functions.py

        base function of AlibabaCloud handlers module (may need OOP rewriting next time)
├── log.py

        log logging module
├── new_ip_append.py

        new IP appending call file
└── old_ip_delete.py

        odl IP deleting call file

functions.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-


"""
create_author : 蛙鱖雞鸛狸猿
create_time   : 2019-08-30
program       : *_* base function of AlibabaCloud handlers module (may need OOP rewriting next time) *_*
"""


import re
import json
import typing

from aliyunsdkcore.client import AcsClient
from aliyunsdkslb.request.v20140515 import DescribeAccessControlListsRequest, DescribeAccessControlListAttributeRequest
from aliyunsdkslb.request.v20140515 import AddAccessControlListEntryRequest, RemoveAccessControlListEntryRequest
from aliyunsdkcdn.request.v20180510 import DescribeUserDomainsRequest, DescribeCdnDomainConfigsRequest
from aliyunsdkcdn.request.v20180510 import BatchSetCdnDomainConfigRequest
from aliyunsdkrds.request.v20140815 import DescribeDBInstancesRequest, DescribeDBInstanceIPArrayListRequest
from aliyunsdkrds.request.v20140815 import ModifySecurityIpsRequest

import log

logger = log.LOG().logger()


@log.log(logger=logger, if_exit=True)
def get_slb_gip_info(re_ip, access_key_id=None, access_key_secret=None, region_id=None, ) -> typing.Generator:
    """
    Get IP info of SLB Access Control.
    :param re_ip: IP to get info.
    :param access_key_id: AlibabaCloud auth access_key_id.
    :param access_key_secret: AlibabaCloud auth access_key_secret.
    :param region_id: AlibabaCloud region_id.
    :return: Dict, {"AclId": ..., }.
    """
    client = AcsClient(access_key_id, access_key_secret, region_id)
    request = DescribeAccessControlListsRequest.DescribeAccessControlListsRequest()
    response = client.do_action_with_exception(request)
    for instance in json.loads((response.decode("utf-8")))["Acls"]["Acl"]:
        request_sub = DescribeAccessControlListAttributeRequest.DescribeAccessControlListAttributeRequest()
        request_sub.set_AclId(instance["AclId"])
        response_sub = client.do_action_with_exception(request_sub)
        data_sub = json.loads((response_sub.decode("utf-8")))
        if re.search(re_ip, str(data_sub)):
            gip_info = {"AclId": data_sub["AclId"], }
            logger.info("get_slb_gip_info:\n\t{gip_info}".format(gip_info=gip_info))
            yield gip_info


@log.log(logger=logger, if_exit=True)
def get_cdn_gip_info(re_ip, access_key_id=None, access_key_secret=None, region_id=None, ) -> typing.Generator:
    """
    Get IP info of SLB Access Control.
    :param re_ip: IP to get info.
    :param access_key_id: AlibabaCloud auth access_key_id.
    :param access_key_secret: AlibabaCloud auth access_key_secret.
    :param region_id: AlibabaCloud region_id.
    :return: Dict, {"DomainNames": ..., }.
    """
    client = AcsClient(access_key_id, access_key_secret, region_id)
    request = DescribeUserDomainsRequest.DescribeUserDomainsRequest()
    response = client.do_action_with_exception(request)
    for instance in json.loads((response.decode("utf-8")))["Domains"]["PageData"]:
        request_sub = DescribeCdnDomainConfigsRequest.DescribeCdnDomainConfigsRequest()
        request_sub.set_DomainName(instance["DomainName"])
        response_sub = client.do_action_with_exception(request_sub)
        data_sub = json.loads((response_sub.decode("utf-8")))
        if re.search(re_ip, str(data_sub)):
            for config in data_sub["DomainConfigs"]["DomainConfig"]:
                if config["FunctionName"] == "ip_allow_list_set":
                    gip_info = {"DomainNames": instance["DomainName"],
                                "ip_allow_list_set": config["FunctionArgs"]["FunctionArg"][0]["ArgValue"]}
                    logger.info("get_cdn_gip_info:\n\t{gip_info}".format(gip_info=gip_info))
                    yield gip_info
                    break


@log.log(logger=logger, if_exit=True)
def get_rds_gip_info(re_ip, access_key_id=None, access_key_secret=None, region_id=None, ) -> typing.Generator:
    """
    Get IP info of RDS Data Security.
    :param re_ip: IP to get info.
    :param access_key_id: AlibabaCloud auth access_key_id.
    :param access_key_secret: AlibabaCloud auth access_key_secret.
    :param region_id: AlibabaCloud region_id.
    :return: Dict, {"DBInstanceId": ..., }.
    """
    client = AcsClient(access_key_id, access_key_secret, region_id)
    request = DescribeDBInstancesRequest.DescribeDBInstancesRequest()
    response = client.do_action_with_exception(request)
    # print(json.loads((response.decode("utf-8"))))
    for instance in json.loads((response.decode("utf-8")))["Items"]["DBInstance"]:
        request_sub = DescribeDBInstanceIPArrayListRequest.DescribeDBInstanceIPArrayListRequest()
        request_sub.set_DBInstanceId(instance["DBInstanceId"])
        response_sub = client.do_action_with_exception(request_sub)
        data_sub = json.loads((response_sub.decode("utf-8")))
        # print(data_sub)
        if re.search(re_ip, str(data_sub)):
            for config in data_sub["Items"]["DBInstanceIPArray"]:
                if re.search(re_ip, str(config)):
                    gip_info = {"DBInstanceId": instance["DBInstanceId"],
                                "DBInstanceIPArrayName": config["DBInstanceIPArrayName"],
                                "DBInstanceIPArrayAttribute": config["DBInstanceIPArrayAttribute"],
                                "WhitelistNetworkType": config["WhitelistNetworkType"],
                                "SecurityIPType": config["SecurityIPType"]}
                    logger.info("get_rds_gip_info:\n\t{gip_info}".format(gip_info=gip_info))
                    yield gip_info
                    break


@log.log(logger=logger, if_exit=True)
def add_slb_gip_info(add_entry: list, acl_id: str, access_key_id=None, access_key_secret=None, region_id=None, ):
    """
    Add IP info into SLB Access Control. See also:
        https://www.alibabacloud.com/help/zh/doc-detail/70023.htm.
    :param add_entry: List consisted of Dict, eg `[{"entry": "IP / IP block", "comment": "..."}, ]`.
    :param acl_id: AclId of SLB.
    :param access_key_id: AlibabaCloud auth access_key_id.
    :param access_key_secret: AlibabaCloud auth access_key_secret.
    :param region_id: AlibabaCloud region_id.
    :return: Python built-in exit code.
    """
    logger.warning("add_slb_gip_info:\n\t{info}".format(info=str(
        {"acl_id": acl_id, "add_entry": add_entry, }
    )))
    client = AcsClient(access_key_id, access_key_secret, region_id)
    request = AddAccessControlListEntryRequest.AddAccessControlListEntryRequest()
    request.set_accept_format('json')
    request.set_AclId(acl_id)
    request.set_AclEntrys(str(add_entry))
    client.do_action_with_exception(request)


@log.log(logger=logger, if_exit=True)
def remove_slb_gip_info(remove_entry: list, acl_id: str, access_key_id=None, access_key_secret=None, region_id=None, ):
    """
    Remove IP info into SLB Access Control. See also:
        https://www.alibabacloud.com/help/zh/doc-detail/70055.htm.
    :param remove_entry: List consisted of Dict, eg `[{"entry": "IP / IP block", "comment": "..."}, ]`.
    :param acl_id: AclId of SLB.
    :param access_key_id: AlibabaCloud auth access_key_id.
    :param access_key_secret: AlibabaCloud auth access_key_secret.
    :param region_id: AlibabaCloud region_id.
    :return: Python built-in exit code.
    """
    logger.warning("remove_slb_gip_info:\n\t{info}".format(info=str(
        {"acl_id": acl_id, "remove_entry": remove_entry, }
    )))
    client = AcsClient(access_key_id, access_key_secret, region_id)
    request = RemoveAccessControlListEntryRequest.RemoveAccessControlListEntryRequest()
    request.set_accept_format('json')
    request.set_AclId(acl_id)
    request.set_AclEntrys(str(remove_entry))
    client.do_action_with_exception(request)


@log.log(logger=logger, if_exit=True)
def modify_cdn_gip_info(modify_entry: list, domain_name: str, access_key_id=None, access_key_secret=None, region_id=None, ):
    """
    Modify IP info into CDN Access Control. See also:
        https://www.alibabacloud.com/help/zh/doc-detail/90915.htm.
    :param modify_entry: List consisted of Dict, eg `[{"functionArgs": [{"argName": "ip_list", "argValue": ...}], "functionName": "ip_allow_list_set"}]`.
    :param domain_name: DomainName of CDN.
    :param access_key_id: AlibabaCloud auth access_key_id.
    :param access_key_secret: AlibabaCloud auth access_key_secret.
    :param region_id: AlibabaCloud region_id.
    :return: Python built-in exit code.
    """
    logger.warning("modify_cdn_gip_info:\n\t{info}".format(info=str(
        {"domain_name": domain_name, "modify_entry": modify_entry, }
    )))
    client = AcsClient(access_key_id, access_key_secret, region_id)
    request = BatchSetCdnDomainConfigRequest.BatchSetCdnDomainConfigRequest()
    request.set_accept_format('json')
    request.set_DomainNames(domain_name)
    request.set_Functions(modify_entry)
    client.do_action_with_exception(request)


@log.log(logger=logger, if_exit=True)
def modify_rds_gip_info(modify_ip: str, modify_mode: str, db_instance_id: str,
                        ip_array_name: str, ip_array_attribute: str, ip_type: str, network_type: str,
                        access_key_id=None, access_key_secret=None, region_id=None, ):
    """
    Modify IP info into RDS Data Security. See also:
        https://www.alibabacloud.com/help/zh/doc-detail/26242.htm?spm=a2c63.p38356.879954.54.6eb34542JGBoSp.
    :param modify_ip: IP address to modify.
    :param modify_mode: modify mode(valid values: Append, Delete, Cover).
    :param db_instance_id: RDS instance ID.
    :param ip_array_name: RDS instance IP array name.
    :param ip_array_attribute: RDS instance IP array attribute.
    :param ip_type: IP type.
    :param network_type: network type.
    :param access_key_id: AlibabaCloud auth access_key_id.
    :param access_key_secret: AlibabaCloud auth access_key_secret.
    :param region_id: AlibabaCloud region_id.
    :return: Python built-in exit code.
    """
    logger.warning("modify_rds_gip_info:\n\t{info}".format(info=str(
        {"db_instance_id": db_instance_id, "ip_array_name": ip_array_name,
         "modify_mode": modify_mode, "modify_ip": modify_ip, }
    )))
    client = AcsClient(access_key_id, access_key_secret, region_id)
    request = ModifySecurityIpsRequest.ModifySecurityIpsRequest()
    request.set_accept_format('json')
    request.set_SecurityIps(modify_ip)
    request.set_ModifyMode(modify_mode)
    request.set_DBInstanceId(db_instance_id)
    request.set_DBInstanceIPArrayName(ip_array_name)
    request.set_DBInstanceIPArrayAttribute(ip_array_attribute)
    request.set_SecurityIPType(ip_type)
    request.set_WhitelistNetworkType(network_type)
    client.do_action_with_exception(request)


if __name__ == "__main__":
    """
    SLB
    """
    # for i in get_slb_gip_info():
    #     print(i)
    # add_slb_gip_info("acl-......",
    #                  [{"entry": "127.0.0.0/32", "comment": "テストの為IP"}, ]
    #                  )
    # remove_slb_gip_info("acl-......",
    #                     [{"entry": "127.0.0.0/32", }, ]
    #                     )
    """
    CDN
    """
    # for i in get_cdn_gip_info():
    #     print(i)
    # modify_cdn_gip_info("www.ip_switch.test.com", [
    #     {"functionArgs": [{"argName": "ip_list", "argValue": "127.0.0.0/32,127.0.0.1/32"}],
    #  "functionName": "ip_allow_list_set"}])
    """
    RDS
    """
    # for i in get_rds_gip_info():
    #     print(i)
    # modify_rds_gip_info("127.0.0.0", "Delete", "rm-......", "test", '', "IPv4", "MIX")

log.py

#!/usr/bin/env python3.6
# -*- coding: utf-8 -*-


"""
create_author : 蛙鱖雞鸛狸猿
create_time   : 2019-08-30
program       : *_* log logging module *_*
"""


import os
import sys
import logging
import functools


class LOG(object):
    """
    Log logging definition.
    """

    def __init__(self, level=logging.INFO, stream=sys.stdout, filemode='a',
                 filename=os.path.dirname(os.path.abspath(__file__)) + '/' + "log",
                 datefmt="%Y-%m-%d %H:%M:%S",
                 format="%(asctime)s\t%(levelname)s\t< Module: %(module)s, Function: %(funcName)s >\t%(message)s",
                 **kwargs):
        """
        LOG init.
        :param level: arg pass to standard library logging.basicConfig().
        :param stream: arg pass to standard library logging.basicConfig().
        :param filemode: arg pass to standard library logging.basicConfig().
        :param filename: arg pass to standard library logging.basicConfig().
        :param datefmt: arg pass to standard library logging.basicConfig().
        :param format: arg pass to standard library logging.basicConfig().
        :param kwargs: arg pass to standard library logging.basicConfig().
        """
        self.level = level
        self.stream = stream
        self.filename = filename
        self.filemode = filemode
        self.datefmt = datefmt
        self.format = format
        self.kwargs = kwargs

    def logger(self, name=__name__):
        """
        Logger object generates.
        :param name: Logger name(parameters pass to standard library logging.getLogger()).
        :return: Logger object.
        """
        args = {
            "level": self.level,
            "stream": self.stream,
            "filename": self.filename,
            "filemode": self.filemode,
            "datefmt": self.datefmt,
            "format": self.format,
        }
        try:
            if self.stream and self.filename:
                del args["stream"]
        except KeyError:
            pass
        logging.basicConfig(**args, **self.kwargs)
        return logging.getLogger(name)


def raise_log(raised_except, msg=''):
    """
    Raise exception by hand to escape error exit caused by built-in [raise].
    :param raised_except: Exception object.
    :param msg: Exception feedback message.
    :return: Python's built-in exit code.
        Output Exception of [raised_except].
    """
    try:
        raise raised_except(msg)
    except raised_except as E:
        logging.exception(E)


def log(logger=None, exc_msg='', if_exit=False, exit_msg='', result_check=False, check_except=None, check_msg=''):
    """
    Log logging decorator function.
    :param logger: Logger object(see also logging.getLogger()).
    :param exc_msg: extra message to return when exceptions catch.
    :param if_exit: Boolean.
        Whether to exit or not when catching exception.
    :param exit_msg: extra message to return when exceptions catch and exit.
    :param result_check: Boolean.
        Whether or not to check Python's False status result of [func]'s return.
    :param check_except: Exception object to raise when [result_check].
    :param check_msg: Exception feedback message to return when [result_check].
    :return: decorated function [func]'s return.
    """
    if not logger:
        logger = LOG().logger()

    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            result = None
            try:
                result = func(*args, **kwargs)
            except BaseException:
                logger.exception(exc_msg)
                if if_exit:
                    sys.exit(exit_msg)
            finally:
                if result_check:
                    if not result:
                        raise_log(check_except, check_msg)
                return result
        return wrapper
    return decorator


if __name__ == "__main__":
    LOG_Test = LOG
    # LOG().logger().info("INFO:Come...")
    # LOG().logger().error("ERROR:Come...")

    # @log()
    # def get(a, b):
    #     return a / b
    # get(a=1, b=0)

new_ip_append.py

#!/usr/bin/env python3.6
# -*- coding: utf-8 -*-


"""
create_author : 蛙鱖雞鸛狸猿
create_time   : 2019-08-30
program       : *_* new IP appending call file *_*
"""


import re
import functions


"""
*******************************************************************************
Unit test button
                        |\_/|
                        | ・x・ |
               \_____/    |
                 |         |
                \       ノ 
             ((( (/ ̄ ̄ ̄ ̄(/ヽ)
"""
UT_FLAG = True
"""
Default `True` for script testing
Set to `False` when production using
*******************************************************************************
"""


new_gip = ""
old_gip = "127.0.0.0" if UT_FLAG else ""
re_ip = re.compile(r"127[.]0[.]0[.]0") if UT_FLAG else re.compile(r"127[.]0[.]0[.]11")

sbcloud_region = ["ap-northeast-1",
                  "cn-qingdao",
                  "cn-beijing",
                  "cn-zhangjiakou",
                  "cn-huhehaote",
                  "cn-hangzhou",
                  "cn-shanghai",
                  "cn-shenzhen",
                  "cn-chengdu",
                  "cn-hongkong",
                  "ap-southeast-1",
                  "ap-southeast-2",
                  "ap-southeast-3",
                  "ap-southeast-5",
                  "ap-south-1",
                  "us-west-1",
                  "us-east-1",
                  "eu-central-1",
                  "eu-west-1",
                  "me-east-1",
                  ]
SBCloud_Access_Key = {"access_key_id": "",
                      "access_key_secret": "",
                      "region_id": sbcloud_region[0],
                      }


# append new IP into SLB
for slb_gip_info in functions.get_slb_gip_info(re_ip, **SBCloud_Access_Key):
    slb_append_info = ([{"entry": "{new_gip}/32".format(new_gip=new_gip), "comment": "汐留GIP"}, ],
                       slb_gip_info["AclId"], )
    functions.add_slb_gip_info(*slb_append_info, **SBCloud_Access_Key)


# append new IP into CDN
for cdn_gip_info in functions.get_cdn_gip_info(re_ip, **SBCloud_Access_Key):
    ip_append_info = re.sub(re_ip, "{old_gip}/32,{new_gip}".format(old_gip=old_gip, new_gip=new_gip),
                            cdn_gip_info["ip_allow_list_set"])
    cdn_append_info = ([{"functionArgs": [{"argName": "ip_list",
                                           "argValue": "{ip_append_info}".format(ip_append_info=ip_append_info)}],
                         "functionName": "ip_allow_list_set"}],
                       cdn_gip_info["DomainNames"], )
    functions.modify_cdn_gip_info(*cdn_append_info, **SBCloud_Access_Key)


# append new IP into RDS
for rds_gip_info in functions.get_rds_gip_info(re_ip, **SBCloud_Access_Key):
    rds_append_info = (new_gip, "Append", rds_gip_info["DBInstanceId"],
                       rds_gip_info["DBInstanceIPArrayName"], rds_gip_info["DBInstanceIPArrayAttribute"],
                       rds_gip_info["SecurityIPType"], rds_gip_info["WhitelistNetworkType"], )
    functions.modify_rds_gip_info(*rds_append_info, **SBCloud_Access_Key)

old_ip_delete.py

#!/usr/bin/env python3.6
# -*- coding: utf-8 -*-


"""
create_author : 蛙鱖雞鸛狸猿
create_time   : 2019-08-30
program       : *_* odl IP deleting call file *_*
"""


import re
import functions


"""
*******************************************************************************
Unit test button
                        |\_/|
                        | ・x・ |
               \_____/    |
                 |         |
                \       ノ 
             ((( (/ ̄ ̄ ̄ ̄(/ヽ)
"""
UT_FLAG = True
"""
Default `True` for script testing
Set to `False` when production using
*******************************************************************************
"""


old_gip = "127.0.0.0" if UT_FLAG else ""
re_ip = re.compile(r"127[.]0[.]0[.]0") if UT_FLAG else re.compile(r"127[.]0[.]0[.]11")


sbcloud_region = ["ap-northeast-1",
                  "cn-qingdao",
                  "cn-beijing",
                  "cn-zhangjiakou",
                  "cn-huhehaote",
                  "cn-hangzhou",
                  "cn-shanghai",
                  "cn-shenzhen",
                  "cn-chengdu",
                  "cn-hongkong",
                  "ap-southeast-1",
                  "ap-southeast-2",
                  "ap-southeast-3",
                  "ap-southeast-5",
                  "ap-south-1",
                  "us-west-1",
                  "us-east-1",
                  "eu-central-1",
                  "eu-west-1",
                  "me-east-1",
                  ]
SBCloud_Access_Key = {"access_key_id": "LTAIhY89cQ2i0gdi",
                      "access_key_secret": "cXkEFvWUjpQvl0d4F0Le0dr2SI9d1o",
                      "region_id": sbcloud_region[0],
                      }


# delete old IP into SLB
for slb_gip_info in functions.get_slb_gip_info(re_ip, **SBCloud_Access_Key):
    slb_remove_info = ([{"entry": "{old_gip}/32".format(old_gip=old_gip), }, ],
                       slb_gip_info["AclId"], )
    functions.remove_slb_gip_info(*slb_remove_info, **SBCloud_Access_Key)


# delete old IP into CDN
for cdn_gip_info in functions.get_cdn_gip_info(re_ip, **SBCloud_Access_Key):
    ip_remove_info = re.sub("{old_gip}/32,".format(old_gip=old_gip), '', cdn_gip_info["ip_allow_list_set"])
    cdn_remove_info = ([{"functionArgs": [{"argName": "ip_list",
                                           "argValue": "{ip_remove_info}".format(ip_remove_info=ip_remove_info)}],
                         "functionName": "ip_allow_list_set"}],
                       cdn_gip_info["DomainNames"], )
    functions.modify_cdn_gip_info(*cdn_remove_info, **SBCloud_Access_Key)


# delete old IP into RDS
for rds_gip_info in functions.get_rds_gip_info(re_ip, **SBCloud_Access_Key):
    rds_remove_info = (old_gip, "Delete", rds_gip_info["DBInstanceId"],
                       rds_gip_info["DBInstanceIPArrayName"], rds_gip_info["DBInstanceIPArrayAttribute"],
                       rds_gip_info["SecurityIPType"], rds_gip_info["WhitelistNetworkType"], )
    functions.modify_rds_gip_info(*rds_remove_info, **SBCloud_Access_Key)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章