83 lines
3.7 KiB
Python
83 lines
3.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate stable local auth secrets. Nothing privileged is emitted to stdout."""
|
|
import base64
|
|
import hashlib
|
|
import hmac
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import secrets
|
|
import time
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
AUTH_DIR = ROOT / 'deploy/supabase'
|
|
|
|
|
|
def read_env(path):
|
|
return dict(line.split('=', 1) for line in path.read_text().splitlines() if line and not line.startswith('#'))
|
|
|
|
|
|
def jwt(secret, role):
|
|
def encode(value):
|
|
return base64.urlsafe_b64encode(json.dumps(value, separators=(',', ':')).encode()).rstrip(b'=').decode()
|
|
now = int(time.time())
|
|
message = encode({'alg': 'HS256', 'typ': 'JWT'}) + '.' + encode({'role': role, 'iss': 'supabase', 'iat': now, 'exp': now + 10 * 365 * 86400})
|
|
signature = base64.urlsafe_b64encode(hmac.new(secret.encode(), message.encode(), hashlib.sha256).digest()).rstrip(b'=').decode()
|
|
return message + '.' + signature
|
|
|
|
|
|
def write_private(path, content):
|
|
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
|
|
with os.fdopen(fd, 'w') as output:
|
|
output.write(content)
|
|
path.chmod(0o600)
|
|
|
|
|
|
def main():
|
|
env_path = AUTH_DIR / '.env'
|
|
if not env_path.exists():
|
|
secret = secrets.token_hex(32)
|
|
demo_path = ROOT / 'DEMO_USER.txt'
|
|
demo = dict(line.split(': ', 1) for line in demo_path.read_text().splitlines() if ': ' in line) if demo_path.exists() else {}
|
|
values = {
|
|
'POSTGRES_PASSWORD': secrets.token_hex(24),
|
|
'JWT_SECRET': secret,
|
|
'SUPABASE_ANON_KEY': jwt(secret, 'anon'),
|
|
'SUPABASE_SERVICE_KEY': jwt(secret, 'service_role'),
|
|
'SUPABASE_PORT': os.getenv('SUPABASE_PORT', '54321'),
|
|
'KONG_BIND_ADDRESS': os.getenv('KONG_BIND_ADDRESS', '0.0.0.0'),
|
|
'SITE_URL': os.getenv('SITE_URL', 'https://defense.naori.cloud'),
|
|
'DEMO_EMAIL': demo.get('Username', 'demo@defense.naori.cloud'),
|
|
'DEMO_PASSWORD': demo.get('Password', secrets.token_urlsafe(18)),
|
|
}
|
|
write_private(env_path, ''.join(f'{key}={value}\n' for key, value in values.items()))
|
|
print('Generated auth secrets and demo credentials in deploy/supabase/.env')
|
|
values = read_env(env_path)
|
|
config = {
|
|
'_format_version': '2.1', '_transform': True,
|
|
'consumers': [
|
|
{'username': 'anon', 'keyauth_credentials': [{'key': values['SUPABASE_ANON_KEY']}]},
|
|
{'username': 'service_role', 'keyauth_credentials': [{'key': values['SUPABASE_SERVICE_KEY']}]},
|
|
],
|
|
'services': [
|
|
{'name': name, 'url': url, 'routes': [{'name': name, 'paths': [path], 'strip_path': True}],
|
|
'plugins': [{'name': 'cors'}, {'name': 'key-auth', 'config': {'hide_credentials': False}}]}
|
|
for name, url, path in [('auth', 'http://auth:9999/', '/auth/v1/'), ('rest', 'http://rest:3000/', '/rest/v1/')]
|
|
],
|
|
}
|
|
config['services'].extend([
|
|
{'name': 'simulation', 'url': 'http://host.docker.internal:8000',
|
|
'routes': [{'name': 'simulation', 'paths': ['/socket.io/'], 'strip_path': False}],
|
|
'read_timeout': 60000, 'write_timeout': 60000},
|
|
{'name': 'frontend', 'url': 'http://frontend:80',
|
|
'routes': [{'name': 'frontend', 'paths': ['/'], 'strip_path': False}]},
|
|
])
|
|
# Kong runs as UID 1337; its bind-mounted config must be readable inside the container.
|
|
write_private(AUTH_DIR / 'kong.json.local', json.dumps(config, indent=2))
|
|
(AUTH_DIR / 'kong.json.local').chmod(0o644)
|
|
write_private(ROOT / 'deploy/auth.env.local', f"SUPABASE_URL=http://127.0.0.1:{values['SUPABASE_PORT']}\nSUPABASE_ANON_KEY={values['SUPABASE_ANON_KEY']}\n")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|