240 lines
8.0 KiB
Python
240 lines
8.0 KiB
Python
import os
|
|
from aws_cdk import (
|
|
Duration,
|
|
RemovalPolicy,
|
|
Stack,
|
|
aws_ec2 as ec2,
|
|
aws_iam as iam,
|
|
aws_cognito as cognito,
|
|
aws_rds as rds,
|
|
CfnOutput
|
|
)
|
|
from constructs import Construct
|
|
|
|
class IptvUpdaterStack(Stack):
|
|
def __init__(
|
|
self,
|
|
scope: Construct,
|
|
construct_id: str,
|
|
freedns_user: str,
|
|
freedns_password: str,
|
|
domain_name: str,
|
|
ssh_public_key: str,
|
|
repo_url: str,
|
|
letsencrypt_email: str,
|
|
**kwargs
|
|
) -> None:
|
|
super().__init__(scope, construct_id, **kwargs)
|
|
|
|
# Create VPC
|
|
vpc = ec2.Vpc(self, "IptvUpdaterVPC",
|
|
max_azs=1, # Use only one AZ for free tier
|
|
nat_gateways=0, # No NAT Gateway to stay in free tier
|
|
subnet_configuration=[
|
|
ec2.SubnetConfiguration(
|
|
name="public",
|
|
subnet_type=ec2.SubnetType.PUBLIC,
|
|
cidr_mask=24
|
|
)
|
|
]
|
|
)
|
|
|
|
# Security Group
|
|
security_group = ec2.SecurityGroup(
|
|
self, "IptvUpdaterSG",
|
|
vpc=vpc,
|
|
allow_all_outbound=True
|
|
)
|
|
|
|
security_group.add_ingress_rule(
|
|
ec2.Peer.any_ipv4(),
|
|
ec2.Port.tcp(443),
|
|
"Allow HTTPS traffic"
|
|
)
|
|
|
|
security_group.add_ingress_rule(
|
|
ec2.Peer.any_ipv4(),
|
|
ec2.Port.tcp(80),
|
|
"Allow HTTP traffic"
|
|
)
|
|
|
|
security_group.add_ingress_rule(
|
|
ec2.Peer.any_ipv4(),
|
|
ec2.Port.tcp(22),
|
|
"Allow SSH traffic"
|
|
)
|
|
|
|
# Key pair for IPTV Updater instance
|
|
key_pair = ec2.KeyPair(
|
|
self,
|
|
"IptvUpdaterKeyPair",
|
|
key_pair_name="iptv-updater-key",
|
|
public_key_material=ssh_public_key
|
|
)
|
|
|
|
# Create IAM role for EC2
|
|
role = iam.Role(
|
|
self, "IptvUpdaterRole",
|
|
assumed_by=iam.ServicePrincipal("ec2.amazonaws.com")
|
|
)
|
|
|
|
# Add SSM managed policy
|
|
role.add_managed_policy(
|
|
iam.ManagedPolicy.from_aws_managed_policy_name(
|
|
"AmazonSSMManagedInstanceCore"
|
|
)
|
|
)
|
|
|
|
# Add Cognito permissions to instance role
|
|
role.add_managed_policy(
|
|
iam.ManagedPolicy.from_aws_managed_policy_name(
|
|
"AmazonCognitoReadOnly"
|
|
)
|
|
)
|
|
|
|
# EC2 Instance
|
|
instance = ec2.Instance(
|
|
self, "IptvUpdaterInstance",
|
|
vpc=vpc,
|
|
instance_type=ec2.InstanceType.of(
|
|
ec2.InstanceClass.T2,
|
|
ec2.InstanceSize.MICRO
|
|
),
|
|
machine_image=ec2.AmazonLinuxImage(
|
|
generation=ec2.AmazonLinuxGeneration.AMAZON_LINUX_2
|
|
),
|
|
security_group=security_group,
|
|
key_pair=key_pair,
|
|
role=role
|
|
)
|
|
|
|
# Create Elastic IP
|
|
eip = ec2.CfnEIP(
|
|
self, "IptvUpdaterEIP",
|
|
domain="vpc",
|
|
instance_id=instance.instance_id
|
|
)
|
|
|
|
# Add Cognito User Pool
|
|
user_pool = cognito.UserPool(
|
|
self, "IptvUpdaterUserPool",
|
|
user_pool_name="iptv-updater-users",
|
|
self_sign_up_enabled=False, # Only admins can create users
|
|
password_policy=cognito.PasswordPolicy(
|
|
min_length=8,
|
|
require_lowercase=True,
|
|
require_digits=True,
|
|
require_symbols=True,
|
|
require_uppercase=True
|
|
),
|
|
account_recovery=cognito.AccountRecovery.EMAIL_ONLY,
|
|
removal_policy=RemovalPolicy.DESTROY
|
|
)
|
|
|
|
# Add App Client with the correct callback URL
|
|
client = user_pool.add_client("IptvUpdaterClient",
|
|
access_token_validity=Duration.minutes(60),
|
|
id_token_validity=Duration.minutes(60),
|
|
refresh_token_validity=Duration.days(1),
|
|
auth_flows=cognito.AuthFlow(
|
|
user_password=True
|
|
),
|
|
o_auth=cognito.OAuthSettings(
|
|
flows=cognito.OAuthFlows(
|
|
implicit_code_grant=True
|
|
)
|
|
),
|
|
prevent_user_existence_errors=True,
|
|
generate_secret=True,
|
|
enable_token_revocation=True
|
|
)
|
|
|
|
# Add domain for hosted UI
|
|
domain = user_pool.add_domain("IptvUpdaterDomain",
|
|
cognito_domain=cognito.CognitoDomainOptions(
|
|
domain_prefix="iptv-updater"
|
|
)
|
|
)
|
|
|
|
# Read the userdata script with proper path resolution
|
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
userdata_path = os.path.join(script_dir, "userdata.sh")
|
|
userdata_file = open(userdata_path, "rb").read()
|
|
|
|
# Creates a userdata object for Linux hosts
|
|
userdata = ec2.UserData.for_linux()
|
|
|
|
# Add environment variables for acme.sh from parameters
|
|
userdata.add_commands(
|
|
f'export FREEDNS_User="{freedns_user}"',
|
|
f'export FREEDNS_Password="{freedns_password}"',
|
|
f'export DOMAIN_NAME="{domain_name}"',
|
|
f'export REPO_URL="{repo_url}"',
|
|
f'export LETSENCRYPT_EMAIL="{letsencrypt_email}"'
|
|
)
|
|
|
|
# Adds one or more commands to the userdata object.
|
|
userdata.add_commands(
|
|
f'echo "COGNITO_USER_POOL_ID={user_pool.user_pool_id}" >> /etc/environment',
|
|
f'echo "COGNITO_CLIENT_ID={client.user_pool_client_id}" >> /etc/environment',
|
|
f'echo "COGNITO_CLIENT_SECRET={client.user_pool_client_secret.to_string()}" >> /etc/environment',
|
|
f'echo "DOMAIN_NAME={domain_name}" >> /etc/environment'
|
|
)
|
|
userdata.add_commands(str(userdata_file, 'utf-8'))
|
|
|
|
# Create RDS Security Group
|
|
rds_sg = ec2.SecurityGroup(
|
|
self, "RdsSecurityGroup",
|
|
vpc=vpc,
|
|
description="Security group for RDS PostgreSQL"
|
|
)
|
|
rds_sg.add_ingress_rule(
|
|
security_group,
|
|
ec2.Port.tcp(5432),
|
|
"Allow PostgreSQL access from EC2 instance"
|
|
)
|
|
|
|
# Create RDS PostgreSQL instance (free tier compatible - db.t3.micro)
|
|
db = rds.DatabaseInstance(
|
|
self, "IptvUpdaterDB",
|
|
engine=rds.DatabaseInstanceEngine.postgres(
|
|
version=rds.PostgresEngineVersion.VER_13
|
|
),
|
|
instance_type=ec2.InstanceType.of(
|
|
ec2.InstanceClass.BURSTABLE2,
|
|
ec2.InstanceSize.MICRO
|
|
),
|
|
vpc=vpc,
|
|
security_groups=[rds_sg],
|
|
allocated_storage=10,
|
|
max_allocated_storage=10,
|
|
database_name="iptv_updater",
|
|
removal_policy=RemovalPolicy.DESTROY,
|
|
deletion_protection=False,
|
|
publicly_accessible=False
|
|
)
|
|
|
|
# Add RDS permissions to instance role
|
|
role.add_managed_policy(
|
|
iam.ManagedPolicy.from_aws_managed_policy_name(
|
|
"AmazonRDSFullAccess"
|
|
)
|
|
)
|
|
|
|
# Update instance with userdata and DB connection info
|
|
userdata.add_commands(
|
|
f'echo "DB_HOST={db.db_instance_endpoint_address}" >> /etc/environment',
|
|
f'echo "DB_NAME=iptv_updater" >> /etc/environment',
|
|
f'echo "DB_USER={db.secret.secret_value_from_json("username").to_string()}" >> /etc/environment',
|
|
f'echo "DB_PASSWORD={db.secret.secret_value_from_json("password").to_string()}" >> /etc/environment'
|
|
)
|
|
instance.add_user_data(userdata.render())
|
|
|
|
# Outputs
|
|
CfnOutput(self, "DBEndpoint", value=db.db_instance_endpoint_address)
|
|
CfnOutput(self, "InstancePublicIP", value=eip.attr_public_ip)
|
|
CfnOutput(self, "UserPoolId", value=user_pool.user_pool_id)
|
|
CfnOutput(self, "UserPoolClientId", value=client.user_pool_client_id)
|
|
CfnOutput(self, "CognitoDomainUrl",
|
|
value=f"https://{domain.domain_name}.auth.{self.region}.amazoncognito.com"
|
|
) |