为Redis编写安全通道
source link: https://allenwind.github.io/blog/5853/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
NLP、深度学习、机器学习、Python、Go
为Redis编写安全通道
最近需要用到服务器的Redis
数据库,但又不想让数据库的监听端口暴露到外网,另外Redis本身不支持数据加密。还有什么方法让本地客户端连上服务器的数据库呢?
答案是使用代理。接下来的实现使用Go
语言,由于它的静态连接特性,编译后放到服务器即可。它的思路很简单,把本地客户端的数据加密后送到服务器,服务器接收后解密转发到本地指定的端口。
client --> localproxy --加密的数据--> remoteproxy -转发到-> Redis
127.0.0.1:7744 remoteAddr:443 127.0.0.1:6379
如果client
支持tls
可以把localproxy
这过程免去,即
tlsclient --加密的数据--> remoteproxy -转发到-> Redis
port:443 port:6379
下面的是TCP
代理的实现,交叉编译后部署到Linux
服务器后运行即可。
package main
import (
"io"
"log"
"net"
)
func main() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
l, err := net.Listen("tcp", ":62815")
if err != nil {
log.Panic(err)
}
for {
client, err := l.Accept()
if err != nil {
log.Println(err)
continue
}
db, err := net.Dial("tcp", "localhost:6379") // connect to Redis
if err != nil {
log.Println(err)
continue
}
go io.Copy(db, client) // copy client data to database
io.Copy(client, db) // copy database data to client
}
}
但TCP
本身并不安全,我们需要在TCP
上添加一层加密SSL
。可以通过openssl
创建服务器的证书和密钥。
package main
import (
"crypto/tls"
"io"
"log"
"net"
)
func main() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
cer, err := tls.LoadX509KeyPair("server.crt", "server.key")
if err != nil {
log.Fatal(err)
}
config := &tls.Config{Certificates: []tls.Certificate{cer}}
l, err := tls.Listen("tcp", ":62815", config)
defer l.Close()
if err != nil {
log.Panic(err)
}
for {
client, err := l.Accept()
if err != nil {
log.Println(err)
continue
}
go handleTCPRequest(client)
}
}
func handleTCPRequest(client net.Conn) {
remote, err := net.Dial("tcp", "localhost:6379") // connect to Redis
if err != nil {
log.Println(err)
return
}
go io.Copy(remote, client) // copy client data to database
io.Copy(client, remote) // copy database data to client
}
客户端代码如下:
package main
import (
"crypto/tls"
"io"
"log"
"net"
)
func main() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
local, err := net.Listen("tcp", ":7744")
defer local.Close()
if err != nil {
log.Fatal(err)
}
for {
client, err := local.Accept()
if err != nil {
log.Println(err)
continue
}
go handleTLSRequest(client)
}
}
func handleTLSRequest(client net.Conn) {
conf := &tls.Config{}
remote, err := tls.Dial("tcp", "localhost:443", conf)
defer remote.Close()
if err != nil {
log.Fatal(err)
}
go io.Copy(remote, client)
io.Copy(client, remote)
}
上面的代码整合下写成命令行工具即可方便使用。如果client
支持tls
可以把localproxy
这过程免去。
事实上,这种方法不仅适用于Redis
,还适用于MongoDB
、MySQL
,不过后两者就已经有SSL
功能。从官方文档看,Redis
推荐使用 spipe 作数据加密。另外,利用类似的方法不难让数据库走socks5
代理,以后有空写写。
除此之外,还可以为代理添加其他功能:为多数据库作负载均衡、读写分离。不过数据加密解密和端口转发还是有一定的延时。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK