python做業堡壘機(第十三週)

做業需求:python

1. 全部的用戶操做日誌要保留在數據庫中mysql

2. 每一個用戶登陸堡壘機後,只須要選擇具體要訪問的設置,就鏈接上了,不須要再輸入目標機器的訪問密碼ios

3. 容許用戶對不一樣的目標設備有不一樣的訪問權限,例:git

  對10.0.2.34 有mysql 用戶的權限web

  對192.168.3.22 有root用戶的權限redis

  對172.33.24.55 沒任何權限sql

4. 分組管理,便可以對設置進行分組,容許用戶訪問某組機器,但對組裏的不一樣機器依然有不一樣的訪問權限 shell

思路解析:數據庫

1.  用戶操做日誌要保留在數據庫中,經過課堂學習對paramiko源碼進行修改,在demons/interactive.py 63行中獲取用戶操做,並將操做記錄到數據庫中。windows

2.  後面的需求使用數據庫,創建多對多關聯,反向取主機IP,主機密碼,對應的堡壘機用戶,並劃分組內用戶權限 ,具體使用sqlalchemy模塊對數據庫表進行操做。

3. 針對做業需求,程序添加了查看日誌功能,並准許默認用戶root查看全部用戶操做,其餘用戶只能查本身下面機器的日誌。

4. 添加了緩存redis減小了數據庫IO操做。

paramiko 用戶操做記錄源碼:

cmd = []
        while True:
            r, w, e = select.select([chan, sys.stdin], [], [])  # 默認阻塞
            if chan in r:  # 鏈接創建好了,channle過來有數據了,
                try:
                    x = u(chan.recv(1024))  # 嘗試收數據
                    if len(x) == 0:  # 收數據收不到,
                        sys.stdout.write('\r\n*** EOF\r\n')
                        break
                    sys.stdout.write(x)  # 標準輸出
                    sys.stdout.flush()  # flush 怕輸出不到,遠程發來的數據,遠程機器返回
                except socket.timeout:
                    pass
            if sys.stdin in r:  # 標準輸入 活動就能返回到r
                x = sys.stdin.read(1)
                if len(x) == 0:
                    break
                if x == "\r":
                    cmd_str = "".join(cmd)
                    print("---->",cmd_str)
                    cmd = []
                else:
                    cmd.append(x)
                chan.send(x)
View Code  

表結構設計圖:

  

README:

做者:yaobin 版本: 堡壘機 示例版本 v0.1 開發環境: python3.6 程序介紹   1. 全部的用戶操做日誌要保留在數據庫中   2. 每一個用戶登陸堡壘機後,只須要選擇具體要訪問的設置,就鏈接上了,不須要再輸入目標機器的訪問密碼   3. 容許用戶對不一樣的目標設備有不一樣的訪問權限,例:     對10.0.2.34 有mysql 用戶的權限     對192.168.3.22 有root用戶的權限     對172.33.24.55 沒任何權限   4. 分組管理,便可以對設置進行分組,容許用戶訪問某組機器,但對組裏的不一樣機器依然有不一樣的訪問權限  文件目錄結構 ├── bin │   ├── __init__.py │   └── tiny.py # 主程序
├── conf │   ├── action_registers.py # 程序命令交互
│   ├── __init__.py │   ├── __pycache__ │   │   ├── action_registers.cpython-36.pyc │   │   ├── __init__.cpython-36.pyc │   │   └── settings.cpython-36.pyc │   └── settings.py # 配置文件
├── log │   └── __init__.py ├── models │   ├── __init__.py │   ├── models_backup.py # 備份測試
│   ├── models.py  # 數據庫表模塊
│   ├── __pycache__ │   │   ├── __init__.cpython-36.pyc │   │   └── models.cpython-36.pyc │   └── test.py # redis測試
├── modules │   ├── actions.py # 歡迎頁和程序命令交互
│   ├── common_filters.py  # 堡壘機用戶主機綁定交互
│   ├── db_conn.py  # mysql鏈接交互
│   ├── __init__.py │   ├── interactive.py # ssh傳輸命令和命令寫入交互
│   ├── __pycache__ │   │   ├── actions.cpython-36.pyc │   │   ├── common_filters.cpython-36.pyc │   │   ├── db_conn.cpython-36.pyc │   │   ├── __init__.cpython-36.pyc │   │   ├── interactive.cpython-36.pyc │   │   ├── ssh_login.cpython-36.pyc │   │   ├── utils.cpython-36.pyc │   │   └── views.cpython-36.pyc │   ├── ssh_login.py # ssh鏈接交互
│   ├── utils.py  # yaml配置交互
│   └── views.py  # 建立表,表數據建立,查看數據庫數據交互
├── Server.zip └── share └── examples ├── new_bindhosts.yml # 主機綁定關係配置文件
        ├── new_groups.yml  # 組建立,組關係綁定配置文件
        ├── new_hosts.yml  # 主機配置文件
        ├── new_remoteusers.yml  # 主機用戶名密碼配置文件
        └── new_user.yml  # 堡壘機用戶配置文件
View Code

建立表和使用方法:

先要建立數據庫:
 create database tinytest charset utf8;

1. python3 bin/tiny.py  syncdb
2. python3 bin/tiny.py create_hosts -f  share/examples/new_hosts.yml
3. python3 bin/tiny.py create_remoteusers -f share/examples/new_remoteusers.yml 
4. python3 bin/tiny.py create_users -f share/examples/new_user.yml 
5. python3 bin/tiny.py create_groups -f share/examples/new_groups.yml
6. python3 bin/tiny.py create_bindhosts -f share/examples/new_bindhosts.yml
7. python3 bin/tiny.py start_session
View Code

程序核心代碼:

bin

tiny.py

#!/usr/bin/env python # -*- coding:utf-8 -*- # Time:2017/12/15 21:22
__Author__ = 'Sean Yao'
import os import sys BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) print(BASE_DIR) sys.path.append(BASE_DIR) if __name__ == '__main__': from modules.actions import excute_from_command_line excute_from_command_line(sys.argv)
View Code

conf

action_registers

#!/usr/bin/env python # -*- coding:utf-8 -*- # Time:2017/12/14 18:53
__Author__ = 'Sean Yao'
from modules import views actions = { 'start_session': views.start_session,  # 鏈接server
    # 'stop': views.stop_server,
    'syncdb': views.syncdb,  # 同步數據
    'create_users': views.create_users,  # 建立users
    'create_groups': views.create_groups,  # 建立組
    'create_hosts': views.create_hosts,  # 建立主機
    'create_bindhosts': views.create_bindhosts,  # 建立綁定關係
    'create_remoteusers': views.create_remoteusers,  # 建立遠程用戶
    'view_user_record': views.user_record_cmd  # 查看用戶操做命令
}
View Code

settings.py

#!/usr/bin/env python # -*- coding:utf-8 -*- # Time:2017/12/14 18:53
__Author__ = 'Sean Yao'
# 鏈接數據庫字段 # ConnParams = "mysql+pymysql://root:123456@192.168.84.66/tinydb?charset=utf8"
ConnParams = "mysql+pymysql://root:123456@192.168.84.66/tinytest?charset=utf8"
View Code

models

models.py

#!/usr/bin/env python # -*- coding:utf-8 -*- # Time:2017/12/14 19:06
__Author__ = 'Sean Yao'
import datetime from sqlalchemy import Table, Column, Integer, String, DATE, ForeignKey, Enum, UniqueConstraint, DateTime, Text # uniqueconstraint 聯合惟一
from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declarative_base from sqlalchemy_utils import ChoiceType, PasswordType  # sqlalchemy_utils sqalchemy_utils插件
from sqlalchemy import create_engine # from sqlalchemy.orm import sessionmaker
 Base = declarative_base()  # 基類

