jumpserver.py 9.32 KB
#!/usr/bin/python
# coding: utf-8


import os
import sys
import subprocess
import struct
import fcntl
import termios
import signal
import re
import time
from Crypto.Cipher import AES
from binascii import b2a_hex, a2b_hex
import ConfigParser
import paramiko
import pxssh
import pexpect

cur_dir = os.path.abspath(os.path.dirname(__file__))
sys.path.append('%s/webroot/AutoSa/' % cur_dir)
os.environ['DJANGO_SETTINGS_MODULE'] = 'AutoSa.settings'

import django
django.setup()
from UserManage.models import User, Logs, Pid
from Assets.models import Assets


cf = ConfigParser.ConfigParser()
cf.read('%s/jumpserver.conf' % cur_dir)

db_host = cf.get('db', 'host')
db_port = cf.getint('db', 'port')
db_user = cf.get('db', 'user')
db_password = cf.get('db', 'password')
db_db = cf.get('db', 'db')
log_dir = os.path.join(cur_dir, 'logs')
user_table = cf.get('jumpserver', 'user_table')
assets_table = cf.get('jumpserver', 'assets_table')
assets_user_table = cf.get('jumpserver', 'assets_user_table')
key = cf.get('jumpserver', 'key')


class PyCrypt(object):
    """It's used to encrypt and decrypt password."""
    def __init__(self, key):
        self.key = key
        self.mode = AES.MODE_CBC

    def encrypt(self, text):
        cryptor = AES.new(self.key, self.mode, b'0000000000000000')
        length = 16
        count = len(text)
        if count < length:
            add = (length - count)
            text += ('\0' * add)
        elif count > length:
            add = (length - (count % length))
            text += ('\0' * add)
        ciphertext = cryptor.encrypt(text)
        return b2a_hex(ciphertext)

    def decrypt(self, text):
        cryptor = AES.new(self.key, self.mode, b'0000000000000000')
        plain_text = cryptor.decrypt(a2b_hex(text))
        return plain_text.rstrip('\0')


def sigwinch_passthrough(sig, data):
    """This function use to set the window size of the terminal!"""
    winsize = getwinsize()
    try:
        foo.setwinsize(winsize[0], winsize[1])
    except:
        pass


def getwinsize():
    """This function use to get the size of the windows!"""
    if 'TIOCGWINSZ' in dir(termios):
        TIOCGWINSZ = termios.TIOCGWINSZ
    else:
        TIOCGWINSZ = 1074295912L  # Assume
    s = struct.pack('HHHH', 0, 0, 0, 0)
    x = fcntl.ioctl(sys.stdout.fileno(), TIOCGWINSZ, s)
    return struct.unpack('HHHH', x)[0:2]


def run_cmd(cmd):
    """run command and return stdout"""
    pipe = subprocess.Popen(cmd, 
                            shell=True, 
                            stdout=subprocess.PIPE, 
                            stderr=subprocess.PIPE)
    if pipe.stdout:
        stdout = pipe.stdout.read().strip()
        pipe.wait()
        return stdout
    if pipe.stderr:
        stderr = pipe.stderr.read()
        pipe.wait()
        return stderr


def connect(host, port, user, password):
    """Use pexpect module to connect other server."""
    log_date_dir = '%s/%s' % (log_dir, time.strftime('%Y%m%d'))
    if not os.path.isdir(log_date_dir):
        os.mkdir(log_date_dir)
        os.chmod(log_date_dir, 0777)
    structtime_start = time.localtime()
    datetime_start = time.strftime('%Y%m%d%H%M%S', structtime_start)
    logtime_start = time.strftime('%Y/%m/%d %H:%M:%S', structtime_start)
    timestamp_start = int(time.mktime(structtime_start))
    logfile_name = "%s/%s_%s_%s" % (log_date_dir, host, user, datetime_start)

    try:
        global foo
        foo = pxssh.pxssh()
        foo.login(host, user, password, port=port, auto_prompt_reset=False)
        log = Logs(user=user, host=host, logfile=logfile_name, start_time=timestamp_start, ppid=os.getpid())  # 日志信息记录到数据库
        log.save()
        pid = Pid(ppid=os.getpid(), cpid=foo.pid, start_time=timestamp_start, logid=log.id)
        pid.save()

        logfile = open(logfile_name, 'a')  # 记录日志文件
        logfile.write('\nDateTime:%s' % logtime_start)
        foo.logfile = logfile
        foo.sendline('')
        signal.signal(signal.SIGWINCH, sigwinch_passthrough)

        foo.interact(escape_character=chr(28))  # 进入交互模式
        logfile.write('\nEndTime: %s' % time.strftime('%Y/%m/%d %H:%M:%S'))
        log.finish = 1
        log.end_time = int(time.time())
        log.save()

    except pxssh.ExceptionPxssh as e:
        print('登录失败: %s' % e)
    except pexpect.EOF:
        print('登录失败: Host refuse')
    except KeyboardInterrupt:
        foo.logout()
        log.finish = 1
        log.end_time = int(time.time())
        log.save()


