溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么通過(guò)MySQL-Proxy實(shí)現(xiàn)數(shù)據(jù)庫(kù)的認(rèn)證、授權(quán)與審計(jì)

發(fā)布時(shí)間:2021-12-18 15:32:13 來(lái)源:億速云 閱讀:198 作者:柒染 欄目:數(shù)據(jù)安全

怎么通過(guò)MySQL-Proxy實(shí)現(xiàn)數(shù)據(jù)庫(kù)的認(rèn)證、授權(quán)與審計(jì),針對(duì)這個(gè)問(wèn)題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問(wèn)題的小伙伴找到更簡(jiǎn)單易行的方法。

0x00 前言

總來(lái)的話數(shù)據(jù)庫(kù)里面的賬號(hào)就會(huì)越來(lái)越多,賬號(hào)授權(quán)也是一個(gè)蛋疼的工作。特別是還會(huì)有“mysql從刪庫(kù)到跑路”的問(wèn)題,員工離職刪除賬號(hào)也會(huì)十分的麻煩。對(duì)于財(cái)大氣粗的財(cái)閥來(lái)說(shuō)這個(gè)問(wèn)題很好解決,買一套設(shè)備就好了,但是對(duì)于創(chuàng)業(yè)公司要力爭(zhēng)做到“零元黨”,不然也沒(méi)辦法完全體現(xiàn)出自己的價(jià)值。

0x01 調(diào)研

最初的想法是對(duì)開源mysql的代理工具做一次二次開發(fā),于是乎開始搜集類似工具的資料。無(wú)意中發(fā)現(xiàn)mysql-proxy居然預(yù)留了6個(gè)鉤子允許用戶通過(guò)Lua腳本去調(diào)用他們,也就是說(shuō)我們可以自行編寫Lua腳本來(lái)掌握“用戶的命運(yùn)”。

connect_server()                  當(dāng)代理服務(wù)器接受到客戶端連接請(qǐng)求時(shí)會(huì)調(diào)用該函數(shù)

read_handshake()               當(dāng)mysql服務(wù)器返回握手相應(yīng)時(shí)會(huì)被調(diào)用

read_auth()                         當(dāng)客戶端發(fā)送認(rèn)證信息時(shí)會(huì)被調(diào)用

read_auth_result(aut)           當(dāng)mysql返回認(rèn)證結(jié)果時(shí)會(huì)被調(diào)用

read_query(packet)             當(dāng)客戶端提交一個(gè)sql語(yǔ)句時(shí)會(huì)被調(diào)用

read_query_result(inj)          當(dāng)mysql返回查詢結(jié)果時(shí)會(huì)被調(diào)用

顯然,通過(guò)上述的read_auth和read_query兩個(gè)鉤子函數(shù),我們可以實(shí)現(xiàn)對(duì)mysql數(shù)據(jù)庫(kù)的認(rèn)證、授權(quán)和審計(jì)的工作。

0x02 設(shè)計(jì)

我們的目標(biāo)是認(rèn)證、授權(quán)和審計(jì),那么read_auth函數(shù)可以實(shí)現(xiàn)認(rèn)證和授權(quán),read_query可以實(shí)現(xiàn)審計(jì)的功能。read_query比較容易實(shí)現(xiàn),只需要get到用戶發(fā)來(lái)的sql語(yǔ)句寫到消息隊(duì)列里就好了,我這里就簡(jiǎn)單地寫到redis的list中。

read_auth函數(shù)就要相對(duì)復(fù)雜些,不僅需要對(duì)用戶提交的一次性password進(jìn)行校驗(yàn),還需要讀取授權(quán)信息,讓用戶登錄到mysql的時(shí)候華麗的變身成為我們指定的身份。

怎么通過(guò)MySQL-Proxy實(shí)現(xiàn)數(shù)據(jù)庫(kù)的認(rèn)證、授權(quán)與審計(jì)

1.用戶訪問(wèn)Openresty,后端的lua腳本會(huì)調(diào)用公司內(nèi)部使用的im軟件的消息接口,將生成的一次性口令發(fā)送給用戶。與此同時(shí),也會(huì)將該口令寫入redis。

2.用戶使用mysql客戶端連接指定的mysql-proxy,此時(shí)進(jìn)入read_auth鉤子函數(shù),先對(duì)用戶提交的口令進(jìn)行確認(rèn)。然后會(huì)去redis請(qǐng)求當(dāng)前數(shù)據(jù)庫(kù)對(duì)應(yīng)developer、master、owner三個(gè)role的授權(quán)名單,查看三個(gè)名單中是否含有當(dāng)前用戶,如果有則將用戶以其對(duì)應(yīng)的role跳轉(zhuǎn)到數(shù)據(jù)庫(kù)上。

3.當(dāng)認(rèn)證授權(quán)成功結(jié)束后,用戶通過(guò)上一步授權(quán)的role來(lái)訪問(wèn)后端mysql,并且執(zhí)行的所有sql語(yǔ)句都會(huì)進(jìn)入read_query鉤子函數(shù)被記錄到redis的隊(duì)列中。

0x03 代碼

 local password =assert(require("mysql.password"))
