Wire Forge-native release infrastructure
Some checks failed
Cache: Publish Nix / Publish Nix Cache (push) Waiting to run
Build Rust / Cargo Test (push) Successful in 4m14s
Build Site / Next.js Build (push) Failing after 6s
Infra: OpenTofu / OpenTofu (grafana) (push) Successful in 5s
Lint Governance / BEP Metadata (push) Successful in 0s
Build: Android / Android Rust Core Stub (push) Failing after 23s
Some checks failed
Cache: Publish Nix / Publish Nix Cache (push) Waiting to run
Build Rust / Cargo Test (push) Successful in 4m14s
Build Site / Next.js Build (push) Failing after 6s
Infra: OpenTofu / OpenTofu (grafana) (push) Successful in 5s
Lint Governance / BEP Metadata (push) Successful in 0s
Build: Android / Android Rust Core Stub (push) Failing after 23s
This commit is contained in:
parent
97c569fb35
commit
002bd382e9
199 changed files with 14268 additions and 185 deletions
4
infra/grafana/.gitignore
vendored
Normal file
4
infra/grafana/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
.terraform/
|
||||
backend.hcl
|
||||
terraform.tfvars
|
||||
*.tfplan
|
||||
29
infra/grafana/.terraform.lock.hcl
generated
Normal file
29
infra/grafana/.terraform.lock.hcl
generated
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# This file is maintained automatically by "tofu init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.opentofu.org/grafana/grafana" {
|
||||
version = "4.37.0"
|
||||
constraints = ">= 4.36.0, < 5.0.0"
|
||||
hashes = [
|
||||
"h1:EqimatXUHgE9jIko0ifoR+kOLVCQlLGvj13iH5GoCnE=",
|
||||
"zh:02dcb17aae3e97a4b946e28267a80845b130e4a062ebd0f7dbf15ef1f65713e7",
|
||||
"zh:07b57580fe411de152a3b29a5ac1e0ff2541f9a62904c5b9703cf7e3c63bbbfa",
|
||||
"zh:089b7ce58b919732bedcb95e2a5d99553956a3637e89bb0b814de82050cca635",
|
||||
"zh:08c15290aafeec6945cd5321b5cdc2adb5f077b26fb77225984e3acd055293f7",
|
||||
"zh:0e0d0f89d8f2ca89541b5981c9388263af15c83c98f57d831a2dc162f1586af5",
|
||||
"zh:3363c8c5a987951ec6bf69bfdd1293d2fb1e72aa94e272280b69ef2dd3988e34",
|
||||
"zh:479e2f82ad2b7522851c828f33d6ab7a0cfce44304f25b5b1f5ddabed0fb879c",
|
||||
"zh:4d9d619b6c6c619f58624a9fba8414201fe68403eb5e1588b95d8c31ecc9c71e",
|
||||
"zh:545f24ef5b1d87d641a577556ada8b0ac67417e0bc51ac01238898fb6429d67a",
|
||||
"zh:8b43993924d12d946e002acbf54c8bf7f05a269436ad085c45317c6020ba10df",
|
||||
"zh:965cf4bf12a8a8e858d791743716433d71a17ed2a15e6cd183dc3d7349be24e3",
|
||||
"zh:9a1205bc5cf01f143611afb97934f2cbc5c9225f6bc5c279f6424ea4da96151b",
|
||||
"zh:b49d28a1a0226799a9421b50dcdc2c85de5858d42f92c8443745d80c9dd5e0cd",
|
||||
"zh:b618aa9e7272447b2e1ce46bb399e39d21f605de48c756b6af0b42fddca50674",
|
||||
"zh:b9dac24ca39fcc95575f0051390c71dabdd540ea2c365bb87b5621e5d6219594",
|
||||
"zh:ba3b79a13ece3bd782a79afe590200940ce740eee809ed4cb9e1086253dba1d2",
|
||||
"zh:ba3f77877ed139cbf0d9af9d1b84e827c49fc42c60e545c67c9d05d1e63e9b0c",
|
||||
"zh:ba84824517ef8bb78122f7bdcbd49bbdf289df9b82040dfbee913a73f29fc158",
|
||||
"zh:de116fd3a62a20f183793c760fa4d9521cee7d718165924cc9b0012e297636b5",
|
||||
]
|
||||
}
|
||||
46
infra/grafana/README.md
Normal file
46
infra/grafana/README.md
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# Burrow Grafana API Control Plane
|
||||
|
||||
This OpenTofu stack creates Grafana folders and dashboards through the official
|
||||
`grafana/grafana` provider.
|
||||
|
||||
This is not a Grafana deployment mechanism. NixOS owns the Grafana service,
|
||||
authentication, data sources, and boot-time provisioning.
|
||||
|
||||
## Run
|
||||
|
||||
Use the repository wrapper so the OpenTofu runtime comes from Nix:
|
||||
|
||||
```sh
|
||||
Scripts/grafana-tofu.sh init -backend-config=backend.hcl
|
||||
Scripts/grafana-tofu.sh plan -var-file=terraform.tfvars
|
||||
```
|
||||
|
||||
For a local syntax check without remote state:
|
||||
|
||||
```sh
|
||||
Scripts/grafana-tofu.sh init -backend=false
|
||||
Scripts/grafana-tofu.sh validate
|
||||
```
|
||||
|
||||
Manual Forgejo runs use `.forgejo/workflows/infra-opentofu.yml`. Plan, apply,
|
||||
and import modes need `GRAFANA_AUTH` or a decrypted admin password so the
|
||||
Grafana provider can reach the API.
|
||||
|
||||
## Adoption
|
||||
|
||||
The default stack manages the checked-in dashboards:
|
||||
|
||||
```text
|
||||
services/grafana/dashboards/burrow-overview.json
|
||||
services/grafana/dashboards/headscale.json
|
||||
services/grafana/dashboards/observability.json
|
||||
```
|
||||
|
||||
Import existing production objects before enabling apply:
|
||||
|
||||
```sh
|
||||
Scripts/grafana-tofu.sh import 'grafana_folder.folders["burrow"]' 'burrow'
|
||||
Scripts/grafana-tofu.sh import 'grafana_dashboard.dashboards["burrow-overview"]' 'burrow-overview'
|
||||
Scripts/grafana-tofu.sh import 'grafana_dashboard.dashboards["burrow-headscale"]' 'burrow-headscale'
|
||||
Scripts/grafana-tofu.sh import 'grafana_dashboard.dashboards["burrow-observability"]' 'burrow-observability'
|
||||
```
|
||||
5
infra/grafana/backend.hcl.example
Normal file
5
infra/grafana/backend.hcl.example
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
bucket = "burrow-opentofu-state"
|
||||
key = "grafana/grafana.tfstate"
|
||||
region = "us-west-2"
|
||||
dynamodb_table = "burrow-opentofu-locks"
|
||||
encrypt = true
|
||||
17
infra/grafana/grafana.tf
Normal file
17
infra/grafana/grafana.tf
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
resource "grafana_folder" "folders" {
|
||||
for_each = var.manage_grafana_dashboards ? var.grafana_folders : {}
|
||||
|
||||
title = each.value.title
|
||||
uid = each.value.uid
|
||||
}
|
||||
|
||||
resource "grafana_dashboard" "dashboards" {
|
||||
for_each = var.manage_grafana_dashboards ? var.grafana_dashboards : {}
|
||||
|
||||
config_json = file(startswith(each.value.path, "/") ? each.value.path : "${path.module}/${each.value.path}")
|
||||
folder = each.value.folder
|
||||
message = each.value.message
|
||||
overwrite = each.value.overwrite
|
||||
|
||||
depends_on = [grafana_folder.folders]
|
||||
}
|
||||
9
infra/grafana/outputs.tf
Normal file
9
infra/grafana/outputs.tf
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
output "grafana_dashboard_urls" {
|
||||
description = "Grafana dashboard URLs managed by this stack."
|
||||
value = { for name, dashboard in grafana_dashboard.dashboards : name => dashboard.url }
|
||||
}
|
||||
|
||||
output "grafana_folder_uids" {
|
||||
description = "Grafana folder UIDs managed by this stack."
|
||||
value = { for name, folder in grafana_folder.folders : name => folder.uid }
|
||||
}
|
||||
4
infra/grafana/providers.tf
Normal file
4
infra/grafana/providers.tf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
provider "grafana" {
|
||||
url = var.grafana_url
|
||||
auth = var.grafana_auth
|
||||
}
|
||||
18
infra/grafana/terraform.tfvars.example
Normal file
18
infra/grafana/terraform.tfvars.example
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
grafana_url = "https://graphs.burrow.net"
|
||||
|
||||
manage_grafana_dashboards = true
|
||||
|
||||
grafana_folders = {
|
||||
burrow = {
|
||||
title = "Burrow"
|
||||
uid = "burrow"
|
||||
}
|
||||
}
|
||||
|
||||
grafana_dashboards = {
|
||||
burrow-overview = {
|
||||
path = "../../services/grafana/dashboards/burrow-overview.json"
|
||||
folder = "burrow"
|
||||
overwrite = true
|
||||
}
|
||||
}
|
||||
56
infra/grafana/variables.tf
Normal file
56
infra/grafana/variables.tf
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
variable "grafana_url" {
|
||||
description = "Grafana base URL."
|
||||
type = string
|
||||
default = "https://graphs.burrow.net"
|
||||
}
|
||||
|
||||
variable "grafana_auth" {
|
||||
description = "Grafana API token or basic auth in username:password form. Prefer GRAFANA_AUTH in CI."
|
||||
type = string
|
||||
default = null
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "manage_grafana_dashboards" {
|
||||
description = "Manage Grafana folders and dashboards through the Grafana API."
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "grafana_folders" {
|
||||
description = "Grafana folders to create before dashboards."
|
||||
type = map(object({
|
||||
title = string
|
||||
uid = optional(string)
|
||||
}))
|
||||
default = {
|
||||
burrow = {
|
||||
title = "Burrow"
|
||||
uid = "burrow"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variable "grafana_dashboards" {
|
||||
description = "Grafana dashboards to manage from checked-in JSON."
|
||||
type = map(object({
|
||||
path = string
|
||||
folder = optional(string)
|
||||
message = optional(string, "Managed by Burrow OpenTofu")
|
||||
overwrite = optional(bool, true)
|
||||
}))
|
||||
default = {
|
||||
burrow-overview = {
|
||||
path = "../../services/grafana/dashboards/burrow-overview.json"
|
||||
folder = "burrow"
|
||||
}
|
||||
burrow-headscale = {
|
||||
path = "../../services/grafana/dashboards/headscale.json"
|
||||
folder = "burrow"
|
||||
}
|
||||
burrow-observability = {
|
||||
path = "../../services/grafana/dashboards/observability.json"
|
||||
folder = "burrow"
|
||||
}
|
||||
}
|
||||
}
|
||||
12
infra/grafana/versions.tf
Normal file
12
infra/grafana/versions.tf
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
terraform {
|
||||
required_version = ">= 1.8.0"
|
||||
|
||||
required_providers {
|
||||
grafana = {
|
||||
source = "grafana/grafana"
|
||||
version = ">= 4.36.0, < 5.0"
|
||||
}
|
||||
}
|
||||
|
||||
backend "s3" {}
|
||||
}
|
||||
5
infra/identity/.gitignore
vendored
Normal file
5
infra/identity/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
.terraform/
|
||||
backend.hcl
|
||||
terraform.tfstate
|
||||
terraform.tfstate.backup
|
||||
*.tfplan
|
||||
20
infra/identity/.terraform.lock.hcl
generated
Normal file
20
infra/identity/.terraform.lock.hcl
generated
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# This file is maintained automatically by "tofu init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.opentofu.org/hashicorp/google" {
|
||||
version = "6.50.0"
|
||||
constraints = ">= 5.40.0, < 7.0.0"
|
||||
hashes = [
|
||||
"h1:MAAe4zFFdqS9M5rpmJK/vKgdb6ZMD/s/0Xd97yTDipA=",
|
||||
"zh:1d4695f807d998f11fcdcfa174766287b82a8093513af857bcdad2d81c642480",
|
||||
"zh:3173ac5df0294624d113812e49e2a55714aff7db617488168cecdf4168df9e29",
|
||||
"zh:34d2b3d44c23bd6354fc4ab5917b302872ea1ab8de107034567f955b1717fa5b",
|
||||
"zh:3a77f3cc2f3664cd5aaeeef4d044e6ec1695a079588fffec3ca03953664e5f04",
|
||||
"zh:6b444e4b629ea8dc8cb112a39dde098dc5584d26d6de4177558f556a9a226696",
|
||||
"zh:96545c8cd4d3a57069c5d1799eab5aedd887e16d98b5559a195f6d2c2d9bc674",
|
||||
"zh:ba464caafde95ee16671d6b5ec90f053ed77a9d06c567456db6efd9160fa3165",
|
||||
"zh:d876938e5b0d3f57a984d9be72467995f87fef6569968623415dc51d9f54d30b",
|
||||
"zh:dfd908d873e314ab807d0abc9cfd42d2611cd06dc1b9ec719ebdbb738e8e68d6",
|
||||
"zh:f9f16819a7738d564afd45fd169ba61004ec4e4e7089d2a4950cb8895be1fe1f",
|
||||
]
|
||||
}
|
||||
145
infra/identity/README.md
Normal file
145
infra/identity/README.md
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
# Burrow Identity KMS and WIF
|
||||
|
||||
This OpenTofu stack owns Google Cloud KMS and Workload Identity Federation
|
||||
resources for Burrow identity and release automation.
|
||||
|
||||
It may manage:
|
||||
|
||||
- the `burrow-identity` Google KMS key ring
|
||||
- non-exportable KMS signing keys for Authentik, the SAML CA, and release
|
||||
signing
|
||||
- dedicated non-exportable KMS signing keys for APT, RPM, pacman, Flatpak, and
|
||||
AUR source release surfaces
|
||||
- a non-exportable KMS signing key for the Apple Developer ID Application CSR
|
||||
path (`apple-developer-id-application`)
|
||||
- a non-exportable KMS signing key for the Apple iOS Distribution CSR path
|
||||
(`apple-ios-distribution`)
|
||||
- a non-exportable software KMS signing key for Sparkle Ed25519 appcast
|
||||
signatures (`sparkle-ed25519`)
|
||||
- an Authentik-backed WIF pool/provider for Forgejo runners
|
||||
- the runner service account and its narrow IAM bindings for the
|
||||
`burrow-automation` group and the Authentik WIF client ID
|
||||
`google-wif.burrow.net`
|
||||
|
||||
It must not manage:
|
||||
|
||||
- Authentik private signing key material
|
||||
- OpenBao bootstrap tokens
|
||||
- store credentials or service-account JSON keys
|
||||
|
||||
Forgejo CI should authenticate with `Scripts/ci/google-wif-auth.sh`. That helper
|
||||
uses Authentik to obtain an OIDC token and lets Google WIF impersonate
|
||||
`burrow-forgejo-runner@project-88c23ce9-918a-470a-b33.iam.gserviceaccount.com`
|
||||
in project `project-88c23ce9-918a-470a-b33`.
|
||||
|
||||
## Apple Developer ID CSR
|
||||
|
||||
`Scripts/apple/google-kms-csr.py` builds a standard PKCS#10 CSR using the public
|
||||
key from `projects/project-88c23ce9-918a-470a-b33/locations/global/keyRings/burrow-identity/cryptoKeys/apple-developer-id-application`
|
||||
and asks Google KMS to sign the CSR payload. The private key remains
|
||||
non-exportable in KMS.
|
||||
|
||||
Forgejo can generate the Apple-uploadable CSR with:
|
||||
|
||||
```sh
|
||||
Scripts/forgejo-dispatch.sh apple-developer-id-kms-csr.yml --ref main
|
||||
```
|
||||
|
||||
Apple currently documents Developer ID certificate creation through the
|
||||
Developer website or Xcode. After downloading the returned `.cer`, keep it with
|
||||
release artifacts; do not convert this key path into an exportable `.p12`.
|
||||
|
||||
The active certificate created from this path is Apple certificate
|
||||
`9JKN6HXBHC`, stored at
|
||||
`Apple/Certificates/developer-id-application-9JKN6HXBHC.cer`. Its public key
|
||||
matches KMS key version `apple-developer-id-application/cryptoKeyVersions/1`.
|
||||
|
||||
## Apple iOS Distribution CSR
|
||||
|
||||
The iOS App Store distribution certificate uses the same CSR builder with the
|
||||
`apple-ios-distribution` KMS key:
|
||||
|
||||
```sh
|
||||
Scripts/apple/google-kms-csr.py \
|
||||
--kms-key apple-ios-distribution \
|
||||
--common-name "Burrow iOS Distribution" \
|
||||
--output ios-distribution.certSigningRequest
|
||||
```
|
||||
|
||||
The App Store Connect API can create the certificate from that CSR:
|
||||
|
||||
```sh
|
||||
Scripts/apple/create-asc-certificate.mjs \
|
||||
--certificate-type IOS_DISTRIBUTION \
|
||||
--csr-file ios-distribution.certSigningRequest \
|
||||
--out-dir Apple/Certificates
|
||||
```
|
||||
|
||||
The active certificate created from this path is Apple certificate
|
||||
`3G42677598`, stored at `Apple/Certificates/ios-distribution-3G42677598.cer`.
|
||||
Its public key matches KMS key version
|
||||
`apple-ios-distribution/cryptoKeyVersions/1`.
|
||||
|
||||
## Sparkle Appcast Signing
|
||||
|
||||
Sparkle appcasts use `sparkle-ed25519`, a non-exportable Google KMS key with
|
||||
algorithm `EC_SIGN_ED25519` and software protection level. Google KMS rejects
|
||||
Ed25519 at HSM protection level, so this key is intentionally separate from the
|
||||
HSM RSA keys used for Apple certificate CSRs and repository signing.
|
||||
|
||||
The public EdDSA key embedded in macOS release builds is:
|
||||
|
||||
```text
|
||||
Myv9ZNZT6YGKMtMezh52ra4WqaeEKc4VlvVU0evhJeI=
|
||||
```
|
||||
|
||||
The appcast signer is:
|
||||
|
||||
```sh
|
||||
Scripts/sparkle/sign-appcast-kms.py \
|
||||
--appcast dist/builds/<build>/sparkle/<channel>/appcast.xml \
|
||||
--artifact-dir dist/builds/<build>/sparkle/<channel>
|
||||
```
|
||||
|
||||
## Run
|
||||
|
||||
```sh
|
||||
Scripts/identity-tofu.sh init -backend-config=backend.hcl
|
||||
Scripts/identity-tofu.sh plan -var-file=terraform.tfvars
|
||||
```
|
||||
|
||||
For local syntax work without remote state:
|
||||
|
||||
```sh
|
||||
Scripts/identity-tofu.sh init -backend=false
|
||||
Scripts/identity-tofu.sh validate
|
||||
```
|
||||
|
||||
Start with `manage_google = false`. Import existing resources before enabling
|
||||
management, then turn on one resource group at a time.
|
||||
|
||||
## Bootstrap Imports
|
||||
|
||||
The first live Apple Developer ID pass may need these resources imported before
|
||||
an `identity` apply can own them:
|
||||
|
||||
```sh
|
||||
Scripts/identity-tofu.sh import 'google_kms_key_ring.identity[0]' \
|
||||
projects/project-88c23ce9-918a-470a-b33/locations/global/keyRings/burrow-identity
|
||||
|
||||
Scripts/identity-tofu.sh import 'google_kms_crypto_key.signing["apple_developer_id_application"]' \
|
||||
projects/project-88c23ce9-918a-470a-b33/locations/global/keyRings/burrow-identity/cryptoKeys/apple-developer-id-application
|
||||
|
||||
Scripts/identity-tofu.sh import 'google_kms_crypto_key.signing["apple_ios_distribution"]' \
|
||||
projects/project-88c23ce9-918a-470a-b33/locations/global/keyRings/burrow-identity/cryptoKeys/apple-ios-distribution
|
||||
|
||||
Scripts/identity-tofu.sh import 'google_kms_crypto_key.signing["sparkle_ed25519"]' \
|
||||
projects/project-88c23ce9-918a-470a-b33/locations/global/keyRings/burrow-identity/cryptoKeys/sparkle-ed25519
|
||||
|
||||
Scripts/identity-tofu.sh import 'google_service_account.runner[0]' \
|
||||
projects/project-88c23ce9-918a-470a-b33/serviceAccounts/burrow-forgejo-runner@project-88c23ce9-918a-470a-b33.iam.gserviceaccount.com
|
||||
```
|
||||
|
||||
Run imports with the same `manage_google`, key-ring, and service-account
|
||||
variables that will be used for apply. Key-level IAM bindings should be imported
|
||||
after the signing keys and service account exist.
|
||||
4
infra/identity/backend.hcl.example
Normal file
4
infra/identity/backend.hcl.example
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
bucket = "burrow-opentofu-state"
|
||||
key = "identity/identity.tfstate"
|
||||
region = "us-west-2"
|
||||
encrypt = true
|
||||
173
infra/identity/google.tf
Normal file
173
infra/identity/google.tf
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
locals {
|
||||
google_required_services = toset([
|
||||
"cloudkms.googleapis.com",
|
||||
"cloudresourcemanager.googleapis.com",
|
||||
"iam.googleapis.com",
|
||||
"iamcredentials.googleapis.com",
|
||||
"sts.googleapis.com",
|
||||
])
|
||||
|
||||
google_existing_kms_key_ring_id = var.google_kms_key_ring_id != "" ? var.google_kms_key_ring_id : "projects/${var.google_project_id}/locations/${var.google_kms_key_ring_location}/keyRings/${var.google_kms_key_ring_name}"
|
||||
|
||||
google_kms_key_ring_id = var.manage_google_kms_key_ring ? (
|
||||
google_kms_key_ring.identity[0].id
|
||||
) : local.google_existing_kms_key_ring_id
|
||||
|
||||
google_runner_service_account_email = var.manage_google_runner_service_account ? (
|
||||
google_service_account.runner[0].email
|
||||
) : var.google_runner_service_account_email
|
||||
|
||||
google_runner_wif_group_members = var.google_wif_runner_group != "" ? (
|
||||
toset([
|
||||
"principalSet://iam.googleapis.com/projects/${var.google_project_number}/locations/global/workloadIdentityPools/${var.google_wif_pool_id}/attribute.burrow_groups/${var.google_wif_runner_group}"
|
||||
])
|
||||
) : (
|
||||
toset([])
|
||||
)
|
||||
|
||||
google_runner_wif_client_members = var.google_wif_runner_client_id != "" ? (
|
||||
toset([
|
||||
"principalSet://iam.googleapis.com/projects/${var.google_project_number}/locations/global/workloadIdentityPools/${var.google_wif_pool_id}/attribute.burrow_client_id/${var.google_wif_runner_client_id}"
|
||||
])
|
||||
) : (
|
||||
toset([])
|
||||
)
|
||||
|
||||
google_runner_wif_members = setunion(
|
||||
local.google_runner_wif_group_members,
|
||||
local.google_runner_wif_client_members,
|
||||
var.google_wif_runner_group == "" && var.google_wif_runner_client_id == "" ? toset([
|
||||
"principalSet://iam.googleapis.com/projects/${var.google_project_number}/locations/global/workloadIdentityPools/${var.google_wif_pool_id}/*"
|
||||
]) : toset([])
|
||||
)
|
||||
|
||||
google_signer_members = setunion(
|
||||
var.google_release_signer_members,
|
||||
toset(["serviceAccount:${local.google_runner_service_account_email}"]),
|
||||
)
|
||||
|
||||
google_signer_grants = var.manage_google ? {
|
||||
for grant in flatten([
|
||||
for key_name in keys(google_kms_crypto_key.signing) : [
|
||||
for member in local.google_signer_members : {
|
||||
id = "${key_name}|${member}"
|
||||
key_name = key_name
|
||||
member = member
|
||||
}
|
||||
]
|
||||
]) : grant.id => grant
|
||||
} : {}
|
||||
}
|
||||
|
||||
resource "google_project_service" "identity" {
|
||||
for_each = var.manage_google ? local.google_required_services : toset([])
|
||||
|
||||
project = var.google_project_id
|
||||
service = each.value
|
||||
disable_on_destroy = false
|
||||
}
|
||||
|
||||
resource "google_kms_key_ring" "identity" {
|
||||
count = var.manage_google && var.manage_google_kms_key_ring ? 1 : 0
|
||||
|
||||
project = var.google_project_id
|
||||
location = var.google_kms_key_ring_location
|
||||
name = var.google_kms_key_ring_name
|
||||
|
||||
depends_on = [google_project_service.identity]
|
||||
}
|
||||
|
||||
resource "google_kms_crypto_key" "signing" {
|
||||
for_each = var.manage_google && local.google_kms_key_ring_id != "" ? var.google_signing_keys : {}
|
||||
|
||||
name = each.value.name
|
||||
key_ring = local.google_kms_key_ring_id
|
||||
purpose = "ASYMMETRIC_SIGN"
|
||||
labels = merge(
|
||||
{
|
||||
project = "burrow"
|
||||
component = "identity"
|
||||
managed_by = "opentofu"
|
||||
},
|
||||
var.tags,
|
||||
try(each.value.labels, {}),
|
||||
)
|
||||
|
||||
version_template {
|
||||
algorithm = try(each.value.algorithm, "RSA_SIGN_PKCS1_2048_SHA256")
|
||||
protection_level = try(each.value.protection_level, "HSM")
|
||||
}
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_service_account" "runner" {
|
||||
count = var.manage_google && var.manage_google_runner_service_account ? 1 : 0
|
||||
|
||||
project = var.google_project_id
|
||||
account_id = var.google_runner_service_account_id
|
||||
display_name = "Burrow Forgejo Runner"
|
||||
description = "Authentik-backed WIF service account for Burrow Forgejo runners."
|
||||
|
||||
depends_on = [google_project_service.identity]
|
||||
}
|
||||
|
||||
resource "google_iam_workload_identity_pool" "burrow" {
|
||||
count = var.manage_google ? 1 : 0
|
||||
|
||||
project = var.google_project_id
|
||||
workload_identity_pool_id = var.google_wif_pool_id
|
||||
display_name = "Burrow Authentik"
|
||||
description = "Authentik-issued identities for Burrow automation."
|
||||
disabled = false
|
||||
|
||||
depends_on = [google_project_service.identity]
|
||||
}
|
||||
|
||||
resource "google_iam_workload_identity_pool_provider" "forgejo_runners" {
|
||||
count = var.manage_google ? 1 : 0
|
||||
|
||||
project = var.google_project_id
|
||||
workload_identity_pool_id = google_iam_workload_identity_pool.burrow[0].workload_identity_pool_id
|
||||
workload_identity_pool_provider_id = var.google_wif_provider_id
|
||||
display_name = "Burrow Forgejo Runners"
|
||||
description = "Authentik OIDC provider for Burrow Forgejo runner cloud access."
|
||||
disabled = false
|
||||
|
||||
attribute_mapping = {
|
||||
"google.subject" = "assertion.sub"
|
||||
"google.groups" = "assertion.groups"
|
||||
"attribute.burrow_subject" = "assertion.sub"
|
||||
"attribute.burrow_username" = "assertion.preferred_username"
|
||||
"attribute.burrow_email" = "assertion.email"
|
||||
"attribute.burrow_groups" = "assertion.groups"
|
||||
"attribute.burrow_client_id" = "assertion.azp"
|
||||
}
|
||||
|
||||
attribute_condition = var.google_wif_attribute_condition
|
||||
|
||||
oidc {
|
||||
issuer_uri = var.google_wif_issuer_uri
|
||||
allowed_audiences = var.google_wif_allowed_audiences
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_service_account_iam_member" "runner_wif" {
|
||||
for_each = var.manage_google ? local.google_runner_wif_members : toset([])
|
||||
|
||||
service_account_id = var.manage_google_runner_service_account ? (
|
||||
google_service_account.runner[0].name
|
||||
) : "projects/${var.google_project_id}/serviceAccounts/${local.google_runner_service_account_email}"
|
||||
role = "roles/iam.workloadIdentityUser"
|
||||
member = each.value
|
||||
}
|
||||
|
||||
resource "google_kms_crypto_key_iam_member" "signer" {
|
||||
for_each = local.google_signer_grants
|
||||
|
||||
crypto_key_id = google_kms_crypto_key.signing[each.value.key_name].id
|
||||
role = "roles/cloudkms.signerVerifier"
|
||||
member = each.value.member
|
||||
}
|
||||
19
infra/identity/outputs.tf
Normal file
19
infra/identity/outputs.tf
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
output "google_kms_key_ring_id" {
|
||||
description = "Google KMS key ring id used by the identity stack."
|
||||
value = local.google_kms_key_ring_id
|
||||
}
|
||||
|
||||
output "google_runner_service_account_email" {
|
||||
description = "Forgejo runner service account email."
|
||||
value = local.google_runner_service_account_email
|
||||
}
|
||||
|
||||
output "google_wif_pool_name" {
|
||||
description = "Google WIF pool resource name when managed."
|
||||
value = try(google_iam_workload_identity_pool.burrow[0].name, null)
|
||||
}
|
||||
|
||||
output "google_signing_key_ids" {
|
||||
description = "Managed Google KMS signing key ids."
|
||||
value = { for name, key in google_kms_crypto_key.signing : name => key.id }
|
||||
}
|
||||
3
infra/identity/providers.tf
Normal file
3
infra/identity/providers.tf
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
provider "google" {
|
||||
project = var.google_project_id
|
||||
}
|
||||
15
infra/identity/terraform.tfvars.example
Normal file
15
infra/identity/terraform.tfvars.example
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
manage_google = false
|
||||
|
||||
google_project_id = "project-88c23ce9-918a-470a-b33"
|
||||
google_project_number = "416198671487"
|
||||
|
||||
# Import the key ring before setting this true if it already exists.
|
||||
manage_google_kms_key_ring = false
|
||||
|
||||
# Create this only after the Authentik WIF provider design is final.
|
||||
manage_google_runner_service_account = false
|
||||
|
||||
google_wif_issuer_uri = "https://auth.burrow.net/application/o/google-cloud/"
|
||||
google_wif_allowed_audiences = ["google-wif.burrow.net"]
|
||||
google_wif_runner_group = "burrow-automation"
|
||||
google_wif_runner_client_id = "google-wif.burrow.net"
|
||||
201
infra/identity/variables.tf
Normal file
201
infra/identity/variables.tf
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
variable "manage_google" {
|
||||
description = "Manage Google Cloud identity and WIF resources."
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "google_project_id" {
|
||||
description = "Google Cloud project id for Burrow identity resources."
|
||||
type = string
|
||||
default = "project-88c23ce9-918a-470a-b33"
|
||||
}
|
||||
|
||||
variable "google_project_number" {
|
||||
description = "Google Cloud project number for IAM principal URIs."
|
||||
type = string
|
||||
default = "416198671487"
|
||||
}
|
||||
|
||||
variable "google_kms_key_ring_name" {
|
||||
description = "Google KMS key ring for identity and release signing keys."
|
||||
type = string
|
||||
default = "burrow-identity"
|
||||
}
|
||||
|
||||
variable "google_kms_key_ring_location" {
|
||||
description = "Google KMS key ring location."
|
||||
type = string
|
||||
default = "global"
|
||||
}
|
||||
|
||||
variable "google_kms_key_ring_id" {
|
||||
description = "Existing Google KMS key ring id. Leave blank to derive it from project, location, and name."
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "manage_google_kms_key_ring" {
|
||||
description = "Create/manage the Google KMS key ring. Import existing rings before enabling this."
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "google_signing_keys" {
|
||||
description = "Non-exportable Burrow signing keys to create in the identity key ring."
|
||||
type = map(object({
|
||||
name = string
|
||||
algorithm = optional(string, "RSA_SIGN_PKCS1_2048_SHA256")
|
||||
protection_level = optional(string, "HSM")
|
||||
labels = optional(map(string), {})
|
||||
}))
|
||||
default = {
|
||||
authentik = {
|
||||
name = "authentik-signing"
|
||||
labels = {
|
||||
key_target = "authentik"
|
||||
}
|
||||
}
|
||||
saml_ca = {
|
||||
name = "saml-ca"
|
||||
labels = {
|
||||
key_target = "saml-ca"
|
||||
}
|
||||
}
|
||||
release = {
|
||||
name = "release-signing"
|
||||
labels = {
|
||||
key_target = "release-signing"
|
||||
}
|
||||
}
|
||||
package_apt_repository = {
|
||||
name = "package-apt-repository"
|
||||
labels = {
|
||||
key_target = "package-apt-repository"
|
||||
repo_format = "apt"
|
||||
}
|
||||
}
|
||||
package_rpm_repository = {
|
||||
name = "package-rpm-repository"
|
||||
labels = {
|
||||
key_target = "package-rpm-repository"
|
||||
repo_format = "rpm"
|
||||
}
|
||||
}
|
||||
package_pacman_repository = {
|
||||
name = "package-pacman-repository"
|
||||
labels = {
|
||||
key_target = "package-pacman-repository"
|
||||
repo_format = "pacman"
|
||||
}
|
||||
}
|
||||
package_flatpak_repository = {
|
||||
name = "package-flatpak-repository"
|
||||
labels = {
|
||||
key_target = "package-flatpak-repository"
|
||||
repo_format = "flatpak"
|
||||
}
|
||||
}
|
||||
package_aur_source = {
|
||||
name = "package-aur-source"
|
||||
labels = {
|
||||
key_target = "package-aur-source"
|
||||
repo_format = "aur"
|
||||
}
|
||||
}
|
||||
apple_developer_id_application = {
|
||||
name = "apple-developer-id-application"
|
||||
labels = {
|
||||
key_target = "apple-developer-id-application"
|
||||
apple_cert_type = "developer-id-application"
|
||||
}
|
||||
}
|
||||
apple_ios_distribution = {
|
||||
name = "apple-ios-distribution"
|
||||
labels = {
|
||||
key_target = "apple-ios-distribution"
|
||||
apple_cert_type = "ios-distribution"
|
||||
}
|
||||
}
|
||||
sparkle_ed25519 = {
|
||||
name = "sparkle-ed25519"
|
||||
algorithm = "EC_SIGN_ED25519"
|
||||
protection_level = "SOFTWARE"
|
||||
labels = {
|
||||
key_target = "sparkle-ed25519"
|
||||
release_surface = "sparkle"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variable "google_release_signer_members" {
|
||||
description = "Additional IAM members allowed to sign/verify with Burrow release and package repository keys."
|
||||
type = set(string)
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "manage_google_runner_service_account" {
|
||||
description = "Create/manage the Forgejo runner service account for WIF."
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "google_runner_service_account_id" {
|
||||
description = "Google service account id for Authentik-backed Forgejo runners."
|
||||
type = string
|
||||
default = "burrow-forgejo-runner"
|
||||
}
|
||||
|
||||
variable "google_runner_service_account_email" {
|
||||
description = "Existing runner service account email when not managed here."
|
||||
type = string
|
||||
default = "burrow-forgejo-runner@project-88c23ce9-918a-470a-b33.iam.gserviceaccount.com"
|
||||
}
|
||||
|
||||
variable "google_wif_pool_id" {
|
||||
description = "Workload Identity Federation pool id for Burrow automation."
|
||||
type = string
|
||||
default = "burrow-authentik"
|
||||
}
|
||||
|
||||
variable "google_wif_provider_id" {
|
||||
description = "Workload Identity Federation provider id for Authentik-issued OIDC tokens."
|
||||
type = string
|
||||
default = "forgejo-runners"
|
||||
}
|
||||
|
||||
variable "google_wif_issuer_uri" {
|
||||
description = "Authentik OIDC issuer used by Google WIF."
|
||||
type = string
|
||||
default = "https://auth.burrow.net/application/o/google-cloud/"
|
||||
}
|
||||
|
||||
variable "google_wif_allowed_audiences" {
|
||||
description = "Allowed audiences for Authentik-issued WIF tokens."
|
||||
type = list(string)
|
||||
default = ["google-wif.burrow.net"]
|
||||
}
|
||||
|
||||
variable "google_wif_runner_group" {
|
||||
description = "Mapped Authentik group allowed to assume the runner service account. Empty allows the whole pool."
|
||||
type = string
|
||||
default = "burrow-automation"
|
||||
}
|
||||
|
||||
variable "google_wif_runner_client_id" {
|
||||
description = "Mapped Authentik OIDC client ID allowed to assume the runner service account for machine-to-machine CI. Empty disables this grant."
|
||||
type = string
|
||||
default = "google-wif.burrow.net"
|
||||
}
|
||||
|
||||
variable "google_wif_attribute_condition" {
|
||||
description = "CEL condition applied to Authentik-issued WIF tokens."
|
||||
type = string
|
||||
default = "assertion.iss == 'https://auth.burrow.net/application/o/google-cloud/'"
|
||||
}
|
||||
|
||||
variable "tags" {
|
||||
description = "Additional labels applied to supported Google resources."
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
12
infra/identity/versions.tf
Normal file
12
infra/identity/versions.tf
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
terraform {
|
||||
required_version = ">= 1.8.0"
|
||||
|
||||
required_providers {
|
||||
google = {
|
||||
source = "hashicorp/google"
|
||||
version = ">= 5.40.0, < 7.0"
|
||||
}
|
||||
}
|
||||
|
||||
backend "s3" {}
|
||||
}
|
||||
5
infra/openbao/.gitignore
vendored
Normal file
5
infra/openbao/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
.terraform/
|
||||
backend.hcl
|
||||
terraform.tfstate
|
||||
terraform.tfstate.backup
|
||||
*.tfplan
|
||||
43
infra/openbao/.terraform.lock.hcl
generated
Normal file
43
infra/openbao/.terraform.lock.hcl
generated
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# This file is maintained automatically by "tofu init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.opentofu.org/hashicorp/google" {
|
||||
version = "6.50.0"
|
||||
constraints = ">= 5.40.0, < 7.0.0"
|
||||
hashes = [
|
||||
"h1:MAAe4zFFdqS9M5rpmJK/vKgdb6ZMD/s/0Xd97yTDipA=",
|
||||
"zh:1d4695f807d998f11fcdcfa174766287b82a8093513af857bcdad2d81c642480",
|
||||
"zh:3173ac5df0294624d113812e49e2a55714aff7db617488168cecdf4168df9e29",
|
||||
"zh:34d2b3d44c23bd6354fc4ab5917b302872ea1ab8de107034567f955b1717fa5b",
|
||||
"zh:3a77f3cc2f3664cd5aaeeef4d044e6ec1695a079588fffec3ca03953664e5f04",
|
||||
"zh:6b444e4b629ea8dc8cb112a39dde098dc5584d26d6de4177558f556a9a226696",
|
||||
"zh:96545c8cd4d3a57069c5d1799eab5aedd887e16d98b5559a195f6d2c2d9bc674",
|
||||
"zh:ba464caafde95ee16671d6b5ec90f053ed77a9d06c567456db6efd9160fa3165",
|
||||
"zh:d876938e5b0d3f57a984d9be72467995f87fef6569968623415dc51d9f54d30b",
|
||||
"zh:dfd908d873e314ab807d0abc9cfd42d2611cd06dc1b9ec719ebdbb738e8e68d6",
|
||||
"zh:f9f16819a7738d564afd45fd169ba61004ec4e4e7089d2a4950cb8895be1fe1f",
|
||||
]
|
||||
}
|
||||
|
||||
provider "registry.opentofu.org/hashicorp/vault" {
|
||||
version = "5.9.0"
|
||||
constraints = ">= 4.5.0, < 6.0.0"
|
||||
hashes = [
|
||||
"h1:9U4AT36IqNYXfQMR7nf+oywulVKYua473W4NmuwVb7M=",
|
||||
"zh:03cb46fab606872e3fc06e27f8d14b911b7a9781d0f32af335c7287a9dbe4f4f",
|
||||
"zh:1e69677b09329b009a8791f9cbb556790fb4dbddc1360f953345259892dc6c5b",
|
||||
"zh:4e5a3dc82e70d88f5671968b7f2217163815be259d0b26dfcc9a897409c491fa",
|
||||
"zh:51b82b358fe52099ef29cae62e2040ae0224ceea40a7dfdf0b69de90fb0233a8",
|
||||
"zh:60ec3477fbe712b3269eec2e4d9d5921e2e68d0861d77e11e91bea088aab27bf",
|
||||
"zh:64e58a3e3fd61005b54d71eee792b8576ab42ea4cd711515043a3ac0d8bc7919",
|
||||
"zh:69142fb83aff31e27f5b3d9e7afb7594013b63e490cfcef8678fd0d724609359",
|
||||
"zh:6f2e58ba6bd41c4d9e0e752e55eb7d86104f9d786cf57fa8dc960c61b574f9ce",
|
||||
"zh:73390b7cb186310e5aaf4a1baaa639ab0302a8da4db130b4e28ebced3ba103fe",
|
||||
"zh:a2214b3fda93cfed89ba1a7d714fe8b015176927605b215365978efdaae1c6db",
|
||||
"zh:b275ee0f0a63765364d2e6979e3a11464d2c2ce6d3ce9bd15b3f503aea6f12b0",
|
||||
"zh:bb2cd553b1c589a0f6f5b0575e1b176a9d91c1b20c6375d1afd63bb2a47fd014",
|
||||
"zh:bc38e48ae5cdb8219fef326366f46e6a25e7f77f9bbcd9bdc80c7cb960de81ed",
|
||||
"zh:e54af55cf667759a400af07b927b5034f1733e6527f9352b84001205561dcf9e",
|
||||
"zh:f617688ef4568c0de8a28d5e1fa3714fb2eeab9f8cf7bcf20af42a1a511844be",
|
||||
]
|
||||
}
|
||||
36
infra/openbao/README.md
Normal file
36
infra/openbao/README.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Burrow OpenBao Control Plane
|
||||
|
||||
This OpenTofu stack manages non-secret infrastructure around
|
||||
`vault.burrow.net`.
|
||||
|
||||
It may manage:
|
||||
|
||||
- Google KMS seal-wrapping keys for OpenBao
|
||||
- the OpenBao `age/` KV v2 mount used for migrated runtime secrets
|
||||
- OpenBao policies and Authentik OIDC roles
|
||||
- AppRole auth scaffolding for machine consumers
|
||||
|
||||
It must not manage:
|
||||
|
||||
- secret values
|
||||
- root tokens
|
||||
- AppRole SecretIDs
|
||||
- release-signing key material
|
||||
|
||||
## Run
|
||||
|
||||
```sh
|
||||
Scripts/openbao-tofu.sh init -backend-config=backend.hcl
|
||||
Scripts/openbao-tofu.sh plan -var-file=terraform.tfvars
|
||||
```
|
||||
|
||||
For local syntax work:
|
||||
|
||||
```sh
|
||||
Scripts/openbao-tofu.sh init -backend=false
|
||||
Scripts/openbao-tofu.sh validate
|
||||
```
|
||||
|
||||
The Vault/OpenBao provider reads `VAULT_TOKEN` from the environment. Keep
|
||||
`manage_openbao_config = false` until `vault.burrow.net` is initialized and an
|
||||
operator token is available out of band.
|
||||
4
infra/openbao/backend.hcl.example
Normal file
4
infra/openbao/backend.hcl.example
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
bucket = "burrow-opentofu-state"
|
||||
key = "openbao/openbao.tfstate"
|
||||
region = "us-west-2"
|
||||
encrypt = true
|
||||
65
infra/openbao/google.tf
Normal file
65
infra/openbao/google.tf
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
locals {
|
||||
google_required_services = toset([
|
||||
"cloudkms.googleapis.com",
|
||||
"cloudresourcemanager.googleapis.com",
|
||||
"iam.googleapis.com",
|
||||
])
|
||||
|
||||
google_existing_kms_key_ring_id = var.google_kms_key_ring_id != "" ? var.google_kms_key_ring_id : "projects/${var.google_project_id}/locations/${var.google_kms_key_ring_location}/keyRings/${var.google_kms_key_ring_name}"
|
||||
|
||||
google_kms_key_ring_id = var.manage_google_kms_key_ring ? (
|
||||
google_kms_key_ring.openbao[0].id
|
||||
) : local.google_existing_kms_key_ring_id
|
||||
}
|
||||
|
||||
resource "google_project_service" "openbao" {
|
||||
for_each = var.manage_google ? local.google_required_services : toset([])
|
||||
|
||||
project = var.google_project_id
|
||||
service = each.value
|
||||
disable_on_destroy = false
|
||||
}
|
||||
|
||||
resource "google_kms_key_ring" "openbao" {
|
||||
count = var.manage_google && var.manage_google_kms_key_ring ? 1 : 0
|
||||
|
||||
project = var.google_project_id
|
||||
location = var.google_kms_key_ring_location
|
||||
name = var.google_kms_key_ring_name
|
||||
|
||||
depends_on = [google_project_service.openbao]
|
||||
}
|
||||
|
||||
resource "google_kms_crypto_key" "openbao_seal" {
|
||||
count = var.manage_google && local.google_kms_key_ring_id != "" ? 1 : 0
|
||||
|
||||
name = var.google_openbao_seal_key_name
|
||||
key_ring = local.google_kms_key_ring_id
|
||||
purpose = "ENCRYPT_DECRYPT"
|
||||
labels = merge(
|
||||
{
|
||||
project = "burrow"
|
||||
component = "openbao"
|
||||
managed_by = "opentofu"
|
||||
key_target = "seal"
|
||||
},
|
||||
var.tags,
|
||||
)
|
||||
|
||||
version_template {
|
||||
algorithm = "GOOGLE_SYMMETRIC_ENCRYPTION"
|
||||
protection_level = "HSM"
|
||||
}
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_kms_crypto_key_iam_member" "openbao_seal" {
|
||||
for_each = var.manage_google && local.google_kms_key_ring_id != "" ? var.google_openbao_seal_members : toset([])
|
||||
|
||||
crypto_key_id = google_kms_crypto_key.openbao_seal[0].id
|
||||
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
|
||||
member = each.value
|
||||
}
|
||||
125
infra/openbao/openbao.tf
Normal file
125
infra/openbao/openbao.tf
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
resource "vault_mount" "age" {
|
||||
count = var.manage_openbao_config ? 1 : 0
|
||||
|
||||
path = var.age_mount_path
|
||||
type = "kv-v2"
|
||||
description = "Migrated Burrow age-managed runtime secrets."
|
||||
|
||||
options = {
|
||||
version = "2"
|
||||
}
|
||||
}
|
||||
|
||||
resource "vault_policy" "admin" {
|
||||
count = var.manage_openbao_config ? 1 : 0
|
||||
|
||||
name = "burrow-openbao-admin"
|
||||
|
||||
policy = <<-EOT
|
||||
path "*" {
|
||||
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
|
||||
}
|
||||
EOT
|
||||
}
|
||||
|
||||
resource "vault_policy" "runtime_read" {
|
||||
count = var.manage_openbao_config ? 1 : 0
|
||||
|
||||
name = "burrow-runtime-read"
|
||||
|
||||
policy = <<-EOT
|
||||
path "${var.age_mount_path}/data/*" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
|
||||
path "${var.age_mount_path}/metadata/*" {
|
||||
capabilities = ["list", "read"]
|
||||
}
|
||||
EOT
|
||||
}
|
||||
|
||||
resource "vault_auth_backend" "approle" {
|
||||
count = var.manage_openbao_config ? 1 : 0
|
||||
|
||||
path = var.approle_auth_path
|
||||
type = "approle"
|
||||
description = "Machine auth for Burrow runtime secret consumers."
|
||||
|
||||
tune {
|
||||
token_type = "default-service"
|
||||
}
|
||||
}
|
||||
|
||||
resource "vault_approle_auth_backend_role" "runtime" {
|
||||
count = var.manage_openbao_config ? 1 : 0
|
||||
|
||||
backend = vault_auth_backend.approle[0].path
|
||||
role_name = var.runtime_approle_role_name
|
||||
bind_secret_id = true
|
||||
|
||||
token_policies = [vault_policy.runtime_read[0].name]
|
||||
token_ttl = var.runtime_approle_token_ttl_seconds
|
||||
token_max_ttl = var.runtime_approle_token_max_ttl_seconds
|
||||
token_num_uses = 0
|
||||
|
||||
secret_id_ttl = var.runtime_approle_secret_id_ttl_seconds
|
||||
secret_id_num_uses = var.runtime_approle_secret_id_num_uses
|
||||
}
|
||||
|
||||
resource "vault_jwt_auth_backend" "authentik" {
|
||||
count = var.manage_openbao_config ? 1 : 0
|
||||
|
||||
path = "oidc"
|
||||
type = "oidc"
|
||||
oidc_discovery_url = var.authentik_issuer
|
||||
bound_issuer = var.authentik_issuer
|
||||
oidc_client_id = var.authentik_client_id
|
||||
default_role = "burrow-admin"
|
||||
|
||||
tune {
|
||||
listing_visibility = "unauth"
|
||||
token_type = "default-service"
|
||||
}
|
||||
}
|
||||
|
||||
resource "vault_jwt_auth_backend_role" "admin" {
|
||||
count = var.manage_openbao_config ? 1 : 0
|
||||
|
||||
backend = vault_jwt_auth_backend.authentik[0].path
|
||||
role_name = "burrow-admin"
|
||||
role_type = "oidc"
|
||||
user_claim = "email"
|
||||
|
||||
allowed_redirect_uris = [
|
||||
"https://vault.burrow.net/ui/vault/auth/oidc/oidc/callback",
|
||||
"http://localhost:8250/oidc/callback",
|
||||
]
|
||||
|
||||
bound_audiences = [var.authentik_client_id]
|
||||
bound_claims = {
|
||||
groups = var.authentik_admin_group
|
||||
}
|
||||
oidc_scopes = ["openid", "profile", "email", "groups"]
|
||||
token_policies = [vault_policy.admin[0].name]
|
||||
}
|
||||
|
||||
resource "vault_jwt_auth_backend_role" "runtime" {
|
||||
count = var.manage_openbao_config ? 1 : 0
|
||||
|
||||
backend = vault_jwt_auth_backend.authentik[0].path
|
||||
role_name = "burrow-runtime-read"
|
||||
role_type = "oidc"
|
||||
user_claim = "email"
|
||||
|
||||
allowed_redirect_uris = [
|
||||
"https://vault.burrow.net/ui/vault/auth/oidc/oidc/callback",
|
||||
"http://localhost:8250/oidc/callback",
|
||||
]
|
||||
|
||||
bound_audiences = [var.authentik_client_id]
|
||||
bound_claims = {
|
||||
groups = var.authentik_runtime_group
|
||||
}
|
||||
oidc_scopes = ["openid", "profile", "email", "groups"]
|
||||
token_policies = [vault_policy.runtime_read[0].name]
|
||||
}
|
||||
14
infra/openbao/outputs.tf
Normal file
14
infra/openbao/outputs.tf
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
output "google_kms_key_ring_id" {
|
||||
description = "Google KMS key ring id used by the OpenBao stack."
|
||||
value = local.google_kms_key_ring_id
|
||||
}
|
||||
|
||||
output "google_openbao_seal_key_id" {
|
||||
description = "Google KMS seal key id when managed."
|
||||
value = try(google_kms_crypto_key.openbao_seal[0].id, null)
|
||||
}
|
||||
|
||||
output "openbao_age_mount_path" {
|
||||
description = "OpenBao KV v2 mount path for migrated age secrets."
|
||||
value = var.age_mount_path
|
||||
}
|
||||
8
infra/openbao/providers.tf
Normal file
8
infra/openbao/providers.tf
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
provider "google" {
|
||||
project = var.google_project_id
|
||||
}
|
||||
|
||||
provider "vault" {
|
||||
address = var.openbao_address
|
||||
skip_tls_verify = var.openbao_skip_tls_verify
|
||||
}
|
||||
8
infra/openbao/terraform.tfvars.example
Normal file
8
infra/openbao/terraform.tfvars.example
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
manage_google = false
|
||||
manage_openbao_config = false
|
||||
google_project_id = "project-88c23ce9-918a-470a-b33"
|
||||
openbao_address = "https://vault.burrow.net"
|
||||
authentik_issuer = "https://auth.burrow.net/application/o/openbao/"
|
||||
authentik_client_id = "vault.burrow.net"
|
||||
authentik_admin_group = "burrow-admins"
|
||||
authentik_runtime_group = "burrow-automation"
|
||||
137
infra/openbao/variables.tf
Normal file
137
infra/openbao/variables.tf
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
variable "openbao_address" {
|
||||
description = "OpenBao API address. The Vault provider reads the token from VAULT_TOKEN."
|
||||
type = string
|
||||
default = "https://vault.burrow.net"
|
||||
}
|
||||
|
||||
variable "openbao_skip_tls_verify" {
|
||||
description = "Skip TLS verification for local bootstrap only."
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "age_mount_path" {
|
||||
description = "KV v2 mount path for migrated age-managed runtime secrets."
|
||||
type = string
|
||||
default = "age"
|
||||
}
|
||||
|
||||
variable "manage_openbao_config" {
|
||||
description = "Manage OpenBao mounts, policies, and Authentik OIDC auth. Requires VAULT_TOKEN."
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "authentik_issuer" {
|
||||
description = "Authentik OpenBao OIDC issuer."
|
||||
type = string
|
||||
default = "https://auth.burrow.net/application/o/openbao/"
|
||||
}
|
||||
|
||||
variable "authentik_client_id" {
|
||||
description = "Public Authentik OpenBao OIDC client id."
|
||||
type = string
|
||||
default = "vault.burrow.net"
|
||||
}
|
||||
|
||||
variable "authentik_admin_group" {
|
||||
description = "Authentik group allowed to administer OpenBao."
|
||||
type = string
|
||||
default = "burrow-admins"
|
||||
}
|
||||
|
||||
variable "authentik_runtime_group" {
|
||||
description = "Authentik group allowed to read runtime secrets."
|
||||
type = string
|
||||
default = "burrow-automation"
|
||||
}
|
||||
|
||||
variable "approle_auth_path" {
|
||||
description = "OpenBao AppRole auth mount path for machine consumers."
|
||||
type = string
|
||||
default = "approle"
|
||||
}
|
||||
|
||||
variable "runtime_approle_role_name" {
|
||||
description = "AppRole role name used by Burrow runtime secret consumers."
|
||||
type = string
|
||||
default = "burrow-runtime"
|
||||
}
|
||||
|
||||
variable "runtime_approle_token_ttl_seconds" {
|
||||
description = "Token TTL for runtime AppRole login."
|
||||
type = number
|
||||
default = 3600
|
||||
}
|
||||
|
||||
variable "runtime_approle_token_max_ttl_seconds" {
|
||||
description = "Maximum token TTL for runtime AppRole login."
|
||||
type = number
|
||||
default = 7200
|
||||
}
|
||||
|
||||
variable "runtime_approle_secret_id_ttl_seconds" {
|
||||
description = "SecretID TTL for runtime AppRole. Zero means no expiry; rotate manually."
|
||||
type = number
|
||||
default = 0
|
||||
}
|
||||
|
||||
variable "runtime_approle_secret_id_num_uses" {
|
||||
description = "SecretID use limit for runtime AppRole. Zero means unlimited uses; rotate manually."
|
||||
type = number
|
||||
default = 0
|
||||
}
|
||||
|
||||
variable "manage_google" {
|
||||
description = "Manage Google Cloud KMS resources for OpenBao seal wrapping."
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "google_project_id" {
|
||||
description = "Google Cloud project id for OpenBao seal wrapping."
|
||||
type = string
|
||||
default = "project-88c23ce9-918a-470a-b33"
|
||||
}
|
||||
|
||||
variable "manage_google_kms_key_ring" {
|
||||
description = "Create/manage the Google KMS key ring. Existing rings should be imported before enabling this."
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "google_kms_key_ring_name" {
|
||||
description = "Google KMS key ring for OpenBao seal wrapping."
|
||||
type = string
|
||||
default = "burrow-identity"
|
||||
}
|
||||
|
||||
variable "google_kms_key_ring_location" {
|
||||
description = "Google KMS key ring location."
|
||||
type = string
|
||||
default = "global"
|
||||
}
|
||||
|
||||
variable "google_kms_key_ring_id" {
|
||||
description = "Existing Google KMS key ring id."
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "google_openbao_seal_key_name" {
|
||||
description = "Google KMS symmetric key used for OpenBao seal wrapping."
|
||||
type = string
|
||||
default = "openbao-seal"
|
||||
}
|
||||
|
||||
variable "google_openbao_seal_members" {
|
||||
description = "IAM members allowed to encrypt/decrypt with the OpenBao seal key."
|
||||
type = set(string)
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "tags" {
|
||||
description = "Additional labels applied to supported Google resources."
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
16
infra/openbao/versions.tf
Normal file
16
infra/openbao/versions.tf
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
terraform {
|
||||
required_version = ">= 1.8.0"
|
||||
|
||||
required_providers {
|
||||
google = {
|
||||
source = "hashicorp/google"
|
||||
version = ">= 5.40.0, < 7.0"
|
||||
}
|
||||
vault = {
|
||||
source = "hashicorp/vault"
|
||||
version = ">= 4.5.0, < 6.0"
|
||||
}
|
||||
}
|
||||
|
||||
backend "s3" {}
|
||||
}
|
||||
5
infra/releases/.gitignore
vendored
Normal file
5
infra/releases/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
.terraform/
|
||||
*.tfstate
|
||||
*.tfstate.*
|
||||
backend.hcl
|
||||
terraform.tfvars
|
||||
20
infra/releases/.terraform.lock.hcl
generated
Normal file
20
infra/releases/.terraform.lock.hcl
generated
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# This file is maintained automatically by "tofu init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.opentofu.org/hashicorp/google" {
|
||||
version = "6.50.0"
|
||||
constraints = ">= 5.40.0, < 7.0.0"
|
||||
hashes = [
|
||||
"h1:MAAe4zFFdqS9M5rpmJK/vKgdb6ZMD/s/0Xd97yTDipA=",
|
||||
"zh:1d4695f807d998f11fcdcfa174766287b82a8093513af857bcdad2d81c642480",
|
||||
"zh:3173ac5df0294624d113812e49e2a55714aff7db617488168cecdf4168df9e29",
|
||||
"zh:34d2b3d44c23bd6354fc4ab5917b302872ea1ab8de107034567f955b1717fa5b",
|
||||
"zh:3a77f3cc2f3664cd5aaeeef4d044e6ec1695a079588fffec3ca03953664e5f04",
|
||||
"zh:6b444e4b629ea8dc8cb112a39dde098dc5584d26d6de4177558f556a9a226696",
|
||||
"zh:96545c8cd4d3a57069c5d1799eab5aedd887e16d98b5559a195f6d2c2d9bc674",
|
||||
"zh:ba464caafde95ee16671d6b5ec90f053ed77a9d06c567456db6efd9160fa3165",
|
||||
"zh:d876938e5b0d3f57a984d9be72467995f87fef6569968623415dc51d9f54d30b",
|
||||
"zh:dfd908d873e314ab807d0abc9cfd42d2611cd06dc1b9ec719ebdbb738e8e68d6",
|
||||
"zh:f9f16819a7738d564afd45fd169ba61004ec4e4e7089d2a4950cb8895be1fe1f",
|
||||
]
|
||||
}
|
||||
54
infra/releases/README.md
Normal file
54
infra/releases/README.md
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# Burrow Release Storage
|
||||
|
||||
This OpenTofu stack owns Google Cloud Storage buckets used as Burrow's first
|
||||
release-storage backup target.
|
||||
|
||||
Garage is the required S3-compatible primary target. CI uses repo-owned storage
|
||||
wrappers: they upload to Garage first, then mirror to GCS unless the backup is
|
||||
explicitly disabled.
|
||||
|
||||
The host activation point is `services.burrow.garage`, enabled by the forge
|
||||
host. It creates the initial single-node Garage layout and buckets from
|
||||
host-local runtime secrets.
|
||||
|
||||
It may manage:
|
||||
|
||||
- the release artifact bucket served by `releases.burrow.net`
|
||||
- the signed package repository bucket served by `packages.burrow.net`
|
||||
- the private Nix cache backup bucket for `nix.burrow.net`
|
||||
- public object-read IAM for download buckets
|
||||
- object-admin IAM for the Burrow Forgejo runner service account
|
||||
|
||||
It must not manage:
|
||||
|
||||
- Google service-account JSON keys
|
||||
- release signing keys
|
||||
- package repository metadata
|
||||
- DNS or CDN cutover
|
||||
|
||||
GCS access is keyless. Forgejo jobs run `Scripts/ci/google-wif-auth.sh` to
|
||||
trade an Authentik-issued OIDC token for the Google runner service account in
|
||||
project `project-88c23ce9-918a-470a-b33`, then use `gcloud storage` against the
|
||||
managed backup buckets. The normal release path does not use rclone or Google
|
||||
service-account JSON keys.
|
||||
|
||||
The Nix cache backup bucket defaults to private. Attic remains the public cache
|
||||
surface at `nix.burrow.net`; GCS is only a backup target for the Garage `attic`
|
||||
bucket.
|
||||
|
||||
## Run
|
||||
|
||||
```sh
|
||||
Scripts/releases-tofu.sh init -backend-config=backend.hcl
|
||||
Scripts/releases-tofu.sh plan -var-file=terraform.tfvars
|
||||
```
|
||||
|
||||
For local syntax work without remote state:
|
||||
|
||||
```sh
|
||||
Scripts/releases-tofu.sh init -backend=false
|
||||
Scripts/releases-tofu.sh validate
|
||||
```
|
||||
|
||||
Start with `manage_google = false`. Import existing buckets before enabling
|
||||
management if they already exist.
|
||||
3
infra/releases/backend.hcl.example
Normal file
3
infra/releases/backend.hcl.example
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
bucket = "burrow-opentofu-state"
|
||||
key = "releases/releases.tfstate"
|
||||
region = "us-west-2"
|
||||
29
infra/releases/outputs.tf
Normal file
29
infra/releases/outputs.tf
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
output "google_release_bucket_name" {
|
||||
description = "GCS bucket used for release artifacts."
|
||||
value = try(google_storage_bucket.release_storage["releases"].name, var.google_release_bucket_name)
|
||||
}
|
||||
|
||||
output "google_package_bucket_name" {
|
||||
description = "GCS bucket used for signed package repositories."
|
||||
value = try(google_storage_bucket.release_storage["packages"].name, var.google_package_bucket_name)
|
||||
}
|
||||
|
||||
output "google_nix_cache_bucket_name" {
|
||||
description = "GCS bucket used for Nix cache backups."
|
||||
value = try(google_storage_bucket.release_storage["nix-cache"].name, var.google_nix_cache_bucket_name)
|
||||
}
|
||||
|
||||
output "google_release_bucket_uri" {
|
||||
description = "GCS URI for release artifacts."
|
||||
value = "gs://${try(google_storage_bucket.release_storage["releases"].name, var.google_release_bucket_name)}"
|
||||
}
|
||||
|
||||
output "google_package_bucket_uri" {
|
||||
description = "GCS URI for signed package repositories."
|
||||
value = "gs://${try(google_storage_bucket.release_storage["packages"].name, var.google_package_bucket_name)}"
|
||||
}
|
||||
|
||||
output "google_nix_cache_bucket_uri" {
|
||||
description = "GCS URI for Nix cache backups."
|
||||
value = "gs://${try(google_storage_bucket.release_storage["nix-cache"].name, var.google_nix_cache_bucket_name)}"
|
||||
}
|
||||
3
infra/releases/providers.tf
Normal file
3
infra/releases/providers.tf
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
provider "google" {
|
||||
project = var.google_project_id
|
||||
}
|
||||
116
infra/releases/storage.tf
Normal file
116
infra/releases/storage.tf
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
locals {
|
||||
google_required_services = toset([
|
||||
"storage.googleapis.com",
|
||||
])
|
||||
|
||||
release_storage_buckets = {
|
||||
releases = {
|
||||
name = var.google_release_bucket_name
|
||||
description = "Burrow release artifacts"
|
||||
public = var.google_release_public_read
|
||||
}
|
||||
packages = {
|
||||
name = var.google_package_bucket_name
|
||||
description = "Burrow signed package repositories"
|
||||
public = var.google_package_public_read
|
||||
}
|
||||
nix-cache = {
|
||||
name = var.google_nix_cache_bucket_name
|
||||
description = "Burrow Nix cache backups"
|
||||
public = var.google_nix_cache_public_read
|
||||
}
|
||||
}
|
||||
|
||||
release_storage_writer_members = setunion(
|
||||
var.google_release_extra_writer_members,
|
||||
toset(["serviceAccount:${var.google_runner_service_account_email}"]),
|
||||
)
|
||||
|
||||
release_storage_writer_grants = var.manage_google ? {
|
||||
for grant in flatten([
|
||||
for bucket_key in keys(local.release_storage_buckets) : [
|
||||
for member in local.release_storage_writer_members : {
|
||||
id = "${bucket_key}|${member}"
|
||||
bucket_key = bucket_key
|
||||
member = member
|
||||
}
|
||||
]
|
||||
]) : grant.id => grant
|
||||
} : {}
|
||||
|
||||
release_storage_public_grants = var.manage_google ? {
|
||||
for bucket_key, bucket in local.release_storage_buckets : bucket_key => bucket
|
||||
if bucket.public
|
||||
} : {}
|
||||
}
|
||||
|
||||
resource "google_project_service" "release_storage" {
|
||||
for_each = var.manage_google ? local.google_required_services : toset([])
|
||||
|
||||
project = var.google_project_id
|
||||
service = each.value
|
||||
disable_on_destroy = false
|
||||
}
|
||||
|
||||
resource "google_storage_bucket" "release_storage" {
|
||||
for_each = var.manage_google ? local.release_storage_buckets : {}
|
||||
|
||||
project = var.google_project_id
|
||||
name = each.value.name
|
||||
location = var.google_storage_location
|
||||
uniform_bucket_level_access = true
|
||||
public_access_prevention = each.value.public ? "inherited" : "enforced"
|
||||
force_destroy = false
|
||||
|
||||
labels = merge(
|
||||
{
|
||||
project = "burrow"
|
||||
component = "release-storage"
|
||||
managed_by = "opentofu"
|
||||
bucket = each.key
|
||||
},
|
||||
var.tags,
|
||||
)
|
||||
|
||||
versioning {
|
||||
enabled = true
|
||||
}
|
||||
|
||||
cors {
|
||||
origin = var.google_storage_cors_origins
|
||||
method = ["GET", "HEAD", "OPTIONS"]
|
||||
response_header = ["Content-Type", "Content-Length", "ETag", "Cache-Control"]
|
||||
max_age_seconds = 3600
|
||||
}
|
||||
|
||||
lifecycle_rule {
|
||||
condition {
|
||||
age = 30
|
||||
with_state = "ARCHIVED"
|
||||
num_newer_versions = 5
|
||||
matches_storage_class = ["STANDARD"]
|
||||
}
|
||||
|
||||
action {
|
||||
type = "Delete"
|
||||
}
|
||||
}
|
||||
|
||||
depends_on = [google_project_service.release_storage]
|
||||
}
|
||||
|
||||
resource "google_storage_bucket_iam_member" "release_storage_public_viewer" {
|
||||
for_each = local.release_storage_public_grants
|
||||
|
||||
bucket = google_storage_bucket.release_storage[each.key].name
|
||||
role = "roles/storage.objectViewer"
|
||||
member = "allUsers"
|
||||
}
|
||||
|
||||
resource "google_storage_bucket_iam_member" "release_storage_writer" {
|
||||
for_each = local.release_storage_writer_grants
|
||||
|
||||
bucket = google_storage_bucket.release_storage[each.value.bucket_key].name
|
||||
role = "roles/storage.objectAdmin"
|
||||
member = each.value.member
|
||||
}
|
||||
12
infra/releases/terraform.tfvars.example
Normal file
12
infra/releases/terraform.tfvars.example
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
manage_google = false
|
||||
|
||||
google_project_id = "project-88c23ce9-918a-470a-b33"
|
||||
google_storage_location = "US"
|
||||
google_runner_service_account_email = "burrow-forgejo-runner@project-88c23ce9-918a-470a-b33.iam.gserviceaccount.com"
|
||||
google_release_bucket_name = "burrow-net-releases"
|
||||
google_package_bucket_name = "burrow-net-packages"
|
||||
google_nix_cache_bucket_name = "burrow-net-nix-cache"
|
||||
google_release_public_read = true
|
||||
google_package_public_read = true
|
||||
google_nix_cache_public_read = false
|
||||
google_release_extra_writer_members = []
|
||||
81
infra/releases/variables.tf
Normal file
81
infra/releases/variables.tf
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
variable "manage_google" {
|
||||
description = "Manage Google Cloud release storage resources."
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "google_project_id" {
|
||||
description = "Google Cloud project id for Burrow release storage."
|
||||
type = string
|
||||
default = "project-88c23ce9-918a-470a-b33"
|
||||
}
|
||||
|
||||
variable "google_storage_location" {
|
||||
description = "Google Cloud Storage bucket location for release, package, and cache backup buckets."
|
||||
type = string
|
||||
default = "US"
|
||||
}
|
||||
|
||||
variable "google_runner_service_account_email" {
|
||||
description = "Forgejo runner service account email allowed to publish release, package, and cache backup objects."
|
||||
type = string
|
||||
default = "burrow-forgejo-runner@project-88c23ce9-918a-470a-b33.iam.gserviceaccount.com"
|
||||
}
|
||||
|
||||
variable "google_release_bucket_name" {
|
||||
description = "GCS bucket for release artifacts served by releases.burrow.net."
|
||||
type = string
|
||||
default = "burrow-net-releases"
|
||||
}
|
||||
|
||||
variable "google_package_bucket_name" {
|
||||
description = "GCS bucket for signed package repositories served by packages.burrow.net."
|
||||
type = string
|
||||
default = "burrow-net-packages"
|
||||
}
|
||||
|
||||
variable "google_nix_cache_bucket_name" {
|
||||
description = "GCS backup bucket for the Garage-backed Burrow Nix cache."
|
||||
type = string
|
||||
default = "burrow-net-nix-cache"
|
||||
}
|
||||
|
||||
variable "google_release_public_read" {
|
||||
description = "Grant allUsers objectViewer on the release artifact bucket."
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "google_package_public_read" {
|
||||
description = "Grant allUsers objectViewer on the package repository bucket."
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "google_nix_cache_public_read" {
|
||||
description = "Grant allUsers objectViewer on the Nix cache backup bucket."
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "google_release_extra_writer_members" {
|
||||
description = "Additional IAM members allowed to write release, repository, and cache backup objects."
|
||||
type = set(string)
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "google_storage_cors_origins" {
|
||||
description = "Allowed origins for public release/package downloads."
|
||||
type = list(string)
|
||||
default = [
|
||||
"https://burrow.net",
|
||||
"https://releases.burrow.net",
|
||||
"https://packages.burrow.net",
|
||||
]
|
||||
}
|
||||
|
||||
variable "tags" {
|
||||
description = "Additional labels applied to supported Google resources."
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
12
infra/releases/versions.tf
Normal file
12
infra/releases/versions.tf
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
terraform {
|
||||
required_version = ">= 1.8.0"
|
||||
|
||||
required_providers {
|
||||
google = {
|
||||
source = "hashicorp/google"
|
||||
version = ">= 5.40.0, < 7.0"
|
||||
}
|
||||
}
|
||||
|
||||
backend "s3" {}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue