1
0

rotate_passwords.py 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. import pymysql
  2. import os
  3. import secrets
  4. import string
  5. import subprocess
  6. from dotenv import load_dotenv
  7. def generate_password(length=16):
  8. characters = string.ascii_letters + string.digits + "!@#$%^&*"
  9. return ''.join(secrets.choice(characters) for _ in range(length))
  10. def update_env_file(passwords):
  11. env_file = '.env'
  12. lines = []
  13. if os.path.exists(env_file):
  14. with open(env_file, 'r') as f:
  15. lines = f.readlines()
  16. # Remove old password lines
  17. lines = [l for l in lines if not any(l.startswith(f"{k}=") for k in passwords.keys())]
  18. # Add new passwords
  19. for key, val in passwords.items():
  20. lines.append(f"{key}={val}\n")
  21. with open(env_file, 'w') as f:
  22. f.writelines(lines)
  23. print("✅ .env file updated with new synchronized passwords.")
  24. def main():
  25. load_dotenv()
  26. print("🔄 Starting Password Synchronization Routine...")
  27. # 1. Connect to MySQL as root
  28. try:
  29. conn = pymysql.connect(
  30. host='192.168.130.170', # Assuming we run this from host to mapped port, or within a container network
  31. port=3307,
  32. user='root',
  33. password=os.environ.get('MYSQL_ROOT_PASSWORD', 'root_pass'),
  34. database='food_db'
  35. )
  36. except Exception as e:
  37. print(f"❌ Could not connect to MySQL: {e}")
  38. return
  39. # 2. Generate new passwords
  40. new_passwords = {
  41. 'DB_READER_PASS': generate_password(),
  42. 'DB_LOADER_PASS': generate_password(),
  43. 'DB_APP_AUTH_PASS': generate_password()
  44. }
  45. # 3. Update MySQL Users
  46. try:
  47. with conn.cursor() as cursor:
  48. cursor.execute("ALTER USER 'db_reader'@'%%' IDENTIFIED BY %s", (new_passwords['DB_READER_PASS'],))
  49. cursor.execute("ALTER USER 'db_loader'@'%%' IDENTIFIED BY %s", (new_passwords['DB_LOADER_PASS'],))
  50. cursor.execute("ALTER USER 'db_app_auth'@'%%' IDENTIFIED BY %s", (new_passwords['DB_APP_AUTH_PASS'],))
  51. cursor.execute("FLUSH PRIVILEGES")
  52. conn.commit()
  53. print("✅ Database user passwords rotated successfully.")
  54. except Exception as e:
  55. print(f"❌ Failed to alter database users: {e}")
  56. finally:
  57. conn.close()
  58. # 4. Update .env file so Docker Compose picks it up
  59. update_env_file(new_passwords)
  60. # 5. Gracefully restart client containers to sync connection
  61. print("🔄 Restarting App and Ingest containers to synchronize new credentials...")
  62. try:
  63. # Pass a clean environment without cached variables so docker-compose reads the updated .env file
  64. env = os.environ.copy()
  65. for k in new_passwords.keys():
  66. env.pop(k, None)
  67. subprocess.run(["docker-compose", "up", "-d", "app"], check=True, env=env)
  68. # We don't necessarily need to restart ingest if it's manual, but we can recreate it if it was running.
  69. print("✅ Client containers synchronized with new database passwords!")
  70. except Exception as e:
  71. print(f"⚠️ Failed to restart docker containers: {e}")
  72. if __name__ == "__main__":
  73. main()