前言
前面章节中提到了PostgreSQL的peer
认证方式,说到这种认证方式是通过Unix Domain Socket
连接方式认证的。本章节就来详细谈谈Unix Domain Socket
及其应用。
Unix Domain Socket VS TCP/UDP Socket
看到Socket
这个词,很多人可能会联想到网络编程非常常见的TCP/UDP Socket
。本质上他们的作用都一样,都是为了网络通信而生。Unix Domain Socket是在此基础之上发展的IPC机制,在本机两个进程之间直接建立通信方式,不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,所以性能上和效率上比TCP/UDP Socket高得多,并且Unix Domain Socket是一种可靠的通信机制,而network socket是不可靠的通信,可能发生丢包、乱序等现象。
Unix Domain Socket的表现形式
是以文件
形式存放在本机上,通常不占用容量。以大多数运维人员非常熟悉的MySQL
服务为例,默认配置下MySQL服务运行时会创建一个这样的文件:
# ll /var/lib/mysql/mysql.sock
srwxrwxrwx 1 mysql mysql 0 12月 28 21:30 /var/lib/mysql/mysql.sock
#^ 注意第一个file类型标记是s
# file /var/lib/mysql/mysql.sock
/var/lib/mysql/mysql.sock : socket
不同的操作系统可能这个文件位置略有不同,具体位置参考MySQL的配置文件中的socket
配置项。
咋一看有点像命名管道
(named pipe),而且作用也差不多。但是命名管道只能支持字节流,而Unix Domain Socket既能支持字节流,又能支持队列。
另外Unix Domain Socket使用系统文件的地址来作为自己的身份,所以可以用于权限认证(PostgreSQL的Peer认证),它可以被系统进程引用。所以可以支持不同的进程同时使用,而命名管道是没有这种功能的。
Unix Domain Socket的使用
很多运行于Linux/Unix上的服务不止可以监听一个TCP/UDP端口,还支持监听一个Unix Domain Socket。如Mysql的socket
配置,redis的unixsocket
配置,nginx的listen
配置,Postgresql的unix_socket_directories
配置等等。只要客户端支持Unix Domain Socket,就可以直接使用这种方式连接。
如MySQL的[client]
配置段下面socket
配置就直接指向unix domain socket地址,代表默认使用unix domain socket连接。psql
默认连接方式也是使用unix domain socket,会在/var/run/postgresql/
目录下查找.s.PGSQL.5432
这个socket。可以通过psql -h /path/to/socket/directory/ -p port
切换其他目录下的socket文件。
大多数使用C/C++
语言开发的client也都支持Unix Domain Socket。比如Python(使用官方的解释器Cython)的Mysql driver Connector/Python,就有这样的连接方式:
cnx = mysql.connector.connect(user='root',password='password',unix_socket='/var/lib/mysql/mysql.sock')
直接指定连接本地的Unix Domain Socket访问Mysql服务。
再比如PostgreSQL的Python driver Psycopg2的文档就直接写明connect()
函数不指明host
参数,默认就使用Unix Domain Socket等等。
再比如说流行的web服务器Nginx,可能很多人都没不知道Nginx的listen
指令是可以支持unix domain socket的:
server {
listen unix:/var/run/nginx.sock;
}
这样可以限制这个虚拟主机不能直接暴露给外网用户,需要暴露的时候可以在加一个反向代理,以便做一些其他的配置限制:
upstream backend-nginx {
server unix:/var/run/nginx.sock;
}
server {
location / {
proxy_pass http://backend-nginx;
}
}
Unix Domain Socket的限制
Unix Domain Socket的性能很高,安全性好,在network socket端口有限的情况下,Unix Domain Socket无需占用有限的TCP/UDP端口。现在谈谈这玩意的限制了。很明显的可以看到两个限制:
- 只能本机访问,不能用于远程访问(Docker可以利用挂载的形式实现本机不同容器或容器与宿主机之前的访问,但还是在同一台主机上)
- 只在
POSIX
兼容的系统有实现。意味着Windows下没有对应的实现,所以MySQL这一类的服务跑在Windows下就默认关闭Unix Domain Socket的功能了
还有就是像Java
这种编程语言为了简化跨平台的兼容性问题,底层也不提供Unix Domain Socket的支持。比如JDBC
就没有Unix Domain Socket的连接实现,所以jdbc url在连接本机服务的时候,也只能写127.0.0.1
,不能写Unix Domain Socket路径。
PostgreSQL的local连接与peer认证
说清楚了Unix Domain Socket之后,PostgreSQL的peer认证就很容易理解了。
有关postgresql完整的认证方式,参见官方文档: https://www.postgresql.org/docs/current/auth-methods.html
我们先看看默认安装下的postgresql的pg_hba.conf
文件(已过滤空行和注释行):
local all postgres peer
local all all peer
host all all 127.0.0.1/32 md5
host all all ::1/128 md5
local replication all peer
host replication all 127.0.0.1/32 md5
host replication all ::1/128 md5
其中host
代表client的访问方式是TCP
,而local
就代表client的访问方式是Unix Domain Socket。所以可以很明显的看到local
行的配置是没有ip配置段的。
那么再说说peer
认证方式是怎么回事呢?之前提到过,Unix Domain Socket是可以拿到用户身份信息的,这个在Unix/Linux系统中就是用户账号,而peer
则代表着用户psql以user
身份运行,连接到数据库上对应的用户身份就是user
这种方式。所以peer
认证只支持local
连接方式,所以在windows不起作用。
举个例子,如果我当前以myuser
这个账号登录到Linux系统中,运行psql
命令,那么连接到数据库上就是myuser
这个用户,不需要密码认证。如果postgresql数据库中没有myuser
这个用户,就会报错找不到这个用户。
再看看postgres
这个用户默认配置下限定local
连接方式,认证方式是peer
。代表postgres
(默认的postgresql的管理员账户)必须以Unix Domain Socket方式连接,并且使用操作系统中的postgres
用户身份运行。由于这个账户默认在Linux系统中是不允许直接登录的,所以我们需要su
或sudo
命令切换用户身份运行,这里我们以sudo
为例:
sudo -u postgres psql
由于psql
默认就是以Unix Domain Socket方式连接(Windows系统则以TCP方式),所以无需指定其他连接参数。
再比如我们在postgresql上创建了一个用户myadmin
,但是我们的操作系统中没有myadmin
这个账户,那么此时我们就不能使用local
渠道连接了,只能以host
方式连接,即TCP方式:
psql -U myadmin -h 127.0.0.1
直接指定-h
参数为127.0.0.1
或localhost
,限定psql
使用TCP方式连接postgresql,则可以命中后面的host all all 127.0.0.1/32 md5
这个配置,于是连接到数据库中。
psql使用unix domain socket连接本机其他postgresql实例的方案:
psql -h /path/to/socket/dir/ -p other_port