可以期望脚本从stdin读取数据吗? [英] Can expect scripts read data from stdin?

查看:87
本文介绍了可以期望脚本从stdin读取数据吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

执行expect脚本时,参数在ps ax上可见,如果它们很敏感,则可能是安全漏洞.

尝试自动打开iTerm2上的选项卡,运行ssh admin@host,并在询问Enter passphrase for key '/Users/admin/.ssh/key'时输入密码(密钥使用该密码加密).

Host host
HostName 1.2.3.4
IdentityFile ~/.ssh/key

我想使用read -sp 'Passphrase: ' passphrase将密码短语提供给bash,然后将其通过管道传递到expect(从OPSEC的角度来看这并不完美,但比ps ax上的密码短语泄漏要好得多).

也许有更好的方法吗?

下面是一些可以工作的代码,但会泄漏ps ax上的密码短语.已注释掉是我希望的可能性(将密码短语输入expect).

batch.sh

#!/bin/bash

function new_tab() {
  command=${1//\"/\\\"}
  osascript \
    -e "tell application \"iTerm2\"" \
    -e "tell current window" \
    -e "create tab with default profile" \
    -e "delay 1" \
    -e "tell current session" \
    -e "write text \"$command\"" \
    -e "end tell" \
    -e "end tell" \
    -e "end tell" > /dev/null
}

hostnames=(
  "hostname-1"
  "hostname-2"
)

read -sp 'Passphrase: ' passphrase

for hostname in "${hostnames[@]}"; do
  # new_tab "echo $passphrase | expect $(pwd)/expect.exp \"$hostname\""
  new_tab "expect $(pwd)/expect.exp \"$hostname\" \"$passphrase\""
done

expect.exp

#!/usr/bin/expect

set hostname [lindex $argv 0]
set passphrase [lindex $argv 1]

spawn ssh admin@$hostname
expect "passphrase"
send "$passphrase\r"
interact

解决方案

是的,expect可以从stdin读取,但是有一个警告:从stdin读取与interact不兼容.

请参见 https://stackoverflow.com/a/57847199/4579271

读取单个变量

#!/usr/bin/expect

set passphrase [gets stdin]

读取多个变量

#!/usr/bin/expect

set data [gets stdin]
scan $data "%s %s" hostname passphrase

另一种方法是使用环境变量(如Glenn所建议的),但还有一个警告:环境变量仅可用于定义它们的外壳及其子对象.

因此,在batch.sh中定义的

环境变量在使用osascript创建的iTerm2选项卡中将不可用.

因此,我唯一安全的选择是完全删除osascript并使所有代码(batch.shexpect.exp)在同一外壳中执行,并使用环境变量在bash.

batch.sh

#!/bin/bash

hostnames=(
  "hostname-1"
  "hostname-2"
)

read -sp 'SSH key passphrase: ' passphrase

echo ""

export PASSPHRASE=$passphrase

for hostname in "${hostnames[@]}"; do
  export HOSTNAME=$hostname
  expect "$(dirname "$0")/expect.exp"
done

expect.exp

#!/usr/bin/expect

set timeout 10

spawn ssh admin@$env(HOSTNAME)

expect {
  default {
    puts "\nCould not connect to $env(HOSTNAME)"
    exit 1
  }
  "passphrase" {
    send "$env(PASSPHRASE)\r"
  }
}

expect {
  default {
    puts "\nWrong passphrase"
    exit 1
  }
  "admin@$env(HOSTNAME)" {
    # Add automation commands here, then exit SSH session to close expect script moving on to the next hostname
    send "exit\r"
  }
}

interact

When executing expect scripts, arguments are visible on ps ax which can be a security vulnerability if they are sensitive.

Trying to automate opening a tab on iTerm2, running ssh admin@host and entering the passphrase when asked Enter passphrase for key '/Users/admin/.ssh/key' (the key is encrypted using that passphrase).

Host host
HostName 1.2.3.4
IdentityFile ~/.ssh/key

I would like to supply the passphrase to bash using read -sp 'Passphrase: ' passphrase and then pipe it to expect (which isn’t perfect at all from an OPSEC perspective but much better than having the passphrase leaked on ps ax).

Perhaps there is a better way?

Bellow is some code that works but leaks the passphrase on ps ax. Commented out is what I wish was possible (piping the passphrase to expect).

batch.sh

#!/bin/bash

function new_tab() {
  command=${1//\"/\\\"}
  osascript \
    -e "tell application \"iTerm2\"" \
    -e "tell current window" \
    -e "create tab with default profile" \
    -e "delay 1" \
    -e "tell current session" \
    -e "write text \"$command\"" \
    -e "end tell" \
    -e "end tell" \
    -e "end tell" > /dev/null
}

hostnames=(
  "hostname-1"
  "hostname-2"
)

read -sp 'Passphrase: ' passphrase

for hostname in "${hostnames[@]}"; do
  # new_tab "echo $passphrase | expect $(pwd)/expect.exp \"$hostname\""
  new_tab "expect $(pwd)/expect.exp \"$hostname\" \"$passphrase\""
done

expect.exp

#!/usr/bin/expect

set hostname [lindex $argv 0]
set passphrase [lindex $argv 1]

spawn ssh admin@$hostname
expect "passphrase"
send "$passphrase\r"
interact

解决方案

Yes, expect can read from stdin but there is a caveat: reading from stdin isn’t compatible with interact.

See https://stackoverflow.com/a/57847199/4579271

Reading a single variable

#!/usr/bin/expect

set passphrase [gets stdin]

Reading multiple variables

#!/usr/bin/expect

set data [gets stdin]
scan $data "%s %s" hostname passphrase

Another approach is to use environment variables (as suggested by Glenn) but there is another caveat: environment variables are only available to the shell in which they are defined and its children.

Environment variables defined in batch.sh would therefore not be available in the iTerm2 tabs created using osascript.

So the only secure option I have is to drop osascript altogether and have all the code (batch.sh and expect.exp) execute in the same shell and use environment variables to pass variables between bash and expect.

batch.sh

#!/bin/bash

hostnames=(
  "hostname-1"
  "hostname-2"
)

read -sp 'SSH key passphrase: ' passphrase

echo ""

export PASSPHRASE=$passphrase

for hostname in "${hostnames[@]}"; do
  export HOSTNAME=$hostname
  expect "$(dirname "$0")/expect.exp"
done

expect.exp

#!/usr/bin/expect

set timeout 10

spawn ssh admin@$env(HOSTNAME)

expect {
  default {
    puts "\nCould not connect to $env(HOSTNAME)"
    exit 1
  }
  "passphrase" {
    send "$env(PASSPHRASE)\r"
  }
}

expect {
  default {
    puts "\nWrong passphrase"
    exit 1
  }
  "admin@$env(HOSTNAME)" {
    # Add automation commands here, then exit SSH session to close expect script moving on to the next hostname
    send "exit\r"
  }
}

interact

这篇关于可以期望脚本从stdin读取数据吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