带有代理转发的 scp 在受限环境中失败

scp with agent forwarding fails in restricted environment

环境

A => B => C

A。我们的电脑

~/.ssh/config大致是这样的:

Host B
    ServerAliveInterval 30
    IdentifyFile ~/.ssh/id_rsa
    User a-user
    Port 10022
    ForwardAgent yes

B。跳转主机

/etc/ssh/sshd_config大概是这样的:

RSAAuthentication yes
PubkeyAuthentication yes
AllowTcpForwarding no
ChallengeResponseAuthentication no
PasswordAuthentication no
ForceCommand "ssh C"

C。目标主机

情况

方法一:ShellScript 技巧

我写了下面复杂的ShellScript...

# Overrides native ssh command
ssh() {

    # Configuration
    local password="PASSWORD"
    local specialhost="FUMIDAI"

    # Special jump host..?
    if [[ "$@" =~ $specialhost ]]; then

        # Divide arguments into ssh's and additonal commands' and itself
        local sshargs
        sshargs=("ssh" "-t")
        local reach=false
        while (( $# > 0 )); do
            if [ "--" = "" ]; then
                shift
                break
            elif [[ "" =~ ^-[bcDEeFIiLlmOopQRSWw]+$ ]]; then
                sshargs=("${sshargs[@]}" "")
                shift
                if (( $# > 0 )); then
                    sshargs=("${sshargs[@]}" "")
                    shift
                fi
            elif [[ "" =~ ^-.*$ ]]; then
                sshargs=("${sshargs[@]}" "")
                shift
            elif ! $reach; then
                sshargs=("${sshargs[@]}" "")
                reach=true
                shift
            else
                break
            fi
        done

        # No additional commands?
        if (( $# == 0 )); then 

            # Automatically input password and interact 
            expect <(echo '
                set timeout -1
                set password [lindex $argv 0]
                set sshargs [lrange $argv 1 end]
                proc are_you_sure {} {
                    send [gets stdin]
                    send "\n"
                    expect "password:" password_input "Please type" are_you_sure
                }
                proc password_input {} {
                    global password
                    send $password
                    send "\n"
                    expect "password:" password_input "Last login:" interact
                }
                eval spawn -noecho command $sshargs
                expect "Are you sure" are_you_sure "password:" password_input "Last login:" interact
            ') $password "${sshargs[@]}"

        # We have sub commands!
        else 

            # Generate random boundary
            local boundary=$(mktemp -u boundary---------XXXXXXXXXXXXXXXXXXXXXXXXXX)

            # Is STDIN owned by terminal?
            if [ -t 0 ]; then

                # Automatically input password,
                # input commands and execute them,
                # receive base64 encoded output,
                # finally exit when the last boundary appeared.
                expect <(echo '
                    set timeout -1
                    set password [lindex $argv 0]
                    set boundary [lindex $argv 2]
                    set bashargs [lrange $argv 3 [expr 3 + [lindex $argv 1] - 1]]
                    set sshargs [lrange $argv [expr 3 + [lindex $argv 1]] end]
                    set boundary_count 0
                    proc are_you_sure {} {
                        send [gets stdin]
                        send "\n"
                        expect "password:" password_input "Please type" are_you_sure
                    }
                    proc password_input {} {
                        global password
                        send $password
                        send "\n"
                        expect "password:" password_input "Last login:" command_input
                    }
                    proc command_input {} {
                        global boundary
                        global bashargs
                        global eb
                        expect_background "$boundary---" observe_boundary
                        set eb "echo \"$boundary\"\"---\""
                        set en "echo \"\""
                        set args [join $bashargs]
                        send "$eb && ( $args | base64 ) 2>/dev/null && $eb && $en && $en\n"
                        interact
                    }
                    proc observe_boundary {} {
                        global boundary
                        global boundary_count
                        if { $boundary_count < 1 } {
                            incr boundary_count
                            expect_background "$boundary---" observe_boundary
                        } else {
                            exit 0
                        }
                    }
                    eval spawn -noecho command $sshargs
                    expect "Are you sure" are_you_sure "password:" password_input "Last login:" command_input
                ') $password $# $boundary "$@" "${sshargs[@]}" |

                # Filter by boundaries
                awk '
                    BEGIN {
                        count=0
                        skip=0
                    }
                    [=12=] ~ "'$boundary'---" {
                        ++count
                        skip=1
                    }
                    count==1 {
                        if (skip != 1) {
                            print [=12=]
                        } else {
                            skip=0
                        }
                    }
                ' |

                # Retrive it! (in OSX "-D" option means decoding, not "-d")
                base64 -D

            # Piped, or redirected
            else 

                # Encode STDIN
                base64 |

                # Prevent buffer flooding
                (sleep 3 && cat && sleep 3) |

                # Automatically input password,
                # input commands and execute them,
                # decode STDIN and process them and re-encode,
                # receive base64 encoded output,
                # finally exit when the last boundary appeared.
                expect <(echo '
                    set timeout -1
                    set password [lindex $argv 0]
                    set boundary [lindex $argv 2]
                    set bashargs [lrange $argv 3 [expr 3 + [lindex $argv 1] - 1]]
                    set sshargs [lrange $argv [expr 3 + [lindex $argv 1]] end]
                    set boundary_count 0
                    proc are_you_sure {} {
                        send [gets stdin]
                        send "\n"
                        expect "password:" password_input "Please type" are_you_sure
                    }
                    proc password_input {} {
                        global password
                        send $password
                        send "\n"
                        expect "password:" password_input "Last login:" command_input
                    }
                    proc command_input {} {
                        global boundary
                        global bashargs
                        global eb
                        expect_background "$boundary---" observe_boundary
                        set eb "echo \"$boundary\"\"---\""
                        set en "echo \"\""
                        set args [join $bashargs]
                        send "$eb && ( base64 -d | $args | base64 ) 2>/dev/null && $eb && $en && $en\n"
                        fconfigure stdin -blocking 0
                        while {[gets stdin line] != -1} {
                            send "$line\n"
                        }
                        send [=12=]4
                        interact
                    }
                    proc observe_boundary {} {
                        global boundary
                        global boundary_count
                        if { $boundary_count < 1 } {
                            incr boundary_count
                            expect_background "$boundary---" observe_boundary
                        } else {
                            send "logout\n"
                        }
                    }
                    eval spawn -noecho command $sshargs
                    expect "Are you sure" are_you_sure "password:" password_input "Last login:" command_input
                ') $password $# $boundary "$@" "${sshargs[@]}" |

                # Filter by boundaries
                awk '
                    BEGIN {
                        count=0
                        skip=0
                    }
                    [=12=] ~ "'$boundary'---" {
                        ++count
                        skip=1
                    }
                    count==1 {
                        if (skip != 1) {
                            print [=12=]
                        } else {
                            skip=0
                        }
                    }
                ' |

                # Retrive it! (in OSX "-D" option means decoding, not "-d")
                base64 -D

            fi

        fi

    # No, regular ssh
    else 

        # Call native ssh command
        command ssh "$@"

    fi

}

作品:

无效:

方法 2:scp 使用代理转发

我的最终目标是将本地文件发送到远程计算机,因此 scp 似乎是另一个不错的选择。但是,ssh 与代理转发一起使用,而 scp 不...

问题

非常抱歉,很长很长的问题。谢谢。

补充说明

cat excample.tar.gz| ssh jgn-mba tar -xf - -C /tmp 的输出:

我在 expect 之后困住了 | tee ~/Desktop/tee.log | 以发现奇怪的结果:

是的,最后一个边界没有出现...

您不能将 scp 与代理转发一起使用,因为它会明确关闭它。在大多数情况下,这是有道理的:允许您正在向或从中复制文件的系统在连接期间使用您的钥匙串通常是不必要的风险。

引用 from the source:

   addargs(&args, "%s", ssh_program);
   addargs(&args, "-x");
   addargs(&args, "-oForwardAgent=no");
   addargs(&args, "-oPermitLocalCommand=no");
   addargs(&args, "-oClearAllForwardings=yes");

当然,您可以构建一个没有这样做的补丁版本。


另一种选择是构建一个代理到 ssh 的脚本,但忽略您不想要的参数:

scp -S /path/to/yourscript file host:/path/to/file

...那么,/path/to/yourscript 可以是这样的:

#!/bin/bash
args=( )
while (( $# )); do
  case  in
    -oForwardAgent=no)         : ;;
    -oClearAllForwardings=yes) : ;;
    *)                         args+=( "" ) ;;
  esac
  shift
done
exec /usr/bin/ssh "${args[@]}"