Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3b229deb1b | |||
| 8fc5105d10 | |||
| c920451844 | |||
| 493c33483b | |||
| 58dafb7ac2 | |||
| 68584acffb | |||
| bd66d55c99 | |||
| fe662001c5 | |||
| c0fc234a1c | |||
| d53b6841e1 | |||
| 38849ba79f | |||
| 0385d2a1ef | |||
| c068f56eca | |||
| 99b13d81bc | |||
| 9cee6024d3 | |||
| bd7bef9f99 | |||
| 96092cf7a1 | |||
| fd82876f5f | |||
| ee08fd5585 | |||
| 4c5fe742f0 | |||
| aa9b2eac80 | |||
| 3f39229c29 | |||
| 42187f86d5 |
@@ -33,6 +33,14 @@ locals {
|
||||
})
|
||||
if length(regexall("/team/", file_path)) > 0
|
||||
}
|
||||
user = {
|
||||
for file_path, content in local.all_configs :
|
||||
"${split("/", file_path)[0]}/${trimsuffix(basename(file_path), ".yaml")}" => merge(content, {
|
||||
username = trimsuffix(basename(file_path), ".yaml")
|
||||
gitea_url = split("/", file_path)[0]
|
||||
})
|
||||
if length(regexall("/user/", file_path)) > 0
|
||||
}
|
||||
branch_protection = merge([
|
||||
for file_path, content in local.all_configs : {
|
||||
for idx, rule in try(content.branch_protection, []) :
|
||||
|
||||
@@ -8,5 +8,6 @@ branch_protection:
|
||||
status_check_contexts:
|
||||
- "ci/woodpecker/pr/pre-commit"
|
||||
- "ci/woodpecker/pr/kubeconform"
|
||||
- "ci/woodpecker/pr/vector-test"
|
||||
approval_whitelist_users:
|
||||
- "benvin"
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
description: "Builds AlmaLinux node rootfs tarballs for bootapi image-based provisioning; on v* tag publishes almalinux9-node-<ver>.tar.zst to the artifactapi rootfs-images repo."
|
||||
private: false
|
||||
default_branch: "main"
|
||||
default_delete_branch_after_merge: true
|
||||
default_merge_style: "squash"
|
||||
branch_protection:
|
||||
- rule_name: "main"
|
||||
enable_push: false
|
||||
status_check_contexts:
|
||||
- "ci/woodpecker/pr/pre-commit"
|
||||
- "ci/woodpecker/pr/lint"
|
||||
approval_whitelist_users:
|
||||
- "benvin"
|
||||
@@ -0,0 +1,13 @@
|
||||
description: "Live kickstart/iPXE templates + distro catalog for bootapi. Pulled by bootapi via git-sync; CI validates templates render for every distro."
|
||||
private: false
|
||||
default_branch: "main"
|
||||
default_delete_branch_after_merge: true
|
||||
default_merge_style: "squash"
|
||||
branch_protection:
|
||||
- rule_name: "main"
|
||||
enable_push: false
|
||||
status_check_contexts:
|
||||
- "ci/woodpecker/pr/pre-commit"
|
||||
- "ci/woodpecker/pr/validate"
|
||||
approval_whitelist_users:
|
||||
- "benvin"
|
||||
@@ -0,0 +1,14 @@
|
||||
description: "PXE/kickstart boot service replacing Cobbler. Renders kickstart + iPXE from NetBox data over HTTP. Go API."
|
||||
private: false
|
||||
default_branch: "main"
|
||||
default_delete_branch_after_merge: true
|
||||
default_merge_style: "squash"
|
||||
branch_protection:
|
||||
- rule_name: "main"
|
||||
enable_push: false
|
||||
status_check_contexts:
|
||||
- "ci/woodpecker/pr/pre-commit"
|
||||
- "ci/woodpecker/pr/test"
|
||||
- "ci/woodpecker/pr/build"
|
||||
approval_whitelist_users:
|
||||
- "benvin"
|
||||
@@ -0,0 +1,14 @@
|
||||
description: "Kubernetes operator for managing Kea DHCP clusters, subnets, and PXE client classes"
|
||||
private: false
|
||||
default_branch: "main"
|
||||
default_delete_branch_after_merge: true
|
||||
default_merge_style: "squash"
|
||||
branch_protection:
|
||||
- rule_name: "main"
|
||||
enable_push: false
|
||||
status_check_contexts:
|
||||
- "ci/woodpecker/pr/pre-commit"
|
||||
- "ci/woodpecker/pr/test"
|
||||
- "ci/woodpecker/pr/build"
|
||||
approval_whitelist_teams:
|
||||
- "Owners"
|
||||
@@ -0,0 +1,14 @@
|
||||
description: "Archives raw logs from NATS JetStream to S3 as zstd-compressed, GPG-encrypted, indexed objects; plus a CLI to search and retrieve them. Go service + CLI."
|
||||
private: false
|
||||
default_branch: "main"
|
||||
default_delete_branch_after_merge: true
|
||||
default_merge_style: "squash"
|
||||
branch_protection:
|
||||
- rule_name: "main"
|
||||
enable_push: false
|
||||
status_check_contexts:
|
||||
- "ci/woodpecker/pr/pre-commit"
|
||||
- "ci/woodpecker/pr/test"
|
||||
- "ci/woodpecker/pr/build"
|
||||
approval_whitelist_users:
|
||||
- "benvin"
|
||||
@@ -0,0 +1,19 @@
|
||||
description: "Terraform provider for the Vault/OpenBao Gitea token secrets engine (giteavaultsecret)"
|
||||
private: false
|
||||
default_branch: "main"
|
||||
default_delete_branch_after_merge: true
|
||||
# Squash-only: the gitea provider has no "default merge style", so we restrict
|
||||
# the allowed styles to squash to force it.
|
||||
allow_merge_commits: false
|
||||
allow_rebase: false
|
||||
allow_rebase_explicit: false
|
||||
allow_squash_merge: true
|
||||
branch_protection:
|
||||
- rule_name: "main"
|
||||
enable_push: false
|
||||
status_check_contexts:
|
||||
- "ci/woodpecker/pr/pre-commit"
|
||||
- "ci/woodpecker/pr/build"
|
||||
- "ci/woodpecker/pr/test"
|
||||
approval_whitelist_teams:
|
||||
- "Owners"
|
||||
@@ -0,0 +1,19 @@
|
||||
description: "HashiCorp Vault / OpenBao secrets engine for Gitea: mints ephemeral, scoped per-user access tokens via the admin API, with seeded-admin basic-auth and root password rotation"
|
||||
private: false
|
||||
default_branch: "main"
|
||||
default_delete_branch_after_merge: true
|
||||
# Squash-only: the gitea provider has no "default merge style", so we restrict
|
||||
# the allowed styles to squash to force it.
|
||||
allow_merge_commits: false
|
||||
allow_rebase: false
|
||||
allow_rebase_explicit: false
|
||||
allow_squash_merge: true
|
||||
branch_protection:
|
||||
- rule_name: "main"
|
||||
enable_push: false
|
||||
status_check_contexts:
|
||||
- "ci/woodpecker/pr/pre-commit"
|
||||
- "ci/woodpecker/pr/build"
|
||||
- "ci/woodpecker/pr/test"
|
||||
approval_whitelist_teams:
|
||||
- "Owners"
|
||||
@@ -0,0 +1,14 @@
|
||||
description: "Monorepo of Go CLI tools for managing Vault tokens across multiple vault instances: vctl (login/renew per-context tokens) and vctx (context-aware wrapper around the vault CLI)."
|
||||
private: false
|
||||
default_branch: "main"
|
||||
default_delete_branch_after_merge: true
|
||||
default_merge_style: "squash"
|
||||
branch_protection:
|
||||
- rule_name: "main"
|
||||
enable_push: false
|
||||
status_check_contexts:
|
||||
- "ci/woodpecker/pr/build"
|
||||
- "ci/woodpecker/pr/test"
|
||||
- "ci/woodpecker/pr/pre-commit"
|
||||
approval_whitelist_users:
|
||||
- "benvin"
|
||||
@@ -0,0 +1,21 @@
|
||||
# Purpose-built Gitea site-admin bot for the vault-plugin-secrets-gitea engine.
|
||||
# The engine seeds itself from this account's credentials (Basic Auth) to mint
|
||||
# and delete per-user tokens for any user. Its randomly generated password is
|
||||
# written ONCE to Vault KV (vault_seed_path below) and never updated after; the
|
||||
# Vault gitea engine reads that seed only when first creating gitea/config and
|
||||
# then rotates it (rotate-root) so only Vault holds the live password.
|
||||
#
|
||||
# A local (not external-auth) account with 2FA disabled is required so the
|
||||
# engine can change the password via the admin API during rotate-root.
|
||||
email: gitea-vault-admin@unkin.net
|
||||
full_name: "Gitea Vault Admin"
|
||||
description: "site-admin bot; credentials seeded to Vault for vault-plugin-secrets-gitea"
|
||||
# Site admin so the engine may mint/delete tokens for any user. No org/repo
|
||||
# creation; profile visible only to signed-in users.
|
||||
visibility: limited
|
||||
admin: true
|
||||
allow_create_organization: false
|
||||
max_repo_creation: 0
|
||||
# Seed this account's generated password to Vault KV (mount "kv") at this path,
|
||||
# where the Vault gitea secrets engine reads it at creation time. Written once.
|
||||
vault_seed_path: "service/vault/au/syd1/secret_backend/gitea/config"
|
||||
@@ -0,0 +1,14 @@
|
||||
# teabot "implementer" personality account.
|
||||
# Used by the teabot daemon (unkin/teabot) to open issues/PRs as a distinct
|
||||
# identity so its work is attributable. Auth is via an API token issued
|
||||
# out-of-band (vault-plugin-secrets-gitea / static KV) -- no token or usable
|
||||
# password lives in this repo's state beyond the generated placeholder.
|
||||
email: teabot-implementer@unkin.net
|
||||
full_name: "Teabot Implementer"
|
||||
description: "teabot implementer bot -- automated agent (unkin/teabot)"
|
||||
# Conservative bot posture: not a site admin, cannot create orgs or repos,
|
||||
# profile visible only to signed-in users.
|
||||
visibility: limited
|
||||
admin: false
|
||||
allow_create_organization: false
|
||||
max_repo_creation: 0
|
||||
@@ -0,0 +1,14 @@
|
||||
# teabot "reviewer" personality account.
|
||||
# Used by the teabot daemon (unkin/teabot) to review pull requests as a distinct
|
||||
# identity so its reviews are attributable. Auth is via an API token issued
|
||||
# out-of-band (vault-plugin-secrets-gitea / static KV) -- no token or usable
|
||||
# password lives in this repo's state beyond the generated placeholder.
|
||||
email: teabot-reviewer@unkin.net
|
||||
full_name: "Teabot Reviewer"
|
||||
description: "teabot reviewer bot -- automated agent (unkin/teabot)"
|
||||
# Conservative bot posture: not a site admin, cannot create orgs or repos,
|
||||
# profile visible only to signed-in users.
|
||||
visibility: limited
|
||||
admin: false
|
||||
allow_create_organization: false
|
||||
max_repo_creation: 0
|
||||
@@ -21,4 +21,5 @@ inputs = {
|
||||
repository = local.config.repository
|
||||
branch_protection = local.config.branch_protection
|
||||
team = local.config.team
|
||||
user = local.config.user
|
||||
}
|
||||
|
||||
@@ -11,6 +11,15 @@ provider "woodpecker" {
|
||||
server = "https://ci.k8s.syd1.au.unkin.net"
|
||||
}
|
||||
|
||||
# Address + token come from VAULT_ADDR / VAULT_TOKEN in the environment (the
|
||||
# Makefile authenticates via k8s auth before running terragrunt). Used to seed
|
||||
# bot-account credentials into Vault KV (see modules/user vault_seed_path).
|
||||
# skip_child_token: the CI token from the k8s auth role cannot create child
|
||||
# tokens (apply fails with "failed to create limited child token").
|
||||
provider "vault" {
|
||||
skip_child_token = true
|
||||
}
|
||||
|
||||
terraform {
|
||||
backend "consul" {
|
||||
address = "https://consul.service.consul"
|
||||
|
||||
@@ -38,6 +38,27 @@ module "repository" {
|
||||
depends_on = [module.organisation]
|
||||
}
|
||||
|
||||
module "user" {
|
||||
source = "./modules/user"
|
||||
|
||||
for_each = var.user
|
||||
|
||||
username = each.value.username
|
||||
email = each.value.email
|
||||
login_name = each.value.login_name
|
||||
full_name = each.value.full_name
|
||||
description = each.value.description
|
||||
visibility = each.value.visibility
|
||||
admin = each.value.admin
|
||||
restricted = each.value.restricted
|
||||
active = each.value.active
|
||||
allow_create_organization = each.value.allow_create_organization
|
||||
max_repo_creation = each.value.max_repo_creation
|
||||
must_change_password = each.value.must_change_password
|
||||
vault_seed_path = each.value.vault_seed_path
|
||||
vault_seed_mount = each.value.vault_seed_mount
|
||||
}
|
||||
|
||||
module "team" {
|
||||
source = "./modules/team"
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
# The gitea_user resource requires a password argument. These accounts
|
||||
# authenticate via API tokens (issued out-of-band by the
|
||||
# vault-plugin-secrets-gitea engine, or a static KV entry initially), so the
|
||||
# password is never used interactively. It is generated here so nothing
|
||||
# sensitive is hardcoded, and kept only in state.
|
||||
resource "random_password" "this" {
|
||||
length = 32
|
||||
special = true
|
||||
override_special = "!@#%^&*()-_=+"
|
||||
}
|
||||
|
||||
resource "gitea_user" "this" {
|
||||
username = var.username
|
||||
login_name = coalesce(var.login_name, var.username)
|
||||
email = var.email
|
||||
password = random_password.this.result
|
||||
|
||||
full_name = var.full_name
|
||||
description = var.description
|
||||
visibility = var.visibility
|
||||
|
||||
admin = var.admin
|
||||
restricted = var.restricted
|
||||
active = var.active
|
||||
allow_create_organization = var.allow_create_organization
|
||||
max_repo_creation = var.max_repo_creation
|
||||
must_change_password = var.must_change_password
|
||||
|
||||
# Do not e-mail the (possibly unmonitored) bot address on creation.
|
||||
send_notification = false
|
||||
|
||||
lifecycle {
|
||||
# The provider cannot read the password back from the API; ignore it so the
|
||||
# account never shows perpetual drift once created.
|
||||
ignore_changes = [password]
|
||||
}
|
||||
}
|
||||
|
||||
# Optional one-time seed of the account's credentials to Vault KV, for the
|
||||
# vault-plugin-secrets-gitea engine to consume when it first creates gitea/config.
|
||||
# random_password never regenerates (no keepers), so the seed is stable; the
|
||||
# ignore_changes below makes the write strictly create-only, so a later
|
||||
# rotate-root on the Vault side (which diverges the live password from this seed)
|
||||
# is never clobbered by a re-apply here.
|
||||
resource "vault_kv_secret_v2" "seed" {
|
||||
count = var.vault_seed_path != null ? 1 : 0
|
||||
|
||||
mount = var.vault_seed_mount
|
||||
name = var.vault_seed_path
|
||||
data_json = jsonencode({
|
||||
admin_username = var.username
|
||||
admin_password = random_password.this.result
|
||||
})
|
||||
|
||||
lifecycle {
|
||||
ignore_changes = [data_json]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
terraform {
|
||||
required_version = ">= 1.10"
|
||||
required_providers {
|
||||
gitea = {
|
||||
source = "go-gitea/gitea"
|
||||
version = "0.7.0"
|
||||
}
|
||||
random = {
|
||||
source = "hashicorp/random"
|
||||
version = ">= 3.5"
|
||||
}
|
||||
vault = {
|
||||
source = "hashicorp/vault"
|
||||
version = ">= 4.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
variable "username" {
|
||||
description = "Username of the Gitea account (immutable; changing it recreates the user)"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "login_name" {
|
||||
description = "Login name, may differ from username. Defaults to the username."
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "email" {
|
||||
description = "E-Mail address of the user"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "full_name" {
|
||||
description = "Display / full name of the user"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "description" {
|
||||
description = "Profile description of the user"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "visibility" {
|
||||
description = "Profile visibility: public, limited or private"
|
||||
type = string
|
||||
default = "limited"
|
||||
}
|
||||
|
||||
variable "admin" {
|
||||
description = "Whether the user is a site administrator"
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "restricted" {
|
||||
description = "Restricted users can only access repositories/orgs they are explicitly added to"
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "active" {
|
||||
description = "Whether the account is active"
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "allow_create_organization" {
|
||||
description = "Whether the user may create organisations"
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "max_repo_creation" {
|
||||
description = "Maximum number of repositories the user may create (0 disallows creation)"
|
||||
type = number
|
||||
default = 0
|
||||
}
|
||||
|
||||
variable "must_change_password" {
|
||||
description = "Force a password change on first login"
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "vault_seed_path" {
|
||||
description = <<-EOT
|
||||
Optional. When set, the account's generated password is seeded ONCE to Vault
|
||||
KV at this secret name (under vault_seed_mount) as admin_username +
|
||||
admin_password, for the vault-plugin-secrets-gitea engine to consume at
|
||||
creation time. The write is create-only; subsequent changes are ignored so
|
||||
the seed never churns (and never overwrites a rotated password).
|
||||
EOT
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "vault_seed_mount" {
|
||||
description = "KV v2 mount holding the seeded credential (used only when vault_seed_path is set)"
|
||||
type = string
|
||||
default = "kv"
|
||||
}
|
||||
@@ -9,5 +9,13 @@ terraform {
|
||||
source = "Kichiyaki/woodpecker"
|
||||
version = "0.5.0"
|
||||
}
|
||||
random = {
|
||||
source = "hashicorp/random"
|
||||
version = ">= 3.5"
|
||||
}
|
||||
vault = {
|
||||
source = "hashicorp/vault"
|
||||
version = ">= 4.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,27 @@ variable "branch_protection" {
|
||||
}
|
||||
|
||||
|
||||
variable "user" {
|
||||
description = "Map of Gitea user accounts to create"
|
||||
type = map(object({
|
||||
username = string
|
||||
email = string
|
||||
login_name = optional(string)
|
||||
full_name = optional(string, "")
|
||||
description = optional(string, "")
|
||||
visibility = optional(string, "limited")
|
||||
admin = optional(bool, false)
|
||||
restricted = optional(bool, false)
|
||||
active = optional(bool, true)
|
||||
allow_create_organization = optional(bool, false)
|
||||
max_repo_creation = optional(number, 0)
|
||||
must_change_password = optional(bool, false)
|
||||
vault_seed_path = optional(string)
|
||||
vault_seed_mount = optional(string, "kv")
|
||||
}))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "team" {
|
||||
description = "Map of teams to create"
|
||||
type = map(object({
|
||||
|
||||
Reference in New Issue
Block a user