Merger: automatically add new hosts to the known_hosts file

To improve the experience for new users, have the mergers automatically
add new ssh hosts to their known hosts files.  They will still validate
existing entries, so a paranoid sysadmin can still pre-populate the file
to avoid trojan attacks on the initial connection.  New users in the
typical case of a secure internal network will not need to manually add
host keys to start Zuul.

This matches the behavior of the scheduler with its stream-events
connection to Gerrit.

Change-Id: Ib416bbb2ffcfedd5d114709af3931e154a1a92ac
This commit is contained in:
James E. Blair 2018-10-23 11:57:09 -07:00
parent 4c5caf78a2
commit 56ec35538a
4 changed files with 41 additions and 21 deletions

View File

@ -42,7 +42,7 @@ services:
- mysql
environment:
no_proxy: "${no_proxy},gerrit"
command: "sh -c 'ansible-playbook /var/playbooks/scheduler.yaml; zuul-scheduler -d'"
command: "sh -c 'ansible-playbook /var/playbooks/wait-to-start.yaml; zuul-scheduler -d'"
image: zuul/zuul-scheduler
volumes:
- "./etc_zuul/:/etc/zuul/:z"
@ -63,7 +63,7 @@ services:
privileged: true
environment:
no_proxy: "${no_proxy},gerrit"
command: "sh -c 'ansible-playbook /var/playbooks/executor.yaml; zuul-executor -d'"
command: "zuul-executor -d"
depends_on:
- scheduler
image: zuul/zuul-executor

View File

@ -1,16 +0,0 @@
# Until https://review.openstack.org/608453 merges, we need to add
# Gerrit's SSH host keys to the known hosts_file before starting.
- hosts: localhost
gather_facts: false
tasks:
- name: Wait for gerrit to start
wait_for:
host: gerrit
port: 29418
- name: Ensure SSH directory exists
file:
state: directory
path: /root/.ssh
- name: Fetch SSH host keys from Gerrit
shell: ssh-keyscan -p 29418 gerrit > /root/.ssh/known_hosts

View File

@ -1,5 +1,5 @@
# The Zuul scheduler needs to be able to connect to the remote systems
# in order to start.
# Zuul needs to be able to connect to the remote systems in order to
# start.
- hosts: localhost
gather_facts: false

View File

@ -14,7 +14,7 @@
# under the License.
from contextlib import contextmanager
from urllib.parse import urlsplit, urlunsplit
from urllib.parse import urlsplit, urlunsplit, urlparse
import logging
import os
import re
@ -23,6 +23,7 @@ import time
import git
import gitdb
import paramiko
import zuul.model
@ -82,6 +83,7 @@ class Repo(object):
'GIT_HTTP_LOW_SPEED_TIME': speed_time,
}
self.git_timeout = git_timeout
self.sshkey = sshkey
if sshkey:
self.env['GIT_SSH_COMMAND'] = 'ssh -i %s' % (sshkey,)
@ -93,6 +95,10 @@ class Repo(object):
self._initialized = False
self.retry_attempts = retry_attempts
self.retry_interval = retry_interval
try:
self._setup_known_hosts()
except Exception:
self.log.exception("Unable to set up known_hosts for %s" % remote)
try:
self._ensure_cloned()
self._git_set_remote_url(
@ -100,6 +106,36 @@ class Repo(object):
except Exception:
self.log.exception("Unable to initialize repo for %s" % remote)
def _setup_known_hosts(self):
url = urlparse(self.remote_url)
if 'ssh' not in url.scheme:
return
port = url.port or 22
username = url.username or self.username
path = os.path.expanduser('~/.ssh')
os.makedirs(path)
path = os.path.expanduser('~/.ssh/known_hosts')
if not os.path.exists(path):
with open(path, 'w'):
pass
client = paramiko.SSHClient()
client.load_system_host_keys()
client.load_host_keys(path)
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
client.connect(url.hostname,
username=username,
port=port,
key_filename=self.sshkey)
finally:
# If we don't close on exceptions to connect we can leak the
# connection and DoS Gerrit.
client.close()
def _ensure_cloned(self):
repo_is_cloned = os.path.exists(os.path.join(self.local_path, '.git'))
if self._initialized and repo_is_cloned: