linux or mac上使用expect实现自动化

调用expect 交互性匹配命令行输出并进行自动输入

以下来自linux.die.net对于expect的部分摘要

  1. expect [ -dDinN ] [ -c cmds ] [ [ -[f|b] ] cmdfile ] [ args ]
  2. Introduction
    1. Expect is a program that “talks” to other interactive programs according to a script. Following the script, Expect knows what can be expected from a program and what the correct response should be. An interpreted language provides branching and high-level control structures to direct the dialogue. In addition, the user can take control and interact directly when desired, afterward returning control to the script.
    2. Expectk is a mixture of Expect and Tk. It behaves just like Expect and Tk’s wish. Expect can also be used directly in C or C++ (that is, without Tcl). See libexpect(3).
    3. The name “Expect” comes from the idea of send/expect sequences popularized by uucp, kermit and other modem control programs. However unlike uucp, Expect is generalized so that it can be run as a user-level command with any program and task in mind. Expect can actually talk to several programs at the same time.
    4. For example, here are some things Expect can do:
      1. Cause your computer to dial you back, so that you can login without paying for the call. Start a game (e.g., rogue) and if the optimal configuration doesn’t appear, restart it (again and again) until it does, then hand over control to you.
      2. Run fsck, and in response to its questions, answer “yes”, “no” or give control back to you, based on predetermined criteria.
      3. Connect to another network or BBS (e.g., MCI Mail, CompuServe) and automatically retrieve your mail so that it appears as if it was originally sent to your local system.
      4. Carry environment variables, current directory, or any kind of information across rlogin, telnet, tip, su, chgrp, etc.

expect 相关文档可以查阅expect官方文档

获取googleAuth token

  • google-auth的secret可以从google-auth软件导出配置查看,配置文件是一串类似这样的字符串(otpauth://totp/%s?secret=%s&issuer=%s&algorithm=%s&digits=%d&period=%d)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
//get_token.go

package main

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base32"
	"encoding/binary"
	"flag"
	"fmt"
	"time"
)

type TfActorAuthenticator struct {
	Issuer    string
	Algorithm string
	Period    int64
	Digits    int
}

// NewGoogleAuthenticator google双因子认证
func NewGoogleAuthenticator(issuer string) *TfActorAuthenticator {
	return &TfActorAuthenticator{
		Issuer:    issuer,
		Algorithm: "SHA1",
		Period:    30,
		Digits:    6,
	}
}

func (tfa *TfActorAuthenticator) TotpString(secret string) string {
	// base32编码秘钥:K
	key := make([]byte, base32.StdEncoding.DecodedLen(len(secret)))
	base32.StdEncoding.Decode(key, []byte(secret))

	// 根据当前时间计算随机数:C
	message := make([]byte, 8)
	binary.BigEndian.PutUint64(message, uint64(time.Now().Unix()/tfa.Period))

	// 使用sha1对K和C做hmac得到20个字节的密串:HMAC-SHA-1(K, C)
	hmacsha1 := hmac.New(sha1.New, key)
	hmacsha1.Write(message)
	hash := hmacsha1.Sum([]byte{})

	// 从20个字节的密串取最后一个字节,取该字节的低四位
	offset := hash[len(hash)-1] & 0xF
	truncatedHash := hash[offset : offset+4]

	// 按照大端方式组成一个整数
	bin := (binary.BigEndian.Uint32(truncatedHash) & 0x7FFFFFFF) % 1000000

	// 将数字转成特定长度字符串,不够的补0
	return fmt.Sprintf(`%0*d`, tfa.Digits, bin)
}

var secret = flag.String("secret", "your default secret", "the tfa`s secret")

func main() {
	flag.Parse()

	authClient := NewGoogleAuthenticator("")

	token := authClient.TotpString(*secret)
	fmt.Println("your_password" + token)
}

使用expect捕捉屏幕输出并自动填充密码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#~/.ssh/expects/connect.sh

#!/usr/bin/expect
set timeout 20
set jump_server you_jump_server_url
set port 8080
set user admin
set token [exec sh -c {$HOME/codes/go/src/demo/get_goken -secret your_secret}]
spawn ssh $user@$jump_server:$port
expect {
        "*Password:*" {
                send "$token\n"
                exp_continue;
        }
        .
        .
        .
        .
        more case...
    }
}
interact

usage

1
2
go build -o $HOME/codes/go/src/connect/get_token $HOME/codes/go/src/connect
echo 'alias connect_vm="/usr/bin/expect $HOME/.ssh/expects/connect.sh"' >> ~/.zshrc

congratulations!!!