• odoo 结合fail2ban


    在配置fail2ban的时候遇到很多坑,现在记录下

    先参考一篇博客的教程:

    https://atjason.com/IT/ss_fail2ban.html

    可以使用echo命令来模拟日志的写入,不需要搭建日志服务

    echo '2016-07-17 18:05:03 ERROR can not parse header when handling connection from 112.10.137.6:20463' >> /var/log/shadowsocks1.log
    

    其中需要注意的一点 是,2016-07-17 18:05:03时间不能随便设置,时间与当前时间不能相差太多,不然fail2ban 总是忽略.

    如果按照上边的教程能够实现,再来尝试odoo服务.

    odoo 用户登录使用odoo自带的功能即可,详情可参考上一篇博客
    主要是数据库密码的防暴力破解,看了下源码,odoo并没有做相关的防范措施,要是被人恶意破解开,感觉就是灾难.
    odoo 数据库密码可以尝试的方式有:备份,复制,删除,创建,还原,更改密码,所以要把控好这几入口
    而且就算尝试密码错误后,后台日志没有记录,添加日志需要修改源码.

    关于odoo 日志的时间

    日志的时间使用的utc时区,与系统的时间总是相差8个小时,这点也需要做调整:
    调整的方式参考:http://chenliy.com/post/330

    调整的相关源码:

    调整时区

    import datetime
    
    class DBFormatter(logging.Formatter):
        def format(self, record):
            record.pid = os.getpid()
            record.dbname = getattr(threading.current_thread(), 'dbname', '?')
            return logging.Formatter.format(self, record)
    
        def formatTime(self, record, datefmt=None):
            now = datetime.datetime.now() + datetime.timedelta(hours=8)
            ct = time.localtime(time.mktime(now.timetuple()))
            print(time.tzname)
            if datefmt:
                s = time.strftime(datefmt, ct)
            else:
                t = time.strftime("%Y-%m-%d %H:%M:%S", ct)
                s = "%s,%03d" % (t, record.msecs)
            return s
    

    添加日志打印

        @http.route('/web/database/create', type='http', auth="none", methods=['POST'], csrf=False)
        def create(self, master_pwd, name, lang, password, **post):
            try:
                if not re.match(DBNAME_PATTERN, name):
                    raise Exception(_('Invalid database name. Only alphanumerical characters, underscore, hyphen and dot are allowed.'))
                # country code could be = "False" which is actually True in python
                country_code = post.get('country_code') or False
                dispatch_rpc('db', 'create_database', [master_pwd, name, bool(post.get('demo')), lang, password, post['login'], country_code, post['phone']])
                request.session.authenticate(name, post['login'], password)
                return http.local_redirect('/web/')
            except Exception as e:
                error = "Database creation error: %s" % (str(e) or repr(e))
                _logger.info('{} -Action:database create, database auth failed'.format(request.httprequest.remote_addr))
            return self._render_template(error=error)
    
        @http.route('/web/database/duplicate', type='http', auth="none", methods=['POST'], csrf=False)
        def duplicate(self, master_pwd, name, new_name):
            try:
                if not re.match(DBNAME_PATTERN, new_name):
                    raise Exception(_('Invalid database name. Only alphanumerical characters, underscore, hyphen and dot are allowed.'))
                dispatch_rpc('db', 'duplicate_database', [master_pwd, name, new_name])
                return http.local_redirect('/web/database/manager')
            except Exception as e:
                error = "Database duplication error: %s" % (str(e) or repr(e))
                _logger.info('{} -Action:database duplicate, database auth failed'.format(request.httprequest.remote_addr))
                return self._render_template(error=error)
    
        @http.route('/web/database/drop', type='http', auth="none", methods=['POST'], csrf=False)
        def drop(self, master_pwd, name):
            try:
                dispatch_rpc('db','drop', [master_pwd, name])
                request._cr = None  # dropping a database leads to an unusable cursor
                return http.local_redirect('/web/database/manager')
            except Exception as e:
                error = "Database deletion error: %s" % (str(e) or repr(e))
                _logger.info('{} -Action:database drop, database auth failed'.format(request.httprequest.remote_addr))
    
                return self._render_template(error=error)
    
        @http.route('/web/database/backup', type='http', auth="none", methods=['POST'], csrf=False)
        def backup(self, master_pwd, name, backup_format = 'zip'):
            try:
                odoo.service.db.check_super(master_pwd)
                ts = datetime.datetime.utcnow().strftime("%Y-%m-%d_%H-%M-%S")
                filename = "%s_%s.%s" % (name, ts, backup_format)
                headers = [
                    ('Content-Type', 'application/octet-stream; charset=binary'),
                    ('Content-Disposition', content_disposition(filename)),
                ]
                dump_stream = odoo.service.db.dump_db(name, None, backup_format)
                response = werkzeug.wrappers.Response(dump_stream, headers=headers, direct_passthrough=True)
                return response
            except Exception as e:
                _logger.exception('Database.backup')
                error = "Database backup error: %s" % (str(e) or repr(e))
                _logger.info('{} - Action:database backup, database auth failed'.format(request.httprequest.remote_addr))
    
                return self._render_template(error=error)
    
        @http.route('/web/database/restore', type='http', auth="none", methods=['POST'], csrf=False)
        def restore(self, master_pwd, backup_file, name, copy=False):
            try:
                data_file = None
                db.check_super(master_pwd)
                with tempfile.NamedTemporaryFile(delete=False) as data_file:
                    backup_file.save(data_file)
                db.restore_db(name, data_file.name, str2bool(copy))
                return http.local_redirect('/web/database/manager')
            except Exception as e:
                error = "Database restore error: %s" % (str(e) or repr(e))
                _logger.info('{} - Action:database restore, database auth failed'.format(request.httprequest.remote_addr))
    
                return self._render_template(error=error)
            finally:
                if data_file:
                    os.unlink(data_file.name)
    
        @http.route('/web/database/change_password', type='http', auth="none", methods=['POST'], csrf=False)
        def change_password(self, master_pwd, master_pwd_new):
            try:
                dispatch_rpc('db', 'change_admin_password', [master_pwd, master_pwd_new])
                return http.local_redirect('/web/database/manager')
            except Exception as e:
                error = "Master password update error: %s" % (str(e) or repr(e))
                _logger.info('{} - Action:database change_password, database auth failed'.format(request.httprequest.remote_addr))
    
                return self._render_template(error=error)
    
    

    fail2ban 配置:

    # /etc/fail2ban/jail.local
    [odoo]
    enabled = true
    port = http,https
    bantime = 8100   # 900s (15 min) + 7200s (2 hours diffecence in odoo log and systemtime)
    maxretry = 4
    findtime = 8100   # 900s (15 min) + 7200s (2 hours diffecence in odoo log and systemtime)
    # logpath = /var/log/test.log
    logpath = /opt/logs/odoo/odoo12_dev_stdout.log
    
    
    # /etc/fail2ban/filter.d/odoo.conf
    [INCLUDES]
      
    # Read common prefixes. If any customizations available -- read them from
    # common.local
    before = common.conf
    
    [DEFAULT]
    
    _daemon = odoo
    
    [Definition]
    failregex = odoo.addons.web.controllers.main: <HOST> -Action:database .*?, database auth
    ignoreregex =
    
    # maxlines = 1
    
    # journalmatch = _SYSTEMD_UNIT=sshd.service + _COMM=sshd
    

    重启fail2ban :service fail2ban reload
    到这里不出意外的话,可以看到fail2ban 的测试日志:fail2ban set loglevel TRACEDEBUG

    image

    问题延申:

    1. odoo服务是由其他nginx 服务器反向代理的, 那么这么配置就没有太大的意义,目前觉得可行的办法是将当前的日志文件共享到nginx 所在的服务器中,然后使用nginx 服务器的fail2ban 来禁用ip
  • 相关阅读:
    收藏的一些前端学习的网址
    使用box-shadow 实现水波、音波的效果
    asyncjs,waterfall的使用
    兼容opacity的方法
    在php框架中写正规则表达式时的磕绊
    浏览器的渲染原理
    正规则表达式判断数字
    ie6,7,8不兼容rgba,写background时候不要写成rgba
    jquery-ias插件详解
    制作手机页面过程中遇到的一点问题
  • 原文地址:https://www.cnblogs.com/qianxunman/p/15909029.html
Copyright © 2020-2023  润新知