add credentials option support

This commit is contained in:
Jonas Heinrich 2026-03-22 10:19:01 +01:00
parent 56f1f4e8fb
commit ecaf5ab897
2 changed files with 42 additions and 5 deletions

View file

@ -36,6 +36,22 @@ logger = logging.getLogger(__name__)
SITE_BASE_URL = os.environ.get("MEINANTRAG_BASE_URL", "http://localhost:8000") SITE_BASE_URL = os.environ.get("MEINANTRAG_BASE_URL", "http://localhost:8000")
def resolve_env_value(value):
"""
Resolve environment variable values that may use file: prefix.
If the value starts with 'file:', read the content from the specified path.
"""
if value and value.startswith("file:"):
file_path = value[5:]
try:
with open(file_path, 'r') as f:
return f.read().strip()
except (IOError, OSError) as e:
logger.warning(f"Failed to read credential file {file_path}: {e}")
return None
return value
class BaseTemplateResource: class BaseTemplateResource:
"""Base class for resources that need template rendering""" """Base class for resources that need template rendering"""
@ -141,8 +157,9 @@ class DatenschutzResource(BaseTemplateResource):
class GenerateAntragResource: class GenerateAntragResource:
def __init__(self): def __init__(self):
# Initialize Gemini API # Initialize Gemini API with file-macro support
api_key = os.environ.get("GOOGLE_GEMINI_API_KEY") api_key_raw = os.environ.get("GOOGLE_GEMINI_API_KEY")
api_key = resolve_env_value(api_key_raw)
if api_key: if api_key:
genai.configure(api_key=api_key) genai.configure(api_key=api_key)
self.model = genai.GenerativeModel("gemini-2.5-pro") self.model = genai.GenerativeModel("gemini-2.5-pro")

View file

@ -20,12 +20,27 @@ in
type = lib.types.attrsOf lib.types.str; type = lib.types.attrsOf lib.types.str;
default = { }; default = { };
example = { example = {
GOOGLE_GEMINI_API_KEY = "your-api-key-here";
MEINANTRAG_BASE_URL = "https://example.com"; MEINANTRAG_BASE_URL = "https://example.com";
GOOGLE_GEMINI_API_KEY = "file:/run/secrets/gemini_api_key";
}; };
description = '' description = ''
Additional environment variables to pass to the MeinAntrag service. Additional environment variables to pass to the MeinAntrag service.
For example, set GOOGLE_GEMINI_API_KEY for Gemini API integration. Values starting with "file:" will be read from the specified path.
For secrets with systemd LoadCredential, use the credentials option instead.
'';
};
credentials = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = { };
example = {
GOOGLE_GEMINI_API_KEY = "/run/secrets/gemini_api_key";
};
description = ''
Credentials to pass to the MeinAntrag service.
Maps environment variable names to file paths containing the secret values.
These are loaded via systemd's LoadCredential mechanism.
The Python app will automatically read the value from the file.
''; '';
}; };
@ -64,7 +79,8 @@ in
"PYTHONPATH=${pkgs.meinantrag}/share/meinantrag:${pkgs.meinantrag.pythonPath}" "PYTHONPATH=${pkgs.meinantrag}/share/meinantrag:${pkgs.meinantrag.pythonPath}"
"MEINANTRAG_TEMPLATES_DIR=${pkgs.meinantrag}/share/meinantrag/templates" "MEINANTRAG_TEMPLATES_DIR=${pkgs.meinantrag}/share/meinantrag/templates"
"MEINANTRAG_STATIC_DIR=${pkgs.meinantrag}/share/meinantrag/assets" "MEINANTRAG_STATIC_DIR=${pkgs.meinantrag}/share/meinantrag/assets"
] ++ (lib.mapAttrsToList (name: value: "${name}=${value}") cfg.settings); ] ++ (lib.mapAttrsToList (name: value: "${name}=${value}") cfg.settings)
++ (lib.mapAttrsToList (name: _: "${name}=file:/run/credentials/uwsgi.service/${name}") cfg.credentials);
settings = { settings = {
"static-map" = "/static=${pkgs.meinantrag}/share/meinantrag/assets"; "static-map" = "/static=${pkgs.meinantrag}/share/meinantrag/assets";
@ -74,6 +90,10 @@ in
}; };
}; };
# Load credentials via systemd's LoadCredential mechanism
systemd.services.uwsgi.serviceConfig.LoadCredential =
lib.mapAttrsToList (key: value: "${key}:${value}") cfg.credentials;
# Ensure meinantrag user and group exist # Ensure meinantrag user and group exist
users.users.meinantrag = { users.users.meinantrag = {
isSystemUser = true; isSystemUser = true;