Linted and formatted all files
This commit is contained in:
@@ -1,80 +1,69 @@
|
||||
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,
|
||||
aws_ssm as ssm,
|
||||
CfnOutput
|
||||
)
|
||||
|
||||
from aws_cdk import CfnOutput, Duration, RemovalPolicy, Stack
|
||||
from aws_cdk import aws_cognito as cognito
|
||||
from aws_cdk import aws_ec2 as ec2
|
||||
from aws_cdk import aws_iam as iam
|
||||
from aws_cdk import aws_rds as rds
|
||||
from aws_cdk import aws_ssm as ssm
|
||||
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:
|
||||
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",
|
||||
vpc = ec2.Vpc(
|
||||
self,
|
||||
"IptvUpdaterVPC",
|
||||
max_azs=2, # Need at least 2 AZs for RDS subnet group
|
||||
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
|
||||
name="public", subnet_type=ec2.SubnetType.PUBLIC, cidr_mask=24
|
||||
),
|
||||
ec2.SubnetConfiguration(
|
||||
name="private",
|
||||
subnet_type=ec2.SubnetType.PRIVATE_ISOLATED,
|
||||
cidr_mask=24
|
||||
)
|
||||
]
|
||||
cidr_mask=24,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
# Security Group
|
||||
security_group = ec2.SecurityGroup(
|
||||
self, "IptvUpdaterSG",
|
||||
vpc=vpc,
|
||||
allow_all_outbound=True
|
||||
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"
|
||||
ec2.Peer.any_ipv4(), ec2.Port.tcp(443), "Allow HTTPS traffic"
|
||||
)
|
||||
|
||||
security_group.add_ingress_rule(
|
||||
ec2.Peer.any_ipv4(),
|
||||
ec2.Port.tcp(22),
|
||||
"Allow SSH traffic"
|
||||
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"
|
||||
)
|
||||
|
||||
# Allow PostgreSQL port for tunneling restricted to developer IP
|
||||
security_group.add_ingress_rule(
|
||||
ec2.Peer.ipv4("47.189.88.48/32"), # Developer IP
|
||||
ec2.Port.tcp(5432),
|
||||
"Allow PostgreSQL traffic for tunneling"
|
||||
"Allow PostgreSQL traffic for tunneling",
|
||||
)
|
||||
|
||||
# Key pair for IPTV Updater instance
|
||||
@@ -82,13 +71,14 @@ class IptvUpdaterStack(Stack):
|
||||
self,
|
||||
"IptvUpdaterKeyPair",
|
||||
key_pair_name="iptv-updater-key",
|
||||
public_key_material=ssh_public_key
|
||||
public_key_material=ssh_public_key,
|
||||
)
|
||||
|
||||
# Create IAM role for EC2
|
||||
role = iam.Role(
|
||||
self, "IptvUpdaterRole",
|
||||
assumed_by=iam.ServicePrincipal("ec2.amazonaws.com")
|
||||
self,
|
||||
"IptvUpdaterRole",
|
||||
assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"),
|
||||
)
|
||||
|
||||
# Add SSM managed policy
|
||||
@@ -99,37 +89,36 @@ class IptvUpdaterStack(Stack):
|
||||
)
|
||||
|
||||
# Add EC2 describe permissions
|
||||
role.add_to_policy(iam.PolicyStatement(
|
||||
actions=["ec2:DescribeInstances"],
|
||||
resources=["*"]
|
||||
))
|
||||
role.add_to_policy(
|
||||
iam.PolicyStatement(actions=["ec2:DescribeInstances"], resources=["*"])
|
||||
)
|
||||
|
||||
# Add SSM SendCommand permissions
|
||||
role.add_to_policy(iam.PolicyStatement(
|
||||
actions=["ssm:SendCommand"],
|
||||
resources=[
|
||||
f"arn:aws:ec2:{self.region}:{self.account}:instance/*", # Allow on all EC2 instances
|
||||
f"arn:aws:ssm:{self.region}:{self.account}:document/AWS-RunShellScript" # Required for the RunShellScript document
|
||||
]
|
||||
))
|
||||
role.add_to_policy(
|
||||
iam.PolicyStatement(
|
||||
actions=["ssm:SendCommand"],
|
||||
resources=[
|
||||
# Allow on all EC2 instances
|
||||
f"arn:aws:ec2:{self.region}:{self.account}:instance/*",
|
||||
# Required for the RunShellScript document
|
||||
f"arn:aws:ssm:{self.region}:{self.account}:document/AWS-RunShellScript",
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
# Add Cognito permissions to instance role
|
||||
role.add_managed_policy(
|
||||
iam.ManagedPolicy.from_aws_managed_policy_name(
|
||||
"AmazonCognitoReadOnly"
|
||||
)
|
||||
iam.ManagedPolicy.from_aws_managed_policy_name("AmazonCognitoReadOnly")
|
||||
)
|
||||
|
||||
# EC2 Instance
|
||||
instance = ec2.Instance(
|
||||
self, "IptvUpdaterInstance",
|
||||
self,
|
||||
"IptvUpdaterInstance",
|
||||
vpc=vpc,
|
||||
vpc_subnets=ec2.SubnetSelection(
|
||||
subnet_type=ec2.SubnetType.PUBLIC
|
||||
),
|
||||
vpc_subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PUBLIC),
|
||||
instance_type=ec2.InstanceType.of(
|
||||
ec2.InstanceClass.T2,
|
||||
ec2.InstanceSize.MICRO
|
||||
ec2.InstanceClass.T2, ec2.InstanceSize.MICRO
|
||||
),
|
||||
machine_image=ec2.AmazonLinuxImage(
|
||||
generation=ec2.AmazonLinuxGeneration.AMAZON_LINUX_2023
|
||||
@@ -138,7 +127,7 @@ class IptvUpdaterStack(Stack):
|
||||
key_pair=key_pair,
|
||||
role=role,
|
||||
# Option: 1: Enable auto-assign public IP (free tier compatible)
|
||||
associate_public_ip_address=True
|
||||
associate_public_ip_address=True,
|
||||
)
|
||||
|
||||
# Option: 2: Create Elastic IP (not free tier compatible)
|
||||
@@ -150,7 +139,8 @@ class IptvUpdaterStack(Stack):
|
||||
|
||||
# Add Cognito User Pool
|
||||
user_pool = cognito.UserPool(
|
||||
self, "IptvUpdaterUserPool",
|
||||
self,
|
||||
"IptvUpdaterUserPool",
|
||||
user_pool_name="iptv-updater-users",
|
||||
self_sign_up_enabled=False, # Only admins can create users
|
||||
password_policy=cognito.PasswordPolicy(
|
||||
@@ -158,37 +148,33 @@ class IptvUpdaterStack(Stack):
|
||||
require_lowercase=True,
|
||||
require_digits=True,
|
||||
require_symbols=True,
|
||||
require_uppercase=True
|
||||
require_uppercase=True,
|
||||
),
|
||||
account_recovery=cognito.AccountRecovery.EMAIL_ONLY,
|
||||
removal_policy=RemovalPolicy.DESTROY
|
||||
removal_policy=RemovalPolicy.DESTROY,
|
||||
)
|
||||
|
||||
# Add App Client with the correct callback URL
|
||||
client = user_pool.add_client("IptvUpdaterClient",
|
||||
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
|
||||
),
|
||||
auth_flows=cognito.AuthFlow(user_password=True),
|
||||
o_auth=cognito.OAuthSettings(
|
||||
flows=cognito.OAuthFlows(
|
||||
implicit_code_grant=True
|
||||
)
|
||||
flows=cognito.OAuthFlows(implicit_code_grant=True)
|
||||
),
|
||||
prevent_user_existence_errors=True,
|
||||
generate_secret=True,
|
||||
enable_token_revocation=True
|
||||
enable_token_revocation=True,
|
||||
)
|
||||
|
||||
# Add domain for hosted UI
|
||||
domain = user_pool.add_domain("IptvUpdaterDomain",
|
||||
cognito_domain=cognito.CognitoDomainOptions(
|
||||
domain_prefix="iptv-updater"
|
||||
)
|
||||
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")
|
||||
@@ -196,46 +182,56 @@ class IptvUpdaterStack(Stack):
|
||||
|
||||
# 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}"'
|
||||
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'
|
||||
(
|
||||
f'echo "COGNITO_USER_POOL_ID='
|
||||
f'{user_pool.user_pool_id}" >> /etc/environment'
|
||||
),
|
||||
(
|
||||
f'echo "COGNITO_CLIENT_ID='
|
||||
f'{client.user_pool_client_id}" >> /etc/environment'
|
||||
),
|
||||
(
|
||||
f'echo "COGNITO_CLIENT_SECRET='
|
||||
f'{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'))
|
||||
userdata.add_commands(str(userdata_file, "utf-8"))
|
||||
|
||||
# Create RDS Security Group
|
||||
rds_sg = ec2.SecurityGroup(
|
||||
self, "RdsSecurityGroup",
|
||||
self,
|
||||
"RdsSecurityGroup",
|
||||
vpc=vpc,
|
||||
description="Security group for RDS PostgreSQL"
|
||||
description="Security group for RDS PostgreSQL",
|
||||
)
|
||||
rds_sg.add_ingress_rule(
|
||||
security_group,
|
||||
ec2.Port.tcp(5432),
|
||||
"Allow PostgreSQL access from EC2 instance"
|
||||
"Allow PostgreSQL access from EC2 instance",
|
||||
)
|
||||
|
||||
# Create RDS PostgreSQL instance (free tier compatible - db.t3.micro)
|
||||
db = rds.DatabaseInstance(
|
||||
self, "IptvUpdaterDB",
|
||||
self,
|
||||
"IptvUpdaterDB",
|
||||
engine=rds.DatabaseInstanceEngine.postgres(
|
||||
version=rds.PostgresEngineVersion.VER_13
|
||||
),
|
||||
instance_type=ec2.InstanceType.of(
|
||||
ec2.InstanceClass.T3,
|
||||
ec2.InstanceSize.MICRO
|
||||
ec2.InstanceClass.T3, ec2.InstanceSize.MICRO
|
||||
),
|
||||
vpc=vpc,
|
||||
vpc_subnets=ec2.SubnetSelection(
|
||||
@@ -247,39 +243,43 @@ class IptvUpdaterStack(Stack):
|
||||
database_name="iptv_updater",
|
||||
removal_policy=RemovalPolicy.DESTROY,
|
||||
deletion_protection=False,
|
||||
publicly_accessible=False # Avoid public IPv4 charges
|
||||
publicly_accessible=False, # Avoid public IPv4 charges
|
||||
)
|
||||
|
||||
# Add RDS permissions to instance role
|
||||
role.add_managed_policy(
|
||||
iam.ManagedPolicy.from_aws_managed_policy_name(
|
||||
"AmazonRDSFullAccess"
|
||||
)
|
||||
iam.ManagedPolicy.from_aws_managed_policy_name("AmazonRDSFullAccess")
|
||||
)
|
||||
|
||||
# Store DB connection info in SSM Parameter Store
|
||||
ssm.StringParameter(self, "DBHostParam",
|
||||
ssm.StringParameter(
|
||||
self,
|
||||
"DBHostParam",
|
||||
parameter_name="/iptv-updater/DB_HOST",
|
||||
string_value=db.db_instance_endpoint_address
|
||||
string_value=db.db_instance_endpoint_address,
|
||||
)
|
||||
ssm.StringParameter(self, "DBNameParam",
|
||||
ssm.StringParameter(
|
||||
self,
|
||||
"DBNameParam",
|
||||
parameter_name="/iptv-updater/DB_NAME",
|
||||
string_value="iptv_updater"
|
||||
string_value="iptv_updater",
|
||||
)
|
||||
ssm.StringParameter(self, "DBUserParam",
|
||||
ssm.StringParameter(
|
||||
self,
|
||||
"DBUserParam",
|
||||
parameter_name="/iptv-updater/DB_USER",
|
||||
string_value=db.secret.secret_value_from_json("username").to_string()
|
||||
string_value=db.secret.secret_value_from_json("username").to_string(),
|
||||
)
|
||||
ssm.StringParameter(self, "DBPassParam",
|
||||
ssm.StringParameter(
|
||||
self,
|
||||
"DBPassParam",
|
||||
parameter_name="/iptv-updater/DB_PASSWORD",
|
||||
string_value=db.secret.secret_value_from_json("password").to_string()
|
||||
string_value=db.secret.secret_value_from_json("password").to_string(),
|
||||
)
|
||||
|
||||
# Add SSM read permissions to instance role
|
||||
role.add_managed_policy(
|
||||
iam.ManagedPolicy.from_aws_managed_policy_name(
|
||||
"AmazonSSMReadOnlyAccess"
|
||||
)
|
||||
iam.ManagedPolicy.from_aws_managed_policy_name("AmazonSSMReadOnlyAccess")
|
||||
)
|
||||
|
||||
# Update instance with userdata
|
||||
@@ -293,6 +293,8 @@ class IptvUpdaterStack(Stack):
|
||||
# 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"
|
||||
)
|
||||
CfnOutput(
|
||||
self,
|
||||
"CognitoDomainUrl",
|
||||
value=f"https://{domain.domain_name}.auth.{self.region}.amazoncognito.com",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user