Commit fe55b022 authored by Jérome Perrin's avatar Jérome Perrin

seleniumserver: publish ssh fingerprint

so that on first connection clients can check the fingerprint key
instead of blindly accepting it.
parent 381f49e3
......@@ -19,4 +19,4 @@ md5sum = c4ac5de141ae6a64848309af03e51d88
[template-selenium]
filename = instance-selenium.cfg.in
md5sum = 4167621b473f81892d38389ac427c6ba
md5sum = 8e6b9629c1489559ec88a67d7b1bdf41
......@@ -176,6 +176,19 @@ extra-args=-t dsa
<=ssh-keygen-base
extra-args=-t ecdsa -b 521
[ssh-key-fingerprint-command]
recipe = plone.recipe.command
# recent openssh client display ECDSA key's fingerprint as SHA256
command = ${openssh-output:keygen} -lf $${ssh-host-ecdsa-key:output}
[ssh-key-fingerprint]
recipe = collective.recipe.shelloutput
# XXX because collective.recipe.shelloutput ignore errors, we run the same
# command in a plone.recipe.command so that if fails if something goes wrong.
commands =
fingerprint = $${ssh-key-fingerprint-command:command}
[sshd-config]
recipe = slapos.recipe.template:jinja2
rendered = $${directory:etc}/sshd.conf
......@@ -268,6 +281,7 @@ url = $${selenium-server-frontend-instance:url}
admin-url = $${selenium-server-frontend-instance:admin-url}
ssh-url = $${sshd-service:url}
ssh-fingerprint = $${ssh-key-fingerprint:fingerprint}
# to run a local node - useful to see what tests are doing or
# using to use unsupported browsers like safari or edge or to test
# on mobile using appium.
......
......@@ -16,8 +16,12 @@ extends =
./buildout.hash.cfg
parts =
slapos-cookbook
template
slapos-cookbook
collective.recipe.shelloutput
template
[collective.recipe.shelloutput]
recipe = zc.recipe.egg
[selenium-server]
recipe = slapos.recipe.build:download
......@@ -41,4 +45,5 @@ output = ${buildout:directory}/template-selenium.cfg
[versions]
plone.recipe.command = 1.1
collective.recipe.shelloutput = 0.1
slapos.recipe.template = 4.3
......@@ -32,6 +32,9 @@ import os
import tempfile
import unittest
import urlparse
import base64
import hashlib
import contextlib
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from io import BytesIO
......@@ -343,20 +346,46 @@ class TestSSHServer(SeleniumServerTestCase):
self.assertEqual('ssh', parsed.scheme)
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.client.WarningPolicy)
client.connect(
username=urlparse.urlparse(ssh_url).username,
hostname=urlparse.urlparse(ssh_url).hostname,
port=urlparse.urlparse(ssh_url).port,
pkey=self.ssh_key,
)
channel = client.invoke_shell()
channel.settimeout(30)
# apparently we sometimes need to send something on the first ssh connection
channel.send('\n')
# openssh prints a warning 'Attempt to write login records by non-root user (aborting)'
# so we received more than the lenght of the asserted message.
self.assertIn("Welcome to SlapOS Selenium Server.", channel.recv(100))
class TestKeyPolicy(object):
"""Accept server key and keep it in self.key for inspection
"""
def missing_host_key(self, client, hostname, key):
self.key = key
key_policy = TestKeyPolicy()
client.set_missing_host_key_policy(key_policy)
with contextlib.closing(client):
client.connect(
username=urlparse.urlparse(ssh_url).username,
hostname=urlparse.urlparse(ssh_url).hostname,
port=urlparse.urlparse(ssh_url).port,
pkey=self.ssh_key,
)
# Check fingerprint from server matches the published one.
# The publish format is the raw output of ssh-keygen and is something like this:
# 521 SHA256:9aZruv3LmFizzueIFdkd78eGtzghDoPSCBXFkkrHqXE user@hostname (ECDSA)
# we only want to parse SHA256:9aZruv3LmFizzueIFdkd78eGtzghDoPSCBXFkkrHqXE
_, fingerprint_string, _, key_type = parameter_dict['ssh-fingerprint'].split()
self.assertEqual(key_type, '(ECDSA)')
fingerprint_algorithm, fingerprint = fingerprint_string.split(':', 1)
self.assertEqual(fingerprint_algorithm, 'SHA256')
# Paramiko does not allow to get the fingerprint as SHA256 easily yet
# https://github.com/paramiko/paramiko/pull/1103
self.assertEqual(
fingerprint,
# XXX with sha256, we need to remove that trailing =
base64.b64encode(hashlib.new(fingerprint_algorithm, key_policy.key.asbytes()).digest())[:-1]
)
channel = client.invoke_shell()
channel.settimeout(30)
# apparently we sometimes need to send something on the first ssh connection
channel.send('\n')
# openssh prints a warning 'Attempt to write login records by non-root user (aborting)'
# so we received more than the lenght of the asserted message.
self.assertIn("Welcome to SlapOS Selenium Server.", channel.recv(100))
class TestFirefox52(BrowserCompatibilityMixin, SeleniumServerTestCase):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment