生产实践:
自动认证快速登陆中控机,服务器
学习技巧:
Python pexpect、click库,谷歌双因素认证使用
脚本内容:
可以使用脚本做中控机认证,或者进入交互模式登陆中控机,服务器,无需每次输入登陆密码和谷歌验证码。
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# create by anzhihe 20210513
import click
import pexpect
import pyotp
import struct, fcntl, sys, signal
import termios
def sigwinch_passthrough (sig, data):
"""设置合适的终端窗口大小
This returns the window size of the child tty.
The return value is a tuple of (rows, cols).
"""
if not global_pexpect_instance:
return
if 'TIOCGWINSZ' in dir(termios):
TIOCGWINSZ = termios.TIOCGWINSZ
else:
TIOCGWINSZ = 1074295912 # Assume
s = struct.pack('HHHH', 0, 0, 0, 0)
a = struct.unpack ('HHHH', fcntl.ioctl(sys.stdout.fileno(), TIOCGWINSZ , s))
# print(a[0],a[1])
global_pexpect_instance.setwinsize(a[0],a[1])
def get_auth_token(secret):
"""获取谷歌认证码"""
totp = pyotp.TOTP(secret)
return totp.now()
def login_relay(child, secret, interact):
"""登陆中控机"""
index = child.expect(["Verification code", pexpect.EOF, pexpect.TIMEOUT])
if index in (0, 2):
token = get_auth_token(secret)
child.sendline(token)
index = child.expect(["relay", pexpect.EOF, pexpect.TIMEOUT])
if (index == 0):
click.echo(click.style('认证成功:)', fg='blue'))
# 取消标准输出,否则会导致每个字符重复显示两遍
child.logfile_read = None
if interact:
# 在中控机上执行指定命令,登陆到指定服务器
#child.sendline('ssh anzhihe@machine')
#click.clear() # 清屏
#child.interact() # 将控制权交给用户
child.sendline('chegva-web-console02.bj')
index = child.expect(["console", pexpect.EOF, pexpect.TIMEOUT])
if index == 0:
child.sendline('sudo su -') # 切换到root用户
click.clear() # 清屏
child.interact() # 将控制权交给用户
else:
child.close()
elif index == 1:
click.echo(click.style('认证失败:(', fg='red'))
child.close()
CONTEXT_SETTINGS = dict(help_option_names = ['-h', '--help'])
@click.command(context_settings = CONTEXT_SETTINGS)
@click.option('--username', '-u', required=True, type=str, help='username', default='anzhihe')
@click.option('--password','-p', required=True, type=str, help='password', default='passwd')
@click.option('--secret', '-s', required=True, type=str, help='secret', default='google_auth_key')
@click.option('--interact', '-i', is_flag=True, help='进入交互模式', default=False)
def login(username: str, password: str, secret: str, interact: bool):
"""连接中控机"""
child = pexpect.spawn('ssh %s@relay' % username, timeout=3)
global global_pexpect_instance
global_pexpect_instance = child
sigwinch_passthrough(1,2)
#child.sendline() # 忽略交互前二维码图片无法识别显示乱码的问题
## debug模式
#child.logfile = open("logfile.txt", 'wb')
#child.logfile_read = sys.stdout.buffer
# 重定向输出到stdout,不包含输入的密码
if interact:
child.logfile_read = sys.stdout.buffer
# relay二维码bug,第一次匹配必须输入内容才能继续输出
#child.sendline()
# 开始匹配
index = child.expect(["id_rsa", "code", "relay", pexpect.EOF, pexpect.TIMEOUT])
if index == 0:
child.sendline(password)
login_relay(child, secret, interact)
elif index == 1:
login_relay(child, secret, interact)
elif index == 2:
click.echo(click.style('亲,已经认证过了哦~', fg='green'))
child.logfile_read = None
if interact:
#child.sendline('ssh anzhihe@machine')
#click.clear()
#child.interact()
child.sendline('chegva-web-console02.bj')
index = child.expect(["console", pexpect.EOF, pexpect.TIMEOUT])
if index == 0:
child.sendline('sudo su -') # 切换到root用户
click.clear() # 清屏
child.interact() # 将控制权交给用户
else:
child.close()
if __name__ == '__main__':
login()◎查看效果
~ relay -h Usage: relay [OPTIONS]Options: -u, --username TEXT username [required] -p, --password TEXT password [required] -s, --secret TEXT secret [required] -i, --interact 进入交互模式 -h, --help Show this message and exit. # 指定用户、密码、key认证 ~ relay -u anzhihe -p passwd -s secret # 只认证不登陆 ~ relay # 认证后进入交互模式登陆中控机,服务器 ~ relay -i
SSH 持久化连接配置:
# ssh 持久化连接 $ cat ~/.ssh/config Host * User anzhihe ServerAliveInterval 30 ControlMaster auto ControlPath /tmp/ssh-%r@%h:%p ControlPersist yes StrictHostKeyChecking=no Compression=yes AddKeysToAgent yes PubkeyAcceptedKeyTypes +ssh-dss ForwardAgent=yes
参考: