使用 ssl 证书和带有卷的密钥部署 postgresql docker

Posted

技术标签:

【中文标题】使用 ssl 证书和带有卷的密钥部署 postgresql docker【英文标题】:Deploying postgresql docker with ssl certificate and key with volumes 【发布时间】:2019-07-31 00:25:35 【问题描述】:

我正在尝试部署一个 postgresql 容器。我正在尝试使用卷将 ssl 证书和密钥放入容器中,但我无法获得正确的权限。这些文件需要容器的postgres 用户可读,但也有有限的权限(600)。

是否可以使用卷,或者我必须为此覆盖 Dockerfile?

谢谢。

【问题讨论】:

【参考方案1】:

可以将密钥和证书挂载到 postgres 容器中,以便 postgres 从那里使用它们。但是您将不得不面对server.key 的所有者和权限的问题。

来自PostgreSQL Documentation on this subject:

在 Unix 系统上,server.key 的权限必须禁止任何 进入世界或团体;通过命令 chmod 0600 实现这一点 服务器.key。或者,该文件可以由 root 拥有并具有 组读权限(即 0640 权限)。

这意味着您必须:

    server.key 文件的所有者设置为rootpostgres。 根据 server.key 文件的所有者,您必须分别设置 600640 权限。 (更新:这里暗示该文件的组所有者是一个包含postgres用户的组,就像默认的postgres组)

如果您在 Windows 主机上工作,您将很难处理。因为您映射到容器中的卷中任何文件的权限都是-rwxr-xr-x (755),而所有者是root。只要文件是从 Windows 卷安装的,您就无法更改此设置。如果您尝试在文件上使用chmod,它只会静默失败。

另一方面,如果您使用的是 linux 主机,则可以轻松完成此操作。来自主机系统的权限将保留在映像中。所有权也将如此。我的意思是,server.key 的数字所有者和组所有者在它们被卷映射到容器时将被保留。在主机和容器之间,它们共享 linux ACL,因此它们只是观察文件的相同属性。 (所有者、组所有者、权限)。所以如果你的宿主机本地linux用户有UID:GID1000:1000,而你创建了server.key文件,那么文件的UID:GID也将设置为1000:1000。如果您随后将文件映射到容器中,并从内部观察它 - 它也只会看到 1000:1000。这意味着,当从 linux 主机映射时,我们可以从容器内部和外部控制 UID:GID

注意。您指定为文件所有者的UID 的用户不一定是用户,允许设置不存在的文件UID:GID 所有者。 p>

在 postgres alpine 派生图像中,postgres 用户/组具有 UID:GID 70:70。在debian 派生上,postgres UID:GID999:999。毫不奇怪,root 对它们都有0:0

这意味着要么必须:

    更改文件server.keyUID:GID我们启动容器之后,当卷已经挂载时。 更改文件server.keyUID:GID我们启动容器之前

由于在容器启动后设置它,将意味着篡改 postgres 映像的启动脚本 - 让我们选择在启动容器之前设置它们。在您挂载它们的本地文件系统中。

600 权限和postgres 设置为server.key 的所有者

如果您要使用alpine 衍生产品,您需要将所有者/组更改为70:70。如果您使用的是debian 派生类,则使用999:999

您的主机上可能没有用户,例如 UID: 70,但这不是问题。

示例:

chown 70:70 server.key # 70:70 for alpine, 999:999 for debian
chmod 600 server.key

640 权限和root 设置为server.key 的所有者

这个例子也适用于高山图像

示例:

chown 0:70 server.key
chmod 640 server.key

此时您可以开始了。您只需要将密钥和证书映射到容器中,然后像往常一样启动 postgres。

解决方案(linux/unix/macOS)

我将在此处包含一个脚本 sn-p,它将为您完成所有关于 alpine 衍生产品的工作。此示例将设置 server.key 的 root 所有者和 postgres 组所有者。

# generate the server.key and server.crt
openssl req -new -text -passout pass:abcd -subj /CN=localhost -out server.req
openssl rsa -in privkey.pem -passin pass:abcd -out server.key
openssl req -x509 -in server.req -text -key server.key -out server.crt

# set postgres (alpine) user as owner of the server.key and permissions to 600
chown 0:70 server.key
chmod 640 server.key