# 多對多關聯 # 關聯表堡壘機用戶ID和遠程主機ID
user_m2m_bindhost = Table('user_m2m_bindhost', Base.metadata, Column('userprofile_id', Integer, ForeignKey('user_profile.id')), Column('bind_host_id', Integer, ForeignKey('bind_host.id')),) # 關聯表遠程主機ID和組
bindhost_m2m_hostgroup = Table('bindhost_m2m_hostgroup', Base.metadata, Column('bindhost_id', Integer, ForeignKey('bind_host.id')), Column('hostgroup_id', Integer, ForeignKey('host_group.id')),) # 關聯表堡壘機用戶和組
user_m2m_hostgroup = Table('userprofile_m2m_hostgroup', Base.metadata, Column('userprofile_id', Integer, ForeignKey('user_profile.id')), Column('hostgroup_id', Integer, ForeignKey('host_group.id')),) class BindHost(Base): ''' 關聯關係 192.168.1.11 web 192.168.1.11 mysql '''
    __tablename__ = "bind_host"
    # 聯合惟一
    __table_args__ = (UniqueConstraint('host_id', 'remoteuser_id', name='_host_remoteuser_uc'),) id = Column(Integer, primary_key=True) # 外鍵
    host_id = Column(Integer, ForeignKey('host.id')) remoteuser_id = Column(Integer, ForeignKey('remote_user.id')) # 外鍵關聯遠程主機,反響查綁定的主機
    host = relationship('Host', backref='bind_hosts') # 外鍵關聯堡壘機用戶,backref,反向查綁定的堡壘機用戶
    remote_user = relationship("RemoteUser", backref='bind_hosts') def __repr__(self): # return "<%s -- %s -- %s>" % (self.host.ip,
        # self.remote_user.username,
        # self.host_group.name)

        return "<%s -- %s >" % (self.host.ip, self.remote_user.username) class Host(Base): ''' 遠程主機 '''
    __tablename__ = 'host' id = Column(Integer, primary_key=True) hostname = Column(String(64), unique=True) ip = Column(String(64), unique=True) port = Column(Integer, default=22) # 不要讓主機關聯主機組,這樣權限給主機組了,應該是將用戶密碼和主機組綁定,
    # 好比root 123 sh root 123 bj 這樣他能夠用全部的權限,

    def __repr__(self): return self.hostname class HostGroup(Base): ''' 遠程主機組 '''
    __tablename__ = 'host_group' id = Column(Integer, primary_key=True) name = Column(String(64), unique=True) # 經過bindhost_m2m_hostgroup 關聯綁定主機和主機組反查到主機組
    bind_hosts = relationship("BindHost", secondary="bindhost_m2m_hostgroup", backref="host_groups") def __repr__(self): return self.name class RemoteUser(Base): ''' 遠程主機密碼錶 '''
    __tablename__ = 'remote_user'
    # 聯合惟一,驗證類型,用戶名密碼
    __table_args__ = (UniqueConstraint('auth_type', 'username', 'password', name='_user_passwd_uc'),) id = Column(Integer, primary_key=True) AuthTypes = [ ('ssh-password', 'SSH/Password'),  # 第一個是存在數據庫裏的,第二個具體的值
        ('ssh-key', 'SSH/KEY') ] auth_type = Column(ChoiceType(AuthTypes)) username = Column(String(32)) password = Column(String(128)) def __repr__(self): return self.username class Userprofile(Base): ''' 堡壘機用戶密碼錶 '''
    __tablename__ = 'user_profile' id = Column(Integer, primary_key=True) username = Column(String(32), unique=True) password = Column(String(128)) # 多對多關聯經過user_m2m_bindhost關聯堡壘機表和主機表能反查到堡壘機用戶
    bind_hosts = relationship("BindHost", secondary='user_m2m_bindhost', backref='user_profiles') # 多對多關聯經過userprofile_m2m_hostgroup關聯堡壘機表和組反查到堡壘機用戶
    host_groups = relationship("HostGroup", secondary='userprofile_m2m_hostgroup', backref='user_profiles') def __repr__(self): return self.username class AuditLog(Base): ''' 用戶操做日誌表 '''
    __tablename__ = 'audit_log' id = Column(Integer, primary_key=True) user_id = Column(Integer, ForeignKey('user_profile.id')) bind_host_id = Column(Integer, ForeignKey('bind_host.id')) # # action_choices
    # action_choices = [
    # (0, 'CMD'),
    # (1, 'Login'),
    # (2, 'Logout'),
    # (3, 'GetFile'),
    # (4, 'SendFile'),
    # (5, 'Exception'),
    # ]
    action_choices = [ (u'cmd', u'CMD'), (u'login', u'Login'), (u'logout', u'Logout'), ] action_type = Column(ChoiceType(action_choices)) # 命令可能存的數值更大
    # cmd = Column(String(255))
    cmd = Column(Text(65535)) date = Column(DateTime) user_profile = relationship("Userprofile") bind_host = relationship("BindHost")
View Code

modules

actions.py

#!/usr/bin/env python # -*- coding:utf-8 -*- # Time:2017/12/15 21:31
__Author__ = 'Sean Yao'

from conf import action_registers from modules import utils def help_msg(): ''' print help msgs :return: '''
    print("\033[31;1mAvailable commands:\033[0m") for key in action_registers.actions: print("\t", key) def excute_from_command_line(argvs): ''' print :param argvs: :return: '''
    if len(argvs) < 2: help_msg() exit() if argvs[1] not in action_registers.actions: utils.print_err("Command [%s] does not exist!" % argvs[1], quit=True) # utils 工具箱
    action_registers.actions[argvs[1]](argvs[1:])
View Code

common_filters.py

#!/usr/bin/env python # -*- coding:utf-8 -*- # Time:2017/12/18 18:19
__Author__ = 'Sean Yao'
from models import models from modules.db_conn import engine, session from modules.utils import print_err def bind_hosts_filter(vals): ''' :param vals: :return: '''
    print('**>', vals.get('bind_hosts')) bind_hosts = session.query(models.BindHost).filter(models.Host.hostname.in_(vals.get('bind_hosts'))).all() if not bind_hosts: print_err("none of [%s] exist in bind_host table." % vals.get('bind_hosts'), quit=True) return bind_hosts def user_profiles_filter(vals): ''' :param vals: :return: ''' user_profiles = session.query(models.Userprofile).filter(models.Userprofile.username.in_(vals.get('user_profiles')) ).all() if not user_profiles: print_err("none of [%s] exist in user_profile table." % vals.get('user_profiles'), quit=True) return user_profiles
View Code

db_conn.py

#!/usr/bin/env python # -*- coding:utf-8 -*- # Time:2017/12/15 23:21
__Author__ = 'Sean Yao'
from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from conf import settings engine = create_engine(settings.ConnParams) # 建立與數據庫的會話session class ,注意,這裏返回給session的是個class,不是實例
SessionCls = sessionmaker(bind=engine) session = SessionCls()
View Code

interactive.py

# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com> # # This file is part of paramiko. # # Paramiko is free software; you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation; either version 2.1 of the License, or (at your option) # any later version. # # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with Paramiko; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.


import socket import sys from paramiko.py3compat import u from models import models # from modules.views import log_recording
import datetime import redis import time # windows does not have termios...
try: import termios import tty has_termios = True except ImportError: has_termios = False def interactive_shell(chan, user_obj, bind_host_obj, cmd_caches, log_recording): ''' :param chan: :param user_obj: :param bind_host_obj: 主機 :param cmd_caches: 命令列表 :param log_recording: 日誌記錄 :return: '''
    # 判斷是不是windows shell
    if has_termios: posix_shell(chan, user_obj, bind_host_obj, cmd_caches, log_recording) else: windows_shell(chan) def posix_shell(chan, user_obj, bind_host_obj, cmd_caches, log_recording): ''' :param chan: :param user_obj: :param bind_host_obj: :param cmd_caches: :param log_recording: :return: '''
    import select oldtty = termios.tcgetattr(sys.stdin) try: tty.setraw(sys.stdin.fileno()) tty.setcbreak(sys.stdin.fileno()) chan.settimeout(0.0) cmd = '' tab_key = False while True: r, w, e = select.select([chan, sys.stdin], [], []) if chan in r: try: x = u(chan.recv(1024)) if tab_key: if x not in ('\x07', '\r\n'): # print('tab:',x)
                            cmd += x tab_key = False if len(x) == 0: sys.stdout.write('\r\n*** EOF\r\n') # test for redis to mysql
                        break sys.stdout.write(x) sys.stdout.flush() except socket.timeout: pass
            if sys.stdin in r: x = sys.stdin.read(1) if '\r' != x: cmd += x else: user_record_cmd = user_obj.username + '_user_record' pool = redis.ConnectionPool(host='192.168.84.66', port=6379) user_record = [user_obj.id, bind_host_obj.id, 'cmd', cmd, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())] r = redis.Redis(connection_pool=pool) r.lpush(user_record_cmd, user_record) cmd = ''
                    # 最後用戶退出的時候取出來log_item 列表循環寫入數據庫
                if '\t' == x: tab_key = True if len(x) == 0: break chan.send(x) finally: termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty) # thanks to Mike Looijmans for this code
def windows_shell(chan): ''' :param chan: :return: '''
    import threading sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.\r\n\r\n") def writeall(sock): while True: data = sock.recv(256) if not data: sys.stdout.write('\r\n*** EOF ***\r\n\r\n') sys.stdout.flush() break sys.stdout.write(data.decode()) sys.stdout.flush() writer = threading.Thread(target=writeall, args=(chan,)) writer.start() try: while True: d = sys.stdin.read(1) if not d: break chan.send(d) except EOFError: # user hit ^Z or F6
        pass
View Code

ssh_login.py

#!/usr/bin/env python # -*- coding:utf-8 -*- # Time:2017/12/17 9:54
__Author__ = 'Sean Yao'
import base64 import getpass import os import socket import sys import traceback from paramiko.py3compat import input from models import models import redis import datetime import time import paramiko try: import interactive except ImportError: from . import interactive def ssh_login(user_obj, bind_host_obj, mysql_engine, log_recording): ''' ssh登錄 :param user_obj: :param bind_host_obj: :param mysql_engine: 鏈接數據庫 :param log_recording: 寫日誌記錄 :return: '''
    # now, connect and use paramiko Client to negotiate SSH2 across the connection
    try: client = paramiko.SSHClient() client.load_system_host_keys() client.set_missing_host_key_policy(paramiko.WarningPolicy()) print('*** Connecting...') client.connect(bind_host_obj.host.ip, bind_host_obj.host.port, bind_host_obj.remote_user.username, bind_host_obj.remote_user.password, timeout=30) cmd_caches = [] chan = client.invoke_shell() # print(repr(client.get_transport()))
        print('*** Here we go!\n') # 鏈接redis
        pool = redis.ConnectionPool(host='192.168.84.66', port=6379) # 傳一個命令列表給redis
        user_record = [user_obj.id, bind_host_obj.id, 'login', time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())] r = redis.Redis(connection_pool=pool) # 用用戶名作key前綴,避免衝突
        key_name = str(user_obj.username)+'_login' r.lpush(key_name, user_record) interactive.interactive_shell(chan, user_obj, bind_host_obj, cmd_caches, log_recording) chan.close() client.close() # 數據庫寫入操做
        login_record = r.lrange(key_name, 0, -1) login_redis_record = login_record[0].decode().replace('[', '').replace(']', '').split(',') log_item = models.AuditLog(user_id=login_redis_record[0], bind_host_id=login_redis_record[1], action_type='login', cmd='login', date=login_redis_record[3].replace("'", '')) cmd_caches.append(log_item) log_recording(user_obj, bind_host_obj, cmd_caches) user_record_cmd = user_obj.username+'_user_record' cmd_redis_record = r.lrange(user_record_cmd, 0, -1) for i in cmd_redis_record: cmd_caches = [] v = i.decode().replace('[', '').replace(']', '').split(',') v2 = v[3].replace("'", '') # print(v[0], v[1], v[2], v[3], v[4])
            log_item = models.AuditLog(user_id=v[0], bind_host_id=v[1], action_type='cmd', cmd=v2, date=v[4].replace("'", '')) cmd_caches.append(log_item) log_recording(user_obj, bind_host_obj, cmd_caches) # 當退出的時候將redis的值寫入到數據庫而且清空redis
        logout_caches = [] logout_caches.append(models.AuditLog(user_id=user_obj.id, bind_host_id=bind_host_obj.id, action_type='logout', cmd='logout', date=datetime.datetime.now())) log_recording(user_obj, bind_host_obj, logout_caches) # 清空keys
 r.delete(key_name) r.delete(user_record_cmd) except Exception as e: print('*** Caught exception: %s: %s' % (e.__class__, e)) traceback.print_exc() try: client.close() except: pass sys.exit(1)
View Code

utils.py

#!/usr/bin/env python # -*- coding:utf-8 -*- # Time:2017/12/15 21:48
__Author__ = 'Sean Yao'
import yaml try: from yaml import CLoader as Loader, CDumper as Dumper except ImportError: from yaml import Loader, Dumper def print_err(msg, quit=False): ''' :param msg: :param quit: :return: ''' output = "\033[31;1mError: %s\033[0m" % msg if quit: exit(output) else: print(output) def yaml_parser(yml_filename): ''' yaml方法load yaml file and return :param yml_filename: :return: '''
    try: yaml_file = open(yml_filename, 'r') data = yaml.load(yaml_file) return data except Exception as e: print_err(e)
View Code

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Time:2017/12/15 21:34
__Author__ = 'Sean Yao'
from models import models
from conf import settings
from modules.utils import print_err, yaml_parser
from modules.db_conn import engine, session
from modules import ssh_login
from modules import common_filters
import codecs

def syncdb(argvs):
    '''
    建立表結構方法
    :param argvs:
    :return:
    '''
    print("Syncing DB....")
    engine = models.create_engine(settings.ConnParams, echo=True)
    models.Base.metadata.create_all(engine)  # 建立全部表結構

def create_hosts(argvs):
    '''
    create 主機
    :param argvs:
    :return:
    '''
    if '-f' in argvs:
        # 指定一個文件名不然報錯
        hosts_file = argvs[argvs.index("-f") +1]
    else:
        print_err("invalid usage, should be:\ncreate_hosts -f <the new hosts file>", quit=True)
    source = yaml_parser(hosts_file)  # 傳文件回來
    if source:  # 循環字典
        print(source)
        for key, val in source.items():
            print(key, val)
            obj = models.Host(hostname=key, ip=val.get('ip'), port=val.get('port') or 22)
            # 添加到表
            session.add(obj)
        session.commit()

def create_remoteusers(argvs):
    '''
    create 堡壘機用戶
    :param argvs:
    :return:
    '''
    if '-f' in argvs:
        remoteusers_file = argvs[argvs.index("-f") +1]
    else:
        print_err("invalid usage, should be:\ncreate_remoteusers -f <the new remoteusers file>", quit=True)
    source = yaml_parser(remoteusers_file)
    if source:
        for key, val in source.items():
            print(key, val)
            obj = models.RemoteUser(username=val.get('username'), auth_type=val.get('auth_type'),
                                    password=val.get('password'))
            session.add(obj)
        session.commit()

def create_users(argvs):
    '''
    create little_finger access user
    :param argvs:
    :return:
    '''
    if '-f' in argvs:
        user_file = argvs[argvs.index("-f") +1 ]
    else:
        print_err("invalid usage, should be:\ncreateusers -f <the new users file>",quit=True)

    source = yaml_parser(user_file)
    if source:
        for key, val in source.items():
            print(key, val)
            obj = models.Userprofile(username=key, password=val.get('password'))
            if val.get('groups'):
                groups = session.query(models.HostGroup).filter(models.HostGroup.name.in_(val.get('groups'))).all()
                if not groups:
                    print_err("none of [%s] exist in group table." % val.get('groups'), quit=True)
                obj.groups = groups
            if val.get('bind_hosts'):
                bind_hosts = common_filters.bind_hosts_filter(val)
                obj.bind_hosts = bind_hosts
            #print(obj)
            session.add(obj)
        session.commit()

def create_groups(argvs):
    '''
    create groups
    :param argvs:
    :return:
    '''
    if '-f' in argvs:
        group_file = argvs[argvs.index("-f") + 1]
    else:
        print_err("invalid usage, should be:\ncreategroups -f <the new groups file>", quit=True)
    source = yaml_parser(group_file)
    if source:
        for key, val in source.items():
            print(key, val)
            obj = models.HostGroup(name=key)
            if val.get('bind_hosts'):
                bind_hosts = common_filters.bind_hosts_filter(val)
                obj.bind_hosts = bind_hosts

            if val.get('user_profiles'):
                user_profiles = common_filters.user_profiles_filter(val)
                obj.user_profiles = user_profiles
            session.add(obj)
        session.commit()

def create_bindhosts(argvs):
    '''
    create bind hosts
    :param argvs:
    :return:
    '''
    if '-f' in argvs:
        bindhosts_file = argvs[argvs.index("-f") + 1]
    else:
        print_err("invalid usage, should be:\ncreate_hosts -f <the new bindhosts file>",quit=True)
    source = yaml_parser(bindhosts_file)
    if source:
        for key, val in source.items():
            print(key, val)
            # 獲取到了主機
            host_obj = session.query(models.Host).filter(models.Host.hostname == val.get('hostname')).first()
            # 取hostname
            assert host_obj  # 斷言,必須存在
            for item in val['remote_users']:  # 判斷
                print(item)
                assert item.get('auth_type')
                if item.get('auth_type') == 'ssh-password':  # 判斷認證password
                    remoteuser_obj = session.query(models.RemoteUser).filter(
                                                        models.RemoteUser.username == item.get('username'),
                                                        models.RemoteUser.password == item.get('password')
                                                    ).first()
                else:
                    # 獲取遠程用戶
                    remoteuser_obj = session.query(models.RemoteUser).filter(
                                                        models.RemoteUser.username == item.get('username'),
                                                        models.RemoteUser.auth_type == item.get('auth_type'),
                                                    ).first()
                if not remoteuser_obj:  # 沒取到,程序退出
                    print_err("RemoteUser obj %s does not exist." % item, quit=True)
                bindhost_obj = models.BindHost(host_id=host_obj.id, remoteuser_id=remoteuser_obj.id)
                session.add(bindhost_obj)  # 獲取到關係後添加session
                # for groups this host binds to
                if source[key].get('groups'):  # 獲取組
                    group_objs = session.query(models.HostGroup).filter(models.HostGroup.name.in_
                                                                        (source[key].get('groups'))).all()
                    assert group_objs
                    print('groups:', group_objs)
                    bindhost_obj.host_groups = group_objs
                # for user_profiles this host binds to
                if source[key].get('user_profiles'):  # 判斷是否直接屬於哪一臺機器
                    userprofile_objs = session.query(models.Userprofile).filter(models.Userprofile.username.in_(
                        source[key].get('user_profiles')
                    )).all()
                    assert userprofile_objs
                    print("userprofiles:", userprofile_objs)
                    bindhost_obj.user_profiles = userprofile_objs
                # print(bindhost_obj)
        session.commit()

def auth():
    '''
    用戶驗證
    do the user login authentication
    :return:
    '''
    count = 0
    while count < 3:
        username = input("\033[32;1mUsername:\033[0m").strip()
        if len(username) == 0:
            continue
        password = input("\033[32;1mPassword:\033[0m").strip()
        if len(password) == 0:
            continue
        user_obj = session.query(models.Userprofile).filter(models.Userprofile.username == username,
                                                            models.Userprofile.password == password).first()
        if user_obj:
            return user_obj
        else:
            print("wrong username or password, you have %s more chances." % (3-count-1))
            count += 1
    else:
        print_err("too many attempts.")

def welcome_msg(user):
    WELCOME_MSG = '''\033[32;1m
    ------------- Welcome [%s] login TinyServer -------------
    \033[0m''' % user.username
    print(WELCOME_MSG)

def start_session(argvs):
    print('going to start sesssion ')
    user = auth()
    if user:
        welcome_msg(user)
        # print(user.bind_hosts)
        # print(user.host_groups)
        exit_flag = False
        while not exit_flag:
            if user.bind_hosts:
                # 顯示未分組的機器
                print('\033[32;1mz.\tungroupped hosts (%s)\033[0m' % len(user.bind_hosts))
            for index, group in enumerate(user.host_groups):
                print('\033[32;1m%s.\t%s (%s)\033[0m' % (index, group.name, len(group.bind_hosts)))
            # 用戶輸入
            choice = input("[%s]:" % user.username).strip()
            if len(choice) == 0:
                continue
            # 若是是z 打印未分組機器
            if choice == 'z':
                print("------ Group: ungroupped hosts ------")
                for index, bind_host in enumerate(user.bind_hosts):
                    print("  %s.\t%s@%s(%s)" % (index,
                                                bind_host.remote_user.username,
                                                bind_host.host.hostname,
                                                bind_host.host.ip,
                                                ))
                print("----------- END -----------")
            elif choice.isdigit():  # 打印分組的機器
                choice = int(choice)
                if choice < len(user.host_groups):
                    print("------ Group: %s ------" % user.host_groups[choice].name)
                    for index, bind_host in enumerate(user.host_groups[choice].bind_hosts):
                        print("  %s.\t%s@%s(%s)" % (index,
                                                    bind_host.remote_user.username,
                                                    bind_host.host.hostname,
                                                    bind_host.host.ip,
                                                    ))
                    print("----------- END -----------")

                    # host selection 選擇機器去登錄
                    while not exit_flag:
                        user_option = input("[(b)back, (q)quit, select host to login]:").strip()
                        if len(user_option) == 0:
                            continue
                        if user_option == 'b':
                            break
                        if user_option == 'q':
                            exit_flag = True
                        if user_option.isdigit():
                            user_option = int(user_option)
                            if user_option < len(user.host_groups[choice].bind_hosts):
                                print('host:', user.host_groups[choice].bind_hosts[user_option])
                                # print('audit log:', user.host_groups[choice].bind_hosts[user_option].audit_logs)
                                ssh_login.ssh_login(user,  # 傳用戶,用戶組,連上對應的
                                                    user.host_groups[choice].bind_hosts[user_option],
                                                    session, log_recording)
                else:
                    print("no this option..")

def log_recording(user_obj, bind_host_obj, logs):
    '''
    flush user operations on remote host into DB
    :param user_obj:
    :param bind_host_obj:
    :param logs: list format [logItem1,logItem2,...]
    :return:
    '''
    # print("\033[41;1m--logs:\033[0m", logs)
    session.add_all(logs)
    session.commit()
def user_record_cmd(argvs):
    '''
    查看操做記錄方法
    :param argvs:
    :return:
    '''
    print('going to start view record')
    user = auth()
    # 默認root能夠查全部人的記錄
    if user.username == 'root':
        print('welcome %s ' % user.username)
        exit_flag = False
        # 用戶對象
        user_obj = session.query(models.Userprofile).filter().all()
        # 循環查看堡壘機用戶操做
        while not exit_flag:
            for user_profile_list in user_obj:
                # 打印堡壘機用戶,根據堡壘機用戶ID選擇其管轄的機器並打印日誌
                print("%s.\t%s" % (user_profile_list.id, user_profile_list.username))
            choice = input("[%s]:" % user.username).strip()
            for user_profile_list in user_obj:
                if str(choice) == str(user_profile_list.id):
                    if user_profile_list.bind_hosts:
                        # 顯示未分組的機器
                        print('\033[32;1mz.\tungroupped hosts (%s)\033[0m' % len(user_profile_list.bind_hosts))
                    else:
                        print(' no binding groups ')
                    for index, group in enumerate(user_profile_list.host_groups):
                        print('\033[32;1m%s.\t%s (%s)\033[0m' % (index, group.name, len(group.bind_hosts)))
                    choice = input("[%s]:" % user.username).strip()
                    if choice.isdigit():  # 打印分組的機器
                        choice = int(choice)
                        if choice < len(user_profile_list.host_groups):
                            print("------ Group: %s ------" % user_profile_list.host_groups[choice].name)
                            for index, bind_host in enumerate(user_profile_list.host_groups[choice].bind_hosts):
                                print("  %s.\t%s@%s(%s)" % (index,
                                                            bind_host.remote_user.username,
                                                            bind_host.host.hostname,
                                                            bind_host.host.ip,
                                                            ))
                            print("----------- END -----------")
                            # host selection 選擇機器去查看操做信息
                            while not exit_flag:
                                user_option = input("[(b)back, (q)quit, select host to login]:").strip()
                                if len(user_option) == 0:
                                    continue
                                if user_option == 'b':
                                    break
                                if user_option == 'q':
                                    exit_flag = True
                                if user_option.isdigit():
                                    user_option = int(user_option)
                                    if user_option < len(user_profile_list.host_groups[choice].bind_hosts):
                                        # print('host:', user_profile_list.host_groups[choice].bind_hosts[user_option])
                                        data = \
                                            session.query(models.AuditLog).filter(
                                                models.AuditLog.user_id == user_profile_list.id,
                                                models.AuditLog.bind_host_id == user_profile_list.host_groups[choice].
                                                bind_hosts[user_option].id).all()
                                        if data:
                                            for index, i in enumerate(data):
                                                # redis 寫入value的時候帶有了\t \n 等須要轉義
                                                # 第一個註釋從數據庫裏讀註釋的這種不能轉移\t,
                                                # 第二個和現行的倆種中文轉義有些問題
                                                # print(i.user_id, i.bind_host_id, i.action_type, i.cmd, i.date)
                                                # print(i.user_id, i.bind_host_id, i.action_type,
                                                #        codecs.getdecoder("unicode_escape")(i.cmd)[0], i.date)
                                                # print(i.user_id, i.bind_host_id, i.action_type,
                                                #       i.cmd.encode().decode('unicode-escape'), i.date)
                                                print(index, i.date, i.cmd.encode().decode('unicode-escape'))
                                        else:
                                            print('no record in host:', user_profile_list.host_groups[choice].
                                                  bind_hosts[user_option])
    # 其餘人只能查本身的操做記錄
    else:
        exit_flag = False
        while not exit_flag:
            if user.bind_hosts:
                # 顯示未分組的機器
                print('\033[32;1mz.\tungroupped hosts (%s)\033[0m' % len(user.bind_hosts))
            for index, group in enumerate(user.host_groups):
                print('\033[32;1m%s.\t%s (%s)\033[0m' % (index, group.name, len(group.bind_hosts)))
            choice1 = input("[%s]:" % user.username).strip()
            # 查詢選項
            if choice1 == 'z':
                print("------ Group: ungroupped hosts ------")
                for index, bind_host in enumerate(user.bind_hosts):
                    print("  %s.\t%s@%s(%s)" % (index,
                                                bind_host.remote_user.username,
                                                bind_host.host.hostname,
                                                bind_host.host.ip,
                                                ))
                print("----------- END -----------")
            elif choice1.isdigit():  # 打印分組的機器
                choice = int(choice1)
                if choice < len(user.host_groups):
                    print("------ Group: %s ------" % user.host_groups[choice].name)
                    for index, bind_host in enumerate(user.host_groups[choice].bind_hosts):
                        print("  %s.\t%s@%s(%s)" % (index,
                                                    bind_host.remote_user.username,
                                                    bind_host.host.hostname,
                                                    bind_host.host.ip,
                                                    ))
                    print("----------- END -----------")

                    # host selection 選擇機器去查看操做信息
                    while not exit_flag:
                        user_option = input("[(b)back, (q)quit, select host to view record]:").strip()
                        if len(user_option) == 0:
                            continue
                        if user_option == 'b':
                            break
                        if user_option == 'q':
                            exit_flag = True
                        if user_option.isdigit():
                            user_option = int(user_option)
                            if user_option < len(user.host_groups[choice].bind_hosts):
                                data = session.query(models.AuditLog)\
                                    .filter(models.AuditLog.user_id == user.id,
                                            models.AuditLog.bind_host_id == user.host_groups[choice].
                                            bind_hosts[user_option].id).all()
                                # print(user.host_groups[choice].bind_hosts[user_option].id)
                                if data:
                                    for index, i in enumerate(data):
                                        print(index, i.date, i.cmd.encode().decode('unicode-escape'))
                                else:
                                    print('no record in host:', user.host_groups[choice].bind_hosts[user_option])
                else:
                    print("no this option..")
View Code

share

new_bindhosts.yml

bind1: hostname: server1 remote_users: - user0: username: root auth_type: ssh-password password: 123456 groups: - bj_group user_profiles: - sean bind2: hostname: server2 remote_users: - user0: username: root auth_type: ssh-password password: 123456 groups: - bj_group - sh_group user_profiles: - sean - jack bind3: hostname: server3 remote_users: - user0: username: root auth_type: ssh-password password: 123456 groups: - bj_group - sh_group user_profiles: - sean - jack bind4: hostname: server2 remote_users: - user2: username: colin auth_type: ssh-password password: 123@123 groups: - web_servers user_profiles: - root bind5: hostname: server3 remote_users: - user3: username: web auth_type: ssh-password password: 12345678
    - user1: username: mysql auth_type: ssh-password password: 12345678 groups: - web_servers - db_servers user_profiles: - root
View Code

new_groups.yml

bj_group: user_profiles: - sean sh_group: user_profiles: - jack db_servers: user_profiles: - root web_servers: user_profiles: - root
View Code

new_hosts.yml

server1: ip: 192.168.84.66 port: 12321 server2: ip: 192.168.84.67 port: 12321 server3: ip: 192.168.84.68 port: 12321
View Code

new_remoteusers.yml

user0: auth_type: ssh-password username: root password: 123456 user1: auth_type: ssh-password username: mysql password: 12345678 user2: auth_type: ssh-password username: colin password: 123@123 user3: auth_type: ssh-password username: web password: 12345678 user4: auth_type: ssh-key username: root
View Code

new_user.yml

root: password: 123@456 sean: password: 123456 jack: password: 123456
View Code

程序測試樣圖:

1. 建立表和插入表數據

2. 查看綁定關係

3. 登錄和寫入命令

4. 不一樣用戶權限,組權限,登錄權限和查看日誌權限

相關文章
相關標籤/搜索