def ip_all_select(username):
    """select all the server of the user can control."""
    ip_all = []
    ip_all_dict = {}
    user = User.objects.get(username=username)
    all_assets_user = user.assetsuser_set.all()
    for assets_user in all_assets_user:
        ip_all.append(assets_user.aid.ip)
        ip_all_dict[assets_user.aid.ip] = assets_user.aid.comment

    return ip_all, ip_all_dict


def sth_select(username='', ip=''):
    """if username: return password elif ip return port"""
    if username:
        user = User.objects.get(username=username)
        ldap_password = user.ldap_password
        return ldap_password

    if ip:
        asset = Assets.objects.get(ip=ip)
        port = asset.port
        return port
    return None


def remote_exec_cmd(host, user, cmd):
    jm = PyCrypt(key)
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    port = sth_select(ip=host)
    password = jm.decrypt(sth_select(username=username))
    try:
        ssh.connect(host, port, user, password)
    except paramiko.AuthenticationException:
        print 'Password Error .'
        return None
    stdin, stdout, stderr = ssh.exec_command(cmd)
    print '\033[32m' + '#'*15 + ' ' + host + ' ' + '#'*15 + '\n' + '\033[0m'
    output = stdout.read()
    error = stderr.read()
    if output:
        print output
    if error:
        print error
    print '\033[32m' + '#'*15 + '  End result  ' + '#'*15 + '\n' + '\033[0m'
    ssh.close()


def match_ip(all_ip, string):
    ip_matched = []
    pattern = re.compile(r'%s' % string)

    for ip in all_ip:
        if pattern.search(ip):
            ip_matched.append(ip)
    return ip_matched


def print_prompt():
        print """
\033[1;32m###  Welcome Use JumpServer To Login. ### \033[0m
1) Type \033[32mIP ADDRESS\033[0m To Login.
2) Type \033[32mP/p\033[0m To Print The Servers You Available.
3) Type \033[32mE/e\033[0m To Execute Command On Several Servers.
4) Type \033[32mQ/q\033[0m To Quit.
"""


def print_your_server(username):
    ip_all, ip_all_dict = ip_all_select(username)
    for ip in ip_all:
        if ip_all_dict[ip]:
            print "%s -- %s" % (ip, ip_all_dict[ip])
        else:
            print ip


def exec_cmd_servers(username):
    print '\nInput the \033[32mHost IP(s)\033[0m,Separated by Commas, q/Q to Quit.\n'
    while True:
        hosts = raw_input('\033[1;32mip(s)>: \033[0m')
        if hosts in ['q', 'Q']:
            break
        hosts = hosts.split(',')
        hosts.append('')
        hosts = list(set(hosts))
        hosts.remove('')
        ip_all, ip_all_dict = ip_all_select(username)
        no_perm = set(hosts)-set(ip_all)
        if no_perm:
            print "You have NO PERMISSION on %s..." % list(no_perm)
            continue
        print '\nInput the \033[32mCommand\033[0m , The command will be Execute on servers, q/Q to quit.\n'
        while True:
            cmd = raw_input('\033[1;32mCmd(s): \033[0m')
            if cmd in ['q', 'Q']:
                break
            exec_log_dir = os.path.join(log_dir, 'exec_cmds')
            if not os.path.isdir(exec_log_dir):
                os.mkdir(exec_log_dir)
                os.chmod(exec_log_dir, 0777)
            filename = "%s/%s.log" % (exec_log_dir, time.strftime('%Y%m%d'))
            f = open(filename, 'a')
            f.write("DateTime: %s User: %s Host: %s Cmds: %s\n" %
                    (time.strftime('%Y/%m/%d %H:%M:%S'), username, hosts, cmd))
            for host in hosts:
                remote_exec_cmd(host, username, cmd)


def connect_one(username, option):
    ip_input = option.strip()
    ip_all, ip_all_dict = ip_all_select(username)
    ip_matched = match_ip(ip_all, ip_input)
    ip_len = len(ip_matched)
    ip = ''
    if ip_len >= 1:
        if ip_len == 1:
            ip = ip_matched[0]
        else:
            if ip_input in ip_matched:
                ip = ip_input
            else:
                for one_ip in ip_matched:
                    print one_ip
        if ip:
            password = jm.decrypt(sth_select(username=username))
            port = sth_select(ip=ip)
            print "Connecting %s ..." % ip
            connect(ip, port, username, password)
    else:
        print '\033[31mNo permision .\033[0m'


if __name__ == '__main__':
    username = run_cmd('whoami')
    jm = PyCrypt(key)
    print_prompt()
    try:
        while True:
            try:
                option = raw_input("\033[1;32mOpt or IP>:\033[0m ")
            except EOFError:
                print
                continue
            if option in ['P', 'p']:
                print_your_server(username)
                continue
            elif option in ['e', 'E']:
                exec_cmd_servers(username)
            elif option in ['q', 'Q']:
                sys.exit()
            else:
                connect_one(username, option)
    #except (BaseException, Exception):
    except IndexError:
        print "Exit."
        sys.exit()