# start a postgres docker container, mapping the .key and .crt into the image.
docker run -d --name postgres \ 
  -v "$PWD/server.crt:/var/lib/postgresql/server.crt:ro" \
  -v "$PWD/server.key:/var/lib/postgresql/server.key:ro" \
  postgres:11-alpine \
  -c ssl=on \
  -c ssl_cert_file=/var/lib/postgresql/server.crt \
  -c ssl_key_file=/var/lib/postgresql/server.key

我希望这能解决问题?

生成密钥和证书的密钥来源是this gist。

自己构建映像(Windows 解决方案)

我将包含一个关于如何自己构建映像的小指南,以便您可以拥有一个带有 ssl 的 postgres 数据库容器。这也适用于 Windows。

这里是 Dockerfile 将为您做这件事:

Dockerfile

FROM postgres:11-alpine

# On Windows root will own the files, and they will have permissions 755
COPY server.key /var/lib/postgresql/server.key
COPY server.crt /var/lib/postgresql/server.crt

# update the privileges on the .key, no need to touch the .crt  
RUN chmod 600 /var/lib/postgresql/server.key
RUN chown postgres:postgres /var/lib/postgresql/server.key

构建镜像:

docker build -t mypg:01 .

然后运行:

docker run -d --name postgres mypg:01 \
  -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt \
  -c ssl_key_file=/var/lib/postgresql/server.key

【讨论】:

我无法使用 root 作为所有者和 0640 作为权限使其工作。数据库因 `FATAL: could not load private key file "/etc/postgresql/server.key": Permission denied`而失败。 感谢@AndreasLorenzen 非常详细的回答!我尝试了两种方法(chown 70:70 && chmod 0600chown 0:70 && chmod 640),第一种方法我得到could not load private key file "/var/lib/postgresql/server.key": Permission denied,第二种方法我得到private key file "/var/lib/postgresql/server.key" has group or world access 此解决方案似乎在 osx 中不起作用,显然该文件未使用正确的所有者安装。 mac os x 的任何解决方案? 好吧,在这种情况下,您自己构建映像的“Windows”解决方案会起作用。如果您可以确认它可以这样工作,我将更新答案。我添加了 macos,因为我读到当某个地方的卷映射文件时,docker 不会以这种方式运行 - 但情况似乎并非如此。不过,我没有 Mac 可以在自己身上进行测试。【参考方案2】:

在我的特殊情况下,我有兴趣在通过 docker-compose 使用标准 postgres 映像时启用 SSL。此解决方案允许initdb 照常运行,这对于设置数据库和用户很有用。

docker-compose.yaml

version: '3'
services:
  postgres:
    image: postgres:12.2
    environment:
      - POSTGRES_PASSWORD=password
      - POSTGRES_USER=myuser
      - POSTGRES_HOST_AUTH_METHOD=trust
    volumes:
      - ./postgres-initdb:/docker-entrypoint-initdb.d/
      - ./postgres-certs/:/var/lib/postgresql/certs/

postgres-initdb/config.sql

ALTER SYSTEM SET ssl_cert_file TO '/var/lib/postgresql/certs/server.crt';
ALTER SYSTEM SET ssl_key_file TO '/var/lib/postgresql/certs/server.key';
ALTER SYSTEM SET ssl TO 'ON';

这适用于任何配置。对于 SSL,您还需要生成证书(取自 a Gist):

set -euo pipefail

mkdir postgres-certs
cd postgres-certs

openssl req -new -text -passout pass:abcd -subj /CN=localhost -out server.req -keyout privkey.pem
openssl rsa -in privkey.pem -passin pass:abcd -out server.key
openssl req -x509 -in server.req -text -key server.key -out server.crt
chmod 600 server.key
test $(uname -s) == Linux && chown 999 server.key

【讨论】:

这个答案提供了服务器证书/密钥,如何获取客户端证书/密钥以使用 ssl=verify-full 连接到 postgres? 恐怕我帮不了你@steve。这仅用于开发目的。

以上是关于使用 ssl 证书和带有卷的密钥部署 postgresql docker的主要内容,如果未能解决你的问题,请参考以下文章

带有自签名 ssl 证书的 prometheus 的 Https

如何用宝塔面板部署HTTPS/SSL证书

如何在linux下安装ssl证书?

Liberty SSL证书密钥不再起作用

带有 SSL 和证书的 iPhoneHTTPServer

如何使用 Postgres 数据库 url 字符串传递证书路径以进行 SSL 连接