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

Reply via email to