你可以在 .ssh/config 中设置密码以实现自动登录吗?

我正在使用Ubuntu 11.10和ssh每天连接到许多服务器,所以我将它们的参数放在.ssh/config文件中,像这样:

Host Home
User netmoon
Port 22
HostName test.com

有没有办法在这个文件中为每个连接设置密码,这样当服务器要求输入密码时,终端会自动输入密码并发送给服务器?
我需要这个功能是因为有时候我离开电脑一段时间,当我回来后,输入密码并按下Enter键时,终端会显示“连接已关闭”。
附言:我不想使用公钥/私钥对。

1我和你处于同样的情况,因为我只能通过svn访问ssh,所以无法上传我的公钥。如果我尝试使用ssh svnhost命令,我会得到"( success ( 2 2 ( ) ( edit-pipeline svndiff1 absent-entries commit-revprops depth log-revprops partial-replay ) ) )"的svnserve响应,而不是shell。 - Uberto
请参考以下链接:http://unix.stackexchange.com/a/83991/26493 和 http://andre.frimberger.de/index.php/linux/reading-ssh-password-from-stdin-the-openssh-5-6p1-compatible-way/,这些链接提供了一种利用`SSH_ASKPASS`的方法。 - Olaf Dietsche
15个回答

以方便为代价来牺牲安全性从来都不是一个好的选择...
你可以使用openssh-client软件包中的ssh-copy-id命令吗?
根据man ssh-copy-id的说明:
ssh-copy-id是一个脚本,它使用ssh登录到远程机器,并将指定的身份文件追加到该机器的~/.ssh/authorized_keys文件中。

15如果远程管理员坚持禁用公钥授权,这个方法就不起作用了。 - tomasz
3是的,但在您完全直接监督和控制的系统上保持现实是不会妥协的。比如说,在一个仅用于开发目的、没有外部连接的虚拟机上。 - Scott
47坚持毫无理由地实施严苛的安全措施从来都不会有好结果。 - cwallenpoole
9有时候,事情会有好的结局。 - devth
4在我看来,坚持使用密码进行身份验证比不使用密码更加危险。我经常将经常使用的ssh密码设置为环境变量,因为我不愿意记住一组随机字符串。要求用户输入这些密码只是在要求他们被妥善存储。 - eggmatters
2我很确定我读过“我不想使用公钥/私钥对”,但是找不到具体在哪里...所以:"我不想使用公钥/私钥对" => 好的,为什么你不使用公钥/私钥对呢? - Arcesilas
1我真的很好奇,将密码保存在ssh_config文件中与在您的系统上存储私钥有什么不同?使用密码会导致客户端和服务器之间的加密变得更弱吗? - Andre Helberg
这不是一个与安全相关的问题。所提出的问题涉及自动化,而不是安全。 - mcantsin
1@AndreHelberg,如果私钥是未加密的话,那么它同样不安全。然而,如果您的SSH密钥需要密码短语来解锁,也就是说它以加密形式存储在磁盘上,那么情况就不同了。在您的SSH配置中使用UseKeychain yes,结合本地密钥链管理器(例如macOS密钥链访问、GNOME密钥环/libsecret),意味着如果您已登录到本地机器,加密的密钥将自动解密,而无需提示您输入密钥的密码短语,但密钥仍然以加密形式存储在磁盘上。 - Jivan Pal

如果您不想使用公钥/私钥对,可以编写一个expect脚本,根据目标地址自动为您输入密码。
编辑:我的意思是您可以编写一个脚本,一方面使用expect为您输入密码,另一方面从配置文件中读取给定用户和主机的密码。例如,以下Python脚本适用于晴天情况:
#!/usr/bin/python                                                                                                                        
import argparse
from ConfigParser import ConfigParser
import pexpect

def main(args):
    url = args.url
    user, host = url.split('@', 1)

    cfg_file = 'ssh.cfg'
    cfg = ConfigParser()
    cfg.read(cfg_file)
    passwd = cfg.get(user, host)

    child = pexpect.spawn('ssh {0}'.format(url))
    child.expect('password:')
    child.sendline(passwd)
    child.interact()

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Run ssh through pexpect')
    parser.add_argument('url')
    args = parser.parse_args()
    main(args)

