Python: boto3 и скрипт обновления AWS Security Group

By | 12/31/2017
 

PythonИмеется VPN сервис, работающий в AWS на EC2, доступ к которому ограничен с помощью security group.

Задача: набросать скрипт, который работал бы под Linux/Windows (VPN-ом пользуюсь не я один), который позволял бы быстро добавить запись с новым IP для доступа.

Используем Python 3 и boto3.

Зависимости

Первым делом – необходимо придумать как установить зависимости, в частности – библиотеку boto3, что бы упростить жизнь пользователю (хотя Python всё-равно придётся устанавливать вручную).

Добавляем функцию pip_install():

...
def pip_install(pkg_name):
    pip.main(['install', pkg_name])
...

И её вызов, сейчас скрипт полностью выглядит так:

#!/usr/bin/env python


import sys
import pip


def pip_install(pkg_name):
    pip.main(['install', pkg_name])


if __name__ == '__main__':

    print('\nChecking for dependencies...')
    try:
        import boto3
        print('boto3 library already installed - OK.\n')
    except ImportError:
        print('boto3 not found - going to install it now...\n')
        pip_install('boto3')
        print('\nDone - please, restart script nnow.\n')
        exit(0)

Запускаем, проверяем:

./aws_update_setevoy_vpn_sg.py
Checking for dependencies...
boto3 library already installed - OK.

AWS IAM пользователь

Теперь, прежде чем продолжать – добавим отдельного пользователя, у которого будет доступ только к определённой security group.

Создаём новую IAM policy с правами DescribeSecurityGroups и AuthorizeSecurityGroupIngress:

 

Создаём пользователя с programmatic access (доступ в AWS Console ему совсем не нужен):

 

Подключаем ему созданную политику:

Проверяем доступ.

Задаём переменные для авторизации:

export AWS_ACCESS_KEY_ID=AKI***WNA
export AWS_SECRET_ACCESS_KEY=oaZ***uMW

Пробуем получить список EC2 машин, к которому доступа мы не давали (DescribeInstances):

aws ec2 describe-instances
An error occurred (UnauthorizedOperation) when calling the DescribeInstances operation: You are not authorized to perform this operation.

Хорошо, и пробуем получить список групп безопасности – тут права заданы, доступ должен быть:

aws ec2 describe-security-groups --output text
SECURITYGROUPS  rtfm_prod_db_access     sg-3e723c58     rtfm_prod_db_access     264418146286    vpc-3941cb5c
IPPERMISSIONS   3306    tcp     3306
...

ОК, работает.

boto3

EC2 client

Теперь можно приступать к работе с ресурсами AWS.

Первым делом – создаём клиент – EC2.Client:

...
def create_conn(aws_access_key, aws_secret_key, ec2_region):
    conn = boto3.client('ec2', aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key, region_name=ec2_region)
    return conn
...

Добавляем вызов этой функции:

...
    # create boto3 connection first
    client = create_conn('AKI***EZQ', '+HKp***k1P', 'eu-west-1')

Добавление rule

Добавляем ещё одну функцию add_rule(), в которой вызываем метод authorize_security_group_ingress() из объекта client:

...
def add_rule(client, sg_id, cidr):
    client.authorize_security_group_ingress(GroupId=sg_id, IpProtocol='udp',CidrIp=cidr,FromPort=1194,ToPort=1194)

И добавляем запуск всего, теперь скрипт полностью выглядит так:

#!/usr/bin/env python


import sys
import pip


def pip_install(pkg_name):

    pip.main(['install', pkg_name])


def create_conn(aws_access_key, aws_secret_key, ec2_region):

    conn = boto3.client('ec2', aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key, region_name=ec2_region)
    return conn


def add_rule(client, sg_id, cidr):

    client.authorize_security_group_ingress(GroupId=sg_id, IpProtocol='udp',CidrIp=cidr,FromPort=1194,ToPort=1194)


if __name__ == '__main__':

    # check for first argument
    assert(len(sys.argv) == 2)

    print('\nChecking for dependencies...')
    try:
        import boto3
        print('boto3 library already installed - OK.\n')
    except ImportError:
        print('boto3 not found - going to install it now...\n')
        pip_install('boto3')
        print('\nDone - please, restart script nnow.\n')
        exit(0)

    # add /32 mask to an IP
    cidr = sys.argv[1] + '/32'

    # vars
    aws_access_key = 'AKI***WNA'
    aws_secret_key = 'oaZ***uMW'
    ec2_region = 'eu-west-1'
    sg_id = 'sg-7cdef305'

    # create boto3 connection first
    print('Creating connection...')
    client = create_conn(aws_access_key, aws_secret_key, ec2_region)

    # add new rule with IP from first arg
    print('Adding new rule with IP {}...'.format(cidr))
    add_rule(client, sg_id, cidr)

    print('\nDone.\n')

Запускаем:

./aws_update_setevoy_vpn_sg.py 0.0.0.0
Checking for dependencies...
boto3 library already installed - OK.
Creating connection...
Adding new rule with IP 0.0.0.0/32...
Done.

Проверяем:

aws ec2 describe-security-groups --group-ids sg-7cdef305 --output text
...
IPPERMISSIONS   1194    udp     1194
...
IPRANGES        0.0.0.0/32

Готово:

ToDoBetter

Что хорошо было бы добавить в этот скрипт – это:

  1. авторизация – было бы правильнее реализовать через ~/.aws/credentials или через переменные окружения (см. тут>>>), а не хардкодить прямо в скрипт, но т.к. на машине пользователя AWS CLI нет, а рассказывать как в Windows добавить переменные – не хочется, то передаём так;
  2. удаление старого правила – но тут надо сделать выборку по полю Description каждого правила, а в boto3 я такой возможности не нашёл;
  3. добавить описание для добавляемого правила, но опять-таки – см. п. 1.