Add Terraform Stage 1

This commit is contained in:
Tobias Manske 2023-09-14 07:09:02 +02:00
parent af3e66f901
commit f0bae5e031
Signed by: tobias
GPG Key ID: 9164B527694A0709
23 changed files with 597 additions and 0 deletions

View File

@ -15,6 +15,9 @@ environment:
ANSIBLE_FORCE_COLOR: true ANSIBLE_FORCE_COLOR: true
ANSIBLE_HOME: /drone/src/.ansible ANSIBLE_HOME: /drone/src/.ansible
ANSIBLE_VAULT_PASSWORD_FILE: "/drone/src/vault_pass" ANSIBLE_VAULT_PASSWORD_FILE: "/drone/src/vault_pass"
SUMMON_PROVIDER: /drone/src/summon-wrapper
PASSAGE_DIR: /drone/src/.passage/store
PASSAGE_IDENTITIES_FILE: /drone/src/ssh_key
node: node:
ansible: "true" ansible: "true"
@ -28,11 +31,13 @@ steps:
from_secret: vault_pass from_secret: vault_pass
SSH_KEY: SSH_KEY:
from_secret: ssh_key from_secret: ssh_key
GIT_SSH_COMMAND: ssh -i /drone/src/ssh_key -o StrictHostKeyChecking=no
commands: commands:
- echo $${VAULT_PASS} > /drone/src/vault_pass - echo $${VAULT_PASS} > /drone/src/vault_pass
- echo $${SSH_KEY} | base64 -d > /drone/src/ssh_key - echo $${SSH_KEY} | base64 -d > /drone/src/ssh_key
- chmod 600 /drone/src/ssh_key - chmod 600 /drone/src/ssh_key
- chmod 600 /drone/src/vault_pass - chmod 600 /drone/src/vault_pass
- git clone ssh://git@git.tobiasmanske.de:7779/tobias/infrastructure-vault.git $${PASSAGE_DIR}
- name: Prepare Runner - name: Prepare Runner
image: registry.tobiasmanske.de/ansible-runner:latest image: registry.tobiasmanske.de/ansible-runner:latest
pull: always pull: always
@ -41,6 +46,13 @@ steps:
- mkdir $ANSIBLE_HOME - mkdir $ANSIBLE_HOME
- ansible-galaxy install -r requirements.yaml - ansible-galaxy install -r requirements.yaml
- ansible-playbook --private-key ../ssh_key --inventory=inventory.yaml runner-pre.yaml - ansible-playbook --private-key ../ssh_key --inventory=inventory.yaml runner-pre.yaml
- name: Run Terraform
image: registry.tobiasmanske.de/terraform-runner:latest
pull: always
commands:
- cd tf-stage-1
- summon terraform init
- summon terraform plan # do not run right now. this is still in debug :christ:
- name: Run Ansible - name: Run Ansible
image: registry.tobiasmanske.de/ansible-runner:latest image: registry.tobiasmanske.de/ansible-runner:latest
pull: always pull: always

3
summon-wrapper Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
passage show $(echo "${@}"|tr : \ )

View File

@ -0,0 +1,44 @@
module "dns-tobiasmanske-de" {
source = "./modules/dns"
account_id = var.cloudflare_account_id
zone = "tobiasmanske.de"
records = [
{ type = "A", name = "web", value = "185.216.177.198" },
{ type = "AAAA", name = "web", value = "2a03:4000:4f:9f2::1" },
{ type = "CNAME", name = "@", value = "web.tobiasmanske.de" },
{ type = "CNAME", name = "s3", value = "web.tobiasmanske.de" },
{ type = "CNAME", name = "minio", value = "web.tobiasmanske.de" },
{ type = "CNAME", name = "doc", value = "web.tobiasmanske.de" },
{ type = "CNAME", name = "drone", value = "web.tobiasmanske.de" },
{ type = "CNAME", name = "git", value = "web.tobiasmanske.de" },
{ type = "CNAME", name = "meet", value = "web.tobiasmanske.de" },
{ type = "CNAME", name = "oauth", value = "web.tobiasmanske.de" },
{ type = "CNAME", name = "registry", value = "web.tobiasmanske.de" },
{ type = "CNAME", name = "registry-auth", value = "web.tobiasmanske.de" },
{ type = "CNAME", name = "traefik-fa", value = "web.tobiasmanske.de" },
{ type = "CNAME", name = "repo", value = "web.tobiasmanske.de" },
{ type = "CNAME", name = "rss", value = "web.tobiasmanske.de" },
{ type = "CNAME", name = "search", value = "web.tobiasmanske.de" },
{ type = "CNAME", name = "test", value = "web.tobiasmanske.de" },
{ type = "CNAME", name = "calendar", value = "web.tobiasmanske.de" },
{ type = "CNAME", name = "www", value = "web.tobiasmanske.de" },
{ type = "CNAME", name = "grafana", value = "web.tobiasmanske.de" },
{ type = "CNAME", name = "prometheus", value = "web.tobiasmanske.de" },
{ type = "CNAME", name = "status", value = "mon1.hel1.chaoswg.org" },
{ type = "CNAME", name = "auth", value = "infra.unruhig.eu" },
{ type = "TXT", name = "@", value = "google-site-verification=I7WrzPjqHIL6EATWd8UWfvx6ScDzqjA3DGZi-J-F1e0" },
# Mail settings
{ type = "A", name = "mail", value = "202.61.232.207" },
{ type = "MX", name = "@", value = "mxe8cf.netcup.net", priority = 50 },
{ type = "MX", name = "@", value = "mail.tobiasmanske.de", priority = 10 },
{ type = "CNAME", name = "autoconfig", value = "autoconfig.netcup.net" },
{ type = "CNAME", name = "key1._domainkey", value = "key1._domainkey.webhosting.systems" },
{ type = "CNAME", name = "key2._domainkey", value = "key2._domainkey.webhosting.systems" },
{ type = "TXT", name = "@", value = "v=spf1 mx include:_spf.webhosting.systems -all" },
{ type = "TXT", name = "_dmarc", value = "v=DMARC1; p=quarantine;pct=100;rua=mailto:postmaster+dmarc@tobiasmanske.de;" },
]
}

View File

@ -0,0 +1,12 @@
module "dns-unruhig-eu" {
source = "./modules/dns"
account_id = var.cloudflare_account_id
zone = "unruhig.eu"
records = [
{ type = "A", name = "infra", value = "37.221.198.143" },
{ type = "AAAA", name = "infra", value = "2a03:4000:9:176::1" },
]
}

26
tf-stage-1/main.tf Normal file
View File

@ -0,0 +1,26 @@
terraform {
backend "s3" {
bucket = "infra"
key = "terraform/keycloak.tfstate"
endpoint = "https://s3.tobiasmanske.de"
force_path_style = true
region = "us-east-1"
skip_credentials_validation = true
skip_region_validation = true
skip_metadata_api_check = true
}
required_providers {
keycloak = {
source = "mrparkers/keycloak"
version = "~> 4.3.0"
}
cloudflare = {
source = "cloudflare/cloudflare"
version = "~> 4.0"
}
}
}
data "keycloak_realm" "realm" {
realm = var.realm
}

View File

@ -0,0 +1,14 @@
resource "cloudflare_zone" "zone" {
account_id = var.account_id
zone = var.zone
}
resource "cloudflare_record" "records" {
zone_id = cloudflare_zone.zone.id
for_each = { for record in var.records : uuidv5("dns", "${record.type}/${record.name}/${record.value}") => record } # Hackery.
name = each.value.name
value = each.value.value
type = each.value.type
ttl = 1
priority = each.value.type == "MX" ? each.value.priority : null
}

View File

@ -0,0 +1,9 @@
terraform {
required_providers {
cloudflare = {
source = "cloudflare/cloudflare"
version = "~> 4.0"
}
}
}

View File

@ -0,0 +1,10 @@
variable "account_id" {
type = string
sensitive = true
}
variable "zone" {
type = string
}
variable "records" {
type = set(object({ type = string, name = string, value = string, priority = optional(number) }))
}

View File

@ -0,0 +1,74 @@
data "keycloak_realm" "realm" {
realm = var.realm
}
resource "keycloak_openid_client" "client" {
realm_id = data.keycloak_realm.realm.id
client_id = var.client_id
client_secret = var.client_secret
name = var.client_name
description = var.description
enabled = var.enabled
access_type = var.access_type
client_authenticator_type = var.client_authenticator_type
root_url = var.root_url
base_url = var.base_url
admin_url = var.admin_url
backchannel_logout_url = var.backchannel_logout_url
valid_redirect_uris = var.valid_redirect_uris
web_origins = var.web_origins
login_theme = var.login_theme
standard_flow_enabled = true
implicit_flow_enabled = false
direct_access_grants_enabled = true
service_accounts_enabled = false
frontchannel_logout_enabled = false
}
resource "keycloak_role" "restricted-access" {
realm_id = data.keycloak_realm.realm.id
client_id = keycloak_openid_client.client.id
name = "restricted-access"
description = "Restricts access to the client"
}
resource "keycloak_role" "admin-role" {
realm_id = data.keycloak_realm.realm.id
client_id = keycloak_openid_client.client.id
name = "${var.admin_role_name != null ? "${var.admin_role_name}" : "${var.client_name}-admin"}"
description = "Client Admin permissions"
}
resource "keycloak_group" "access_group" {
realm_id = data.keycloak_realm.realm.id
name = var.client_name
}
resource "keycloak_group" "admin_group" {
realm_id = data.keycloak_realm.realm.id
parent_id = keycloak_group.access_group.id
name = "${var.client_name}-admin"
}
resource "keycloak_group_roles" "access_group_roles" {
realm_id = data.keycloak_realm.realm.id
group_id = keycloak_group.access_group.id
role_ids = [
keycloak_role.restricted-access.id
]
}
resource "keycloak_group_roles" "admin_group_roles" {
realm_id = data.keycloak_realm.realm.id
group_id = keycloak_group.admin_group.id
role_ids = [
keycloak_role.admin-role.id
]
}

View File

@ -0,0 +1,8 @@
terraform {
required_providers {
keycloak = {
source = "mrparkers/keycloak"
version = "~> 4.3.0"
}
}
}

View File

@ -0,0 +1,12 @@
output "client" {
value = keycloak_openid_client.client
}
output "admin_group" {
value = keycloak_group.admin_group
}
output "access_group" {
value = keycloak_group.access_group
}
output "realm" {
value = data.keycloak_realm.realm
}

View File

@ -0,0 +1,75 @@
variable "client_id" {
type = string
}
variable "client_name" {
type = string
}
variable "admin_role_name" {
type = string
default = null
}
variable "client_secret" {
type = string
default = null
sensitive = true
}
variable "description" {
type = string
}
variable "access_type" {
type = string
default = "CONFIDENTIAL"
}
variable "realm" {
type = string
}
variable "client_authenticator_type" {
type = string
default = "client-secret"
}
variable "root_url" { # requires web_origins, admin_url and valid_redirect_uris to be set...
type = string
default = null
}
variable "admin_url" {
type = string
default = null
}
variable "base_url" {
type = string
default = null
}
variable "web_origins" {
type = list(string)
default = null
}
variable "backchannel_logout_url" {
type = string
default = null
description = "The URL to which a backchannel logout request will be sent from the Keycloak server."
}
variable "valid_redirect_uris" {
type = list(string)
}
variable "enabled" {
type = bool
default = true
}
# Default settings for all clients:
variable "login_theme" {
type = string
default = "keywind"
}

10
tf-stage-1/providers.tf Normal file
View File

@ -0,0 +1,10 @@
provider "keycloak" {
client_id = "terraform"
client_secret = var.keycloak_client_secret
url = "https://auth.tobiasmanske.de"
realm = "tobiasmanske.de"
}
provider "cloudflare" {
api_token = var.cloudflare_api_token
}

9
tf-stage-1/secrets.yml Normal file
View File

@ -0,0 +1,9 @@
---
TF_VAR_grafana_secret: !var keycloak/grafana/secret
TF_VAR_hedgedoc_secret: !var keycloak/hedgedoc/secret
TF_VAR_miniflux_secret: !var keycloak/miniflux/secret
TF_VAR_keycloak_client_secret: !var keycloak/terraform/secret
TF_VAR_cloudflare_api_token: !var extern/cloudflare/api_token
TF_VAR_cloudflare_account_id: !var extern/cloudflare/account_id
AWS_ACCESS_KEY_ID: !var terraform/state/s3_access_key
AWS_SECRET_ACCESS_KEY: !var terraform/state/s3_secret_key

View File

@ -0,0 +1,37 @@
module "giteaclient" {
source = "./modules/kc-client"
realm = var.realm
client_id = "gitea"
client_name = "Gitea"
description = "git.tobiasmanske.de"
root_url = "https://git.tobiasmanske.de"
admin_url = "https://git.tobiasmanske.de"
base_url = ""
valid_redirect_uris = ["https://git.tobiasmanske.de/user/oauth2/Keycloak/callback"]
web_origins = ["https://git.tobiasmanske.de"]
}
resource "keycloak_openid_user_property_protocol_mapper" "gitea-username-mapper" {
realm_id = module.giteaclient.realm.id
client_id = module.giteaclient.client.id
name = "username"
user_property = "username"
claim_name = "preferred_username"
add_to_userinfo = true
add_to_access_token = true
add_to_id_token = false
}
resource "keycloak_openid_user_client_role_protocol_mapper" "gitea-role-mapper" {
realm_id = module.giteaclient.realm.id
client_id = module.giteaclient.client.id
# client_id_for_role_mappings = module.giteaclient.client.id
multivalued = true
name = "user-client-role-mapper"
claim_name = "roles"
add_to_userinfo = true
add_to_access_token = true
add_to_id_token = false
}

View File

@ -0,0 +1,8 @@
module "kc-client" {
source = "./modules/kc-client"
client_id = "debug"
client_name = "Debug client"
description = "A client for debugging purposes"
realm = var.realm
valid_redirect_uris = ["http://localhost:8080/*"]
}

View File

@ -0,0 +1,70 @@
module "grafanaclient" {
source = "./modules/kc-client"
realm = var.realm
client_id = "grafana"
client_name = "Grafana"
client_secret = var.grafana_secret
description = "https://grafana.tobiasmanske.de"
admin_role_name = "serveradmin"
root_url = "https://grafana.tobiasmanske.de"
admin_url = "https://grafana.tobiasmanske.de"
base_url = "https://grafana.tobiasmanske.de"
valid_redirect_uris = ["https://grafana.tobiasmanske.de/*"]
web_origins = ["https://grafana.tobiasmanske.de"]
}
resource "keycloak_openid_group_membership_protocol_mapper" "grafana-membership-mapper" {
realm_id = module.grafanaclient.realm.id
client_id = module.grafanaclient.client.id
name = "Group Mapper"
claim_name = "groups"
full_path = false
add_to_userinfo = true
add_to_access_token = false
add_to_id_token = true
}
resource "keycloak_openid_user_property_protocol_mapper" "grafana-username-mapper" {
realm_id = module.grafanaclient.realm.id
client_id = module.grafanaclient.client.id
name = "username"
user_property = "username"
claim_name = "preferred_username"
add_to_userinfo = true
add_to_access_token = true
add_to_id_token = false
}
resource "keycloak_openid_user_client_role_protocol_mapper" "grafana-role-mapper" {
realm_id = module.grafanaclient.realm.id
client_id = module.grafanaclient.client.id
multivalued = true
name = "user-client-role-mapper"
claim_name = "resource_access.$${client_id}.roles"
add_to_userinfo = true
add_to_access_token = true
add_to_id_token = false
}
resource "keycloak_role" "grafana-admin" {
realm_id = module.grafanaclient.realm.id
client_id = module.grafanaclient.client.id
name = "admin"
description = "Admin"
}
resource "keycloak_role" "grafana-editor" {
realm_id = module.grafanaclient.realm.id
client_id = module.grafanaclient.client.id
name = "editor"
description = "Editor"
}
resource "keycloak_role" "grafana-viewer" {
realm_id = module.grafanaclient.realm.id
client_id = module.grafanaclient.client.id
name = "viewer"
description = "Viewer"
}

View File

