Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| afbea53d94 |
@@ -22,18 +22,3 @@ repos:
|
||||
"-d {extends: relaxed, rules: {line-length: disable}, ignore: chart}",
|
||||
"-s",
|
||||
]
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: vault-yaml-tests
|
||||
name: vault yaml definitions pass their unit tests
|
||||
entry: python3 -m unittest discover -s tests -t .
|
||||
language: python
|
||||
additional_dependencies: [pyyaml, pydantic]
|
||||
pass_filenames: false
|
||||
always_run: true
|
||||
- id: tofu-module-tests
|
||||
name: tofu module tests pass
|
||||
entry: sh -c 'for m in auth_jwt_backend auth_jwt_role; do (cd modules/vault_cluster/modules/$m && tofu init -backend=false -input=false >/dev/null && tofu test) || exit 1; done'
|
||||
language: system
|
||||
pass_filenames: false
|
||||
always_run: true
|
||||
|
||||
@@ -4,7 +4,7 @@ when:
|
||||
|
||||
steps:
|
||||
- name: apply
|
||||
image: artifactapi.k8s.syd1.au.unkin.net/docker-internal/opentofu:0.1.0
|
||||
image: git.unkin.net/unkin/almalinux9-opentofu:20260606
|
||||
environment:
|
||||
VAULT_AUTH_METHOD: kubernetes
|
||||
VAULT_VERSION: "1.20.0"
|
||||
|
||||
@@ -3,7 +3,7 @@ when:
|
||||
|
||||
steps:
|
||||
- name: plan
|
||||
image: artifactapi.k8s.syd1.au.unkin.net/docker-internal/opentofu:0.1.0
|
||||
image: git.unkin.net/unkin/almalinux9-opentofu:20260606
|
||||
environment:
|
||||
VAULT_AUTH_METHOD: kubernetes
|
||||
VAULT_VERSION: "1.20.0"
|
||||
|
||||
@@ -3,7 +3,7 @@ when:
|
||||
|
||||
steps:
|
||||
- name: pre-commit
|
||||
image: artifactapi.k8s.syd1.au.unkin.net/docker-internal/opentofu:0.1.0
|
||||
image: git.unkin.net/unkin/almalinux9-opentofu:20260606
|
||||
commands:
|
||||
- uvx pre-commit run --all-files
|
||||
backend_options:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.PHONY: init plan apply format test
|
||||
.PHONY: init plan apply format
|
||||
|
||||
VAULT_AUTH_METHOD ?= approle
|
||||
VAULT_K8S_ROLE ?= woodpecker_terraform_vault
|
||||
@@ -28,10 +28,6 @@ apply: init
|
||||
@$(call vault_env) && \
|
||||
terragrunt run --all --parallelism 2 --non-interactive apply
|
||||
|
||||
test:
|
||||
@uv run --with pyyaml --with pydantic python -m unittest discover -s tests -t .
|
||||
@for m in auth_jwt_backend auth_jwt_role; do (cd modules/vault_cluster/modules/$$m && tofu init -backend=false -input=false >/dev/null && tofu test) || exit 1; done
|
||||
|
||||
format:
|
||||
@echo "Formatting OpenTofu files..."
|
||||
@tofu fmt -recursive .
|
||||
|
||||
@@ -35,35 +35,3 @@ You can check the Consul KV store by accessing the Consul UI or using the consul
|
||||
```bash
|
||||
consul kv get terraform/state
|
||||
```
|
||||
|
||||
# GitHub Actions JWT roles
|
||||
|
||||
`auth/jwt/github/iuclabs` trusts GitHub Actions OIDC tokens. One org-wide role, `default` (`config/auth_jwt_role/jwt/github/iuclabs/default.yaml`, the mount's `default_role`, so workflows needn't name it), accepts any IUCLabs repo for `push`, `release`, `schedule` and `pull_request` runs on `refs/heads/*`, `refs/tags/*` or `refs/pull/*/merge`. Role validation rejects any other event (`pull_request_target`, `workflow_run`, comment events), any other ref glob, and globs in any other claim. Fork pull requests can't mint OIDC tokens unless "Send write tokens to workflows from pull requests" is enabled on the repo.
|
||||
|
||||
Workflows log in with audience `https://vault.service.consul:8200`:
|
||||
|
||||
```yaml
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
steps:
|
||||
- uses: hashicorp/vault-action@v3
|
||||
with:
|
||||
url: https://vault.service.consul:8200
|
||||
method: jwt
|
||||
path: jwt/github/iuclabs
|
||||
jwtGithubAudience: https://vault.service.consul:8200
|
||||
secrets: |
|
||||
kv/data/github/IUCLabs/<repo>/ref/refs/heads/main/.secrets/deploy token | DEPLOY_TOKEN
|
||||
```
|
||||
|
||||
The role attaches a templated read-only policy, so each run reads only its own KV-v2 secrets:
|
||||
|
||||
- `kv/github/<org>/<repo>/event/<event_name>/*` is not ref-scoped: any run of that event in the repo, on any branch or tag a writer pushes, can read it; keep it for secrets every such run may use
|
||||
- `kv/github/<org>/<repo>/ref/<full ref>/.secrets/*`, e.g. `ref/refs/heads/main/.secrets/`, `ref/refs/tags/v1.2.3/.secrets/`; git refnames can't contain a `.`-prefixed component, so branch `x` can never read branch `x/y`'s secrets
|
||||
|
||||
On OpenBao older than the fork's v2.6, a ref path component of exactly `+` acts as a single-segment wildcard in the templated policy. Public IUCLabs repos block such refs via a terraform-git ruleset; private repos are not protected, so a writer in a private repo can read that repo's other refs' secrets.
|
||||
|
||||
`user_claim` is `jti`, so every run creates its own Vault identity entity; Vault never garbage-collects these, so entity count grows with CI volume.
|
||||
|
||||
Extra policies are granted via a policy yaml `auth: {jwt/github/iuclabs: [default]}`; the KV read policy is attached automatically.
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
token_ttl: 120
|
||||
token_max_ttl: 120
|
||||
bind_secret_id: false
|
||||
token_bound_cidrs:
|
||||
- "10.10.12.200/32"
|
||||
- "198.18.25.102/32"
|
||||
- "198.18.26.91/32"
|
||||
- "198.18.27.40/32"
|
||||
use_deterministic_role_id: true
|
||||
@@ -1,9 +0,0 @@
|
||||
token_ttl: 120
|
||||
token_max_ttl: 120
|
||||
bind_secret_id: false
|
||||
token_bound_cidrs:
|
||||
- "10.10.12.200/32"
|
||||
- "198.18.25.102/32"
|
||||
- "198.18.26.91/32"
|
||||
- "198.18.27.40/32"
|
||||
use_deterministic_role_id: true
|
||||
@@ -1,7 +0,0 @@
|
||||
# GitHub Actions OIDC for the IUCLabs org; the org-wide role lives in
|
||||
# config/auth_jwt_role/jwt/github/iuclabs/.
|
||||
---
|
||||
oidc_discovery_url: "https://token.actions.githubusercontent.com"
|
||||
bound_issuer: "https://token.actions.githubusercontent.com"
|
||||
description: "GitHub Actions OIDC - IUCLabs"
|
||||
default_role: "default"
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
bound_audiences: ["https://vault.service.consul:8200"]
|
||||
bound_claims:
|
||||
repository_owner: IUCLabs
|
||||
event_name: push,release,schedule,pull_request
|
||||
ref: refs/heads/*,refs/tags/*,refs/pull/*/merge
|
||||
claim_mappings:
|
||||
repository: repository
|
||||
repository_owner: repository_owner
|
||||
ref: ref
|
||||
ref_type: ref_type
|
||||
event_name: event_name
|
||||
workflow: workflow
|
||||
job_workflow_ref: job_workflow_ref
|
||||
run_id: run_id
|
||||
run_attempt: run_attempt
|
||||
sha: sha
|
||||
actor: actor
|
||||
environment: environment
|
||||
user_claim: jti
|
||||
token_ttl: 900
|
||||
token_max_ttl: 3600
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
# Kubernetes auth role for rspamd: the rspamd service account in the mail
|
||||
# namespace, used by VaultDynamicSecret to read litellm/creds/rspamd.
|
||||
bound_service_account_names:
|
||||
- rspamd
|
||||
bound_service_account_namespaces:
|
||||
- mail
|
||||
token_ttl: 600
|
||||
token_max_ttl: 600
|
||||
audience: vault
|
||||
@@ -1,15 +0,0 @@
|
||||
bound_service_account_names:
|
||||
- default
|
||||
- arrproxy-ci
|
||||
- autobackup-operator-ci
|
||||
- ghp
|
||||
- jellyfin-plugin-sso
|
||||
- kea-operator-ci
|
||||
- mediamark-ci
|
||||
- plugin-docker-buildx
|
||||
- repospawner-ci
|
||||
bound_service_account_namespaces:
|
||||
- woodpecker
|
||||
token_ttl: 600
|
||||
token_max_ttl: 600
|
||||
audience: https://kubernetes.default.svc.cluster.local
|
||||
@@ -1,7 +0,0 @@
|
||||
bound_service_account_names:
|
||||
- terraform-binarylane
|
||||
bound_service_account_namespaces:
|
||||
- woodpecker
|
||||
token_ttl: 600
|
||||
token_max_ttl: 600
|
||||
audience: https://kubernetes.default.svc.cluster.local
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
bound_service_account_names:
|
||||
- terraform-netbox
|
||||
- terraform-infra
|
||||
bound_service_account_namespaces:
|
||||
- woodpecker
|
||||
token_ttl: 600
|
||||
@@ -118,19 +118,6 @@ locals {
|
||||
})
|
||||
if startswith(file_path, "auth_oidc_group/")
|
||||
}
|
||||
auth_jwt_backend = {
|
||||
for file_path, content in local.all_configs :
|
||||
trimsuffix(replace(file_path, "auth_jwt_backend/", ""), ".yaml") => content
|
||||
if startswith(file_path, "auth_jwt_backend/")
|
||||
}
|
||||
auth_jwt_role = {
|
||||
for file_path, content in local.all_configs :
|
||||
trimsuffix(replace(file_path, "auth_jwt_role/", ""), ".yaml") => merge(content, {
|
||||
role_name = trimsuffix(basename(file_path), ".yaml")
|
||||
backend = dirname(replace(file_path, "auth_jwt_role/", ""))
|
||||
})
|
||||
if startswith(file_path, "auth_jwt_role/")
|
||||
}
|
||||
auth_kubernetes_backend = {
|
||||
for file_path, content in local.all_configs :
|
||||
trimsuffix(replace(file_path, "auth_kubernetes_backend/", ""), ".yaml") => content
|
||||
@@ -313,47 +300,6 @@ locals {
|
||||
})
|
||||
if startswith(file_path, "netbox_secret_backend_role/")
|
||||
}
|
||||
identity_oidc = {
|
||||
for file_path, content in local.all_configs :
|
||||
trimsuffix(basename(file_path), ".yaml") => content
|
||||
if startswith(file_path, "identity_oidc/")
|
||||
}
|
||||
identity_oidc_key = {
|
||||
for file_path, content in local.all_configs :
|
||||
trimsuffix(basename(file_path), ".yaml") => content
|
||||
if startswith(file_path, "identity_oidc_key/")
|
||||
}
|
||||
identity_oidc_role = {
|
||||
for file_path, content in local.all_configs :
|
||||
trimsuffix(replace(file_path, "identity_oidc_role/", ""), ".yaml") => merge(content, {
|
||||
name = trimsuffix(basename(file_path), ".yaml")
|
||||
key = dirname(replace(file_path, "identity_oidc_role/", ""))
|
||||
})
|
||||
if startswith(file_path, "identity_oidc_role/")
|
||||
}
|
||||
identity_oidc_client = {
|
||||
for file_path, content in local.all_configs :
|
||||
trimsuffix(replace(file_path, "identity_oidc_client/", ""), ".yaml") => merge(content, {
|
||||
name = trimsuffix(basename(file_path), ".yaml")
|
||||
key = dirname(replace(file_path, "identity_oidc_client/", ""))
|
||||
})
|
||||
if startswith(file_path, "identity_oidc_client/")
|
||||
}
|
||||
identity_oidc_scope = {
|
||||
for file_path, content in local.all_configs :
|
||||
trimsuffix(basename(file_path), ".yaml") => content
|
||||
if startswith(file_path, "identity_oidc_scope/")
|
||||
}
|
||||
identity_oidc_assignment = {
|
||||
for file_path, content in local.all_configs :
|
||||
trimsuffix(basename(file_path), ".yaml") => content
|
||||
if startswith(file_path, "identity_oidc_assignment/")
|
||||
}
|
||||
identity_oidc_provider = {
|
||||
for file_path, content in local.all_configs :
|
||||
trimsuffix(basename(file_path), ".yaml") => content
|
||||
if startswith(file_path, "identity_oidc_provider/")
|
||||
}
|
||||
ghp_secret_backend = {
|
||||
for file_path, content in local.all_configs :
|
||||
trimsuffix(basename(file_path), ".yaml") => content
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
consul_roles:
|
||||
- terraform-binarylane
|
||||
ttl: 120
|
||||
max_ttl: 300
|
||||
datacenters: []
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
consul_roles:
|
||||
- terraform-netbox
|
||||
- terraform-infra
|
||||
ttl: 120
|
||||
max_ttl: 300
|
||||
datacenters: []
|
||||
@@ -1,2 +0,0 @@
|
||||
---
|
||||
issuer: https://vault.service.consul:8200
|
||||
@@ -1,2 +0,0 @@
|
||||
---
|
||||
algorithm: RS256
|
||||
@@ -1,2 +0,0 @@
|
||||
---
|
||||
algorithm: RS256
|
||||
@@ -1,3 +0,0 @@
|
||||
---
|
||||
client_id: artifactapi
|
||||
ttl: 900
|
||||
@@ -1,3 +0,0 @@
|
||||
---
|
||||
client_id: artifactapi
|
||||
ttl: 900
|
||||
@@ -1,3 +0,0 @@
|
||||
---
|
||||
client_id: encapi-testing
|
||||
ttl: 900
|
||||
@@ -1,3 +0,0 @@
|
||||
---
|
||||
client_id: encapi
|
||||
ttl: 900
|
||||
@@ -1,6 +0,0 @@
|
||||
allowed_kubernetes_namespaces:
|
||||
- "vm-system"
|
||||
- "observability"
|
||||
- "logging"
|
||||
kubernetes_role_type: "Role"
|
||||
extra_labels: {}
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
models:
|
||||
- claude-haiku-4-5
|
||||
- claude-sonnet-4-6
|
||||
max_budget: 50
|
||||
ttl: 3600 # seconds (1h)
|
||||
max_ttl: 86400 # seconds (24h)
|
||||
|
||||
+3
-22
@@ -1,44 +1,25 @@
|
||||
# Single declarative source for the terraform-netbox NetBox service identity. The
|
||||
# Single declarative source for the terraform-infra NetBox service identity. The
|
||||
# filename stem is the engine role name AND the NetBox username (1:1); config.hcl
|
||||
# derives both from it, so neither is repeated below. Creating this file creates
|
||||
# the user: the netbox_user_management module synthesizes the NetBox user + object
|
||||
# permissions from the permissions block, and the engine role mints ephemeral
|
||||
# tokens for that same user. write_enabled true because terraform-netbox manages
|
||||
# tokens for that same user. write_enabled true because terraform-infra manages
|
||||
# NetBox IPAM/DCIM; very short TTLs because a token is minted per plan/apply and
|
||||
# revoked when the run's lease ends.
|
||||
#
|
||||
# extras.customfield is what lets the runner declare the custom fields bootapi
|
||||
# reads (pxe_enabled, provision_template, domain, gateway, nameservers), and
|
||||
# dcim.platform is how a device gets the platform slug bootapi selects a distro
|
||||
# catalog entry by.
|
||||
---
|
||||
write_enabled: true
|
||||
ttl: 120 # 2m
|
||||
max_ttl: 300 # 5m
|
||||
permissions:
|
||||
- name: terraform-netbox-write
|
||||
object_types:
|
||||
- object_types:
|
||||
- ipam.prefix
|
||||
- ipam.ipaddress
|
||||
- ipam.iprange
|
||||
- ipam.role
|
||||
- dcim.device
|
||||
- dcim.devicerole
|
||||
- dcim.devicetype
|
||||
- dcim.interface
|
||||
- dcim.macaddress
|
||||
- dcim.manufacturer
|
||||
- dcim.platform
|
||||
- extras.customfield
|
||||
- extras.tag
|
||||
actions:
|
||||
- view
|
||||
- add
|
||||
- change
|
||||
- delete
|
||||
# Sites are looked up with a data source, never created here.
|
||||
- name: terraform-netbox-read
|
||||
object_types:
|
||||
- dcim.site
|
||||
actions:
|
||||
- view
|
||||
@@ -7,12 +7,6 @@
|
||||
# sha256 pins the released v0.2.0 binary; bump it in lockstep with any RPM
|
||||
# upgrade or OpenBao will refuse to launch the plugin. Registration only
|
||||
# succeeds once the Puppet PR has installed the binary on the nodes.
|
||||
#
|
||||
# Terraform retunes mounts onto a bumped version; after apply, run
|
||||
# `bao write sys/plugins/reload/backend mounts=arrstack scope=global`.
|
||||
#
|
||||
# Once versioned, keep `version` set: dropping it leaves the mount pinned to a
|
||||
# deregistered version.
|
||||
type: secret
|
||||
command: vault-plugin-secrets-arrstack
|
||||
version: "0.2.0"
|
||||
|
||||
@@ -3,8 +3,6 @@ algorithm_signer: rsa-sha2-256
|
||||
ttl: 315360000 # 87600 * 3600
|
||||
allow_host_certificates: true
|
||||
allow_user_certificates: false
|
||||
# "*" must be the sole entry: OpenBao only treats allowed_domains as unrestricted
|
||||
# when the whole string is "*", and otherwise matches entries exactly or by suffix.
|
||||
allowed_domains: "*"
|
||||
allowed_domains: "unkin.net,main.unkin.net,consul"
|
||||
allow_subdomains: true
|
||||
allow_bare_domains: false
|
||||
|
||||
@@ -63,8 +63,6 @@ inputs = {
|
||||
auth_oidc_backend = local.config.auth_oidc_backend
|
||||
auth_oidc_role = local.config.auth_oidc_role
|
||||
auth_oidc_group = local.config.auth_oidc_group
|
||||
auth_jwt_backend = local.config.auth_jwt_backend
|
||||
auth_jwt_role = local.config.auth_jwt_role
|
||||
auth_kubernetes_backend = local.config.auth_kubernetes_backend
|
||||
auth_kubernetes_role = local.config.auth_kubernetes_role
|
||||
kv_secret_backend = local.config.kv_secret_backend
|
||||
@@ -100,14 +98,6 @@ inputs = {
|
||||
ghp_secret_backend = local.config.ghp_secret_backend
|
||||
ghp_secret_backend_role = local.config.ghp_secret_backend_role
|
||||
|
||||
identity_oidc = local.config.identity_oidc
|
||||
identity_oidc_key = local.config.identity_oidc_key
|
||||
identity_oidc_role = local.config.identity_oidc_role
|
||||
identity_oidc_client = local.config.identity_oidc_client
|
||||
identity_oidc_scope = local.config.identity_oidc_scope
|
||||
identity_oidc_assignment = local.config.identity_oidc_assignment
|
||||
identity_oidc_provider = local.config.identity_oidc_provider
|
||||
|
||||
# Pass policy maps to vault_cluster module
|
||||
policy_auth_map = local.policies.policy_auth_map
|
||||
policy_rules_map = local.policies.policy_rules_map
|
||||
|
||||
@@ -113,40 +113,6 @@ module "auth_oidc_group" {
|
||||
depends_on = [module.auth_oidc_backend]
|
||||
}
|
||||
|
||||
module "auth_jwt_backend" {
|
||||
source = "./modules/auth_jwt_backend"
|
||||
|
||||
for_each = var.auth_jwt_backend
|
||||
|
||||
path = each.key
|
||||
description = each.value.description
|
||||
oidc_discovery_url = each.value.oidc_discovery_url
|
||||
bound_issuer = each.value.bound_issuer
|
||||
default_role = each.value.default_role
|
||||
listing_visibility = each.value.listing_visibility
|
||||
default_lease_ttl = each.value.default_lease_ttl
|
||||
max_lease_ttl = each.value.max_lease_ttl
|
||||
}
|
||||
|
||||
module "auth_jwt_role" {
|
||||
source = "./modules/auth_jwt_role"
|
||||
|
||||
for_each = var.auth_jwt_role
|
||||
|
||||
backend = each.value.backend
|
||||
role_name = each.value.role_name
|
||||
user_claim = each.value.user_claim
|
||||
bound_audiences = each.value.bound_audiences
|
||||
backend_accessor = module.auth_jwt_backend[each.value.backend].accessor
|
||||
bound_claims = each.value.bound_claims
|
||||
claim_mappings = each.value.claim_mappings
|
||||
token_ttl = each.value.token_ttl
|
||||
token_max_ttl = each.value.token_max_ttl
|
||||
token_policies = try(var.policy_auth_map[each.value.backend][each.value.role_name], [])
|
||||
|
||||
depends_on = [module.auth_jwt_backend]
|
||||
}
|
||||
|
||||
module "auth_kubernetes_backend" {
|
||||
source = "./modules/auth_kubernetes_backend"
|
||||
|
||||
@@ -430,43 +396,6 @@ module "plugin" {
|
||||
plugin_version = each.value.version
|
||||
}
|
||||
|
||||
locals {
|
||||
plugin_mounts = merge(
|
||||
{ for path, mount in var.arrstack_secret_backend : path => mount.plugin },
|
||||
{ for path, mount in var.ghp_secret_backend : path => mount.plugin },
|
||||
{ for path, mount in var.gitea_secret_backend : path => mount.plugin },
|
||||
{ for path, mount in var.gpg_secret_backend : path => mount.plugin },
|
||||
{ for path, mount in var.litellm_secret_backend : path => mount.plugin },
|
||||
{ for path, mount in var.netbox_secret_backend : path => mount.plugin },
|
||||
{ for path, mount in var.rancher_secret_backend : path => mount.plugin },
|
||||
)
|
||||
}
|
||||
|
||||
# Pins each plugin-backed mount to its plugin's catalog version. Unversioned
|
||||
# catalog entries need no pin (OpenBao rejects an empty plugin_version tune).
|
||||
module "mount_plugin_version" {
|
||||
source = "./modules/mount_plugin_version"
|
||||
|
||||
for_each = {
|
||||
for path, plugin in local.plugin_mounts : path => var.plugins[plugin].version
|
||||
if try(var.plugins[plugin].version, null) != null
|
||||
}
|
||||
|
||||
path = each.key
|
||||
plugin_version = each.value
|
||||
|
||||
depends_on = [
|
||||
module.plugin,
|
||||
module.arrstack_secret_backend,
|
||||
module.ghp_secret_backend,
|
||||
module.gitea_secret_backend,
|
||||
module.gpg_secret_backend,
|
||||
module.litellm_secret_backend,
|
||||
module.netbox_secret_backend,
|
||||
module.rancher_secret_backend,
|
||||
]
|
||||
}
|
||||
|
||||
module "arrstack_secret_backend" {
|
||||
source = "./modules/arrstack_secret_backend"
|
||||
|
||||
@@ -754,105 +683,3 @@ module "pki_mount_only" {
|
||||
delta_rebuild_interval = each.value.delta_rebuild_interval
|
||||
}
|
||||
|
||||
|
||||
module "identity_oidc" {
|
||||
source = "./modules/identity_oidc"
|
||||
|
||||
for_each = var.identity_oidc
|
||||
|
||||
issuer = each.value.issuer
|
||||
}
|
||||
|
||||
module "identity_oidc_key" {
|
||||
source = "./modules/identity_oidc_key"
|
||||
|
||||
for_each = var.identity_oidc_key
|
||||
|
||||
name = each.key
|
||||
algorithm = each.value.algorithm
|
||||
rotation_period = each.value.rotation_period
|
||||
verification_ttl = each.value.verification_ttl
|
||||
|
||||
depends_on = [module.identity_oidc]
|
||||
}
|
||||
|
||||
module "identity_oidc_role" {
|
||||
source = "./modules/identity_oidc_role"
|
||||
|
||||
for_each = var.identity_oidc_role
|
||||
|
||||
name = each.value.name
|
||||
key = each.value.key
|
||||
template = each.value.template
|
||||
ttl = each.value.ttl
|
||||
client_id = each.value.client_id
|
||||
|
||||
depends_on = [module.identity_oidc, module.identity_oidc_key]
|
||||
}
|
||||
|
||||
module "identity_oidc_assignment" {
|
||||
source = "./modules/identity_oidc_assignment"
|
||||
|
||||
for_each = var.identity_oidc_assignment
|
||||
|
||||
name = each.key
|
||||
entity_ids = each.value.entity_ids
|
||||
group_ids = each.value.group_ids
|
||||
}
|
||||
|
||||
module "identity_oidc_client" {
|
||||
source = "./modules/identity_oidc_client"
|
||||
|
||||
for_each = var.identity_oidc_client
|
||||
|
||||
name = each.value.name
|
||||
key = each.value.key
|
||||
redirect_uris = each.value.redirect_uris
|
||||
assignments = each.value.assignments
|
||||
client_type = each.value.client_type
|
||||
id_token_ttl = each.value.id_token_ttl
|
||||
access_token_ttl = each.value.access_token_ttl
|
||||
|
||||
depends_on = [module.identity_oidc, module.identity_oidc_key, module.identity_oidc_assignment]
|
||||
}
|
||||
|
||||
module "identity_oidc_key_allowed_client_id" {
|
||||
source = "./modules/identity_oidc_key_allowed_client_id"
|
||||
|
||||
for_each = merge(
|
||||
{ for k, v in var.identity_oidc_role : "role/${k}" => { key = v.key, client_id = module.identity_oidc_role[k].client_id } },
|
||||
{ for k, v in var.identity_oidc_client : "client/${k}" => { key = v.key, client_id = module.identity_oidc_client[k].client_id } },
|
||||
)
|
||||
|
||||
key_name = each.value.key
|
||||
allowed_client_id = each.value.client_id
|
||||
|
||||
depends_on = [module.identity_oidc_key]
|
||||
}
|
||||
|
||||
module "identity_oidc_scope" {
|
||||
source = "./modules/identity_oidc_scope"
|
||||
|
||||
for_each = var.identity_oidc_scope
|
||||
|
||||
name = each.key
|
||||
description = each.value.description
|
||||
template = each.value.template
|
||||
}
|
||||
|
||||
module "identity_oidc_provider" {
|
||||
source = "./modules/identity_oidc_provider"
|
||||
|
||||
for_each = var.identity_oidc_provider
|
||||
|
||||
name = each.key
|
||||
issuer_host = each.value.issuer_host
|
||||
https_enabled = each.value.https_enabled
|
||||
scopes_supported = each.value.scopes_supported
|
||||
allowed_client_ids = [
|
||||
for c in each.value.allowed_clients :
|
||||
c == "*" ? c : one([for k, v in var.identity_oidc_client : module.identity_oidc_client[k].client_id if v.name == c])
|
||||
]
|
||||
|
||||
depends_on = [module.identity_oidc, module.identity_oidc_scope, module.identity_oidc_client]
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
resource "vault_jwt_auth_backend" "jwt" {
|
||||
path = var.path
|
||||
type = "jwt"
|
||||
description = var.description
|
||||
oidc_discovery_url = var.oidc_discovery_url
|
||||
bound_issuer = var.bound_issuer
|
||||
default_role = var.default_role
|
||||
|
||||
# provider 5.6.0 panics on an all-null tune block
|
||||
dynamic "tune" {
|
||||
for_each = anytrue([for v in [var.default_lease_ttl, var.max_lease_ttl, var.listing_visibility] : v != null]) ? [1] : []
|
||||
content {
|
||||
default_lease_ttl = var.default_lease_ttl
|
||||
max_lease_ttl = var.max_lease_ttl
|
||||
listing_visibility = var.listing_visibility
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
output "accessor" {
|
||||
value = vault_jwt_auth_backend.jwt.accessor
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
terraform {
|
||||
required_version = ">= 1.10"
|
||||
required_providers {
|
||||
vault = {
|
||||
source = "hashicorp/vault"
|
||||
version = "5.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
mock_provider "vault" {}
|
||||
|
||||
variables {
|
||||
path = "jwt/github/iuclabs"
|
||||
oidc_discovery_url = "https://token.actions.githubusercontent.com"
|
||||
bound_issuer = "https://token.actions.githubusercontent.com"
|
||||
}
|
||||
|
||||
run "omit_tune_when_unset" {
|
||||
command = plan
|
||||
|
||||
assert {
|
||||
condition = length(vault_jwt_auth_backend.jwt.tune) == 0
|
||||
error_message = "no tune block may render when every tune value is null"
|
||||
}
|
||||
}
|
||||
|
||||
run "render_tune_when_set" {
|
||||
command = plan
|
||||
variables { max_lease_ttl = "1h" }
|
||||
|
||||
assert {
|
||||
condition = length(vault_jwt_auth_backend.jwt.tune) == 1 && vault_jwt_auth_backend.jwt.tune[0].max_lease_ttl == "1h"
|
||||
error_message = "tune block must render with the configured values"
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
variable "path" {
|
||||
description = "Mount path of the JWT auth backend"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "description" {
|
||||
description = "Human-readable description of the auth mount"
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "oidc_discovery_url" {
|
||||
description = "OIDC discovery URL used to fetch the issuer's signing keys"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "bound_issuer" {
|
||||
description = "Value the iss claim of every presented token must match"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "default_role" {
|
||||
description = "Role used when a login request names none"
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "listing_visibility" {
|
||||
description = "Specifies whether to show this mount in the UI-specific listing endpoint. Valid values are 'unauth' or 'hidden'"
|
||||
type = string
|
||||
default = null
|
||||
validation {
|
||||
condition = var.listing_visibility == null || contains(["unauth", "hidden"], var.listing_visibility)
|
||||
error_message = "listing_visibility must be either 'unauth' or 'hidden'."
|
||||
}
|
||||
}
|
||||
|
||||
variable "default_lease_ttl" {
|
||||
description = "Specifies the default time-to-live. If set, this overrides the global default. Must be a valid duration string"
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "max_lease_ttl" {
|
||||
description = "Specifies the maximum time-to-live. If set, this overrides the global default. Must be a valid duration string"
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
locals {
|
||||
alias = "identity.entity.aliases.${var.backend_accessor}.metadata"
|
||||
kv_prefixes = [
|
||||
"github/{{${local.alias}.repository}}/event/{{${local.alias}.event_name}}",
|
||||
"github/{{${local.alias}.repository}}/ref/{{${local.alias}.ref}}/.secrets",
|
||||
]
|
||||
}
|
||||
|
||||
resource "vault_policy" "kv" {
|
||||
name = "${var.backend}/${var.role_name}/kv-read"
|
||||
policy = join("\n", flatten([for p in local.kv_prefixes : [
|
||||
"path \"kv/data/${p}/*\" {\n capabilities = [\"read\"]\n}",
|
||||
"path \"kv/metadata/${p}/*\" {\n capabilities = [\"read\", \"list\"]\n}",
|
||||
]]))
|
||||
}
|
||||
|
||||
resource "vault_jwt_auth_backend_role" "role" {
|
||||
backend = var.backend
|
||||
role_name = var.role_name
|
||||
role_type = "jwt"
|
||||
user_claim = var.user_claim
|
||||
bound_audiences = var.bound_audiences
|
||||
bound_claims = var.bound_claims
|
||||
bound_claims_type = "glob"
|
||||
claim_mappings = var.claim_mappings
|
||||
token_ttl = var.token_ttl
|
||||
token_max_ttl = var.token_max_ttl
|
||||
token_policies = concat(var.token_policies, [vault_policy.kv.name])
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
terraform {
|
||||
required_version = ">= 1.10"
|
||||
required_providers {
|
||||
vault = {
|
||||
source = "hashicorp/vault"
|
||||
version = "5.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
mock_provider "vault" {}
|
||||
|
||||
variables {
|
||||
backend = "jwt/github/iuclabs"
|
||||
role_name = "default"
|
||||
backend_accessor = "auth_jwt_test"
|
||||
bound_audiences = ["https://vault.service.consul:8200"]
|
||||
}
|
||||
|
||||
run "accept_org_role" {
|
||||
command = plan
|
||||
variables { bound_claims = { repository_owner = "IUCLabs", event_name = "push,release,schedule,pull_request", ref = "refs/heads/*,refs/tags/*,refs/pull/*/merge" } }
|
||||
|
||||
assert {
|
||||
condition = vault_policy.kv.policy == chomp(<<-EOT
|
||||
path "kv/data/github/{{identity.entity.aliases.auth_jwt_test.metadata.repository}}/event/{{identity.entity.aliases.auth_jwt_test.metadata.event_name}}/*" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
path "kv/metadata/github/{{identity.entity.aliases.auth_jwt_test.metadata.repository}}/event/{{identity.entity.aliases.auth_jwt_test.metadata.event_name}}/*" {
|
||||
capabilities = ["read", "list"]
|
||||
}
|
||||
path "kv/data/github/{{identity.entity.aliases.auth_jwt_test.metadata.repository}}/ref/{{identity.entity.aliases.auth_jwt_test.metadata.ref}}/.secrets/*" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
path "kv/metadata/github/{{identity.entity.aliases.auth_jwt_test.metadata.repository}}/ref/{{identity.entity.aliases.auth_jwt_test.metadata.ref}}/.secrets/*" {
|
||||
capabilities = ["read", "list"]
|
||||
}
|
||||
EOT
|
||||
)
|
||||
error_message = "rendered KV policy paths changed"
|
||||
}
|
||||
assert {
|
||||
condition = contains(vault_jwt_auth_backend_role.role.token_policies, "jwt/github/iuclabs/default/kv-read")
|
||||
error_message = "role must carry the KV read policy"
|
||||
}
|
||||
assert {
|
||||
condition = vault_jwt_auth_backend_role.role.bound_claims_type == "glob" && vault_jwt_auth_backend_role.role.user_claim == "jti"
|
||||
error_message = "role must glob-match claims and alias on jti"
|
||||
}
|
||||
}
|
||||
|
||||
run "ref_paths_cannot_overlap_child_refs" {
|
||||
command = plan
|
||||
variables { bound_claims = { repository_owner = "IUCLabs", event_name = "push", ref = "refs/heads/*" } }
|
||||
|
||||
assert {
|
||||
condition = length(regexall("metadata\\.ref}}/[^.]", vault_policy.kv.policy)) == 0
|
||||
error_message = "ref paths must end in a dot-prefixed segment git refnames cannot contain"
|
||||
}
|
||||
}
|
||||
|
||||
run "accept_exact_refs" {
|
||||
command = plan
|
||||
variables { bound_claims = { repository_owner = "IUCLabs", event_name = "push,release,schedule,pull_request", ref = "refs/heads/main,refs/tags/v1.2.3,refs/pull/7/merge" } }
|
||||
|
||||
assert {
|
||||
condition = vault_jwt_auth_backend_role.role.bound_claims.ref == "refs/heads/main,refs/tags/v1.2.3,refs/pull/7/merge"
|
||||
error_message = "exact refs must pass through to bound_claims unchanged"
|
||||
}
|
||||
}
|
||||
|
||||
run "accept_with_repository" {
|
||||
command = plan
|
||||
variables { bound_claims = { repository_owner = "IUCLabs", event_name = "push,release,schedule,pull_request", ref = "refs/heads/*,refs/tags/*,refs/pull/*/merge", repository = "IUCLabs/app" } }
|
||||
}
|
||||
|
||||
run "reject_missing_repository_owner" {
|
||||
command = plan
|
||||
variables { bound_claims = { event_name = "push,release,schedule,pull_request", ref = "refs/heads/*,refs/tags/*,refs/pull/*/merge" } }
|
||||
expect_failures = [var.bound_claims]
|
||||
}
|
||||
|
||||
run "reject_missing_ref" {
|
||||
command = plan
|
||||
variables { bound_claims = { repository_owner = "IUCLabs", event_name = "push,release,schedule,pull_request" } }
|
||||
expect_failures = [var.bound_claims]
|
||||
}
|
||||
|
||||
run "reject_missing_event_name" {
|
||||
command = plan
|
||||
variables { bound_claims = { repository_owner = "IUCLabs", ref = "refs/heads/*,refs/tags/*,refs/pull/*/merge" } }
|
||||
expect_failures = [var.bound_claims]
|
||||
}
|
||||
|
||||
run "reject_repository_owner_wildcard" {
|
||||
command = plan
|
||||
variables { bound_claims = { repository_owner = "*", event_name = "push,release,schedule,pull_request", ref = "refs/heads/*,refs/tags/*,refs/pull/*/merge" } }
|
||||
expect_failures = [var.bound_claims]
|
||||
}
|
||||
|
||||
run "reject_repository_owner_glob" {
|
||||
command = plan
|
||||
variables { bound_claims = { repository_owner = "IUC*", event_name = "push,release,schedule,pull_request", ref = "refs/heads/*,refs/tags/*,refs/pull/*/merge" } }
|
||||
expect_failures = [var.bound_claims]
|
||||
}
|
||||
|
||||
run "reject_event_pull_request_target" {
|
||||
command = plan
|
||||
variables { bound_claims = { repository_owner = "IUCLabs", event_name = "push,pull_request_target", ref = "refs/heads/*,refs/tags/*,refs/pull/*/merge" } }
|
||||
expect_failures = [var.bound_claims]
|
||||
}
|
||||
|
||||
run "reject_event_workflow_run" {
|
||||
command = plan
|
||||
variables { bound_claims = { repository_owner = "IUCLabs", event_name = "workflow_run", ref = "refs/heads/*,refs/tags/*,refs/pull/*/merge" } }
|
||||
expect_failures = [var.bound_claims]
|
||||
}
|
||||
|
||||
run "reject_event_issue_comment" {
|
||||
command = plan
|
||||
variables { bound_claims = { repository_owner = "IUCLabs", event_name = "issue_comment", ref = "refs/heads/*,refs/tags/*,refs/pull/*/merge" } }
|
||||
expect_failures = [var.bound_claims]
|
||||
}
|
||||
|
||||
run "reject_event_workflow_dispatch" {
|
||||
command = plan
|
||||
variables { bound_claims = { repository_owner = "IUCLabs", event_name = "workflow_dispatch", ref = "refs/heads/*,refs/tags/*,refs/pull/*/merge" } }
|
||||
expect_failures = [var.bound_claims]
|
||||
}
|
||||
|
||||
run "reject_ref_star" {
|
||||
command = plan
|
||||
variables { bound_claims = { repository_owner = "IUCLabs", event_name = "push,release,schedule,pull_request", ref = "refs/*" } }
|
||||
expect_failures = [var.bound_claims]
|
||||
}
|
||||
|
||||
run "reject_ref_bare_star" {
|
||||
command = plan
|
||||
variables { bound_claims = { repository_owner = "IUCLabs", event_name = "push,release,schedule,pull_request", ref = "*" } }
|
||||
expect_failures = [var.bound_claims]
|
||||
}
|
||||
|
||||
run "reject_ref_pull_star" {
|
||||
command = plan
|
||||
variables { bound_claims = { repository_owner = "IUCLabs", event_name = "push,release,schedule,pull_request", ref = "refs/pull/*" } }
|
||||
expect_failures = [var.bound_claims]
|
||||
}
|
||||
|
||||
run "reject_ref_pull_head" {
|
||||
command = plan
|
||||
variables { bound_claims = { repository_owner = "IUCLabs", event_name = "push,release,schedule,pull_request", ref = "refs/pull/*/head" } }
|
||||
expect_failures = [var.bound_claims]
|
||||
}
|
||||
|
||||
run "reject_ref_heads_prefix_glob" {
|
||||
command = plan
|
||||
variables { bound_claims = { repository_owner = "IUCLabs", event_name = "push,release,schedule,pull_request", ref = "refs/heads/rel*" } }
|
||||
expect_failures = [var.bound_claims]
|
||||
}
|
||||
|
||||
run "reject_ref_empty" {
|
||||
command = plan
|
||||
variables { bound_claims = { repository_owner = "IUCLabs", event_name = "push,release,schedule,pull_request", ref = "" } }
|
||||
expect_failures = [var.bound_claims]
|
||||
}
|
||||
|
||||
run "reject_repository_glob" {
|
||||
command = plan
|
||||
variables { bound_claims = { repository_owner = "IUCLabs", event_name = "push,release,schedule,pull_request", ref = "refs/heads/*,refs/tags/*,refs/pull/*/merge", repository = "IUCLabs/*" } }
|
||||
expect_failures = [var.bound_claims]
|
||||
}
|
||||
|
||||
run "reject_environment_empty" {
|
||||
command = plan
|
||||
variables { bound_claims = { repository_owner = "IUCLabs", event_name = "push,release,schedule,pull_request", ref = "refs/heads/*,refs/tags/*,refs/pull/*/merge", environment = "" } }
|
||||
expect_failures = [var.bound_claims]
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
variable "backend" {
|
||||
description = "The unique path of the JWT auth backend to configure"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "role_name" {
|
||||
description = "The name of the role"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "backend_accessor" {
|
||||
description = "Accessor of the JWT auth mount, used to template the KV read policy"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "user_claim" {
|
||||
description = "Claim used as the entity alias name"
|
||||
type = string
|
||||
default = "jti"
|
||||
}
|
||||
|
||||
variable "claim_mappings" {
|
||||
description = "Token claims copied into entity alias metadata"
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "bound_audiences" {
|
||||
description = "List of audiences (aud claim) accepted in the token"
|
||||
type = list(string)
|
||||
validation {
|
||||
condition = length(var.bound_audiences) > 0
|
||||
error_message = "bound_audiences must not be empty."
|
||||
}
|
||||
}
|
||||
|
||||
variable "bound_claims" {
|
||||
description = "Claims the token must carry, glob-matched; only ref may use the allowed glob patterns, comma-separated values form a list"
|
||||
type = map(string)
|
||||
validation {
|
||||
condition = can(regex("^[A-Za-z0-9-]+$", lookup(var.bound_claims, "repository_owner", "")))
|
||||
error_message = "bound_claims.repository_owner must be one exact org name."
|
||||
}
|
||||
validation {
|
||||
condition = alltrue([for v in split(",", lookup(var.bound_claims, "event_name", "")) : contains(["push", "release", "schedule", "pull_request"], trimspace(v))])
|
||||
error_message = "bound_claims.event_name must only list push, release, schedule or pull_request."
|
||||
}
|
||||
validation {
|
||||
condition = alltrue([for v in split(",", lookup(var.bound_claims, "ref", "")) :
|
||||
contains(["refs/heads/*", "refs/tags/*", "refs/pull/*/merge"], trimspace(v)) ||
|
||||
can(regex("^(refs/(heads|tags)/[A-Za-z0-9._/-]+|refs/pull/[0-9]+/merge)$", trimspace(v)))
|
||||
])
|
||||
error_message = "bound_claims.ref must be refs/heads/*, refs/tags/*, refs/pull/*/merge or exact refs of those shapes."
|
||||
}
|
||||
validation {
|
||||
condition = alltrue(flatten([for k, v in var.bound_claims : [
|
||||
for e in split(",", v) : trimspace(e) != "" && !strcontains(e, "*")
|
||||
] if k != "ref"]))
|
||||
error_message = "bound_claims values other than ref must be exact and non-empty."
|
||||
}
|
||||
}
|
||||
|
||||
variable "token_ttl" {
|
||||
description = "The TTL period of tokens issued using this role, in seconds"
|
||||
type = number
|
||||
default = 900
|
||||
}
|
||||
|
||||
variable "token_max_ttl" {
|
||||
description = "The maximum lifetime for generated tokens in number of seconds"
|
||||
type = number
|
||||
default = 3600
|
||||
}
|
||||
|
||||
variable "token_policies" {
|
||||
description = "List of policies to assign to the role (passed from policy_auth_map)"
|
||||
type = list(string)
|
||||
default = []
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
resource "vault_identity_oidc" "this" {
|
||||
issuer = var.issuer
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
terraform {
|
||||
required_version = ">= 1.10"
|
||||
required_providers {
|
||||
vault = {
|
||||
source = "hashicorp/vault"
|
||||
version = "5.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
variable "issuer" {
|
||||
description = "Issuer URL for identity tokens (scheme://host:port, no path); null uses Vault's api_addr"
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
resource "vault_identity_oidc_assignment" "this" {
|
||||
name = var.name
|
||||
entity_ids = var.entity_ids
|
||||
group_ids = var.group_ids
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
terraform {
|
||||
required_version = ">= 1.10"
|
||||
required_providers {
|
||||
vault = {
|
||||
source = "hashicorp/vault"
|
||||
version = "5.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
variable "name" {
|
||||
description = "Assignment name"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "entity_ids" {
|
||||
description = "Identity entity ids in this assignment"
|
||||
type = list(string)
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "group_ids" {
|
||||
description = "Identity group ids in this assignment"
|
||||
type = list(string)
|
||||
default = []
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
resource "vault_identity_oidc_client" "this" {
|
||||
name = var.name
|
||||
key = var.key
|
||||
redirect_uris = var.redirect_uris
|
||||
assignments = var.assignments
|
||||
client_type = var.client_type
|
||||
id_token_ttl = var.id_token_ttl
|
||||
access_token_ttl = var.access_token_ttl
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
output "client_id" {
|
||||
value = vault_identity_oidc_client.this.client_id
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
terraform {
|
||||
required_version = ">= 1.10"
|
||||
required_providers {
|
||||
vault = {
|
||||
source = "hashicorp/vault"
|
||||
version = "5.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
variable "name" {
|
||||
description = "OIDC client name"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "key" {
|
||||
description = "Named key that signs this client's ID tokens"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "redirect_uris" {
|
||||
description = "Allowed redirect URIs"
|
||||
type = list(string)
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "assignments" {
|
||||
description = "Assignment names allowed to authenticate with this client"
|
||||
type = list(string)
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "client_type" {
|
||||
description = "confidential or public"
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "id_token_ttl" {
|
||||
description = "ID token TTL in seconds"
|
||||
type = number
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "access_token_ttl" {
|
||||
description = "Access token TTL in seconds"
|
||||
type = number
|
||||
default = null
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
resource "vault_identity_oidc_key" "this" {
|
||||
name = var.name
|
||||
algorithm = var.algorithm
|
||||
rotation_period = var.rotation_period
|
||||
verification_ttl = var.verification_ttl
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
terraform {
|
||||
required_version = ">= 1.10"
|
||||
required_providers {
|
||||
vault = {
|
||||
source = "hashicorp/vault"
|
||||
version = "5.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
variable "name" {
|
||||
description = "Name of the named key"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "algorithm" {
|
||||
description = "Signing algorithm (RS256, RS384, RS512, ES256, ES384, ES512, EdDSA)"
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "rotation_period" {
|
||||
description = "Seconds between key rotations"
|
||||
type = number
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "verification_ttl" {
|
||||
description = "Seconds a rotated public key stays published for verification"
|
||||
type = number
|
||||
default = null
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
resource "vault_identity_oidc_key_allowed_client_id" "this" {
|
||||
key_name = var.key_name
|
||||
allowed_client_id = var.allowed_client_id
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
terraform {
|
||||
required_version = ">= 1.10"
|
||||
required_providers {
|
||||
vault = {
|
||||
source = "hashicorp/vault"
|
||||
version = "5.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
variable "key_name" {
|
||||
description = "Named key the client id may sign with"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "allowed_client_id" {
|
||||
description = "Role or client id allowed to use the key"
|
||||
type = string
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
resource "vault_identity_oidc_provider" "this" {
|
||||
name = var.name
|
||||
issuer_host = var.issuer_host
|
||||
https_enabled = var.https_enabled
|
||||
scopes_supported = var.scopes_supported
|
||||
allowed_client_ids = var.allowed_client_ids
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
terraform {
|
||||
required_version = ">= 1.10"
|
||||
required_providers {
|
||||
vault = {
|
||||
source = "hashicorp/vault"
|
||||
version = "5.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
variable "name" {
|
||||
description = "OIDC provider name"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "issuer_host" {
|
||||
description = "Issuer host[:port]; null uses Vault's api_addr"
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "https_enabled" {
|
||||
description = "Use https in the issuer URL"
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "scopes_supported" {
|
||||
description = "Scope names the provider serves"
|
||||
type = list(string)
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "allowed_client_ids" {
|
||||
description = "Client ids allowed to use the provider (\"*\" allows all)"
|
||||
type = list(string)
|
||||
default = []
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
resource "vault_identity_oidc_role" "this" {
|
||||
name = var.name
|
||||
key = var.key
|
||||
template = var.template
|
||||
ttl = var.ttl
|
||||
client_id = var.client_id
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
output "client_id" {
|
||||
value = vault_identity_oidc_role.this.client_id
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
terraform {
|
||||
required_version = ">= 1.10"
|
||||
required_providers {
|
||||
vault = {
|
||||
source = "hashicorp/vault"
|
||||
version = "5.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
variable "name" {
|
||||
description = "Role name (read identity/oidc/token/<name> to mint a token)"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "key" {
|
||||
description = "Named key that signs this role's tokens"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "template" {
|
||||
description = "JSON claims template for issued tokens"
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "ttl" {
|
||||
description = "Token TTL in seconds"
|
||||
type = number
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "client_id" {
|
||||
description = "Token audience (aud); null lets Vault generate one"
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
resource "vault_identity_oidc_scope" "this" {
|
||||
name = var.name
|
||||
description = var.description
|
||||
template = var.template
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
terraform {
|
||||
required_version = ">= 1.10"
|
||||
required_providers {
|
||||
vault = {
|
||||
source = "hashicorp/vault"
|
||||
version = "5.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
variable "name" {
|
||||
description = "Scope name"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "description" {
|
||||
description = "Scope description"
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "template" {
|
||||
description = "JSON claims template added when this scope is requested"
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
@@ -16,7 +16,7 @@ locals {
|
||||
resource "vault_kubernetes_secret_backend_role" "role" {
|
||||
backend = var.backend
|
||||
name = var.name
|
||||
allowed_kubernetes_namespaces = sort(var.allowed_kubernetes_namespaces)
|
||||
allowed_kubernetes_namespaces = var.allowed_kubernetes_namespaces
|
||||
kubernetes_role_type = local.use_service_account ? null : var.kubernetes_role_type
|
||||
generated_role_rules = local.use_service_account ? null : file("${path.module}/../../../../../../../../${local.role_rules_file}")
|
||||
service_account_name = var.service_account_name
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
# Pins a plugin-backed mount to its catalog version (sys/mounts/<path>/tune).
|
||||
# OpenBao normalises plugin_version to a leading "v" and reads it back that way.
|
||||
# Tune has no delete; destroying this leaves the mount's pin in place.
|
||||
resource "vault_generic_endpoint" "this" {
|
||||
path = "sys/mounts/${var.path}/tune"
|
||||
data_json = jsonencode({ plugin_version = "v${trimprefix(var.plugin_version, "v")}" })
|
||||
ignore_absent_fields = true
|
||||
disable_delete = true
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
terraform {
|
||||
required_version = ">= 1.10"
|
||||
required_providers {
|
||||
vault = {
|
||||
source = "hashicorp/vault"
|
||||
version = "5.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
variable "path" {
|
||||
description = "Mount path of the plugin-backed secrets engine"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "plugin_version" {
|
||||
description = "Catalog version to pin the mount to (with or without a leading v)"
|
||||
type = string
|
||||
}
|
||||
@@ -9,10 +9,4 @@ resource "vault_plugin" "this" {
|
||||
command = coalesce(var.command, var.name)
|
||||
sha256 = var.sha256
|
||||
version = var.plugin_version
|
||||
|
||||
# A version bump registers the new entry, retunes mounts onto it, then
|
||||
# deregisters the old one, so no mount is left pinned to a missing version.
|
||||
lifecycle {
|
||||
create_before_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,9 +425,3 @@ moved {
|
||||
from = vault_policy.policies["pki_root/pki_root_roles_admin"]
|
||||
to = module.vault_policy["pki_root/roles/admin"].vault_policy.this
|
||||
}
|
||||
|
||||
# Mount enabled by a create that crashed before state was written
|
||||
import {
|
||||
to = module.auth_jwt_backend["jwt/github/iuclabs"].vault_jwt_auth_backend.jwt
|
||||
id = "jwt/github/iuclabs"
|
||||
}
|
||||
|
||||
@@ -102,35 +102,6 @@ variable "auth_oidc_group" {
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "auth_jwt_backend" {
|
||||
description = "Map of JWT auth backends (machine OIDC token login) to create"
|
||||
type = map(object({
|
||||
oidc_discovery_url = string
|
||||
bound_issuer = string
|
||||
default_role = optional(string)
|
||||
description = optional(string)
|
||||
listing_visibility = optional(string)
|
||||
default_lease_ttl = optional(string)
|
||||
max_lease_ttl = optional(string)
|
||||
}))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "auth_jwt_role" {
|
||||
description = "Map of JWT auth roles to create"
|
||||
type = map(object({
|
||||
role_name = string
|
||||
backend = string
|
||||
bound_audiences = list(string)
|
||||
bound_claims = map(string)
|
||||
claim_mappings = optional(map(string), {})
|
||||
user_claim = optional(string, "jti")
|
||||
token_ttl = optional(number, 900)
|
||||
token_max_ttl = optional(number, 3600)
|
||||
}))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "auth_kubernetes_backend" {
|
||||
description = "Map of Kubernetes auth backends to create"
|
||||
type = map(object({
|
||||
@@ -607,76 +578,3 @@ variable "policy_rules_map" {
|
||||
default = {}
|
||||
}
|
||||
|
||||
|
||||
variable "identity_oidc" {
|
||||
description = "Identity token issuer config (singleton in practice)"
|
||||
type = map(object({
|
||||
issuer = optional(string)
|
||||
}))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "identity_oidc_key" {
|
||||
description = "Map of identity OIDC named keys to create"
|
||||
type = map(object({
|
||||
algorithm = optional(string, "RS256")
|
||||
rotation_period = optional(number, 86400)
|
||||
verification_ttl = optional(number, 86400)
|
||||
}))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "identity_oidc_role" {
|
||||
description = "Map of identity token roles to create, keyed <key>/<role>"
|
||||
type = map(object({
|
||||
name = string
|
||||
key = string
|
||||
template = optional(string)
|
||||
ttl = optional(number, 86400)
|
||||
client_id = optional(string)
|
||||
}))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "identity_oidc_client" {
|
||||
description = "Map of identity OIDC provider clients to create, keyed <key>/<client>"
|
||||
type = map(object({
|
||||
name = string
|
||||
key = string
|
||||
redirect_uris = optional(list(string), [])
|
||||
assignments = optional(list(string), [])
|
||||
client_type = optional(string, "confidential")
|
||||
id_token_ttl = optional(number, 86400)
|
||||
access_token_ttl = optional(number, 86400)
|
||||
}))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "identity_oidc_scope" {
|
||||
description = "Map of identity OIDC provider scopes to create"
|
||||
type = map(object({
|
||||
description = optional(string)
|
||||
template = optional(string)
|
||||
}))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "identity_oidc_assignment" {
|
||||
description = "Map of identity OIDC client assignments to create"
|
||||
type = map(object({
|
||||
entity_ids = optional(list(string), [])
|
||||
group_ids = optional(list(string), [])
|
||||
}))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "identity_oidc_provider" {
|
||||
description = "Map of identity OIDC providers to create; allowed_clients are client names or \"*\""
|
||||
type = map(object({
|
||||
issuer_host = optional(string)
|
||||
https_enabled = optional(bool, true)
|
||||
scopes_supported = optional(list(string), [])
|
||||
allowed_clients = optional(list(string), [])
|
||||
}))
|
||||
default = {}
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
# Allow administration of the GitHub Actions (IUCLabs) JWT auth backend: mount
|
||||
# config and per-repo workflow roles.
|
||||
---
|
||||
rules:
|
||||
- path: "auth/jwt/github/iuclabs/*"
|
||||
capabilities:
|
||||
- create
|
||||
- update
|
||||
- read
|
||||
- delete
|
||||
- list
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- tf_vault
|
||||
k8s/au/syd1:
|
||||
- woodpecker_terraform_vault
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
rules:
|
||||
- path: "consul_root/au/syd1/creds/terraform-binarylane"
|
||||
capabilities:
|
||||
- read
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- terraform_binarylane
|
||||
k8s/au/syd1:
|
||||
- woodpecker_terraform_binarylane
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
rules:
|
||||
- path: "consul_root/au/syd1/creds/terraform-infra"
|
||||
capabilities:
|
||||
- read
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- terraform_infra
|
||||
k8s/au/syd1:
|
||||
- woodpecker_terraform_infra
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
rules:
|
||||
- path: "consul_root/au/syd1/creds/terraform-netbox"
|
||||
capabilities:
|
||||
- read
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- terraform_netbox
|
||||
k8s/au/syd1:
|
||||
- woodpecker_terraform_netbox
|
||||
+15
-3
@@ -1,8 +1,20 @@
|
||||
# Allow the vault deployer to manage the gpg mount's OpenPGP keys via the
|
||||
# gpgvaultsecret provider. Registering the plugin itself is a sys/plugins/catalog
|
||||
# grant, carried by policies/sys/plugins/catalog/admin.yaml.
|
||||
# Allow the vault deployer to import the gpg plugin and manage its OpenPGP keys.
|
||||
#
|
||||
# terraform-vault registers the plugin itself (vault_plugin -> sys/plugins/catalog,
|
||||
# a sudo-protected path) and manages keys via the gpgvaultsecret provider, so the
|
||||
# deployer needs catalog access on top of the mount access it already has
|
||||
# (sys/mounts/*). Without this, apply 403s on the plugin registration and on
|
||||
# gpg/keys writes.
|
||||
---
|
||||
rules:
|
||||
# Import / register (and deregister) the gpg plugin in the catalog.
|
||||
- path: "sys/plugins/catalog/secret/vault-plugin-secrets-gpg"
|
||||
capabilities:
|
||||
- create
|
||||
- read
|
||||
- update
|
||||
- delete
|
||||
- sudo
|
||||
# Manage keys (create/rotate/config/delete) in the gpg mount.
|
||||
- path: "gpg/keys/*"
|
||||
capabilities:
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
# Allow the deployer to manage the identity group aliases that map external OIDC
|
||||
# group membership (the ak_groups claim) onto the Vault groups managed under
|
||||
# policies/identity/group/. Both endpoints are needed: create posts to
|
||||
# identity/group-alias, subsequent reads and updates address
|
||||
# identity/group-alias/id/<id>.
|
||||
---
|
||||
rules:
|
||||
- path: "identity/group-alias"
|
||||
capabilities:
|
||||
- create
|
||||
- update
|
||||
- path: "identity/group-alias/*"
|
||||
capabilities:
|
||||
- create
|
||||
- update
|
||||
- read
|
||||
- delete
|
||||
- list
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- tf_vault
|
||||
k8s/au/syd1:
|
||||
- woodpecker_terraform_vault
|
||||
@@ -1,7 +1,7 @@
|
||||
# Allow the deployer to manage the external identity groups that OIDC group
|
||||
# membership (the ak_groups claim) maps onto Vault policies through. Both the
|
||||
# collection endpoint and the per-id endpoints are needed: create posts to
|
||||
# identity/group, subsequent reads and updates address identity/group/id/<id>.
|
||||
# Allow the deployer to manage external identity groups and their aliases, which
|
||||
# is how OIDC group membership (the ak_groups claim) maps onto Vault policies.
|
||||
# Both the collection endpoints and the per-id endpoints are needed: create posts
|
||||
# to identity/group, subsequent reads and updates address identity/group/id/<id>.
|
||||
---
|
||||
rules:
|
||||
- path: "identity/group"
|
||||
@@ -15,6 +15,21 @@ rules:
|
||||
- read
|
||||
- delete
|
||||
- list
|
||||
- path: "identity/group-alias"
|
||||
capabilities:
|
||||
- create
|
||||
- update
|
||||
- path: "identity/group-alias/*"
|
||||
capabilities:
|
||||
- create
|
||||
- update
|
||||
- read
|
||||
- delete
|
||||
- list
|
||||
- path: "identity/lookup/group"
|
||||
capabilities:
|
||||
- create
|
||||
- update
|
||||
|
||||
auth:
|
||||
approle:
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
# Allow the deployer to look up an identity group by name, which is how it
|
||||
# resolves the group it is about to update under policies/identity/group/.
|
||||
---
|
||||
rules:
|
||||
- path: "identity/lookup/group"
|
||||
capabilities:
|
||||
- create
|
||||
- update
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- tf_vault
|
||||
k8s/au/syd1:
|
||||
- woodpecker_terraform_vault
|
||||
@@ -1,51 +0,0 @@
|
||||
# Allow the deployer to manage identity OIDC objects through the vault_identity_oidc*
|
||||
# resources, which only read, write and delete by name and never list or rotate.
|
||||
---
|
||||
rules:
|
||||
- path: "identity/oidc/config"
|
||||
capabilities:
|
||||
- create
|
||||
- read
|
||||
- update
|
||||
- path: "identity/oidc/key/*"
|
||||
capabilities:
|
||||
- create
|
||||
- read
|
||||
- update
|
||||
- delete
|
||||
- path: "identity/oidc/role/*"
|
||||
capabilities:
|
||||
- create
|
||||
- read
|
||||
- update
|
||||
- delete
|
||||
- path: "identity/oidc/client/*"
|
||||
capabilities:
|
||||
- create
|
||||
- read
|
||||
- update
|
||||
- delete
|
||||
- path: "identity/oidc/scope/*"
|
||||
capabilities:
|
||||
- create
|
||||
- read
|
||||
- update
|
||||
- delete
|
||||
- path: "identity/oidc/assignment/*"
|
||||
capabilities:
|
||||
- create
|
||||
- read
|
||||
- update
|
||||
- delete
|
||||
- path: "identity/oidc/provider/*"
|
||||
capabilities:
|
||||
- create
|
||||
- read
|
||||
- update
|
||||
- delete
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- tf_vault
|
||||
k8s/au/syd1:
|
||||
- woodpecker_terraform_vault
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
rules:
|
||||
- path: "identity/oidc/token/artifactapi-writer"
|
||||
capabilities:
|
||||
- read
|
||||
|
||||
auth:
|
||||
k8s/au/syd1:
|
||||
- woodpecker_artifactapi_writer
|
||||
ldap:
|
||||
- vault_admin
|
||||
oidc:
|
||||
- akP-vault-admin
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
rules:
|
||||
- path: "identity/oidc/token/encapi-testing"
|
||||
capabilities:
|
||||
- read
|
||||
|
||||
auth:
|
||||
ldap:
|
||||
- vault_admin
|
||||
oidc:
|
||||
- akP-vault-admin
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
rules:
|
||||
- path: "identity/oidc/token/terraform-artifactapi"
|
||||
capabilities:
|
||||
- read
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- terraform_artifactapi
|
||||
k8s/au/syd1:
|
||||
- woodpecker_terraform_artifactapi
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
rules:
|
||||
- path: "identity/oidc/token/terraform-enc"
|
||||
capabilities:
|
||||
- read
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- terraform_enc
|
||||
k8s/au/syd1:
|
||||
- woodpecker_terraform_enc
|
||||
@@ -1,12 +0,0 @@
|
||||
# Allow access to agent-observability Kubernetes credentials
|
||||
---
|
||||
rules:
|
||||
- path: "kubernetes/au/syd1/creds/agent-observability"
|
||||
capabilities:
|
||||
- update
|
||||
|
||||
auth:
|
||||
ldap:
|
||||
- kubernetes_au_syd1_cluster_operator
|
||||
approle:
|
||||
- agents
|
||||
@@ -1,15 +0,0 @@
|
||||
# Allow the Terraform Authentik runner to read the OAuth2/OIDC client secret that
|
||||
# backs an authentik provider, in whichever namespace the target service lives
|
||||
# (the module's data.vault_kv_secret_v2). The literal + is a Vault single-segment
|
||||
# wildcard: one oauth-credentials secret per onboarded namespace.
|
||||
---
|
||||
rules:
|
||||
- path: "kv/data/kubernetes/namespace/+/default/oauth-credentials"
|
||||
capabilities:
|
||||
- read
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- terraform_authentik
|
||||
k8s/au/syd1:
|
||||
- woodpecker_terraform_authentik
|
||||
@@ -1,14 +0,0 @@
|
||||
# Allow the Terraform Rancher runner to read the OAuth2/OIDC client secret backing
|
||||
# the Rancher keycloakoidc AuthConfig (the same secret Authentik sets on the
|
||||
# provider).
|
||||
---
|
||||
rules:
|
||||
- path: "kv/data/kubernetes/namespace/cattle-system/default/oauth-credentials"
|
||||
capabilities:
|
||||
- read
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- terraform_rancher
|
||||
k8s/au/syd1:
|
||||
- woodpecker_terraform_rancher
|
||||
@@ -1,12 +0,0 @@
|
||||
# Allow reading the IUCLabs GitHub App credentials
|
||||
---
|
||||
rules:
|
||||
- path: "kv/data/service/github/iuclabs/terraform"
|
||||
capabilities:
|
||||
- read
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- terraform_git
|
||||
k8s/au/syd1:
|
||||
- woodpecker_terraform_git
|
||||
@@ -1,11 +1,15 @@
|
||||
# Allow the Terraform Authentik runner to read its own Authentik API token
|
||||
# (provider auth). The OAuth2/OIDC client secrets backing authentik providers are
|
||||
# granted per secret under policies/kv/kubernetes/namespace/.
|
||||
# Allow the Terraform Authentik runner to read:
|
||||
# - its own Authentik API token (provider auth), and
|
||||
# - OAuth2/OIDC client secrets that back authentik providers (per target
|
||||
# service's kv namespace path, via the module's data.vault_kv_secret_v2).
|
||||
---
|
||||
rules:
|
||||
- path: "kv/data/service/terraform/authentik"
|
||||
capabilities:
|
||||
- read
|
||||
- path: "kv/data/kubernetes/namespace/+/default/oauth-credentials"
|
||||
capabilities:
|
||||
- read
|
||||
|
||||
auth:
|
||||
approle:
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
# Allow the Terraform BinaryLane runner to read its BinaryLane API token
|
||||
# (provider auth).
|
||||
---
|
||||
rules:
|
||||
- path: "kv/data/service/terraform/binarylane"
|
||||
capabilities:
|
||||
- read
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- terraform_binarylane
|
||||
k8s/au/syd1:
|
||||
- woodpecker_terraform_binarylane
|
||||
@@ -1,16 +0,0 @@
|
||||
# Lets the agents AppRole seed and rotate the BinaryLane API token (no delete).
|
||||
---
|
||||
rules:
|
||||
- path: "kv/data/service/terraform/binarylane"
|
||||
capabilities:
|
||||
- create
|
||||
- read
|
||||
- update
|
||||
- path: "kv/metadata/service/terraform/binarylane"
|
||||
capabilities:
|
||||
- read
|
||||
- list
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- agents
|
||||
@@ -0,0 +1,18 @@
|
||||
# Allow the terraform-infra runner to read the NetBox + KeaAPI tokens
|
||||
# (netbox_token / kea_token fields) used by the netbox and kea providers.
|
||||
---
|
||||
rules:
|
||||
- path: "kv/data/service/terraform/infra"
|
||||
capabilities:
|
||||
- read
|
||||
# vault_kv_secret_v2 (terraform-infra providers.tf data source) reads the kv-v2
|
||||
# metadata path on every plan/apply; a 403 here fails the plan.
|
||||
- path: "kv/metadata/service/terraform/infra"
|
||||
capabilities:
|
||||
- read
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- terraform_infra
|
||||
k8s/au/syd1:
|
||||
- woodpecker_terraform_infra
|
||||
@@ -1,11 +1,15 @@
|
||||
# Allow the Terraform Rancher runner to read its own Rancher admin API token
|
||||
# (rancher2 provider auth). The OAuth2/OIDC client secret backing the Rancher
|
||||
# keycloakoidc AuthConfig is granted under policies/kv/kubernetes/namespace/.
|
||||
# Allow the Terraform Rancher runner to read:
|
||||
# - its own Rancher admin API token (rancher2 provider auth), and
|
||||
# - the OAuth2/OIDC client secret backing the Rancher keycloakoidc AuthConfig
|
||||
# (same secret Authentik sets on the provider).
|
||||
---
|
||||
rules:
|
||||
- path: "kv/data/service/terraform/rancher"
|
||||
capabilities:
|
||||
- read
|
||||
- path: "kv/data/kubernetes/namespace/cattle-system/default/oauth-credentials"
|
||||
capabilities:
|
||||
- read
|
||||
|
||||
auth:
|
||||
approle:
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
# Lets the agents AppRole read the BinaryLane gateway WireGuard keypairs so they
|
||||
# can be eyaml-encrypted into puppet hiera. Read-only; terraform-binarylane owns writes.
|
||||
---
|
||||
rules:
|
||||
- path: "kv/data/service/wireguard/binarylane/*"
|
||||
capabilities:
|
||||
- read
|
||||
- path: "kv/metadata/service/wireguard/binarylane/*"
|
||||
capabilities:
|
||||
- read
|
||||
- list
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- agents
|
||||
@@ -1,23 +0,0 @@
|
||||
# Let the terraform-binarylane runner store the WireGuard keypairs it generates
|
||||
# for the BinaryLane gateways. delete lets `terraform destroy` and keypair
|
||||
# replacement remove a secret and its metadata; vault_kv_secret_v2 reads
|
||||
# metadata on every plan/apply.
|
||||
---
|
||||
rules:
|
||||
- path: "kv/data/service/wireguard/binarylane/*"
|
||||
capabilities:
|
||||
- create
|
||||
- read
|
||||
- update
|
||||
- delete
|
||||
- path: "kv/metadata/service/wireguard/binarylane/*"
|
||||
capabilities:
|
||||
- read
|
||||
- list
|
||||
- delete
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- terraform_binarylane
|
||||
k8s/au/syd1:
|
||||
- woodpecker_terraform_binarylane
|
||||
@@ -1,10 +0,0 @@
|
||||
# Lets rspamd mint a Haiku-scoped LiteLLM virtual key for its GPT/LLM module.
|
||||
---
|
||||
rules:
|
||||
- path: "litellm/creds/rspamd"
|
||||
capabilities:
|
||||
- read
|
||||
|
||||
auth:
|
||||
k8s/au/syd1:
|
||||
- rspamd
|
||||
@@ -0,0 +1,21 @@
|
||||
# Allow the terraform-infra runner to mint an ephemeral NetBox token from the
|
||||
# terraform-infra role (netbox/creds/terraform-infra), replacing the static
|
||||
# netbox_token it used to read from kv/service/terraform/*. The e-breuninger
|
||||
# netbox provider authenticates with the minted token; the lease revokes it when
|
||||
# the run ends.
|
||||
#
|
||||
# Bound to both the terraform-infra AppRole and its Woodpecker k8s auth role,
|
||||
# mirroring the terraform-ipam pattern. Both principals are created by the
|
||||
# terraform-infra Vault onboarding (separate from this netbox change); until
|
||||
# that onboarding lands this policy exists but attaches to nothing.
|
||||
---
|
||||
rules:
|
||||
- path: "netbox/creds/terraform-infra"
|
||||
capabilities:
|
||||
- read
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- terraform_infra
|
||||
k8s/au/syd1:
|
||||
- woodpecker_terraform_infra
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user