website/docs: install: add aws (#12082)
This commit is contained in:
		 Marc 'risson' Schmitt
					Marc 'risson' Schmitt
				
			
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			 GitHub
						GitHub
					
				
			
						parent
						
							13b2543221
						
					
				
				
					commit
					fda6054285
				
			
							
								
								
									
										421
									
								
								website/docs/install-config/install/aws/app.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										421
									
								
								website/docs/install-config/install/aws/app.py
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,421 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| import json | ||||
|  | ||||
| from aws_cdk import ( | ||||
|     App, | ||||
|     CfnOutput, | ||||
|     CfnParameter, | ||||
|     Duration, | ||||
|     RemovalPolicy, | ||||
|     Stack, | ||||
| ) | ||||
| from aws_cdk import ( | ||||
|     aws_ec2 as ec2, | ||||
| ) | ||||
| from aws_cdk import ( | ||||
|     aws_ecs as ecs, | ||||
| ) | ||||
| from aws_cdk import ( | ||||
|     aws_efs as efs, | ||||
| ) | ||||
| from aws_cdk import ( | ||||
|     aws_elasticache as elasticache, | ||||
| ) | ||||
| from aws_cdk import ( | ||||
|     aws_elasticloadbalancingv2 as elbv2, | ||||
| ) | ||||
| from aws_cdk import ( | ||||
|     aws_rds as rds, | ||||
| ) | ||||
| from aws_cdk import ( | ||||
|     aws_secretsmanager as secretsmanager, | ||||
| ) | ||||
| from constructs import Construct | ||||
|  | ||||
| from authentik import __version__ | ||||
|  | ||||
|  | ||||
| class AuthentikStack(Stack): | ||||
|     def __init__(self, scope: Construct, id: str, **kwargs): | ||||
|         super().__init__(scope, id, *kwargs) | ||||
|  | ||||
|         ### Inputs | ||||
|  | ||||
|         db_instance_type = CfnParameter( | ||||
|             self, | ||||
|             "DBInstanceType", | ||||
|             type="String", | ||||
|             default="m5.large", | ||||
|             description="RDS PostgreSQL instance type (without the leading db.)", | ||||
|         ) | ||||
|         db_version = CfnParameter( | ||||
|             self, "DBVersion", type="String", default="17.1", description="RDS PostgreSQL version" | ||||
|         ) | ||||
|         db_storage = CfnParameter( | ||||
|             self, | ||||
|             "DBStorage", | ||||
|             type="Number", | ||||
|             default=10, | ||||
|             min_value=10, | ||||
|             description="RDS PostgreSQL storage size in GB", | ||||
|         ) | ||||
|  | ||||
|         redis_instance_type = CfnParameter( | ||||
|             self, | ||||
|             "RedisInstanceType", | ||||
|             type="String", | ||||
|             default="cache.t4g.medium", | ||||
|             description="ElastiCache Redis instance type (with the leading cache.)", | ||||
|         ) | ||||
|         redis_version = CfnParameter( | ||||
|             self, | ||||
|             "RedisVersion", | ||||
|             type="String", | ||||
|             default="7.1", | ||||
|             description="ElastiCache Redis version", | ||||
|         ) | ||||
|  | ||||
|         authentik_image = CfnParameter( | ||||
|             self, | ||||
|             "AuthentikImage", | ||||
|             type="String", | ||||
|             default="ghcr.io/goauthentik/server", | ||||
|             description="authentik Docker image", | ||||
|         ) | ||||
|         authentik_version = CfnParameter( | ||||
|             self, | ||||
|             "AuthentikVersion", | ||||
|             type="String", | ||||
|             default=__version__, | ||||
|             description="authentik Docker image tag", | ||||
|         ) | ||||
|  | ||||
|         server_cpu = CfnParameter( | ||||
|             self, | ||||
|             "AuthentikServerCPU", | ||||
|             type="Number", | ||||
|             default=512, | ||||
|             description="authentik server CPU units (1024 = 1 vCPU)", | ||||
|         ) | ||||
|         server_memory = CfnParameter( | ||||
|             self, | ||||
|             "AuthentikServerMemory", | ||||
|             type="Number", | ||||
|             default=1024, | ||||
|             description="authentik server memory in MiB", | ||||
|         ) | ||||
|         server_desired_count = CfnParameter( | ||||
|             self, | ||||
|             "AuthentikServerDesiredCount", | ||||
|             type="Number", | ||||
|             default=2, | ||||
|             min_value=1, | ||||
|             description="Desired number of authentik server tasks", | ||||
|         ) | ||||
|  | ||||
|         worker_cpu = CfnParameter( | ||||
|             self, | ||||
|             "AuthentikWorkerCPU", | ||||
|             type="Number", | ||||
|             default=512, | ||||
|             description="authentik worker CPU units (1024 = 1 vCPU)", | ||||
|         ) | ||||
|         worker_memory = CfnParameter( | ||||
|             self, | ||||
|             "AuthentikWorkerMemory", | ||||
|             type="Number", | ||||
|             default=1024, | ||||
|             description="authentik worker memory in MiB", | ||||
|         ) | ||||
|         worker_desired_count = CfnParameter( | ||||
|             self, | ||||
|             "AuthentikWorkerDesiredCount", | ||||
|             type="Number", | ||||
|             default=2, | ||||
|             min_value=1, | ||||
|             description="Desired number of authentik worker tasks", | ||||
|         ) | ||||
|  | ||||
|         certificate_arn = CfnParameter( | ||||
|             self, | ||||
|             "CertificateARN", | ||||
|             type="String", | ||||
|             description="ACM certificate ARN for HTTPS access", | ||||
|         ) | ||||
|  | ||||
|         ### Resources | ||||
|  | ||||
|         # VPC | ||||
|  | ||||
|         vpc = ec2.Vpc(self, "AuthentikVpc", max_azs=2, nat_gateways=1) | ||||
|  | ||||
|         # Security Groups | ||||
|  | ||||
|         db_security_group = ec2.SecurityGroup( | ||||
|             self, "DatabaseSG", vpc=vpc, description="Security Group for authentik RDS PostgreSQL" | ||||
|         ) | ||||
|         redis_security_group = ec2.SecurityGroup( | ||||
|             self, "RedisSG", vpc=vpc, description="Security Group for authentik ElastiCache Redis" | ||||
|         ) | ||||
|         authentik_security_group = ec2.SecurityGroup( | ||||
|             self, "AuthentikSG", vpc=vpc, description="Security Group for authentik services" | ||||
|         ) | ||||
|         db_security_group.add_ingress_rule( | ||||
|             peer=authentik_security_group, | ||||
|             connection=ec2.Port.tcp(5432), | ||||
|             description="Allow authentik to connect to RDS PostgreSQL", | ||||
|         ) | ||||
|         redis_security_group.add_ingress_rule( | ||||
|             peer=authentik_security_group, | ||||
|             connection=ec2.Port.tcp(6379), | ||||
|             description="Allow authentik to connect to ElastiCache Redis", | ||||
|         ) | ||||
|  | ||||
|         # Generated secrets | ||||
|  | ||||
|         db_password = secretsmanager.Secret( | ||||
|             self, | ||||
|             "DBPassword", | ||||
|             generate_secret_string=secretsmanager.SecretStringGenerator( | ||||
|                 secret_string_template=json.dumps({"username": "authentik"}), | ||||
|                 generate_string_key="password", | ||||
|                 password_length=64, | ||||
|                 exclude_characters='"@/\\', | ||||
|             ), | ||||
|         ) | ||||
|         secret_key = secretsmanager.Secret( | ||||
|             self, | ||||
|             "AuthentikSecretKey", | ||||
|             generate_secret_string=secretsmanager.SecretStringGenerator( | ||||
|                 password_length=64, exclude_characters='"@/\\' | ||||
|             ), | ||||
|         ) | ||||
|  | ||||
|         # Database | ||||
|  | ||||
|         database = rds.DatabaseInstance( | ||||
|             self, | ||||
|             "AuthentikDB", | ||||
|             engine=rds.DatabaseInstanceEngine.postgres( | ||||
|                 version=rds.PostgresEngineVersion.of(db_version.value_as_string, ""), | ||||
|             ), | ||||
|             instance_type=ec2.InstanceType(db_instance_type.value_as_string), | ||||
|             vpc=vpc, | ||||
|             vpc_subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS), | ||||
|             allocated_storage=db_storage.value_as_number, | ||||
|             security_groups=[db_security_group], | ||||
|             database_name="authentik", | ||||
|             credentials=rds.Credentials.from_secret(db_password), | ||||
|             multi_az=True, | ||||
|             removal_policy=RemovalPolicy.SNAPSHOT, | ||||
|         ) | ||||
|  | ||||
|         # Redis | ||||
|  | ||||
|         redis_subnet_group = elasticache.CfnSubnetGroup( | ||||
|             self, | ||||
|             "AuthentikRedisSubnetGroup", | ||||
|             subnet_ids=vpc.select_subnets( | ||||
|                 subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS | ||||
|             ).subnet_ids, | ||||
|             description="Subnet group for authentik ElastiCache Redis", | ||||
|         ) | ||||
|  | ||||
|         redis = elasticache.CfnReplicationGroup( | ||||
|             self, | ||||
|             "AuthentikRedis", | ||||
|             replication_group_description="Redis cluster for authentik", | ||||
|             engine="redis", | ||||
|             engine_version=redis_version.value_as_string, | ||||
|             cache_node_type=redis_instance_type.value_as_string, | ||||
|             num_cache_clusters=2, | ||||
|             automatic_failover_enabled=True, | ||||
|             security_group_ids=[redis_security_group.security_group_id], | ||||
|             cache_subnet_group_name=redis_subnet_group.ref, | ||||
|         ) | ||||
|  | ||||
|         # Storage | ||||
|  | ||||
|         media_fs = efs.FileSystem( | ||||
|             self, | ||||
|             "AuthentikMediaEFS", | ||||
|             vpc=vpc, | ||||
|             removal_policy=RemovalPolicy.RETAIN, | ||||
|             security_group=ec2.SecurityGroup( | ||||
|                 self, | ||||
|                 "AuthentikMediaEFSSecurityGroup", | ||||
|                 vpc=vpc, | ||||
|                 description="Security group for authentik media EFS", | ||||
|                 allow_all_outbound=True, | ||||
|             ), | ||||
|             encrypted=True, | ||||
|             performance_mode=efs.PerformanceMode.GENERAL_PURPOSE, | ||||
|             throughput_mode=efs.ThroughputMode.BURSTING, | ||||
|         ) | ||||
|         media_fs.connections.allow_default_port_from(authentik_security_group) | ||||
|  | ||||
|         media_access_point = media_fs.add_access_point( | ||||
|             "AuthentikMediaAccessPoint", | ||||
|             path="/media", | ||||
|             create_acl=efs.Acl(owner_uid="1000", owner_gid="1000", permissions="755"), | ||||
|             posix_user=efs.PosixUser(uid="1000", gid="1000"), | ||||
|         ) | ||||
|  | ||||
|         # ECS Cluster | ||||
|  | ||||
|         cluster = ecs.Cluster(self, "AuthentikCluster", vpc=vpc) | ||||
|  | ||||
|         environment = { | ||||
|             "AUTHENTIK_POSTGRESQL__HOST": database.instance_endpoint.hostname, | ||||
|             "AUTHENTIK_POSTGRESQL__USER": "authentik", | ||||
|             "AUTHENTIK_REDIS__HOST": redis.attr_primary_end_point_address, | ||||
|         } | ||||
|  | ||||
|         secrets = { | ||||
|             "AUTHENTIK_POSTGRESQL__PASSWORD": ecs.Secret.from_secrets_manager( | ||||
|                 db_password, field="password" | ||||
|             ), | ||||
|             "AUTHENTIK_SECRET_KEY": ecs.Secret.from_secrets_manager(secret_key), | ||||
|         } | ||||
|  | ||||
|         server_task = ecs.FargateTaskDefinition( | ||||
|             self, | ||||
|             "AuthentikServerTask", | ||||
|             cpu=server_cpu.value_as_number, | ||||
|             memory_limit_mib=server_memory.value_as_number, | ||||
|         ) | ||||
|         server_task.add_volume( | ||||
|             name="media", | ||||
|             efs_volume_configuration=ecs.EfsVolumeConfiguration( | ||||
|                 file_system_id=media_fs.file_system_id, | ||||
|                 transit_encryption="ENABLED", | ||||
|                 authorization_config=ecs.AuthorizationConfig( | ||||
|                     access_point_id=media_access_point.access_point_id, | ||||
|                     iam="ENABLED", | ||||
|                 ), | ||||
|             ), | ||||
|         ) | ||||
|         server_container = server_task.add_container( | ||||
|             "AuthentikServerContainer", | ||||
|             image=ecs.ContainerImage.from_registry( | ||||
|                 f"{authentik_image.value_as_string}:{authentik_version.value_as_string}" | ||||
|             ), | ||||
|             command=["server"], | ||||
|             environment=environment, | ||||
|             secrets=secrets, | ||||
|             logging=ecs.LogDriver.aws_logs(stream_prefix="authentik-server"), | ||||
|             enable_restart_policy=True, | ||||
|             health_check=ecs.HealthCheck( | ||||
|                 command=["CMD", "ak", "healthcheck"], | ||||
|                 interval=Duration.seconds(30), | ||||
|                 retries=3, | ||||
|                 start_period=Duration.seconds(60), | ||||
|                 timeout=Duration.seconds(30), | ||||
|             ), | ||||
|         ) | ||||
|         server_container.add_port_mappings(ecs.PortMapping(container_port=9000)) | ||||
|         server_container.add_mount_points( | ||||
|             ecs.MountPoint(container_path="/media", source_volume="media", read_only=False) | ||||
|         ) | ||||
|         server_service = ecs.FargateService( | ||||
|             self, | ||||
|             "AuthentikServerService", | ||||
|             cluster=cluster, | ||||
|             task_definition=server_task, | ||||
|             desired_count=server_desired_count.value_as_number, | ||||
|             security_groups=[authentik_security_group], | ||||
|             vpc_subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS), | ||||
|             enable_execute_command=True, | ||||
|         ) | ||||
|  | ||||
|         worker_task = ecs.FargateTaskDefinition( | ||||
|             self, | ||||
|             "AuthentikWorkerTask", | ||||
|             cpu=worker_cpu.value_as_number, | ||||
|             memory_limit_mib=worker_memory.value_as_number, | ||||
|         ) | ||||
|         worker_task.add_volume( | ||||
|             name="media", | ||||
|             efs_volume_configuration=ecs.EfsVolumeConfiguration( | ||||
|                 file_system_id=media_fs.file_system_id, | ||||
|                 transit_encryption="ENABLED", | ||||
|                 authorization_config=ecs.AuthorizationConfig( | ||||
|                     access_point_id=media_access_point.access_point_id, | ||||
|                     iam="ENABLED", | ||||
|                 ), | ||||
|             ), | ||||
|         ) | ||||
|         worker_container = worker_task.add_container( | ||||
|             "AuthentikWorkerContainer", | ||||
|             image=ecs.ContainerImage.from_registry( | ||||
|                 f"{authentik_image.value_as_string}:{authentik_version.value_as_string}" | ||||
|             ), | ||||
|             command=["worker"], | ||||
|             environment=environment, | ||||
|             secrets=secrets, | ||||
|             logging=ecs.LogDriver.aws_logs(stream_prefix="authentik-worker"), | ||||
|             enable_restart_policy=True, | ||||
|             health_check=ecs.HealthCheck( | ||||
|                 command=["CMD", "ak", "healthcheck"], | ||||
|                 interval=Duration.seconds(30), | ||||
|                 retries=3, | ||||
|                 start_period=Duration.seconds(60), | ||||
|                 timeout=Duration.seconds(30), | ||||
|             ), | ||||
|         ) | ||||
|         worker_container.add_mount_points( | ||||
|             ecs.MountPoint(container_path="/media", source_volume="media", read_only=False) | ||||
|         ) | ||||
|         worker_service = ecs.FargateService(  # noqa: F841 | ||||
|             self, | ||||
|             "AuthentikWorkerService", | ||||
|             cluster=cluster, | ||||
|             task_definition=worker_task, | ||||
|             desired_count=worker_desired_count.value_as_number, | ||||
|             security_groups=[authentik_security_group], | ||||
|             vpc_subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS), | ||||
|             enable_execute_command=True, | ||||
|         ) | ||||
|  | ||||
|         # Load balancer | ||||
|  | ||||
|         lb = elbv2.ApplicationLoadBalancer( | ||||
|             self, | ||||
|             "AuthentikALB", | ||||
|             vpc=vpc, | ||||
|             internet_facing=True, | ||||
|         ) | ||||
|         https_redirect = lb.add_listener(  # noqa: F841 | ||||
|             "AuthentikHttpListener", | ||||
|             port=80, | ||||
|             default_action=elbv2.ListenerAction.redirect(permanent=True, protocol="HTTPS"), | ||||
|         ) | ||||
|         listener = lb.add_listener( | ||||
|             "AuthentikHttpsListener", | ||||
|             port=443, | ||||
|             certificates=[ | ||||
|                 elbv2.ListenerCertificate(certificate_arn=certificate_arn.value_as_string) | ||||
|             ], | ||||
|         ) | ||||
|         target_group = listener.add_targets(  # noqa: F841 | ||||
|             "AuthentikServerTarget", | ||||
|             protocol=elbv2.ApplicationProtocol.HTTP, | ||||
|             port=9000, | ||||
|             targets=[server_service], | ||||
|             health_check=elbv2.HealthCheck( | ||||
|                 path="/-/health/live/", | ||||
|                 healthy_http_codes="200", | ||||
|             ), | ||||
|         ) | ||||
|  | ||||
|         CfnOutput( | ||||
|             self, | ||||
|             "LoadBalancerDNS", | ||||
|             value=lb.load_balancer_dns_name, | ||||
|         ) | ||||
|  | ||||
|  | ||||
| app = App() | ||||
| AuthentikStack(app, "AuthentikStack") | ||||
| app.synth() | ||||
		Reference in New Issue
	
	Block a user