refactor(gitops): establish platform ownership boundaries
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
# This file is maintained automatically by "terraform init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.terraform.io/hashicorp/vault" {
|
||||
version = "5.7.0"
|
||||
constraints = "~> 5.7.0"
|
||||
hashes = [
|
||||
"h1:Pm0AcUSYmBPZgRahQX/ahiYcjtZODSAEc2rK8r8MQ18=",
|
||||
"zh:1dd9ab6d23f61a5e522efcb462f1fd6f4a210c77b9038c8e12fa5fa663b45d01",
|
||||
"zh:3c98d37ead857c980f7b9285f8c3e1eb7a8fd6d6799275c311c6997973389cc9",
|
||||
"zh:3df895fbaed383e3748ba1b50f5f1046f75503483bc3d783992059f85c85ba31",
|
||||
"zh:3e9faaa0a85c6f03c7fd7f8b7008bb3fbb8777f26c001875947cafa47f91c657",
|
||||
"zh:52a057d0c6cde7cbfd9ceb78a3781dcfc81cf108c533f454530ea6bb87a9bea8",
|
||||
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
|
||||
"zh:8521c3825254a5f7fbff8f42ca57cabf052366f0420f5f239ebebf8292c03d0e",
|
||||
"zh:953563d429e40087eb34faf22f28e781e50eee27cfc9ac1ad04308ba592a647f",
|
||||
"zh:a52dd76bb7f5b86cb8de7380d2e68b47ec4445782c16ee205e6a013be35a57b6",
|
||||
"zh:bdad38c95a14c8cce1eeadcc539cf9bf74902ce7c662b79105ad993bb48ec073",
|
||||
"zh:d3c676d7d12c15b58518fa3ee7fc398a13893b4057fe9bf4bc1fe635f3fb995a",
|
||||
"zh:f8673b6c06da80e912c9e32dd4853f07bfca386968d5b33c9fceb6f68b519959",
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
terraform {
|
||||
required_version = ">= 1.11.0"
|
||||
|
||||
required_providers {
|
||||
vault = {
|
||||
source = "hashicorp/vault"
|
||||
version = "~> 5.7.0"
|
||||
}
|
||||
}
|
||||
|
||||
backend "s3" {}
|
||||
}
|
||||
|
||||
provider "vault" {
|
||||
address = var.vault_addr
|
||||
token = var.vault_token
|
||||
}
|
||||
|
||||
removed {
|
||||
from = module.workload_policies
|
||||
|
||||
lifecycle {
|
||||
destroy = false
|
||||
}
|
||||
}
|
||||
|
||||
removed {
|
||||
from = module.workload_roles
|
||||
|
||||
lifecycle {
|
||||
destroy = false
|
||||
}
|
||||
}
|
||||
|
||||
removed {
|
||||
from = vault_transit_secret_backend_key.project_auth_jwt
|
||||
|
||||
lifecycle {
|
||||
destroy = false
|
||||
}
|
||||
}
|
||||
|
||||
removed {
|
||||
from = vault_policy.platform_admin
|
||||
|
||||
lifecycle {
|
||||
destroy = false
|
||||
}
|
||||
}
|
||||
|
||||
removed {
|
||||
from = vault_kubernetes_auth_backend_role.operator
|
||||
|
||||
lifecycle {
|
||||
destroy = false
|
||||
}
|
||||
}
|
||||
|
||||
removed {
|
||||
from = vault_jwt_auth_backend_role.ci
|
||||
|
||||
lifecycle {
|
||||
destroy = false
|
||||
}
|
||||
}
|
||||
|
||||
locals {
|
||||
platform_policy_dir = "${path.module}/../../../../../policies/vault/dev-k3s/platform"
|
||||
|
||||
database_automation_policy_name = "vault-database-automation-dev"
|
||||
database_mount_path = "database"
|
||||
ci_database_role_name = "project-gitops-vault-database-dev"
|
||||
ci_jwt_auth_path = "jwt-ci"
|
||||
ci_workloads_role_name = "project-gitops-vault-workloads-dev"
|
||||
kubernetes_auth_path = "kubernetes"
|
||||
kv_mount_path = "kv"
|
||||
transit_mount_path = "transit"
|
||||
workloads_automation_policy_name = "vault-workloads-automation-dev"
|
||||
|
||||
automation_roles = var.ci_jwt_oidc_discovery_url == null ? {} : {
|
||||
workloads = {
|
||||
bound_claims = var.ci_workloads_bound_claims
|
||||
name = local.ci_workloads_role_name
|
||||
policy = vault_policy.workloads_automation.name
|
||||
}
|
||||
database = {
|
||||
bound_claims = var.ci_database_bound_claims
|
||||
name = local.ci_database_role_name
|
||||
policy = vault_policy.database_automation.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "vault_mount" "kv" {
|
||||
path = local.kv_mount_path
|
||||
type = "kv"
|
||||
options = {
|
||||
version = "2"
|
||||
}
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "vault_mount" "database" {
|
||||
path = local.database_mount_path
|
||||
type = "database"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "vault_mount" "transit" {
|
||||
path = local.transit_mount_path
|
||||
type = "transit"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "vault_auth_backend" "kubernetes" {
|
||||
path = local.kubernetes_auth_path
|
||||
type = "kubernetes"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "vault_kubernetes_auth_backend_config" "cluster" {
|
||||
backend = vault_auth_backend.kubernetes.path
|
||||
disable_iss_validation = true
|
||||
disable_local_ca_jwt = false
|
||||
kubernetes_host = var.kubernetes_host
|
||||
}
|
||||
|
||||
resource "vault_policy" "workloads_automation" {
|
||||
name = local.workloads_automation_policy_name
|
||||
policy = file("${local.platform_policy_dir}/vault-workloads-automation-dev.hcl")
|
||||
}
|
||||
|
||||
resource "vault_policy" "database_automation" {
|
||||
name = local.database_automation_policy_name
|
||||
policy = file("${local.platform_policy_dir}/vault-database-automation-dev.hcl")
|
||||
}
|
||||
|
||||
resource "vault_jwt_auth_backend" "ci" {
|
||||
count = var.ci_jwt_oidc_discovery_url == null ? 0 : 1
|
||||
|
||||
bound_issuer = var.ci_jwt_bound_issuer
|
||||
oidc_discovery_url = var.ci_jwt_oidc_discovery_url
|
||||
path = local.ci_jwt_auth_path
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
|
||||
precondition {
|
||||
condition = (
|
||||
var.ci_jwt_bound_issuer != null &&
|
||||
length(var.ci_jwt_bound_audiences) > 0 &&
|
||||
length(var.ci_workloads_bound_claims) > 0 &&
|
||||
length(var.ci_database_bound_claims) > 0 &&
|
||||
length([
|
||||
for claim, value in var.ci_workloads_bound_claims : claim
|
||||
if lookup(var.ci_database_bound_claims, claim, value) != value
|
||||
]) > 0
|
||||
)
|
||||
error_message = "Enabled CI JWT auth requires issuer/audience constraints and workload/database claim maps with at least one shared discriminator key carrying different values."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "vault_jwt_auth_backend_role" "automation" {
|
||||
for_each = local.automation_roles
|
||||
|
||||
backend = vault_jwt_auth_backend.ci[0].path
|
||||
bound_audiences = var.ci_jwt_bound_audiences
|
||||
bound_claims = each.value.bound_claims
|
||||
bound_claims_type = "string"
|
||||
role_name = each.value.name
|
||||
role_type = "jwt"
|
||||
token_explicit_max_ttl = var.ci_token_ttl_seconds
|
||||
token_no_default_policy = true
|
||||
token_policies = [each.value.policy]
|
||||
user_claim = var.ci_jwt_user_claim
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
variable "ci_database_bound_claims" {
|
||||
description = "Exact repository, protected-ref, and database-job claims for the database role."
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "ci_jwt_bound_audiences" {
|
||||
description = "Exact CI JWT audiences."
|
||||
type = set(string)
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "ci_jwt_bound_issuer" {
|
||||
description = "Expected CI JWT issuer."
|
||||
type = string
|
||||
default = null
|
||||
nullable = true
|
||||
}
|
||||
|
||||
variable "ci_jwt_oidc_discovery_url" {
|
||||
description = "CI OIDC discovery URL. Null leaves external CI authentication disabled."
|
||||
type = string
|
||||
default = null
|
||||
nullable = true
|
||||
}
|
||||
|
||||
variable "ci_jwt_user_claim" {
|
||||
description = "JWT claim used as the Vault identity alias."
|
||||
type = string
|
||||
default = "sub"
|
||||
}
|
||||
|
||||
variable "ci_token_ttl_seconds" {
|
||||
description = "Maximum lifetime for delegated CI tokens."
|
||||
type = number
|
||||
default = 1800
|
||||
|
||||
validation {
|
||||
condition = (
|
||||
var.ci_token_ttl_seconds >= 60 &&
|
||||
var.ci_token_ttl_seconds <= 3600 &&
|
||||
floor(var.ci_token_ttl_seconds) == var.ci_token_ttl_seconds
|
||||
)
|
||||
error_message = "ci_token_ttl_seconds must be a whole number between 60 and 3600."
|
||||
}
|
||||
}
|
||||
|
||||
variable "ci_workloads_bound_claims" {
|
||||
description = "Exact repository, protected-ref, and workloads-job claims for the workloads role."
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "kubernetes_host" {
|
||||
description = "Kubernetes TokenReview API address."
|
||||
type = string
|
||||
default = "https://kubernetes.default.svc.cluster.local:443"
|
||||
}
|
||||
|
||||
variable "vault_addr" {
|
||||
description = "Vault API address reachable by the foundation operator."
|
||||
type = string
|
||||
default = "http://127.0.0.1:8200"
|
||||
}
|
||||
|
||||
variable "vault_token" {
|
||||
description = "Short-lived bootstrap or security-administrator token."
|
||||
type = string
|
||||
sensitive = true
|
||||
ephemeral = true
|
||||
}
|
||||
Reference in New Issue
Block a user