如何处理命令行上的秘密

2021-06-13 17:23:21

命令行真的没有设计秘密。因此,在命令行中保密秘密需要一些额外的照顾和努力。当时我的homelab我正在为grafana dataSource配置TLS客户端证书。目的是在续签证书时写一些我可以在计时器上运行的东西。命令需要:

当我写这个LIL'Bash管道时,我以为我是非常聪明的人:

Bearer_Token = mhy3b3i3gfpa9otnlqvznjyowlxpgjuod3idjwckrfuvtualgjoobucuf7w9hjfbu; jq-n --arg ca_cert" $(< $ steppath / certs / root_ca.crt)" - arg client_cert" $(< $ cert_location)" - arg client_key" $(< $ key_location)" -f ./datasource.jq |卷曲-s -x put-h"内容类型:应用程序/ json" -h"授权:持票人$ agor_token" -d" $(< / dev / stdin)" --cacert $ steppath / certs / root_ca.crt https:// grafana:3000 / api / datasources / 2

这使用JQ填充API请求(DataSource.jq)的JSON模板,其中包含--ARG传递的变量,然后用CURL放入API。很好,呵呵?

它没有长时间发现这个管道泄漏了整个地方的秘密:

所有这些值(包括私钥文件的贵重内容)可以通过PS在运行这些命令时看到。 PS通过/ proc /< pid> / cmdline找到它们,这是全局可读的任何进程ID。

为了制作赎罪,我正在写这篇文章。我们将在命令行上查看三种方法来处理秘密:使用管道数据,凭据文件和环境变量。我们将介绍这些方法的一些风险,以及如何尽可能安全地使用它们。

JQ -N --rawfile ca_cert $ steppath / certs / root_ca.crt --rawfile client_cert $ cert_location --rawfile client_key $ key_location -f ./datasource.jq | Curl -s -x put -h @api_headers -d @ - --cacert $ steppath / certs / root_ca.crt https:// grafana:3000 / api / datasources / 2

JQ中的--rawfile标志将凭据较近的凭据委托通过委派将文件数据从文件读取到JQ而不是bash的责任来使用它们。

一旦JQ将秘密JSON推入管道,CURL使用-d @ - 标志直接从管道拉动秘密数据并将其用作HTTPS请求主体。

作为消毒示例所示,一个管道通常是通过秘密的优秀方式,如果您使用的程序将通过STDIN接受秘密。因为管道只有两端,因为右边?想象一下,自己悄悄地悄悄地进入一个管道的一端,以及让他们的耳朵到另一个的朋友。这就是这样。

通常。以上所示的$(< / dev / stdin)漏漏使用整洁的bash替代品来制造其他安全的管道不安全。例如,如果您运行:

什么是不喜欢文件的?它有一个主人。它具有权限和访问控制。给每个秘密一个文件!接受秘密的任何程序都应该能够通过传递文件名或通过将文件重定向到STDIN来接受它们。您还可以使用文件将秘密传递到Docker容器中,其中包含卷。

您可能希望加密文件的内容 - 但是,您需要弄清楚如何处理加密密钥。

使用环境变量秘密非常方便。我们不推荐它,因为它很容易泄漏东西:

一些操作系统仍然使每个过程的环境变量是世界可读的。 (但是,在我看过的所有Linuxes中,/ proc /< pid> / Environ不是世界可读的。)

在Docker中,任何具有访问Docker守护程序的人都可以使用Docker检查以查看任何运行容器的所有环境变量。

在SystemD中,通过DBUS接口向用户提供单元文件中的环境变量(请参阅使用凭据文件的替代方案的最近引入LoadCredential =)

导出的环境变量将通过每个新流程,然后谁知道它们会发生什么。它们可能会被抛弃到stdout或记录到调试日志文件。

环境变量可以很容易地结束shell历史记录。在许多shell中,在命令将从shell历史记录隐藏它之前添加额外的空间。 (在Bash中,HistControl变量必须设置为IgnorEspace)

如果命令拿一个密码文件名,则可以使用密码环境变量如果您谨慎。例如,假设我们有一个$ step_ca_password环境变量,我们在bash中运行以下内容:

使用此<()处理替换的语法,Bash将使用回声-n&#34的输出创建文件; $ step_ca_password"并将该文件的名称供应到--password文件。

谢天谢地,在Bash,答案是没有。我们被呼气是一个内置的事实保存,并且永远不会创建一个过程。因此,如果您能够以否则保护环境变量,则这种方法是安全的。缺点是它看起来不安全,因为$ step_ca_password仍然被替换为肯定看起来像它的命令。

秘密经理可能很大,因为它们可以更轻松地让秘密更接近他们使用的位置。例如,码头容器可以向秘密经理呼叫其秘密。但是,秘密经理是一个额外的依赖性.Ooften你需要运行一个秘密经理服务器并点击一个API.and,即使使用秘密经理,您仍可能需要Bash将秘密穿梭于您的目标应用程序中。对于此帖我专注于更轻质的解决方案。

谈到轻量级解决方案,Linux内核存在键控设施。Linux keyring提供了几个范围,用于安全地将键在内存中存储,以便将永远不会交换给Disk.A进程甚至单个线程可以拥有自己的钥匙圈,或者您可以在用户会话中跨所有进程继承的密钥环。要管理keyrings和键,请使用keyctl命令或keyctl系统调用。

如果它没有大量清晰,这是非常不安全的。命令的调用者无法选择隐藏命令行是世界可读的。因此,任何值得其盐的CLI命令不应直接接受密码。

如果您在一个安全的容器内运行,您可能会在命令中直接传递密码。但是,为什么要承担风险?

现在,即使这个人也有一个警告。你有没有这样经营mysql?

为方便起见,这些命令接受密码以防止他们更好的判断力。 但是,在启动时立即,他们将用空白值覆盖argv,有效地隐藏秘密。 如果在此处显示的Curl命令期间运行PS,则会查看: 现在,如果系统过多,则可以从/ proc /< pid> / cmdline可以覆盖它的机会覆盖它的机会。 但是,这种方法完全没有。 这些密码可以很容易地结束审核日志或shell历史记录。 所以,最好完全避免这种方法。 卷曲的替代方案是凭据文件:.NETRC文件可用于存储所需服务器的凭据。 对于MySQL,您可以创建选项文件:.my.cnf或混淆.mylogin.cnf将在启动时读取,可以包含您的密码。