AWS CDK를 사용하는 Linux EC2 배스천 호스트
요구 사항.
퍼블릭 IP를 사용하여 퍼블릭 네트워크 서브넷에 데이터베이스를 배치하는 것과 비교하여 최종 사용자를 위해 RDS 인스턴스에 대한 더 안전한 액세스를 제공합니다.
아래 솔루션은 AWS EC2 클라우드 초기화 기능을 사용하여 새 SSH 공개 키가 public_keys 디렉터리에 배치될 때마다/home/ec2-user/.ssh/authorized_keys 파일을 업데이트하기 위해 필수 Galaxy 모듈과 함께 ansible을 설치합니다.
이렇게 하면 cloud-init 정의가 변경되기 때문에 SSH 키가 추가되거나 제거될 때마다 EC2가 다시 생성됩니다.
이 솔루션은 ansible에 의존하므로 새 키 파일 이름이 ansible/bastion/roles/ssh/tasks/main.yaml에 추가되도록 해야 합니다.
다음은 작업 main.yaml 파일의 예입니다.
- name: Gather EC2 metadata facts
amazon.aws.ec2_metadata_facts:
- name: Set up multiple authorized key taken from files
ansible.posix.authorized_key:
user: ec2-user
state: present
key: '{{ item }}'
with_file:
- public_keys/tom_id_rsa.pub
플레이북 아래 정의:
---
- hosts: localhost
connection: local
remote_user: ec2-user
become: yes
become_method: sudo
gather_facts: yes
roles:
- {role: 'ssh'}
가정.
/ansible이라는 이름의 루트 디렉터리에 다음 디렉터리 구조를 가진 ansible 플레이북이 있습니다.
├── ansible
│ └── bastion
│ ├── ansible.cfg
│ ├── main.yml
│ └── roles
│ └── ssh
│ ├── files
│ │ └── public_keys
│ │ └── tom_id_rsa.pub
│ └── tasks
│ └── main.yaml
데이터 흐름이 있는 아키텍처
아래 아키텍처는 다음과 같은 기사에서 가져온 것입니다.
CDK 코드
가정 섹션에서 필수 구성 요소를 가져오겠습니다.
imported_vpc = ec2.Vpc.from_lookup(
self,
id="imported_vpc",
vpc_id=ssm.StringParameter.value_from_lookup(
self, parameter_name=self.object_names["vpc_id_ssm_param_name"]
),
)
imported_kms_key = kms.Key.from_lookup(
self, id="imported_kms_key", alias_name=f'alias/{self.object_names["shared_kms_key_alias"]}'
)
imported_route53_public_zone = route53.PublicHostedZone.from_public_hosted_zone_attributes(
self,
id="imported_public_hosted_zone_id",
hosted_zone_id=ssm.StringParameter.value_for_string_parameter(
self, parameter_name=self.object_names["ssm_public_dns_zone_id"]
),
zone_name=self.object_names["ssm_public_dns_zone_name"],
)
Bastion을 생성하고 필요한 권한을 부여하자
bastion = self.bastion_host(
props=props,
ansible_bucket=shared_ansible_s3_bucket,
route53_public_zone=imported_route53_public_zone,
shared_kms_key=imported_kms_key,
vpc=imported_vpc,
)
shared_ansible_s3_bucket.grant_read(bastion)
imported_kms_key.grant_decrypt(bastion)
Ansible 플레이북을 S3에 복사해 봅시다.
self.bucket_deployment(
destination_key_prefix="bastion", object_path="../../ansible/bastion", bucket=shared_ansible_s3_bucket
)
bastion_host 및 bucket_deployment에 대한 메서드를 만들어 봅시다.
def bucket_deployment(self, destination_key_prefix: str, object_path: str, bucket: s3.IBucket) -> None:
"""Deploy directory or zip archive to S3 bucket.
:param bucket: The AWS S3 Bucket CDK object to which deployment will occur
:param destination_key_prefix: The prefix which will be used to deploy object into
:param object_path: Path to the directory or zip archive in filesystem
:return:
"""
this_dir = path.dirname(__file__)
source_asset = s3_deployment.Source.asset(path.join(this_dir, object_path))
s3_deployment.BucketDeployment(
self,
id=f"{destination_key_prefix}_deployment",
destination_bucket=bucket,
sources=[source_asset],
destination_key_prefix=destination_key_prefix,
)
def bastion_host(
self,
props: Dict,
ansible_bucket: s3.IBucket,
route53_public_zone: route53.IPublicHostedZone,
shared_kms_key: kms.IKey,
vpc: ec2.IVpc,
) -> ec2.BastionHostLinux:
"""Create bastion host to route network traffic (grant access) to the
resources placed inside private subnets.
:param ansible_bucket: The CDK instance of existing s3 bucket that host ansible playbooks
:param route53_public_zone: The CDK object for Route53 zone.
In this zone the DNS entry for bastion host will be created
:param props: The dictionary which contain configuration values loaded initially from /config/config-env.yaml
:param shared_kms_key: The AWS KMS key shared for this project
:param vpc: The EC2 VPC object, this vpc will be used to place bastion host in it
"""
ansible_copy_keys_init_list: List[ec2.InitCommand] = []
ssh_pub_keys_path = "ansible/bastion/roles/ssh/files/public_keys"
# pylint: disable=W0612
for dir_path, dirs, files in walk(ssh_pub_keys_path):
ansible_copy_keys_init_list.extend(
ec2.InitCommand.shell_command(
shell_command=f"aws s3 cp s3://{ansible_bucket.bucket_name}/bastion/roles/ssh/files/public_keys/{file_name} /tmp/"
)
for file_name in files
)
init = ec2.CloudFormationInit.from_config_sets(
config_sets={
# Applies the configs below in this order
"default": [
"config",
"yum_packages",
"ansible_galaxy_modules_installation",
"ansible_playbook_from_s3",
"copy_ssh_pub_keys_from_s3",
"enable_fail2ban",
]
},
configs={
"config": ec2.InitConfig(
[
ec2.InitCommand.shell_command(shell_command="yum update -y"),
ec2.InitCommand.shell_command(shell_command="amazon-linux-extras install ansible2 -y"),
ec2.InitCommand.shell_command(shell_command="amazon-linux-extras install epel -y"),
]
),
"yum_packages": ec2.InitConfig(
[
ec2.InitPackage.yum("htop"),
ec2.InitPackage.yum("fail2ban"),
]
),
"ansible_galaxy_modules_installation": ec2.InitConfig(
[
ec2.InitCommand.shell_command(
cwd="/root", shell_command="ansible-galaxy collection install ansible.posix"
),
ec2.InitCommand.shell_command(
cwd="/root", shell_command="ansible-galaxy collection install amazon.aws"
),
]
),
"ansible_playbook_from_s3": ec2.InitConfig(
[
ec2.InitCommand.shell_command(shell_command="mkdir /root/ansible"),
ec2.InitCommand.shell_command(
shell_command=f"aws s3 cp s3://{ansible_bucket.bucket_name}/bastion /root/ansible/bastion --recursive"
),
ec2.InitCommand.shell_command(
cwd="/root/ansible/bastion", shell_command="ansible-playbook main.yml"
),
]
),
# This section does not implement any real change on Bastion host. It exists only for
# changing the EC2 cloud-init definition that will force EC2 replacement whenever
# a new ssh public key file will be added to the repository
"copy_ssh_pub_keys_from_s3": ec2.InitConfig(ansible_copy_keys_init_list),
"enable_fail2ban": ec2.InitConfig(
[
ec2.InitCommand.shell_command(shell_command="systemctl enable fail2ban"),
ec2.InitCommand.shell_command(shell_command="systemctl start fail2ban"),
]
),
},
)
init_options = ec2.ApplyCloudFormationInitOptions(
config_sets=["default"], timeout=cdk.Duration.minutes(30), include_url=True, include_role=True
)
security_group = ec2.SecurityGroup(
self,
id="bastion_security_group",
vpc=vpc,
security_group_name=f'{self.object_names["standard_prefix"]}-bastion',
)
security_group.add_ingress_rule(peer=ec2.Peer.any_ipv4(), connection=ec2.Port.tcp(port=22))
ssm.StringParameter(
self,
id="bastion_security_group_id_ssm_param",
string_value=security_group.security_group_id,
parameter_name=self.object_names["bastion_security_group_id_ssm_param"],
)
bastion = ec2.BastionHostLinux(
self,
id="bastion_host",
block_devices=[
ec2.BlockDevice(
device_name="/dev/xvda",
volume=ec2.BlockDeviceVolume.ebs(
volume_size=10,
encrypted=True,
volume_type=ec2.EbsDeviceVolumeType.GP3,
kms_key=shared_kms_key,
delete_on_termination=True,
),
)
],
init=init,
init_options=init_options,
instance_name=f'{props["stage"]}-{props["project"]}-bastion',
instance_type=ec2.InstanceType.of(
instance_class=ec2.InstanceClass.BURSTABLE4_GRAVITON, instance_size=ec2.InstanceSize.MICRO
),
security_group=security_group,
subnet_selection=ec2.SubnetSelection(
subnet_group_name="public",
),
vpc=vpc,
)
route53.CnameRecord(
self,
id="bastion_dns_record",
domain_name=bastion.instance_public_dns_name,
record_name=f"bastion.{route53_public_zone.zone_name}",
zone=route53_public_zone,
comment="bastion host",
ttl=cdk.Duration.minutes(1),
)
return bastion
Reference
이 문제에 관하여(AWS CDK를 사용하는 Linux EC2 배스천 호스트), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/airmonitor/linux-ec2-bastion-host-with-aws-cdk-55ie텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)