162 lines
6.8 KiB
YAML
162 lines
6.8 KiB
YAML
---
|
|
# Read Supabase secrets from Vault; generate and store any that are missing.
|
|
#
|
|
# All __supabase_* facts set here are consumed by the K8s secret tasks in main.yml.
|
|
#
|
|
# JWT note: if jwt_secret is absent OR either JWT token is absent, all three are
|
|
# regenerated together — a partial JWT state (e.g. tokens signed by a different
|
|
# secret) would break auth across all services.
|
|
|
|
# ------------------------------------------------------------------
|
|
# Read existing secrets (may fail if path does not exist yet)
|
|
# ------------------------------------------------------------------
|
|
- name: Read supabase secrets from Vault
|
|
community.hashi_vault.vault_kv2_get:
|
|
path: "{{ supabase_vault_path }}"
|
|
engine_mount_point: "{{ supabase_vault_mount }}"
|
|
register: __supabase_vault_read
|
|
failed_when: false
|
|
no_log: "{{ supabase_no_log }}"
|
|
|
|
- name: Set existing vault data fact
|
|
ansible.builtin.set_fact:
|
|
__sv: "{{ __supabase_vault_read.secret | default({}) }}"
|
|
no_log: "{{ supabase_no_log }}"
|
|
|
|
# ------------------------------------------------------------------
|
|
# Resolve non-JWT secrets: use existing or generate random values
|
|
# ------------------------------------------------------------------
|
|
- name: Resolve non-JWT secrets (generate any that are missing)
|
|
ansible.builtin.set_fact:
|
|
__supabase_db_password: >-
|
|
{{ __sv.db_password
|
|
| default(lookup('community.general.random_string', length=32, special=false)) }}
|
|
__supabase_dashboard_username: >-
|
|
{{ __sv.dashboard_username | default('supabase') }}
|
|
__supabase_dashboard_password: >-
|
|
{{ __sv.dashboard_password
|
|
| default(lookup('community.general.random_string', length=24, special=false)) }}
|
|
__supabase_analytics_public_token: >-
|
|
{{ __sv.analytics_public_token
|
|
| default(lookup('community.general.random_string', length=32, special=false)) }}
|
|
__supabase_analytics_private_token: >-
|
|
{{ __sv.analytics_private_token
|
|
| default(lookup('community.general.random_string', length=32, special=false)) }}
|
|
__supabase_realtime_secret_key_base: >-
|
|
{{ __sv.realtime_secret_key_base
|
|
| default(lookup('community.general.random_string', length=64, special=false)) }}
|
|
__supabase_meta_crypto_key: >-
|
|
{{ __sv.meta_crypto_key
|
|
| default(lookup('community.general.random_string', length=32, special=false)) }}
|
|
__supabase_openai_api_key: >-
|
|
{{ __sv.openai_api_key | default('') }}
|
|
no_log: "{{ supabase_no_log }}"
|
|
|
|
# ------------------------------------------------------------------
|
|
# JWT secret + signed tokens (must be generated as a coherent set)
|
|
# ------------------------------------------------------------------
|
|
- name: Check whether JWT values need to be (re)generated
|
|
ansible.builtin.set_fact:
|
|
__supabase_needs_jwt_gen: >-
|
|
{{ not (__sv.jwt_secret | default('') | string | length > 0)
|
|
or not (__sv.anon_key | default('') | string | length > 0)
|
|
or not (__sv.service_key | default('') | string | length > 0) }}
|
|
|
|
- name: Generate JWT signing secret
|
|
ansible.builtin.set_fact:
|
|
__supabase_jwt_secret: >-
|
|
{{ lookup('community.general.random_string', length=64, special=false) }}
|
|
no_log: "{{ supabase_no_log }}"
|
|
when: __supabase_needs_jwt_gen | bool
|
|
|
|
- name: Use existing JWT signing secret
|
|
ansible.builtin.set_fact:
|
|
__supabase_jwt_secret: "{{ __sv.jwt_secret }}"
|
|
no_log: "{{ supabase_no_log }}"
|
|
when: not __supabase_needs_jwt_gen | bool
|
|
|
|
- name: Generate anon and service_role JWT tokens
|
|
ansible.builtin.command:
|
|
argv:
|
|
- python3
|
|
- -c
|
|
- |
|
|
import hmac, hashlib, base64, json, time, sys
|
|
|
|
def b64url(data):
|
|
if isinstance(data, str):
|
|
data = data.encode()
|
|
return base64.urlsafe_b64encode(data).rstrip(b'=').decode()
|
|
|
|
def make_jwt(secret, role):
|
|
now = int(time.time())
|
|
header = b64url(json.dumps({'alg': 'HS256', 'typ': 'JWT'}, separators=(',', ':')))
|
|
payload = b64url(json.dumps(
|
|
{'role': role, 'iss': 'supabase', 'iat': now, 'exp': now + 157680000},
|
|
separators=(',', ':')
|
|
))
|
|
msg = '{}.{}'.format(header, payload)
|
|
sig = b64url(hmac.new(secret.encode(), msg.encode(), hashlib.sha256).digest())
|
|
return '{}.{}'.format(msg, sig)
|
|
|
|
secret = sys.argv[1]
|
|
print(make_jwt(secret, 'anon'))
|
|
print(make_jwt(secret, 'service_role'))
|
|
- "{{ __supabase_jwt_secret }}"
|
|
register: __supabase_jwt_output
|
|
changed_when: false
|
|
no_log: "{{ supabase_no_log }}"
|
|
when: __supabase_needs_jwt_gen | bool
|
|
|
|
- name: Set generated JWT token facts
|
|
ansible.builtin.set_fact:
|
|
__supabase_anon_key: "{{ __supabase_jwt_output.stdout_lines[0] }}"
|
|
__supabase_service_key: "{{ __supabase_jwt_output.stdout_lines[1] }}"
|
|
no_log: "{{ supabase_no_log }}"
|
|
when: __supabase_needs_jwt_gen | bool
|
|
|
|
- name: Use existing JWT tokens from Vault
|
|
ansible.builtin.set_fact:
|
|
__supabase_anon_key: "{{ __sv.anon_key }}"
|
|
__supabase_service_key: "{{ __sv.service_key }}"
|
|
no_log: "{{ supabase_no_log }}"
|
|
when: not __supabase_needs_jwt_gen | bool
|
|
|
|
# ------------------------------------------------------------------
|
|
# Write back to Vault (always — ensures all fields are present and
|
|
# any newly generated values are persisted before K8s secrets are written)
|
|
# ------------------------------------------------------------------
|
|
- name: Write supabase secrets to Vault
|
|
community.hashi_vault.vault_kv2_write:
|
|
path: "{{ supabase_vault_path }}"
|
|
engine_mount_point: "{{ supabase_vault_mount }}"
|
|
data:
|
|
jwt_secret: "{{ __supabase_jwt_secret }}"
|
|
anon_key: "{{ __supabase_anon_key }}"
|
|
service_key: "{{ __supabase_service_key }}"
|
|
db_password: "{{ __supabase_db_password }}"
|
|
dashboard_username: "{{ __supabase_dashboard_username }}"
|
|
dashboard_password: "{{ __supabase_dashboard_password }}"
|
|
analytics_public_token: "{{ __supabase_analytics_public_token }}"
|
|
analytics_private_token: "{{ __supabase_analytics_private_token }}"
|
|
realtime_secret_key_base: "{{ __supabase_realtime_secret_key_base }}"
|
|
meta_crypto_key: "{{ __supabase_meta_crypto_key }}"
|
|
openai_api_key: "{{ __supabase_openai_api_key }}"
|
|
no_log: "{{ supabase_no_log }}"
|
|
|
|
- name: Write postgres_url to oys/dev/supabase in Vault
|
|
community.hashi_vault.vault_kv2_write:
|
|
path: oys/dev/supabase
|
|
engine_mount_point: "{{ supabase_vault_mount }}"
|
|
data:
|
|
postgres_url: >-
|
|
postgresql://postgres:{{ __supabase_db_password }}@{{ supabase_db_external_host }}:{{ supabase_db_nodeport }}/{{ supabase_db_name }}
|
|
no_log: "{{ supabase_no_log }}"
|
|
|
|
- name: Report vault secret status
|
|
ansible.builtin.debug:
|
|
msg: >-
|
|
{{ 'Generated and stored new Supabase secrets in Vault'
|
|
if __supabase_needs_jwt_gen | bool
|
|
else 'Using existing Supabase secrets from Vault' }}
|