diff --git a/lib/backend.py b/lib/backend.py index 7b362d59aa8f2f66f06a05a0291b5f99035f5f87..fea9da9755b9ca6eb029175ce23dc70fda7873b3 100644 --- a/lib/backend.py +++ b/lib/backend.py @@ -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" diff --git a/lib/cmdlib.py b/lib/cmdlib.py index 04122f4d136669219ffc32d57bd984be2943728c..26057f38dd41ed787805b8bc6528ff13cc0e22c6 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -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): diff --git a/lib/constants.py b/lib/constants.py index 09dd712d71c471879a5cbe459db4a47de2f9408e..856f39c664bec3ba55efe239e6e6ce8f7a68376f 100644 --- a/lib/constants.py +++ b/lib/constants.py @@ -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 diff --git a/lib/ssh.py b/lib/ssh.py index bd084870de8da456f01e4a8c3805e4c5a9dfd24a..7cf772a98f8e7b58cf381dd6117763b5c1c95cf5 100644 --- a/lib/ssh.py +++ b/lib/ssh.py @@ -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) diff --git a/scripts/gnt-instance b/scripts/gnt-instance index d0dc22c55d25b195a9ca7f220396b4de90681fa2..a4f173ab5e57385a6239a5d65d61314a6e1569a4 100755 --- a/scripts/gnt-instance +++ b/scripts/gnt-instance @@ -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)