##// END OF EJS Templates
ssh: generate tmp rename file in tmp dir rather then location of file....
super-admin -
r4838:b8bab954 default
parent child Browse files
Show More
@@ -1,132 +1,133 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 import stat
22 import stat
23 import logging
23 import logging
24 import tempfile
24 import tempfile
25 import datetime
25 import datetime
26
26
27 from . import config_keys
27 from . import config_keys
28 from rhodecode.model.db import true, joinedload, User, UserSshKeys
28 from rhodecode.model.db import true, joinedload, User, UserSshKeys
29
29
30
30
31 log = logging.getLogger(__name__)
31 log = logging.getLogger(__name__)
32
32
33 HEADER = \
33 HEADER = \
34 "# This file is managed by RhodeCode, please do not edit it manually. # \n" \
34 "# This file is managed by RhodeCode, please do not edit it manually. # \n" \
35 "# Current entries: {}, create date: UTC:{}.\n"
35 "# Current entries: {}, create date: UTC:{}.\n"
36
36
37 # Default SSH options for authorized_keys file, can be override via .ini
37 # Default SSH options for authorized_keys file, can be override via .ini
38 SSH_OPTS = 'no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding'
38 SSH_OPTS = 'no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding'
39
39
40
40
41 def get_all_active_keys():
41 def get_all_active_keys():
42 result = UserSshKeys.query() \
42 result = UserSshKeys.query() \
43 .options(joinedload(UserSshKeys.user)) \
43 .options(joinedload(UserSshKeys.user)) \
44 .filter(UserSshKeys.user != User.get_default_user()) \
44 .filter(UserSshKeys.user != User.get_default_user()) \
45 .filter(User.active == true()) \
45 .filter(User.active == true()) \
46 .all()
46 .all()
47 return result
47 return result
48
48
49
49
50 def _generate_ssh_authorized_keys_file(
50 def _generate_ssh_authorized_keys_file(
51 authorized_keys_file_path, ssh_wrapper_cmd, allow_shell, ssh_opts, debug):
51 authorized_keys_file_path, ssh_wrapper_cmd, allow_shell, ssh_opts, debug):
52 import rhodecode
52
53
53 authorized_keys_file_path = os.path.abspath(
54 authorized_keys_file_path = os.path.abspath(
54 os.path.expanduser(authorized_keys_file_path))
55 os.path.expanduser(authorized_keys_file_path))
56 tmp_file_dir = tempfile.gettempdir()
55
57
56 import rhodecode
57 all_active_keys = get_all_active_keys()
58 all_active_keys = get_all_active_keys()
58
59
59 if allow_shell:
60 if allow_shell:
60 ssh_wrapper_cmd = ssh_wrapper_cmd + ' --shell'
61 ssh_wrapper_cmd = ssh_wrapper_cmd + ' --shell'
61 if debug:
62 if debug:
62 ssh_wrapper_cmd = ssh_wrapper_cmd + ' --debug'
63 ssh_wrapper_cmd = ssh_wrapper_cmd + ' --debug'
63
64
64 if not os.path.isfile(authorized_keys_file_path):
65 if not os.path.isfile(authorized_keys_file_path):
65 log.debug('Creating file at %s', authorized_keys_file_path)
66 log.debug('Creating file at %s', authorized_keys_file_path)
66 with open(authorized_keys_file_path, 'w'):
67 with open(authorized_keys_file_path, 'w'):
67 pass
68 pass
68
69
69 if not os.access(authorized_keys_file_path, os.R_OK):
70 if not os.access(authorized_keys_file_path, os.R_OK):
70 raise OSError('Access to file {} is without read access'.format(
71 raise OSError('Access to file {} is without read access'.format(
71 authorized_keys_file_path))
72 authorized_keys_file_path))
72
73
73 line_tmpl = '{ssh_opts},command="{wrapper_command} {ini_path} --user-id={user_id} --user={user} --key-id={user_key_id}" {key}\n'
74 line_tmpl = '{ssh_opts},command="{wrapper_command} {ini_path} --user-id={user_id} --user={user} --key-id={user_key_id}" {key}\n'
74
75
75 fd, tmp_authorized_keys = tempfile.mkstemp(
76 fd, tmp_authorized_keys = tempfile.mkstemp(
76 '.authorized_keys_write',
77 '.authorized_keys_write_operation',
77 dir=os.path.dirname(authorized_keys_file_path))
78 dir=tmp_file_dir)
78
79
79 now = datetime.datetime.utcnow().isoformat()
80 now = datetime.datetime.utcnow().isoformat()
80 keys_file = os.fdopen(fd, 'wb')
81 keys_file = os.fdopen(fd, 'wb')
81 keys_file.write(HEADER.format(len(all_active_keys), now))
82 keys_file.write(HEADER.format(len(all_active_keys), now))
82 ini_path = rhodecode.CONFIG['__file__']
83 ini_path = rhodecode.CONFIG['__file__']
83
84
84 for user_key in all_active_keys:
85 for user_key in all_active_keys:
85 username = user_key.user.username
86 username = user_key.user.username
86 user_id = user_key.user.user_id
87 user_id = user_key.user.user_id
87 # replace all newline from ends and inside
88 # replace all newline from ends and inside
88 safe_key_data = user_key.ssh_key_data\
89 safe_key_data = user_key.ssh_key_data\
89 .strip()\
90 .strip()\
90 .replace('\n', ' ') \
91 .replace('\n', ' ') \
91 .replace('\t', ' ') \
92 .replace('\t', ' ') \
92 .replace('\r', ' ')
93 .replace('\r', ' ')
93
94
94 line = line_tmpl.format(
95 line = line_tmpl.format(
95 ssh_opts=ssh_opts or SSH_OPTS,
96 ssh_opts=ssh_opts or SSH_OPTS,
96 wrapper_command=ssh_wrapper_cmd,
97 wrapper_command=ssh_wrapper_cmd,
97 ini_path=ini_path,
98 ini_path=ini_path,
98 user_id=user_id,
99 user_id=user_id,
99 user=username,
100 user=username,
100 user_key_id=user_key.ssh_key_id,
101 user_key_id=user_key.ssh_key_id,
101 key=safe_key_data)
102 key=safe_key_data)
102
103
103 keys_file.write(line)
104 keys_file.write(line)
104 log.debug('addkey: Key added for user: `%s`', username)
105 log.debug('addkey: Key added for user: `%s`', username)
105 keys_file.close()
106 keys_file.close()
106
107
107 # Explicitly setting read-only permissions to authorized_keys
108 # Explicitly setting read-only permissions to authorized_keys
108 os.chmod(tmp_authorized_keys, stat.S_IRUSR | stat.S_IWUSR)
109 os.chmod(tmp_authorized_keys, stat.S_IRUSR | stat.S_IWUSR)
109 # Rename is atomic operation
110 # Rename is atomic operation
110 os.rename(tmp_authorized_keys, authorized_keys_file_path)
111 os.rename(tmp_authorized_keys, authorized_keys_file_path)
111
112
112
113
113 def generate_ssh_authorized_keys_file(registry):
114 def generate_ssh_authorized_keys_file(registry):
114 log.info('Generating new authorized key file')
115 log.info('Generating new authorized key file')
115
116
116 authorized_keys_file_path = registry.settings.get(
117 authorized_keys_file_path = registry.settings.get(
117 config_keys.authorized_keys_file_path)
118 config_keys.authorized_keys_file_path)
118
119
119 ssh_wrapper_cmd = registry.settings.get(
120 ssh_wrapper_cmd = registry.settings.get(
120 config_keys.wrapper_cmd)
121 config_keys.wrapper_cmd)
121 allow_shell = registry.settings.get(
122 allow_shell = registry.settings.get(
122 config_keys.wrapper_allow_shell)
123 config_keys.wrapper_allow_shell)
123 ssh_opts = registry.settings.get(
124 ssh_opts = registry.settings.get(
124 config_keys.authorized_keys_line_ssh_opts)
125 config_keys.authorized_keys_line_ssh_opts)
125 debug = registry.settings.get(
126 debug = registry.settings.get(
126 config_keys.enable_debug_logging)
127 config_keys.enable_debug_logging)
127
128
128 _generate_ssh_authorized_keys_file(
129 _generate_ssh_authorized_keys_file(
129 authorized_keys_file_path, ssh_wrapper_cmd, allow_shell, ssh_opts,
130 authorized_keys_file_path, ssh_wrapper_cmd, allow_shell, ssh_opts,
130 debug)
131 debug)
131
132
132 return 0
133 return 0
General Comments 0
You need to be logged in to leave comments. Login now