[IMP] config: allow blocking access to db manager

- The `--no-database-list` option will now also block access to database
  management functions and screens.
  Presumably this flag should only be used in production when all
  databases have been provisioned, so the admin should like to block
  access to the db manager at the same time.

- If no `--database` or `-d` parameter is provided, the system will be
  unable to fetch a list of databases at all, so users will be blocked
  with an error message.

- Hide the link on the login screen to the DB manager when it is
  disabled, to prevent sending users to an error page.

- Weak attempt at updating the documentation

Note: the security check for RPC methods could have been done in the RPC
dispatcher, however that would not have protected service methods when
called directly, e.g. by a controller (e.g. the dump method).
This commit is contained in:
Olivier Dony
2017-10-03 12:46:03 +02:00
parent 7d16769263
commit b02dea7688
5 changed files with 54 additions and 13 deletions
+3
View File
@@ -491,6 +491,9 @@ class Home(http.Controller):
if 'login' not in values and request.session.get('auth_login'):
values['login'] = request.session.get('auth_login')
if not odoo.tools.config['list_db']:
values['disable_database_manager'] = True
response = request.render('web.login', values)
response.headers['X-Frame-Options'] = 'DENY'
return response
+7 -3
View File
@@ -116,7 +116,11 @@
<div class="text-center">
<img src="/web/static/src/img/logo2.png" class="img-responsive center-block"/>
</div>
{% if insecure and databases %}
{% if not list_db %}
<div class="alert alert-danger text-center">
The database manager has been disabled by the administrator
</div>
{% elif insecure and databases %}
<div class="alert alert-warning">
Warning, your Odoo database manager is not protected.
Please <a href="#" data-toggle="modal" data-target=".o_database_master">set a master password</a>
@@ -126,7 +130,7 @@
{% if error %}
<div class="alert alert-danger">{{ error }}</div>
{% endif %}
{% if databases %}
{% if list_db and databases %}
<div class="list-group">
{% for db in databases %}
<a href="/web?db={{ db }}" class="list-group-item" >
@@ -161,7 +165,7 @@
<a href="/web/database/manager">Manage databases</a>
</div>
{% endif %}
{% else %}
{% elif list_db %}
<form role="form" action="/web/database/create" method="post">
{{ create_form() }}
<input type="submit" value="Create database" class="btn btn-primary pull-left"/>
+19 -8
View File
@@ -71,7 +71,8 @@ in ``/etc/odoo.conf`` set:
Once it is correctly working and only matching a single database per hostname, it
is strongly recommended to block access to the database manager screens,
and to use the ``--no-database-list`` startup paramater to prevent listing
your databases. See also security_.
your databases, and to block access to the database management screens.
See also security_.
PostgreSQL
@@ -427,10 +428,14 @@ security-related topics:
- Use appropriate database filters ( :option:`--db-filter <odoo-bin --db-filter>`)
to restrict the visibility of your databases according to the hostname.
See :ref:`db_filter`.
You may also use :option:`-d <odoo-bin -d>` to provide your own (comma-separated)
list of available databases to filter from, instead of letting the system fetch
them all from the database backend.
- Once your ``db_filter`` is configured and only matches a single database per hostname,
you should set ``list_db`` configuration option to ``False``, to prevent listing databases
entirely (this is also exposed as the :option:`--no-database-list <odoo-bin --no-database-list>`
- Once your ``db_name`` and ``db_filter`` are configured and only match a single database
per hostname, you should set ``list_db`` configuration option to ``False``, to prevent
listing databases entirely, and to block access to the database management screens
(this is also exposed as the :option:`--no-database-list <odoo-bin --no-database-list>`
command-line option)
- Make sure the PostgreSQL user (:option:`--db_user <odoo-bin --db_user>`) is *not* a super-user,
@@ -473,10 +478,16 @@ Database Manager Security
This setting is used on all database management screens (to create, delete,
dump or restore databases).
If the management screens must not be accessible, or must only be accessible
from a selected set of machines, use the proxy server's features to block
access to all routes starting with ``/web/database`` except (maybe)
``/web/database/selector`` which displays the database-selection screen.
If the management screens must not be accessible at all, you should set ``list_db``
configuration option to ``False``, to block access to all the database selection and
management screens. But be sure to setup an appropriate ``db_name`` parameter
(and optionally, ``db_filter`` too) so that the system can determine the target database
for each request, otherwise users will be blocked as they won't be allowed to choose the
database themselves.
If the management screens must only be accessible from a selected set of machines,
use the proxy server's features to block access to all routes starting with ``/web/database``
except (maybe) ``/web/database/selector`` which displays the database-selection screen.
If the database-management screen should be left accessible, the
``admin_passwd`` setting must be changed from its ``admin`` default: this
+22 -1
View File
@@ -12,12 +12,13 @@ import zipfile
from functools import wraps
from contextlib import closing
from decorator import decorator
import psycopg2
import odoo
from odoo import SUPERUSER_ID
from odoo.exceptions import UserError
from odoo.exceptions import AccessDenied
import odoo.release
import odoo.sql_db
import odoo.tools
@@ -30,6 +31,15 @@ _logger = logging.getLogger(__name__)
class DatabaseExists(Warning):
pass
def check_db_management_enabled(method):
def if_db_mgt_enabled(method, self, *args, **kwargs):
if not odoo.tools.config['list_db']:
_logger.error('Database management functions blocked, admin disabled database listing')
raise AccessDenied()
return method(self, *args, **kwargs)
return decorator(if_db_mgt_enabled, method)
#----------------------------------------------------------
# Master password required
#----------------------------------------------------------
@@ -89,6 +99,7 @@ def _create_empty_database(name):
cr.autocommit(True) # avoid transaction block
cr.execute("""CREATE DATABASE "%s" ENCODING 'unicode' TEMPLATE "%s" """ % (name, chosen_template))
@check_db_management_enabled
def exp_create_database(db_name, demo, lang, user_password='admin', login='admin', country_code=None):
""" Similar to exp_create but blocking."""
_logger.info('Create database `%s`.', db_name)
@@ -96,6 +107,7 @@ def exp_create_database(db_name, demo, lang, user_password='admin', login='admin
_initialize_db(id, db_name, demo, lang, user_password, login, country_code)
return True
@check_db_management_enabled
def exp_duplicate_database(db_original_name, db_name):
_logger.info('Duplicate database `%s` to `%s`.', db_original_name, db_name)
odoo.sql_db.close_db(db_original_name)
@@ -133,6 +145,7 @@ def _drop_conn(cr, db_name):
except Exception:
pass
@check_db_management_enabled
def exp_drop(db_name):
if db_name not in list_dbs(True):
return False
@@ -157,12 +170,14 @@ def exp_drop(db_name):
shutil.rmtree(fs)
return True
@check_db_management_enabled
def exp_dump(db_name, format):
with tempfile.TemporaryFile(mode='w+b') as t:
dump_db(db_name, t, format)
t.seek(0)
return base64.b64encode(t.read()).decode()
@check_db_management_enabled
def dump_db_manifest(cr):
pg_version = "%d.%d" % divmod(cr._obj.connection.server_version / 100, 100)
cr.execute("SELECT name, latest_version FROM ir_module_module WHERE state = 'installed'")
@@ -178,6 +193,7 @@ def dump_db_manifest(cr):
}
return manifest
@check_db_management_enabled
def dump_db(db_name, stream, backup_format='zip'):
"""Dump database `db` into file-like object `stream` if stream is None
return a file object with the dump """
@@ -213,6 +229,7 @@ def dump_db(db_name, stream, backup_format='zip'):
else:
return stdout
@check_db_management_enabled
def exp_restore(db_name, data, copy=False):
data_file = tempfile.NamedTemporaryFile(delete=False)
try:
@@ -223,6 +240,7 @@ def exp_restore(db_name, data, copy=False):
os.unlink(data_file.name)
return True
@check_db_management_enabled
def restore_db(db, dump_file, copy=False):
assert isinstance(db, pycompat.string_types)
if exp_db_exist(db):
@@ -277,6 +295,7 @@ def restore_db(db, dump_file, copy=False):
_logger.info('RESTORE DB: %s', db)
@check_db_management_enabled
def exp_rename(old_name, new_name):
odoo.modules.registry.Registry.delete(old_name)
odoo.sql_db.close_db(old_name)
@@ -298,11 +317,13 @@ def exp_rename(old_name, new_name):
shutil.move(old_fs, new_fs)
return True
@check_db_management_enabled
def exp_change_admin_password(new_password):
odoo.tools.config.set_admin_password(new_password)
odoo.tools.config.save()
return True
@check_db_management_enabled
def exp_migrate_databases(databases):
for db in databases:
_logger.info('migrate database %s', db)
+3 -1
View File
@@ -245,7 +245,9 @@ class configmanager(object):
security = optparse.OptionGroup(parser, 'Security-related options')
security.add_option('--no-database-list', action="store_false", dest='list_db', my_default=True,
help="disable the ability to return the list of databases")
help="Disable the ability to obtain or view the list of databases. "
"Also disable access to the database manager and selector, "
"so be sure to set a proper --database parameter first")
parser.add_option_group(security)
# Advanced options