配置文件的格式如下:
[user_1]
host1 = passwd_1
host2 = passwd_2

[user_2]
host1 = passwd_1
host2 = passwd_2

注意:如前所述,Python脚本需要更复杂才能处理来自SSH的所有可能错误和问题消息以及所有可能的URL(在示例中假设它将类似于user@host,但大多数情况下不使用用户部分),但基本思想仍然是相同的。关于配置文件,您可以使用不同的配置文件或使用.ssh/config并编写自己的代码来解析该文件并获取给定用户和主机的密码。

你能再解释一下吗? - Netmoon
@Netmoon,我在我的回答中添加了一个小例子,以使它更清晰。 - jcollado
这并没有回答如何将密码放入.ssh/config文件的问题。 - Eric Woodruff
1确实,它并没有回答那个问题。但它解决了这个问题:避免手动输入密码并将其存储在文件中。这正是OP所需要的。 - Arcesilas

ProxyCommand怎么样?
Host Home-raw
    HostName test.com
Host Home
   User netmoon
   Port 22
   ProxyCommand sshpass -pmypass ssh netmoon@%h-raw nc localhost %p

你也可以使用ssh -W代替nc
ProxyCommand sshpass -pmypass ssh netmoon@%h-raw -W localhost:%p

使用-W选项不会按预期工作。你有什么解决办法吗? - Toan Nguyen
3使用这个代理命令还是要求我输入密码... - Victor Pudeyev
1我也发现它仍然要求输入密码。所有这些似乎只是将问题从test.com需要密码转移到了Home需要密码。这有什么意义呢?有什么诀窍可以让它正常工作吗? - Martin Bramwell

还有一个名为sshpass的程序。 使用方法: sshpass -p MyPa55word ssh me@myservor.com


31除非您在命令之前加上一个空格(使用sshpass而不是sshpass),否则您刚刚将密码("MyPa55word")存储在了您的shell历史文件中。 - waltinator
1@waltinator 说得好 - igor
3好吧,问题提出者对.ssh/config这样的位置没意见,为什么对shell历史记录有意见呢? - Darth Egregious
虽然它还没有正式加入Homebrew,但你可以通过第三方仓库进行安装。使用命令brew install https://raw.githubusercontent.com/kadwanev/bigboybrew/master/Library/Formula/sshpass.rb即可。更多信息请参考:https://gist.github.com/arunoda/7790979 - Jacob Ford
读取 -s 密码; sshpass -p "$password" ssh me@myservor.com。这将防止密码显示在历史记录中。 - Alexander Bird
一个恰到好处的(或重复的)ps -lf -C sshpass命令将会捕获密码,除非sshpass清除了它的环境。 - waltinator
export SSHPASS='YourPass'; sshpass -e ssh me@server - ThiagoAlves

不行,恐怕这是不可能的。

唯一的真正选择是使用私钥,但您说您不想使用(为什么?)。


12因为我没有在服务器上放置另一个密钥的许可。 - Netmoon
3@Netmoon:如果你能登录,那么你就可以添加一个密钥,对吧?除非系统管理员设置得很奇怪,否则你只需要对你的主目录拥有写入权限。 - Scott Severance
4@ScottSeverance 我认为这个问题所指的就是这种情况。无法添加密钥的能力。是的,这很奇怪,但经常发生。 - user239558
6我曾经有过在共享主机环境中的非常普遍的经验,其中禁用了公钥访问,因此即使您可以添加密钥,它们也不会被使用。虽然这违背了理性,但这是许多托管提供商设置其服务器的方式。 - But those new buttons though..
1有一些答案表明这是可能的。 - Eric Woodruff
1@PiotrDobrogost 在帖子上征求踩票是不对的。如果对这个回答有什么问题,请以礼貌的方式发表。 - Seth
这是对OP问题的唯一有效答案:“我可以在配置文件中放置密码吗?”虽然所有其他回答都很有帮助,但它们并没有直接回答OP的问题。 - the_meter413

你可以在 /usr/local/bin 中创建一个简单的 ssh 脚本替代品。
#!/bin/bash

