Commit 82122173 authored by Iustin Pop's avatar Iustin Pop
Browse files

Rework ssh known-hosts handling.

This changes:
  - cluster setup, we no longer edit /etc/ssh/ssh_known_hosts but our
    own file
  - node add, we no longer remove root's known_hosts (twice)
  - gnt-instance console, both the LU and the script: since now the ssh
    setup is not standard, we need to build the ssh cmdline in the LU
    (instead of manually building it in the script) with the correct
    parameters and use the command line as returned in the script
  - ssh.py, many changes, split options in module-level constants so
    that building the command line in different places is easier/more
    logical
  - backend.py, we no longer remove root's known_hosts in Add node, and
    we allow our own known_hosts file to be uploaded

Reviewed-by: imsnah
parent 02715459
......@@ -122,7 +122,6 @@ def AddNode(dsa, dsapub, rsa, rsapub, ssh, sshpub):
utils.RunCmd(["/etc/init.d/ssh", "restart"])
utils.RemoveFile("/root/.ssh/known_hosts")
return True
......@@ -790,7 +789,7 @@ def UploadFile(file_name, data, mode, uid, gid, atime, mtime):
return False
allowed_files = [constants.CLUSTER_CONF_FILE, "/etc/hosts",
"/etc/ssh/ssh_known_hosts"]
constants.SSH_KNOWN_HOSTS_FILE]
allowed_files.extend(ssconf.SimpleStore().GetFileList())
if file_name not in allowed_files:
logger.Error("Filename passed to UploadFile not in allowed"
......
......@@ -350,10 +350,10 @@ def _UpdateKnownHosts(fullnode, ip, pubkey):
pubkey - the public key of the cluster
"""
if os.path.exists('/etc/ssh/ssh_known_hosts'):
f = open('/etc/ssh/ssh_known_hosts', 'r+')
if os.path.exists(constants.SSH_KNOWN_HOSTS_FILE):
f = open(constants.SSH_KNOWN_HOSTS_FILE, 'r+')
else:
f = open('/etc/ssh/ssh_known_hosts', 'w+')
f = open(constants.SSH_KNOWN_HOSTS_FILE, 'w+')
inthere = False
......@@ -405,12 +405,15 @@ def _UpdateKnownHosts(fullnode, ip, pubkey):
save_lines = save_lines + add_lines
# Write a new file and replace old.
fd, tmpname = tempfile.mkstemp('tmp', 'ssh_known_hosts_', '/etc/ssh')
fd, tmpname = tempfile.mkstemp('.tmp', 'known_hosts.',
constants.DATA_DIR)
newfile = os.fdopen(fd, 'w')
newfile.write(''.join(save_lines))
newfile.close()
try:
newfile.write(''.join(save_lines))
finally:
newfile.close()
logger.Debug("Wrote new known_hosts.")
os.rename(tmpname, '/etc/ssh/ssh_known_hosts')
os.rename(tmpname, constants.SSH_KNOWN_HOSTS_FILE)
elif add_lines:
# Simply appending a new line will do the trick.
......@@ -448,8 +451,6 @@ def _InitSSHSetup(node):
node: the name of this host as a fqdn
"""
utils.RemoveFile('/root/.ssh/known_hosts')
if os.path.exists('/root/.ssh/id_dsa'):
utils.CreateBackup('/root/.ssh/id_dsa')
if os.path.exists('/root/.ssh/id_dsa.pub'):
......@@ -1365,8 +1366,6 @@ class LUAddNode(LogicalUnit):
raise errors.OpExecError("PEM must end with newline")
logger.Info("copy cluster pass to %s and starting the node daemon" % node)
# remove first the root's known_hosts file
utils.RemoveFile("/root/.ssh/known_hosts")
# and then connect with ssh to set password and start ganeti-noded
# note that all the below variables are sanitized at this point,
# either by being constants or by the checks above
......@@ -1442,7 +1441,7 @@ class LUAddNode(LogicalUnit):
dist_nodes.remove(myself.name)
logger.Debug("Copying hosts and known_hosts to all nodes")
for fname in ("/etc/hosts", "/etc/ssh/ssh_known_hosts"):
for fname in ("/etc/hosts", constants.SSH_KNOWN_HOSTS_FILE):
result = rpc.call_upload_file(dist_nodes, fname)
for to_node in dist_nodes:
if not result[to_node]:
......@@ -2798,7 +2797,13 @@ class LUConnectConsole(NoHooksLU):
hyper = hypervisor.GetHypervisor()
console_cmd = hyper.GetShellCommandForConsole(instance.name)
return node, console_cmd
# build ssh cmdline
argv = ["ssh", "-q", "-t"]
argv.extend(ssh.KNOWN_HOSTS_OPTS)
argv.extend(ssh.BATCH_MODE_OPTS)
argv.append(node)
argv.append(console_cmd)
return "ssh", argv
class LUAddMDDRBDComponent(LogicalUnit):
......
......@@ -34,6 +34,7 @@ DATA_DIR = "/var/lib/ganeti"
CLUSTER_CONF_FILE = DATA_DIR + "/config.data"
SSL_CERT_FILE = DATA_DIR + "/server.pem"
WATCHER_STATEFILE = DATA_DIR + "/restart_state"
SSH_KNOWN_HOSTS_FILE = DATA_DIR + "/known_hosts"
NODE_INITD_SCRIPT = "/etc/init.d/ganeti"
DEFAULT_NODED_PORT = 1811
......
......@@ -29,6 +29,31 @@ import os
from ganeti import logger
from ganeti import utils
from ganeti import errors
from ganeti import constants
__all__ = ["SSHCall", "CopyFileToNode", "VerifyNodeHostname",
"KNOWN_HOSTS_OPTS", "BATCH_MODE_OPTS", "ASK_KEY_OPTS"]
KNOWN_HOSTS_OPTS = [
"-oGlobalKnownHostsFile=%s" % constants.SSH_KNOWN_HOSTS_FILE,
"-oUserKnownHostsFile=/dev/null",
]
# Note: BATCH_MODE conflicts with ASK_KEY
BATCH_MODE_OPTS = [
"-oEscapeChar=none",
"-oBatchMode=yes",
"-oStrictHostKeyChecking=yes",
]
ASK_KEY_OPTS = [
"-oStrictHostKeyChecking=ask",
"-oEscapeChar=none",
"-oHashKnownHosts=no",
]
def SSHCall(hostname, user, command, batch=True, ask_key=False):
"""Execute a command on a remote node.
......@@ -40,22 +65,23 @@ def SSHCall(hostname, user, command, batch=True, ask_key=False):
hostname: the target host, string
user: user to auth as
command: the command
batch: if true, ssh will run in batch mode with no prompting
ask_key: if true, ssh will run with StrictHostKeyChecking=ask, so that
we can connect to an unknown host (not valid in batch mode)
Returns:
`utils.RunResult` as for `utils.RunCmd()`
"""
argv = ["ssh", "-q", "-oEscapeChar=none"]
argv = ["ssh", "-q"]
argv.extend(KNOWN_HOSTS_OPTS)
if batch:
argv.append("-oBatchMode=yes")
# if we are in batch mode, we can't ask the key
if ask_key:
raise errors.ProgrammerError("SSH call requested conflicting options")
if ask_key:
argv.append("-oStrictHostKeyChecking=ask")
argv.append("-oHashKnownHosts=no")
else:
argv.append("-oStrictHostKeyChecking=yes")
argv.extend(BATCH_MODE_OPTS)
elif ask_key:
argv.extend(ASK_KEY_OPTS)
argv.extend(["%s@%s" % (user, hostname), command])
return utils.RunCmd(argv)
......@@ -79,8 +105,11 @@ def CopyFileToNode(node, filename):
logger.Error("file %s must be an absolute path" % (filename))
return False
command = ["scp", "-q", "-p", "-oStrictHostKeyChecking=yes",
"-oBatchMode=yes", filename, "%s:%s" % (node, filename)]
command = ["scp", "-q", "-p"]
command.extend(KNOWN_HOSTS_OPTS)
command.extend(BATCH_MODE_OPTS)
command.append(filename)
command.append("%s:%s" % (node, filename))
result = utils.RunCmd(command)
......
......@@ -290,14 +290,14 @@ def ConnectToInstanceConsole(opts, args):
instance_name = args[0]
op = opcodes.OpConnectConsole(instance_name=instance_name)
node, console_cmd = SubmitOpCode(op)
cmd, argv = SubmitOpCode(op)
# drop lock and exec so other commands can run while we have console
utils.Unlock("cmd")
try:
os.execv("/usr/bin/ssh", ["ssh", "-qt", node, console_cmd])
os.execvp(cmd, argv)
finally:
sys.stderr.write("Can't run console command %s on node %s" %
(console_cmd, node))
sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
(cmd, " ".join(argv)))
os._exit(1)
......
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