如何在Ubuntu14.04上使用Reprepro作为安全软件包存储库
包和存储库简介
我们都去过那里——需要一个项目——我们该怎么办? 我们大多数人只是 apt-get install postfix 和 presto! 我们神奇地安装了 Postfix。
不过,这并不是真正的魔法。 包管理器 apt-get 为您搜索、下载和安装包。 这非常方便,但是如果 apt-get 在其标准存储库列表中找不到您需要的程序怎么办? 值得庆幸的是,apt-get 允许用户指定自定义下载位置(称为存储库)。
在本教程中,我们将介绍如何设置您自己的安全存储库并将其公开以供其他人使用。 我们将在 Ubuntu 14.04 LTS Droplet 上创建存储库,并测试从另一个具有相同发行版的 Droplet 的下载。
要充分利用本指南,请务必查看我们的 使用 apt-get 管理软件包的教程。
先决条件
两个 Ubuntu 14.04 LTS Droplet
在指南结束时,您将拥有:
- 准备并发布存储库签名密钥
- 使用 Reprepro(存储库管理器)设置存储库
- 使用 Web 服务器 Nginx 公开存储库
- 在另一台服务器上添加了存储库
准备并发布签名密钥
首先,我们需要一个有效的包签名密钥。 这一步对于安全存储库至关重要,因为我们将对所有包进行数字签名。 包签名使下载者相信源是可信的。
在本节中,您将按照以下步骤生成加密的主公钥和签名子密钥:
- 生成主密钥
- 为包签名生成子密钥
- 从子密钥中分离主密钥
生成主密钥
让我们制作万能钥匙。 此密钥应妥善保管,因为这是人们所信任的。
在开始之前,让我们通过 apt-get 安装 rng-tools:
apt-get install rng-tools
GPG 需要随机数据(称为熵)来生成密钥。 熵通常由 Linux 内核随时间生成并存储在池中。 但是,在云服务器(如 Droplets)上,内核可能无法生成 GPG 所需的熵量。 为了帮助内核,我们安装了 rngd 程序(在 rng-tools 包中找到)。 该程序将向主机服务器(Droplet 所在的位置)询问熵。 一旦检索到,rngd
会将数据添加到熵池中,以供 GPG 等其他应用程序使用。
如果您收到这样的消息:
Trying to create /dev/hwrng device inode... Starting Hardware RNG entropy gatherer daemon: (failed). invoke-rc.d: initscript rng-tools, action "start" failed.
手动启动 rngd 守护进程:
rngd -r /dev/urandom
默认情况下,rngd 会寻找一个特殊的设备来从 /dev/hwrng 中检索熵。 某些 Droplet 没有此设备。 为了补偿,我们通过指定 -r 指令来使用伪随机设备 /dev/urandom。 有关更多信息,您可以查看我们的教程:如何设置附加熵。
现在我们有了一个熵池,我们可以生成主密钥。 通过调用命令 gpg 来执行此操作。 您将看到类似于以下的提示:
gpg --gen-key
gpg (GnuPG) 1.4.16; Copyright (C) 2013 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. 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) Your selection? 1
在提示中指定第一个选项“RSA 和 RSA(默认)”1
。 选择此选项将使 gpg 首先生成一个签名密钥,然后生成一个加密子密钥(都使用 RSA 算法)。 本教程不需要加密密钥,但正如一位伟人曾经说过的那样,“为什么不呢?” 两者兼有没有缺点,将来您可能会使用密钥进行加密。
点击 Enter,系统会提示您输入密钥大小:
RSA keys may be between 1024 and 4096 bits long. What keysize do you want? (2048) 4096
密钥大小与您希望主密钥的安全程度直接相关。 位大小越高,密钥越安全。 Debian 项目建议对任何签名密钥使用 4096 位,因此我将在此处指定 4096。 在接下来的 2-5 年内,如果您愿意使用默认位大小 2048 就足够了。 1024 的大小非常接近不安全,因此不应使用。
按 Enter 提示过期。
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) 0
主密钥通常没有到期日期,但只要您希望使用此密钥,就设置此值。 如果您只打算在接下来的 6 个月内使用此存储库,您可以指定 6m。 0 将使其永久有效。
按 输入 ,然后按 y。 系统将提示您生成“用户 ID”。 其他人和您自己将使用此信息来识别此密钥 - 所以请使用真实信息!
You need a user ID to identify your key; the software constructs the user ID from the Real Name, Comment and Email Address in this form: "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>" Real name: Mark Lopez Email address: mark.lopez@example.com Comment: You selected this USER-ID: "Mark Lopez <mark.lopez@example.com>" Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
如果信息正确,点击 o 和 Enter。 我们需要添加密码以确保只有您可以访问此密钥。 确保 记住此密码 ,因为无法恢复 gpg 密钥密码(这是一件好事)。
You need a Passphrase to protect your secret key. Enter passphrase: (hidden) Repeat passphrase: (hidden)
现在让一些魔法(数学)发生。 这可能需要一点时间,所以请坐下来或喝杯您最喜欢的饮料。
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. Not enough random bytes available. Please do some other work to give the OS a chance to collect more entropy! (Need 300 more bytes) +++++ ................+++++ 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 10E6133F marked as ultimately trusted public and secret key created and signed. gpg: checking the trustdb gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u pub 4096R/10E6133F 2014-08-16 Key fingerprint = 1CD3 22ED 54B8 694A 0975 7164 6C1D 28A0 10E6 133F uid Mark Lopez <mark.lopez@example.com> sub 4096R/7B34E07C 2014-08-16
现在我们有了万能钥匙。 输出显示我们创建了一个用于签名的主密钥(上面 pub 行上的`0E6133F)。 您的密钥将具有不同的 ID。 记下您的签名密钥的 ID(示例使用 10E6133F)。 在创建另一个用于签名的子密钥时,我们将在接下来的步骤中需要该信息。
为包签名生成子密钥
现在我们将创建第二个签名密钥,这样我们就不需要此服务器上的主密钥。 将主密钥视为授予子密钥权限的根权限。 如果用户信任主密钥,则暗示信任子密钥。
在终端执行:
gpg --edit-key 10E6133F
将示例 ID 替换为您的密钥 ID。 该命令将我们带入 gpg 环境。 在这里我们可以编辑我们的新密钥并添加一个子密钥。 您将看到以下输出:
gpg (GnuPG) 1.4.16; Copyright (C) 2013 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. Secret key is available. pub 4096R/10E6133F created: 2014-08-16 expires: never usage: SC trust: ultimate validity: ultimate sub 4096R/7B34E07C created: 2014-08-16 expires: never usage: E [ultimate] (1). Mark Lopez <mark.lopez@example.com> gpg>
在提示符下,键入 addkey
:
addkey
按输入。 GPG 将提示您输入密码。 输入用于加密此密钥的密码。
Key is protected. You need a passphrase to unlock the secret key for user: "Mark Lopez <mark.lopez@example.com>" 4096-bit RSA key, ID 10E6133F, created 2014-08-16 gpg: gpg-agent is not available in this session Enter passphrase: <hidden>
您将看到以下有关密钥类型的提示。
Please select what kind of key you want: (3) DSA (sign only) (4) RSA (sign only) (5) Elgamal (encrypt only) (6) RSA (encrypt only) Your selection? 4
我们要创建一个 signing 子密钥,所以选择“RSA (sign only)”4
。 RSA 对客户端更快,而 DSA 对服务器更快。 在这种情况下,我们选择 RSA,因为对于我们在包上制作的每个签名,可能需要数百个客户端对其进行验证。 这两种类型同样安全。
再次提示我们输入密钥大小。
RSA keys may be between 1024 and 4096 bits long. What keysize do you want? (2048) 4096
本教程使用 4096 来提高安全性。
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) 1y
我们已经有了一个主密钥,所以子密钥的过期时间就不那么重要了。 一年是一个很好的时间框架。
按 Enter,然后在接下来的两个提示中输入两次 y(是)。 一些数学运算会生成另一个密钥。
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. ............+++++ .+++++ pub 4096R/10E6133F created: 2014-08-16 expires: never usage: SC trust: ultimate validity: ultimate sub 4096R/7B34E07C created: 2014-08-16 expires: never usage: E sub 4096R/A72DB3EF created: 2014-08-16 expires: 2015-08-16 usage: S [ultimate] (1). Mark Lopez <mark.lopez@example.com> gpg>
在提示符下键入 save。
save
在上面的输出中,我们的主密钥中的 SC 告诉我们该密钥仅用于签名和认证。 E 表示密钥只能用于加密。 只有 S 才能正确看到我们的签名密钥。
请注意您的新签名密钥的 ID(该示例在上面的第二个 sub 行上显示了 A72DB3EF)。 您的密钥 ID 会有所不同。
输入 save 返回终端并保存新密钥。
save
从子密钥中分离主密钥
创建子密钥的目的是我们不需要服务器上的主密钥,这使得它更安全。 现在我们将把我们的主密钥从我们的子密钥中分离出来。 我们需要导出主密钥和子密钥,然后从 GPG 的存储中删除密钥,然后仅重新导入子密钥。
首先让我们使用 –export-secret-key 和 –export 命令导出整个密钥。 请记住使用您的主密钥 ID!
gpg --export-secret-key 10E6133F > private.key gpg --export 10E6133F >> private.key
默认情况下 –export-secret-key 和 –export 会将密钥打印到我们的控制台,因此我们将输出通过管道传输到一个新文件(private.key[X155X ])。 确保指定您自己的主密钥 ID,如上所述。
重要提示:在安全的地方复制 private.key 文件(不在服务器上)。 可能的位置在软盘或 USB 驱动器上。 此文件包含您的私钥、公钥、加密子密钥和签名子密钥。
将此文件备份到安全位置后,删除该文件:
#back up the private.key file before running this# rm private.key
现在导出您的公钥和子密钥。 确保更改 ID 以匹配主密钥和您生成的第二个子密钥(不要使用第一个子密钥)。
gpg --export 10E6133F > public.key gpg --export-secret-subkeys A72DB3EF > signing.key
现在我们已经备份了我们的密钥,我们可以从我们的服务器中删除我们的主密钥。
gpg --delete-secret-key 10E6133F
仅重新导入我们的签名子密钥。
gpg --import public.key signing.key
检查我们的服务器上是否不再有我们的主密钥:
gpg --list-secret-keys
sec# 4096R/10E6133F 2014-08-16 uid Mark Lopez <mark.lopez@example.com> ssb 4096R/7B34E07C 2014-08-16 ssb 4096R/A72DB3EF 2014-08-16
注意 sec 之后的 #。 这意味着我们的主密钥没有安装。 服务器只包含我们的签名子密钥。
清理你的钥匙:
rm public.key signing.key
您需要做的最后一件事是发布您的签名密钥。
gpg --keyserver keyserver.ubuntu.com --send-key 10E6133F
此命令将您的密钥发布到密钥的公共仓库——在本例中是 Ubuntu 自己的密钥服务器。 这允许其他人下载您的密钥并轻松验证您的包。
使用 Reprepro 设置存储库
现在让我们进入本教程的重点:创建一个 apt-get 存储库。 Apt-get 存储库不是最容易管理的东西。 谢天谢地 R。 伯恩哈德创建了 Reprepro,他曾经“生产、管理和同步 Debian 软件包的本地存储库”(也称为 Mirrorer)。 Reprepro 遵循 GNU 许可证并且完全开源。
安装和配置 Reprepro
可以从默认的 Ubuntu 存储库安装 Reprepro。
apt-get update apt-get install reprepro
Reprepro 的配置是特定于存储库的,这意味着如果您创建多个存储库,则可以有不同的配置。 让我们首先为我们的存储库做一个家。
为这个存储库创建一个专用文件夹并移动到它。
mkdir -p /var/repositories/ cd /var/repositories/
创建配置目录。
mkdir conf cd conf/
创建两个空配置文件(options 和 distributions)。
touch options distributions
在您喜欢的文本编辑器中打开 options 文件(默认安装 nano)。
nano options
此文件包含 Reprepro 的选项,每次运行 Reprepro 时都会读取该文件。 您可以在此处指定几个选项。 有关其他选项,请参阅手册。
在您的文本编辑器中添加以下内容。
ask-passphrase
ask-passphrase 指令告诉 Reprepro 在签名时请求 GPG 密码。 如果我们不将它添加到选项中,如果我们的密钥被加密(它是),Reprepro 将会死掉。
Ctrl + x 然后 y 和 Enter 将保存我们的更改并返回控制台。
打开 发行版 文件。
nano distributions
该文件有四个必需的指令。 将这些添加到文件中。
Codename: trusty Components: main Architectures: i386 amd64 SignWith: A72DB3EF
Codename 指令与已发布的 Debian 发行版的代码名称直接相关,并且是必需的。 这是将下载包的发行版的代号,不一定要与该服务器的发行版相匹配。 例如,Ubuntu 14.04 LTS 版本称为 trusty,Ubuntu 12.04 LTS 称为 precise,Debian 7.6 称为 wheezy。 此存储库适用于 Ubuntu 14.04 LTS,因此应在此处设置 trusty。
Components 字段是必需的。 这只是一个简单的存储库,因此在此处设置 main
。 还有其他命名空间,例如“non-free”或“contrib”——有关正确的命名方案,请参阅 apt-get。
Architectures 是另一个必填字段。 此字段列出了此存储库中的二进制体系结构,以空格分隔。 此存储库将托管 32 位和 64 位服务器的软件包,因此此处设置了 i386 amd64。 根据需要添加或删除架构。
为了指定其他计算机如何验证我们的包,我们使用 SignWith 指令。 这是一个可选指令,但对于签名是必需的。 此示例前面的签名密钥具有 ID A72DB3EF,因此在此处设置。 更改此字段以匹配您生成的子项 ID。
使用 Ctrl + `x 然后 y 和 Enter 保存并退出文件。
您现在已经为 Reprepro 设置了所需的结构。
使用 Reprepro 添加包
首先让我们将目录更改为临时位置。
mkdir -p /tmp/debs cd /tmp/debs
我们需要一些示例包来使用 - wget 它们与:
wget https://github.com/Silvenga/examples/raw/master/example-helloworld_1.0.0.0_amd64.deb wget https://github.com/Silvenga/examples/raw/master/example-helloworld_1.0.0.0_i386.deb
这些包纯粹是为本指南制作的,并包含一个简单的 bash 脚本来证明我们存储库的功能。 如果你愿意,你可以使用不同的包。
运行程序 ls
应该会给我们这样的布局:
ls
example-helloworld_1.0.0.0_amd64.deb example-helloworld_1.0.0.0_i386.deb
我们现在有两个示例包。 一个用于 32 位 (i386) 计算机,另一个用于 64 位 (amd64) 计算机。 您可以将它们都添加到我们的存储库中:
reprepro -b /var/repositories includedeb trusty example-helloworld_1.0.0.0_*
-b 参数指定存储库的“(b)ase”目录。 includedeb 命令需要两个参数 - < distribution code name > and < file path(s) >
。 Reprepro 将提示输入我们的子密钥密码两次。
Exporting indices... C3D099E3A72DB3EF Mark Lopez <mark.lopez@example.com> needs a passphrase Please enter passphrase: < hidden > C3D099E3A72DB3EF Mark Lopez <mark.lopez@example.com> needs a passphrase Please enter passphrase: < hidden >
成功!
列出和删除
我们可以使用 list 命令后跟代号列出托管包。 例如:
reprepro -b /var/repositories/ list trusty trusty|main|i386: example-helloworld 1.0.0.0 trusty|main|amd64: example-helloworld 1.0.0.0
要删除包,请使用 remove 命令。 删除命令需要包的代号和包名。 例如:
reprepro -b /var/repositories/ remove trusty example-helloworld
公开存储库
我们现在有一个包含几个包的本地包存储库。 接下来,我们将安装 Nginx 作为 Web 服务器以公开此存储库。
安装 Nginx
apt-get update apt-get install nginx
Nginx 安装了一个默认的示例配置。 制作该文件的副本以备下次查看。
mv /etc/nginx/sites-available/default /etc/nginx/sites-available/default.bak touch /etc/nginx/sites-available/default
现在我们有了一个空的配置文件,我们可以开始配置我们的 Nginx 服务器来托管我们的新存储库。
使用您喜欢的文本编辑器打开配置文件。
nano /etc/nginx/sites-available/default
并添加以下配置指令:
server { ## Let your repository be the root directory root /var/repositories; ## Always good to log access_log /var/log/nginx/repo.access.log; error_log /var/log/nginx/repo.error.log; ## Prevent access to Reprepro's files location ~ /(db|conf) { deny all; return 404; } }
Nginx 有一些非常合理的默认设置。 我们只需要配置根目录,同时拒绝访问 Reprepro 的文件。 有关更多详细信息,请参阅内嵌注释。
重启 Nginx 服务以加载这些新配置。
service nginx restart
您的公共 Ubuntu 存储库已准备好使用!
您需要您的 Droplet 的 IP 地址来让用户知道存储库的位置。 如果您不知道您的 Droplet 的公共地址,您可以使用 ifconfig 找到它。
ifconfig eth0
eth0 Link encap:Ethernet HWaddr 04:01:23:f9:0e:01 inet addr:198.199.114.168 Bcast:198.199.114.255 Mask:255.255.255.0 inet6 addr: fe80::601:23ff:fef9:e01/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:16555 errors:0 dropped:0 overruns:0 frame:0 TX packets:16815 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:7788170 (7.7 MB) TX bytes:3058446 (3.0 MB)
在上面的例子中,服务器的地址是 198.199.114.168。 你的会有所不同。
使用您的 Reprepro 服务器的 IP 地址,您现在可以将此存储库添加到任何其他适当的服务器。
从我们的新存储库安装一个包
如果您还没有,请使用 Ubuntu 14.04 LTS 启动另一个 Droplet,以便您可以从新存储库进行测试安装。
在新服务器上,下载您的公钥以验证存储库中的包。 回想一下,您将密钥发布到 keyserver.ubuntu.com。
这是通过 apt-key 命令完成的。
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 10E6133F
此命令下载指定的密钥并将密钥添加到 apt-get 数据库。 adv 命令告诉 apt-key 使用 GPG 下载密钥。 其他两个参数直接传递给 GPG。 由于您将密钥上传到“keyserver.ubuntu.com”,因此使用 –keyserver keyserver.ubuntu.com 指令从同一位置检索密钥。 这 --recv-keys 指令指定要添加的确切键。
现在添加 apt-get 的存储库地址以查找。 您需要上一步中的存储库服务器的 IP 地址。 这可以通过 add-apt-repository 程序轻松完成。
add-apt-repository "deb http://198.199.114.168/ trusty main"
注意我们给出的字符串 add-apt-repository。 大多数 Debian 存储库可以使用以下通用格式添加:
deb (repository location) (current distribution code name) (the components name)
存储库位置应设置为您的服务器的位置。 我们有一个 HTTP 服务器,所以协议是 http://。 该示例的位置是 198.199.114.168。 我们服务器的代号是 trusty。 这是一个简单的存储库,因此我们将组件称为“main”。
添加存储库后,请确保运行 apt-get update。 此命令将检查所有已知存储库的更新和更改(包括您刚刚创建的存储库)。
apt-get update
更新 apt-get 后,您现在可以从您的存储库安装示例包。 正常使用 apt-get 命令。
apt-get install example-helloworld
如果一切顺利,您现在可以执行 example-helloworld 并查看:
Hello, World! This package was successfully installed!
恭喜! 您刚刚从您创建的存储库中安装了一个包!
要删除示例包,请运行以下命令:
apt-get remove example-helloworld
这将删除您刚刚安装的示例包。
结论
在本指南中,您着手创建一个安全的 APT 存储库。 您已经学习了如何创建安全密钥来签署包; 如何使用 Reprepro 创建和管理存储库; 以及如何将此存储库添加到另一台服务器。 在此处查看其他指南,以进一步增加您对开源和 Linux 允许您做的酷事情的了解。