[X2Go-Commits] [x2gobroker] 02/02: Make remote agent's SSH HostKey policy configurable globally, backend-wise and per session profile. Fallback to RejectPolicy by default. (See Debian bug #922314).
git-admin at x2go.org
git-admin at x2go.org
Sun Apr 21 15:44:04 CEST 2019
This is an automated email from the git hooks/post-receive script.
x2go pushed a commit to branch master
in repository x2gobroker.
commit 0b51f522ebd1d8747a4f401f6cf274bfcb9ddccd
Author: Mike Gabriel <mike.gabriel at das-netzwerkteam.de>
Date: Sun Apr 21 15:21:23 2019 +0200
Make remote agent's SSH HostKey policy configurable globally, backend-wise and per session profile. Fallback to RejectPolicy by default. (See Debian bug #922314).
---
etc/x2gobroker.conf | 31 ++++++++++++++++++++++++
x2gobroker/agent.py | 9 ++++++-
x2gobroker/brokers/base_broker.py | 42 ++++++++++++++++++++++++++++++++-
x2gobroker/defaults.py | 1 +
x2gobroker/tests/test_broker_agent.py | 44 +++++++++++++++++++++++++++++++----
5 files changed, 121 insertions(+), 6 deletions(-)
diff --git a/etc/x2gobroker.conf b/etc/x2gobroker.conf
index 44e86a4..d88faa6 100644
--- a/etc/x2gobroker.conf
+++ b/etc/x2gobroker.conf
@@ -242,6 +242,37 @@
# below value is the default.
#default-agent-query-mode=NONE
+# X2Go Broker's Host Key Policy (if agent query mode is 'SSH')
+#
+# If X2Go Broker's agent query mode is SSH, the system needs to handle
+# X2Go Server side's SSH host keys in a secure and verifyable manner.
+#
+# The agent-hostkey-policy is the default policy to be used and can be
+# either AutoAddPolicy, WarningPolicy, or RejectPolicy. The policy names
+# match the corresponding class names in Paramiko SSH.
+#
+# IMPORTANT: As RejectPolicy is the only safe default, please be aware that
+# on fresh X2Go Broker setups, SSH agent queries will always fail, until a
+# properly maintained ~x2gobroker/.ssh/known_hosts file is in place.
+#
+# There are two simple ways to create this known_hosts file:
+#
+# (a) su - x2gobroker -c "ssh <x2goserver>"
+#
+# On the command line, you get prompted to confirm the remote
+# X2Go server's Follow OpenSSH interactive dialog for accepting
+# the remote host's host key.
+#
+# (b) x2gobroker-testagent --add-to-known-hosts --host <x2goserver>
+#
+# This command will populate the known_hosts file with the remote
+# X2Go server's hostkey while trying to hail its X2Go Broker Agent
+# The host key's fingerprint will be shown on stdout, but there will
+# be no interactive confirmation. If unsure about this, use approach
+# (a) given above.
+#
+#default-agent-hostkey-policy=RejectPolicy
+
# Probe SSH port of X2Go Servers (availability check)
#
# Just before offering an X2Go Server address to a broker client, the
diff --git a/x2gobroker/agent.py b/x2gobroker/agent.py
index b267277..3530323 100644
--- a/x2gobroker/agent.py
+++ b/x2gobroker/agent.py
@@ -195,8 +195,15 @@ def _call_remote_broker_agent(username, task, cmdline_args=[], remote_agent=None
if remote_agent is None:
logger_error.error('With the SSH agent-query-mode a remote agent host (hostname, hostaddr, port) has to be specified!')
- elif 'host_key_policy' not in remote_agent:
+ elif 'host_key_policy' not in remote_agent or remote_agent['host_key_policy'] == 'WarningPolicy':
remote_agent['host_key_policy'] = paramiko.WarningPolicy()
+ elif remote_agent['host_key_policy'] == 'RejectPolicy':
+ remote_agent['host_key_policy'] = paramiko.RejectPolicy()
+ elif remote_agent['host_key_policy'] == 'AutoAddPolicy':
+ remote_agent['host_key_policy'] = paramiko.AutoAddPolicy()
+ else:
+ logger_error.error('Invalid SSH HostKey Policy: "{policy}", falling back to "RejectPolicy"!'.format(policy=remote_agent['host_key_policy']))
+ remote_agent['host_key_policy'] = paramiko.RejectPolicy()
remote_hostaddr = None
remote_hostname = None
diff --git a/x2gobroker/brokers/base_broker.py b/x2gobroker/brokers/base_broker.py
index 5b4303e..3de1426 100644
--- a/x2gobroker/brokers/base_broker.py
+++ b/x2gobroker/brokers/base_broker.py
@@ -562,6 +562,44 @@ class X2GoBroker(object):
else:
return _mode
+ def get_agent_hostkey_policy(self, profile_id):
+ """\
+ Get the agent hostkey policy (either of 'RejectPolicy',
+ 'AutoAddPolicy' or 'WarningPolicy') that is configured for this
+ X2Go Session Broker instance.
+
+ The returned policy names match the MissingHostkeyPolicy class
+ names as found in Python Paramiko.
+
+ :returns: agent hostkey policy
+ :rtype: ``str``
+
+ """
+ _default_agent_hostkey_policy = "RejectPolicy"
+ _backend_agent_hostkey_policy = ""
+ _agent_hostkey_policy = ""
+
+ _profile = self.get_profile_broker(profile_id)
+ if _profile and 'broker-agent-hostkey-policy' in _profile and _profile['broker-agent-hostkey-policy']:
+ _agent_hostkey_policy = _profile['broker-agent-hostkey-policy']
+ logger_broker.debug('base_broker.X2GoBroker.get_agent_hostkey_policy(): found broker-agent-hostkey-policy in session profile with ID {id}: {value}. This one has precendence over the default and the backend value.'.format(id=profile_id, value=_agent_hostkey_policy))
+
+ elif self.config.has_value('broker_{backend}'.format(backend=self.backend_name), 'agent-hostkey-policy') and self.config.get_value('broker_{backend}'.format(backend=self.backend_name), 'agent-hostkey-policy'):
+ _backend_agent_hostkey_policy = self.config.get_value('broker_{backend}'.format(backend=self.backend_name), 'agent-hostkey-policy')
+ logger_broker.debug('base_broker.X2GoBroker.get_agent_hostkey_policy(): found agent-hostkey-policy in backend config section »{backend}«: {value}. This one has precendence over the default value.'.format(backend=self.backend_name, value=_agent_hostkey_policy))
+
+ elif self.config.has_value('global', 'default-agent-hostkey-policy') and self.config.get_value('global', 'default-agent-hostkey-policy'):
+ _default_agent_hostkey_policy = self.config.get_value('global', 'default-agent-hostkey-policy')
+ logger_broker.debug('base_broker.X2GoBroker.get_agent_hostkey_policy(): found default-agent-hostkey-policy in global config section: {value}'.format(value=_default_agent_hostkey_policy))
+
+ _policy = _agent_hostkey_policy or _backend_agent_hostkey_policy or _default_agent_hostkey_policy
+
+ if _policy not in ('AutoAddPolicy', 'RejectPolicy', 'WarningPolicy'):
+ logger_broker.warn('base_broker.X2GoBroker.get_agent_hostkey_policy(): given hostkey policy ({policy}) is invalid/unknown, falling back to default hostkey policy ({default_policy}).'.format(policy=_policy, default_policy=_default_agent_hostkey_policy))
+ _policy = _default_agent_hostkey_policy
+
+ return _policy
+
def get_session_autologin(self, profile_id):
"""\
Detect if the given profile is configured to try automatic session
@@ -1096,7 +1134,9 @@ class X2GoBroker(object):
remote_agent = {
'hostname': remote_agent_hostname,
'hostaddr': remote_agent_hostaddr,
- 'port': remote_agent_port, }
+ 'port': remote_agent_port,
+ 'host_key_policy': self.get_agent_hostkey_policy(profile_id),
+ }
try:
if x2gobroker.agent.ping(remote_agent=remote_agent):
diff --git a/x2gobroker/defaults.py b/x2gobroker/defaults.py
index e10ccb0..8989688 100644
--- a/x2gobroker/defaults.py
+++ b/x2gobroker/defaults.py
@@ -243,6 +243,7 @@ X2GOBROKER_CONFIG_DEFAULTS = {
'default-authorized-keys': '%h/.x2go/authorized_keys',
'default-sshproxy-authorized-keys': '%h/.x2go/authorized_keys',
'default-agent-query-mode': 'NONE',
+ 'default-agent-hostkey-policy': 'RejectPolicy',
'default-portscan-x2goservers': True,
'default-use-load-checker': False,
'load-checker-intervals': 300,
diff --git a/x2gobroker/tests/test_broker_agent.py b/x2gobroker/tests/test_broker_agent.py
index 8d9e489..a7e4ee8 100644
--- a/x2gobroker/tests/test_broker_agent.py
+++ b/x2gobroker/tests/test_broker_agent.py
@@ -110,23 +110,39 @@ host = host1.mydomain, host2.yourdomain
name = testprofile5
host = host1.mydomain (10.0.2.4), host2.mydomain (10.0.2.5)
broker-agent-query-mode = SSH
+broker-agent-hostkey-policy = WarningPolicy
[testprofile6]
name = testprofile6
host = host1.mydomain (10.0.2.4), host2.mydomain (10.0.2.5)
sshport = 23467
broker-agent-query-mode = SSH
+broker-agent-hostkey-policy = WarningPolicy
[testprofile7]
name = testprofile7
host = docker-vm-1 (docker-server:22001), docker-vm-2 (docker-server:22002)
broker-agent-query-mode = SSH
+broker-agent-hostkey-policy = WarningPolicy
[testprofile8]
name = testprofile8
host = docker-vm-0 (docker-server), docker-vm-1 (docker-server:22001), docker-vm-2 (docker-server:22002)
sshport = 22000
broker-agent-query-mode = SSH
+broker-agent-hostkey-policy = WarningPolicy
+
+[testprofile9]
+name = testprofile9
+host = host1.mydomain (10.0.2.4)
+broker-agent-query-mode = SSH
+broker-agent-hostkey-policy = AutoAddPolicy
+
+[testprofile10]
+name = testprofile10
+host = host1.mydomain (10.0.2.4)
+broker-agent-query-mode = SSH
+broker-agent-hostkey-policy = SomeUnkownPolicy
"""
tf = tempfile.NamedTemporaryFile(mode='w')
@@ -207,7 +223,7 @@ broker-agent-query-mode = SSH
i = 0
while i < 10:
_remoteagent5 = inifile_backend.get_remote_agent('testprofile5')
- self.assertTrue( _remoteagent5 == {'hostname': 'host1.mydomain', 'hostaddr': '10.0.2.4', 'port': 22, 'load_factors': {}, } or _remoteagent5 == {'hostname': 'host2.mydomain', 'hostaddr': '10.0.2.5', 'port': 22, 'load_factors': {}, } )
+ self.assertTrue( _remoteagent5 == {'hostname': 'host1.mydomain', 'hostaddr': '10.0.2.4', 'port': 22, 'load_factors': {}, 'host_key_policy': 'WarningPolicy'} or _remoteagent5 == {'hostname': 'host2.mydomain', 'hostaddr': '10.0.2.5', 'port': 22, 'load_factors': {}, 'host_key_policy': 'WarningPolicy', } )
_session5 = inifile_backend.select_session('testprofile5', 'foo5N')
self.assertTrue( _session5 == {'port': 22, 'server': '10.0.2.4', } or _session5 == {'port': 22, 'server': '10.0.2.5', } )
i += 1
@@ -221,7 +237,7 @@ broker-agent-query-mode = SSH
self.assertTrue( _profile6['host'][0] in ('host1.mydomain', 'host2.mydomain') )
self.assertTrue( 'status' not in _profile6 )
_remoteagent6 = inifile_backend.get_remote_agent('testprofile6')
- self.assertTrue( _remoteagent6 == {'hostname': 'host1.mydomain', 'hostaddr': '10.0.2.4', 'port': 23467, 'load_factors': {}, } or _remoteagent6 == {'hostname': 'host2.mydomain', 'hostaddr': '10.0.2.5', 'port': 23467, 'load_factors': {}, } )
+ self.assertTrue( _remoteagent6 == {'hostname': 'host1.mydomain', 'hostaddr': '10.0.2.4', 'port': 23467, 'load_factors': {}, 'host_key_policy': 'WarningPolicy', } or _remoteagent6 == {'hostname': 'host2.mydomain', 'hostaddr': '10.0.2.5', 'port': 23467, 'load_factors': {}, 'host_key_policy': 'WarningPolicy', } )
_session6 = inifile_backend.select_session('testprofile6', 'foo6N')
self.assertTrue( _session6 == {'port': 23467, 'server': '10.0.2.4', } or _session6 == {'port': 23467, 'server': '10.0.2.5', } )
@@ -233,7 +249,7 @@ broker-agent-query-mode = SSH
i = 0
while i < 10:
_remoteagent7 = inifile_backend.get_remote_agent('testprofile7')
- self.assertTrue( _remoteagent7 == {'hostname': 'docker-vm-1', 'hostaddr': 'docker-server', 'port': 22001, 'load_factors': {}, } or _remoteagent7 == {'hostname': 'docker-vm-2', 'hostaddr': 'docker-server', 'port': 22002, 'load_factors': {}, } )
+ self.assertTrue( _remoteagent7 == {'hostname': 'docker-vm-1', 'hostaddr': 'docker-server', 'port': 22001, 'load_factors': {}, 'host_key_policy': 'WarningPolicy', } or _remoteagent7 == {'hostname': 'docker-vm-2', 'hostaddr': 'docker-server', 'port': 22002, 'load_factors': {}, 'host_key_policy': 'WarningPolicy', } )
_session7 = inifile_backend.select_session('testprofile7', 'foo7N')
self.assertTrue( _session7 == {'port': 22001, 'server': 'docker-server', } or _session7 == {'port': 22001, 'server': 'docker-server', } )
i += 1
@@ -246,11 +262,31 @@ broker-agent-query-mode = SSH
i = 0
while i < 10:
_remoteagent8 = inifile_backend.get_remote_agent('testprofile8')
- self.assertTrue( _remoteagent8 == {'hostname': 'docker-vm-0', 'hostaddr': 'docker-server', 'port': 22000, 'load_factors': {}, } or _remoteagent8 == {'hostname': 'docker-vm-1', 'hostaddr': 'docker-server', 'port': 22001, 'load_factors': {}, } or _remoteagent8 == {'hostname': 'docker-vm-2', 'hostaddr': 'docker-server', 'port': 22002, 'load_factors': {}, } )
+ self.assertTrue( _remoteagent8 == {'hostname': 'docker-vm-0', 'hostaddr': 'docker-server', 'port': 22000, 'load_factors': {}, 'host_key_policy': 'WarningPolicy', } or _remoteagent8 == {'hostname': 'docker-vm-1', 'hostaddr': 'docker-server', 'port': 22001, 'load_factors': {}, 'host_key_policy': 'WarningPolicy', } or _remoteagent8 == {'hostname': 'docker-vm-2', 'hostaddr': 'docker-server', 'port': 22002, 'load_factors': {}, 'host_key_policy': 'WarningPolicy', } )
_session8 = inifile_backend.select_session('testprofile8', 'foo8N')
self.assertTrue( _session8 == {'port': 22000, 'server': 'docker-server', } or _session8 == {'port': 22001, 'server': 'docker-server', } or _session8 == {'port': 22001, 'server': 'docker-server', } )
i += 1
+ # test "testprofile9", test if hostkey policy is propagated from session profile config to remote agent settings
+
+ _list9 = inifile_backend.list_profiles(username='foo9N')
+ _profile9 = _list9['testprofile9']
+ _profile9['host'].sort()
+ self.assertTrue( _profile9['host'][0] in ('host1.mydomain') )
+ self.assertTrue( 'status' not in _profile9 )
+ _remoteagent9 = inifile_backend.get_remote_agent('testprofile9')
+ self.assertTrue( _remoteagent9 == {'hostname': 'host1.mydomain', 'hostaddr': '10.0.2.4', 'port': 22, 'load_factors': {}, 'host_key_policy': 'AutoAddPolicy'})
+
+ # test "testprofile10", test if an invalid hostkey policy is propagated from session profile config to remote agent settings and ignored with RejectPolicy as fallback
+
+ _list10 = inifile_backend.list_profiles(username='foo10N')
+ _profile10 = _list10['testprofile10']
+ _profile10['host'].sort()
+ self.assertTrue( _profile10['host'][0] in ('host1.mydomain') )
+ self.assertTrue( 'status' not in _profile10 )
+ _remoteagent10 = inifile_backend.get_remote_agent('testprofile10')
+ self.assertTrue( _remoteagent10 == {'hostname': 'host1.mydomain', 'hostaddr': '10.0.2.4', 'port': 22, 'load_factors': {}, 'host_key_policy': 'RejectPolicy'})
+
x2gobroker.agent._call_local_broker_agent = _save_local_broker_agent_call
x2gobroker.agent._call_remote_broker_agent = _save_remote_broker_agent_call
x2gobroker.utils.portscan = _save_portscan
--
Alioth's /home/x2go-admin/maintenancescripts/git/hooks/post-receive-email on /srv/git/code.x2go.org/x2gobroker.git
More information about the x2go-commits
mailing list