host=$1
password=`awk "/#Password/ && inhost { print \\\$2 } /Host/ { inhost=0 } /Host $host/ { inhost=1 }" ~/.ssh/config`

if [[ -z "$password" ]]; then
  /usr/bin/ssh $*
else
  sshpass -p $password /usr/bin/ssh $*
fi

然后在您的~/.ssh/config文件中,您可以使用
Host foohost
    User baruser
    #Password foobarpassword

回答你的问题,不,无法在ssh配置文件中配置默认密码。
但是,如果确实如你所说的那样,是因为“有时候我离开电脑一段时间,当我回来后输入密码并按下Enter,终端会显示CONNECTION CLOSED,那么为什么不防止会话关闭呢?SSH可以为你保持连接。
Host Home
  User netmoon
  Port 22
  HostName test.com
  ServerAliveInterval 300
  ServerAliveCountMax 2

我使用 VanDyke Software 开发的应用程序,名为 SecureCRT

http://www.vandyke.com/products/securecrt/

这个软件不是免费的,但价格非常合理。我已经使用它多年了(在Windows上运行,或者使用Wine),用于远程访问、终端仿真和分散式网络管理。他们在2011年初终于发布了一个原生的Linux版本。
它支持复杂的登录设置(或脚本)、存储密码(或证书)、选项卡式多会话等功能。
在启动时,你可以从结构化列表(树状视图)中选择要连接的远程目标(和协议),也可以创建一个连接(然后将其存储)。
我发现它特别适用于具有高级身份验证、非标准端口或防火墙访问协商的远程站点。
如果你经常进行远程访问(作为你主要工作的一部分),那么这个应用程序在使用的第一个月就能证明其价值。

抱歉,我不明白。你能解释一下吗? - Netmoon
1为您重新表达的翻译文本: - david6
2自从上述回复发布以来,SecureCRT已经有过几个迭代版本,包括VanDyke在2013年12月初刚发布的最新版本。每个版本都在改进程序,使其更加多功能。它还具有丰富的API,允许使用Python/VB脚本控制/接口化程序。SecureCRT已经成为我核心工具箱的一部分十年了,我强烈推荐它。 - Ville
同意。我继续对每个新版本进行测试,并且在将其移植到Ubuntu上的早期测试中参与了很多工作。 - david6

受到@Arek Burdach的答案和其他封装的启发,我编写了一个更健壮的封装,可以更好地解析自己的ssh命令。
更新2021-04-26:修复了当主机只匹配前缀时的封装问题(例如,主机为none)。
它的工作原理如下:
  • 首先,我们使用-v和一个不存在的ProxyCommand进行试运行,这样它不会建立任何网络连接,而是立即退出。
  • 然后,我们提取类似于debug1: /home/misty/.ssh/config line 42: Applying options for XXXXXX的行。但是ssh输出的errlog带有\r,因此在传递给grep之前我们将其替换。
根据我的测试,ssh只会识别命令行中第一个-o ProxyCommand=XXX,即使命令行中有第二个ProxyCommand选项或者~/.ssh/config中有ProxyCommand,所以我们的方法非常稳定 ;)
对于ssh
#!/usr/bin/env bash
ORISSH=/usr/bin/ssh
ssh_dryrun=$($ORISSH -v -o "ProxyCommand=FAKE_PROXY_STUB %h %p" "$@" 2>&1 | sed -e 's/\r/\n/g')
host=$( grep -oP 'Applying options for \K(.*?)$' <<< $ssh_dryrun | head -n 1 )

if [[ $host == "*" ]]; then
  password=''
else
  password=`awk "/#Password/ && inhost { print \\\$2 } /^Host/ { inhost=0 } /Host $host$/ { inhost=1 }" ~/.ssh/config`
fi

if [[ -z "$password" ]]; then
  $ORISSH "$@"
else
  sshpass -p $password $ORISSH "$@"
fi

对于scp:
#!/usr/bin/env bash
ORISCP=/usr/bin/scp
scp_dryrun=$($ORISCP -v -o "ProxyCommand=proxyhost %h %p" "$@" 2>&1 | sed -e 's/\r/\n/g')
host=$( grep -oP 'Applying options for \K(.*?)$' <<< $scp_dryrun | head -n 1 )

