utils.py
138 lines
| 4.6 KiB
| text/x-python
|
PythonLexer
r5053 | ||||
r1994 | ||||
r5088 | # Copyright (C) 2016-2023 RhodeCode GmbH | |||
r1994 | # | |||
# This program is free software: you can redistribute it and/or modify | ||||
# it under the terms of the GNU Affero General Public License, version 3 | ||||
# (only), as published by the Free Software Foundation. | ||||
# | ||||
# This program is distributed in the hope that it will be useful, | ||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
# GNU General Public License for more details. | ||||
# | ||||
# You should have received a copy of the GNU Affero General Public License | ||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
# | ||||
# This program is dual-licensed. If you wish to learn more about the | ||||
# RhodeCode Enterprise Edition, including its added features, Support services, | ||||
# and proprietary license terms, please see https://rhodecode.com/licenses/ | ||||
import os | ||||
import stat | ||||
import logging | ||||
import tempfile | ||||
import datetime | ||||
from . import config_keys | ||||
from rhodecode.model.db import true, joinedload, User, UserSshKeys | ||||
log = logging.getLogger(__name__) | ||||
HEADER = \ | ||||
"# This file is managed by RhodeCode, please do not edit it manually. # \n" \ | ||||
"# Current entries: {}, create date: UTC:{}.\n" | ||||
# Default SSH options for authorized_keys file, can be override via .ini | ||||
SSH_OPTS = 'no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding' | ||||
def get_all_active_keys(): | ||||
result = UserSshKeys.query() \ | ||||
r5072 | .join(User) \ | |||
.filter(User != User.get_default_user()) \ | ||||
r1994 | .filter(User.active == true()) \ | |||
.all() | ||||
return result | ||||
def _generate_ssh_authorized_keys_file( | ||||
r2043 | authorized_keys_file_path, ssh_wrapper_cmd, allow_shell, ssh_opts, debug): | |||
r4838 | import rhodecode | |||
r2043 | ||||
r2211 | authorized_keys_file_path = os.path.abspath( | |||
os.path.expanduser(authorized_keys_file_path)) | ||||
r4840 | tmp_file_dir = os.path.dirname(authorized_keys_file_path) | |||
r2211 | ||||
r5072 | if not os.path.exists(tmp_file_dir): | |||
log.debug('SSH authorized_keys file dir does not exist, creating one now...') | ||||
os.makedirs(tmp_file_dir) | ||||
r1994 | all_active_keys = get_all_active_keys() | |||
if allow_shell: | ||||
ssh_wrapper_cmd = ssh_wrapper_cmd + ' --shell' | ||||
r2043 | if debug: | |||
ssh_wrapper_cmd = ssh_wrapper_cmd + ' --debug' | ||||
r1994 | ||||
if not os.path.isfile(authorized_keys_file_path): | ||||
r2211 | log.debug('Creating file at %s', authorized_keys_file_path) | |||
r1994 | with open(authorized_keys_file_path, 'w'): | |||
r5072 | # create a file with write access | |||
r1994 | pass | |||
if not os.access(authorized_keys_file_path, os.R_OK): | ||||
raise OSError('Access to file {} is without read access'.format( | ||||
authorized_keys_file_path)) | ||||
r2186 | line_tmpl = '{ssh_opts},command="{wrapper_command} {ini_path} --user-id={user_id} --user={user} --key-id={user_key_id}" {key}\n' | |||
r1994 | ||||
fd, tmp_authorized_keys = tempfile.mkstemp( | ||||
r4838 | '.authorized_keys_write_operation', | |||
dir=tmp_file_dir) | ||||
r1994 | ||||
now = datetime.datetime.utcnow().isoformat() | ||||
r5072 | keys_file = os.fdopen(fd, 'wt') | |||
r1994 | keys_file.write(HEADER.format(len(all_active_keys), now)) | |||
r2043 | ini_path = rhodecode.CONFIG['__file__'] | |||
r1994 | ||||
for user_key in all_active_keys: | ||||
username = user_key.user.username | ||||
r2043 | user_id = user_key.user.user_id | |||
r2748 | # replace all newline from ends and inside | |||
safe_key_data = user_key.ssh_key_data\ | ||||
.strip()\ | ||||
r2751 | .replace('\n', ' ') \ | |||
.replace('\t', ' ') \ | ||||
r2748 | .replace('\r', ' ') | |||
r2043 | ||||
r2748 | line = line_tmpl.format( | |||
ssh_opts=ssh_opts or SSH_OPTS, | ||||
wrapper_command=ssh_wrapper_cmd, | ||||
ini_path=ini_path, | ||||
user_id=user_id, | ||||
user=username, | ||||
user_key_id=user_key.ssh_key_id, | ||||
key=safe_key_data) | ||||
keys_file.write(line) | ||||
r1994 | log.debug('addkey: Key added for user: `%s`', username) | |||
keys_file.close() | ||||
# Explicitly setting read-only permissions to authorized_keys | ||||
os.chmod(tmp_authorized_keys, stat.S_IRUSR | stat.S_IWUSR) | ||||
# Rename is atomic operation | ||||
os.rename(tmp_authorized_keys, authorized_keys_file_path) | ||||
def generate_ssh_authorized_keys_file(registry): | ||||
log.info('Generating new authorized key file') | ||||
authorized_keys_file_path = registry.settings.get( | ||||
config_keys.authorized_keys_file_path) | ||||
ssh_wrapper_cmd = registry.settings.get( | ||||
config_keys.wrapper_cmd) | ||||
allow_shell = registry.settings.get( | ||||
config_keys.wrapper_allow_shell) | ||||
ssh_opts = registry.settings.get( | ||||
config_keys.authorized_keys_line_ssh_opts) | ||||
r2043 | debug = registry.settings.get( | |||
config_keys.enable_debug_logging) | ||||
r1994 | ||||
_generate_ssh_authorized_keys_file( | ||||
r2043 | authorized_keys_file_path, ssh_wrapper_cmd, allow_shell, ssh_opts, | |||
debug) | ||||
r1994 | ||||
return 0 | ||||