mneethiraj commented on code in PR #920: URL: https://github.com/apache/ranger/pull/920#discussion_r3222832333
########## dev-support/ranger-docker/scripts/admin/ranger_admin_xml_config.py: ########## @@ -0,0 +1,112 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. See accompanying LICENSE file. +# + +import os +import re +import xml.etree.ElementTree as ET + +DEFAULT_RANGER_ADMIN_SITE_CANDIDATES = ( Review Comment: Instead of programmatically updating ranger-admin-site.xml, consider having multiple ranger-admin-site.xml files, one for each DB flavor, and copy the appropriate one to conf/ranger-admin-site.xml during initialization of the container. With this approach, this file will no more be required. ########## dev-support/ranger-docker/README.md: ########## @@ -44,6 +44,15 @@ Use Dockerfiles in this directory to create docker images and run them to build # valid values for RANGER_DB_TYPE: mysql/postgres/oracle ~~~ +- The Ranger Admin container uses `scripts/admin/ranger-admin-site.xml` for all database flavors. The file defaults to PostgreSQL and includes commented MySQL and Oracle blocks; uncomment the matching block and comment the other database flavor settings before starting the stack. Review Comment: Instead of requiring the deployer to update `ranger-admin-site.xml`, consider having multiple ranger-admin-site.xml files, one for each DB flavor, and copy the appropriate one to `conf/ranger-admin-site.xml` during initialization of the container. ########## dev-support/ranger-docker/scripts/admin/user_password_bootstrap.py: ########## @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 + +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. See accompanying LICENSE file. +# +import os +import sys + +from apache_ranger.client.ranger_client import RangerClient +from apache_ranger.client.ranger_user_mgmt_client import RangerUserMgmtClient +from apache_ranger.exceptions import RangerServiceException + +from log_config import configure_logging, get_logger + +logger = get_logger(__name__) + +DEFAULT_BASE_URL = os.environ.get("RANGER_ADMIN_BASE_URL", "http://127.0.0.1:6080").rstrip("/") + + +class UserPasswordBootstrap: + def __init__(self, base_url: str): + self.base_url = base_url.rstrip("/") + + def _get_user_mgmt_client(self, username: str, password: str) -> RangerUserMgmtClient: + return RangerUserMgmtClient(RangerClient(self.base_url, (username, password))) + + def auth_probe_status(self, username: str, password: str) -> int: + try: + user_mgmt = self._get_user_mgmt_client(username, password) + result = user_mgmt.client_http.call_api(RangerUserMgmtClient.FIND_USERS) + return 200 if result is not None else 0 + except RangerServiceException as exc: + if exc.statusCode == 403: + return 403 + if exc.statusCode == 401: + return 401 + logger.debug(f"apache-ranger auth probe failed for {username}: {exc}", exc_info=True) + return exc.statusCode + except Exception as exc: + logger.debug(f"apache-ranger auth probe failed for {username}: {exc}", exc_info=True) + return 0 + + def can_authenticate(self, username: str, password: str) -> bool: + return self.auth_probe_status(username, password) in (200, 403) + + def _update_user_password_with_client(self, admin_password: str, username: str, desired: str) -> None: + user_mgmt = self._get_user_mgmt_client("admin", admin_password) + user = user_mgmt.get_user(username) + if user is None or user.id is None: + raise ValueError(f"Unable to find Ranger user {username}") + + user.password = desired + user_mgmt.update_user_by_id(user.id, user) + + def update_user_password(self, admin_password: str, username: str, desired: str) -> int: + try: + self._update_user_password_with_client(admin_password, username, desired) + return 0 + except RangerServiceException as exc: + logger.error( + f"apache-ranger client update failed for {username}: " + f"status={exc.statusCode}, message={exc.msgDesc or exc}" + ) + return 1 + except Exception as exc: + logger.error(f"apache-ranger client update failed for {username}: {exc}") + return 1 + + def set_admin_password_if_needed(self, desired: str) -> int: + if not desired: + logger.warning("Ranger admin password not configured; skipping admin password update") + return 0 + + if self.can_authenticate("admin", desired): + return 0 + + logger.warning("Unable to authenticate to Ranger as admin with RANGER_ADMIN_PASSWORD.") + logger.warning(f" admin:<env:RANGER_ADMIN_PASSWORD> -> {self.auth_probe_status('admin', desired)}") + logger.warning("For fresh installs, dba.py seeds the initial admin password from RANGER_ADMIN_PASSWORD during schema import. " + "If the database already exists, changing only RANGER_ADMIN_PASSWORD will not rotate the stored admin password." + ) + return 1 + + def update_user_password_if_needed(self, admin_password: str, username: str, desired: str) -> int: + if not desired: + logger.warning(f"No password configured for {username}; skipping password update") + return 0 + + if self.can_authenticate(username, desired): + return 0 + + if not self.can_authenticate("admin", admin_password): + logger.warning(f"Unable to authenticate as admin; skipping password update for {username}.") + logger.warning(f" admin:<provided> -> {self.auth_probe_status('admin', admin_password)}") + return 0 + + logger.info(f"Updating Ranger user password for {username} to configured value") + if self.update_user_password(admin_password, username, desired) != 0: + return 1 + + if not self.can_authenticate(username, desired): + logger.error(f"Password update succeeded but auth check failed for {username}") + return 1 + + return 0 + + def run(self, admin_password: str, usersync_password: str, tagsync_password: str) -> int: + if self.set_admin_password_if_needed(admin_password) != 0: + return 1 + if self.update_user_password_if_needed(admin_password, "rangerusersync", usersync_password) != 0: + return 1 + if self.update_user_password_if_needed(admin_password, "rangertagsync", tagsync_password) != 0: + return 1 + return 0 + + +def main() -> int: + configure_logging() + + bootstrap = UserPasswordBootstrap(DEFAULT_BASE_URL) + return bootstrap.run( Review Comment: Built-in accounts like `admin`, `rangerusersync`, `rangertagsync` are created during database schedma initialization, right? Without `admin` account already in place, this script wouldn't work - due to failure in line 84. What is the purpose of this script? ########## dev-support/ranger-docker/scripts/python/log_config.py: ########## @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. See accompanying LICENSE file. +# + +import logging +import os + + Review Comment: I suggest replacing this file (`log_config.py`) with addition of following line at the begining of `create_ranger_services.py`: ``` logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)-6s %(message)s", handlers=(logging.StreamHandler(), logging.FileHandler('create_ranger_services.log'))) ``` -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