if [[ $host == "*" ]]; then
  password=""
else
  password=`awk "/#Password/ && inhost { print \\\$2 } /^Host/ { inhost=0 } /Host $host$/ { inhost=1 }" ~/.ssh/config`
fi

if [[ -z "$password" ]]; then
  $ORISCP "$@"
else
  sshpass -p $password $ORISCP "$@"
fi

@Nmath 非常感谢您的有益指导!我将来会注意这个问题 :) - Misty

感谢Arek的灵感...
与其运行另一个shell进程,这只是在当前bash shell中运行的一个函数。它运行一个单独的awk命令来解析配置文件,并确定它是否应该从shell变量中获取密码,还是从ssh配置文件中以明文形式写入的密码(使用awk在eval中而不是declare,因为我在使用declare时遇到了问题)。
我尝试了很多种直接在ssh配置文件中使用ProxyCommand的方式来使用sshpass,但除了当我可以通过RSA登录到一个框中,然后需要发送一个密码来打开我的加密目录之外,似乎没有任何一种方式能按预期工作。然而,下面的函数对我来说似乎在所有情况下都有效,甚至适用于Cygwin。
# In your .bash_profile
function ssh(){
    host=$1;
    unset PASSWORD
    unset PASSVAR
    eval $(awk "/ *#[Pp]assvar / && inhost { printf \"PASSVAR=%s\",\$2; exit 1 } / *#[Pp]assword / && inhost { printf \"PASSWORD=%s\",\$2; } /^#?[Hh][oO][sS][tT] / && inhost { inhost=0; exit 1 } /^[Hh][oO][sS][tT] $host\$/ { inhost=1 }" ~/.ssh/config)
    if [[ -z "$PASSWORD" ]] && [[ -z "$PASSVAR" ]]; then
        /usr/bin/ssh -q $* 2>/dev/null
    else
       if [[ -n "$PASSVAR" ]]; then
          PASSWORD=$(TMP=${!PASSVAR-*};echo ${TMP##*-})
       fi
       /usr/local/bin/sshpass -p"$PASSWORD" /usr/bin/ssh -q $* 2>/dev/null
    fi
}
# and setup your passwords (perhaps in .bashrc instead...)
MYPASS_ENVVAR=SomePassword
MYPASSWD_FROM_FILE=$(</home/me/.passwd_in_file)

然后一个~/.ssh/config部分看起来像这样:
Host MyHostname
 Port 22
 Hostname 2.1.2.2
 User merrydan
 #Passvar MYPASS_ENVVAR
 #Password Some!Password

如果在配置部分存在#Passvar,则会覆盖#Password$MYPASS_ENVVAR是保存密码的环境变量。
祝您使用愉快!
还有一些处理大多数scp场景的内容(尚未完全完善,但至少是一个开始...)。
function scp(){
    host=$(echo $* | perl -pe 's/^.*?([A-Za-z-1-9-]+):.*?$/\1/;');
    unset PASSWORD
    unset PASSVAR

    eval $(awk "/^#[Pp]assvar / && inhost { printf \"PASSVAR=%s\",\$2; exit 1; } /^ *#[Pp]assword / && inhost { printf \"PASSWORD=%s\",\$2; } /^#?[Hh][oO][sS][tT] / && inhost { inhost=0; exit 1; } /^[Hh][oO][sS][tT] $host\$/ { inhost=1 }" ~/.ssh/config.d/*)
    if [[ -z "$PASSWORD" ]] && [[ -z "$PASSVAR" ]]; then
        #echo /usr/bin/scp -3 $*
        /usr/bin/scp -3 $* || true
        #echo "SSH Exit Code $?"
    else
       if [[ -n "$PASSVAR" ]]; then
          #PASSWORD=`echo ${!PASSVAR}`
          PASSWORD=$(TMP=${!PASSVAR-*};echo ${TMP##*-})
       fi
       #echo /usr/local/bin/sshpass -p"$PASSWORD" /usr/bin/scp -3 $*
       /usr/bin/sshpass -p"$PASSWORD" /usr/bin/scp -3 $* || true
       #echo "SSHPass Exit Code $?"
    fi
}