|
@@ -0,0 +1,78 @@
|
|
|
|
|
+import pymysql
|
|
|
|
|
+import os
|
|
|
|
|
+import secrets
|
|
|
|
|
+import string
|
|
|
|
|
+import subprocess
|
|
|
|
|
+
|
|
|
|
|
+def generate_password(length=16):
|
|
|
|
|
+ characters = string.ascii_letters + string.digits + "!@#$%^&*"
|
|
|
|
|
+ return ''.join(secrets.choice(characters) for _ in range(length))
|
|
|
|
|
+
|
|
|
|
|
+def update_env_file(passwords):
|
|
|
|
|
+ env_file = '.env'
|
|
|
|
|
+ lines = []
|
|
|
|
|
+ if os.path.exists(env_file):
|
|
|
|
|
+ with open(env_file, 'r') as f:
|
|
|
|
|
+ lines = f.readlines()
|
|
|
|
|
+
|
|
|
|
|
+ # Remove old password lines
|
|
|
|
|
+ lines = [l for l in lines if not any(l.startswith(f"{k}=") for k in passwords.keys())]
|
|
|
|
|
+
|
|
|
|
|
+ # Add new passwords
|
|
|
|
|
+ for key, val in passwords.items():
|
|
|
|
|
+ lines.append(f"{key}={val}\n")
|
|
|
|
|
+
|
|
|
|
|
+ with open(env_file, 'w') as f:
|
|
|
|
|
+ f.writelines(lines)
|
|
|
|
|
+ print("✅ .env file updated with new synchronized passwords.")
|
|
|
|
|
+
|
|
|
|
|
+def main():
|
|
|
|
|
+ print("🔄 Starting Password Synchronization Routine...")
|
|
|
|
|
+
|
|
|
|
|
+ # 1. Connect to MySQL as root
|
|
|
|
|
+ try:
|
|
|
|
|
+ conn = pymysql.connect(
|
|
|
|
|
+ host='127.0.0.1', # Assuming we run this from host to mapped port, or within a container network
|
|
|
|
|
+ user='root',
|
|
|
|
|
+ password='root_pass',
|
|
|
|
|
+ database='food_db'
|
|
|
|
|
+ )
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ print(f"❌ Could not connect to MySQL: {e}")
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ # 2. Generate new passwords
|
|
|
|
|
+ new_passwords = {
|
|
|
|
|
+ 'DB_READER_PASS': generate_password(),
|
|
|
|
|
+ 'DB_LOADER_PASS': generate_password(),
|
|
|
|
|
+ 'DB_APP_AUTH_PASS': generate_password()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ # 3. Update MySQL Users
|
|
|
|
|
+ try:
|
|
|
|
|
+ with conn.cursor() as cursor:
|
|
|
|
|
+ cursor.execute("ALTER USER 'db_reader'@'%' IDENTIFIED BY %s", (new_passwords['DB_READER_PASS'],))
|
|
|
|
|
+ cursor.execute("ALTER USER 'db_loader'@'%' IDENTIFIED BY %s", (new_passwords['DB_LOADER_PASS'],))
|
|
|
|
|
+ cursor.execute("ALTER USER 'db_app_auth'@'%' IDENTIFIED BY %s", (new_passwords['DB_APP_AUTH_PASS'],))
|
|
|
|
|
+ cursor.execute("FLUSH PRIVILEGES")
|
|
|
|
|
+ conn.commit()
|
|
|
|
|
+ print("✅ Database user passwords rotated successfully.")
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ print(f"❌ Failed to alter database users: {e}")
|
|
|
|
|
+ finally:
|
|
|
|
|
+ conn.close()
|
|
|
|
|
+
|
|
|
|
|
+ # 4. Update .env file so Docker Compose picks it up
|
|
|
|
|
+ update_env_file(new_passwords)
|
|
|
|
|
+
|
|
|
|
|
+ # 5. Gracefully restart client containers to sync connection
|
|
|
|
|
+ print("🔄 Restarting App and Ingest containers to synchronize new credentials...")
|
|
|
|
|
+ try:
|
|
|
|
|
+ subprocess.run(["docker-compose", "up", "-d", "app"], check=True)
|
|
|
|
|
+ # We don't necessarily need to restart ingest if it's manual, but we can recreate it if it was running.
|
|
|
|
|
+ print("✅ Client containers synchronized with new database passwords!")
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ print(f"⚠️ Failed to restart docker containers: {e}")
|
|
|
|
|
+
|
|
|
|
|
+if __name__ == "__main__":
|
|
|
|
|
+ main()
|