可以期望脚本从 stdin 读取数据吗?

Can expect scripts read data from 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).

上泄露了密码

也许有更好的方法?

Bellow 是一些有效但在 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

在 bashscript 中,读取密码,然后 export 变量。在期望中,使用 $env(passphrase)

从环境中访问它

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

读取单个变量

#!/usr/bin/expect

set passphrase [gets stdin]

读取多个变量

#!/usr/bin/expect

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

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

batch.sh 中定义的环境变量因此在使用 osascript 创建的 iTerm2 选项卡中不可用。

所以我唯一安全的选择是完全删除 osascript 并让所有代码(batch.shexpect.exp)在同一个 shell 中执行并使用在 bashexpect.

之间传递变量的环境变量

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 "[=12=]")/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