今天下午的时候,有个朋友(感谢Crane.Chen氵)提了一个Oracle的问题,引起了我的好奇,问题的描述如下:
出于安全需要,这边的数据库密码必须90天更改一次,否则账号过期自动锁定。目前我们改密码都需要停掉应用,然后改密码后,启动应用。这样业务就会有中断。有什么方案可以保障即能更改密码,又能热更新应用里连接池的账号密码。现在若不停机,不管先更改连接池还是先改库,都可能因为密码不匹配而锁定账号。
乍一看是个“不合理”的需求,数据库层面我们遵从安全规范来修改密码,我们照做就是了,但是显然做这个事情的成本实在太高,而且存在极高的风险,因为防御导致业务不可用带来的损失其实是很大的。 从一个层面来说,其实是孤立的看待了这个问题,只是考虑了自己,而没有考虑到应用层和全盘。做完自我批评,我来给出这个问题的一些解决之道。
首先我们把这个密码修改的过程和应用层结合起来,应用层通常是使用连接池,而且主要是长连接的方式,如果修改了密码,那么会在数据库的字典配置里面修改密码信息,密码会在新的会话中立刻生效,但是已有的连接还是会保持,通常在业务层也是做了密码的配置文件,很可能看到的密码是一个加密串。
所以最严重的的情况下无非是下面几种:
1)数据库先改密码,应用层后改,已有的长连接依旧可用,但是新连接失败,数据库密码错误超过3次,数据库账号就会被锁定,导致业务不可用。
2)应用层先改密码,数据库层后改,新连接会全部失败,错误密码超过3次,导致账号被锁定,业务不可用。
小结:从以上看出来,不管是什么样的顺序都会导致同样严重的后果,所以也就无所谓先后顺序了。
而回到这个问题本身,我们可以做一些补救措施,我又了解了一些背景信息,这是使用weblogic中间件,通常这些配置都是启动时加载的,连接池目前我知道的还不支持动态热更新,所以要解决的重要问题是防止账号被锁定,数据库这边可以写一个脚本去实时检测账号的状态,如果被锁定,可以快速解锁, 这是一种不够优雅但是快速实用的解决方法。
如果我们更近一步,从整个应用架构的层面来考虑,这个问题是否有解呢。
我们来看下面的这个图:
密码信息在应用层是配置形式,而且是启动自加载的模式,数据库密码是数据字典的基础信息,数据库层面是可以实时变化的,我们要保证业务的持续连接,一种方案就是建立影子账户,这个账户没有实际的数据,只有对等的权限,它平时处于锁定状态,即不可用。而应用层来建立连接的部分可以加一个动态的逻辑,如果连接失败,可以使用影子账户,这个影子账户的密码信息是约定好的,不会轻易发生改变,当需要修改密码的时候,可以按照如下的流程来操作:
1)DBA解锁影子账户
2)DBA修改账户密码
3)应用层修改用户密码配置信息
4)应用层分批次启动应用服务,使得配置生效
5)DBA锁定影子账户
在这个过程中如果连接检测失败,会启用影子账户的来建立连接,在应用服务重启完成之后,就可以将影子账户锁定,应用层来再次动态适配,这样对于业务层来说就是一个动态平衡的过程。
其实对于很多公司来说,随着业务的快速变化,安全问题是挥之不去的隐患。所以密码修改也是一种合情合理的解决方式,当然在这个过程中会有一些可用性的影响,整体来说,我们要保证的是可控。
可能到了这里,我们才进行了一半,上面的方案应用层改造其实还是比较大的,而且对于权限的维护来说需要保证影子账户的权限也要完全一致,否则产生业务影响就很被动了。
我们有没有更好的方案呢,其实最透明的方案是数据库层面来做好这种双层校验,这时我想到了MySQL 8.0的新特性。
在MySQL 8.0.14的release note中是这么低调的一段描述。
我们通俗些可以理解为双密码,retain current password这个语法只在修改密码的场景中使用,在create user中是不能用的。意思是修改密码的时候原来的密码依旧可用,这样对于业务是最友好的方式。
正确理解这个语法的姿势是查看官方文档的解读:
不演示一下不尽兴,我们来看看这个特性的效果。
首先创建一个用户:
mysql> create user test_pwd identified by 'test_pwd1'; Query OK, 0 rows affected (0.01 sec)
然后赋予一些基本的权限
mysql> grant usage on *.* to test_pwd ; Query OK, 0 rows affected (0.01 sec)
这个时候通过客户端连接是OK的。
# mysql -utest_pwd -ptest_pwd1 -P33061 -h127.0.0.1 mysql>
我们开始关键部分,修改用户密码,使用retain current password语法:
mysql> alter user test_pwd identified by 'test_pwd1' retain current password; ERROR 1227 (42000): Access denied; you need (at least one of) the CREATE USER or APPLICATION_PASSWORD_ADMIN privilege(s) for this operation
很遗憾失败了,因为这个操作是需要基本的权限的,根据提示需要APPLICATION_PASSWORD_ADMIN 或者CREATE USER的权限。
我们使用管理员账户修正下权限。
mysql> grant APPLICATION_PASSWORD_ADMIN on *.* to test_pwd; Query OK, 0 rows affected (0.01 sec)
再次修改就可以了。
mysql> alter user test_pwd identified by 'test_pwd2' retain current password; Query OK, 0 rows affected (0.01 sec)
这个时候就达到了密码双活的状态,两个密码都可以用,而且校验始终都可用,幸福感大大增强。
当然我不建议对于业务无限制开放,毕竟这种方式会让双方都很难持续维护密码,所以在业务修改密码,批次启动应用服务后,我们可以回收原来的密码,使用discard old password子句。
mysql> alter user test_pwd DISCARD OLD PASSWORD; Query OK, 0 rows affected (0.01 sec)
这个时候连接会再次失败。
# mysql -utest_pwd -ptest_pwd1 -P33061 -h127.0.0.1 mysql: [Warning] Using a password on the command line interface can be insecure. ERROR 1045 (28000): Access denied for user 'test_pwd'@'localhost' (using password: YES)
这应该算是我们最希望看到的错误场景了,满满的完成任务的使命感。
期待MySQL 8.0更多更好的新特性。。。
————————————————
版权声明:本文为CSDN博主「jeanron100」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yangjianrong1985/article/details/102849496