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

时间:2015-07-13 17:05:54

标签: bash shell ssh expect scp

环境

A => B => C

A。我们的电脑

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

Host B
    ServerAliveInterval 30
    IdentifyFile ~/.ssh/id_rsa
    User a-user
    Port 10022
    ForwardAgent yes
  • Mac OSX Yosemite。

B。跳转主持人

/etc/ssh/sshd_config可能是这样的:

RSAAuthentication yes
PubkeyAuthentication yes
AllowTcpForwarding no
ChallengeResponseAuthentication no
PasswordAuthentication no
ForceCommand "ssh C"
  • 我们无权配置此计算机。
  • A的公钥用authorized_keys编写。

C。目标主持人

  • 虚拟机上的Ubuntu。
  • A的公钥用authorized_keys编写。

情况

  • Assh B使用密码验证。
  • 由于代理转发,
  • Assh B使用公钥认证。
  • A无法ssh B foo使用密码验证。 3次挑战很快就失败了。
  • A无法ssh -t B foo使用密码验证。参数foo被忽略。
  • A无法ssh B foo使用公钥身份验证。参数foo被忽略。
  • A无法ssh -t B foo使用公钥身份验证。参数foo被忽略。

方法1: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 [ "--" = "$1" ]; then
                shift
                break
            elif [[ "$1" =~ ^-[bcDEeFIiLlmOopQRSWw]+$ ]]; then
                sshargs=("${sshargs[@]}" "$1")
                shift
                if (( $# > 0 )); then
                    sshargs=("${sshargs[@]}" "$1")
                    shift
                fi
            elif [[ "$1" =~ ^-.*$ ]]; then
                sshargs=("${sshargs[@]}" "$1")
                shift
            elif ! $reach; then
                sshargs=("${sshargs[@]}" "$1")
                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
                    }
                    $0 ~ "'$boundary'---" {
                        ++count
                        skip=1
                    }
                    count==1 {
                        if (skip != 1) {
                            print $0
                        } 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 \004
                        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
                    }
                    $0 ~ "'$boundary'---" {
                        ++count
                        skip=1
                    }
                    count==1 {
                        if (skip != 1) {
                            print $0
                        } 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

}

使用:

  • ssh B
  • ssh B ls -A
  • ssh B cat example.tar.gz | tar -xf - -C /tmp
  • echo "This will be appeared in STDOUT" | ssh B cat

不能工作:

  • cat example.tar.gz | ssh B tar -xf - -C /tmp

方法2:scp代理转发

我的最终目标是将本地文件发送到远程计算机,因此scp似乎是另一个不错的选择。但是,ssh适用于座席转发,而scp则不会......

问题

  • 为什么我不能将scp与代理转发一起使用?
  • 有没有人有好主意在这些受限制的环境中传输文件?

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

附加说明

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

output

我在| tee ~/Desktop/tee.log |后陷入expect以找到奇怪的结果:

enter image description here

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

1 个答案:

答案 0 :(得分:2)

您无法将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 $1 in
    -oForwardAgent=no)         : ;;
    -oClearAllForwardings=yes) : ;;
    *)                         args+=( "$1" ) ;;
  esac
  shift
done
exec /usr/bin/ssh "${args[@]}"