ce7cb702218693d08ea6d83464d2457c9250027c
This commit is contained in:
commit
34c5f8963c
15 changed files with 145336 additions and 0 deletions
75
terraform/external/oci-relay/.terraform.lock.hcl
generated
vendored
Normal file
75
terraform/external/oci-relay/.terraform.lock.hcl
generated
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# This file is maintained automatically by "tofu init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.opentofu.org/hashicorp/vault" {
|
||||
version = "5.10.1"
|
||||
constraints = "5.10.1"
|
||||
hashes = [
|
||||
"h1:/wVGPEJV4FjoW9C4h3M0pqN4Ie9b03anjUJWeylaciU=",
|
||||
"h1:2kBWbk7XTUlZv6SkvGEkIKXtp7iUeHCcxX8u/bVaq5w=",
|
||||
"h1:4TgeO1IDWziuoDs8zeB2WG2qovoJu6b8HLAg2b4hzF8=",
|
||||
"h1:HsmCwrH+qhRXcU3CIAB316NLPiw0t3alufc0MCJVzus=",
|
||||
"h1:Jv/4vkNUymS3yTTwA72G59IN74auixEOq9C6dN12ZIM=",
|
||||
"h1:WUy6Ea+dhkj8k79vo0qz9/vAyjripwTsf6DqIzzk5Y4=",
|
||||
"h1:bX8M1tM4MD/ZRiazv9JUG0vzZG2IFOHeEDkvE9vVoMY=",
|
||||
"h1:hu9CF1CrQGOCEF9sZEendFKRqbEXuHY4Rz2enypS/FE=",
|
||||
"h1:id6Hv41s6c/JBc1iK/HUA/0L36nQ8MNTnmoXosQRyGU=",
|
||||
"h1:nrNJNXmVUnx/gp6dG24S5Wx6gJJmQ5IpUBdJldWNsgk=",
|
||||
"h1:swXi7Yx24kZY53o7fuWeUe7QX4rNOGixx/fLL9sO3bE=",
|
||||
"h1:tcy+THs72hvdeNLgYicSgK0kA7dpgymaF1ylkg8Bjvw=",
|
||||
"h1:tp2AHrZv9QWX70YYqsk9EFcRJdXm0M4lMEpHE7MoxeI=",
|
||||
"h1:uxfYlF3C4XEdD3a2hEN7aoaEYeTg+bQuvjBePp6H0nU=",
|
||||
"h1:wo5cTkl/1nlxMfdn1yEDIHNoRLMczuK6COH2Id4/zeY=",
|
||||
"zh:0abf976c01f0c0732d0ccc6481e52008be5ee9c8e3d9b5eba0573c640fcf7019",
|
||||
"zh:2aff4d7ee7ba9eb3de2cd5cda16ba92b4ec7a2b43232aec180984241a323b216",
|
||||
"zh:2cc186fd0bfc44e100a22b0b40ae8ddcd0ec210a53c1da65d310ee758b1d2b08",
|
||||
"zh:3f8fb8594736b34af4b26437dd4df4dd4042ad4905223995cfebb8a1f10682ec",
|
||||
"zh:47fb41b18b74073f557dbcd6aad2183e416293405ccd70c0691a279cfe97f8cd",
|
||||
"zh:517e2f2764d671c22d22def0384fdfc521b456458189189c0363375495d114dc",
|
||||
"zh:5a49a2003636f2b8a547d494a6c06d43d62a68299775305408c52eff22b1c11f",
|
||||
"zh:66d4e716920ada84b0c768f4aca4c8948388995462923349a01bd3818d82b618",
|
||||
"zh:7599f652e89a3f18fa4b76a59d115cc63255cc36ce6b273850509ba25031abca",
|
||||
"zh:9c3e38ae7e670de973b6255d7050f526cd2b3ca7c383d7ba7226fc204d97c507",
|
||||
"zh:d04b046023fa9fd69def678f27e001c298ea34fc99ba51f835cda82e496fdb57",
|
||||
"zh:d9acd8810f6660cd51bb4c25596632984ae18e93340c82a102d074c6eac95151",
|
||||
"zh:e161bcb9a22607270b980eeff2ba693335fb62d6978516dff93dd4c91cda99b3",
|
||||
"zh:ef47502f08cfcb5311b7b16a7905e0052bf28359e07cc00ed080ef454e0946cf",
|
||||
"zh:f0640ddb52e7e90c5006ff571f6ad0554e593665320c764c57a3d8b7ec31b490",
|
||||
]
|
||||
}
|
||||
|
||||
provider "registry.opentofu.org/oracle/oci" {
|
||||
version = "8.22.0"
|
||||
constraints = "~> 8.0"
|
||||
hashes = [
|
||||
"h1:48qwYUm+i4MFFo3lknZmAdSJXPnQ8Wg6/hLYfJrXuUQ=",
|
||||
"h1:9Tav4Wy861FQM49vHO3Lkmh0ImP72YqTRcGcosNmSXQ=",
|
||||
"h1:A++gxs9Yn0506+iUS1qh0xcbWJUyt4uR/CG4zUdAwWs=",
|
||||
"h1:HOyTJwl/PBRb0QyTyT7p2skGyYmbCYD1CVlK6tj/zO4=",
|
||||
"h1:KLmzR/KJ10/bPQC02TsfbYFbaL4+vC501CxN9veKlbM=",
|
||||
"h1:LOQYlMa1v3v25JpmIiooq9efRehM+ZOo3VnPSGyJvFg=",
|
||||
"h1:NwBc8IRcp/Wxez14ogPKW7irep3kQpJWi6w8HylHkzk=",
|
||||
"h1:OfHl9/vduUH+dlKQUwByUa3CXyvim5vbUL3Gk30Oxwk=",
|
||||
"h1:UKLe8ehf7l9y2/eOnSIOduh4zVYraAfOBxjFwyRLt6Q=",
|
||||
"h1:j77pZCPt8+B6o7PIxpTyhnzSakxxXn2UKQSzJBWyyEY=",
|
||||
"h1:nUxhV7CCHe2mei34lvvHDHIU8oRjQL2QxjIL+UG0Nho=",
|
||||
"h1:q3WvQ8JpHuTAxV85HrQ8Ok7tXypGlAKAMm2hsc6lrmM=",
|
||||
"h1:xLLhJqN7B91cVYZMEmiY0dAPcEVnUFBiw5k8fu+tWsw=",
|
||||
"h1:yhL8OIMgCrjqjAhK6jasK6+tLNNhL0KLSEEzaUptJNQ=",
|
||||
"zh:0006fcdb52d37285b73c83e91cb5e298b05a6935a15ea865e83a5529ecaef868",
|
||||
"zh:249e76d8a172e11e5e06f77663541537c4c216b847e8d624bb5a9f32529e0aec",
|
||||
"zh:3cf68a19afd7fa3ffa5506025a262d94772177ed3d3f1004eeaeaec4ab197c22",
|
||||
"zh:3f488e5a32374e3a5d9953023c27e4b21d113800c9a50247092f0220316453db",
|
||||
"zh:6dca79b3f8fd2c2b6841ba63cb7ac3e673cd334ffc8937c651588edfc87dcc90",
|
||||
"zh:7263f3973d49cf80d88b8ec60c42cf81cd2a060281a6f8ac8500b64ae41b20a3",
|
||||
"zh:7ca0fd995a102ce1852000dbb9601609e559adf8e34606237e6c3cf5d94caa7e",
|
||||
"zh:7e11709bcb11076141b8df41084573159de8b663ffb8345ccbd380768a1b2d82",
|
||||
"zh:90782c215c3671cfc9d6ee853af8cd16c0fdd8c817b2672bc001b2d57a2bcb65",
|
||||
"zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
|
||||
"zh:a083575c5047062f162b9fffe078852ca0f44bf0293275038fe22d29f67a655a",
|
||||
"zh:b2709a6a63e7510e869a0ff2c6f688b4a3d9e24f7a92588b8bad19d74b4c8892",
|
||||
"zh:b403b916a94a2d782b44286f958e90c6efd6e97def7772059d1a0ef028eb0480",
|
||||
"zh:b787c10f06889433c9f559a0961b7548f49fd826129c279f3301aa1df5f95074",
|
||||
"zh:f234bd1b208ebe25cdfe4edb9b3665d3055f7a8df0ea00d6f10a9ea1ceb99455",
|
||||
]
|
||||
}
|
||||
204
terraform/external/oci-relay/admission-storage.tf
vendored
Normal file
204
terraform/external/oci-relay/admission-storage.tf
vendored
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
data "oci_objectstorage_namespace" "relay" {
|
||||
compartment_id = local.tenancy_ocid
|
||||
}
|
||||
|
||||
resource "oci_objectstorage_bucket" "address_lists" {
|
||||
compartment_id = local.compartment_id
|
||||
namespace = data.oci_objectstorage_namespace.relay.namespace
|
||||
name = "oci-l4-relay-address-lists"
|
||||
access_type = "NoPublicAccess"
|
||||
storage_tier = "Standard"
|
||||
versioning = "Enabled"
|
||||
freeform_tags = var.freeform_tags
|
||||
}
|
||||
|
||||
resource "oci_objectstorage_object_lifecycle_policy" "address_lists" {
|
||||
namespace = data.oci_objectstorage_namespace.relay.namespace
|
||||
bucket = oci_objectstorage_bucket.address_lists.name
|
||||
|
||||
depends_on = [oci_identity_policy.address_list_lifecycle_service]
|
||||
|
||||
rules {
|
||||
action = "DELETE"
|
||||
is_enabled = true
|
||||
name = "delete-web-allow-versions-after-30-days"
|
||||
target = "objects"
|
||||
time_amount = 30
|
||||
time_unit = "DAYS"
|
||||
|
||||
object_name_filter {
|
||||
inclusion_patterns = ["admission/v1/versions/*", "web-allow/v1/versions/*"]
|
||||
}
|
||||
}
|
||||
|
||||
rules {
|
||||
action = "DELETE"
|
||||
is_enabled = true
|
||||
name = "delete-smtp-deny-versions-after-30-days"
|
||||
target = "objects"
|
||||
time_amount = 30
|
||||
time_unit = "DAYS"
|
||||
|
||||
object_name_filter {
|
||||
inclusion_patterns = ["smtp-deny/v1/versions/*"]
|
||||
}
|
||||
}
|
||||
|
||||
rules {
|
||||
action = "DELETE"
|
||||
is_enabled = true
|
||||
name = "delete-previous-smtp-deny-current-versions-after-30-days"
|
||||
target = "previous-object-versions"
|
||||
time_amount = 30
|
||||
time_unit = "DAYS"
|
||||
|
||||
object_name_filter {
|
||||
inclusion_patterns = ["smtp-deny/v1/current.json.gz"]
|
||||
}
|
||||
}
|
||||
|
||||
rules {
|
||||
action = "DELETE"
|
||||
is_enabled = true
|
||||
name = "delete-previous-web-allow-current-versions-after-30-days"
|
||||
target = "previous-object-versions"
|
||||
time_amount = 30
|
||||
time_unit = "DAYS"
|
||||
|
||||
object_name_filter {
|
||||
inclusion_patterns = ["admission/v1/current.json.gz", "web-allow/v1/current.json.gz"]
|
||||
}
|
||||
}
|
||||
|
||||
rules {
|
||||
action = "ABORT"
|
||||
is_enabled = true
|
||||
name = "abort-incomplete-admission-uploads"
|
||||
target = "multipart-uploads"
|
||||
time_amount = 1
|
||||
time_unit = "DAYS"
|
||||
}
|
||||
}
|
||||
|
||||
resource "oci_identity_policy" "address_list_lifecycle_service" {
|
||||
compartment_id = local.tenancy_ocid
|
||||
name = "oci-l4-relay-address-list-lifecycle"
|
||||
description = "Allow the regional Object Storage service to enforce address-list retention"
|
||||
statements = [
|
||||
"Allow service objectstorage-${local.region} to manage object-family in compartment id ${local.compartment_id} where all {target.bucket.name = '${oci_objectstorage_bucket.address_lists.name}', any {request.permission = 'BUCKET_INSPECT', request.permission = 'BUCKET_READ', request.permission = 'OBJECT_INSPECT', request.permission = 'OBJECT_DELETE', request.permission = 'OBJECT_VERSION_DELETE'}}"
|
||||
]
|
||||
}
|
||||
|
||||
resource "oci_identity_dynamic_group" "address_list_readers" {
|
||||
compartment_id = local.tenancy_ocid
|
||||
name = "oci-l4-relay-address-list-readers"
|
||||
description = "OCI L4 relay instances authorized to read validated address lists"
|
||||
matching_rule = "ALL {instance.compartment.id = '${local.compartment_id}', tag.${oci_identity_tag_namespace.relay.name}.${oci_identity_tag.address_list_reader.name}.value = 'enabled'}"
|
||||
}
|
||||
|
||||
resource "oci_identity_policy" "address_list_readers" {
|
||||
compartment_id = local.tenancy_ocid
|
||||
name = "oci-l4-relay-address-list-readers"
|
||||
description = "Allow relay instances to read validated address-list objects"
|
||||
statements = [
|
||||
"Allow dynamic-group ${oci_identity_dynamic_group.address_list_readers.name} to read objects in compartment id ${local.compartment_id} where target.bucket.name = '${oci_objectstorage_bucket.address_lists.name}'"
|
||||
]
|
||||
}
|
||||
|
||||
resource "oci_identity_user" "admission_publisher" {
|
||||
count = local.publisher_enabled ? 1 : 0
|
||||
compartment_id = local.tenancy_ocid
|
||||
name = "oci-l4-relay-admission-publisher"
|
||||
description = "In-cluster Tier R web allow publisher"
|
||||
email = local.publisher_email
|
||||
}
|
||||
|
||||
resource "oci_identity_group" "admission_publishers" {
|
||||
count = local.publisher_enabled ? 1 : 0
|
||||
compartment_id = local.tenancy_ocid
|
||||
name = "oci-l4-relay-admission-publishers"
|
||||
description = "Principals allowed to publish Tier R web allow bundles"
|
||||
}
|
||||
|
||||
resource "oci_identity_user_group_membership" "admission_publisher" {
|
||||
count = local.publisher_enabled ? 1 : 0
|
||||
group_id = oci_identity_group.admission_publishers[0].id
|
||||
user_id = oci_identity_user.admission_publisher[0].id
|
||||
}
|
||||
|
||||
resource "oci_identity_api_key" "admission_publisher" {
|
||||
count = local.publisher_enabled ? 1 : 0
|
||||
user_id = oci_identity_user.admission_publisher[0].id
|
||||
key_value = local.publisher_api_public_key
|
||||
}
|
||||
|
||||
resource "oci_identity_user_capabilities_management" "admission_publisher" {
|
||||
count = local.publisher_enabled ? 1 : 0
|
||||
user_id = oci_identity_user.admission_publisher[0].id
|
||||
can_use_api_keys = true
|
||||
can_use_auth_tokens = false
|
||||
can_use_console_password = false
|
||||
can_use_customer_secret_keys = false
|
||||
can_use_smtp_credentials = false
|
||||
}
|
||||
|
||||
resource "oci_identity_policy" "admission_publisher" {
|
||||
count = local.publisher_enabled ? 1 : 0
|
||||
compartment_id = local.tenancy_ocid
|
||||
name = "oci-l4-relay-admission-publisher"
|
||||
description = "Allow dual publication during the web allow prefix migration"
|
||||
statements = [
|
||||
"Allow group ${oci_identity_group.admission_publishers[0].name} to manage objects in compartment id ${local.compartment_id} where all {target.bucket.name = '${oci_objectstorage_bucket.address_lists.name}', target.object.name = 'admission/v1/versions/*', any {request.permission = 'OBJECT_CREATE', request.permission = 'OBJECT_INSPECT', request.permission = 'OBJECT_READ'}}",
|
||||
"Allow group ${oci_identity_group.admission_publishers[0].name} to manage objects in compartment id ${local.compartment_id} where all {target.bucket.name = '${oci_objectstorage_bucket.address_lists.name}', target.object.name = 'admission/v1/current.json.gz', any {request.permission = 'OBJECT_CREATE', request.permission = 'OBJECT_OVERWRITE', request.permission = 'OBJECT_INSPECT', request.permission = 'OBJECT_READ'}}",
|
||||
"Allow group ${oci_identity_group.admission_publishers[0].name} to manage objects in compartment id ${local.compartment_id} where all {target.bucket.name = '${oci_objectstorage_bucket.address_lists.name}', target.object.name = 'web-allow/v1/versions/*', any {request.permission = 'OBJECT_CREATE', request.permission = 'OBJECT_INSPECT', request.permission = 'OBJECT_READ'}}",
|
||||
"Allow group ${oci_identity_group.admission_publishers[0].name} to manage objects in compartment id ${local.compartment_id} where all {target.bucket.name = '${oci_objectstorage_bucket.address_lists.name}', target.object.name = 'web-allow/v1/current.json.gz', any {request.permission = 'OBJECT_CREATE', request.permission = 'OBJECT_OVERWRITE', request.permission = 'OBJECT_INSPECT', request.permission = 'OBJECT_READ'}}"
|
||||
]
|
||||
}
|
||||
|
||||
resource "oci_identity_user" "smtp_deny_publisher" {
|
||||
count = local.smtp_deny_publisher_enabled ? 1 : 0
|
||||
compartment_id = local.tenancy_ocid
|
||||
name = "oci-l4-relay-smtp-deny-publisher"
|
||||
description = "In-cluster transport-evidence SMTP deny publisher"
|
||||
email = local.smtp_deny_publisher_email
|
||||
}
|
||||
|
||||
resource "oci_identity_group" "smtp_deny_publishers" {
|
||||
count = local.smtp_deny_publisher_enabled ? 1 : 0
|
||||
compartment_id = local.tenancy_ocid
|
||||
name = "oci-l4-relay-smtp-deny-publishers"
|
||||
description = "Principals allowed to publish SMTP deny bundles"
|
||||
}
|
||||
|
||||
resource "oci_identity_user_group_membership" "smtp_deny_publisher" {
|
||||
count = local.smtp_deny_publisher_enabled ? 1 : 0
|
||||
group_id = oci_identity_group.smtp_deny_publishers[0].id
|
||||
user_id = oci_identity_user.smtp_deny_publisher[0].id
|
||||
}
|
||||
|
||||
resource "oci_identity_api_key" "smtp_deny_publisher" {
|
||||
count = local.smtp_deny_publisher_enabled ? 1 : 0
|
||||
user_id = oci_identity_user.smtp_deny_publisher[0].id
|
||||
key_value = local.smtp_deny_publisher_api_public_key
|
||||
}
|
||||
|
||||
resource "oci_identity_user_capabilities_management" "smtp_deny_publisher" {
|
||||
count = local.smtp_deny_publisher_enabled ? 1 : 0
|
||||
user_id = oci_identity_user.smtp_deny_publisher[0].id
|
||||
can_use_api_keys = true
|
||||
can_use_auth_tokens = false
|
||||
can_use_console_password = false
|
||||
can_use_customer_secret_keys = false
|
||||
can_use_smtp_credentials = false
|
||||
}
|
||||
|
||||
resource "oci_identity_policy" "smtp_deny_publisher" {
|
||||
count = local.smtp_deny_publisher_enabled ? 1 : 0
|
||||
compartment_id = local.tenancy_ocid
|
||||
name = "oci-l4-relay-smtp-deny-publisher"
|
||||
description = "Allow immutable SMTP deny versions and current-bundle promotion"
|
||||
statements = [
|
||||
"Allow group ${oci_identity_group.smtp_deny_publishers[0].name} to manage objects in compartment id ${local.compartment_id} where all {target.bucket.name = '${oci_objectstorage_bucket.address_lists.name}', target.object.name = 'smtp-deny/v1/versions/*', any {request.permission = 'OBJECT_CREATE', request.permission = 'OBJECT_INSPECT', request.permission = 'OBJECT_READ'}}",
|
||||
"Allow group ${oci_identity_group.smtp_deny_publishers[0].name} to manage objects in compartment id ${local.compartment_id} where all {target.bucket.name = '${oci_objectstorage_bucket.address_lists.name}', target.object.name = '${var.smtp_deny_bundle_object}', any {request.permission = 'OBJECT_CREATE', request.permission = 'OBJECT_OVERWRITE', request.permission = 'OBJECT_INSPECT', request.permission = 'OBJECT_READ'}}"
|
||||
]
|
||||
}
|
||||
45
terraform/external/oci-relay/budget.tf
vendored
Normal file
45
terraform/external/oci-relay/budget.tf
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
locals {
|
||||
budget_alerts = {
|
||||
actual-50 = {
|
||||
threshold = 50
|
||||
type = "ACTUAL"
|
||||
}
|
||||
actual-80 = {
|
||||
threshold = 80
|
||||
type = "ACTUAL"
|
||||
}
|
||||
actual-100 = {
|
||||
threshold = 100
|
||||
type = "ACTUAL"
|
||||
}
|
||||
forecast-100 = {
|
||||
threshold = 100
|
||||
type = "FORECAST"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "oci_budget_budget" "relay" {
|
||||
amount = var.budget_amount
|
||||
compartment_id = local.tenancy_ocid
|
||||
description = "Monthly budget for the OCI L4 relay compartment"
|
||||
display_name = "oci-l4-relay"
|
||||
reset_period = "MONTHLY"
|
||||
target_type = "COMPARTMENT"
|
||||
targets = [local.compartment_id]
|
||||
freeform_tags = var.freeform_tags
|
||||
}
|
||||
|
||||
resource "oci_budget_alert_rule" "relay" {
|
||||
for_each = local.budget_alerts
|
||||
|
||||
budget_id = oci_budget_budget.relay.id
|
||||
description = "${each.value.type} relay spend reaches ${each.value.threshold}% of budget"
|
||||
display_name = each.key
|
||||
message = "Review OCI L4 relay spend before increasing or retaining paid capacity."
|
||||
recipients = local.budget_alert_recipients
|
||||
threshold = each.value.threshold
|
||||
threshold_type = "PERCENTAGE"
|
||||
type = each.value.type
|
||||
freeform_tags = var.freeform_tags
|
||||
}
|
||||
18
terraform/external/oci-relay/cloud-init.yaml.tftpl
vendored
Normal file
18
terraform/external/oci-relay/cloud-init.yaml.tftpl
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#cloud-config
|
||||
write_files:
|
||||
- path: /etc/hosts
|
||||
append: true
|
||||
content: |
|
||||
${gateway_relay_address} ${log_ingest_hostname}
|
||||
- path: /etc/oci-l4-relay/config.json
|
||||
owner: root:root
|
||||
permissions: "0600"
|
||||
content: |
|
||||
${indent(6, config_json)}
|
||||
- path: /etc/oci-l4-relay-smtp-evidence.json
|
||||
owner: root:root
|
||||
permissions: "0444"
|
||||
content: |
|
||||
${indent(6, smtp_evidence_config_json)}
|
||||
runcmd:
|
||||
- [systemctl, start, oci-l4-relay-bootstrap.service]
|
||||
266
terraform/external/oci-relay/compute.tf
vendored
Normal file
266
terraform/external/oci-relay/compute.tf
vendored
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
resource "oci_core_instance" "relay" {
|
||||
availability_domain = data.oci_identity_availability_domains.available.availability_domains[var.availability_domain_index].name
|
||||
compartment_id = local.compartment_id
|
||||
display_name = "oci-l4-relay"
|
||||
shape = var.shape
|
||||
freeform_tags = var.freeform_tags
|
||||
defined_tags = {
|
||||
"${oci_identity_tag_namespace.relay.name}.${oci_identity_tag.address_list_reader.name}" = "enabled"
|
||||
"${oci_identity_tag_namespace.relay.name}.${oci_identity_tag.instance_principal.name}" = "enabled"
|
||||
}
|
||||
|
||||
agent_config {
|
||||
are_all_plugins_disabled = false
|
||||
is_management_disabled = false
|
||||
is_monitoring_disabled = false
|
||||
|
||||
plugins_config {
|
||||
desired_state = "ENABLED"
|
||||
name = "Compute Instance Run Command"
|
||||
}
|
||||
}
|
||||
|
||||
shape_config {
|
||||
ocpus = var.ocpus
|
||||
memory_in_gbs = var.memory_in_gbs
|
||||
}
|
||||
|
||||
create_vnic_details {
|
||||
assign_public_ip = false
|
||||
display_name = "oci-l4-relay"
|
||||
nsg_ids = [oci_core_network_security_group.relay.id]
|
||||
subnet_id = oci_core_subnet.relay.id
|
||||
}
|
||||
|
||||
source_details {
|
||||
boot_volume_size_in_gbs = 50
|
||||
source_id = local.relay_image_ocid
|
||||
source_type = "image"
|
||||
}
|
||||
|
||||
metadata = {
|
||||
user_data = base64encode(templatefile("${path.module}/cloud-init.yaml.tftpl", {
|
||||
gateway_relay_address = var.gateway_relay_address
|
||||
log_ingest_hostname = var.log_ingest_hostname
|
||||
smtp_evidence_config_json = jsonencode({
|
||||
conntrack_emission_burst = var.relay_conntrack_emission_burst
|
||||
conntrack_emission_rate_per_second = var.relay_conntrack_emission_rate_per_second
|
||||
conntrack_event_buffer = var.relay_conntrack_event_buffer
|
||||
conntrack_sample_denominator = var.relay_conntrack_sample_denominator
|
||||
conntrack_services = concat([
|
||||
{
|
||||
name = "web"
|
||||
ports = [80, 443]
|
||||
target_address = var.gateway_relay_address
|
||||
}
|
||||
], var.mail_relay_enabled ? [
|
||||
{
|
||||
name = "smtp"
|
||||
ports = [25]
|
||||
target_address = local.home_tunnel_address
|
||||
},
|
||||
{
|
||||
name = "authenticated_mail"
|
||||
ports = [465, 993]
|
||||
target_address = local.home_tunnel_address
|
||||
}
|
||||
] : [])
|
||||
maximum_records = var.smtp_deny_maximum_addresses
|
||||
metrics_listen_address = "${split("/", var.relay_tunnel_address)[0]}:${var.relay_metrics_port}"
|
||||
observation_window_seconds = var.smtp_deny_maximum_observation_window_seconds
|
||||
snapshot_interval_seconds = var.smtp_evidence_snapshot_interval_seconds
|
||||
})
|
||||
config_json = jsonencode(merge({
|
||||
admission = {
|
||||
bucket = oci_objectstorage_bucket.address_lists.name
|
||||
maximum_addresses = var.web_allow_maximum_addresses
|
||||
minimum_addresses = var.web_allow_minimum_addresses
|
||||
minimum_intervals = var.web_allow_minimum_intervals
|
||||
namespace = data.oci_objectstorage_namespace.relay.namespace
|
||||
object = var.web_allow_bundle_object
|
||||
}
|
||||
admission_helper = "/usr/local/libexec/oci-l4-relay-admission"
|
||||
gateway_relay_address = var.gateway_relay_address
|
||||
home_wireguard_public_key = local.home_wireguard_public_key
|
||||
log_ingest_hostname = var.log_ingest_hostname
|
||||
log_ingest_secret_ocid = oci_vault_secret.log_ingest.id
|
||||
monitoring_source_ipv4 = var.monitoring_source_ipv4
|
||||
public_key_secret_ocid = oci_vault_secret.wireguard_public_key.id
|
||||
rate_limit_mbit = var.relay_rate_limit_mbit
|
||||
relay_http_quarantine_enabled = var.relay_http_quarantine_enabled
|
||||
relay_http_quarantine_minutes = var.relay_http_quarantine_minutes
|
||||
relay_https_recent_minutes = var.relay_https_recent_minutes
|
||||
relay_metrics_port = var.relay_metrics_port
|
||||
relay_auth_packet_aggregate_burst = var.relay_auth_packet_aggregate_burst
|
||||
relay_auth_packet_aggregate_rate_per_second = var.relay_auth_packet_aggregate_rate_per_second
|
||||
relay_auth_syn_aggregate_burst = var.relay_auth_syn_aggregate_burst
|
||||
relay_auth_syn_aggregate_rate_per_second = var.relay_auth_syn_aggregate_rate_per_second
|
||||
relay_auth_syn_burst = var.relay_auth_syn_burst
|
||||
relay_auth_syn_rate_per_minute = var.relay_auth_syn_rate_per_minute
|
||||
relay_smtp_packet_aggregate_burst = var.relay_smtp_packet_aggregate_burst
|
||||
relay_smtp_packet_aggregate_rate_per_second = var.relay_smtp_packet_aggregate_rate_per_second
|
||||
relay_smtp_syn_aggregate_burst = var.relay_smtp_syn_aggregate_burst
|
||||
relay_smtp_syn_aggregate_rate_per_second = var.relay_smtp_syn_aggregate_rate_per_second
|
||||
relay_smtp_syn_burst = var.relay_smtp_syn_burst
|
||||
relay_smtp_syn_rate_per_minute = var.relay_smtp_syn_rate_per_minute
|
||||
relay_tracker_max_sources = var.relay_tracker_max_sources
|
||||
relay_tunnel_address = var.relay_tunnel_address
|
||||
relay_tunnel_mtu = var.relay_tunnel_mtu
|
||||
relay_unmatched_http_burst = var.relay_unmatched_http_burst
|
||||
relay_unmatched_http_rate_per_hour = var.relay_unmatched_http_rate_per_hour
|
||||
relay_web_packet_aggregate_burst = var.relay_web_packet_aggregate_burst
|
||||
relay_web_packet_aggregate_rate_per_second = var.relay_web_packet_aggregate_rate_per_second
|
||||
relay_web_syn_aggregate_burst = var.relay_web_syn_aggregate_burst
|
||||
relay_web_syn_aggregate_rate_per_second = var.relay_web_syn_aggregate_rate_per_second
|
||||
relay_web_syn_burst = var.relay_web_syn_burst
|
||||
relay_web_syn_rate_per_minute = var.relay_web_syn_rate_per_minute
|
||||
wireguard_listen_port = var.wireguard_listen_port
|
||||
wireguard_seed_secret_ocid = oci_vault_secret.wireguard_seed.id
|
||||
}, jsondecode(var.mail_relay_enabled ? jsonencode({
|
||||
mail_relay_address = local.home_tunnel_address
|
||||
smtp_deny = {
|
||||
bucket = oci_objectstorage_bucket.address_lists.name
|
||||
maximum_addresses = var.smtp_deny_maximum_addresses
|
||||
maximum_intervals = var.smtp_deny_maximum_intervals
|
||||
maximum_lifetime_seconds = var.smtp_deny_maximum_lifetime_seconds
|
||||
maximum_observation_window_seconds = var.smtp_deny_maximum_observation_window_seconds
|
||||
minimum_observations = var.smtp_deny_minimum_observations
|
||||
namespace = data.oci_objectstorage_namespace.relay.namespace
|
||||
object = var.smtp_deny_bundle_object
|
||||
}
|
||||
smtp_deny_helper = "/usr/local/libexec/oci-l4-relay-smtp-deny"
|
||||
}) : "{}")))
|
||||
}))
|
||||
}
|
||||
|
||||
preserve_boot_volume = false
|
||||
|
||||
lifecycle {
|
||||
replace_triggered_by = [terraform_data.relay_instance_generation]
|
||||
ignore_changes = [defined_tags["Oracle-Tags.CreatedBy"], defined_tags["Oracle-Tags.CreatedOn"]]
|
||||
}
|
||||
|
||||
depends_on = [oci_identity_policy.relay]
|
||||
}
|
||||
|
||||
data "oci_core_vnic_attachments" "relay" {
|
||||
compartment_id = local.compartment_id
|
||||
instance_id = oci_core_instance.relay.id
|
||||
}
|
||||
|
||||
data "oci_core_private_ips" "relay" {
|
||||
vnic_id = data.oci_core_vnic_attachments.relay.vnic_attachments[0].vnic_id
|
||||
}
|
||||
|
||||
resource "oci_core_public_ip" "relay" {
|
||||
compartment_id = local.compartment_id
|
||||
display_name = "oci-l4-relay"
|
||||
lifetime = "RESERVED"
|
||||
private_ip_id = one([for address in data.oci_core_private_ips.relay.private_ips : address.id if address.is_primary])
|
||||
freeform_tags = var.freeform_tags
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "oci_identity_dynamic_group" "relay" {
|
||||
compartment_id = local.tenancy_ocid
|
||||
name = "oci-l4-relay"
|
||||
description = "OCI L4 relay instance principal"
|
||||
matching_rule = "ALL {instance.compartment.id = '${local.compartment_id}', tag.${oci_identity_tag_namespace.relay.name}.${oci_identity_tag.instance_principal.name}.value = 'enabled'}"
|
||||
}
|
||||
|
||||
resource "oci_identity_policy" "relay" {
|
||||
compartment_id = local.tenancy_ocid
|
||||
name = "oci-l4-relay"
|
||||
description = "Allow relay key derivation and public-key publication"
|
||||
statements = [
|
||||
"Allow dynamic-group ${oci_identity_dynamic_group.relay.name} to read secret-bundles in compartment id ${local.compartment_id} where target.secret.id = '${oci_vault_secret.wireguard_seed.id}'",
|
||||
"Allow dynamic-group ${oci_identity_dynamic_group.relay.name} to read secret-bundles in compartment id ${local.compartment_id} where target.secret.id = '${oci_vault_secret.wireguard_public_key.id}'",
|
||||
"Allow dynamic-group ${oci_identity_dynamic_group.relay.name} to read secret-bundles in compartment id ${local.compartment_id} where target.secret.id = '${oci_vault_secret.log_ingest.id}'",
|
||||
"Allow dynamic-group ${oci_identity_dynamic_group.relay.name} to {SECRET_UPDATE} in compartment id ${local.compartment_id} where target.secret.id = '${oci_vault_secret.wireguard_public_key.id}'",
|
||||
"Allow dynamic-group ${oci_identity_dynamic_group.relay.name} to use instance-agent-command-execution-family in compartment id ${local.compartment_id} where request.instance.id = target.instance.id",
|
||||
]
|
||||
}
|
||||
|
||||
resource "terraform_data" "relay_instance_generation" {
|
||||
triggers_replace = [
|
||||
"relay",
|
||||
filesha256("${path.module}/cloud-init.yaml.tftpl"),
|
||||
jsonencode(merge({
|
||||
web_allow_bundle_object = var.web_allow_bundle_object
|
||||
web_allow_maximum_addresses = var.web_allow_maximum_addresses
|
||||
web_allow_minimum_addresses = var.web_allow_minimum_addresses
|
||||
web_allow_minimum_intervals = var.web_allow_minimum_intervals
|
||||
gateway_relay_address = var.gateway_relay_address
|
||||
log_ingest_hostname = var.log_ingest_hostname
|
||||
relay_image_ocid = local.relay_image_ocid
|
||||
monitoring_source_ipv4 = var.monitoring_source_ipv4
|
||||
relay_http_quarantine_enabled = var.relay_http_quarantine_enabled
|
||||
relay_http_quarantine_minutes = var.relay_http_quarantine_minutes
|
||||
relay_https_recent_minutes = var.relay_https_recent_minutes
|
||||
relay_auth_packet_aggregate_burst = var.relay_auth_packet_aggregate_burst
|
||||
relay_auth_packet_aggregate_rate_per_second = var.relay_auth_packet_aggregate_rate_per_second
|
||||
relay_auth_syn_aggregate_burst = var.relay_auth_syn_aggregate_burst
|
||||
relay_auth_syn_aggregate_rate_per_second = var.relay_auth_syn_aggregate_rate_per_second
|
||||
relay_auth_syn_burst = var.relay_auth_syn_burst
|
||||
relay_auth_syn_rate_per_minute = var.relay_auth_syn_rate_per_minute
|
||||
relay_rate_limit_mbit = var.relay_rate_limit_mbit
|
||||
relay_smtp_packet_aggregate_burst = var.relay_smtp_packet_aggregate_burst
|
||||
relay_smtp_packet_aggregate_rate_per_second = var.relay_smtp_packet_aggregate_rate_per_second
|
||||
relay_smtp_syn_aggregate_burst = var.relay_smtp_syn_aggregate_burst
|
||||
relay_smtp_syn_aggregate_rate_per_second = var.relay_smtp_syn_aggregate_rate_per_second
|
||||
relay_smtp_syn_burst = var.relay_smtp_syn_burst
|
||||
relay_smtp_syn_rate_per_minute = var.relay_smtp_syn_rate_per_minute
|
||||
relay_web_packet_aggregate_burst = var.relay_web_packet_aggregate_burst
|
||||
relay_web_packet_aggregate_rate_per_second = var.relay_web_packet_aggregate_rate_per_second
|
||||
relay_web_syn_aggregate_burst = var.relay_web_syn_aggregate_burst
|
||||
relay_web_syn_aggregate_rate_per_second = var.relay_web_syn_aggregate_rate_per_second
|
||||
relay_tracker_max_sources = var.relay_tracker_max_sources
|
||||
relay_tunnel_mtu = var.relay_tunnel_mtu
|
||||
relay_tunnel_address = var.relay_tunnel_address
|
||||
relay_metrics_port = var.relay_metrics_port
|
||||
relay_conntrack_emission_burst = var.relay_conntrack_emission_burst
|
||||
relay_conntrack_emission_rate_per_second = var.relay_conntrack_emission_rate_per_second
|
||||
relay_conntrack_event_buffer = var.relay_conntrack_event_buffer
|
||||
relay_conntrack_sample_denominator = var.relay_conntrack_sample_denominator
|
||||
relay_unmatched_http_burst = var.relay_unmatched_http_burst
|
||||
relay_unmatched_http_rate_per_hour = var.relay_unmatched_http_rate_per_hour
|
||||
relay_web_syn_burst = var.relay_web_syn_burst
|
||||
relay_web_syn_rate_per_minute = var.relay_web_syn_rate_per_minute
|
||||
wireguard_listen_port = var.wireguard_listen_port
|
||||
}, jsondecode(var.mail_relay_enabled ? jsonencode({
|
||||
mail_relay_address = local.home_tunnel_address
|
||||
smtp_deny_bundle_object = var.smtp_deny_bundle_object
|
||||
smtp_deny_maximum_addresses = var.smtp_deny_maximum_addresses
|
||||
smtp_deny_maximum_intervals = var.smtp_deny_maximum_intervals
|
||||
smtp_deny_maximum_lifetime_seconds = var.smtp_deny_maximum_lifetime_seconds
|
||||
smtp_deny_maximum_observation_window_seconds = var.smtp_deny_maximum_observation_window_seconds
|
||||
smtp_deny_minimum_observations = var.smtp_deny_minimum_observations
|
||||
smtp_evidence_snapshot_interval_seconds = var.smtp_evidence_snapshot_interval_seconds
|
||||
}) : "{}")))
|
||||
]
|
||||
}
|
||||
|
||||
resource "oci_identity_tag_namespace" "relay" {
|
||||
compartment_id = local.tenancy_ocid
|
||||
name = "quorate_relay"
|
||||
description = "Authorization boundary for OCI L4 relay resources"
|
||||
is_retired = false
|
||||
}
|
||||
|
||||
resource "oci_identity_tag" "instance_principal" {
|
||||
tag_namespace_id = oci_identity_tag_namespace.relay.id
|
||||
name = "instance_principal"
|
||||
description = "Admits an instance to the OCI L4 relay dynamic group"
|
||||
is_retired = false
|
||||
}
|
||||
|
||||
|
||||
resource "oci_identity_tag" "address_list_reader" {
|
||||
tag_namespace_id = oci_identity_tag_namespace.relay.id
|
||||
name = "address_list_reader"
|
||||
description = "Admits an instance to read validated relay address-list objects"
|
||||
is_retired = false
|
||||
}
|
||||
60
terraform/external/oci-relay/email-delivery.tf
vendored
Normal file
60
terraform/external/oci-relay/email-delivery.tf
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
data "oci_email_configuration" "relay" {
|
||||
compartment_id = local.tenancy_ocid
|
||||
}
|
||||
|
||||
resource "oci_email_email_domain" "quorate_dev" {
|
||||
compartment_id = local.compartment_id
|
||||
name = "quorate.dev"
|
||||
description = "Stalwart outbound mail domain"
|
||||
}
|
||||
|
||||
resource "oci_email_dkim" "quorate_dev" {
|
||||
email_domain_id = oci_email_email_domain.quorate_dev.id
|
||||
name = "oci-osaka"
|
||||
description = "OCI Email Delivery signing key"
|
||||
}
|
||||
|
||||
resource "oci_email_sender" "stalwart" {
|
||||
for_each = toset(var.email_approved_senders)
|
||||
|
||||
compartment_id = local.compartment_id
|
||||
email_address = each.value
|
||||
|
||||
depends_on = [oci_email_dkim.quorate_dev]
|
||||
}
|
||||
|
||||
resource "oci_identity_user" "stalwart_email_delivery" {
|
||||
compartment_id = local.tenancy_ocid
|
||||
name = "stalwart-email-delivery"
|
||||
description = "Stalwart outbound SMTP submission"
|
||||
email = "[email protected]"
|
||||
}
|
||||
|
||||
resource "oci_identity_user_capabilities_management" "stalwart_email_delivery" {
|
||||
user_id = oci_identity_user.stalwart_email_delivery.id
|
||||
can_use_api_keys = false
|
||||
can_use_auth_tokens = false
|
||||
can_use_console_password = false
|
||||
can_use_customer_secret_keys = false
|
||||
can_use_smtp_credentials = true
|
||||
}
|
||||
|
||||
resource "oci_identity_group" "stalwart_email_delivery" {
|
||||
compartment_id = local.tenancy_ocid
|
||||
name = "stalwart-email-delivery"
|
||||
description = "Principals authorized for Stalwart outbound Email Delivery"
|
||||
}
|
||||
|
||||
resource "oci_identity_user_group_membership" "stalwart_email_delivery" {
|
||||
group_id = oci_identity_group.stalwart_email_delivery.id
|
||||
user_id = oci_identity_user.stalwart_email_delivery.id
|
||||
}
|
||||
|
||||
resource "oci_identity_policy" "stalwart_email_delivery" {
|
||||
compartment_id = local.tenancy_ocid
|
||||
name = "stalwart-email-delivery"
|
||||
description = "Allow Stalwart to submit through OCI Email Delivery"
|
||||
statements = [
|
||||
"Allow group ${oci_identity_group.stalwart_email_delivery.name} to use email-family in compartment id ${local.compartment_id}",
|
||||
]
|
||||
}
|
||||
14
terraform/external/oci-relay/identity.tf
vendored
Normal file
14
terraform/external/oci-relay/identity.tf
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
resource "oci_identity_compartment" "relay" {
|
||||
compartment_id = local.tenancy_ocid
|
||||
name = "oci-l4-relay"
|
||||
description = "OCI L4 relay resources"
|
||||
enable_delete = true
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
locals {
|
||||
compartment_id = oci_identity_compartment.relay.id
|
||||
}
|
||||
157
terraform/external/oci-relay/monitoring.tf
vendored
Normal file
157
terraform/external/oci-relay/monitoring.tf
vendored
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
locals {
|
||||
health_check_vantage_points = ["aws-tyo", "azr-sin", "goo-cbf"]
|
||||
relay_alarm_pending_duration = "PT3M"
|
||||
}
|
||||
|
||||
resource "oci_health_checks_ping_monitor" "relay_http" {
|
||||
compartment_id = local.compartment_id
|
||||
display_name = "oci-l4-relay-http"
|
||||
interval_in_seconds = 60
|
||||
is_enabled = true
|
||||
port = 80
|
||||
protocol = "TCP"
|
||||
targets = [oci_core_public_ip.relay.ip_address]
|
||||
timeout_in_seconds = 10
|
||||
vantage_point_names = local.health_check_vantage_points
|
||||
freeform_tags = var.freeform_tags
|
||||
}
|
||||
|
||||
resource "oci_health_checks_http_monitor" "relay_https" {
|
||||
compartment_id = local.compartment_id
|
||||
display_name = "oci-l4-relay-https"
|
||||
headers = { Host = "auth.quorate.dev" }
|
||||
interval_in_seconds = 60
|
||||
is_enabled = true
|
||||
method = "GET"
|
||||
path = "/.well-known/openid-configuration"
|
||||
port = 443
|
||||
protocol = "HTTPS"
|
||||
targets = [oci_core_public_ip.relay.ip_address]
|
||||
timeout_in_seconds = 10
|
||||
vantage_point_names = local.health_check_vantage_points
|
||||
freeform_tags = var.freeform_tags
|
||||
}
|
||||
|
||||
resource "oci_health_checks_http_monitor" "relay_mail_https" {
|
||||
count = var.mail_https_health_check_enabled ? 1 : 0
|
||||
|
||||
compartment_id = local.compartment_id
|
||||
display_name = "oci-l4-relay-mail-https"
|
||||
headers = { Host = "mail.quorate.dev" }
|
||||
interval_in_seconds = 60
|
||||
is_enabled = true
|
||||
method = "GET"
|
||||
path = "/healthz/live"
|
||||
port = 443
|
||||
protocol = "HTTPS"
|
||||
targets = [oci_core_public_ip.relay.ip_address]
|
||||
timeout_in_seconds = 10
|
||||
vantage_point_names = local.health_check_vantage_points
|
||||
freeform_tags = var.freeform_tags
|
||||
}
|
||||
|
||||
resource "oci_health_checks_ping_monitor" "relay_mail" {
|
||||
for_each = toset([
|
||||
for port, enabled in local.mail_health_checks_by_port : port if enabled
|
||||
])
|
||||
|
||||
compartment_id = local.compartment_id
|
||||
display_name = "oci-l4-relay-mail-${each.value}"
|
||||
interval_in_seconds = 60
|
||||
is_enabled = true
|
||||
port = tonumber(each.value)
|
||||
protocol = "TCP"
|
||||
targets = [oci_core_public_ip.relay.ip_address]
|
||||
timeout_in_seconds = 10
|
||||
vantage_point_names = local.health_check_vantage_points
|
||||
freeform_tags = var.freeform_tags
|
||||
}
|
||||
|
||||
resource "oci_ons_notification_topic" "relay" {
|
||||
compartment_id = local.compartment_id
|
||||
name = "oci-l4-relay-alerts"
|
||||
description = "Relay availability and health alerts"
|
||||
freeform_tags = var.freeform_tags
|
||||
}
|
||||
|
||||
resource "oci_ons_subscription" "relay_email" {
|
||||
count = local.monitoring_alert_enabled ? 1 : 0
|
||||
|
||||
compartment_id = local.compartment_id
|
||||
endpoint = local.monitoring_alert_email
|
||||
protocol = "EMAIL"
|
||||
topic_id = oci_ons_notification_topic.relay.id
|
||||
freeform_tags = var.freeform_tags
|
||||
}
|
||||
|
||||
resource "oci_monitoring_alarm" "relay_reachability" {
|
||||
body = "At least two external vantage points cannot reach relay HTTP."
|
||||
compartment_id = local.compartment_id
|
||||
destinations = [oci_ons_notification_topic.relay.id]
|
||||
display_name = "oci-l4-relay-reachability"
|
||||
is_enabled = true
|
||||
message_format = "ONS_OPTIMIZED"
|
||||
metric_compartment_id = local.compartment_id
|
||||
namespace = "oci_healthchecks"
|
||||
pending_duration = local.relay_alarm_pending_duration
|
||||
query = "PING.isHealthy[1m]{resourceId = \"${oci_health_checks_ping_monitor.relay_http.id}\"}.grouping().mean() < 0.5"
|
||||
repeat_notification_duration = "PT2H"
|
||||
resolution = "1m"
|
||||
severity = "CRITICAL"
|
||||
freeform_tags = var.freeform_tags
|
||||
}
|
||||
|
||||
resource "oci_monitoring_alarm" "relay_https" {
|
||||
body = "At least two external vantage points report unhealthy relay HTTPS while relay HTTP remains reachable."
|
||||
compartment_id = local.compartment_id
|
||||
destinations = [oci_ons_notification_topic.relay.id]
|
||||
display_name = "oci-l4-relay-https"
|
||||
is_enabled = true
|
||||
message_format = "ONS_OPTIMIZED"
|
||||
metric_compartment_id = local.compartment_id
|
||||
namespace = "oci_healthchecks"
|
||||
pending_duration = local.relay_alarm_pending_duration
|
||||
query = "HTTP.isHealthy[1m]{resourceId = \"${oci_health_checks_http_monitor.relay_https.id}\"}.grouping().mean() < 0.5 && PING.isHealthy[1m]{resourceId = \"${oci_health_checks_ping_monitor.relay_http.id}\"}.grouping().mean() >= 0.5"
|
||||
repeat_notification_duration = "PT2H"
|
||||
resolution = "1m"
|
||||
severity = "CRITICAL"
|
||||
freeform_tags = var.freeform_tags
|
||||
}
|
||||
|
||||
resource "oci_monitoring_alarm" "relay_mail_https" {
|
||||
count = var.mail_https_health_check_enabled ? 1 : 0
|
||||
|
||||
body = "At least two external vantage points report unhealthy Stalwart user HTTPS while relay HTTP remains reachable."
|
||||
compartment_id = local.compartment_id
|
||||
destinations = [oci_ons_notification_topic.relay.id]
|
||||
display_name = "oci-l4-relay-mail-https"
|
||||
is_enabled = true
|
||||
message_format = "ONS_OPTIMIZED"
|
||||
metric_compartment_id = local.compartment_id
|
||||
namespace = "oci_healthchecks"
|
||||
pending_duration = local.relay_alarm_pending_duration
|
||||
query = "HTTP.isHealthy[1m]{resourceId = \"${oci_health_checks_http_monitor.relay_mail_https[0].id}\"}.grouping().mean() < 0.5 && PING.isHealthy[1m]{resourceId = \"${oci_health_checks_ping_monitor.relay_http.id}\"}.grouping().mean() >= 0.5"
|
||||
repeat_notification_duration = "PT2H"
|
||||
resolution = "1m"
|
||||
severity = "CRITICAL"
|
||||
freeform_tags = var.freeform_tags
|
||||
}
|
||||
|
||||
resource "oci_monitoring_alarm" "relay_mail" {
|
||||
for_each = oci_health_checks_ping_monitor.relay_mail
|
||||
|
||||
body = "At least two external vantage points cannot reach relay mail TCP/${each.key} while relay HTTP remains reachable."
|
||||
compartment_id = local.compartment_id
|
||||
destinations = [oci_ons_notification_topic.relay.id]
|
||||
display_name = "oci-l4-relay-mail-${each.key}"
|
||||
is_enabled = true
|
||||
message_format = "ONS_OPTIMIZED"
|
||||
metric_compartment_id = local.compartment_id
|
||||
namespace = "oci_healthchecks"
|
||||
pending_duration = local.relay_alarm_pending_duration
|
||||
query = "PING.isHealthy[1m]{resourceId = \"${each.value.id}\"}.grouping().mean() < 0.5 && PING.isHealthy[1m]{resourceId = \"${oci_health_checks_ping_monitor.relay_http.id}\"}.grouping().mean() >= 0.5"
|
||||
repeat_notification_duration = "PT2H"
|
||||
resolution = "1m"
|
||||
severity = "CRITICAL"
|
||||
freeform_tags = var.freeform_tags
|
||||
}
|
||||
59
terraform/external/oci-relay/network.tf
vendored
Normal file
59
terraform/external/oci-relay/network.tf
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
data "oci_identity_availability_domains" "available" {
|
||||
compartment_id = local.compartment_id
|
||||
}
|
||||
|
||||
resource "oci_core_vcn" "relay" {
|
||||
compartment_id = local.compartment_id
|
||||
cidr_blocks = [var.vcn_cidr]
|
||||
display_name = "oci-l4-relay"
|
||||
dns_label = "ocil4relay"
|
||||
freeform_tags = var.freeform_tags
|
||||
}
|
||||
|
||||
resource "oci_core_internet_gateway" "relay" {
|
||||
compartment_id = local.compartment_id
|
||||
vcn_id = oci_core_vcn.relay.id
|
||||
display_name = "oci-l4-relay"
|
||||
enabled = true
|
||||
freeform_tags = var.freeform_tags
|
||||
}
|
||||
|
||||
resource "oci_core_route_table" "relay" {
|
||||
compartment_id = local.compartment_id
|
||||
vcn_id = oci_core_vcn.relay.id
|
||||
display_name = "oci-l4-relay"
|
||||
freeform_tags = var.freeform_tags
|
||||
|
||||
route_rules {
|
||||
destination = "0.0.0.0/0"
|
||||
destination_type = "CIDR_BLOCK"
|
||||
network_entity_id = oci_core_internet_gateway.relay.id
|
||||
}
|
||||
}
|
||||
|
||||
resource "oci_core_security_list" "relay" {
|
||||
compartment_id = local.compartment_id
|
||||
vcn_id = oci_core_vcn.relay.id
|
||||
display_name = "oci-l4-relay-empty"
|
||||
freeform_tags = var.freeform_tags
|
||||
}
|
||||
|
||||
resource "oci_core_subnet" "relay" {
|
||||
compartment_id = local.compartment_id
|
||||
vcn_id = oci_core_vcn.relay.id
|
||||
cidr_block = var.subnet_cidr
|
||||
display_name = "oci-l4-relay"
|
||||
dns_label = "relay"
|
||||
prohibit_internet_ingress = false
|
||||
prohibit_public_ip_on_vnic = false
|
||||
route_table_id = oci_core_route_table.relay.id
|
||||
security_list_ids = [oci_core_security_list.relay.id]
|
||||
freeform_tags = var.freeform_tags
|
||||
}
|
||||
|
||||
resource "oci_core_network_security_group" "relay" {
|
||||
compartment_id = local.compartment_id
|
||||
vcn_id = oci_core_vcn.relay.id
|
||||
display_name = "oci-l4-relay"
|
||||
freeform_tags = var.freeform_tags
|
||||
}
|
||||
159
terraform/external/oci-relay/outputs.tf
vendored
Normal file
159
terraform/external/oci-relay/outputs.tf
vendored
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
output "instance_id" {
|
||||
description = "OCI relay instance OCID."
|
||||
value = oci_core_instance.relay.id
|
||||
}
|
||||
|
||||
output "compartment_id" {
|
||||
description = "OCI relay compartment OCID."
|
||||
value = local.compartment_id
|
||||
}
|
||||
|
||||
output "availability_domain" {
|
||||
description = "Availability domain used by relay and image-builder instances."
|
||||
value = data.oci_identity_availability_domains.available.availability_domains[var.availability_domain_index].name
|
||||
}
|
||||
|
||||
output "base_image_ocid" {
|
||||
description = "Pinned Oracle Linux platform image used to build relay images."
|
||||
value = var.image_ocid
|
||||
}
|
||||
|
||||
output "relay_network_security_group_id" {
|
||||
description = "NSG attached to the relay instance."
|
||||
value = oci_core_network_security_group.relay.id
|
||||
}
|
||||
|
||||
output "image_builder_network_security_group_id" {
|
||||
description = "Operator-restricted NSG attached only to temporary image builders."
|
||||
value = oci_core_network_security_group.image_builder.id
|
||||
}
|
||||
|
||||
output "relay_subnet_id" {
|
||||
description = "Subnet used by relay and temporary image-builder instances."
|
||||
value = oci_core_subnet.relay.id
|
||||
}
|
||||
|
||||
output "public_ip" {
|
||||
description = "Reserved public IPv4 address used by DNS, WireGuard, and OPNsense aliases."
|
||||
value = oci_core_public_ip.relay.ip_address
|
||||
}
|
||||
|
||||
output "wireguard_listen_port" {
|
||||
description = "Public UDP listen port used by WireGuard and OPNsense."
|
||||
value = var.wireguard_listen_port
|
||||
}
|
||||
|
||||
output "relay_tunnel_address" {
|
||||
description = "OCI WireGuard transfer address."
|
||||
value = var.relay_tunnel_address
|
||||
}
|
||||
|
||||
output "relay_metrics_port" {
|
||||
description = "TCP port for metrics on the OCI WireGuard interface."
|
||||
value = var.relay_metrics_port
|
||||
}
|
||||
|
||||
output "home_tunnel_address" {
|
||||
description = "Home WireGuard transfer address."
|
||||
value = local.home_tunnel_address
|
||||
}
|
||||
|
||||
output "gateway_relay_address" {
|
||||
description = "Gateway R address targeted by OCI DNAT."
|
||||
value = var.gateway_relay_address
|
||||
}
|
||||
|
||||
output "wireguard_seed_secret_id" {
|
||||
description = "OCI Vault secret OCID containing the relay WireGuard seed."
|
||||
value = oci_vault_secret.wireguard_seed.id
|
||||
}
|
||||
|
||||
output "wireguard_public_key_secret_id" {
|
||||
description = "OCI Vault secret OCID where bootstrap publishes the relay WireGuard public key."
|
||||
value = oci_vault_secret.wireguard_public_key.id
|
||||
}
|
||||
|
||||
output "log_ingest_secret_id" {
|
||||
description = "OCI Vault secret OCID containing the relay log ingestion credential."
|
||||
value = oci_vault_secret.log_ingest.id
|
||||
}
|
||||
|
||||
output "address_list_bucket_name" {
|
||||
description = "Private OCI Object Storage bucket containing validated relay address lists."
|
||||
value = oci_objectstorage_bucket.address_lists.name
|
||||
}
|
||||
|
||||
output "object_storage_namespace" {
|
||||
description = "OCI Object Storage namespace used by the admission publisher."
|
||||
value = data.oci_objectstorage_namespace.relay.namespace
|
||||
}
|
||||
|
||||
output "admission_publisher_user_id" {
|
||||
description = "Dedicated in-cluster admission publisher user OCID."
|
||||
value = try(oci_identity_user.admission_publisher[0].id, null)
|
||||
}
|
||||
|
||||
output "admission_publisher_fingerprint" {
|
||||
description = "Fingerprint of the dedicated admission publisher API key."
|
||||
value = try(oci_identity_api_key.admission_publisher[0].fingerprint, null)
|
||||
}
|
||||
|
||||
output "smtp_deny_publisher_user_id" {
|
||||
description = "Dedicated SMTP deny publisher user OCID."
|
||||
value = try(oci_identity_user.smtp_deny_publisher[0].id, null)
|
||||
}
|
||||
|
||||
output "smtp_deny_publisher_fingerprint" {
|
||||
description = "Fingerprint of the dedicated SMTP deny publisher API key."
|
||||
value = try(oci_identity_api_key.smtp_deny_publisher[0].fingerprint, null)
|
||||
}
|
||||
|
||||
output "wan_controller_user_id" {
|
||||
description = "Dedicated WAN controller user OCID."
|
||||
value = oci_identity_user.wan_controller.id
|
||||
}
|
||||
|
||||
output "wan_controller_fingerprint" {
|
||||
description = "Fingerprint of the WAN controller API key."
|
||||
value = oci_identity_api_key.wan_controller.fingerprint
|
||||
}
|
||||
|
||||
output "wireguard_security_rule_id" {
|
||||
description = "WireGuard ingress rule identifier reconciled by the WAN controller."
|
||||
value = oci_core_network_security_group_security_rule.wireguard.id
|
||||
}
|
||||
|
||||
output "relay_http_monitor_id" {
|
||||
description = "OCI Health Checks monitor OCID for relay TCP port 80 reachability."
|
||||
value = oci_health_checks_ping_monitor.relay_http.id
|
||||
}
|
||||
|
||||
output "relay_https_monitor_id" {
|
||||
description = "OCI Health Checks monitor OCID for the relay HTTPS path."
|
||||
value = oci_health_checks_http_monitor.relay_https.id
|
||||
}
|
||||
|
||||
output "relay_mail_https_monitor_id" {
|
||||
description = "OCI Health Checks monitor OCID for Stalwart user HTTPS."
|
||||
value = try(oci_health_checks_http_monitor.relay_mail_https[0].id, null)
|
||||
}
|
||||
|
||||
output "email_dkim_dns_name" {
|
||||
description = "DNS owner name required for OCI Email Delivery DKIM."
|
||||
value = oci_email_dkim.quorate_dev.dns_subdomain_name
|
||||
}
|
||||
|
||||
output "email_dkim_cname_target" {
|
||||
description = "DNS CNAME target required for OCI Email Delivery DKIM."
|
||||
value = oci_email_dkim.quorate_dev.cname_record_value
|
||||
}
|
||||
|
||||
output "email_smtp_endpoint" {
|
||||
description = "Regional OCI Email Delivery SMTP submission endpoint."
|
||||
value = data.oci_email_configuration.relay.smtp_submit_endpoint
|
||||
}
|
||||
|
||||
output "email_smtp_user_id" {
|
||||
description = "Dedicated IAM user OCID for out-of-band SMTP credential provisioning."
|
||||
value = oci_identity_user.stalwart_email_delivery.id
|
||||
}
|
||||
140
terraform/external/oci-relay/providers.tf
vendored
Normal file
140
terraform/external/oci-relay/providers.tf
vendored
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
terraform {
|
||||
required_version = ">= 1.11.0, < 2.0.0"
|
||||
|
||||
backend "pg" {
|
||||
conn_str = "postgres://10.234.158.153:5432/tf_state?sslmode=require"
|
||||
schema_name = "ext_oci_relay"
|
||||
}
|
||||
|
||||
required_providers {
|
||||
oci = {
|
||||
source = "oracle/oci"
|
||||
version = "~> 8.0"
|
||||
}
|
||||
vault = {
|
||||
source = "hashicorp/vault"
|
||||
version = "5.10.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "vault" {}
|
||||
|
||||
ephemeral "vault_kv_secret_v2" "operator_api" {
|
||||
mount = "secret"
|
||||
name = "oci/operator-api"
|
||||
}
|
||||
|
||||
data "vault_generic_secret" "relay_config" {
|
||||
path = "secret/oci/relay/tofu-config"
|
||||
}
|
||||
|
||||
locals {
|
||||
relay_config = data.vault_generic_secret.relay_config.data
|
||||
tenancy_ocid = nonsensitive(local.relay_config["tenancy_ocid"])
|
||||
region = nonsensitive(local.relay_config["region"])
|
||||
budget_alert_recipients = local.relay_config["budget_alert_recipients"]
|
||||
monitoring_alert_email = local.relay_config["budget_alert_recipients"]
|
||||
home_wireguard_public_key = nonsensitive(local.relay_config["home_wireguard_public_key"])
|
||||
relay_image_ocid = nonsensitive(local.relay_config["relay_image_ocid"])
|
||||
publisher_api_public_key = local.relay_config["publisher_api_public_key"]
|
||||
wan_controller_public_key = local.relay_config["wan_controller_api_public_key"]
|
||||
publisher_email = local.relay_config["publisher_email"]
|
||||
smtp_deny_publisher_api_public_key = try(local.relay_config["smtp_deny_publisher_api_public_key"], "")
|
||||
smtp_deny_publisher_email = try(local.relay_config["smtp_deny_publisher_email"], "")
|
||||
monitoring_alert_enabled = nonsensitive(local.monitoring_alert_email) != ""
|
||||
publisher_enabled = nonsensitive(local.publisher_email) != ""
|
||||
smtp_deny_publisher_enabled = var.mail_relay_enabled && nonsensitive(local.smtp_deny_publisher_email) != ""
|
||||
home_tunnel_address = cidrhost(var.relay_tunnel_address, 2)
|
||||
}
|
||||
|
||||
check "relay_vault_configuration" {
|
||||
assert {
|
||||
condition = can(regex("^ocid1\\.tenancy\\.", local.tenancy_ocid))
|
||||
error_message = "The relay Vault configuration must contain an OCI tenancy OCID."
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = can(regex("^[^@[:space:]]+@[^@[:space:]]+$", local.monitoring_alert_email))
|
||||
error_message = "The relay Vault alert recipient must be one email address."
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = can(regex("^-----BEGIN PUBLIC KEY-----", local.publisher_api_public_key))
|
||||
error_message = "The relay Vault publisher public key must be PEM encoded."
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = can(regex("^-----BEGIN PUBLIC KEY-----", local.wan_controller_public_key))
|
||||
error_message = "The relay Vault WAN controller public key must be PEM encoded."
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = can(regex("^[^@[:space:]]+@[^@[:space:]]+$", local.publisher_email))
|
||||
error_message = "The relay Vault publisher email must be one email address."
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = can(regex("^ocid1\\.image\\.", local.relay_image_ocid))
|
||||
error_message = "The relay Vault configuration must contain a custom relay image OCID."
|
||||
}
|
||||
}
|
||||
|
||||
resource "terraform_data" "relay_configuration" {
|
||||
input = {
|
||||
imaps_health_check_enabled = var.imaps_health_check_enabled
|
||||
imaps_ingress_source_cidrs = var.imaps_ingress_source_cidrs
|
||||
mail_https_health_check_enabled = var.mail_https_health_check_enabled
|
||||
mail_relay_enabled = var.mail_relay_enabled
|
||||
smtp_health_check_enabled = var.smtp_health_check_enabled
|
||||
smtp_ingress_source_cidrs = var.smtp_ingress_source_cidrs
|
||||
smtp_deny_maximum_addresses = var.smtp_deny_maximum_addresses
|
||||
smtp_deny_maximum_intervals = var.smtp_deny_maximum_intervals
|
||||
smtp_deny_maximum_lifetime_seconds = var.smtp_deny_maximum_lifetime_seconds
|
||||
smtp_deny_maximum_observation_window_seconds = var.smtp_deny_maximum_observation_window_seconds
|
||||
smtp_deny_minimum_observations = var.smtp_deny_minimum_observations
|
||||
smtp_evidence_snapshot_interval_seconds = var.smtp_evidence_snapshot_interval_seconds
|
||||
relay_metrics_port = var.relay_metrics_port
|
||||
submissions_health_check_enabled = var.submissions_health_check_enabled
|
||||
submissions_ingress_source_cidrs = var.submissions_ingress_source_cidrs
|
||||
web_allow_maximum_addresses = var.web_allow_maximum_addresses
|
||||
web_allow_minimum_addresses = var.web_allow_minimum_addresses
|
||||
web_allow_minimum_intervals = var.web_allow_minimum_intervals
|
||||
}
|
||||
|
||||
lifecycle {
|
||||
precondition {
|
||||
condition = var.mail_relay_enabled || alltrue([
|
||||
for source_cidrs in values(local.mail_ingress_source_cidrs_by_port) : length(source_cidrs) == 0
|
||||
])
|
||||
error_message = "Mail ingress sources cannot be admitted while relay guest mail forwarding is disabled."
|
||||
}
|
||||
|
||||
precondition {
|
||||
condition = var.mail_relay_enabled || !anytrue(concat(
|
||||
values(local.mail_health_checks_by_port),
|
||||
[var.mail_https_health_check_enabled]
|
||||
))
|
||||
error_message = "Mail health checks cannot be enabled while relay guest mail forwarding is disabled."
|
||||
}
|
||||
|
||||
precondition {
|
||||
condition = !var.mail_relay_enabled || can(regex("^-----BEGIN PUBLIC KEY-----", local.smtp_deny_publisher_api_public_key))
|
||||
error_message = "The relay Vault SMTP deny publisher public key must be PEM encoded when mail relay is enabled."
|
||||
}
|
||||
|
||||
precondition {
|
||||
condition = !var.mail_relay_enabled || can(regex("^[^@[:space:]]+@[^@[:space:]]+$", local.smtp_deny_publisher_email))
|
||||
error_message = "The relay Vault SMTP deny publisher email must be one email address when mail relay is enabled."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "oci" {
|
||||
auth = "APIKey"
|
||||
tenancy_ocid = ephemeral.vault_kv_secret_v2.operator_api.data["tenancy_ocid"]
|
||||
user_ocid = ephemeral.vault_kv_secret_v2.operator_api.data["user_ocid"]
|
||||
fingerprint = ephemeral.vault_kv_secret_v2.operator_api.data["fingerprint"]
|
||||
private_key = ephemeral.vault_kv_secret_v2.operator_api.data["private_key"]
|
||||
region = ephemeral.vault_kv_secret_v2.operator_api.data["region"]
|
||||
}
|
||||
70
terraform/external/oci-relay/secrets.tf
vendored
Normal file
70
terraform/external/oci-relay/secrets.tf
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
resource "oci_kms_vault" "relay" {
|
||||
compartment_id = local.compartment_id
|
||||
display_name = "oci-l4-relay"
|
||||
vault_type = "DEFAULT"
|
||||
freeform_tags = var.freeform_tags
|
||||
}
|
||||
|
||||
resource "oci_kms_key" "relay" {
|
||||
compartment_id = local.compartment_id
|
||||
display_name = "oci-l4-relay"
|
||||
management_endpoint = oci_kms_vault.relay.management_endpoint
|
||||
protection_mode = "SOFTWARE"
|
||||
freeform_tags = var.freeform_tags
|
||||
|
||||
key_shape {
|
||||
algorithm = "AES"
|
||||
length = 32
|
||||
}
|
||||
}
|
||||
|
||||
resource "oci_vault_secret" "wireguard_seed" {
|
||||
compartment_id = local.compartment_id
|
||||
key_id = oci_kms_key.relay.id
|
||||
secret_name = "oci-l4-relay-wireguard-seed"
|
||||
vault_id = oci_kms_vault.relay.id
|
||||
description = "Seed material for the OCI relay WireGuard private key"
|
||||
enable_auto_generation = true
|
||||
freeform_tags = var.freeform_tags
|
||||
|
||||
secret_generation_context {
|
||||
generation_type = "BYTES"
|
||||
generation_template = "BYTES_512"
|
||||
}
|
||||
}
|
||||
|
||||
resource "oci_vault_secret" "wireguard_public_key" {
|
||||
compartment_id = local.compartment_id
|
||||
key_id = oci_kms_key.relay.id
|
||||
secret_name = "oci-l4-relay-wireguard-public-key"
|
||||
vault_id = oci_kms_vault.relay.id
|
||||
description = "Published OCI relay WireGuard public key"
|
||||
freeform_tags = var.freeform_tags
|
||||
|
||||
secret_content {
|
||||
content = base64encode("pending")
|
||||
content_type = "BASE64"
|
||||
}
|
||||
|
||||
lifecycle {
|
||||
ignore_changes = [secret_content]
|
||||
}
|
||||
}
|
||||
|
||||
resource "oci_vault_secret" "log_ingest" {
|
||||
compartment_id = local.compartment_id
|
||||
key_id = oci_kms_key.relay.id
|
||||
secret_name = "oci-l4-relay-log-ingest"
|
||||
vault_id = oci_kms_vault.relay.id
|
||||
description = "Bearer credential for authenticated relay log ingestion"
|
||||
freeform_tags = var.freeform_tags
|
||||
|
||||
secret_content {
|
||||
content = base64encode("pending")
|
||||
content_type = "BASE64"
|
||||
}
|
||||
|
||||
lifecycle {
|
||||
ignore_changes = [secret_content]
|
||||
}
|
||||
}
|
||||
177
terraform/external/oci-relay/security.tf
vendored
Normal file
177
terraform/external/oci-relay/security.tf
vendored
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
resource "oci_core_network_security_group_security_rule" "web" {
|
||||
for_each = toset(["80", "443"])
|
||||
|
||||
network_security_group_id = oci_core_network_security_group.relay.id
|
||||
direction = "INGRESS"
|
||||
protocol = "6"
|
||||
source = "0.0.0.0/0"
|
||||
source_type = "CIDR_BLOCK"
|
||||
stateless = false
|
||||
description = "Public L4 relay TCP ${each.value}"
|
||||
|
||||
tcp_options {
|
||||
destination_port_range {
|
||||
min = tonumber(each.value)
|
||||
max = tonumber(each.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
locals {
|
||||
mail_ingress_source_cidrs_by_port = {
|
||||
"25" = var.smtp_ingress_source_cidrs
|
||||
"465" = var.submissions_ingress_source_cidrs
|
||||
"993" = var.imaps_ingress_source_cidrs
|
||||
}
|
||||
mail_health_checks_by_port = {
|
||||
"25" = var.smtp_health_check_enabled
|
||||
"465" = var.submissions_health_check_enabled
|
||||
"993" = var.imaps_health_check_enabled
|
||||
}
|
||||
}
|
||||
|
||||
resource "oci_core_network_security_group_security_rule" "mail" {
|
||||
for_each = merge([
|
||||
for port, source_cidrs in local.mail_ingress_source_cidrs_by_port : {
|
||||
for source in source_cidrs : "${source}:${port}" => {
|
||||
source = source
|
||||
port = tonumber(port)
|
||||
}
|
||||
}
|
||||
]...)
|
||||
|
||||
network_security_group_id = oci_core_network_security_group.relay.id
|
||||
direction = "INGRESS"
|
||||
protocol = "6"
|
||||
source = each.value.source
|
||||
source_type = "CIDR_BLOCK"
|
||||
stateless = false
|
||||
description = "Mail relay TCP ${each.value.port} from ${each.value.source}"
|
||||
|
||||
tcp_options {
|
||||
destination_port_range {
|
||||
min = each.value.port
|
||||
max = each.value.port
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "oci_core_network_security_group_security_rule" "mail_monitoring" {
|
||||
for_each = merge([
|
||||
for port, enabled in local.mail_health_checks_by_port : {
|
||||
for source in enabled ? toset([
|
||||
for address in var.monitoring_source_ipv4 : "${address}/32"
|
||||
if !anytrue([
|
||||
for source_cidr in local.mail_ingress_source_cidrs_by_port[port] : cidrcontains(source_cidr, address)
|
||||
])
|
||||
]) : toset([]) : "${source}:${port}" => {
|
||||
source = source
|
||||
port = tonumber(port)
|
||||
}
|
||||
}
|
||||
]...)
|
||||
|
||||
network_security_group_id = oci_core_network_security_group.relay.id
|
||||
direction = "INGRESS"
|
||||
protocol = "6"
|
||||
source = each.value.source
|
||||
source_type = "CIDR_BLOCK"
|
||||
stateless = false
|
||||
description = "Mail health TCP ${each.value.port} from ${each.value.source}"
|
||||
|
||||
tcp_options {
|
||||
destination_port_range {
|
||||
min = each.value.port
|
||||
max = each.value.port
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "oci_core_network_security_group_security_rule" "wireguard" {
|
||||
network_security_group_id = oci_core_network_security_group.relay.id
|
||||
direction = "INGRESS"
|
||||
protocol = "17"
|
||||
source = "0.0.0.0/0"
|
||||
source_type = "CIDR_BLOCK"
|
||||
stateless = false
|
||||
description = "Home WireGuard peer"
|
||||
|
||||
udp_options {
|
||||
destination_port_range {
|
||||
min = var.wireguard_listen_port
|
||||
max = var.wireguard_listen_port
|
||||
}
|
||||
}
|
||||
|
||||
lifecycle {
|
||||
ignore_changes = [source]
|
||||
}
|
||||
}
|
||||
|
||||
resource "oci_core_network_security_group_security_rule" "icmp_fragmentation" {
|
||||
network_security_group_id = oci_core_network_security_group.relay.id
|
||||
direction = "INGRESS"
|
||||
protocol = "1"
|
||||
source = "0.0.0.0/0"
|
||||
source_type = "CIDR_BLOCK"
|
||||
stateless = false
|
||||
description = "IPv4 path MTU discovery"
|
||||
|
||||
icmp_options {
|
||||
type = 3
|
||||
code = 4
|
||||
}
|
||||
}
|
||||
|
||||
resource "oci_core_network_security_group_security_rule" "egress" {
|
||||
network_security_group_id = oci_core_network_security_group.relay.id
|
||||
direction = "EGRESS"
|
||||
protocol = "all"
|
||||
destination = "0.0.0.0/0"
|
||||
destination_type = "CIDR_BLOCK"
|
||||
stateless = false
|
||||
description = "Relay egress"
|
||||
}
|
||||
|
||||
resource "oci_core_network_security_group" "image_builder" {
|
||||
compartment_id = local.compartment_id
|
||||
vcn_id = oci_core_vcn.relay.id
|
||||
display_name = "oci-l4-relay-image-builder"
|
||||
freeform_tags = var.freeform_tags
|
||||
}
|
||||
|
||||
resource "oci_core_network_security_group_security_rule" "image_builder_ssh" {
|
||||
count = var.image_builder_ssh_source_cidr == null ? 0 : 1
|
||||
|
||||
network_security_group_id = oci_core_network_security_group.image_builder.id
|
||||
direction = "INGRESS"
|
||||
protocol = "6"
|
||||
source = var.image_builder_ssh_source_cidr
|
||||
source_type = "CIDR_BLOCK"
|
||||
stateless = false
|
||||
description = "Operator SSH to temporary image builders"
|
||||
|
||||
tcp_options {
|
||||
destination_port_range {
|
||||
min = 22
|
||||
max = 22
|
||||
}
|
||||
}
|
||||
|
||||
lifecycle {
|
||||
precondition {
|
||||
condition = var.image_builder_ssh_source_cidr != null
|
||||
error_message = "image_builder_ssh_source_cidr must be an IPv4 /32 for image builds."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "oci_core_network_security_group_security_rule" "image_builder_egress" {
|
||||
network_security_group_id = oci_core_network_security_group.image_builder.id
|
||||
direction = "EGRESS"
|
||||
protocol = "all"
|
||||
destination = "0.0.0.0/0"
|
||||
destination_type = "CIDR_BLOCK"
|
||||
stateless = false
|
||||
description = "Temporary image-builder egress"
|
||||
}
|
||||
661
terraform/external/oci-relay/variables.tf
vendored
Normal file
661
terraform/external/oci-relay/variables.tf
vendored
Normal file
|
|
@ -0,0 +1,661 @@
|
|||
variable "availability_domain_index" {
|
||||
description = "Availability-domain index selected from the compartment."
|
||||
type = number
|
||||
default = 0
|
||||
}
|
||||
|
||||
variable "shape" {
|
||||
description = "OCI compute shape for the relay."
|
||||
type = string
|
||||
default = "VM.Standard.A1.Flex"
|
||||
}
|
||||
|
||||
variable "ocpus" {
|
||||
description = "OCPUs assigned to the flexible relay shape."
|
||||
type = number
|
||||
default = 1
|
||||
}
|
||||
|
||||
variable "memory_in_gbs" {
|
||||
description = "Memory assigned to the flexible relay shape."
|
||||
type = number
|
||||
default = 4
|
||||
}
|
||||
|
||||
variable "image_builder_ssh_source_cidr" {
|
||||
description = "Current operator IPv4 /32 admitted for Packer image builders."
|
||||
type = string
|
||||
default = null
|
||||
nullable = true
|
||||
|
||||
validation {
|
||||
condition = (
|
||||
var.image_builder_ssh_source_cidr == null ||
|
||||
(can(cidrhost(var.image_builder_ssh_source_cidr, 0)) && endswith(var.image_builder_ssh_source_cidr, "/32"))
|
||||
)
|
||||
error_message = "image_builder_ssh_source_cidr must be null or an IPv4 /32."
|
||||
}
|
||||
}
|
||||
|
||||
variable "budget_amount" {
|
||||
description = "Monthly relay-compartment budget in the tenancy rate-card currency."
|
||||
type = number
|
||||
default = 35
|
||||
}
|
||||
|
||||
variable "log_ingest_hostname" {
|
||||
description = "Gateway R hostname used for authenticated relay log ingestion."
|
||||
type = string
|
||||
default = "relay-logs.quorate.dev"
|
||||
|
||||
validation {
|
||||
condition = can(regex("^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+$", var.log_ingest_hostname))
|
||||
error_message = "log_ingest_hostname must be a lower-case DNS hostname."
|
||||
}
|
||||
}
|
||||
|
||||
variable "monitoring_source_ipv4" {
|
||||
description = "Exact OCI Health Checks source IPv4 addresses admitted ahead of geographic filtering."
|
||||
type = list(string)
|
||||
default = [
|
||||
"13.114.80.229",
|
||||
"13.115.206.223",
|
||||
"20.191.152.138",
|
||||
"20.198.221.215",
|
||||
"35.226.104.15",
|
||||
"130.211.189.141",
|
||||
]
|
||||
|
||||
validation {
|
||||
condition = (
|
||||
length(var.monitoring_source_ipv4) >= 3 &&
|
||||
length(distinct(var.monitoring_source_ipv4)) == length(var.monitoring_source_ipv4) &&
|
||||
alltrue([for address in var.monitoring_source_ipv4 : can(cidrhost("${address}/32", 0))])
|
||||
)
|
||||
error_message = "monitoring_source_ipv4 must contain at least three unique IPv4 addresses."
|
||||
}
|
||||
}
|
||||
|
||||
variable "image_ocid" {
|
||||
description = "Pinned OCI Osaka platform-image OCID for the relay instance."
|
||||
type = string
|
||||
default = "********"
|
||||
}
|
||||
|
||||
variable "vcn_cidr" {
|
||||
description = "IPv4 CIDR for the relay VCN."
|
||||
type = string
|
||||
default = "10.27.161.96/24"
|
||||
}
|
||||
|
||||
variable "subnet_cidr" {
|
||||
description = "IPv4 CIDR for the public relay subnet."
|
||||
type = string
|
||||
default = "10.27.161.96/28"
|
||||
}
|
||||
|
||||
variable "wireguard_listen_port" {
|
||||
description = "Public UDP listen port for the OCI WireGuard peer."
|
||||
type = number
|
||||
default = 51820
|
||||
|
||||
validation {
|
||||
condition = var.wireguard_listen_port >= 1024 && var.wireguard_listen_port <= 65535
|
||||
error_message = "wireguard_listen_port must be between 1024 and 65535."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_tunnel_address" {
|
||||
description = "OCI WireGuard interface address with prefix length."
|
||||
type = string
|
||||
default = "10.40.68.115/30"
|
||||
}
|
||||
|
||||
variable "relay_metrics_port" {
|
||||
description = "TCP port for relay metrics on the WireGuard interface."
|
||||
type = number
|
||||
default = 19100
|
||||
|
||||
validation {
|
||||
condition = var.relay_metrics_port >= 1024 && var.relay_metrics_port <= 65535
|
||||
error_message = "relay_metrics_port must be between 1024 and 65535."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_conntrack_sample_denominator" {
|
||||
description = "Deterministic one-in-N sampling denominator for completed accepted relay connections."
|
||||
type = number
|
||||
default = 10
|
||||
|
||||
validation {
|
||||
condition = var.relay_conntrack_sample_denominator >= 1 && var.relay_conntrack_sample_denominator <= 10000
|
||||
error_message = "relay_conntrack_sample_denominator must be between 1 and 10000."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_conntrack_emission_rate_per_second" {
|
||||
description = "Maximum sustained completed-connection records emitted per second."
|
||||
type = number
|
||||
default = 1
|
||||
|
||||
validation {
|
||||
condition = var.relay_conntrack_emission_rate_per_second >= 1 && var.relay_conntrack_emission_rate_per_second <= 100
|
||||
error_message = "relay_conntrack_emission_rate_per_second must be between 1 and 100."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_conntrack_emission_burst" {
|
||||
description = "Maximum burst of completed-connection records emitted before rate limiting."
|
||||
type = number
|
||||
default = 10
|
||||
|
||||
validation {
|
||||
condition = var.relay_conntrack_emission_burst >= 1 && var.relay_conntrack_emission_burst <= 1000
|
||||
error_message = "relay_conntrack_emission_burst must be between 1 and 1000."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_conntrack_event_buffer" {
|
||||
description = "In-memory completed-connection event buffer between netlink decoding and bounded emission."
|
||||
type = number
|
||||
default = 4096
|
||||
|
||||
validation {
|
||||
condition = var.relay_conntrack_event_buffer >= 1 && var.relay_conntrack_event_buffer <= 65536
|
||||
error_message = "relay_conntrack_event_buffer must be between 1 and 65536."
|
||||
}
|
||||
}
|
||||
|
||||
variable "gateway_relay_address" {
|
||||
description = "Gateway R LoadBalancer IPv4 address."
|
||||
type = string
|
||||
default = "10.104.40.127"
|
||||
}
|
||||
|
||||
variable "mail_relay_enabled" {
|
||||
description = "Configure mail forwarding on the relay guest."
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "smtp_ingress_source_cidrs" {
|
||||
description = "IPv4 source CIDRs admitted by the OCI NSG to SMTP TCP/25."
|
||||
type = set(string)
|
||||
default = ["0.0.0.0/0"]
|
||||
|
||||
validation {
|
||||
condition = alltrue([
|
||||
for cidr in var.smtp_ingress_source_cidrs :
|
||||
can(cidrhost(cidr, 0)) && can(regex("^[0-9.]+/[0-9]+$", cidr))
|
||||
])
|
||||
error_message = "smtp_ingress_source_cidrs must contain only IPv4 CIDRs."
|
||||
}
|
||||
}
|
||||
|
||||
variable "submissions_ingress_source_cidrs" {
|
||||
description = "IPv4 source CIDRs admitted by the OCI NSG to submissions TCP/465."
|
||||
type = set(string)
|
||||
default = ["0.0.0.0/0"]
|
||||
|
||||
validation {
|
||||
condition = alltrue([
|
||||
for cidr in var.submissions_ingress_source_cidrs :
|
||||
can(cidrhost(cidr, 0)) && can(regex("^[0-9.]+/[0-9]+$", cidr))
|
||||
])
|
||||
error_message = "submissions_ingress_source_cidrs must contain only IPv4 CIDRs."
|
||||
}
|
||||
}
|
||||
|
||||
variable "imaps_ingress_source_cidrs" {
|
||||
description = "IPv4 source CIDRs admitted by the OCI NSG to IMAPS TCP/993."
|
||||
type = set(string)
|
||||
default = ["0.0.0.0/0"]
|
||||
|
||||
validation {
|
||||
condition = alltrue([
|
||||
for cidr in var.imaps_ingress_source_cidrs :
|
||||
can(cidrhost(cidr, 0)) && can(regex("^[0-9.]+/[0-9]+$", cidr))
|
||||
])
|
||||
error_message = "imaps_ingress_source_cidrs must contain only IPv4 CIDRs."
|
||||
}
|
||||
}
|
||||
|
||||
variable "smtp_health_check_enabled" {
|
||||
description = "Enable the non-submitting OCI TCP/25 health check and admit its source addresses."
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "submissions_health_check_enabled" {
|
||||
description = "Enable the non-submitting OCI TCP/465 health check and admit its source addresses."
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "imaps_health_check_enabled" {
|
||||
description = "Enable the non-submitting OCI TCP/993 health check and admit its source addresses."
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "mail_https_health_check_enabled" {
|
||||
description = "Enable the non-submitting OCI mail HTTPS health check."
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "relay_rate_limit_mbit" {
|
||||
description = "Traffic-control ceiling for inner traffic sent toward home."
|
||||
type = number
|
||||
default = 150
|
||||
}
|
||||
|
||||
variable "relay_tunnel_mtu" {
|
||||
description = "WireGuard inner MTU shared with the home relay peer."
|
||||
type = number
|
||||
default = 1420
|
||||
|
||||
validation {
|
||||
condition = var.relay_tunnel_mtu >= 1280 && var.relay_tunnel_mtu <= 1500
|
||||
error_message = "relay_tunnel_mtu must be between 1280 and 1500."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_web_syn_rate_per_minute" {
|
||||
description = "Per-source TCP SYN rate on ports 80 and 443 that increments the over-limit counter."
|
||||
type = number
|
||||
default = 120
|
||||
|
||||
validation {
|
||||
condition = var.relay_web_syn_rate_per_minute >= 1
|
||||
error_message = "relay_web_syn_rate_per_minute must be positive."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_web_syn_burst" {
|
||||
description = "Per-source TCP SYN burst accepted before the over-limit counter increments."
|
||||
type = number
|
||||
default = 120
|
||||
|
||||
validation {
|
||||
condition = var.relay_web_syn_burst >= 1
|
||||
error_message = "relay_web_syn_burst must be positive."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_web_syn_aggregate_rate_per_second" {
|
||||
description = "Aggregate public web SYN rate admitted before excess packets are dropped."
|
||||
type = number
|
||||
default = 1000
|
||||
|
||||
validation {
|
||||
condition = var.relay_web_syn_aggregate_rate_per_second >= 1
|
||||
error_message = "relay_web_syn_aggregate_rate_per_second must be positive."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_web_syn_aggregate_burst" {
|
||||
description = "Aggregate public web SYN burst admitted before excess packets are dropped."
|
||||
type = number
|
||||
default = 1000
|
||||
|
||||
validation {
|
||||
condition = var.relay_web_syn_aggregate_burst >= 1
|
||||
error_message = "relay_web_syn_aggregate_burst must be positive."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_web_packet_aggregate_rate_per_second" {
|
||||
description = "Aggregate public web packet rate admitted before conntrack and DNAT."
|
||||
type = number
|
||||
default = 20000
|
||||
|
||||
validation {
|
||||
condition = var.relay_web_packet_aggregate_rate_per_second >= 1000
|
||||
error_message = "relay_web_packet_aggregate_rate_per_second must be at least 1000."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_web_packet_aggregate_burst" {
|
||||
description = "Aggregate public web packet burst admitted before excess packets are dropped."
|
||||
type = number
|
||||
default = 20000
|
||||
|
||||
validation {
|
||||
condition = var.relay_web_packet_aggregate_burst >= var.relay_web_packet_aggregate_rate_per_second
|
||||
error_message = "relay_web_packet_aggregate_burst must be at least the per-second packet rate."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_smtp_syn_rate_per_minute" {
|
||||
description = "Per-source TCP/25 SYN rate before a source enters temporary transport quarantine."
|
||||
type = number
|
||||
default = 60
|
||||
|
||||
validation {
|
||||
condition = var.relay_smtp_syn_rate_per_minute >= 1
|
||||
error_message = "relay_smtp_syn_rate_per_minute must be positive."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_smtp_syn_burst" {
|
||||
description = "Per-source TCP/25 SYN burst allowed before transport quarantine."
|
||||
type = number
|
||||
default = 20
|
||||
|
||||
validation {
|
||||
condition = var.relay_smtp_syn_burst >= 1
|
||||
error_message = "relay_smtp_syn_burst must be positive."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_smtp_syn_aggregate_rate_per_second" {
|
||||
description = "Aggregate TCP/25 SYN rate admitted before excess packets are dropped."
|
||||
type = number
|
||||
default = 500
|
||||
|
||||
validation {
|
||||
condition = var.relay_smtp_syn_aggregate_rate_per_second >= 1
|
||||
error_message = "relay_smtp_syn_aggregate_rate_per_second must be positive."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_smtp_syn_aggregate_burst" {
|
||||
description = "Aggregate TCP/25 SYN burst admitted before excess packets are dropped."
|
||||
type = number
|
||||
default = 500
|
||||
|
||||
validation {
|
||||
condition = var.relay_smtp_syn_aggregate_burst >= 1
|
||||
error_message = "relay_smtp_syn_aggregate_burst must be positive."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_smtp_packet_aggregate_rate_per_second" {
|
||||
description = "Aggregate TCP/25 packet rate admitted before conntrack and DNAT."
|
||||
type = number
|
||||
default = 10000
|
||||
|
||||
validation {
|
||||
condition = var.relay_smtp_packet_aggregate_rate_per_second >= 1
|
||||
error_message = "relay_smtp_packet_aggregate_rate_per_second must be positive."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_smtp_packet_aggregate_burst" {
|
||||
description = "Aggregate TCP/25 packet burst admitted before excess packets are dropped."
|
||||
type = number
|
||||
default = 10000
|
||||
|
||||
validation {
|
||||
condition = var.relay_smtp_packet_aggregate_burst >= var.relay_smtp_packet_aggregate_rate_per_second
|
||||
error_message = "relay_smtp_packet_aggregate_burst must cover at least one second."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_auth_syn_rate_per_minute" {
|
||||
description = "Per-source authenticated-mail SYN rate before a source enters temporary transport quarantine."
|
||||
type = number
|
||||
default = 30
|
||||
|
||||
validation {
|
||||
condition = var.relay_auth_syn_rate_per_minute >= 1
|
||||
error_message = "relay_auth_syn_rate_per_minute must be positive."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_auth_syn_burst" {
|
||||
description = "Per-source authenticated-mail SYN burst allowed before transport quarantine."
|
||||
type = number
|
||||
default = 15
|
||||
|
||||
validation {
|
||||
condition = var.relay_auth_syn_burst >= 1
|
||||
error_message = "relay_auth_syn_burst must be positive."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_auth_syn_aggregate_rate_per_second" {
|
||||
description = "Aggregate authenticated-mail SYN rate admitted before excess packets are dropped."
|
||||
type = number
|
||||
default = 200
|
||||
|
||||
validation {
|
||||
condition = var.relay_auth_syn_aggregate_rate_per_second >= 1
|
||||
error_message = "relay_auth_syn_aggregate_rate_per_second must be positive."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_auth_syn_aggregate_burst" {
|
||||
description = "Aggregate authenticated-mail SYN burst admitted before excess packets are dropped."
|
||||
type = number
|
||||
default = 200
|
||||
|
||||
validation {
|
||||
condition = var.relay_auth_syn_aggregate_burst >= 1
|
||||
error_message = "relay_auth_syn_aggregate_burst must be positive."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_auth_packet_aggregate_rate_per_second" {
|
||||
description = "Aggregate authenticated-mail packet rate admitted before conntrack and DNAT."
|
||||
type = number
|
||||
default = 5000
|
||||
|
||||
validation {
|
||||
condition = var.relay_auth_packet_aggregate_rate_per_second >= 1
|
||||
error_message = "relay_auth_packet_aggregate_rate_per_second must be positive."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_auth_packet_aggregate_burst" {
|
||||
description = "Aggregate authenticated-mail packet burst admitted before excess packets are dropped."
|
||||
type = number
|
||||
default = 5000
|
||||
|
||||
validation {
|
||||
condition = var.relay_auth_packet_aggregate_burst >= var.relay_auth_packet_aggregate_rate_per_second
|
||||
error_message = "relay_auth_packet_aggregate_burst must cover at least one second."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_http_quarantine_enabled" {
|
||||
description = "Temporarily deny port 80 after repeated attempts without a recent port 443 connection."
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "relay_http_quarantine_minutes" {
|
||||
description = "Lifetime of an HTTP-only quarantine."
|
||||
type = number
|
||||
default = 30
|
||||
|
||||
validation {
|
||||
condition = var.relay_http_quarantine_minutes >= 1 && var.relay_http_quarantine_minutes <= 1440
|
||||
error_message = "relay_http_quarantine_minutes must be between 1 and 1440."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_https_recent_minutes" {
|
||||
description = "Period during which a port 443 connection exempts a source from HTTP-only quarantine."
|
||||
type = number
|
||||
default = 10
|
||||
|
||||
validation {
|
||||
condition = var.relay_https_recent_minutes >= 1 && var.relay_https_recent_minutes <= 1440
|
||||
error_message = "relay_https_recent_minutes must be between 1 and 1440."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_unmatched_http_rate_per_hour" {
|
||||
description = "Port 80 attempt rate without recent HTTPS that triggers HTTP-only quarantine."
|
||||
type = number
|
||||
default = 30
|
||||
|
||||
validation {
|
||||
condition = var.relay_unmatched_http_rate_per_hour >= 1
|
||||
error_message = "relay_unmatched_http_rate_per_hour must be positive."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_unmatched_http_burst" {
|
||||
description = "Immediate unmatched port 80 attempts accepted before HTTP-only quarantine."
|
||||
type = number
|
||||
default = 5
|
||||
|
||||
validation {
|
||||
condition = var.relay_unmatched_http_burst >= 1
|
||||
error_message = "relay_unmatched_http_burst must be positive."
|
||||
}
|
||||
}
|
||||
|
||||
variable "relay_tracker_max_sources" {
|
||||
description = "Maximum per-source entries retained by each nftables ingress tracker."
|
||||
type = number
|
||||
default = 65536
|
||||
|
||||
validation {
|
||||
condition = var.relay_tracker_max_sources >= 1024 && var.relay_tracker_max_sources <= 1048576
|
||||
error_message = "relay_tracker_max_sources must be between 1024 and 1048576."
|
||||
}
|
||||
}
|
||||
|
||||
variable "web_allow_bundle_object" {
|
||||
description = "Private Object Storage key containing the current normalized web allow bundle."
|
||||
type = string
|
||||
default = "web-allow/v1/current.json.gz"
|
||||
}
|
||||
|
||||
variable "web_allow_minimum_addresses" {
|
||||
description = "Minimum admitted IPv4 addresses accepted from an update bundle."
|
||||
type = number
|
||||
default = 1000000000
|
||||
|
||||
validation {
|
||||
condition = var.web_allow_minimum_addresses >= 1 && var.web_allow_minimum_addresses <= 4294967296
|
||||
error_message = "web_allow_minimum_addresses must be between 1 and the IPv4 address-space size."
|
||||
}
|
||||
}
|
||||
|
||||
variable "web_allow_maximum_addresses" {
|
||||
description = "Maximum admitted IPv4 addresses accepted from an update bundle."
|
||||
type = number
|
||||
default = 3500000000
|
||||
|
||||
validation {
|
||||
condition = (
|
||||
var.web_allow_maximum_addresses >= var.web_allow_minimum_addresses &&
|
||||
var.web_allow_maximum_addresses <= 4294967296
|
||||
)
|
||||
error_message = "web_allow_maximum_addresses must be at least the minimum and no larger than the IPv4 address space."
|
||||
}
|
||||
}
|
||||
|
||||
variable "web_allow_minimum_intervals" {
|
||||
description = "Minimum admitted IPv4 intervals accepted from an update bundle."
|
||||
type = number
|
||||
default = 10000
|
||||
|
||||
validation {
|
||||
condition = var.web_allow_minimum_intervals >= 1 && var.web_allow_minimum_intervals <= 500000
|
||||
error_message = "web_allow_minimum_intervals must be between 1 and 500000."
|
||||
}
|
||||
}
|
||||
|
||||
variable "smtp_deny_bundle_object" {
|
||||
description = "Private Object Storage key containing the current SMTP deny bundle."
|
||||
type = string
|
||||
default = "smtp-deny/v1/current.json.gz"
|
||||
}
|
||||
|
||||
variable "smtp_deny_minimum_observations" {
|
||||
description = "Minimum transport observations required for each SMTP deny entry."
|
||||
type = number
|
||||
default = 20
|
||||
|
||||
validation {
|
||||
condition = var.smtp_deny_minimum_observations >= 1
|
||||
error_message = "smtp_deny_minimum_observations must be positive."
|
||||
}
|
||||
}
|
||||
|
||||
variable "smtp_deny_maximum_addresses" {
|
||||
description = "Maximum IPv4 addresses covered by an SMTP deny bundle."
|
||||
type = number
|
||||
default = 4096
|
||||
|
||||
validation {
|
||||
condition = var.smtp_deny_maximum_addresses >= 1 && var.smtp_deny_maximum_addresses <= 4294967296
|
||||
error_message = "smtp_deny_maximum_addresses must be between 1 and the IPv4 address-space size."
|
||||
}
|
||||
}
|
||||
|
||||
variable "smtp_deny_maximum_intervals" {
|
||||
description = "Maximum IPv4 intervals in an SMTP deny bundle."
|
||||
type = number
|
||||
default = 1024
|
||||
|
||||
validation {
|
||||
condition = var.smtp_deny_maximum_intervals >= 1 && var.smtp_deny_maximum_intervals <= 100000
|
||||
error_message = "smtp_deny_maximum_intervals must be between 1 and 100000."
|
||||
}
|
||||
}
|
||||
|
||||
variable "smtp_deny_maximum_lifetime_seconds" {
|
||||
description = "Maximum lifetime of an SMTP deny bundle or entry."
|
||||
type = number
|
||||
default = 86400
|
||||
|
||||
validation {
|
||||
condition = var.smtp_deny_maximum_lifetime_seconds >= 300 && var.smtp_deny_maximum_lifetime_seconds <= 604800
|
||||
error_message = "smtp_deny_maximum_lifetime_seconds must be between 300 and 604800."
|
||||
}
|
||||
}
|
||||
|
||||
variable "smtp_deny_maximum_observation_window_seconds" {
|
||||
description = "Maximum observation window represented by one SMTP deny entry."
|
||||
type = number
|
||||
default = 600
|
||||
|
||||
validation {
|
||||
condition = (
|
||||
var.smtp_deny_maximum_observation_window_seconds >= 60 &&
|
||||
var.smtp_deny_maximum_observation_window_seconds <= var.smtp_deny_maximum_lifetime_seconds
|
||||
)
|
||||
error_message = "smtp_deny_maximum_observation_window_seconds must be at least 60 and no greater than the maximum lifetime."
|
||||
}
|
||||
}
|
||||
|
||||
variable "smtp_evidence_snapshot_interval_seconds" {
|
||||
description = "Cadence for local SMTP deny evidence snapshots."
|
||||
type = number
|
||||
default = 60
|
||||
|
||||
validation {
|
||||
condition = contains([30, 60], var.smtp_evidence_snapshot_interval_seconds)
|
||||
error_message = "smtp_evidence_snapshot_interval_seconds must be a tested 30 or 60 second cadence."
|
||||
}
|
||||
}
|
||||
|
||||
variable "freeform_tags" {
|
||||
description = "Free-form tags applied to OCI relay resources."
|
||||
type = map(string)
|
||||
default = {
|
||||
managed-by = "opentofu"
|
||||
service = "oci-l4-relay"
|
||||
}
|
||||
}
|
||||
|
||||
variable "email_approved_senders" {
|
||||
description = "Approved Stalwart sender addresses; enable @quorate.dev only after OCI DKIM becomes active."
|
||||
type = list(string)
|
||||
default = ["@quorate.dev"]
|
||||
|
||||
validation {
|
||||
condition = alltrue([
|
||||
for sender in var.email_approved_senders :
|
||||
sender == "@quorate.dev" || can(regex("^[^@[:space:]]+@quorate\\.dev$", sender))
|
||||
])
|
||||
error_message = "email_approved_senders may contain @quorate.dev or addresses within quorate.dev."
|
||||
}
|
||||
}
|
||||
40
terraform/external/oci-relay/wan-controller.tf
vendored
Normal file
40
terraform/external/oci-relay/wan-controller.tf
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
resource "oci_identity_user" "wan_controller" {
|
||||
compartment_id = local.tenancy_ocid
|
||||
name = "oci-l4-relay-wan-controller"
|
||||
description = "In-cluster controller for the relay WireGuard NSG source"
|
||||
email = local.publisher_email
|
||||
}
|
||||
|
||||
resource "oci_identity_group" "wan_controllers" {
|
||||
compartment_id = local.tenancy_ocid
|
||||
name = "oci-l4-relay-wan-controllers"
|
||||
description = "Principals allowed to update relay NSG security rules"
|
||||
}
|
||||
|
||||
resource "oci_identity_user_group_membership" "wan_controller" {
|
||||
group_id = oci_identity_group.wan_controllers.id
|
||||
user_id = oci_identity_user.wan_controller.id
|
||||
}
|
||||
|
||||
resource "oci_identity_api_key" "wan_controller" {
|
||||
user_id = oci_identity_user.wan_controller.id
|
||||
key_value = local.wan_controller_public_key
|
||||
}
|
||||
|
||||
resource "oci_identity_user_capabilities_management" "wan_controller" {
|
||||
user_id = oci_identity_user.wan_controller.id
|
||||
can_use_api_keys = true
|
||||
can_use_auth_tokens = false
|
||||
can_use_console_password = false
|
||||
can_use_customer_secret_keys = false
|
||||
can_use_smtp_credentials = false
|
||||
}
|
||||
|
||||
resource "oci_identity_policy" "wan_controller" {
|
||||
compartment_id = local.tenancy_ocid
|
||||
name = "oci-l4-relay-wan-controller"
|
||||
description = "Allow the WAN controller to update NSG rules in the relay compartment"
|
||||
statements = [
|
||||
"Allow group ${oci_identity_group.wan_controllers.name} to manage network-security-groups in compartment id ${local.compartment_id} where request.operation = 'UpdateNetworkSecurityGroupSecurityRules'"
|
||||
]
|
||||
}
|
||||
Loading…
Reference in a new issue