使用pexpect自动认证快速登陆中控机、服务器

  • 生产实践:

    自动认证快速登陆中控机,服务器

  • 学习技巧:

   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


参考:

anzhihe 安志合个人博客,版权所有 丨 如未注明,均为原创 丨 转载请注明转自:https://chegva.com/4414.html | ☆★★每天进步一点点,加油!★★☆ | 

您可能还感兴趣的文章!

发表评论

电子邮件地址不会被公开。 必填项已用*标注