Podman签署和分发容器镜像

Podman签署和分发容器镜像

签署容器镜像的动机是只信任专门的镜像提供者以减轻中间人 (MITM) 攻击或对容器注册表的攻击。签署镜像的一种方法是使用 GNU Privacy Guard ( GPG ) 密钥。这种技术通常与任何符合 OCI 的容器注册表兼容,例如:Quay.io。值得一提的是,OpenShift 集成容器注册表开箱即用地支持这种签名机制,这使得单独的签名存储变得不必要。

从技术角度来看,我们可以利用 Podman 对镜像进行签名,然后再将其推送到远程注册表。之后,所有运行 Podman 的系统都必须配置为从远程服务器检索签名,远程服务器可以是任何简单的 Web 服务器。这意味着在镜像拉取操作期间,每个未签名的镜像都将被拒绝。

在使用 Podman 和 GPG 对容器镜像进行签名时,通常需要考虑四个主要事项:

  1. 我们需要签名机器上的有效 GPG 私钥和每个系统上的相应公钥,这将拉取镜像
  2. Web 服务器必须在可以访问签名存储的地方运行
  3. 必须在任何 /etc/containers/registries.d/*.yaml文件中配置 Web 服务器
  4. 每个镜像拉取系统都必须配置为包含强制策略配置policy.conf

生成GPG密钥

//生成GPG密钥
[root@localhost ~]# gpg --full-gen-key
gpg (GnuPG) 2.2.20; Copyright (C) 2020 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

gpg: directory '/root/.gnupg' created
gpg: keybox '/root/.gnupg/pubring.kbx' created
Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
  (14) Existing key from card
Your selection?							#默认回车
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048)		#默认回车
Requested keysize is 2048 bits
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0)					#默认回车
Key does not expire at all
Is this correct? (y/N) y				#按y该密钥不会过期

GnuPG needs to construct a user ID to identify your key.

Real name: guguniao						#用户id
Email address: yf1121@163.com			#邮箱
Comment: xxxx							#描述
You selected this USER-ID:
    "guguniao (xxxx) <yf1121@163.com>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o		#按O提交
//第一个方框是设置给密钥设置密码,第二个是确认密码。
                                 ┌──────────────────────────────────────────────────────┐
                                 │ Please enter the passphrase to                       │
                                 │ protect your new key                                 │
                                 │                                                      │
                                 │ Passphrase: ********________________________________ │
                                 │                                                      │
                                 │       <OK>                              <Cancel>     │
                                 └──────────────────────────────────────────────────────┘

                                 ┌──────────────────────────────────────────────────────┐
                                 │ Please re-enter this passphrase                      │
                                 │                                                      │
                                 │ Passphrase: ********________________________________ │
                                 │                                                      │
                                 │       <OK>                              <Cancel>     │
                                 └──────────────────────────────────────────────────────┘
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: /root/.gnupg/trustdb.gpg: trustdb created
gpg: key 75A8BC88C9C0AC53 marked as ultimately trusted
gpg: directory '/root/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/root/.gnupg/openpgp-revocs.d/6308282BF98C9D14D7F9F85875A8BC88C9C0AC53.rev'
public and secret key created and signed.

pub   rsa2048 2022-08-15 [SC]
      6308282BF98C9D14D7F9F85875A8BC88C9C0AC53
uid                      guguniao (xxxx) <yf1121@163.com>
sub   rsa2048 2022-08-15 [E]

//查看已有的密钥
[root@localhost ~]# gpg --list-keys yf1121@163.com
pub   rsa2048 2022-08-15 [SC]
      6308282BF98C9D14D7F9F85875A8BC88C9C0AC53
uid           [ultimate] guguniao (xxxx) <yf1121@163.com>
sub   rsa2048 2022-08-15 [E]

部署私有仓库

有两种方案:

  • 在另一台主机部署harbor私有仓库

​ 👉部署harbor的文章

  • 直接在本机部署私有仓库,如下面操作所示
[root@localhost ~]# podman run -d -p 5000:5000 docker.io/registry
c579dd21d3addd8a181aede2c30bb9c349a5f65d3d62256f747473d7c205a8f5
[root@localhost ~]# podman container ps
CONTAINER ID  IMAGE                              COMMAND               CREATED         STATUS             PORTS                   NAMES
c579dd21d3ad  docker.io/library/registry:latest  /etc/docker/regis...  24 seconds ago  Up 24 seconds ago  0.0.0.0:5000->5000/tcp  trusting_hellman

拉取alpine镜像作为本次用作测试的镜像

//拉取镜像
[root@localhost ~]# podman pull docker://docker.io/alpine:latest
Trying to pull docker.io/library/alpine:latest...
Getting image source signatures
Copying blob 59bf1c3509f3 done
Copying config c059bfaa84 done
Writing manifest to image destination
Storing signatures
c059bfaa849c4d8e4aecaeb3a10c2d9b3d85f5165c66ad3a4d937758128c4d18

给alpine打上标签指向本地的私有仓库

[root@localhost ~]# podman tag alpine localhost:5000/alpine

为了让镜像能上传至本地的私有仓库,修改/etc/containers/registries.d/default.yaml文件

我们可以看到我们配置了两个签名存储:

  • sigstore: 引用 Web 服务器进行签名读取
  • sigstore-staging: 引用文件路径进行签名写入
[root@localhost ~]# vim /etc/containers/registries.d/default.yaml
default-docker:
  sigstore: http://localhost:8000 	  #添加这一行
  sigstore-staging: file:///var/lib/containers/sigstore

推送alpine镜像至本地的私有仓库

//--tls-verify是访问私有仓库时需要https验证,=false是关闭https验证。
//--sign-by是使用指定的密钥在目标处添加签名,这里填生成该密钥的邮箱
[root@localhost ~]# podman push --tls-verify=false --sign-by yf1121@163.com localhost:5000/alpine
Getting image source signatures
Copying blob 8d3ac3489996 done
Copying config c059bfaa84 [======================================] 1.4KiB / 1.4KiB
Writing manifest to image destination
Signing manifest
Storing signatures
                            ┌────────────────────────────────────────────────────────────────┐
                            │ Please enter the passphrase to unlock the OpenPGP secret key:  │
                            │ "guguniao (xxxx) <yf1121@163.com>"                             │
                            │ 2048-bit RSA key, ID 75A8BC88C9C0AC53,                         │
                            │ created 2022-08-15.                                            │
                            │                                                                │
                            │                                                                │
                            │ Passphrase: ********__________________________________________ │
                            │                                                                │
                            │         <OK>                                    <Cancel>       │
                            └────────────────────────────────────────────────────────────────┘

现在看一下系统签名存储,我们会看到有一个新的签名可用,这是由镜像推送引起的。每推送一个镜像就会生成一个签名。

[root@localhost ~]# ls /var/lib/containers/sigstore
'alpine@sha256=964248be4bb8e3052c8b411271126f70c5c5015df31e014bfc41fad50edf78d8'

前面编辑的默认签名存储 /etc/containers/registries.d/default.yaml引用了一个正在监听的 Web 服务器 http://localhost:8000。我们只需在本地临时签名存储中启动一个新服务器:

//下载python3,下面的命令会需要此依赖关系
[root@localhost ~]# dnf -y install python36
//启动用来读取签名的web服务器
[root@localhost ~]# bash -c 'cd /var/lib/containers/sigstore && python3 -m http.server'
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
192.168.92.1 - - [15/Aug/2022 21:48:47] "GET / HTTP/1.1" 200 -
192.168.92.1 - - [15/Aug/2022 21:48:47] code 404, message File not found
192.168.92.1 - - [15/Aug/2022 21:48:47] "GET /favicon.ico HTTP/1.1" 404 -

image-20220815214859577
删除本地镜像用来进行拉取镜像验证

[root@localhost ~]# podman rmi docker.io/alpine localhost:5000/alpine
Untagged: docker.io/library/alpine:latest
Untagged: localhost:5000/alpine:latest
Deleted: c059bfaa849c4d8e4aecaeb3a10c2d9b3d85f5165c66ad3a4d937758128c4d18

我们必须编写一个策略来强制签名必须是有效的。这可以通过在 中添加新规则来完成/etc/containers/policy.json

[root@localhost ~]# vim /etc/containers/policy.json
{
  "default": [{ "type": "insecureAcceptAnything" }],
  "transports": {
    "docker": {
      "localhost:5000": [		#把这一行修改为localhost:5000
        {
          "type": "signedBy",
          "keyType": "GPGKeys",
          "keyPath": "/tmp/key.gpg"		#修改为密钥所在的位置
        }
      ]
    }
  }
}

这个/tmp/key.gpg位置的密钥不存在,因此我们必须将GPG密钥放在那里

[root@localhost ~]# gpg --output /tmp/key.gpg --armor --export yf1121@163.com

拉取本地私有仓库中的alpine镜像,目前也只有这一个镜像,因为我们创建这个私有仓库后只上传了这个镜像

/拉取镜像
[root@localhost ~]# podman pull --tls-verify=false localhost:5000/alpine
Trying to pull localhost:5000/alpine:latest...
Getting image source signatures
Checking if image destination supports signatures
Copying blob 3c4e9198e8c1 done
Copying config c059bfaa84 done
Writing manifest to image destination
Storing signatures
c059bfaa849c4d8e4aecaeb3a10c2d9b3d85f5165c66ad3a4d937758128c4d18

拉取镜像后我们可以在web服务器的日志中看到签名被访问过

127.0.0.1 - - [15/Aug/2022 22:00:30] "GET /alpine@sha256=964248be4bb8e3052c8b411271126f70c5c5015df31e014bfc41fad50edf78d8/signature-2 HTTP/1.1" 404 -
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值