使用SSL证书连接Postgres的Golang

3

首先,这个问题与语言无关。我正在尝试编写一个简单的应用程序,使用SSL连接到PostgreSQL。

  1. 我使用脚本创建了证书:
# Create CA private key
openssl genrsa -des3 -out root.key 4096
#Remove a passphrase
openssl rsa -in root.key -out root.key

# Create a root Certificate Authority (CA)
openssl \
    req -new -x509 \
    -days 365 \
    -subj "/CN=localhost" \
    -key root.key \
    -out root.crt

# Create server key
openssl genrsa -des3 -out server.key 4096
#Remove a passphrase
openssl rsa -in server.key -out server.key

# Create a root certificate signing request
openssl \
    req -new \
    -key server.key \
    -subj "/CN=localhost" \
    -text \
    -out server.csr

# Create server certificate
openssl \
    x509 -req \
    -in server.csr \
    -text \
    -days 365 \
    -CA root.crt \
    -CAkey root.key \
    -CAcreateserial \
    -out server.crt

我使用以下命令创建了一个数据库:

init.sql

该文件包含数据库的初始化脚本。
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

CREATE TABLE TESTING_DATA (
    ID SERIAL PRIMARY KEY,
    UUID UUID UNIQUE NOT NULL DEFAULT uuid_generate_v4(),
    NAME TEXT NOT NULL,
    INFO NUMERIC(3, 2)
);

INSERT INTO TESTING_DATA (NAME, INFO)
VALUES
    ('Notebook', 1),
    ('Office supplies', 2),
    ('Pencil', 2),
    ('Office supplies', 1),
    ('Eraser', 1),
    ('Coffee', 1),
    ('Cookies', 2),
    ('Puzzles', 5)
;

postgresql.conf

ssl = on
ssl_ca_file = '/etc/postgres/security/root.crt'
ssl_cert_file = '/etc/postgres/security/server.crt'
ssl_key_file = '/etc/postgres/security/server.key'
password_encryption = scram-sha-256

pg_hba.conf

local     all      all                md5
host      all      all  127.0.0.1/32  md5
hostssl   all      all  0.0.0.0/0     cert clientcert=1

Dockerfile

FROM postgres:12-alpine

ENV POSTGRES_USER=pguser
ENV POSTGRES_PASSWORD=pgpassword
ENV POSTGRES_DB=securitylearning

COPY pg_hba.conf postgresql.conf /etc/postgresql/config/
COPY --chown=postgres:postgres root.crt server.crt server.key /etc/postgres/security/
COPY init.sql /docker-entrypoint-initdb.d/
EXPOSE 5432
CMD ["postgres", "-c", "config_file=/etc/postgresql/config/postgresql.conf", "-c", "hba_file=/etc/postgresql/config/pg_hba.conf"]

我启动了一个容器,并确保从容器本身可以连接到数据库并从表中选择一些内容。

  1. 我创建了一个简单的程序: server.go
package main

import (
    "database/sql"
    "fmt"

    _ "github.com/lib/pq"
)

func main() {
    connection := fmt.Sprint(
        " host=localhost",
        " port=5432",
        " user=pguser",
        " dbname=securitylearning",
        " sslmode=verify-full",
        " sslrootcert=root.crt",
        " sslkey=client.key",
        " sslcert=client.crt",
    )
    db, err := sql.Open("postgres", connection)
    defer db.Close()
    if err != nil {
        panic(err)
    }
    err = db.Ping()
    if err != nil {
        panic(err)
    }
    row := db.QueryRow("SELECT * FROM TESTING_DATA")
    fmt.Println(row)
}

我尝试过:
  • root.crtserver.crtserver.key文件放在编译二进制文件旁边,并在go文件的连接字符串中添加sslrootcertsslcertsslkey,分别进行设置
  • 将相同的文件,但名称为root.crtpostgresql.crtpostgresql.key,放在~/.postgresql/目录中,因为pq默认使用它们。

目前,这种方法不起作用。我随机得到其中一种错误:

read: connection reset by peer

或者

EOF

你能帮忙吗?我错过了什么?或者你能给我指点一些资源吗?谢谢。

更新1 感谢评论中的建议,我创建了客户端密钥和证书,使用以下命令:

# Create client key
openssl genrsa -out client.key 4096
#Remove a passphrase
openssl rsa -in client.key -out client.key

# Create client certificate signing request
openssl \
    req -new \
    -key client.key \
    -subj "/CN=172.17.0.2" \
    -out client.csr

# Create client certificate
openssl \
    x509 -req \
    -in client.csr \
    -CA root.crt \
    -CAkey root.key \
    -CAcreateserial \
    -days 365 \
    -text \
    -out client.crt

我在中国使用172.17.0.2,因为这是从Docker容器的角度看到的主机IP。

我尝试了以下两种方法:

  • 在程序中使用连接字符串中的以下键
" sslrootcert=root.crt",
" sslkey=client.key",
" sslcert=client.crt",
  • root.crtclient.keyclient.srt 复制到 ~/.postgresql/ 目录下,尝试使用 psql 进行连接。
psql "host=localhost port=5432 user=pguser dbname=securitylearning sslmode=verify-full sslrootcert=root.crt sslkey=client.key sslcert=client.crt"

或者不需要密码。

两种方式都无法连接。在psql的情况下,我收到以下错误信息: psql: server closed the connection unexpectedly This probably means the server terminated abnormally before or while processing the request.


你查看了服务器日志文件吗?在尝试从程序中执行之前,你是否尝试过仅使用 psql 命令? - Patrick Mevzek
我尝试使用 psql "host=localhost port=5432 user=postgres dbname=securitylearning sslmode=verify-full sslrootcert=root.crt" 命令,但是出现了 psql: server closed the connection unexpectedly This probably means the server terminated abnormally before or while processing the request. 的错误提示。主机上的 psql 版本为 12.3(失败),容器中的版本为 12.2(成功)。日志中没有任何相关信息。 - Alexander Shelyugov
您没有描述如何创建客户端证书,但是您已经配置了使用clientcert。(虽然这不应该导致此错误消息,而应该是另一个错误消息)。 - jjanes
@jjanes 谢谢!我更新了问题。我没有考虑到这一点。另外,我尝试连接而没有客户端证书 - pg_hba.conf 中的md5选项也不起作用... - Alexander Shelyugov
1个回答

3

感谢评论中的建议,我成功地解决了它。

首先,按照建议,我退后一步并尝试采用更小的步骤进行。例如,从主机安全连接到psql

错误1

我忘记将以下属性添加到postgresql.conf中。

listen_addresses = '*'

根据文档

如果列表为空,则服务器根本不会侦听任何IP接口,这种情况下只能使用Unix域套接字进行连接。

错误2

我对证书及其通用名称(CN)存在一些误解。应将以下要点应用于创建证书的脚本中。简而言之:

  • CA的CN可以是任何内容,只要与服务器的CN不同即可。详情请见此问题和答案
  • 服务器的CN必须是客户端从中调用服务器的IP /主机名(这里是localhost。但如果数据库位于cooldatabase.com<-这将是服务器的CN)
  • 客户端的CN必须是我们将用来连接的用户名(这里是pguser

当我解决了这两个问题后-我成功通过psql和go程序进行了连接!此外,默认的postgresql.conf非常有用!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接