local proto =assert(require("mysql.proto"))
assert(require("redis"))
--字符串切割
function string:split(sep) 
local sep, fields = sep or ":", {} 
local pattern = string.format("([^%s]+)", sep) 
self:gsub(pattern, function (c) fields[#fields + 1] = c end) 
return fields 
end 
function read_query( packet )
if packet:byte() == proxy.COM_QUERY then
local con = proxy.connection
local redis = Redis.connect('your_redis_ip',6379)
--獲取ip對(duì)應(yīng)的域名
redis:select('3')
local domain = redis:get(con.server.dst.name:split(':')[1])
--將執(zhí)行的sql語(yǔ)句放入redis隊(duì)列中
redis:select('2')
redis:lpush('mysql_command_queue',os.date("%Y-%m-%d%H:%M:%S",os.time())
.. " " .. con.client.src.address .. "" .. con.client.username .. " " ..
domain .. " [" ..packet:sub(2) .. "]")
if packet:sub(2) == "SELECT 1" then
proxy.queries:append(1, packet)
end
end
end
function read_auth()
local names = {}
--developer、master、owner三個(gè)角色權(quán)限逐級(jí)增大
local roles = {[1] = 'developer',[2] = 'master',[3] = 'owner'}
local con = proxy.connection
local s = con.server
local role = ''
--認(rèn)證
local redis = Redis.connect('your_redis_ip', 6379)
local pass = redis:get(con.client.username)
ifpassword.scramble(s.scramble_buffer, password.hash(pass)) ~=con.client.scrambled_password then
--認(rèn)證失敗返回錯(cuò)誤信息
proxy.response.type = proxy.MYSQLD_PACKET_ERR
proxy.response.errmsg ="Password error!"
return proxy.PROXY_SEND_RESULT
end
redis:del(con.client.username)
--獲取ip對(duì)應(yīng)的域名
redis:select('3')
local domain = redis:get(con.server.dst.name:split(':')[1])
redis:select('1')
--獲取用戶對(duì)于當(dāng)前數(shù)據(jù)庫(kù)的role
for i,v inipairs(roles) do
--查詢“domain:role”返回相應(yīng)的名單并將名單切割為table
names = redis:get(domain .. ":" .. v):split(',')
for k,name in ipairs(names) do
if name == con.client.username then
role = v
break
end
end
end
--無(wú)授權(quán)信息返回錯(cuò)誤信息
if role == '' then
proxy.response.type = proxy.MYSQLD_PACKET_ERR
proxy.response.errmsg = "Unauthorized access!"
return proxy.PROXY_SEND_RESULT
end
--最新mysql-proxy加入的新屬性
local protocol_41_default_capabilities = 8 + 512 + 32768
proxy.queries:append(1, 
proto.to_response_packet({ 
username = role,  
response = password.scramble(s.scramble_buffer, password.hash(“your_role_password”)),
--最新mysql-proxy加入的新屬性
server_capabilities=protocol_41_default_capabilities
}) 
)
return proxy.PROXY_SEND_QUERY
end

0x04 效果

[root@ip-172-31-24-123 ~]# mysql -u test -h your_mysql-proxy_ip -P your_mysql-proxy_port-pEnter password:Welcome to the MySQL monitor.  Commands end with ; or \g.Your MySQL connection id is 30341Server version: 5.7.12 MySQL CommunityServer (GPL)Copyright (c) 2000, 2018, Oracle and/or itsaffiliates. All rights reserved.Oracle is a registered trademark of OracleCorporation and/or itsaffiliates. Other names may be trademarksof their respectiveowners.Type 'help;' or '\h' for help. Type '\c' toclear the current input statement.mysql> select user();+-------------------------+| user()                  |+-------------------------+| developer@your_ip|+-------------------------+1 row in set (0.01 sec)

顯然,使用用戶名test登錄mysql-proxy,最終跳轉(zhuǎn)到mysql上時(shí)用戶已經(jīng)變?yōu)閐eveloper。

0x05 總結(jié)

用于非業(yè)務(wù)場(chǎng)景連接數(shù)據(jù)庫(kù),比如開發(fā)運(yùn)維人員在公司連接數(shù)據(jù)庫(kù)。

管理腳本需要監(jiān)控每個(gè)mysql-proxy進(jìn)程的狀態(tài),負(fù)責(zé)他們的啟動(dòng)和停止,以及將他們的域名解析為ip存入redis中。

授權(quán)腳本讀取一個(gè)yaml文件,將文件中的授權(quán)規(guī)則同步到redis中。

每個(gè)數(shù)據(jù)庫(kù)中都只需要新建developer、master、owner三個(gè)賬號(hào),yaml配置文件中的內(nèi)容決定用戶使用以上哪種role登錄到mysql。

mysql-proxy需要使用源碼編譯安裝。

啟動(dòng)mysql-proxy的命令為: 

mysql-proxy/bin/mysql-proxy--proxy-address=mysql-proxy_ip:mysql-proxy_port --proxy-backend-addresses=mysql_ip:mysql_port--max-open-files=1024 --user=root --log-file=/var/log/mysql-proxy--log-level=debug --proxy-lua-script=your_lua_file &

關(guān)于怎么通過(guò)MySQL-Proxy實(shí)現(xiàn)數(shù)據(jù)庫(kù)的認(rèn)證、授權(quán)與審計(jì)問(wèn)題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒(méi)有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識(shí)。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI