• 二次开发Jumpserver实现主备,实现user&key,系统用户&key的同步


      这是jumpserver二次开发系列第三篇 ,前两篇是关于用户认证模块的,调用现有的认证接口认证并获取用户信息。

      此篇是关于如何实现双机热备,要实现互备,就要确保用户及系统用户信息不只同步到另外一台数据库,还需要把用户及系统用户的秘钥信息同步到另外一台服务器,并创建用户,当然删除也需要同步。

           ps:在原代码基础上添加的代码,在每个代码框内用绿色背景斜体标记,否则为整体添加。

    一、修改setting,增加server_type配置,从jumpserver.conf配置文件读取主备服务器信息。

    # master & slave host type
    HOST_TYPE = config.get('server_type', 'host_type')
    MASTER_HOST = config.get('server_type', 'master_host')
    SLAVE_HOST = config.get('server_type', 'slave_host')

    二、修改 install.py,以便安装时输入主备服务器信息,并写入配置文件jumpserver.conf

    class PreSetup(object):
        def __init__(self):
            self.db_host = '127.0.0.1'
            self.db_port = 3306
            self.db_user = 'jumpserver'
            self.db_pass = '5Lov@wife'
            self.db = 'jumpserver'
            self.mail_host = 'smtp.qq.com'
            self.mail_port = 25
            self.mail_addr = 'hello@jumpserver.org'
            self.mail_pass = ''
            self.host_type = 'master'
            self.master_host = '192.168.3.85'
            self.slave_host = '192.168.3.86'
        def _input_server_type(self):
            while True:
                self.host_type = raw_input('请输入服务器类型master或者slave:').strip()
                self.master_host = raw_input('请输入主服务器IP:').strip()
                self.slave_host = raw_input('请输入从服务器IP:').strip()
                print
                break
        def start(self):
            color_print('请务必先查看手册')
            time.sleep(3)
            self.check_platform()
            self._rpm_repo()
            self._depend_rpm()
            self._require_pip()
            self._set_env()
            self._input_ip()
            self._input_mysql()
            self._input_smtp()
            self._input_server_type()
            self.write_conf()
            os.system('python %s' % os.path.join(jms_dir, 'install/next.py'))
        def write_conf(self, conf_file=os.path.join(jms_dir, 'jumpserver.conf')):
            color_print('开始写入配置文件', 'green')
            conf = ConfigParser.ConfigParser()
            conf.read(conf_file)
            conf.set('base', 'url', 'http://%s' % self.ip)
            conf.set('base', 'key', self.key)
            conf.set('db', 'host', self.db_host)
            conf.set('db', 'port', self.db_port)
            conf.set('db', 'user', self.db_user)
            conf.set('db', 'password', self.db_pass)
            conf.set('db', 'database', self.db)
            conf.set('mail', 'email_host', self.mail_host)
            conf.set('mail', 'email_port', self.mail_port)
            conf.set('mail', 'email_host_user', self.mail_addr)
            conf.set('mail', 'email_host_password', self.mail_pass)
            conf.set('server_type', 'host_type', self.host_type)
            conf.set('server_type', 'master_host', self.master_host)
            conf.set('server_type', 'slave_host', self.slave_host)
            with open(conf_file, 'w') as f:
                conf.write(f)

    三、修改juser下面的user_api.py 实现远程在另外一台服务器上创建用户和同步秘钥

    定义ssh远程登录、执行函数
    from jumpserver.settings import HOST_TYPE, MASTER_HOST, SLAVE_HOST
    import paramiko
    
    
    def ssh_login(cmd):
        """
        定义ssh远程登录、执行函数
        """
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        if 'master' == HOST_TYPE.lower():
            ssh.connect(SLAVE_HOST, 22, 'root', key_filename='/root/.ssh/id_rsa')
        else:
            ssh.connect(MASTER_HOST, 22, 'root', key_filename='/root/.ssh/id_rsa')
        stdin, stdout, stderr = ssh.exec_command(cmd)
        ssh.close()

    修改函数gen_ssh_key  ,同步key前先判定key目录是否存在,如不存在先创建

    def gen_ssh_key(username, password='', key_dir=os.path.join(KEY_DIR, 'user'),authorized_keys=True, home="/home", length=2048):
        """    generate a user ssh key in a property dir    生成一个用户ssh密钥对    """
        logger.debug('生成ssh key, 并设置authorized_keys')
        private_key_file = os.path.join(key_dir, username+'.pem')
        mkdir(key_dir, mode=777)
        if os.path.isfile(private_key_file):
            os.unlink(private_key_file)
        ret = bash('echo -e  "y
    "|ssh-keygen -t rsa -f %s -b %s -P "%s"' % (private_key_file, length, password))
        if authorized_keys:
            auth_key_dir = os.path.join(home, username, '.ssh')
            mkdir(auth_key_dir, username=username, mode=700)
            authorized_key_file = os.path.join(auth_key_dir, 'authorized_keys')
            with open(private_key_file+'.pub') as pub_f:
                with open(authorized_key_file, 'w') as auth_f:
                    auth_f.write(pub_f.read())
            os.chmod(authorized_key_file, 0600)
    if 'master' == HOST_TYPE.lower(): cmd = "if ssh %s test -d %s ;then echo %s exists; else ssh %s mkdir -p %s;fi" % (SLAVE_HOST, key_dir, key_dir, SLAVE_HOST, key_dir) res = subprocess.call(cmd, shell=True) logger.info(res) bash('scp -P22 %s root@%s:%s' % (private_key_file, SLAVE_HOST, key_dir)) bash('scp -P22 %s root@%s:%s' % (private_key_file+'.pub', SLAVE_HOST, key_dir)) bash('scp -P22 %s root@%s:/home/%s/.ssh/' % (authorized_key_file, SLAVE_HOST, username)) else: cmd = "if ssh %s test -d %s ;then echo %s exists; else ssh %s mkdir -p %s;fi" % (MASTER_HOST, key_dir, key_dir, MASTER_HOST, key_dir) res = subprocess.call(cmd, shell=True) logger.info(cmd) bash('scp -P22 %s root@%s:%s' % (private_key_file, MASTER_HOST, key_dir)) bash('scp -P22 %s root@%s:%s' % (private_key_file+'.pub', MASTER_HOST, key_dir)) bash('scp -P22 %s root@%s:/home/%s/.ssh/' % (authorized_key_file, MASTER_HOST, username)) chown(authorized_key_file, username) ssh_login("chown -R %s:%s /home/%s/" % (username, username, username)) ssh_login("chmod 700 /home/%s/.ssh" % username)
    def server_add_user(username, ssh_key_pwd=''):
    """
    add a system user in jumpserver
    在jumpserver服务器上添加一个用户
    """
    bash("adduser -s '%s' '%s'" % (os.path.join(BASE_DIR, 'init.sh'), username))
    ssh_login("adduser -s '%s' '%s'" % (os.path.join(BASE_DIR, 'init.sh'), username))
    ssh_login("mkdir -p /home/%s/.ssh" % username)
    gen_ssh_key(username, ssh_key_pwd)

    删除用户也需要在另外一台服务上同步删除
    def server_del_user(username):
        """
        delete a user from jumpserver linux system
        删除系统上的某用户
        """
        bash('userdel -r -f %s' % username)
        ssh_login('userdel -rf %s' % username)
        logger.debug('rm -f %s/%s_*.pem' % (os.path.join(KEY_DIR, 'user'), username))
        bash('rm -f %s/%s.pem*' % (os.path.join(KEY_DIR, 'user'), username))
        ssh_login('rm -f %s/%s.pem*' % (os.path.join(KEY_DIR, 'user'), username))

    四、修改 juser下面的views.py 用户下载秘钥文件后需要在另外一台服务器也同步删除

    def down_key(request):
        if is_role_request(request, 'super'):
            uuid_r = request.GET.get('uuid', '')
        else:
            uuid_r = request.user.uuid
        if uuid_r:
            user = get_object(User, uuid=uuid_r)
            if user:
                username = user.username
                private_key_file = os.path.join(KEY_DIR, 'user', username+'.pem')
                print private_key_file
                if os.path.isfile(private_key_file):
                    f = open(private_key_file)
                    data = f.read()
                    f.close()
                    response = HttpResponse(data, content_type='application/octet-stream')
                    response['Content-Disposition'] = 'attachment; filename=%s' % os.path.basename(private_key_file)
                    if request.user.role == 'CU':
                        os.unlink(private_key_file)
                        ssh_login('rm -rf %s' % private_key_file)
    return response return HttpResponse('No Key File. Contact Admin.')

    五、修改jperm 下面的 utils.py  主要是同步系统用户(role_user)的信息和秘钥

    from jumpserver.settings import KEY_DIR
    from jumpserver.api import logger, bash
    from juser.user_api import ssh_login
    from jumpserver.settings import HOST_TYPE, MASTER_HOST, SLAVE_HOST
    def gen_keys(key="", key_path_dir=""):
        """
        在KEY_DIR下创建一个 uuid命名的目录,并且在该目录下 生产一对秘钥
        :return: 返回目录名(uuid)
        """
        key_basename = "key-" + uuid4().hex
        if not key_path_dir:
            key_path_dir = os.path.join(KEY_DIR, 'role_key', key_basename)
        private_key = os.path.join(key_path_dir, 'id_rsa')
        public_key = os.path.join(key_path_dir, 'id_rsa.pub')
        mkdir(key_path_dir, mode=755)
        ssh_login("mkdir -p '%s'" % key_path_dir)
       if not key:
            key = RSAKey.generate(2048)
            key.write_private_key_file(private_key)
        else:
            key_file = os.path.join(key_path_dir, 'id_rsa')
            with open(key_file, 'w') as f:
                f.write(key)
                f.close()
            with open(key_file) as f:
                try:
                    key = RSAKey.from_private_key(f)
                except SSHException, e:
                    shutil.rmtree(key_path_dir, ignore_errors=True)
                    raise SSHException(e)
        os.chmod(private_key, 0644)
        with open(public_key, 'w') as content_file:
            for data in [key.get_name(), " ",key.get_base64(),
                         " %s@%s" % ("jumpserver", os.uname()[1])]:
                content_file.write(data)
      if 'master' == HOST_TYPE.lower():
            bash('scp -P22 %s/* root@%s:%s' % (key_path_dir, SLAVE_HOST, key_path_dir))
        else:
            bash('scp -P22 %s/* root@%s:%s' % (key_path_dir, MASTER_HOST, key_path_dir))
        return key_path_dir

    六、修改jperm下面的view.py

    函数 perm_role_delete

    # TODO: 判断返回结果,处理异常
                # 删除存储的秘钥,以及目录
                try:
                    key_files = os.listdir(role_key)
                    for key_file in key_files:
                        os.remove(os.path.join(role_key, key_file))
                    os.rmdir(role_key)
                    ssh_login('rm -rf %s' % role_key)
                except OSError, e:
                    logger.warning(u"Delete Role: delete key error, %s" % e)
                    raise ServerError(u"删除系统用户key失败: %s" % e)
                logger.info(u"delete role %s - delete role key directory: %s" % (role.name, role_key))
                # 数据库里删除记录
                role.delete()
                return HttpResponse(u"删除系统用户: %s" % role.name)

     七、主备服务器设置使用ssh秘钥登录

    1、用 ssh-key-gen 在master创建公钥和密钥
    ligh@local-host$ ssh-keygen -t  rsa
    Enter file in which to save the key (/home/jsmith/.ssh/id_rsa):[Enter key] 
    Enter passphrase (empty for no passphrase): [Press enter key]
    Enter same passphrase again: [Pess enter key]
    Your identification has been saved in /home/jsmith/.ssh/id_rsa.
    Your public key has been saved in /home/jsmith/.ssh/id_rsa.pub. 
    The key fingerprint is: 33:b3:fe:af:95:95:18:11:31:d5:de:96:2f:f2:35:f9 
    ligh@local-host

    2、用 ssh-copy-id 把公钥复制到slave上
    ligh@local-host$ ssh-copy-id -i ~/.ssh/id_rsa.pub  root@192.168.3.85
    ligh@remote-host‘s password:
    Now try logging into the machine, with ―ssh ?remote-host‘‖, and check in: 
    .ssh/authorized_keys to make sure we haven‘t added extra keys that you weren‘t expecting.
    [注: ssh-copy-id 把密钥追加到远程主机的 .ssh/authorized_key 上.]
     
    3、在slave上做同样操作

    八、mysql数据库主主配置(Centos7)

    1、master 配置

    master:192.168.0.13

    slave: 192.168.0.12

    修改配置

    [root@localhost ~]# vim /etc/my.cnf 
    [mysqld]
    server-id=1
    log-bin=mysql-bin
    binlog-ignore-db=mysql  #  同步除mysql库之外所有库
    slave_skip_errors = 1062
     
    主服务器上创建用于同步的账号: 
    GRANT REPLICATION SLAVE,RELOAD,SUPER ON *.* TO 'username'@'192.168.0.12' IDENTIFIED BY 'password'; 
    GRANT REPLICATION SLAVE ON *.* TO 'username'@'192.168.0.13' IDENTIFIED BY 'password'; 
    重启服务

     [root@localhost ~]# systemctl restart mariadb.service

    查看是否启动日志记录: 
    MariaDB [mysql]> show master status; 
    ERROR 2006 (HY000): MySQL server has gone away
    No connection. Trying to reconnect...
    Connection id:    2
    Current database: mysql
    +------------------+----------+----------------+------------------+
    | File             | Position | Binlog_Do_DB   | Binlog_Ignore_DB |
    +------------------+----------+----------------+------------------+
    | mysql-bin.000002 |      245 | jumpserver |                  |
    +------------------+----------+----------------+------------------+
    1 row in set (0.01 sec)
     
    从数据写入主数据库:(当从数据库设置后再运行,注意:mysql-bin.000002是个变量,请根据show master status;显示出来的实际值填写) 
    CHANGE MASTER TO MASTER_HOST='192.168.0.12',MASTER_PORT=3306,MASTER_USER='username',MASTER_PASSWORD='password',MASTER_LOG_FILE='mysql-bin.000002',MASTER_LOG_POS=245; 
     
    2、从数据库配置
    [root@localhost ~]# vim /etc/my.cnf 
    [mysqld]
    server-id=2  # 不能与master一样
    log-bin=mysql-bin
    binlog-ignore-db=mysql  #  同步除mysql库之外所有库
    slave_skip_errors = 1062
     
    主服务器上创建用于同步的账号: 
    GRANT REPLICATION SLAVE,RELOAD,SUPER ON *.* TO 'username'@'192.168.0.13' IDENTIFIED BY 'password'; 
    GRANT REPLICATION SLAVE ON *.* TO 'username'@'192.168.0.12' IDENTIFIED BY 'password'; 
    重启服务

     [root@localhost ~]# systemctl restart mariadb.service

    主数据写入从数据库:
    CHANGE MASTER TO MASTER_HOST='192.168.0.13',MASTER_PORT=3306,MASTER_USER='username',MASTER_PASSWORD='password',MASTER_LOG_FILE='mysql-bin.000002',MASTER_LOG_POS=245; 

    mysql主从配置后都要运行以下3条指令 

    stop slave;   #停止同步 
    reset slave;  #复位同步 
    start slave;   #启动同步 

    检查状态 
     
    MariaDB [mysql]>  show slave statusG; 
    *************************** 1. row ***************************
                   Slave_IO_State: Waiting for master to send event
                      Master_Host: 192.168.0.12
                      Master_User: username
                      Master_Port: 3306
                    Connect_Retry: 60
                  Master_Log_File: mysql-bin.000002
              Read_Master_Log_Pos: 245
                   Relay_Log_File: mariadb-relay-bin.000003
                    Relay_Log_Pos: 529
            Relay_Master_Log_File: mysql-bin.000002
                 Slave_IO_Running: Yes
                Slave_SQL_Running: Yes
  • 相关阅读:
    atom编辑器中编辑C代码调用printf函数打印中文字符出现乱码
    C 库函数
    函数原型、函数声明和函数定义之间关系
    函数指针
    const 类型限定符
    AttributeError: 'str' object has no attribute '__buffer__'
    python3继承中发生的显式覆盖
    python3中的隐式继承
    黑科技
    理解jquery的$.extend()、$.fn和$.fn.extend()
  • 原文地址:https://www.cnblogs.com/mageguoshi/p/5765555.html
Copyright © 2020-2023  润新知