Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Aws private api deployment #725

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,4 @@ ENV/
cdk.out/
deployment/k8s/titiler/values-test.yaml
docs/src/api/
.idea
2 changes: 1 addition & 1 deletion deployment/aws/cdk.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"app": "python3 cdk/app.py"
"app": "python cdk/app.py"
}
2 changes: 1 addition & 1 deletion deployment/aws/cdk/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
"""AWS App."""
"""AWS App"""
13 changes: 13 additions & 0 deletions deployment/aws/cdk/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from aws_cdk import aws_logs as logs
from aws_cdk.aws_apigatewayv2_integrations_alpha import HttpLambdaIntegration
from config import StackSettings
from construct.private_api import TitilerPrivateApiStack
from constructs import Construct

settings = StackSettings()
Expand Down Expand Up @@ -175,6 +176,16 @@ def __init__(
)
)

private_api = TitilerPrivateApiStack(
app,
f"{settings.name}-private-api-{settings.stage}",
vpc_endpoint_id=settings.vpc_endpoint_id,
memory=settings.memory,
timeout=settings.timeout,
concurrent=settings.max_concurrent,
permissions=perms,
environment=settings.env,
)

ecs_stack = titilerECSStack(
app,
Expand All @@ -197,6 +208,7 @@ def __init__(
environment=settings.env,
)


# Tag infrastructure
for key, value in {
"Project": settings.name,
Expand All @@ -207,6 +219,7 @@ def __init__(
if value:
Tags.of(ecs_stack).add(key, value)
Tags.of(lambda_stack).add(key, value)
Tags.of(private_api).add(key, value)


app.synth()
3 changes: 3 additions & 0 deletions deployment/aws/cdk/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,7 @@ class StackSettings(BaseSettings):
# Default: - No specific limit - account limit.
max_concurrent: Optional[int] = None

# The VPC Endpoint ID
vpc_endpoint_id: None | str = None

model_config = SettingsConfigDict(env_prefix="TITILER_STACK_", env_file=".env")
1 change: 1 addition & 0 deletions deployment/aws/cdk/construct/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Constructs Custom Package"""
116 changes: 116 additions & 0 deletions deployment/aws/cdk/construct/private_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"""Private API Construct"""

import os
from typing import Any, Dict, List, Optional, cast

from aws_cdk import CfnOutput, Duration, Stack
from aws_cdk.aws_apigateway import (
EndpointConfiguration,
EndpointType,
LambdaIntegration,
RestApi,
)
from aws_cdk.aws_iam import AnyPrincipal, Effect, PolicyDocument, PolicyStatement
from aws_cdk.aws_lambda import Code, Function, IFunction, Runtime
from aws_cdk.aws_logs import RetentionDays
from constructs import Construct


class TitilerPrivateApiStack(Stack):
"""
Titiler Private API Stack

Private api configuration for titiler.

author: @jeandsmith
"""

def __init__(
self,
scope: Construct,
id: str,
vpc_endpoint_id: None | str,
memory: int = 1024,
timeout: int = 30,
runtime: Runtime = Runtime.PYTHON_3_11,
code_dir: str = "./",
concurrent: Optional[int] = None,
permissions: Optional[List[PolicyStatement]] = None,
environment: Optional[Dict] = None,
**kwargs: Any,
) -> None:
"""Define the stack"""
super().__init__(scope, id, **kwargs)

permissions = permissions or []
environment = environment or {}

lambda_function = Function(
self,
f"{id}-lambda",
runtime=runtime,
code=Code.from_docker_build(
path=os.path.abspath(code_dir),
file="lambda/Dockerfile",
),
handler="handler.handler",
memory_size=memory,
reserved_concurrent_executions=concurrent,
timeout=Duration.seconds(timeout),
environment=environment,
log_retention=RetentionDays.ONE_WEEK,
)

for perm in permissions:
lambda_function.add_to_role_policy(perm)

policy = (
PolicyDocument(
statements=[
PolicyStatement(
principals=[AnyPrincipal()],
effect=Effect.DENY,
actions=["execute-api:Invoke"],
resources=[
Stack.of(self).format_arn(
service="execute-api", resource="*"
)
],
conditions={
"StringNotEquals": {"aws:SourceVpce": vpc_endpoint_id}
},
),
PolicyStatement(
principals=[AnyPrincipal()],
effect=Effect.ALLOW,
actions=["execute-api:Invoke"],
resources=[
Stack.of(self).format_arn(
service="execute-api", resource="*"
)
],
),
]
)
if vpc_endpoint_id
else None
)

endpoint_config = (
EndpointConfiguration(types=[EndpointType.PRIVATE])
if vpc_endpoint_id
else EndpointConfiguration(types=[EndpointType.REGIONAL])
)

api = RestApi(
self,
f"{id}-endpoint",
default_integration=LambdaIntegration(
handler=cast(IFunction, lambda_function)
),
policy=policy,
endpoint_configuration=endpoint_config,
)
api.root.add_proxy()

CfnOutput(self, "Endpoint", value=api.url)
Loading