【问题标题】:What is the secure/correct way of adding www.github.com to the known_hosts file?将 www.github.com 添加到 known_hosts 文件的安全/正确方法是什么?
【发布时间】:2018-09-15 03:44:05
【问题描述】:

我想通过 ssh 访问我的 github 存储库。当我第一次访问存储库时,系统会询问我是否要将 github ssh 服务器添加到我的 known_hosts 文件中,这可以正常工作。该请求还向我显示了该服务器的 RSA 密钥指纹,我可以手动验证它是否与 github here 提供的相同。

这些是 OpenSSH 6.8 及更高版本(base64 格式)中显示的 SHA256 哈希:

SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8 (RSA)
SHA256:br9IjFspm1vxR3iA35FWE+4VTyz1hYVLIE2t1/CeyWQ (DSA)

问题是我想通过添加公钥来阻止该请求 在我第一次访问我的 git 存储库之前到我的 known_hosts 文件。这可以通过使用ssh-keyscan -t rsa www.github.com 命令来完成,该命令将为我提供known_hosts 文件所需格式的公钥。但是人们反复提到,这不安全,容易受到中间人攻击。他们没有提到的是如何正确地做。

那么如何使用github页面上提供的RSA指纹安全获取ssh服务器的主机公钥呢?我或多或少在寻找ssh-keyscan 命令的选项,该选项可让我添加预期的 rsa 指纹,并在主机指纹与给定指纹不匹配时导致命令失败。

感谢您的宝贵时间!

