@emilia-protocol/gate 0.9.5 → 0.10.0
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 +70 -0
- package/LICENSE +190 -0
- package/README.md +75 -16
- package/action-control-manifest.js +26 -0
- package/adapters/_kit.js +47 -5
- package/adapters/aws.js +15 -4
- package/adapters/github-demo.mjs +25 -22
- package/adapters/github.js +1 -1
- package/adapters/jira.js +1 -0
- package/adapters/linear.js +1 -0
- package/adapters/salesforce.js +1 -0
- package/adapters/stripe.js +1 -1
- package/adapters/supabase.js +26 -4
- package/adapters/vercel.js +33 -4
- package/aec-execution.js +1 -3
- package/breakglass.js +285 -51
- package/control-plane.js +341 -0
- package/coverage.js +722 -0
- package/custody-demo.mjs +13 -3
- package/demo.mjs +9 -3
- package/deploy/helm/README.md +16 -8
- package/deploy/helm/emilia-gate/Chart.yaml +3 -1
- package/deploy/helm/emilia-gate/templates/NOTES.txt +3 -2
- package/deploy/helm/emilia-gate/templates/deployment.yaml +55 -1
- package/deploy/helm/emilia-gate/values.yaml +18 -2
- package/deploy/helm/emilia-gate-service/Chart.yaml +11 -0
- package/deploy/helm/emilia-gate-service/README.md +81 -0
- package/deploy/helm/emilia-gate-service/templates/NOTES.txt +12 -0
- package/deploy/helm/emilia-gate-service/templates/_helpers.tpl +83 -0
- package/deploy/helm/emilia-gate-service/templates/deployment.yaml +153 -0
- package/deploy/helm/emilia-gate-service/templates/migration-job.yaml +73 -0
- package/deploy/helm/emilia-gate-service/templates/networkpolicy.yaml +113 -0
- package/deploy/helm/emilia-gate-service/templates/pdb.yaml +14 -0
- package/deploy/helm/emilia-gate-service/templates/service.yaml +20 -0
- package/deploy/helm/emilia-gate-service/templates/serviceaccount.yaml +8 -0
- package/deploy/helm/emilia-gate-service/tests/fixtures/001_gate.sql +26 -0
- package/deploy/helm/emilia-gate-service/tests/fixtures/002_runtime_access.sql +32 -0
- package/deploy/helm/emilia-gate-service/tests/fixtures/gate.config.mjs +29 -0
- package/deploy/helm/emilia-gate-service/tests/render-check.sh +145 -0
- package/deploy/helm/emilia-gate-service/values.schema.json +145 -0
- package/deploy/helm/emilia-gate-service/values.yaml +166 -0
- package/deploy/sql/001-runtime.sql +707 -0
- package/deploy/terraform/README.md +24 -14
- package/deploy/terraform/main.tf +59 -0
- package/deploy/terraform/service/README.md +81 -0
- package/deploy/terraform/service/main.tf +718 -0
- package/deploy/terraform/service/outputs.tf +19 -0
- package/deploy/terraform/service/tests/validate.sh +24 -0
- package/deploy/terraform/service/tests/validation.tftest.hcl +168 -0
- package/deploy/terraform/service/variables.tf +459 -0
- package/deploy/terraform/service/versions.tf +10 -0
- package/deploy/terraform/variables.tf +95 -2
- package/deployment-attestation.js +248 -0
- package/eg1-conformance.js +46 -12
- package/enterprise.js +6 -2
- package/ep-assure.mjs +3 -2
- package/evidence-postgres.js +357 -0
- package/evidence.js +10 -3
- package/execution-binding.js +141 -26
- package/index.js +556 -115
- package/key-registry.js +51 -19
- package/mcp.js +4 -2
- package/network-witness.js +500 -0
- package/package.json +25 -5
- package/reliance-kernel.js +5 -6
- package/reliance-packet.js +43 -10
- package/reports/assurance-package.js +45 -19
- package/reports/reperform.js +78 -18
- package/reports/underwriter.js +1 -1
- package/settlement.js +300 -0
- package/store-postgres.js +14 -0
- package/store.js +26 -5
- package/strict-json.js +86 -0
- package/witness-postgres.js +97 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
output "service_name" {
|
|
2
|
+
description = "ClusterIP Service name."
|
|
3
|
+
value = kubernetes_service_v1.gate.metadata[0].name
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
output "service_dns" {
|
|
7
|
+
description = "In-cluster DNS name for the Gate service."
|
|
8
|
+
value = "${kubernetes_service_v1.gate.metadata[0].name}.${var.namespace}.svc"
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
output "deployment_name" {
|
|
12
|
+
description = "Gate Deployment name."
|
|
13
|
+
value = kubernetes_deployment_v1.gate.metadata[0].name
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
output "migration_job_name" {
|
|
17
|
+
description = "Migration Job name, or null when migrations are disabled."
|
|
18
|
+
value = var.migration_enabled ? kubernetes_job_v1.migration[0].metadata[0].name : null
|
|
19
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
module_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
|
+
terraform_bin="${TERRAFORM_BIN:-terraform}"
|
|
6
|
+
|
|
7
|
+
if ! command -v "$terraform_bin" >/dev/null 2>&1; then
|
|
8
|
+
echo "terraform is required (or set TERRAFORM_BIN)" >&2
|
|
9
|
+
exit 127
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
"$terraform_bin" fmt -check -recursive "$module_dir"
|
|
13
|
+
|
|
14
|
+
tmp_dir="$(mktemp -d)"
|
|
15
|
+
trap 'rm -rf "$tmp_dir"' EXIT
|
|
16
|
+
cp "$module_dir"/*.tf "$tmp_dir"/
|
|
17
|
+
cp -R "$module_dir/tests" "$tmp_dir/tests"
|
|
18
|
+
|
|
19
|
+
TF_DATA_DIR="$tmp_dir/.terraform" "$terraform_bin" -chdir="$tmp_dir" init \
|
|
20
|
+
-backend=false -input=false >/dev/null
|
|
21
|
+
TF_DATA_DIR="$tmp_dir/.terraform" "$terraform_bin" -chdir="$tmp_dir" validate
|
|
22
|
+
TF_DATA_DIR="$tmp_dir/.terraform" "$terraform_bin" -chdir="$tmp_dir" test
|
|
23
|
+
|
|
24
|
+
echo "Terraform formatting, provider-schema, and validation-contract tests passed"
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
mock_provider "kubernetes" {}
|
|
2
|
+
|
|
3
|
+
run "valid_distinct_migration_and_secret_env" {
|
|
4
|
+
command = plan
|
|
5
|
+
|
|
6
|
+
variables {
|
|
7
|
+
namespace = "gate-system"
|
|
8
|
+
image = "registry.example.test/security/emilia-gate-service@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
|
9
|
+
configuration_secret_name = "gate-configuration"
|
|
10
|
+
postgres_secret_name = "gate-postgres"
|
|
11
|
+
migration_postgres_secret_name = "gate-postgres-migrate"
|
|
12
|
+
migration_postgres_secret_key = "database-url"
|
|
13
|
+
api_token_secret_name = "gate-api-token"
|
|
14
|
+
issuer_roots_secret_name = "gate-issuer-roots"
|
|
15
|
+
github_token_secret_name = "gate-github"
|
|
16
|
+
github_token_secret_key = "token"
|
|
17
|
+
secret_env = {
|
|
18
|
+
SIEM_TOKEN = {
|
|
19
|
+
secret_name = "gate-siem"
|
|
20
|
+
secret_key = "token"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
assert {
|
|
26
|
+
condition = anytrue([
|
|
27
|
+
for item in kubernetes_deployment_v1.gate.spec[0].template[0].spec[0].container[0].env :
|
|
28
|
+
item.name == "GITHUB_TOKEN"
|
|
29
|
+
&& try(item.value_from[0].secret_key_ref[0].name == "gate-github", false)
|
|
30
|
+
])
|
|
31
|
+
error_message = "GitHub token must render only as a secretKeyRef."
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
assert {
|
|
35
|
+
condition = anytrue([
|
|
36
|
+
for item in kubernetes_deployment_v1.gate.spec[0].template[0].spec[0].container[0].env :
|
|
37
|
+
item.name == "SIEM_TOKEN"
|
|
38
|
+
&& try(item.value_from[0].secret_key_ref[0].name == "gate-siem", false)
|
|
39
|
+
])
|
|
40
|
+
error_message = "Generic secret environment input must render as a secretKeyRef."
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
assert {
|
|
44
|
+
condition = anytrue([
|
|
45
|
+
for item in kubernetes_job_v1.migration[0].spec[0].template[0].spec[0].container[0].env :
|
|
46
|
+
item.name == "DATABASE_URL"
|
|
47
|
+
&& try(item.value_from[0].secret_key_ref[0].name == "gate-postgres-migrate", false)
|
|
48
|
+
])
|
|
49
|
+
error_message = "Migration Job must use the distinct migration Secret."
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
assert {
|
|
53
|
+
condition = (
|
|
54
|
+
kubernetes_deployment_v1.gate.spec[0].template[0].spec[0].container[0].startup_probe[0].http_get[0].path == "/v1/live"
|
|
55
|
+
&& kubernetes_deployment_v1.gate.spec[0].template[0].spec[0].container[0].liveness_probe[0].http_get[0].path == "/v1/live"
|
|
56
|
+
&& kubernetes_deployment_v1.gate.spec[0].template[0].spec[0].container[0].readiness_probe[0].http_get[0].path == "/v1/ready"
|
|
57
|
+
)
|
|
58
|
+
error_message = "Gate probes must use the service live and ready routes."
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
run "reject_explicit_latest_image" {
|
|
63
|
+
command = plan
|
|
64
|
+
|
|
65
|
+
variables {
|
|
66
|
+
namespace = "gate-system"
|
|
67
|
+
image = "registry.example.test/security/emilia-gate-service:latest"
|
|
68
|
+
configuration_secret_name = "gate-configuration"
|
|
69
|
+
postgres_secret_name = "gate-postgres"
|
|
70
|
+
migration_postgres_secret_name = "gate-postgres-migrate"
|
|
71
|
+
migration_postgres_secret_key = "database-url"
|
|
72
|
+
api_token_secret_name = "gate-api-token"
|
|
73
|
+
issuer_roots_secret_name = "gate-issuer-roots"
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
expect_failures = [var.image]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
run "reject_implicit_latest_image" {
|
|
80
|
+
command = plan
|
|
81
|
+
|
|
82
|
+
variables {
|
|
83
|
+
namespace = "gate-system"
|
|
84
|
+
image = "registry.example.test/security/emilia-gate-service"
|
|
85
|
+
configuration_secret_name = "gate-configuration"
|
|
86
|
+
postgres_secret_name = "gate-postgres"
|
|
87
|
+
migration_postgres_secret_name = "gate-postgres-migrate"
|
|
88
|
+
migration_postgres_secret_key = "database-url"
|
|
89
|
+
api_token_secret_name = "gate-api-token"
|
|
90
|
+
issuer_roots_secret_name = "gate-issuer-roots"
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
expect_failures = [var.image]
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
run "reject_missing_migration_secret" {
|
|
97
|
+
command = plan
|
|
98
|
+
|
|
99
|
+
variables {
|
|
100
|
+
namespace = "gate-system"
|
|
101
|
+
image = "registry.example.test/security/emilia-gate-service@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
|
102
|
+
configuration_secret_name = "gate-configuration"
|
|
103
|
+
postgres_secret_name = "gate-postgres"
|
|
104
|
+
api_token_secret_name = "gate-api-token"
|
|
105
|
+
issuer_roots_secret_name = "gate-issuer-roots"
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
expect_failures = [kubernetes_job_v1.migration]
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
run "reject_equal_migration_secret" {
|
|
112
|
+
command = plan
|
|
113
|
+
|
|
114
|
+
variables {
|
|
115
|
+
namespace = "gate-system"
|
|
116
|
+
image = "registry.example.test/security/emilia-gate-service@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
|
117
|
+
configuration_secret_name = "gate-configuration"
|
|
118
|
+
postgres_secret_name = "gate-postgres"
|
|
119
|
+
migration_postgres_secret_name = "gate-postgres"
|
|
120
|
+
migration_postgres_secret_key = "database-url"
|
|
121
|
+
api_token_secret_name = "gate-api-token"
|
|
122
|
+
issuer_roots_secret_name = "gate-issuer-roots"
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
expect_failures = [kubernetes_job_v1.migration]
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
run "allow_disabled_migration_without_secret" {
|
|
129
|
+
command = plan
|
|
130
|
+
|
|
131
|
+
variables {
|
|
132
|
+
namespace = "gate-system"
|
|
133
|
+
image = "registry.example.test/security/emilia-gate-service@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
|
134
|
+
configuration_secret_name = "gate-configuration"
|
|
135
|
+
postgres_secret_name = "gate-postgres"
|
|
136
|
+
api_token_secret_name = "gate-api-token"
|
|
137
|
+
issuer_roots_secret_name = "gate-issuer-roots"
|
|
138
|
+
migration_enabled = false
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
assert {
|
|
142
|
+
condition = length(kubernetes_job_v1.migration) == 0
|
|
143
|
+
error_message = "Disabled migrations must not create a Job."
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
run "reject_secret_env_override" {
|
|
148
|
+
command = plan
|
|
149
|
+
|
|
150
|
+
variables {
|
|
151
|
+
namespace = "gate-system"
|
|
152
|
+
image = "registry.example.test/security/emilia-gate-service@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
|
153
|
+
configuration_secret_name = "gate-configuration"
|
|
154
|
+
postgres_secret_name = "gate-postgres"
|
|
155
|
+
migration_postgres_secret_name = "gate-postgres-migrate"
|
|
156
|
+
migration_postgres_secret_key = "database-url"
|
|
157
|
+
api_token_secret_name = "gate-api-token"
|
|
158
|
+
issuer_roots_secret_name = "gate-issuer-roots"
|
|
159
|
+
secret_env = {
|
|
160
|
+
DATABASE_URL = {
|
|
161
|
+
secret_name = "attacker-secret"
|
|
162
|
+
secret_key = "database-url"
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
expect_failures = [kubernetes_deployment_v1.gate]
|
|
168
|
+
}
|
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
variable "name" {
|
|
2
|
+
description = "Kubernetes resource name prefix."
|
|
3
|
+
type = string
|
|
4
|
+
default = "emilia-gate-service"
|
|
5
|
+
|
|
6
|
+
validation {
|
|
7
|
+
condition = can(regex("^[a-z0-9]([-a-z0-9]*[a-z0-9])?$", var.name)) && length(var.name) <= 52
|
|
8
|
+
error_message = "name must be a DNS label no longer than 52 characters."
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
variable "namespace" {
|
|
13
|
+
description = "Existing Kubernetes namespace in which to deploy."
|
|
14
|
+
type = string
|
|
15
|
+
|
|
16
|
+
validation {
|
|
17
|
+
condition = can(regex("^[a-z0-9]([-a-z0-9]*[a-z0-9])?$", var.namespace))
|
|
18
|
+
error_message = "namespace must be a Kubernetes DNS label."
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
variable "image" {
|
|
23
|
+
description = "Complete BYOC image reference built from Dockerfile.gate. No official public image is implied. Use a digest in production."
|
|
24
|
+
type = string
|
|
25
|
+
|
|
26
|
+
validation {
|
|
27
|
+
condition = (
|
|
28
|
+
trimspace(var.image) == var.image
|
|
29
|
+
&& (
|
|
30
|
+
can(regex("@sha256:[a-f0-9]{64}$", var.image))
|
|
31
|
+
|| (
|
|
32
|
+
can(regex(":[A-Za-z0-9_][A-Za-z0-9._-]{0,127}$", element(reverse(split("/", var.image)), 0)))
|
|
33
|
+
&& !endswith(lower(var.image), ":latest")
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
error_message = "image must use an explicit non-latest tag or sha256 digest; untagged and implicit-latest references are refused."
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
variable "image_pull_policy" {
|
|
42
|
+
description = "Kubernetes image pull policy."
|
|
43
|
+
type = string
|
|
44
|
+
default = "IfNotPresent"
|
|
45
|
+
|
|
46
|
+
validation {
|
|
47
|
+
condition = contains(["Always", "IfNotPresent", "Never"], var.image_pull_policy)
|
|
48
|
+
error_message = "image_pull_policy must be Always, IfNotPresent, or Never."
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
variable "image_pull_secrets" {
|
|
53
|
+
description = "Names of existing image pull Secrets."
|
|
54
|
+
type = list(string)
|
|
55
|
+
default = []
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
variable "replicas" {
|
|
59
|
+
description = "Gate service replicas. Two is the production-shaped default."
|
|
60
|
+
type = number
|
|
61
|
+
default = 2
|
|
62
|
+
|
|
63
|
+
validation {
|
|
64
|
+
condition = var.replicas >= 1 && floor(var.replicas) == var.replicas
|
|
65
|
+
error_message = "replicas must be a positive integer."
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
variable "port" {
|
|
70
|
+
description = "Gate service container and ClusterIP port."
|
|
71
|
+
type = number
|
|
72
|
+
default = 8080
|
|
73
|
+
|
|
74
|
+
validation {
|
|
75
|
+
condition = var.port >= 1 && var.port <= 65535
|
|
76
|
+
error_message = "port must be between 1 and 65535."
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
variable "log_level" {
|
|
81
|
+
description = "EP_GATE_LOG_LEVEL passed to the service."
|
|
82
|
+
type = string
|
|
83
|
+
default = "info"
|
|
84
|
+
|
|
85
|
+
validation {
|
|
86
|
+
condition = contains(["debug", "info", "warn", "error"], var.log_level)
|
|
87
|
+
error_message = "log_level must be debug, info, warn, or error."
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
variable "configuration_secret_name" {
|
|
92
|
+
description = "Name of an existing Secret containing the operator-owned gate.config.mjs and migrate.mjs modules."
|
|
93
|
+
type = string
|
|
94
|
+
|
|
95
|
+
validation {
|
|
96
|
+
condition = trimspace(var.configuration_secret_name) != ""
|
|
97
|
+
error_message = "configuration_secret_name is required."
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
variable "configuration_secret_key" {
|
|
102
|
+
description = "Key in configuration_secret_name containing the apps/gate-service ESM config module."
|
|
103
|
+
type = string
|
|
104
|
+
default = "gate.config.mjs"
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
variable "migration_script_secret_key" {
|
|
108
|
+
description = "Key in configuration_secret_name containing the idempotent migration module."
|
|
109
|
+
type = string
|
|
110
|
+
default = "migrate.mjs"
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
variable "configuration_mount_path" {
|
|
114
|
+
description = "Read-only directory where operator configuration modules are mounted."
|
|
115
|
+
type = string
|
|
116
|
+
default = "/app/config"
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
variable "postgres_secret_name" {
|
|
120
|
+
description = "Name of an existing Secret containing the Postgres connection URL. Only the reference is stored by Terraform."
|
|
121
|
+
type = string
|
|
122
|
+
|
|
123
|
+
validation {
|
|
124
|
+
condition = trimspace(var.postgres_secret_name) != ""
|
|
125
|
+
error_message = "postgres_secret_name is required."
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
variable "postgres_secret_key" {
|
|
130
|
+
description = "Key in postgres_secret_name containing the connection URL."
|
|
131
|
+
type = string
|
|
132
|
+
default = "database-url"
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
variable "postgres_env_name" {
|
|
136
|
+
description = "Runtime environment variable populated from the Postgres Secret."
|
|
137
|
+
type = string
|
|
138
|
+
default = "DATABASE_URL"
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
variable "migration_postgres_secret_name" {
|
|
142
|
+
description = "Existing Secret for a DDL-capable migration login. Required when migration_enabled is true and must differ from postgres_secret_name."
|
|
143
|
+
type = string
|
|
144
|
+
default = null
|
|
145
|
+
nullable = true
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
variable "migration_postgres_secret_key" {
|
|
149
|
+
description = "Key in migration_postgres_secret_name containing the migration URL. Required when migration_enabled is true."
|
|
150
|
+
type = string
|
|
151
|
+
default = null
|
|
152
|
+
nullable = true
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
variable "api_token_secret_name" {
|
|
156
|
+
description = "Name of an existing Secret containing the Gate action-API bearer token. Only the reference is stored by Terraform."
|
|
157
|
+
type = string
|
|
158
|
+
|
|
159
|
+
validation {
|
|
160
|
+
condition = trimspace(var.api_token_secret_name) != ""
|
|
161
|
+
error_message = "api_token_secret_name is required."
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
variable "api_token_secret_key" {
|
|
166
|
+
description = "Key in api_token_secret_name containing the Gate API token."
|
|
167
|
+
type = string
|
|
168
|
+
default = "api-token"
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
variable "api_token_env_name" {
|
|
172
|
+
description = "Runtime environment variable populated from the Gate API-token Secret."
|
|
173
|
+
type = string
|
|
174
|
+
default = "EMILIA_GATE_API_TOKEN"
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
variable "kms_secret_name" {
|
|
178
|
+
description = "Optional existing Secret containing KMS configuration used by an operator extension. The Gate service itself does not mint or require a signing key."
|
|
179
|
+
type = string
|
|
180
|
+
default = null
|
|
181
|
+
nullable = true
|
|
182
|
+
|
|
183
|
+
validation {
|
|
184
|
+
condition = var.kms_secret_name == null ? true : trimspace(var.kms_secret_name) != ""
|
|
185
|
+
error_message = "kms_secret_name must be null or non-empty."
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
variable "kms_secret_key" {
|
|
190
|
+
description = "Key in kms_secret_name containing the KMS key identifier/config."
|
|
191
|
+
type = string
|
|
192
|
+
default = "kms-key-id"
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
variable "kms_env_name" {
|
|
196
|
+
description = "Runtime environment variable populated from the KMS Secret."
|
|
197
|
+
type = string
|
|
198
|
+
default = "EP_KMS_KEY_ID"
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
variable "issuer_roots_secret_name" {
|
|
202
|
+
description = "Name of an existing Secret containing the pinned issuer-root set. Only the reference is stored by Terraform."
|
|
203
|
+
type = string
|
|
204
|
+
|
|
205
|
+
validation {
|
|
206
|
+
condition = trimspace(var.issuer_roots_secret_name) != ""
|
|
207
|
+
error_message = "issuer_roots_secret_name is required."
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
variable "issuer_roots_secret_key" {
|
|
212
|
+
description = "Key in issuer_roots_secret_name containing the pinned issuer-root set."
|
|
213
|
+
type = string
|
|
214
|
+
default = "issuer-roots.json"
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
variable "issuer_roots_env_name" {
|
|
218
|
+
description = "Runtime environment variable populated from the issuer-roots Secret."
|
|
219
|
+
type = string
|
|
220
|
+
default = "EP_GATE_ISSUER_ROOTS"
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
variable "github_token_secret_name" {
|
|
224
|
+
description = "Optional existing Secret containing a GitHub token. Only the Secret reference enters Terraform state."
|
|
225
|
+
type = string
|
|
226
|
+
default = null
|
|
227
|
+
nullable = true
|
|
228
|
+
|
|
229
|
+
validation {
|
|
230
|
+
condition = var.github_token_secret_name == null ? true : can(regex("^[a-z0-9]([-a-z0-9.]*[a-z0-9])?$", var.github_token_secret_name))
|
|
231
|
+
error_message = "github_token_secret_name must be null or a Kubernetes Secret name."
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
variable "github_token_secret_key" {
|
|
236
|
+
description = "Key in github_token_secret_name containing the GitHub token."
|
|
237
|
+
type = string
|
|
238
|
+
default = "token"
|
|
239
|
+
|
|
240
|
+
validation {
|
|
241
|
+
condition = trimspace(var.github_token_secret_key) != ""
|
|
242
|
+
error_message = "github_token_secret_key must be non-empty."
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
variable "github_token_env_name" {
|
|
247
|
+
description = "Runtime environment variable populated from github_token_secret_name."
|
|
248
|
+
type = string
|
|
249
|
+
default = "GITHUB_TOKEN"
|
|
250
|
+
|
|
251
|
+
validation {
|
|
252
|
+
condition = can(regex("^[A-Za-z_][A-Za-z0-9_]*$", var.github_token_env_name))
|
|
253
|
+
error_message = "github_token_env_name must be a valid environment variable name."
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
variable "secret_env" {
|
|
258
|
+
description = "Additional runtime environment variables populated from existing Kubernetes Secrets. Values are references only; secret bytes never enter this module."
|
|
259
|
+
type = map(object({
|
|
260
|
+
secret_name = string
|
|
261
|
+
secret_key = string
|
|
262
|
+
}))
|
|
263
|
+
default = {}
|
|
264
|
+
|
|
265
|
+
validation {
|
|
266
|
+
condition = alltrue([
|
|
267
|
+
for env_name, reference in var.secret_env :
|
|
268
|
+
can(regex("^[A-Za-z_][A-Za-z0-9_]*$", env_name))
|
|
269
|
+
&& trimspace(reference.secret_name) != ""
|
|
270
|
+
&& trimspace(reference.secret_key) != ""
|
|
271
|
+
])
|
|
272
|
+
error_message = "secret_env keys must be environment variable names and each Secret name/key must be non-empty."
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
variable "extra_env" {
|
|
277
|
+
description = "Additional non-secret literal environment variables. Use external Secret wiring for sensitive values."
|
|
278
|
+
type = map(string)
|
|
279
|
+
default = {}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
variable "migration_enabled" {
|
|
283
|
+
description = "Run the database migration Job before the Deployment. Keep enabled outside controlled recovery operations."
|
|
284
|
+
type = bool
|
|
285
|
+
default = true
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
variable "migration_command" {
|
|
289
|
+
description = "Command run by the migration Job."
|
|
290
|
+
type = list(string)
|
|
291
|
+
default = ["node", "/app/config/migrate.mjs"]
|
|
292
|
+
|
|
293
|
+
validation {
|
|
294
|
+
condition = length(var.migration_command) > 0
|
|
295
|
+
error_message = "migration_command must contain at least one element."
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
variable "migration_revision" {
|
|
300
|
+
description = "Bump when migration inputs change without changing the image; it is included in the immutable Job name."
|
|
301
|
+
type = string
|
|
302
|
+
default = "schema-v1"
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
variable "migration_active_deadline_seconds" {
|
|
306
|
+
description = "Maximum migration Job runtime."
|
|
307
|
+
type = number
|
|
308
|
+
default = 600
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
variable "migration_backoff_limit" {
|
|
312
|
+
description = "Migration Job retry limit."
|
|
313
|
+
type = number
|
|
314
|
+
default = 3
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
variable "resources" {
|
|
318
|
+
description = "Service container requests and limits."
|
|
319
|
+
type = object({
|
|
320
|
+
requests = map(string)
|
|
321
|
+
limits = map(string)
|
|
322
|
+
})
|
|
323
|
+
default = {
|
|
324
|
+
requests = {
|
|
325
|
+
cpu = "100m"
|
|
326
|
+
memory = "128Mi"
|
|
327
|
+
}
|
|
328
|
+
limits = {
|
|
329
|
+
cpu = "1"
|
|
330
|
+
memory = "512Mi"
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
variable "migration_resources" {
|
|
336
|
+
description = "Migration Job requests and limits."
|
|
337
|
+
type = object({
|
|
338
|
+
requests = map(string)
|
|
339
|
+
limits = map(string)
|
|
340
|
+
})
|
|
341
|
+
default = {
|
|
342
|
+
requests = {
|
|
343
|
+
cpu = "50m"
|
|
344
|
+
memory = "64Mi"
|
|
345
|
+
}
|
|
346
|
+
limits = {
|
|
347
|
+
cpu = "500m"
|
|
348
|
+
memory = "256Mi"
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
variable "pdb_enabled" {
|
|
354
|
+
description = "Create a PodDisruptionBudget for service pods."
|
|
355
|
+
type = bool
|
|
356
|
+
default = true
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
variable "pdb_min_available" {
|
|
360
|
+
description = "Minimum service pods available during voluntary disruption."
|
|
361
|
+
type = string
|
|
362
|
+
default = "1"
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
variable "network_policy_enabled" {
|
|
366
|
+
description = "Create default-deny and explicit allow NetworkPolicies for gate and migration pods."
|
|
367
|
+
type = bool
|
|
368
|
+
default = true
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
variable "allow_same_namespace_ingress" {
|
|
372
|
+
description = "Allow pods in the deployment namespace to reach the gate service port."
|
|
373
|
+
type = bool
|
|
374
|
+
default = true
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
variable "dns_namespace_labels" {
|
|
378
|
+
description = "Namespace selector labels for cluster DNS."
|
|
379
|
+
type = map(string)
|
|
380
|
+
default = {
|
|
381
|
+
"kubernetes.io/metadata.name" = "kube-system"
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
variable "dns_pod_labels" {
|
|
386
|
+
description = "Pod selector labels for cluster DNS."
|
|
387
|
+
type = map(string)
|
|
388
|
+
default = {
|
|
389
|
+
"k8s-app" = "kube-dns"
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
variable "postgres_port" {
|
|
394
|
+
description = "Postgres egress port."
|
|
395
|
+
type = number
|
|
396
|
+
default = 5432
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
variable "postgres_pod_labels" {
|
|
400
|
+
description = "Same-namespace Postgres pod selector. Set to an empty map when using only managed-Postgres CIDRs."
|
|
401
|
+
type = map(string)
|
|
402
|
+
default = {
|
|
403
|
+
"app.kubernetes.io/name" = "postgresql"
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
variable "postgres_egress_cidrs" {
|
|
408
|
+
description = "Stable managed-Postgres or egress-proxy CIDRs allowed on postgres_port."
|
|
409
|
+
type = list(string)
|
|
410
|
+
default = []
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
variable "kms_egress_cidrs" {
|
|
414
|
+
description = "Stable KMS proxy/NAT CIDRs allowed on TCP 443. Standard NetworkPolicy cannot select KMS FQDNs."
|
|
415
|
+
type = list(string)
|
|
416
|
+
default = []
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
variable "github_egress_cidrs" {
|
|
420
|
+
description = "Stable GitHub egress-proxy CIDRs allowed on TCP 443. Prefer an FQDN-aware CNI policy over public IP snapshots."
|
|
421
|
+
type = list(string)
|
|
422
|
+
default = []
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
variable "siem_egress_cidrs" {
|
|
426
|
+
description = "Stable SIEM egress-proxy CIDRs allowed on TCP 443. Prefer an FQDN-aware CNI policy for SaaS SIEM endpoints."
|
|
427
|
+
type = list(string)
|
|
428
|
+
default = []
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
variable "pod_annotations" {
|
|
432
|
+
description = "Additional service pod annotations."
|
|
433
|
+
type = map(string)
|
|
434
|
+
default = {}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
variable "labels" {
|
|
438
|
+
description = "Additional labels added to resources. Do not override selector labels."
|
|
439
|
+
type = map(string)
|
|
440
|
+
default = {}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
variable "node_selector" {
|
|
444
|
+
description = "Service pod node selector."
|
|
445
|
+
type = map(string)
|
|
446
|
+
default = {}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
variable "tolerations" {
|
|
450
|
+
description = "Service pod tolerations in Kubernetes provider shape."
|
|
451
|
+
type = list(object({
|
|
452
|
+
key = optional(string)
|
|
453
|
+
operator = optional(string)
|
|
454
|
+
value = optional(string)
|
|
455
|
+
effect = optional(string)
|
|
456
|
+
toleration_seconds = optional(number)
|
|
457
|
+
}))
|
|
458
|
+
default = []
|
|
459
|
+
}
|