diff --git a/ansible/arcodange/factory/inventory/group_vars/hard_disk/gitea.yml b/ansible/arcodange/factory/inventory/group_vars/hard_disk/gitea.yml
index c0da59f..5a6fb75 100644
--- a/ansible/arcodange/factory/inventory/group_vars/hard_disk/gitea.yml
+++ b/ansible/arcodange/factory/inventory/group_vars/hard_disk/gitea.yml
@@ -21,7 +21,7 @@ gitea:
external: true
services:
gitea:
- image: gitea/gitea:1.22.1
+ image: gitea/gitea:1.22.2
container_name: gitea
restart: always
environment:
@@ -41,6 +41,7 @@ gitea:
GITEA__mailer__SMTP_PORT: 465
GITEA__mailer__PASSWD: '{{ gitea_vault.GITEA__mailer__PASSWD }}'
GITEA__server__SSH_PORT: 2222
+ GITEA__server__SSH_DOMAIN: "{{ lookup('dig', groups.gitea[0]) }}"
GITEA__server__SSH_LISTEN_PORT: 22
networks:
- gitea
diff --git a/ansible/arcodange/factory/playbooks/03_cicd.yml b/ansible/arcodange/factory/playbooks/03_cicd.yml
index 28ce3e7..f35817f 100644
--- a/ansible/arcodange/factory/playbooks/03_cicd.yml
+++ b/ansible/arcodange/factory/playbooks/03_cicd.yml
@@ -37,7 +37,7 @@
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
extra_hosts:
- gitea.arcodange.duckdns.org: '{{ lookup("dig", groups.gitea[0]) }}'
+ gitea.arcodange.duckdns.org: '{{ lookup("dig", "gitea.arcodange.duckdns.org") }}'
configs:
- config.yaml
configs:
diff --git a/ansible/arcodange/factory/playbooks/setup/gitea.yml b/ansible/arcodange/factory/playbooks/setup/gitea.yml
index 240ad4c..a6f0dbc 100644
--- a/ansible/arcodange/factory/playbooks/setup/gitea.yml
+++ b/ansible/arcodange/factory/playbooks/setup/gitea.yml
@@ -192,4 +192,10 @@
body_format: json
body:
image: "{{ gitea_org_avatar_img['content'] }}"
- status_code: 204
\ No newline at end of file
+ status_code: 204
+
+ post_tasks:
+ - include_role:
+ name: arcodange.factory.gitea_token
+ vars:
+ gitea_token_delete: true
\ No newline at end of file
diff --git a/ansible/arcodange/factory/playbooks/tools/hashicorp_vault.yml b/ansible/arcodange/factory/playbooks/tools/hashicorp_vault.yml
index 94733ba..67777cf 100644
--- a/ansible/arcodange/factory/playbooks/tools/hashicorp_vault.yml
+++ b/ansible/arcodange/factory/playbooks/tools/hashicorp_vault.yml
@@ -8,9 +8,25 @@
- name: gitea_admin_password
prompt: Enter gitea admin password
unsafe: true # password can contain uncommon chars such as '{'
+
+ roles:
+ - arcodange.factory.gitea_token
tasks:
- name: Setup Hashicorp Vault
include_role:
- name: hashicorp_vault
\ No newline at end of file
+ name: hashicorp_vault
+
+ - name: share VAULT CA
+ block:
+
+ - name: read traefik CA
+ include_role:
+ name: arcodange.factory.traefik_certs
+
+ post_tasks:
+ - include_role:
+ name: arcodange.factory.gitea_token
+ vars:
+ gitea_token_delete: true
\ No newline at end of file
diff --git a/ansible/arcodange/factory/playbooks/tools/roles/hashicorp_vault/files/hashicorp_vault.tf b/ansible/arcodange/factory/playbooks/tools/roles/hashicorp_vault/files/hashicorp_vault.tf
index c1f1ee4..0e9c287 100644
--- a/ansible/arcodange/factory/playbooks/tools/roles/hashicorp_vault/files/hashicorp_vault.tf
+++ b/ansible/arcodange/factory/playbooks/tools/roles/hashicorp_vault/files/hashicorp_vault.tf
@@ -25,6 +25,10 @@ variable "gitea_app" {
})
}
+# kubectl -n kube-system exec $(kubectl -n kube-system get pod -l app.kubernetes.io/name=traefik -o jsonpath="{.items[0]['.metadata.name']}") -- cat /data/acme.json | jq '(.letsencrypt.Certificates | map(select(.domain.main=="arcodange.duckdns.org")))[0]' | jq '.certificate' -r | base64 -d | openssl x509
+variable "ca_pem" {
+ type = string
+}
terraform {
required_providers {
vault = {
@@ -45,9 +49,11 @@ resource "vault_jwt_auth_backend" "gitea" {
path = "gitea"
type = "oidc"
oidc_discovery_url = var.gitea_app.url
+ oidc_discovery_ca_pem = var.ca_pem
oidc_client_id = var.gitea_app.id
oidc_client_secret = var.gitea_app.secret
bound_issuer = var.gitea_app.url
+
tune {
allowed_response_headers = []
audit_non_hmac_request_keys = []
@@ -65,13 +71,50 @@ resource "vault_jwt_auth_backend_role" "gitea" {
role_name = "default"
token_policies = ["default"]
+
user_claim = "email"
role_type = "oidc"
allowed_redirect_uris = [
+ "http://localhost:8250/oidc/callback", # for command line login
"${var.vault_address}/ui/vault/auth/gitea/oidc/callback",
+ "https://webapp.arcodange.duckdns.org/oauth-callback",
]
}
+resource "vault_jwt_auth_backend" "gitea_jwt" {
+ description = var.gitea_app.description
+ default_role = "gitea_cicd"
+ path = "gitea_jwt"
+ type = "jwt"
+ oidc_discovery_url = var.gitea_app.url
+ oidc_discovery_ca_pem = var.ca_pem
+ bound_issuer = var.gitea_app.url
+
+ tune {
+ allowed_response_headers = []
+ audit_non_hmac_request_keys = []
+ audit_non_hmac_response_keys = []
+ default_lease_ttl = "15m"
+ listing_visibility = "hidden"
+ max_lease_ttl = "15m"
+ passthrough_request_headers = []
+ token_type = "default-batch"
+ }
+}
+
+resource "vault_jwt_auth_backend_role" "gitea_jwt_cicd" {
+ backend = vault_jwt_auth_backend.gitea_jwt.path
+ role_name = "gitea_cicd"
+ token_policies = ["default"]
+
+ bound_audiences = [
+ var.gitea_app.id,
+ ]
+
+ user_claim = "email"
+ role_type = "jwt"
+}
+
data "vault_policy_document" "admin" {
rule {
path = "*"
@@ -87,8 +130,29 @@ resource "vault_identity_entity" "admin" {
name = var.admin_email
policies = [vault_policy.admin.name]
}
-resource "vault_identity_entity_alias" "admin" {
+resource "vault_identity_entity_alias" "admin_gitea" {
name = var.admin_email
mount_accessor = vault_jwt_auth_backend.gitea.accessor
canonical_id = vault_identity_entity.admin.id
+}
+resource "vault_identity_entity_alias" "admin_gitea_jwt" {
+ name = var.admin_email
+ mount_accessor = vault_jwt_auth_backend.gitea_jwt.accessor
+ canonical_id = vault_identity_entity.admin.id
+}
+
+resource "vault_mount" "kvv1" {
+ path = "kvv1"
+ type = "kv"
+ options = { version = "1" }
+ description = "KV Version 1 secret engine mount"
+}
+
+resource "vault_kv_secret" "google_credential" {
+ path = "${vault_mount.kvv1.path}/google/credentials"
+ data_json = jsonencode(
+ {
+ credentials = file("~/.config/gcloud/application_default_credentials.json")
+ }
+ )
}
\ No newline at end of file
diff --git a/ansible/arcodange/factory/playbooks/tools/roles/hashicorp_vault/files/playwright_setupGiteaApp.js b/ansible/arcodange/factory/playbooks/tools/roles/hashicorp_vault/files/playwright_setupGiteaApp.js
index 60dfe68..d6ec3e1 100644
--- a/ansible/arcodange/factory/playbooks/tools/roles/hashicorp_vault/files/playwright_setupGiteaApp.js
+++ b/ansible/arcodange/factory/playbooks/tools/roles/hashicorp_vault/files/playwright_setupGiteaApp.js
@@ -72,7 +72,9 @@ async function setupApp() {
console.warn('app not found');
await applicationsPanel.locator('input[name="application_name"]').fill(appName);
await applicationsPanel.locator('textarea[name="redirect_uris"]').fill([
- `${vaultAddress}/ui/vault/auth/gitea/oidc/callback`
+ 'http://localhost:8250/oidc/callback', // for command line login
+ `${vaultAddress}/ui/vault/auth/gitea/oidc/callback`,
+ 'https://webapp.arcodange.duckdns.org/oauth-callback',
].join('\n'));
await applicationsPanel.locator('form[action="/admin/applications/oauth2"] > button').dblclick()
diff --git a/ansible/arcodange/factory/playbooks/tools/roles/hashicorp_vault/tasks/gitea_oidc_auth.yml b/ansible/arcodange/factory/playbooks/tools/roles/hashicorp_vault/tasks/gitea_oidc_auth.yml
index 0b0d4a3..31baad7 100644
--- a/ansible/arcodange/factory/playbooks/tools/roles/hashicorp_vault/tasks/gitea_oidc_auth.yml
+++ b/ansible/arcodange/factory/playbooks/tools/roles/hashicorp_vault/tasks/gitea_oidc_auth.yml
@@ -15,8 +15,11 @@
- include_role:
name: arcodange.factory.playwright
+- include_role:
+ name: arcodange.factory.traefik_certs
+
- set_fact:
- gite_app: '{{ playwright_job.stdout | from_json }}'
+ gitea_app: '{{ playwright_job.stdout | from_json }}'
volume_name: tofu-{{ ansible_date_time.iso8601.replace(':','-') }}
@@ -31,15 +34,35 @@
--entrypoint=''
ghcr.io/opentofu/opentofu:latest
{{ command }}
+ register: last_tofu_command
loop:
- tofu init -no-color
- >-
tofu apply -auto-approve -no-color
- -var='gitea_app={{ gite_app | to_json }}'
+ -var='gitea_app={{ gitea_app | to_json }}'
-var='vault_address={{ vault_address }}'
-var='vault_token={{ vault_root_token }}'
+ -var='ca_pem={{ traefik_cert_pem }}'
loop_control:
loop_var: command
+ extended: true
+ label: tofu command n°{{ ansible_loop.index0 }}
always:
- - shell: docker volume rm {{ volume_name }}
\ No newline at end of file
+ - shell: docker volume rm {{ volume_name }}
+ - #when: last_tofu_command.stderr | length > 0
+ debug:
+ var: last_tofu_command
+ # msg: '{{ last_tofu_command.stderr }}'
+
+- include_role:
+ name: arcodange.factory.gitea_secret
+ vars:
+ gitea_secret_name: vault_oauth__sh_b64
+ gitea_secret_value: >-
+ {{ lookup('ansible.builtin.template', 'oidc_jwt_token.sh.j2', template_vars = {
+ 'GITEA_BASE_URL': 'https://gitea.arcodange.duckdns.org',
+ 'OIDC_CLIENT_ID': gitea_app.id,
+ 'OIDC_CLIENT_SECRET': gitea_app.secret,
+ }) | b64encode }}
+ gitea_owner_type: 'org' # value != 'user'
\ No newline at end of file
diff --git a/ansible/arcodange/factory/playbooks/tools/roles/hashicorp_vault/templates/oidc_jwt_token.sh.j2 b/ansible/arcodange/factory/playbooks/tools/roles/hashicorp_vault/templates/oidc_jwt_token.sh.j2
new file mode 100644
index 0000000..00ca013
--- /dev/null
+++ b/ansible/arcodange/factory/playbooks/tools/roles/hashicorp_vault/templates/oidc_jwt_token.sh.j2
@@ -0,0 +1,107 @@
+#!/bin/bash
+set -eu
+
+# Variables à ajuster selon ta configuration
+CLIENT_ID="{{ OIDC_CLIENT_ID }}"
+CLIENT_SECRET="{{ OIDC_CLIENT_SECRET }}"
+REDIRECT_URI="{{ OIDC_CLIENT_CALLBACK | default('https://webapp.arcodange.duckdns.org/oauth-callback') }}" # Redirige ici après l'authentification
+AUTH_URL="{{ GITEA_BASE_URL | default('https://gitea.arcodange.duckdns.org') }}/login/oauth/authorize"
+TOKEN_URL="{{ GITEA_BASE_URL | default('https://gitea.arcodange.duckdns.org') }}/login/oauth/access_token"
+ISSUER="https://gitea.arcodange.duckdns.org/"
+# SCOPE="openid email profile groups" # Scope que tu souhaites obtenir - profile groups
+SCOPE="email openid read:user" # Scope que tu souhaites obtenir - profile groups
+set +u
+STATE="$RANDOM-$RANDOM-$RANDOM-$RANDOM-$RANDOM-$RANDOM" # Anti-CSRF (valeur aléatoire)
+set -u
+CODE=""
+
+MAX_ATTEMPTS=100 # Nombre maximum de tentatives
+INTERVAL=5 # Intervalle en secondes entre chaque tentative
+
+# Fonction pour effectuer le polling
+poll_state() {
+ local attempt=1
+
+ while [ $attempt -le $MAX_ATTEMPTS ]; do
+ #echo "Tentative $attempt/$MAX_ATTEMPTS: Requête à l'endpoint /retrieve pour state=$STATE..."
+
+ # Effectuer la requête GET
+ RESPONSE=$(curl -s -w "%{http_code}" -o /tmp/response_body "https://webapp.arcodange.duckdns.org/retrieve?state=$STATE")
+ HTTP_CODE=$(tail -n1 <<< "$RESPONSE")
+
+ if [ "$HTTP_CODE" == "200" ]; then
+ CODE=$(cat /tmp/response_body)
+ echo ''
+ return 0
+ elif [ "$HTTP_CODE" == "404" ]; then
+ echo -n "."
+ else
+ echo "Erreur lors de la requête (HTTP $HTTP_CODE)."
+ return 1
+ fi
+
+ # Attendre avant de refaire une requête
+ sleep $INTERVAL
+ attempt=$((attempt + 1))
+ done
+
+ echo "Échec après $MAX_ATTEMPTS tentatives, code non trouvé."
+ return 1
+}
+
+# 1. Rediriger l'utilisateur vers l'URL d'authentification
+echo "Ouvrez le lien suivant dans votre navigateur pour vous authentifier dans Gitea:"
+echo "$AUTH_URL?client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI&response_type=code&scope=$(sed 's/ /%20/g' <<<$SCOPE)&state=$STATE"
+
+
+# Device Flow simulation (non implémenté par Gitea)
+
+if [ -n ${GITHUB_ACTOR:-} ]; then
+ >&2 echo "hello $GITHUB_ACTOR :)"
+ poll_state
+else
+ # 2. L'utilisateur doit coller le code ici
+ # stty -echo
+ printf "Collez le code d'autorisation (code+state) ici après authentification: "
+ read CODE_STATE
+ # stty echo
+ CODE=`awk '{print $2}' <<< $CODE_STATE`
+ p_STATE=`awk '{print $4}' <<< $CODE_STATE`
+
+ if [ $STATE != $p_STATE ]; then
+ >&2 echo 'mauvais csrf (state)'
+ exit 1
+ fi
+fi
+
+# 3. Échanger le code d'autorisation contre un access token
+RESPONSE=$(curl -s -X POST $TOKEN_URL \
+ -H "Content-Type: application/x-www-form-urlencoded" \
+ -d "client_id=$CLIENT_ID" \
+ -d "client_secret=$CLIENT_SECRET" \
+ -d "grant_type=authorization_code" \
+ -d "scope=$SCOPE" \
+ -d "code=$CODE" \
+ -d "redirect_uri=$REDIRECT_URI")
+
+# 4. Extraire l'access token de la réponse
+ACCESS_TOKEN=$(echo $RESPONSE | sed -n 's/.*"access_token":"\([^"]*\)".*/\1/p')
+ID_TOKEN=$(echo $RESPONSE | sed -n 's/.*"id_token":"\([^"]*\)".*/\1/p')
+
+# 5. Vérifier si le token a été obtenu et l'afficher
+if [ -z "$ACCESS_TOKEN" ]; then
+ echo "Erreur: Impossible d'obtenir un token d'accès."
+ echo "Réponse de l'API: $RESPONSE"
+ exit 1
+fi
+
+if [ -n ${GITHUB_ACTOR:-} ]; then
+ >&2 echo "using `cut -d '.' -f 2 <<< $ID_TOKEN | base64 -d 2>/dev/null | jq '.email'` account"
+ echo "::add-mask::$ACCESS_TOKEN"
+ echo "::add-mask::$ID_TOKEN"
+ echo "id_token=$ID_TOKEN" >> $GITHUB_OUTPUT
+ echo "access_token=$ACCESS_TOKEN" >> $GITHUB_OUTPUT
+else
+ echo -n "$ACCESS_TOKEN" > .access_token
+ echo -n "$ID_TOKEN" > .id_token
+fi
diff --git a/ansible/arcodange/factory/roles/gitea_repo/defaults/main.yml b/ansible/arcodange/factory/roles/gitea_repo/defaults/main.yml
index 92f71ac..a07a87e 100644
--- a/ansible/arcodange/factory/roles/gitea_repo/defaults/main.yml
+++ b/ansible/arcodange/factory/roles/gitea_repo/defaults/main.yml
@@ -6,4 +6,4 @@ gitea_username: arcodange
gitea_organization: arcodange-org
# URL de base du serveur Gitea
-gitea_base_url: http://{{ groups.gitea[0] }}:3000
+gitea_base_url: http://{{ groups.gitea[0] }}:3000
\ No newline at end of file
diff --git a/ansible/arcodange/factory/roles/gitea_repo/tasks/main.yml b/ansible/arcodange/factory/roles/gitea_repo/tasks/main.yml
index 6cccfa9..2dcd952 100644
--- a/ansible/arcodange/factory/roles/gitea_repo/tasks/main.yml
+++ b/ansible/arcodange/factory/roles/gitea_repo/tasks/main.yml
@@ -1,8 +1,3 @@
-- name: Generate Gitea Token
- when: gitea_api_token is undefined
- include_role:
- name: arcodange.factory.gitea_token
-
- name: Vérifier si le dépôt existe dans Gitea
uri:
url: "{{ gitea_base_url }}/api/v1/repos/{{ gitea_organization }}/{{ gitea_repo_name }}"
diff --git a/ansible/arcodange/factory/roles/gitea_secret/tasks/main.yml b/ansible/arcodange/factory/roles/gitea_secret/tasks/main.yml
index e6fdf76..78e09cd 100644
--- a/ansible/arcodange/factory/roles/gitea_secret/tasks/main.yml
+++ b/ansible/arcodange/factory/roles/gitea_secret/tasks/main.yml
@@ -1,7 +1,3 @@
-- name: Generate Gitea Token
- include_role:
- name: arcodange.factory.gitea_token
-
- name: Préparer l'URL de l'API pour mettre à jour ou ajouter un secret
set_fact:
gitea_api_url: |
diff --git a/ansible/arcodange/factory/roles/gitea_sync/defaults/main.yml b/ansible/arcodange/factory/roles/gitea_sync/defaults/main.yml
index 507389f..5bc0185 100644
--- a/ansible/arcodange/factory/roles/gitea_sync/defaults/main.yml
+++ b/ansible/arcodange/factory/roles/gitea_sync/defaults/main.yml
@@ -2,4 +2,6 @@ gitea_username: arcodange
gitea_organization: arcodange-org
# URL de base du serveur Gitea
-gitea_base_url: http://{{ groups.gitea[0] }}:3000
\ No newline at end of file
+gitea_base_url: http://{{ groups.gitea[0] }}:3000
+
+gitea_token_fact_name: arcodange_factory_gitea_sync_token
\ No newline at end of file
diff --git a/ansible/arcodange/factory/roles/gitea_sync/tasks/main.yml b/ansible/arcodange/factory/roles/gitea_sync/tasks/main.yml
index b8e15ca..a54b5df 100644
--- a/ansible/arcodange/factory/roles/gitea_sync/tasks/main.yml
+++ b/ansible/arcodange/factory/roles/gitea_sync/tasks/main.yml
@@ -16,10 +16,6 @@
status_code: 200
register: gitlab_repos
-- name: Generate Gitea Token
- include_role:
- name: arcodange.factory.gitea_token
-
- name: Lister les dépôts de l'organisation Gitea
uri:
url: "{{ gitea_base_url }}/api/v1/orgs/{{ gitea_organization }}/repos"
diff --git a/ansible/arcodange/factory/roles/gitea_token/tasks/main.yml b/ansible/arcodange/factory/roles/gitea_token/tasks/main.yml
index e161c49..7f5b66e 100644
--- a/ansible/arcodange/factory/roles/gitea_token/tasks/main.yml
+++ b/ansible/arcodange/factory/roles/gitea_token/tasks/main.yml
@@ -1,7 +1,10 @@
# to see generated tokens
# go to https://gitea.arcodange.duckdns.org/user/settings/applications
-- when: lookup('ansible.builtin.varnames', '^' ~ gitea_token_fact_name ~ '$') | length == 0 or gitea_token_delete
+- when: >-
+ lookup('ansible.builtin.varnames', '^' ~ gitea_token_fact_name ~ '$') | length == 0
+ or lookup('vars', gitea_token_fact_name) == 'deleted'
+ or gitea_token_delete
block:
- &createTokenTask
@@ -46,5 +49,11 @@
msg: 'WARN: gitea_api_token required when gitea_token_delete or gitea_token_replace is true'
- ansible.builtin.set_fact:
- '{{ gitea_token_fact_name }}': '{{ (gitea_api_token_cmd.rc == 0) | ternary(gitea_api_token_cmd.stdout, gitea_api_token_cmd_bis.stdout) }}'
- when: not gitea_token_delete
\ No newline at end of file
+ '{{ gitea_token_fact_name }}': >-
+ {{
+ 'deleted' if gitea_token_delete else
+ (
+ (gitea_api_token_cmd.rc == 0)
+ | ternary(gitea_api_token_cmd.stdout, gitea_api_token_cmd_bis.stdout)
+ )
+ }}
\ No newline at end of file
diff --git a/ansible/arcodange/factory/roles/traefik_certs/defaults/main.yml b/ansible/arcodange/factory/roles/traefik_certs/defaults/main.yml
new file mode 100644
index 0000000..e69de29
diff --git a/ansible/arcodange/factory/roles/traefik_certs/tasks/main.yml b/ansible/arcodange/factory/roles/traefik_certs/tasks/main.yml
new file mode 100644
index 0000000..003a0c8
--- /dev/null
+++ b/ansible/arcodange/factory/roles/traefik_certs/tasks/main.yml
@@ -0,0 +1,11 @@
+- when: traefik_certs_pem is not defined
+ block:
+ - shell: >-
+ kubectl -n kube-system exec
+ $(kubectl -n kube-system get pod -l app.kubernetes.io/name=traefik
+ -o jsonpath="{.items[0]['.metadata.name']}") --
+ cat /data/acme.json | jq '(.letsencrypt.Certificates | map(select(.domain.main=="arcodange.duckdns.org")))[0]'
+ | jq '.certificate' -r | base64 -d | openssl x509
+ register: traefik_certs_cmd
+ - set_fact:
+ traefik_cert_pem: '{{ traefik_certs_cmd.stdout }}'
\ No newline at end of file
diff --git a/doc/adr/04_tool_hashicorp_vault.md b/doc/adr/04_tool_hashicorp_vault.md
index 264b26d..5d1b1a6 100644
--- a/doc/adr/04_tool_hashicorp_vault.md
+++ b/doc/adr/04_tool_hashicorp_vault.md
@@ -35,7 +35,7 @@ sequenceDiagram
Ansible ->> Vault: unseal(unsealKey)
Ansible ->> Vault: revoke vaultRootToken
- rect rgb(255, 266, 255)
+ rect rgb(255, 255, 255)
Ansible ->> Gitea : setupApp(adminPassword)
activate Gitea
@@ -53,4 +53,19 @@ sequenceDiagram
end
end
+
+ rect rgb(180,150,100)
+ Ansible ->> Vault : share Google Credentials for open tofu GCS backend
+ Ansible ->> Gitea : gives oidc auth script for vault
+ activate Gitea
+ rect rgb(255,255,255)
+ Gitea ->> Vault: auth with oidc auth script
+ create actor Admin AS Admin User
+ Gitea ->> Admin: poll for Admin login
+ Note left of Admin: copy paste link
generated by
oidc auth script
+ Vault ->> Gitea: Google Credentials for open tofu GCS backend
+ Gitea ->> Vault: configure vault with open tofu
+ end
+ deactivate Gitea
+ end
```
\ No newline at end of file