【问题讨论】:

    标签: github ssh openssh


    【解决方案1】:

    在这种情况下,我会使用 ssh-keyscan。
    相反,我会使用它通过将其指纹与 GitHub 提供的指纹进行比较来仔细检查结果。

    然后继续使用SSH GitHub test,检查我是否得到:

    Hi username! You've successfully authenticated, but GitHub does not
    provide shell access.
    

    所以,as recommended here,对于手动过程:

    ssh-keyscan github.com >> githubKey
    

    生成指纹:

    ssh-keygen -lf githubKey
    

    与 GitHub 提供的对比一下

    最后,将githubKey 的内容复制到您的~/.ssh/known_hosts 文件中。


    您可以使用wercker/step-add-to-known_hosts 自动执行该过程(仍然包括指纹步骤检查):它是wercker step,但可以推断为它自己的独立脚本。

    - add-to-known_hosts:
        hostname: github.com
        fingerprint: 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48
        type: rsa
    

    但这将缺乏对help.github.com/articles/github-s-ssh-key-fingerprints的检查:见下文。


    使用 nmap 并没有多大帮助,因为 explained here:

    使用 nmap 获取 SSH 主机密钥指纹,然后将其与 ssh-keyscan 所说的指纹进行比较:在这两种情况下,指纹都来自相同的地方。
    它与任何其他自动化解决方案一样容易受到 MITM 的攻击。

    验证 SSH 公钥的唯一安全有效的方法是通过一些受信任的带外通道。 (或者设置某种密钥签名基础设施。)

    在这里,help.github.com/articles/github-s-ssh-key-fingerprints 仍然是“受信任的带外通道”。

    【讨论】:

    • 好的,但是如何从SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8 指纹中获取我必须添加到我的known_hosts 文件中的行?添加类似gihub.com ssh-rsa SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8 的内容不起作用。这中间的步骤正是我所缺少的。
    • @Knitschi 我已经修改了我的答案并添加了缺失的步骤。
    【解决方案2】:

    根据VonC的回答,下面的脚本可以自动验证并添加密钥。像这样使用它:

    $ ./add-key.sh github.com nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8
    

    它会告诉您它是否成功验证并保存了指纹。
    有关使用信息,请使用./add-key.sh --help

    脚本:

    #!/usr/bin/env bash
    
    # Settings
    knownhosts="$HOME/.ssh/known_hosts"
    
    if [ "x$1" == "x-h" ] || [ "x$1" == "x--help" ] || [ ${#1} == 0 ]; then
        echo "Usage: $0 <host> <fingerprint> [<port>]"
        echo "Example: $0 github.com nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8"
        echo "The default port is 22."
        echo "The script will download the ssh keys from <host>, check if any match"
        echo "the <fingerprint>, and add that one to $knownhosts."
        exit 1
    fi
    
    # Argument handling
    host=$1
    fingerprint=$2
    port=$(if [ -n "$3" ]; then echo "$3"; else echo 22; fi)
    
    # Download the actual key (you cannot convert a fingerprint to the original key)
    keys="$(ssh-keyscan -p $port $host |& grep -v ^\#)";
    echo "$keys" | grep -v "^$host" # Show any errors
    keys="$(echo "$keys" | grep "^$host")"; # Remove errors from the variable
    if [ ${#keys} -lt 20 ]; then echo Error downloading keys; exit 2; fi
    
    # Find which line contains the key matching this fingerprint
    line=$(ssh-keygen -lf <(echo "$keys") | grep -n "$fingerprint" | cut -b 1-1)
    
    if [ ${#line} -gt 0 ]; then  # If there was a matching fingerprint (todo: shouldn't this be -ge or so?)
        # Take that line
        key=$(head -$line <(echo "$keys") | tail -1)
        # Check if the key part (column 3) of that line is already in $knownhosts
        if [ -n "$(grep "$(echo "$key" | awk '{print $3}')" $knownhosts)" ]; then
            echo "Key already in $knownhosts."
            exit 3
        else
            # Add it to known hosts
            echo "$key" >> $knownhosts
            # And tell the user what kind of key they just added
            keytype=$(echo "$key" | awk '{print $2}')
            echo Fingerprint verified and $keytype key added to $knownhosts
        fi
    else  # If there was no matching fingerprint
        echo MITM? These are the received fingerprints:
        ssh-keygen -lf <(echo "$keys")
        echo Generated from these received keys:
        echo "$keys"
        exit 1
    fi
    

    【讨论】:

    • 我的回答很好。 +1
    • 如果它还检查它是否已经存在,那就太棒了,这样它就可以在 CI 设置中运行(例如,在每次提交时),而不是无休止地附加到 known_hosts 文件。
    • @ChrisTrahey 好主意!已实施。
    • 这很好,直到ssh-keyscan -p $port $host 2&gt; /dev/null 失败并且没有告诉你原因。我认为使用ssh-keyscan github.com 2&gt;&amp;1 | grep -v '#' 会更好。
    • @NorseGaud 不完全是,而且您的建议不起作用。首先,脚本当前会告诉您下载失败,您可以从那里进行调试,但我同意显示错误更好。使用2&gt;&amp;1 的提议只是捕获了stderr(并且可以更简单地写成command |&amp; grep),所以当放入一个变量时(就像脚本一样),抑制stderr 的问题仍然存在。必须以某种方式将 stdout 和 stderr 分开。我已经编辑了脚本来做到这一点。
    【解决方案3】:

    我的单行允许错误报告失败:

    touch ~/.ssh/known_hosts && if [ $(grep -c 'github.com ssh-rsa' ~/.ssh/known_hosts) -lt 1 ]; then KEYS=$(KEYS=$(ssh-keyscan github.com 2>&1 | grep -v '#'); ssh-keygen -lf <(echo $KEYS) || echo $KEYS); if [[ $KEYS =~ '(RSA)' ]]; then if [ $(curl -s https://help.github.com/en/github/authenticating-to-github/githubs-ssh-key-fingerprints | grep -c $(echo $KEYS | awk '{print $2}')) -gt 0 ]; then echo '[GitHub key successfully verified]' && ssh-keyscan github.com 1>~/.ssh/known_hosts; fi; else echo \"ssh-keygen -lf failed:\\n$KEYS\"; exit 1; fi; unset KEYS; fi
    

    【讨论】:

    • 请注意,这仅适用于github.com,并不是基于指纹安全添加密钥的通用解决方案。我也不确定为什么它必须是一个巨大的、难以理解的行......
    • @Luc One liner 允许您将其放入 CI 脚本中,而不会使它们膨胀。
    • 我不想审核你的 CI 设置:P
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-09
    • 2023-04-09
    • 1970-01-01
    相关资源
    最近更新 更多