Hi Ashesh, Can you please review the attached patch, and apply if you're happy with it?
The purpose is to auto-generate the various security keys that are currently in the configuration file, and store them in the SQLite database. This allows us to remove the checks for config_local.py and the hard-coded default keys which are causing some problems with packaging: - Hard coded defaults are fine for Desktop mode, and packages generally aim to make that work primarily. - Hard coded defaults are a security risk for Server mode, hence we currently require the user to manually setup keys, which is currently being overridden by packagers for Desktop mode. This change ensures that we have unique security keys for every installation, whether running in desktop or server mode (generated from os.urandom). Thanks! -- Dave Page Blog: http://pgsnake.blogspot.com Twitter: @pgsnake EnterpriseDB UK: http://www.enterprisedb.com The Enterprise PostgreSQL Company
diff --git a/web/config.py b/web/config.py index 20714f9..8411d79 100644 --- a/web/config.py +++ b/web/config.py @@ -140,21 +140,13 @@ DEFAULT_SERVER_PORT = 5050 # Enable CSRF protection? CSRF_ENABLED = True -# Secret key for signing CSRF data. Override this in config_local.py if -# running on a web server -CSRF_SESSION_KEY = 'SuperSecret1' - -# Secret key for signing cookies. Override this in config_local.py if -# running on a web server -SECRET_KEY = 'SuperSecret2' - -# Salt used when hashing passwords. Override this in config_local.py if -# running on a web server -SECURITY_PASSWORD_SALT = 'SuperSecret3' - # Hashing algorithm used for password storage SECURITY_PASSWORD_HASH = 'pbkdf2_sha512' +# NOTE: CSRF_SESSION_KEY, SECRET_KEY and SECURITY_PASSWORD_SALT are no +# longer part of the main configuration, but are stored in the +# configuration databases 'keys' table and are auto-generated. + # Should HTML be minified on the fly when not in debug mode? # Note: This is disabled by default as it will error when processing the # docs. If the serving of docs is handled by an Apache HTTPD diff --git a/web/pgAdmin4.py b/web/pgAdmin4.py index 1fb34f9..f894f8b 100644 --- a/web/pgAdmin4.py +++ b/web/pgAdmin4.py @@ -32,18 +32,6 @@ config.SETTINGS_SCHEMA_VERSION = SCHEMA_VERSION # Sanity checks ########################################################################## -# Check for local settings if running in server mode -if config.SERVER_MODE is True: - local_config = os.path.join(os.path.dirname(os.path.realpath(__file__)), - 'config_local.py') - if not os.path.isfile(local_config): - print("The configuration file %s does not exist.\n" % local_config) - print("Before running this application, ensure that config_local.py has been created") - print("and sets values for SECRET_KEY, SECURITY_PASSWORD_SALT and CSRF_SESSION_KEY") - print("at bare minimum. See config.py for more information and a complete list of") - print("settings. Exiting...") - sys.exit(1) - # Check if the database exists. If it does not, create it. if not os.path.isfile(config.SQLITE_PATH): setupfile = os.path.join(os.path.dirname(os.path.realpath(__file__)), diff --git a/web/pgadmin/__init__.py b/web/pgadmin/__init__.py index d988172..21fe636 100644 --- a/web/pgadmin/__init__.py +++ b/web/pgadmin/__init__.py @@ -26,7 +26,7 @@ from pgadmin.utils.session import create_session_interface from werkzeug.local import LocalProxy from werkzeug.utils import find_modules -from pgadmin.model import db, Role, Server, ServerGroup, User, Version +from pgadmin.model import db, Role, Server, ServerGroup, User, Version, Keys # Configuration settings import config @@ -127,11 +127,6 @@ def create_app(app_name=config.APP_NAME): app.config.update(dict(PROPAGATE_EXCEPTIONS=True)) ########################################################################## - # Setup session management - ########################################################################## - app.session_interface = create_session_interface(app) - - ########################################################################## # Setup logging and log the application startup ########################################################################## @@ -222,7 +217,19 @@ def create_app(app_name=config.APP_NAME): from setup import do_upgrade do_upgrade(app, user_datastore, security, version) + ########################################################################## + # Setup security + ########################################################################## + with app.app_context(): + config.CSRF_SESSION_KEY = Keys.query.filter_by(name = 'CSRF_SESSION_KEY').first().value + config.SECRET_KEY = Keys.query.filter_by(name = 'SECRET_KEY').first().value + config.SECURITY_PASSWORD_SALT = Keys.query.filter_by(name = 'SECURITY_PASSWORD_SALT').first().value + + app.session_interface = create_session_interface(app) + + ########################################################################## # Load all available server drivers + ########################################################################## driver.init_app(app) ########################################################################## diff --git a/web/pgadmin/model/__init__.py b/web/pgadmin/model/__init__.py index 019e9b1..9727d2b 100644 --- a/web/pgadmin/model/__init__.py +++ b/web/pgadmin/model/__init__.py @@ -29,7 +29,7 @@ from flask_sqlalchemy import SQLAlchemy # ########################################################################## -SCHEMA_VERSION = 13 +SCHEMA_VERSION = 14 ########################################################################## # @@ -207,3 +207,10 @@ class Process(db.Model): end_time = db.Column(db.String(), nullable=True) exit_code = db.Column(db.Integer(), nullable=True) acknowledge = db.Column(db.String(), nullable=True) + + +class Keys(db.Model): + """Define the keys table.""" + __tablename__ = 'keys' + name = db.Column(db.String(), nullable=False, primary_key=True) + value = db.Column(db.String(), nullable=False) \ No newline at end of file diff --git a/web/setup.py b/web/setup.py index 64273fb..90697fa 100755 --- a/web/setup.py +++ b/web/setup.py @@ -10,6 +10,7 @@ """Perform the initial setup of the application, by creating the auth and settings database.""" +import base64 import getpass import os import random @@ -22,7 +23,7 @@ from flask_security import Security, SQLAlchemyUserDatastore from flask_security.utils import encrypt_password from pgadmin.model import db, Role, User, Server, \ - ServerGroup, Version + ServerGroup, Version, Keys # Configuration settings import config @@ -40,6 +41,16 @@ if hasattr(__builtins__, 'raw_input'): def do_setup(app): """Create a new settings database from scratch""" + + # Get some defaults for the various keys + with app.app_context(): + config.CSRF_SESSION_KEY = base64.urlsafe_b64encode(os.urandom(32)) + app.jinja_env.globals['config']['CSRF_SESSION_KEY'] = config.CSRF_SESSION_KEY + config.SECRET_KEY = base64.urlsafe_b64encode(os.urandom(32)) + app.jinja_env.globals['config']['SECRET_KEY'] = config.SECRET_KEY + config.SECURITY_PASSWORD_SALT = base64.urlsafe_b64encode(os.urandom(32)) + app.jinja_env.globals['config']['SECURITY_PASSWORD_SALT'] = config.SECURITY_PASSWORD_SALT + if config.SERVER_MODE is False: print("NOTE: Configuring authentication for DESKTOP mode.") email = config.DESKTOP_USER @@ -116,6 +127,17 @@ def do_setup(app): name='ConfigDB', value=config.SETTINGS_SCHEMA_VERSION ) db.session.merge(version) + db.session.commit() + + # Create the keys + key = Keys(name='CSRF_SESSION_KEY', value=config.CSRF_SESSION_KEY) + db.session.merge(key) + + key = Keys(name='SECRET_KEY', value=config.SECRET_KEY) + db.session.merge(key) + + key = Keys(name='SECURITY_PASSWORD_SALT', value=config.SECURITY_PASSWORD_SALT) + db.session.merge(key) db.session.commit() @@ -329,6 +351,23 @@ ALTER TABLE SERVER ADD COLUMN discovery_id TEXT """) + if int(version.value) < 14: + db.engine.execute(""" +CREATE TABLE keys ( + name TEST NOT NULL, + value TEXT NOT NULL, + PRIMARY KEY (name)) + """) + + sql = "INSERT INTO keys (name, value) VALUES ('CSRF_SESSION_KEY', '%s')" % base64.urlsafe_b64encode(os.urandom(32)) + db.engine.execute(sql) + + sql = "INSERT INTO keys (name, value) VALUES ('SECRET_KEY', '%s')" % base64.urlsafe_b64encode(os.urandom(32)) + db.engine.execute(sql) + + sql = "INSERT INTO keys (name, value) VALUES ('SECURITY_PASSWORD_SALT', '%s')" % base64.urlsafe_b64encode(os.urandom(32)) + db.engine.execute(sql) + # Finally, update the schema version version.value = config.SETTINGS_SCHEMA_VERSION db.session.merge(version) @@ -364,15 +403,6 @@ if __name__ == '__main__': 'config_local.py' ) - if not os.path.isfile(local_config): - print(""" - The configuration file - {0} does not exist. - Before running this application, ensure that config_local.py has been created - and sets values for SECRET_KEY, SECURITY_PASSWORD_SALT and CSRF_SESSION_KEY - at bare minimum. See config.py for more information and a complete list of - settings. Exiting...""".format(local_config)) - sys.exit(1) - # Check if the database exists. If it does, tell the user and exit. if os.path.isfile(config.SQLITE_PATH): print("""
-- Sent via pgadmin-hackers mailing list (pgadmin-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers