Skip to content

Commit

Permalink
fix db credential management with vault
Browse files Browse the repository at this point in the history
  • Loading branch information
itsbekas committed Jan 25, 2025
1 parent bed0a02 commit 377f39b
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 14 deletions.
10 changes: 6 additions & 4 deletions backend/lifehub/config/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,19 @@ def check_mariadb() -> None:
timeout: int = 60
interval: int = 5
start_time = time.time()
engine = get_engine()
while True:
try:
connection = get_engine().connect()
connection = engine.connect()
connection.close()
print("Successfully connected to MariaDB")
break
except Exception:
except Exception as e:
if time.time() - start_time > timeout:
print("Could not connect to MariaDB within the timeout period")
exit(1)
print("MariaDB not ready, waiting...")
print(e)
time.sleep(interval)


Expand Down Expand Up @@ -101,13 +103,13 @@ def pre_run_checks() -> None:


def create_db_tables() -> None:
BaseModel.metadata.create_all(get_engine())
BaseModel.metadata.create_all(get_engine(admin=True))


def pre_run_setup() -> None:
setup_vault() # Must run before db checks since it sets up the db credentials
check_mariadb()
create_db_tables()
setup_vault()
setup_providers()
setup_admin_user()
setup_admin_tokens()
3 changes: 2 additions & 1 deletion backend/lifehub/config/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ def getenv(key: str) -> str:
VAULT_TOKEN = getenv("VAULT_TOKEN")
VAULT_DB_USER = getenv("VAULT_DB_USER")
VAULT_DB_PASSWORD = getenv("VAULT_DB_PASSWORD")
VAULT_DB_ROLE = "mariadb-role"
VAULT_DB_ROLE = "lifehub-app"
VAULT_DB_ADMIN_ROLE = "lifehub-admin"

ADMIN_USERNAME = getenv("ADMIN_USERNAME")
ADMIN_PASSWORD = getenv("ADMIN_PASSWORD")
Expand Down
28 changes: 23 additions & 5 deletions backend/lifehub/config/vault.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
DB_HOST,
DB_NAME,
VAULT_ADDR,
VAULT_DB_ADMIN_ROLE,
VAULT_DB_PASSWORD,
VAULT_DB_ROLE,
VAULT_DB_USER,
Expand Down Expand Up @@ -48,18 +49,35 @@ def setup_vault() -> None:
client.secrets.database.configure(
name=DB_NAME,
plugin_name="mysql-database-plugin",
connection_url=f"{VAULT_DB_USER}:{VAULT_DB_PASSWORD}@tcp({DB_HOST}:3306)/",
allowed_roles=VAULT_DB_ROLE,
connection_url="{{username}}:{{password}}@tcp(" + DB_HOST + ":3306)/",
allowed_roles=[VAULT_DB_ROLE, VAULT_DB_ADMIN_ROLE],
username=VAULT_DB_USER,
password=VAULT_DB_PASSWORD,
)

# Define the role
client.secrets.database.create_or_update_role(
# Create a read/write role for the database secret engine
client.secrets.database.create_role(
name=VAULT_DB_ROLE,
db_name=DB_NAME,
creation_statements=[
"CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}';",
"GRANT SELECT ON " + DB_NAME + ".* TO '{{name}}'@'%';",
"GRANT SELECT, INSERT, UPDATE, DELETE ON "
+ DB_NAME
+ ".* TO '{{name}}'@'%';",
],
default_ttl="1h",
max_ttl="24h",
)

# Create an admin role for the database secret engine
client.secrets.database.create_role(
name=VAULT_DB_ADMIN_ROLE,
db_name=DB_NAME,
creation_statements=[
"CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}';",
"GRANT ALL PRIVILEGES ON " + DB_NAME + ".* TO '{{name}}'@'%';",
"GRANT GRANT OPTION ON " + DB_NAME + ".* TO '{{name}}'@'%';",
],
default_ttl="1h",
max_ttl="2h",
)
10 changes: 6 additions & 4 deletions backend/lifehub/core/common/database_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
DB_HOST,
DB_NAME,
VAULT_ADDR,
VAULT_DB_ADMIN_ROLE,
VAULT_DB_ROLE,
VAULT_TOKEN,
)
Expand All @@ -19,24 +20,25 @@
from sqlalchemy.orm import Session


def get_db_credentials() -> dict[str, str]:
def get_db_credentials(admin: bool = False) -> dict[str, str]:
"""
Fetch dynamic credentials from Vault.
"""
client = hvac.Client(url=VAULT_ADDR, token=VAULT_TOKEN)
creds = client.secrets.database.generate_credentials(VAULT_DB_ROLE)
role = VAULT_DB_ADMIN_ROLE if admin else VAULT_DB_ROLE
creds = client.secrets.database.generate_credentials(name=role)

return {
"username": creds["data"]["username"],
"password": creds["data"]["password"],
}


def get_engine() -> Engine:
def get_engine(admin: bool = False) -> Engine:
"""
Create a SQLAlchemy engine with dynamic credentials.
"""
creds = get_db_credentials()
creds = get_db_credentials(admin)
db_url = f"mariadb+mariadbconnector://{creds['username']}:{creds['password']}@{DB_HOST}:3306/{DB_NAME}"
return create_engine(db_url)

Expand Down
2 changes: 2 additions & 0 deletions init-scripts/mariadb/create-vault-user.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/bin/bash

db_name="${MYSQL_DATABASE:-db_name}"
# Default variables for Vault user and password
vault_user="${VAULT_DB_USER:-vault_user}"
vault_password="${VAULT_DB_PASSWORD:-default_vault_password}"
Expand All @@ -9,5 +10,6 @@ mariadb -u root -p"${MYSQL_ROOT_PASSWORD}" <<EOF
CREATE USER IF NOT EXISTS '${vault_user}'@'%' IDENTIFIED BY '${vault_password}';
GRANT CREATE USER, DROP, SELECT, INSERT, UPDATE, DELETE, ALTER, EXECUTE ON *.* TO '${vault_user}'@'%';
GRANT ALL PRIVILEGES ON mysql.* TO '${vault_user}'@'%';
GRANT ALL PRIVILEGES ON ${db_name}.* TO '${vault_user}'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
EOF

0 comments on commit 377f39b

Please sign in to comment.