@ -0,0 +1,51 @@
module "hedgedocclient" {
source = "./modules/kc-client"
realm = var.realm
client_id = "hedgedoc"
client_name = "hedgedoc"
client_secret = var.hedgedoc_secret
description = "doc.tobiasmanske.de"
root_url = "https://doc.tobiasmanske.de"
admin_url = ""
base_url = ""
valid_redirect_uris = ["https://doc.tobiasmanske.de/*"]
web_origins = ["https://doc.tobiasmanske.de"]
}
resource "keycloak_openid_user_session_note_protocol_mapper" "hedgedoc-id-mapper" {
realm_id = module.hedgedocclient.realm.id
client_id = module.hedgedocclient.client.id
name = "id"
claim_name = "clientId"
claim_value_type = "String"
session_note = "clientId"
add_to_access_token = true
add_to_id_token = true
}
resource "keycloak_openid_user_session_note_protocol_mapper" "hedgedoc-host-mapper" {
realm_id = module.hedgedocclient.realm.id
client_id = module.hedgedocclient.client.id
name = "host"
claim_name = "clientHost"
claim_value_type = "String"
session_note = "clientHost"
add_to_access_token = true
add_to_id_token = true
}
resource "keycloak_openid_user_session_note_protocol_mapper" "hedgedoc-ip-mapper" {
realm_id = module.hedgedocclient.realm.id
client_id = module.hedgedocclient.client.id
name = "ip"
claim_name = "clientAddress"
claim_value_type = "String"
session_note = "clientAddress"
add_to_access_token = true
add_to_id_token = true
}

View File

@ -0,0 +1,51 @@
module "minifluxclient" {
source = "./modules/kc-client"
realm = var.realm
client_id = "miniflux"
client_name = "Miniflux"
client_secret = var.miniflux_secret
description = "rss.tobiasmanske.de"
root_url = "https://rss.tobiasmanske.de"
admin_url = ""
base_url = ""
valid_redirect_uris = ["/oauth2/oidc/callback"]
web_origins = []
}
resource "keycloak_openid_user_session_note_protocol_mapper" "miniflux-id-mapper" {
realm_id = module.minifluxclient.realm.id
client_id = module.minifluxclient.client.id
name = "id"
claim_name = "clientId"
claim_value_type = "String"
session_note = "clientId"
add_to_access_token = true
add_to_id_token = true
}
resource "keycloak_openid_user_session_note_protocol_mapper" "miniflux-host-mapper" {
realm_id = module.minifluxclient.realm.id
client_id = module.minifluxclient.client.id
name = "host"
claim_name = "clientHost"
claim_value_type = "String"
session_note = "clientHost"
add_to_access_token = true
add_to_id_token = true
}
resource "keycloak_openid_user_session_note_protocol_mapper" "miniflux-ip-mapper" {
realm_id = module.minifluxclient.realm.id
client_id = module.minifluxclient.client.id
name = "ip"
claim_name = "clientAddress"
claim_value_type = "String"
session_note = "clientAddress"
add_to_access_token = true
add_to_id_token = true
}

View File

@ -0,0 +1 @@
realm = "tobiasmanske.de"

View File

@ -0,0 +1,13 @@
data "keycloak_user" "ialistannen" {
realm_id = data.keycloak_realm.realm.id
username = "ialistannen"
}
resource "keycloak_user_groups" "ialistannen_groups" {
realm_id = data.keycloak_realm.realm.id
user_id = data.keycloak_user.ialistannen.id
exhaustive = false
group_ids = [
module.hedgedocclient.access_group.id,
]
}

View File

@ -0,0 +1,18 @@
data "keycloak_user" "rad4day" {
realm_id = data.keycloak_realm.realm.id
username = "rad4day"
}
resource "keycloak_user_groups" "rad4day_groups" {
realm_id = data.keycloak_realm.realm.id
user_id = data.keycloak_user.rad4day.id
exhaustive = false
group_ids = [
module.giteaclient.access_group.id,
module.grafanaclient.access_group.id,
module.giteaclient.admin_group.id,
module.grafanaclient.admin_group.id,
module.hedgedocclient.access_group.id,
module.minifluxclient.access_group.id,
]
}

30
tf-stage-1/variables.tf Normal file
View File

@ -0,0 +1,30 @@
variable "realm" {
type = string
}
variable "grafana_secret" {
type = string
sensitive = true
}
variable "hedgedoc_secret" {
type = string
sensitive = true
}
variable "miniflux_secret" {
type = string
sensitive = true
}
variable "cloudflare_api_token" {
type = string
sensitive = true
}
variable "cloudflare_account_id" {
type = string
sensitive = true
}
variable "keycloak_client_secret" {
type = string
sensitive = true
}