@hasna/uptime 0.1.24 → 0.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/Dockerfile.package +2 -2
- package/README.md +14 -3
- package/bun.lock +221 -0
- package/dist/cli/index.js +247 -7
- package/dist/cloud-plan.d.ts +11 -0
- package/dist/cloud-plan.d.ts.map +1 -1
- package/dist/cloud-plan.js +23 -6
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +90 -6
- package/dist/workers.d.ts +34 -0
- package/dist/workers.d.ts.map +1 -0
- package/dist/workers.js +69 -0
- package/docs/aws-deployment-runbook.md +34 -10
- package/docs/aws-runtime-security.md +34 -12
- package/docs/cloud-source-of-truth.md +8 -4
- package/docs/deployment-metadata.example.json +4 -2
- package/infra/aws/README.md +40 -16
- package/infra/aws/main.tf +124 -42
- package/infra/aws/outputs.tf +8 -0
- package/infra/aws/terraform.tfvars.example +5 -1
- package/infra/aws/variables.tf +79 -5
- package/package.json +7 -2
package/infra/aws/main.tf
CHANGED
|
@@ -17,16 +17,21 @@ data "aws_caller_identity" "current" {}
|
|
|
17
17
|
data "aws_partition" "current" {}
|
|
18
18
|
|
|
19
19
|
locals {
|
|
20
|
-
prefix
|
|
21
|
-
container_port
|
|
22
|
-
evidence_bucket
|
|
23
|
-
efs_uid
|
|
24
|
-
efs_gid
|
|
25
|
-
hosted_sqlite_db_path
|
|
26
|
-
efs_enabled_services
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
prefix = "${var.service_name}-${var.stage}"
|
|
21
|
+
container_port = 3899
|
|
22
|
+
evidence_bucket = "hasna-${var.stage}-${var.service_name}-evidence"
|
|
23
|
+
efs_uid = 10001
|
|
24
|
+
efs_gid = 10001
|
|
25
|
+
hosted_sqlite_db_path = "/data/uptime/uptime.db"
|
|
26
|
+
efs_enabled_services = toset(["web"])
|
|
27
|
+
expected_runtime_package_integrity = coalesce(var.runtime_package_integrity, "")
|
|
28
|
+
use_alb_https = var.protected_access_mode == "alb_https_cert"
|
|
29
|
+
use_cloudfront = var.protected_access_mode == "cloudfront_default_domain"
|
|
30
|
+
cloudfront_https_origin = (
|
|
31
|
+
local.use_cloudfront && var.cloudfront_origin_protocol_policy == "https-only"
|
|
32
|
+
)
|
|
33
|
+
alb_https_listener_enabled = local.use_alb_https || local.cloudfront_https_origin
|
|
34
|
+
use_origin_verify = local.use_cloudfront && var.enable_cloudfront_origin_verify_header
|
|
30
35
|
services = {
|
|
31
36
|
web = {
|
|
32
37
|
desired_count = lookup(var.desired_counts, "web", 0)
|
|
@@ -35,22 +40,22 @@ locals {
|
|
|
35
40
|
}
|
|
36
41
|
scheduler = {
|
|
37
42
|
desired_count = lookup(var.desired_counts, "scheduler", 0)
|
|
38
|
-
command = ["bun", "dist/cli/index.js", "cloud", "
|
|
43
|
+
command = ["bun", "dist/cli/index.js", "cloud", "workers", "run", "--role", "scheduler"]
|
|
39
44
|
secrets = { APP_ENV = var.app_env_secret_arn }
|
|
40
45
|
}
|
|
41
46
|
"public-probe" = {
|
|
42
47
|
desired_count = lookup(var.desired_counts, "public-probe", 0)
|
|
43
|
-
command = ["bun", "dist/cli/index.js", "cloud", "
|
|
48
|
+
command = ["bun", "dist/cli/index.js", "cloud", "workers", "run", "--role", "public-probe"]
|
|
44
49
|
secrets = { PROBE_CONFIG = var.public_probe_secret_arn }
|
|
45
50
|
}
|
|
46
51
|
reporter = {
|
|
47
52
|
desired_count = lookup(var.desired_counts, "reporter", 0)
|
|
48
|
-
command = ["bun", "dist/cli/index.js", "cloud", "
|
|
53
|
+
command = ["bun", "dist/cli/index.js", "cloud", "workers", "run", "--role", "reporter"]
|
|
49
54
|
secrets = { REPORTING_CONFIG = var.reporting_secret_arn }
|
|
50
55
|
}
|
|
51
56
|
migration = {
|
|
52
57
|
desired_count = lookup(var.desired_counts, "migration", 0)
|
|
53
|
-
command = ["bun", "dist/cli/index.js", "cloud", "
|
|
58
|
+
command = ["bun", "dist/cli/index.js", "cloud", "workers", "run", "--role", "migration"]
|
|
54
59
|
secrets = { APP_ENV = var.app_env_secret_arn }
|
|
55
60
|
}
|
|
56
61
|
}
|
|
@@ -89,28 +94,28 @@ locals {
|
|
|
89
94
|
startPeriod = 30
|
|
90
95
|
}
|
|
91
96
|
scheduler = {
|
|
92
|
-
command = ["CMD-SHELL", "bun
|
|
97
|
+
command = ["CMD-SHELL", "bun dist/cli/index.js cloud workers preflight --role scheduler --healthcheck --json >/tmp/open-uptime-worker-preflight.json"]
|
|
93
98
|
interval = 30
|
|
94
99
|
timeout = 5
|
|
95
100
|
retries = 3
|
|
96
101
|
startPeriod = 30
|
|
97
102
|
}
|
|
98
103
|
"public-probe" = {
|
|
99
|
-
command = ["CMD-SHELL", "bun
|
|
104
|
+
command = ["CMD-SHELL", "bun dist/cli/index.js cloud workers preflight --role public-probe --healthcheck --json >/tmp/open-uptime-worker-preflight.json"]
|
|
100
105
|
interval = 30
|
|
101
106
|
timeout = 5
|
|
102
107
|
retries = 3
|
|
103
108
|
startPeriod = 30
|
|
104
109
|
}
|
|
105
110
|
reporter = {
|
|
106
|
-
command = ["CMD-SHELL", "bun
|
|
111
|
+
command = ["CMD-SHELL", "bun dist/cli/index.js cloud workers preflight --role reporter --healthcheck --json >/tmp/open-uptime-worker-preflight.json"]
|
|
107
112
|
interval = 30
|
|
108
113
|
timeout = 5
|
|
109
114
|
retries = 3
|
|
110
115
|
startPeriod = 30
|
|
111
116
|
}
|
|
112
117
|
migration = {
|
|
113
|
-
command = ["CMD-SHELL", "bun
|
|
118
|
+
command = ["CMD-SHELL", "bun dist/cli/index.js cloud workers preflight --role migration --healthcheck --json >/tmp/open-uptime-worker-preflight.json"]
|
|
114
119
|
interval = 30
|
|
115
120
|
timeout = 5
|
|
116
121
|
retries = 3
|
|
@@ -236,9 +241,18 @@ resource "aws_codebuild_project" "image_builder" {
|
|
|
236
241
|
- aws ecr get-login-password --region ${var.region} | docker login --username AWS --password-stdin ${data.aws_caller_identity.current.account_id}.dkr.ecr.${var.region}.amazonaws.com
|
|
237
242
|
build:
|
|
238
243
|
commands:
|
|
239
|
-
-
|
|
244
|
+
- EXPECTED_RUNTIME_PACKAGE_INTEGRITY='${local.expected_runtime_package_integrity}'
|
|
245
|
+
- PACKAGE_TARBALL=$(npm pack @hasna/uptime@${var.runtime_package_version} --silent)
|
|
246
|
+
- PACKAGE_INTEGRITY=$(npm view @hasna/uptime@${var.runtime_package_version} dist.integrity --json | tr -d '"')
|
|
247
|
+
- test -n "$PACKAGE_INTEGRITY"
|
|
248
|
+
- |
|
|
249
|
+
if [ -n "$EXPECTED_RUNTIME_PACKAGE_INTEGRITY" ] && [ "$PACKAGE_INTEGRITY" != "$EXPECTED_RUNTIME_PACKAGE_INTEGRITY" ]; then
|
|
250
|
+
echo "runtime package integrity mismatch" >&2
|
|
251
|
+
exit 1
|
|
252
|
+
fi
|
|
253
|
+
- printf 'runtime package integrity %s\n' "$PACKAGE_INTEGRITY"
|
|
240
254
|
- mkdir package
|
|
241
|
-
- tar -xzf
|
|
255
|
+
- tar -xzf "$PACKAGE_TARBALL" -C package --strip-components=1
|
|
242
256
|
- cd package
|
|
243
257
|
- docker build -f Dockerfile.package -t ${aws_ecr_repository.open_uptime.repository_url}:${var.runtime_package_version} .
|
|
244
258
|
- docker push ${aws_ecr_repository.open_uptime.repository_url}:${var.runtime_package_version}
|
|
@@ -366,8 +380,19 @@ resource "aws_security_group_rule" "alb_https_ingress" {
|
|
|
366
380
|
cidr_blocks = var.alb_ingress_cidr_blocks
|
|
367
381
|
}
|
|
368
382
|
|
|
383
|
+
resource "aws_security_group_rule" "alb_https_from_cloudfront" {
|
|
384
|
+
count = local.cloudfront_https_origin ? 1 : 0
|
|
385
|
+
type = "ingress"
|
|
386
|
+
description = "HTTPS from CloudFront origin-facing ranges"
|
|
387
|
+
security_group_id = aws_security_group.alb.id
|
|
388
|
+
from_port = 443
|
|
389
|
+
to_port = 443
|
|
390
|
+
protocol = "tcp"
|
|
391
|
+
prefix_list_ids = [data.aws_ec2_managed_prefix_list.cloudfront_origin_facing[0].id]
|
|
392
|
+
}
|
|
393
|
+
|
|
369
394
|
resource "aws_security_group_rule" "alb_http_from_cloudfront" {
|
|
370
|
-
count = local.use_cloudfront ? 1 : 0
|
|
395
|
+
count = local.use_cloudfront && !local.cloudfront_https_origin ? 1 : 0
|
|
371
396
|
type = "ingress"
|
|
372
397
|
description = "HTTP from CloudFront origin-facing ranges"
|
|
373
398
|
security_group_id = aws_security_group.alb.id
|
|
@@ -881,21 +906,37 @@ resource "aws_lb_target_group" "web" {
|
|
|
881
906
|
}
|
|
882
907
|
|
|
883
908
|
resource "aws_lb_listener" "https" {
|
|
884
|
-
count = local.
|
|
909
|
+
count = local.alb_https_listener_enabled ? 1 : 0
|
|
885
910
|
load_balancer_arn = aws_lb.open_uptime.arn
|
|
886
911
|
port = 443
|
|
887
912
|
protocol = "HTTPS"
|
|
888
913
|
certificate_arn = var.certificate_arn
|
|
889
914
|
tags = local.tags
|
|
890
915
|
|
|
891
|
-
default_action {
|
|
892
|
-
|
|
893
|
-
|
|
916
|
+
dynamic "default_action" {
|
|
917
|
+
for_each = local.cloudfront_https_origin && local.use_origin_verify ? [] : [1]
|
|
918
|
+
content {
|
|
919
|
+
type = "forward"
|
|
920
|
+
target_group_arn = aws_lb_target_group.web.arn
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
dynamic "default_action" {
|
|
925
|
+
for_each = local.cloudfront_https_origin && local.use_origin_verify ? [1] : []
|
|
926
|
+
content {
|
|
927
|
+
type = "fixed-response"
|
|
928
|
+
|
|
929
|
+
fixed_response {
|
|
930
|
+
content_type = "text/plain"
|
|
931
|
+
message_body = "forbidden"
|
|
932
|
+
status_code = "403"
|
|
933
|
+
}
|
|
934
|
+
}
|
|
894
935
|
}
|
|
895
936
|
}
|
|
896
937
|
|
|
897
938
|
resource "aws_lb_listener" "http_cloudfront" {
|
|
898
|
-
count = local.use_cloudfront ? 1 : 0
|
|
939
|
+
count = local.use_cloudfront && !local.cloudfront_https_origin ? 1 : 0
|
|
899
940
|
load_balancer_arn = aws_lb.open_uptime.arn
|
|
900
941
|
port = 80
|
|
901
942
|
protocol = "HTTP"
|
|
@@ -924,7 +965,7 @@ resource "aws_lb_listener" "http_cloudfront" {
|
|
|
924
965
|
}
|
|
925
966
|
|
|
926
967
|
resource "aws_lb_listener_rule" "http_cloudfront_origin_verify" {
|
|
927
|
-
count = local.use_origin_verify ? 1 : 0
|
|
968
|
+
count = local.use_origin_verify && !local.cloudfront_https_origin ? 1 : 0
|
|
928
969
|
listener_arn = aws_lb_listener.http_cloudfront[0].arn
|
|
929
970
|
priority = var.cloudfront_origin_verify_listener_rule_priority
|
|
930
971
|
tags = local.tags
|
|
@@ -942,6 +983,25 @@ resource "aws_lb_listener_rule" "http_cloudfront_origin_verify" {
|
|
|
942
983
|
}
|
|
943
984
|
}
|
|
944
985
|
|
|
986
|
+
resource "aws_lb_listener_rule" "https_cloudfront_origin_verify" {
|
|
987
|
+
count = local.use_origin_verify && local.cloudfront_https_origin ? 1 : 0
|
|
988
|
+
listener_arn = aws_lb_listener.https[0].arn
|
|
989
|
+
priority = var.cloudfront_origin_verify_listener_rule_priority
|
|
990
|
+
tags = local.tags
|
|
991
|
+
|
|
992
|
+
action {
|
|
993
|
+
type = "forward"
|
|
994
|
+
target_group_arn = aws_lb_target_group.web.arn
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
condition {
|
|
998
|
+
http_header {
|
|
999
|
+
http_header_name = var.cloudfront_origin_verify_header_name
|
|
1000
|
+
values = [var.cloudfront_origin_verify_header_value]
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
945
1005
|
resource "aws_cloudfront_distribution" "open_uptime" {
|
|
946
1006
|
count = local.use_cloudfront ? 1 : 0
|
|
947
1007
|
enabled = true
|
|
@@ -951,7 +1011,7 @@ resource "aws_cloudfront_distribution" "open_uptime" {
|
|
|
951
1011
|
tags = local.tags
|
|
952
1012
|
|
|
953
1013
|
origin {
|
|
954
|
-
domain_name = aws_lb.open_uptime.dns_name
|
|
1014
|
+
domain_name = local.cloudfront_https_origin ? var.cloudfront_origin_domain_name : aws_lb.open_uptime.dns_name
|
|
955
1015
|
origin_id = "${local.prefix}-alb"
|
|
956
1016
|
|
|
957
1017
|
dynamic "custom_header" {
|
|
@@ -965,7 +1025,7 @@ resource "aws_cloudfront_distribution" "open_uptime" {
|
|
|
965
1025
|
custom_origin_config {
|
|
966
1026
|
http_port = 80
|
|
967
1027
|
https_port = 443
|
|
968
|
-
origin_protocol_policy =
|
|
1028
|
+
origin_protocol_policy = var.cloudfront_origin_protocol_policy
|
|
969
1029
|
origin_ssl_protocols = ["TLSv1.2"]
|
|
970
1030
|
}
|
|
971
1031
|
}
|
|
@@ -1000,7 +1060,12 @@ resource "aws_cloudfront_distribution" "open_uptime" {
|
|
|
1000
1060
|
cloudfront_default_certificate = true
|
|
1001
1061
|
}
|
|
1002
1062
|
|
|
1003
|
-
depends_on = [
|
|
1063
|
+
depends_on = [
|
|
1064
|
+
aws_lb_listener.http_cloudfront,
|
|
1065
|
+
aws_lb_listener.https,
|
|
1066
|
+
aws_lb_listener_rule.http_cloudfront_origin_verify,
|
|
1067
|
+
aws_lb_listener_rule.https_cloudfront_origin_verify,
|
|
1068
|
+
]
|
|
1004
1069
|
}
|
|
1005
1070
|
|
|
1006
1071
|
resource "aws_route53_record" "open_uptime" {
|
|
@@ -1016,6 +1081,19 @@ resource "aws_route53_record" "open_uptime" {
|
|
|
1016
1081
|
}
|
|
1017
1082
|
}
|
|
1018
1083
|
|
|
1084
|
+
resource "aws_route53_record" "cloudfront_origin" {
|
|
1085
|
+
count = local.cloudfront_https_origin && var.hosted_zone_id != null ? 1 : 0
|
|
1086
|
+
zone_id = var.hosted_zone_id
|
|
1087
|
+
name = var.cloudfront_origin_domain_name
|
|
1088
|
+
type = "A"
|
|
1089
|
+
|
|
1090
|
+
alias {
|
|
1091
|
+
name = aws_lb.open_uptime.dns_name
|
|
1092
|
+
zone_id = aws_lb.open_uptime.zone_id
|
|
1093
|
+
evaluate_target_health = true
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1019
1097
|
data "aws_iam_policy_document" "ecs_assume_role" {
|
|
1020
1098
|
statement {
|
|
1021
1099
|
actions = ["sts:AssumeRole"]
|
|
@@ -1194,12 +1272,14 @@ resource "aws_ecs_task_definition" "service" {
|
|
|
1194
1272
|
}
|
|
1195
1273
|
|
|
1196
1274
|
resource "aws_ecs_service" "web" {
|
|
1197
|
-
name
|
|
1198
|
-
cluster
|
|
1199
|
-
task_definition
|
|
1200
|
-
desired_count
|
|
1201
|
-
launch_type
|
|
1202
|
-
|
|
1275
|
+
name = "${local.prefix}-web"
|
|
1276
|
+
cluster = aws_ecs_cluster.open_uptime.id
|
|
1277
|
+
task_definition = aws_ecs_task_definition.service["web"].arn
|
|
1278
|
+
desired_count = local.services.web.desired_count
|
|
1279
|
+
launch_type = "FARGATE"
|
|
1280
|
+
enable_ecs_managed_tags = true
|
|
1281
|
+
propagate_tags = "SERVICE"
|
|
1282
|
+
tags = local.tags
|
|
1203
1283
|
|
|
1204
1284
|
deployment_circuit_breaker {
|
|
1205
1285
|
enable = true
|
|
@@ -1226,12 +1306,14 @@ resource "aws_ecs_service" "worker" {
|
|
|
1226
1306
|
for key, value in local.services : key => value if key != "web" && key != "migration"
|
|
1227
1307
|
}
|
|
1228
1308
|
|
|
1229
|
-
name
|
|
1230
|
-
cluster
|
|
1231
|
-
task_definition
|
|
1232
|
-
desired_count
|
|
1233
|
-
launch_type
|
|
1234
|
-
|
|
1309
|
+
name = "${local.prefix}-${each.key}"
|
|
1310
|
+
cluster = aws_ecs_cluster.open_uptime.id
|
|
1311
|
+
task_definition = aws_ecs_task_definition.service[each.key].arn
|
|
1312
|
+
desired_count = each.value.desired_count
|
|
1313
|
+
launch_type = "FARGATE"
|
|
1314
|
+
enable_ecs_managed_tags = true
|
|
1315
|
+
propagate_tags = "SERVICE"
|
|
1316
|
+
tags = local.tags
|
|
1235
1317
|
|
|
1236
1318
|
deployment_circuit_breaker {
|
|
1237
1319
|
enable = true
|
package/infra/aws/outputs.tf
CHANGED
|
@@ -18,6 +18,14 @@ output "cloudfront_domain_name" {
|
|
|
18
18
|
value = try(aws_cloudfront_distribution.open_uptime[0].domain_name, null)
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
output "cloudfront_origin_protocol_policy" {
|
|
22
|
+
value = local.use_cloudfront ? var.cloudfront_origin_protocol_policy : null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
output "cloudfront_origin_domain_name" {
|
|
26
|
+
value = local.use_cloudfront ? (local.cloudfront_https_origin ? var.cloudfront_origin_domain_name : aws_lb.open_uptime.dns_name) : null
|
|
27
|
+
}
|
|
28
|
+
|
|
21
29
|
output "protected_access_url" {
|
|
22
30
|
value = var.protected_access_mode == "cloudfront_default_domain" ? "https://${aws_cloudfront_distribution.open_uptime[0].domain_name}" : "https://${var.hostname}"
|
|
23
31
|
}
|
|
@@ -11,6 +11,9 @@ workspace_id = "workspace-id"
|
|
|
11
11
|
vpc_id = "vpc-xxxxxxxx"
|
|
12
12
|
ecr_repository_name = "open-uptime"
|
|
13
13
|
protected_access_mode = "cloudfront_default_domain"
|
|
14
|
+
cloudfront_origin_protocol_policy = "http-only"
|
|
15
|
+
allow_cloudfront_http_origin_live_traffic = false
|
|
16
|
+
cloudfront_origin_domain_name = null
|
|
14
17
|
enable_cloudfront_origin_verify_header = false
|
|
15
18
|
cloudfront_origin_verify_header_name = "X-Open-Uptime-Origin-Verify"
|
|
16
19
|
cloudfront_origin_verify_header_value = null
|
|
@@ -19,7 +22,8 @@ alb_ingress_cidr_blocks = []
|
|
|
19
22
|
private_subnet_ids = ["subnet-replace-private-a", "subnet-replace-private-b"]
|
|
20
23
|
private_route_table_ids = ["rtb-replace-private"]
|
|
21
24
|
container_image = "123456789012.dkr.ecr.us-east-1.amazonaws.com/open-uptime@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
|
22
|
-
runtime_package_version
|
|
25
|
+
runtime_package_version = "0.1.26"
|
|
26
|
+
runtime_package_integrity = null
|
|
23
27
|
certificate_arn = null
|
|
24
28
|
hosted_zone_id = null
|
|
25
29
|
app_env_secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret:open-uptime/prod/app/env"
|
package/infra/aws/variables.tf
CHANGED
|
@@ -77,7 +77,7 @@ variable "ecr_repository_name" {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
variable "protected_access_mode" {
|
|
80
|
-
description = "Protected web access mode. cloudfront_default_domain uses the CloudFront HTTPS default domain and restricts ALB
|
|
80
|
+
description = "Protected web access mode. cloudfront_default_domain uses the CloudFront HTTPS default domain and restricts the ALB origin to CloudFront origin-facing ranges. alb_https_cert uses an ALB HTTPS listener with certificate_arn."
|
|
81
81
|
type = string
|
|
82
82
|
default = "cloudfront_default_domain"
|
|
83
83
|
|
|
@@ -87,6 +87,46 @@ variable "protected_access_mode" {
|
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
variable "cloudfront_origin_protocol_policy" {
|
|
91
|
+
description = "CloudFront-to-ALB origin protocol policy. Keep http-only until an origin hostname and matching ACM certificate are approved; set https-only with cloudfront_origin_domain_name and certificate_arn before token-bearing live traffic."
|
|
92
|
+
type = string
|
|
93
|
+
default = "http-only"
|
|
94
|
+
|
|
95
|
+
validation {
|
|
96
|
+
condition = contains(["http-only", "https-only"], var.cloudfront_origin_protocol_policy)
|
|
97
|
+
error_message = "cloudfront_origin_protocol_policy must be http-only or https-only."
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
variable "allow_cloudfront_http_origin_live_traffic" {
|
|
102
|
+
description = "Explicit risk acceptance for setting web desired count above 0 while CloudFront-to-ALB origin transport is http-only. Keep false unless a named operator accepts the temporary HTTP-origin bridge risk for a bounded smoke."
|
|
103
|
+
type = bool
|
|
104
|
+
default = false
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
variable "cloudfront_origin_domain_name" {
|
|
108
|
+
description = "DNS hostname CloudFront uses for the ALB custom origin when cloudfront_origin_protocol_policy is https-only. The hostname must resolve to the ALB and match certificate_arn. Leave null for the default HTTP-origin bridge."
|
|
109
|
+
type = string
|
|
110
|
+
default = null
|
|
111
|
+
nullable = true
|
|
112
|
+
|
|
113
|
+
validation {
|
|
114
|
+
condition = (
|
|
115
|
+
var.cloudfront_origin_domain_name == null
|
|
116
|
+
|| can(regex("^[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)+$", var.cloudfront_origin_domain_name))
|
|
117
|
+
)
|
|
118
|
+
error_message = "cloudfront_origin_domain_name must be null or a valid DNS hostname."
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
validation {
|
|
122
|
+
condition = (
|
|
123
|
+
!(var.protected_access_mode == "cloudfront_default_domain" && var.cloudfront_origin_protocol_policy == "https-only")
|
|
124
|
+
|| var.cloudfront_origin_domain_name != null
|
|
125
|
+
)
|
|
126
|
+
error_message = "cloudfront_origin_domain_name is required when CloudFront HTTPS origin is enabled."
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
90
130
|
variable "enable_cloudfront_origin_verify_header" {
|
|
91
131
|
description = "When true in cloudfront_default_domain mode, CloudFront sends a private origin header and the ALB listener rejects requests missing the matching value."
|
|
92
132
|
type = bool
|
|
@@ -201,7 +241,7 @@ variable "container_image" {
|
|
|
201
241
|
variable "runtime_package_version" {
|
|
202
242
|
description = "Published @hasna/uptime package version that CodeBuild should build into the ECR image."
|
|
203
243
|
type = string
|
|
204
|
-
default = "0.1.
|
|
244
|
+
default = "0.1.26"
|
|
205
245
|
|
|
206
246
|
validation {
|
|
207
247
|
condition = can(regex("^[0-9]+\\.[0-9]+\\.[0-9]+(-[0-9A-Za-z.-]+)?$", var.runtime_package_version))
|
|
@@ -209,8 +249,20 @@ variable "runtime_package_version" {
|
|
|
209
249
|
}
|
|
210
250
|
}
|
|
211
251
|
|
|
252
|
+
variable "runtime_package_integrity" {
|
|
253
|
+
description = "Optional expected npm dist.integrity value for @hasna/uptime@runtime_package_version. When set, CodeBuild verifies the registry tarball integrity before building the image."
|
|
254
|
+
type = string
|
|
255
|
+
default = null
|
|
256
|
+
nullable = true
|
|
257
|
+
|
|
258
|
+
validation {
|
|
259
|
+
condition = var.runtime_package_integrity == null || can(regex("^sha512-[A-Za-z0-9+/=]+$", var.runtime_package_integrity))
|
|
260
|
+
error_message = "runtime_package_integrity must be null or an npm sha512 integrity string."
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
212
264
|
variable "certificate_arn" {
|
|
213
|
-
description = "ACM certificate ARN for ALB HTTPS mode. Leave null when
|
|
265
|
+
description = "ACM certificate ARN for ALB HTTPS mode or CloudFront HTTPS-origin mode. Leave null only when the ALB is not serving HTTPS."
|
|
214
266
|
type = string
|
|
215
267
|
default = null
|
|
216
268
|
|
|
@@ -220,8 +272,11 @@ variable "certificate_arn" {
|
|
|
220
272
|
}
|
|
221
273
|
|
|
222
274
|
validation {
|
|
223
|
-
condition
|
|
224
|
-
|
|
275
|
+
condition = (
|
|
276
|
+
!(var.protected_access_mode == "alb_https_cert" || (var.protected_access_mode == "cloudfront_default_domain" && var.cloudfront_origin_protocol_policy == "https-only"))
|
|
277
|
+
|| var.certificate_arn != null
|
|
278
|
+
)
|
|
279
|
+
error_message = "certificate_arn is required when protected_access_mode is alb_https_cert or CloudFront HTTPS origin is enabled."
|
|
225
280
|
}
|
|
226
281
|
}
|
|
227
282
|
|
|
@@ -298,6 +353,25 @@ variable "desired_counts" {
|
|
|
298
353
|
])
|
|
299
354
|
error_message = "EFS SQLite bridge requires web desired count 0 or 1 and scheduler/public-probe/reporter/migration desired counts 0."
|
|
300
355
|
}
|
|
356
|
+
|
|
357
|
+
validation {
|
|
358
|
+
condition = (
|
|
359
|
+
lookup(var.desired_counts, "web", 0) == 0
|
|
360
|
+
|| var.protected_access_mode != "cloudfront_default_domain"
|
|
361
|
+
|| var.enable_cloudfront_origin_verify_header
|
|
362
|
+
)
|
|
363
|
+
error_message = "web desired count above 0 in cloudfront_default_domain mode requires enable_cloudfront_origin_verify_header=true."
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
validation {
|
|
367
|
+
condition = (
|
|
368
|
+
lookup(var.desired_counts, "web", 0) == 0
|
|
369
|
+
|| var.protected_access_mode != "cloudfront_default_domain"
|
|
370
|
+
|| var.cloudfront_origin_protocol_policy == "https-only"
|
|
371
|
+
|| var.allow_cloudfront_http_origin_live_traffic
|
|
372
|
+
)
|
|
373
|
+
error_message = "web desired count above 0 requires CloudFront HTTPS-origin mode, or explicit allow_cloudfront_http_origin_live_traffic=true risk acceptance for a bounded smoke."
|
|
374
|
+
}
|
|
301
375
|
}
|
|
302
376
|
|
|
303
377
|
variable "alarm_actions" {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/uptime",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.26",
|
|
4
4
|
"description": "Local-first uptime and downtime monitoring service with CLI, MCP, SDK, SQLite persistence, and a dashboard.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"Dockerfile",
|
|
27
27
|
"Dockerfile.package",
|
|
28
28
|
".dockerignore",
|
|
29
|
+
"bun.lock",
|
|
29
30
|
"infra/aws/README.md",
|
|
30
31
|
"infra/aws/.terraform.lock.hcl",
|
|
31
32
|
"infra/aws/*.tf",
|
|
@@ -68,10 +69,14 @@
|
|
|
68
69
|
"./cloud-plan": {
|
|
69
70
|
"types": "./dist/cloud-plan.d.ts",
|
|
70
71
|
"import": "./dist/cloud-plan.js"
|
|
72
|
+
},
|
|
73
|
+
"./workers": {
|
|
74
|
+
"types": "./dist/workers.d.ts",
|
|
75
|
+
"import": "./dist/workers.js"
|
|
71
76
|
}
|
|
72
77
|
},
|
|
73
78
|
"scripts": {
|
|
74
|
-
"build": "rm -rf dist && bun build src/cli/index.ts --outdir dist/cli --target bun --external @modelcontextprotocol/sdk && bun build src/mcp/index.ts --outdir dist/mcp --target bun --external @modelcontextprotocol/sdk && bun build src/index.ts src/api.ts src/service.ts src/store.ts src/checks.ts src/imports.ts src/report.ts src/probes.ts src/cloud-plan.ts src/types.ts src/paths.ts src/dashboard.ts src/version.ts --root src --outdir dist --target bun && tsc -p tsconfig.build.json --emitDeclarationOnly --outDir dist && chmod +x dist/cli/index.js dist/mcp/index.js",
|
|
79
|
+
"build": "rm -rf dist && bun build src/cli/index.ts --outdir dist/cli --target bun --external @modelcontextprotocol/sdk && bun build src/mcp/index.ts --outdir dist/mcp --target bun --external @modelcontextprotocol/sdk && bun build src/index.ts src/api.ts src/service.ts src/store.ts src/checks.ts src/imports.ts src/report.ts src/probes.ts src/cloud-plan.ts src/workers.ts src/types.ts src/paths.ts src/dashboard.ts src/version.ts --root src --outdir dist --target bun && tsc -p tsconfig.build.json --emitDeclarationOnly --outDir dist && chmod +x dist/cli/index.js dist/mcp/index.js",
|
|
75
80
|
"typecheck": "tsc --noEmit",
|
|
76
81
|
"test": "bun test ./tests",
|
|
77
82
|
"dev:cli": "bun run src/cli/index.ts",
|