-
Notifications
You must be signed in to change notification settings - Fork 10
/
search_plus_index.json
1 lines (1 loc) · 667 KB
/
search_plus_index.json
1
{"./":{"url":"./","title":"Introduction","keywords":"","body":"前言 2021 年 12 月 09 日,HashiCorp 在纳斯达克成功挂牌上市。作为 HashiCorp 多年的老粉,实在是感慨万千。近年来开源软件作为一种商业模式一再被证明行之有效,初创团队可以借由一款优秀的开源工具吸引投资,快速迭代,培育一个茁壮的社区,在社区影响力足够大,渗透到足够多的企业后,再提供各种付费增值服务实现盈利,这对更多的走在这条路上的创业团队来说无疑是一个好消息。 从社区活跃度来看 HashiCorp 最受欢迎和知名度最高的产品是 Terraform,已经有大量相关的文章、教程(笔者也为 Terraform 在中国的推广做了一点 V 小的工作)。但实际上 HashiCorp 提供的是一个立体的自动化云计算运营框架,而 Terraform 也只是这个立体框架中的一个组成部分,想要把云玩好,我们还需要更多。 HashiCorp 将云的自动化分为四个层面: 基础设施 安全 网络 应用程序 HashiCorp 为这四层分别设计了四种主要工具,就是: 基础设施:Terraform 安全:Vault 网络:Consul 应用程序:Nomad 可以说 HashiCorp 宇宙中这四个产品是一等公民,它们彼此之间还有很多相互协同的功能,构成了一个立体的矩阵,另外再辅以二线辅助产品 Vagrant、Packer、Boundary、Waypoint。这些产品都有一些共同的特征,例如跨平台、单一可执行文件、基于 Hashicorp Go-Plugin 的插件化设计,以及使用 HCL (HashiCorp Configuration Language)作为主力配置语言等等。 笔者曾经把 Terraform 文档中关键部分翻译为中文电子书《Terraform 入门教程》,帮助了不少想要学习 Terraform 但不想阅读英文文档的朋友。曾经我写过《HashiCorp 全家桶》系列的文章,浮光掠影地介绍过一个同时使用 Terraform、Consul 和 Nomad 实现一个简单的云原生平台的案例,但当时因为精力和能力有限,并没有把 Vault 纳入;后来总觉得缺了一块,于是开始学习 Vault,这个 Vault 学习之旅给我带来了很大的震撼,我意识到曾经我的许多的做法在 Vault 诞生后都是错误的,或者说是过时的了。 随后看到越来越多的网络安全事件,例如各种数据泄露、心怀不满的员工恶意删除、泄漏重要数据等等。最重要的事件是 2021 年 06 月 10 日,第十三届全国人民代表大会常务委员会第二十九次会议通过了《中华人民共和国数据安全法》,数据安全、信息安全的重要性已经被国家立法确定,对现在的企业来说,数据安全工作是决定企业生死存亡的高压线,而国内业界对此普遍是处于懵懂和不知所措的阶段的。 云时代需要的是动态防御 提到“固若金汤”,传统语境下想象到的是重兵防守森严壁垒的,钢筋混泥土修筑的立体化防线,由雷区、铁丝网、拒马、各种明碉暗堡和多层次远程火力所筑成,敌人哪怕付出惨痛代价也无法越雷池一步 遗憾的是历史表明,这种成本高昂的静态防线一般起不到什么作用,想要把敌人完全抵挡在外部是一种天真的想法,进攻者可能会以各种令人匪夷所思的方式悄悄绕过重兵布防的防线而出现在空虚的大后方。传统的基于数据中心的信息安全体系往往被设计成类似这样的静态防线,主要的防御被设立在系统的边界之上,这种防御思想如同马奇诺防线一样,认为只要把外部流量隔离在外部,使其无法窥探到内部的信息就可以高枕无忧。这种思想存在三个问题: 如同马奇诺防线一样,你永远无法确定攻击者找不到任何途径得到内部的信息,首先内部员工本身就有可能是恶意攻击者 一旦攻击者设法获取了某些内部机密信息,很难阻止他悄悄地执行恶意行为,因为这些机密信息很难更换,这就导致我们倾向于永远不更新这些信息,使得攻击者一旦得手后就可以持续进行攻击 我们在边界上的层层防卫虽然无法彻底消除攻击的风险,但却能成功地给正常的开发维护工作带来障碍。我们越是增加审批流程,我们的迭代速度就越慢,就有越多的人力浪费在通过层层审批和各种隔离上。在高度动态化的云时代这种矛盾显得尤其突出 HashiCorp Vault 针对安全防护提出了不同的思路,它不再把防护看作是一个静态的、重型的,卡在边关上的防线,而是提出了一种动态的、轻量级的,运行在系统纵深方方面面的一个防护过程。它提倡动态机密,一次一密,如果性能允许,我们甚至可以为生产环境的每一个容器,公司的每一名开发,他们使用受 Vault 保护的系统的每一次会话都建立一个专属的临时的拥有最小权限的安全凭证,并且该凭证必须绑定一个有效期,这个有效期可以是静态的(例如10分钟有效),也可以是动态的(只在进程、容器运行期间有效)。这一秒使用的密码,可能下一秒就变了;数据库中保存的敏感数据这一秒还是用这个版本的密钥加密的,下一秒可能就更新成用另一个新版本密钥加密了。一切的机密都是动态的,一切的机密都绑定了一个尽可能短的生命周期,即使敌人攻进了纵深,它所面临的仍然是一个高度设防、高度动态的流动的环境;即使敌人得到了数据,数据也因为对应的密钥被按时轮换掉而无法解密。古希腊哲学家赫拉克利特说:“人不能两次踏进同一条河流”,Vault 说:“你不应该两次使用同一个机密”。旧时代的安全防护尽量阻止变更,而云原生时代高频率的变更提升了安全防护。 曾经我心中发了一个愿,想要把 HashiCorp 的这四大产品逐一翻成中文教程,为中国云计算与信息技术的提升做一份贡献,我愿做一块垫脚石,让社区能够通过我的这一点工作广泛接纳 HashiCorp 的工具链,进而接纳这一套自动化运营模型,如果能够进一步发展出属于我们中国的更先进的工具链和模型那真是善莫大焉。 在 Terraform 之后,我曾经在 Vault 和 Consul 之间犹豫过,但在看到那么多信息安全事件后我认为中国的技术社区更加迫切地需要大规模采用 Vault,或者说是同等级的类似产品。于是就这么定了下来,陆陆续续写了一些有关 Vault 的文章,想着积攒到一定程度后能够编纂成一套系统性的教程。在 2021 年底意外获得了一个超过一周的假期,由于零星散发的疫情,所以不想冒险远行度假,索性就静下心来用这个假期为 Vault 翻译一册中文手册。 与编写《Terraform 入门教程》时一样,本书的编写借鉴了 HashiCorp 官方的认证考试《HashiCorp Security Automation Certification》所编写的考纲,力求能够深入浅出地使读者能够掌握如何使用 Vault,如何围绕着 Vault 设计一套安全的机密管理体系,如何将这套体系与其他日常使用的工具和工作流整合起来。 如果读者满足以下一条或者多条情况,那么可能就是本书的目标读者: 从事运维、开发或者架构工作 好奇如何提升整个 DevOps 生命周期的安全性 对 HashiCorp 技术栈感兴趣 不喜欢或者没有时间阅读英文文档 本书编写时使用的是 Vault 1.9.2 版本。HashiCorp 的主流产品在近年获得大额融资后迭代速度非常快,如果读者发现某些内容已经由于版本更新而不再准确时敬请包涵,并且以官方文档为准。我个人有一种预感,那就是本书将永远无法完整描述 Vault 的方方面面,也无法做到完全准确,它将是一本永远在编写中的书,而这其实是一件非常好的事情。 如同 Terraform 一样,今天的 Vault 实际上有三个版本:Vault 开源版,Vault 企业版,以及HashiCorp 托管的 Vault 云服务版。本书将聚焦于开源版的内容,对其他版本感兴趣的读者敬请自行查阅相关文档。 另外由于 Vault 的开放式设计,它可以与大量三方系统进行集成,涵盖了几乎所有主流的云平台以及需要进行机密管理的常见系统。以笔者一人之力,哪怕是用尽假期也是无法涵盖这许许多多的产品和平台,所以只能依照笔者任性的个人喜好选择一二进行翻译,如有没有包括的部分烦请移步官方文档。笔者也无法同时精通这些不同平台和产品的知识,虽然竭尽自己驽钝的脑力也无法确保对各种知识的肤浅理解没有错误,所以具有相关领域专业知识的读者如果读到什么地方感觉到不对,请不要犹豫,这几乎可以肯定是笔者能力不足导致的错误,敬请以你的专业知识为准。 由于笔者的能力有限,书中如有错误在所难免,敬请方家届时能够不吝斧正赐教。另外因为很早就开始想写这本书,陆陆续续攒了一些零散章节,现在趁假期一并汇编成集,不免会由于写作时间跨越几年而导致前后出现不一致或是重复叙述某个概念,万望读者海涵,在此先行谢过。 "},"1.什么是vault/1.secret_sprawl.html":{"url":"1.什么是vault/1.secret_sprawl.html","title":"机密蔓生的危害及其治理","keywords":"","body":"机密蔓生的危害及其治理 当我们讨论信息安全时,不论是身份认证、权限控制、数据加密,一个绕不过去的核心问题就是对机密的管理。何谓机密?所谓机密就是用以代表某个特定身份来执行认证与授权管理的信息,举例来说,数据库的账号密码是机密,调用 API 的令牌(Token)是机密,用来认证服务器与客户端身份的证书是机密,加解密关键数据的密钥也是机密。对机密的管理和保护必然是信息安全体系中的核心,是重中之重,外部攻击者一旦获取到机密信息就可以冒充合法程序和用户,对系统安全和数据安全造成严重威胁。 机密蔓生的问题 何谓机密蔓生(Secret Sprawl)?其实在传统的研发场景中我们经常能够看到这样的问题:生产环境的数据库用户名密码被以明文形式编码在配置文件中,或是保存在生产环境中某个不设防的配置管理服务中;某个应用调用云服务接口所用的令牌被硬编码在代码里,随后不小心被程序员上传到一个公共的 Github 仓库中等等。 机密蔓生描述的就是这样的问题,系统机密信息被零散地以不同形式保存在许多彼此不相关的地方,我们即不知道哪些系统保存了哪些机密,也不知道哪些机密被哪些系统哪些人获取了。即使我们发现某个机密信息流失在外,我们也无法回溯该机密信息是何人申请使用,何人泄漏,我们也无法确信立即从生产环境中吊销该机密不会导致生产环境的故障,因为我们不知道哪些系统正在使用这些机密。 机密蔓生引发了一连串的严重问题,导致我们既不能事前防备机密泄漏,也无法事后快速响应进行损害管制。 针对机密蔓生的治理 治理机密蔓生问题最有效的手段就是集中管理机密。 DevOps 运动、持续交付(CI)以及基础设施即代码(Infrastructure as Code)都强调要建立生产环境的单一事实来源(Single Source Of Truth),也就是生产环境的一切都源自于同一个源头,最佳实践是源自于某一个 Git 仓库中的代码。坚持对生产环境的变更只能通过对这个 Git 仓库提交代码变更来实现,我们可以把对一个复杂系统的治理限制为对有限个代码仓库的变更管理。 管理机密问题,杜绝机密蔓生也是一样的思路,建立机密的单一事实来源,这个单一事实来源可以是目前公有云平台普遍提供的密钥管理服务(KMS),也可以是像 HashiCorp Vault 这样的开源、多云平台的软件。坚持所有的机密信息都是从单一事实来源获取,并且使用工具链确保单一事实来源中管理的机密发生变更时能够及时通知相关的合法应用更新机密信息,同时对谁有权使用什么样的机密、谁正在使用这些机密,某个机密是谁申请的这些信息进行集中管理,可以有效治理机密蔓生问题。 "},"1.什么是vault/2.什么是vault.html":{"url":"1.什么是vault/2.什么是vault.html","title":"什么是 Vault","keywords":"","body":"什么是 Vault 用 Thoughtworks 技术雷达的描述: 在企业级应用开发过程中,团队每时每刻都需要管理各种各样的私密信息,从个人的登陆密码、到生产环境的 SSH Key 以及数据库登录信息、API 认证信息等。通常的做法是将这些机密信息保存在某个文件中,并且放置到 git 之类的源代码管理工具中。个人和应用可以通过拉取仓库来访问这些信息。但这种方式弊端很多,比如跨团队分享存在安全隐患、文件格式难以维护、私密信息难以回收等。 尤其是在微服务如此风靡的今天,如何让开发者添加私密信息、应用程序能轻松的获取私密信息、采用不同策略更新私密信息、适时回收私密信息等变得越来越关键。所以企业需要一套统一的接口来处理私密信息的方方面面,而 HashiCorp Vault 就是这样的一款工具。 简单来说,在我们日常的工作中,免不了要和许多的机密信息打交道,可以是云服务的 Access Key 和 Secret Key,也可以是生产服务器的证书、SSH 口令、证书,或者数据库的用户名密码。以往在工作中我们经常面临着这样的问题: 执行密码轮换策略很痛苦 掌握机密的员工离职后可能泄密或是恶意报复 开发者不小心把机密信息随着代码上传到公网的源码仓库造成泄密 管理多个系统的机密非常麻烦 需要将机密信息安全地加密后存储,但又不想将密钥暴露给应用程序,以防止应用程序被入侵后连带密钥一起泄漏 Vault 就是用来解决这些问题的利器。 一个AWS的例子 考虑一个最简单的 Terraform 脚本: variable \"access_key\" {} variable \"secret_key\" {} provider \"aws\" { region = \"us-east-1\" access_key = var.access_key secret_key = var.secret_key } 如果我们运行这段 Terraform 的话: $ terraform apply var.access_key Enter a value: AKIAZXN6JNH5KFERRZNM var.secret_key Enter a value: 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 An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # aws_vpc.example will be created ...... 糟了!为了向读者们演示这个例子,我不小心把我的 AWS Access Key 和 Secret Key 暴露给了读者(请大家放过笔者,不要去用这些信息做坏事)。 如果使用 Vault 我们如何规避这个问题? 启动Vault 我们首先到 Vault 下载页面(如果是 Mac 用户,使用 brew 更快捷)下载对应版本的 Vault 可执行文件。为了给大家演示,我们使用最简单的 dev 模式启动,该模式绝对不可用在生产环境中。 $ export VAULT_ADDR=http://127.0.0.1:8200 $ vault server -dev ==> Vault server configuration: Api Address: http://127.0.0.1:8200 Cgo: disabled Cluster Address: https://127.0.0.1:8201 Go Version: go1.17.5 Listener 1: tcp (addr: \"127.0.0.1:8200\", cluster address: \"127.0.0.1:8201\", max_request_duration: \"1m30s\", max_request_size: \"33554432\", tls: \"disabled\") Log Level: info Mlock: supported: false, enabled: false Recovery Mode: false Storage: inmem Version: Vault v1.9.2 Version Sha: f4c6d873e2767c0d6853b5d9ffc77b0d297bfbdf+CHANGES ==> Vault server started! Log data will stream in below: ... WARNING! dev mode is enabled! In this mode, Vault runs entirely in-memory and starts unsealed with a single unseal key. The root token is already authenticated to the CLI, so you can immediately begin using Vault. You may need to set the following environment variable: $ export VAULT_ADDR='http://127.0.0.1:8200' The unseal key and root token are displayed below in case you want to seal/unseal the Vault or re-authenticate. Unseal Key: gyxfaC82CzIMeSkhYvlQBbX25+X0uKKiBiH/b5CA8V4= Root Token: s.XNTda9o6L4BEgQONQ6Xad9RY Development mode should NOT be used in production installations! 在这里值得我们注意的是输出中的 Root 令牌,在这里是s.0hiFJxxBsQGNKHjsfmray9r0。这个 Token 就是我们登陆Vault进行配置所需要的凭证: $ vault login s.0hiFJxxBsQGNKHjsfmray9r0 Success! You are now authenticated. The token information displayed below is already stored in the token helper. You do NOT need to run \"vault login\" again. Future Vault requests will automatically use this token. Key Value --- ----- token s.0hiFJxxBsQGNKHjsfmray9r0 token_accessor aYkWkRyTEsaqbSn4duRp1dTy token_duration ∞ token_renewable false token_policies [\"root\"] identity_policies [] policies [\"root\"] 这样就是登陆成功,这时我们是 Vault 系统的 root 用户,拥有最高的权限。如果您看到的是这样的: $ vault login s.XNTda9o6L4BEgQONQ6Xad9RY Error authenticating: error looking up token: Get \"https://127.0.0.1:8200/v1/auth/token/lookup-self\": http: server gave HTTP response to HTTPS client 这是由于您当前的会话没有配置VAULT_ADDR这个环境变量,Vault 默认会访问 https://127.0.0.1:8200 这个地址,而我们的 Vault 服务是-dev模式,没有配置证书和 tls,所以无法访问。我们只需要配置VAULT_ADDR: $ export VAULT_ADDR='http://127.0.0.1:8200' $ vault login s.XNTda9o6L4BEgQONQ6Xad9RY 就可以了。 Vault 是一个基于插件的开放式系统,它使用被称为机密引擎(Secrets Engine)的插件来操作不同系统的机密信息。在我们配置 AWS 信息之前,我们要首先启用AWS插件: $ vault secrets enable aws Success! Enabled the aws secrets engine at: aws/ Vault 的数据结构是一个虚拟的树形文件系统,在不指定-path参数时启用一个 Secrets Engine,默认就是赋予这个 Engine 在同名路径下拥有操作权限,在这里 aws 插件对应的路径就是aws/,当然我们也可以指定一个路径。这个路径用处很大,比如我们可以是 aws/事业线/团队/项目 这样,就可以把不同部门不同团队不同产品的机密分开管理。 下面我们要配置 AWS 插件: $ vault write aws/config/root \\ access_key=AKIAZXN6JNH5D6ZSHS5V \\ secret_key=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 \\ region=us-east-1 Success! Data written to: aws/config/root 笔者把刚才用的 AWS Access Key 和 Secret Key 配置到了 aws/config/root 下,这样在 aws/ 路径下执行的 AWS 操作,都会使用这组 Key 来执行。 下面我们要创建一个角色,让它可以操作 VPC: $ vault write aws/roles/vpc \\ credential_type=iam_user \\ policy_document=- 这时假设我们重新用刚才的 Terraform 脚本创建基础设施,在创建前我们先用 Vault 读一个具有刚才创建的 VPC 角色的 AWS Access Key 和 Secret Key: $ vault read aws/creds/vpc Key Value --- ----- lease_id aws/creds/vpc/w8OTDxIri80TCkGtoZPkc6Qc lease_duration 768h lease_renewable true access_key AKIAZXN6JNH5CKXB2JPO secret_key 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 security_token 我们会看到 Vault 返回了一组 access_key 和 secret_key,而且和我们刚才配置到 root 里的 key 是不同的。另外我们可以看到两个信息: lease_id aws/creds/vpc/w8OTDxIri80TCkGtoZPkc6Qc lease_duration 768h Vault 中把动态生成的机密信息关联了一个 lease 租约,每个租约都有对应的 id 以及有效时间,当租约过期后 Vault 会调用插件,删除租约所对应的凭证。目前我们生成的这组 Key 在 768 小时内是可用的。 让我们试试看使用它们: $ terraform destroy -force var.access_key Enter a value: AKIAZXN6JNH5CKXB2JPO var.secret_key Enter a value: w6lEdAzhhdtbIQyfTGXLiHYltYBaKP9OkyZJ/X1S aws_vpc.example: Refreshing state... [id=vpc-078628cc3ea3d053a] aws_vpc.example: Destroying... [id=vpc-078628cc3ea3d053a] aws_vpc.example: Destruction complete after 2s Destroy complete! Resources: 1 destroyed. ByersdeMacBook-Pro:aws_vpc_sample byers$ terraform apply var.access_key Enter a value: AKIAZXN6JNH5FHUGY2DF var.secret_key Enter a value: MOheb/FOzR1HZFBhVXJ1jMpRkbRzb1vxOlMt4Xk3 An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # aws_vpc.example will be created + resource \"aws_vpc\" \"example\" { + arn = (known after apply) + assign_generated_ipv6_cidr_block = false + cidr_block = \"10.0.0.0/16\" + default_network_acl_id = (known after apply) + default_route_table_id = (known after apply) + default_security_group_id = (known after apply) + dhcp_options_id = (known after apply) + enable_classiclink = (known after apply) + enable_classiclink_dns_support = (known after apply) + enable_dns_hostnames = (known after apply) + enable_dns_support = true + id = (known after apply) + instance_tenancy = \"default\" + ipv6_association_id = (known after apply) + ipv6_cidr_block = (known after apply) + main_route_table_id = (known after apply) + owner_id = (known after apply) } Plan: 1 to add, 0 to change, 0 to destroy. Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value: yes aws_vpc.example: Creating... aws_vpc.example: Still creating... [10s elapsed] aws_vpc.example: Creation complete after 19s [id=vpc-048217f069fc891d1] Apply complete! Resources: 1 added, 0 changed, 0 destroyed. 是成功的。如果我们登陆 AWS 的 Web 控制台,去 IAM 看一下,我们可以看到刚才创建的用户: 需要注意的是,这组 key 的有效时间是 768 小时,如果您是在笔者撰写本文的 32 天之内读到这篇文章的话,您仍然可以使用这组 Key,这可不妙。我们可以吊销这个租约: $ vault lease revoke aws/creds/vpc/w8OTDxIri80TCkGtoZPkc6Qc All revocation operations queued successfully! 然后,对应的 iam 用户就被删除了: 当然我们也可以设置生成的 Key 的有效时间: $ vault secrets tune -default-lease-ttl=2m aws/ $ vault read aws/creds/vpc Key Value --- ----- lease_id aws/creds/vpc/6HULqs5ESLcjRZ6cxO1vrlYY lease_duration 2m lease_renewable true access_key AKIAZXN6JNH5FSRANNAB secret_key 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 security_token 我们设置 aws/ 路径下的机密信息默认的有效期是 2 分钟,这样这组 Key 在 2 分钟以后就会被 Vault 删除(所以你们别试了) 通过这种方式,管理员可以把高权限的机密保存在 Vault 里,然后根据实际需求配置各团队所需要的目录、账号、权限以及策略,团队根据自己的身份可以动态获取机密,并且不用担心泄密问题。 "},"1.什么是vault/3.vault_security_model.html":{"url":"1.什么是vault/3.vault_security_model.html","title":"Vault 安全模型","keywords":"","body":"Vault 的安全模型 由于 Vault 的性质及其管理的数据的机密性,Vault 的安全模型非常关键。 Vault 安全模型的总体目标是提供机密性、完整性、可用性、问责制和身份验证。 这意味着数据在传输中以及落盘后都必须是安全的,不会被窃听或篡改。客户端必须经过适当的身份验证和授权才能访问数据或修改策略。所有交互都必须是可审计的,并且可以唯一地追溯到原始实体。系统必须能够抵御故意绕过其访问控制的任何尝试。 威胁模型 以下是 Vault 威胁模型的各个部分: 对 Vault 通信的窃听。客户端与 Vault 的通信,以及从 Vault 到其存储后端,或 Vault 集群节点之间的通信应该是安全的,不会被窃听。 篡改已存储的或传输中的数据。任何篡改都应该是可检测的,并导致 Vault 中止事务处理。 未经身份验证以及授权的数据访问或是系统控制。所有的请求都必须按照相关的安全策略进行。 对数据的访问或管理系统的行为无法审计。如果启用审计日志,所有的请求与响应都必须在客户端接收到任何机密材料之前被记录下来。 系统存储的机密的机密性。任何通过 Vault 保存在存储后端的数据都必须是安全的,不会被窃听。实际上,这意味着所有落盘数据都必须加密。 系统发生故障时机密材料的可用性。Vault 支持在高可用性配置中运行,以避免失去可用性。 以下部分不在 Vault 威胁模型范围内: 防止对存储后端的随意控制。可以对存储后端执行任意操作的攻击者可以通过多种难以或不可能防御的方式破坏安全性。例如,攻击者可以删除或破坏存储后端的所有内容,从而导致 Vault 的全部数据丢失。控制读取的能力将允许攻击者以众所周知的状态进行快照并回滚状态更改,如果这对他们有利的话。 防止机密材料的存在性的泄露。可以读取存储后端的攻击者可能会观察到机密材料的存在和存储,即使它是保密的。 防止对正在运行的 Vault 进行内存分析。如果攻击者能够检查正在运行的 Vault 实例的内存状态,那么数据的机密性可能会受到威胁。 防止 Vault 使用的外部系统或服务中的缺陷。一些身份验证方法或机密引擎将敏感操作委托给 Vault 外部的系统。如果攻击者可以破坏凭据或以其他方式利用这些外部系统中的漏洞,则数据的机密性或完整性可能会受到损害。 防止底层主机上的恶意插件或代码执行。如果攻击者可以获得对底层主机的代码执行或写入权限,那么数据的机密性或完整性可能会受到损害。 外部威胁综述 鉴于 Vault 的架构,我们关注 3 个不同的 Vault 系统。 客户端,它通过 API 与 Vault 对话 Vault 服务,它提供 API 和服务请求 存储后端,服务器利用它来读取和写入数据 Vault 客户端和服务器之间没有默认的相互信任。客户端使用 TLS 来验证服务器的身份并建立安全的通信通道。服务器要求客户端为用于识别客户端的每个请求提供客户端令牌。只允许没有令牌的客户端发出登录请求。 集群内 Vault 实例之间的所有服务器之间的流量(即高可用性、企业复制或集成存储)都使用相互验证的 TLS 来确保传输中数据的机密性和完整性。节点在加入集群之前通过解封质询或一次性激活令牌进行身份验证。 Vault 使用的存储后端在设计上也是不受信任的。 Vault 对向后端发出的所有请求使用安全屏障(Security Barrier)。安全屏障使用具有 96 位随机数的 Galois Counter Mode (GCM) 的 256 位高级加密标准 (AES) 密码自动加密离开 Vault 的所有数据。随机数是为每个加密对象随机生成的。当从安全屏障读取数据时,在解密过程中验证 GCM 身份验证标签以检测数据有无篡改。 使用某些后端时,Vault 可能会通过 TLS 与后端通信,以提供额外的安全层。在某些情况下,例如文件后端不需要这样做。由于存储后端是不可信的,即使与后端的通信被拦截,窃听者也只能访问加密数据。 内部威胁综述 在 Vault 系统中,一个关键的安全问题是攻击者试图访问他们无权访问的机密材料。如果攻击者已被允许对 Vault 进行某种级别的访问并且能够进行身份验证,则这是一种内部威胁。 当客户端首次通过 Vault 进行身份验证时,auth 身份验证方法用于验证客户端的身份并返回关联的 ACL 策略列表。该关联是由 Vault 的运维团队提前配置的。例如,\"engineering\" 团队中的 GitHub 用户可能会映射到 \"engineering\" 和 \"ops\" Vault 策略。然后 Vault 生成一个客户端令牌,它是一个随机生成的序列化值,并将其映射到策略列表,然后将此客户端令牌返回给客户端。 在每个请求中,客户端都会提供此令牌。然后 Vault 使用它来检查令牌是否有效并且没有被吊销或过期,并根据关联的策略生成 ACL。 Vault 使用严格的默认拒绝执行策略。这意味着除非相关策略允许给定操作,否则它将被拒绝。每个策略都指定了授予 Vault 中路径的访问级别。合并策略时(如果多个策略与客户端相关联),则使用允许的最高访问级别。例如,如果 \"engineering\" 策略允许对\"eng/\" 路径进行读或者更新操作,而 \"ops\" 策略允许对 \"ops/\" 路径进行读访问,则用户将获得这些路径的并集。使用最具体的定义策略匹配策略,可能是完全匹配或最长前缀匹配 glob 模式。有关详细信息,请参阅策略语法。 某些操作仅允许 \"root\" 用户操作,这是 Vault 中内置的一项杰出策略。这类似于 Unix 系统上的 root 用户或 Windows 上的管理员的概念。尽管可以为客户端提供根令牌或与根策略相关联,但 Vault 支持 \"sudo\" 特权的概念。作为策略的一部分,用户可能会被授予某些路径的 \"sudo\" 权限,这样他们仍然可以执行安全敏感操作,而无需被授予对 Vault 的全局 root 访问权限。 最后,Vault 支持通过 Shamir 算法支持双人规则解封。当 Vault 启动时,它以密封状态启动。这意味着 Vault 此时并不知晓从存储后端读取和写入所需的加密密钥。解封过程需要提供主密钥,以便可以获取加密密钥。分发主密钥的风险在于,有权访问它的单个恶意行为者可以解密整个 Vault。相反,Shamir 算法允许我们将主密钥拆分为多个分片或多个部分。分片的数量和所需的阈值是可配置的,但默认情况下,Vault 会生成 5 个分片,必须提供其中的任意 3 个才能重建主密钥。 通过使用机密分片算法,我们避免了对主密钥持有者的绝对信任,并完全避免存储主密钥。只能通过重建分片来检索主密钥。这些分片对向 Vault 提出任何请求没有用处,只能用于解封。解封后,标准 ACL 机制将用于所有请求。 打个比方,银行将保险箱放在保险库内。每个保险箱都有一把钥匙,而金库门既有密码也有钥匙。拱顶被钢和混凝土包裹着,所以门是唯一可以通行的入口。类似于 Vault,加密算法是保护数据的钢筋混凝土。虽然可以通过混凝土隧道或暴力破解加密密钥,但这将非常耗时。打开银行金库需要两个因素:钥匙和组合。同样,Vault 需要提供多个分片来重建主密钥。解封后,每个保险箱仍然需要所有者提供密钥,同样,Vault ACL 系统会保护所有存储的机密。 "},"1.什么是vault/4.安装Vault.html":{"url":"1.什么是vault/4.安装Vault.html","title":"安装 Vault","keywords":"","body":"安装 Vault Linux HashiCorp 官方提供了签名的 Vault 安装包: Ubuntu/Debian 先添加 HashiCorp GPG key: $ curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add - 然后添加 HashiCorp 官方的 Linux 仓库: $ sudo apt-add-repository \"deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main\" 更新 apt 并安装: $ sudo apt-get update && sudo apt-get install vault 注意,如果要安装 Vault 企业版,请将 install vault 替换成 install vault-enterprise。 另外,因为我们已经添加了 HashiCorp 的仓库,所以此时我们也可以通过 apt 安装 Terraform、Consul、Nomad 以及 Packer。 CentOS/RHEL 首先安装 yum-config-manager 来管理我们的仓库: $ sudo yum install -y yum-utils 使用 yum-config-manager 添加 HashiCorp 官方 Linux 仓库: $ sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo 安装 Vault: $ sudo yum -y install vault 注意,如果要安装 Vault 企业版,请将 install vault 替换成 install vault-enterprise。 另外,因为我们已经添加了 HashiCorp 的仓库,所以此时我们也可以通过 apt 安装 Terraform、Consul、Nomad 以及 Packer。 Fedora 安装 dnf config-manager 来管理我们的仓库: $ sudo dnf install -y dnf-plugins-core 使用 dnf config-manager 添加 HashiCorp 官方 Linux 仓库: $ sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/fedora/hashicorp.repo 安装 Vault: sudo dnf -y install vault 注意,如果要安装 Vault 企业版,请将 install vault 替换成 install vault-enterprise。 另外,因为我们已经添加了 HashiCorp 的仓库,所以此时我们也可以通过 apt 安装 Terraform、Consul、Nomad 以及 Packer。 Amazon Linux 首先安装 yum-config-manager 来管理我们的仓库: $ sudo yum install -y yum-utils 使用 yum-config-manager 添加 HashiCorp 官方 Linux 仓库: $ sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo 安装 Vault: $ sudo yum -y install vault 注意,如果要安装 Vault 企业版,请将 install vault 替换成 install vault-enterprise。 另外,因为我们已经添加了 HashiCorp 的仓库,所以此时我们也可以通过 apt 安装 Terraform、Consul、Nomad 以及 Packer。 Windows Windows 下可以使用 Chocolatey 安装 Vautl (链接是安装 Chocolatey 的官方说明): >choco install vault 需要注意的是,Chocolatey 中的 Vault 安装包不是 HashiCorp 官方维护的。 macOS 要在 macOS 下使用 Homebrew 安装 Vault,我们必须确保 Xcode 的命令行工具已经如 Homebrew 安装文档中描述的安装好了。如果安装 Vault 的过程中遇到了像 xcrun: error: invalid active developer path 的错误,用户需要确认自己的 Xcode 命令行工具是否已经妥善安装,然后重试。 使用 Homebrew 安装 Vault,首先要添加 HashiCorp 官方仓库: $ brew tap hashicorp/tap 然后安装 Vault: $ brew install hashicorp/tap/vault 可以用以下命令来升级: $ brew upgrade hashicorp/tap/vault 验证安装成功 安装完 Vault 后,可以执行 vault 命令来验证 Vault 是否已经正确安装: $ vault Usage: vault [args] Common commands: read Read data and retrieves secrets write Write data, configuration, and secrets delete Delete secrets and configuration list List data or secrets login Authenticate locally agent Start a Vault agent server Start a Vault server status Print seal and HA status unwrap Unwrap a wrapped secret Other commands: audit Interact with audit devices auth Interact with auth methods debug Runs the debug command kv Interact with Vault's Key-Value storage lease Interact with leases monitor Stream log messages from a Vault server namespace Interact with namespaces operator Perform operator-specific tasks path-help Retrieve API help for paths plugin Interact with Vault plugins and catalog policy Interact with policies print Prints runtime configurations secrets Interact with secrets engines ssh Initiate an SSH session token Interact with tokens 如果执行 vault 命令时报错反馈找不到该命令,那么可能是 PATH 环境变量没有被正确设置,请确认 PATH 环境变量包含了 Vault 可执行文件所在的路径。 "},"1.什么是vault/5.启动标准的Vault服务.html":{"url":"1.什么是vault/5.启动标准的Vault服务.html","title":"启动一个标准的 Vault 服务","keywords":"","body":"启动一个标准的 Vault 服务 如果读者还记得我们之前使用 dev 模式启动的过程时会发现,dev 模式启动后是直接输出了 Root 令牌,并且直接可以登录的: $ vault server -dev ==> Vault server configuration: Api Address: http://127.0.0.1:8200 Cgo: disabled Cluster Address: https://127.0.0.1:8201 Go Version: go1.17.5 Listener 1: tcp (addr: \"127.0.0.1:8200\", cluster address: \"127.0.0.1:8201\", max_request_duration: \"1m30s\", max_request_size: \"33554432\", tls: \"disabled\") Log Level: info Mlock: supported: false, enabled: false Recovery Mode: false Storage: inmem Version: Vault v1.9.2 Version Sha: f4c6d873e2767c0d6853b5d9ffc77b0d297bfbdf+CHANGES ==> Vault server started! Log data will stream in below: ... WARNING! dev mode is enabled! In this mode, Vault runs entirely in-memory and starts unsealed with a single unseal key. The root token is already authenticated to the CLI, so you can immediately begin using Vault. You may need to set the following environment variable: $ export VAULT_ADDR='http://127.0.0.1:8200' The unseal key and root token are displayed below in case you want to seal/unseal the Vault or re-authenticate. Unseal Key: Rfmlaa+GXHAyGMHjtxPgfd6ZVSY01zA4MVdRfnYpxkA= Root Token: s.j9YXmamyMC1xnD13iCooz0W3 Development mode should NOT be used in production installations! 然而这只能是用作演示,不可用于生产环境,比如在 dev 模式下,我们写入 Vault 的所有信息实际上都是保存在内存中的,服务一旦重启数据就将全部丢失。 一个生产环境推荐的 Vault 架构如下: HashiCorp 推荐使用 Consul 作为 Vault 的数据存储 Backend(推荐但并不强制,Vault 支持丰富的数据存储 Backend)。在一个生产环境中,我们将会部署一个 Consul 集群,但是在本书中为了演示方便,我们将使用 dev 模式启动 Consul: $ consul agent -dev 然后我们要准备一个 Vault 的配置文件,我们就起名叫 config.hcl 吧: storage \"consul\"{ address = \"127.0.0.1:8500\" path = \"vault/\" } listener \"tcp\"{ address = \"127.0.0.1:8200\" tls_disable = 1 } 我们在 storage 配置节中声明使用本机启动的 Consul 作为 Backend 存储,路径在 vault/ 下(这是 Consul KV 存储的路径),并且在本机 8200 端口提供服务。由于是演示使用,我们没有为 Vault 准备证书,所以我们通过 tls_disable = 1 禁用了 tls。 准备好了配置文件后,我们启动 Vault: $ vault server -config=config.hcl WARNING! mlock is not supported on this system! An mlockall(2)-like syscall to prevent memory from being swapped to disk is not supported on this system. For better security, only run Vault on systems where this call is supported. If you are running Vault in a Docker container, provide the IPC_LOCK cap to the container. ==> Vault server configuration: Api Address: http://127.0.0.1:8200 Cgo: disabled Cluster Address: https://127.0.0.1:8201 Go Version: go1.17.5 Listener 1: tcp (addr: \"127.0.0.1:8200\", cluster address: \"127.0.0.1:8201\", max_request_duration: \"1m30s\", max_request_size: \"33554432\", tls: \"disabled\") Log Level: info Mlock: supported: false, enabled: false Recovery Mode: false Storage: consul (HA available) Version: Vault v1.9.2 Version Sha: f4c6d873e2767c0d6853b5d9ffc77b0d297bfbdf+CHANGES ==> Vault server started! Log data will stream in below: 值得注意的是我们看到了一个 WARNING 信息,提示我们当前操作系统不支持 mlock。 mlock 是操作系统提供的一个系统调用,它的作用是防止操作系统把进程的内存换出到磁盘上的虚拟内存中。由于 Vault 中保存的是企业最关键的机密信息,如果内存中的明文数据被写入磁盘,就存在泄密的可能,所以生产环境必须启用 mlock。笔者测试的环境使用的是 mac,作为演示笔者不再折腾 mlock。 细心的读者会发现,与使用 dev 模式相比,使用配置文件走正常流程启动 Vault 后我们没有看到 Root 令牌,这是怎么一回事呢? 我们知道生产环境下 Vault 中的机密数据是保存在 Backend 中的,那如果 Backend 中的数据被未授权的人获取了怎么办?比如有内鬼把硬盘做了快照,然后偷偷在其他地方复原了? Vault 保存在 Backend 中的数据都是加密的。那么第二个问题就是,密钥存在哪里?如果密钥本身也是存在 Backend 中的,那么攻击者复制了硬盘以后,也会得到这个密钥,还是会造成泄密。 Vault 将这个密钥称为 Master Key 主密钥,Vault 默认使用 Shamir 算法,把主密钥切分成 M 份,管理员必须至少提供其中的 N 份才能还原出主密钥(这里的 M 和 N 都是可配置的,M>=N)理想状态下,我们必须把这 M 份密钥分配给公司内 M 个不同的人,只有在获取其中 N 个人的授权后,Vault 才可以成功解密主密钥,开始工作。Vault 刚启动时由于无法解密主密钥,所以处于 Seal 封印状态,这时 Vault 是无法进行任何操作的。解密主密钥的过程称为 Unseal 解封。 dev 模式启动的 Vault 实际上帮助我们自动地把创建主密钥、解封的步骤都完成了,所以我们启动后可以立即得到 Root 令牌。生产模式部署的 Vault 在启动后,我们还需要一系列的后续动作: $ vault operator init Unseal Key 1: 4g8FTfPiyE5f/6vzURVm+ljTEfOCRQPXSZp1WteK1rJu Unseal Key 2: si53pSe8AulsCZelBPFypf1dyuNeLgUVGKdVfbNWc3+3 Unseal Key 3: qhONs6vqG7eO7DeRgatuCbVPMZ2X06g/U6gnWVliO+de Unseal Key 4: H8GNnfQdkeACSuHo4dyj+yNrRTs/UEVZbYWHNn6drRR3 Unseal Key 5: uSIbqitWpwcfzP4Tg3y4GJO7NIxRQ+qiy0ZbJBvmHSpY Initial Root Token: s.n3YQoQMDILejIuWPcQBIGW3Q Vault initialized with 5 key shares and a key threshold of 3. Please securely distribute the key shares printed above. When the Vault is re-sealed, restarted, or stopped, you must supply at least 3 of these keys to unseal it before it can start servicing requests. Vault does not store the generated master key. Without at least 3 keys to reconstruct the master key, Vault will remain permanently sealed! It is possible to generate new unseal keys, provided you have a quorum of existing unseal keys shares. See \"vault operator rekey\" for more information. 在这里我们先执行了 vault operator init 命令,它创建了 Master Key 和 Root 令牌,并且返回了 5 条 Unseal Key。从后续输出中我们可以得知,我们至少需要提供 3 条 Unseal Key 才能成功解封。执行 init 操作的管理员这时应立即把 Unseal Key 用安全的方式传递给 5 个不同的人,并且确保他们彼此不知道其他人的 Key。除了这 5 个人以外,不应该以任何方式在任何地方保存这些 Unseal Key,Vault 也不会以任何形式保存这些 Key。这 5 个人应挑选公司中拥有很高地位,工作岗位稳定的人员,比如 CEO/CTO/董事长等(个人建议) 让我们看一下当前 Vault 的状态: $ vault status Key Value --- ----- Seal Type shamir Initialized true Sealed true Total Shares 5 Threshold 3 Unseal Progress 0/3 Unseal Nonce n/a Version 1.9.2 Storage Type consul HA Enabled true vault status 命令的输出告诉我们,当前 Vault 已成功初始化,但处于 Seal 状态。Seal 用的是 Shamir 算法,需要提供 5 条中的 3 条 Unseal Key 才能解封,当前已经输入了 0 条 Unseal Key。 需要指出的是,由于在提供 3 条 Unseal Key 之前,Vault 自身也是无法知道已经接收到的 Unseal Key 是否正确的,所以这里的 Unseal Progress 只代表已经接受了多少条 Unseal Key,不代表它们都是正确的。 在封印状态下如果我们尝试使用 Root 令牌登陆会怎么样: $ vault login s.n3YQoQMDILejIuWPcQBIGW3Q Error authenticating: error looking up token: Error making API request. URL: GET http://localhost:8200/v1/auth/token/lookup-self Code: 503. Errors: * Vault is sealed Vault 拒绝了我们的操作,告诉我们,Vault 当前正处于封印状态。 让我们输入一条 Unseal Key: $ vault operator unseal 4g8FTfPiyE5f/6vzURVm+ljTEfOCRQPXSZp1WteK1rJu Key Value --- ----- Seal Type shamir Initialized true Sealed true Total Shares 5 Threshold 3 Unseal Progress 1/3 Unseal Nonce f0cd0a86-16e3-d097-c4bc-bb4692ae16be Version 1.9.2 Storage Type consul HA Enabled true 我们可以看到,Unseal Progress 变成了 1/3,Sealed 状态仍然为 true。让我们输入剩下的两条: $ vault operator unseal si53pSe8AulsCZelBPFypf1dyuNeLgUVGKdVfbNWc3+3 Key Value --- ----- Seal Type shamir Initialized true Sealed true Total Shares 5 Threshold 3 Unseal Progress 2/3 Unseal Nonce f0cd0a86-16e3-d097-c4bc-bb4692ae16be Version 1.9.2 Storage Type consul HA Enabled true $ vault operator unseal qhONs6vqG7eO7DeRgatuCbVPMZ2X06g/U6gnWVliO+de Key Value --- ----- Seal Type shamir Initialized true Sealed false Total Shares 5 Threshold 3 Version 1.9.2 Storage Type consul Cluster Name vault-cluster-cf228f17 Cluster ID f715d277-6934-1bf4-70fe-b04e16fdf2e5 HA Enabled true HA Cluster n/a HA Mode standby Active Node Address 可以看到,输入第二条时 Vault 仍然处于封印状态,而输入第三条后,Sealed 变成了 fasle,Vault 成功解封。这五条 Unseal Key 可以任选三条,可以以任意顺序输入,单条 Unseal Key 重复输入是无效的。让我们重新尝试登陆: $ vault login s.n3YQoQMDILejIuWPcQBIGW3Q Success! You are now authenticated. The token information displayed below is already stored in the token helper. You do NOT need to run \"vault login\" again. Future Vault requests will automatically use this token. Key Value --- ----- token s.n3YQoQMDILejIuWPcQBIGW3Q token_accessor 3IVU2OOmeJ6uPpwWIxENzML8 token_duration ∞ token_renewable false token_policies [\"root\"] identity_policies [] policies [\"root\"] 这次就成功了,Vault 解封后就可以正常操作了。 可以看到的是,在 Vault 的工作中,Master Key 处于一个至关重要的位置,它太过重要以至于必须严加保管。所以执行 init 后输出 Unseal Key 的那个瞬间,应该在它们的生命周期中仅有的一次聚首,从此这批 Unseal Key 绝对不可以彼此相见,必须交给独立的不同的人进行保管。 当拥有 Root 权限的管理员确信系统面临着迫在眉睫的威胁时,也可以立即重新封印 Vault: $ vault operator seal Success! Vault is sealed. 这时 Vault 又重新进入了封印状态,需要重新解封才能正常工作。 重建 Root 令牌 由于 Root 令牌代表的是 Valut 中权限最高的账号,所以 Root 令牌仅应被用于初始化后创建其他管理员账号,而后应被吊销,不在日常操作中使用。但是如果管理不慎,Root 令牌面临泄密风险,又或者 Root 令牌遗失,我们如何更换 Root 令牌? 首先想要重建 Root 令牌,Vault 必须处于解封状态(封印状态下 Vault 无法执行任何操作)。重建 Root 令牌有两种办法,分别是一次性密码(one time password, otp)以及 pgp。本文仅讲述 otp 方法: $ vault operator generate-root -init A One-Time-Password has been generated for you and is shown in the OTP field. You will need this value to decode the resulting root token, so keep it safe. Nonce d89404b8-118d-2ce1-4ad4-d09d6398fa8d Started true Progress 0/3 Complete false OTP dhox62ecCxYDfKvy3T1k2e7GqC OTP Length 26 让我们那构想这样一种可能,一个内鬼主动发起重建 Root 令牌操作,以吊销原 Root 令牌并让自己获取新的 Root 令牌,如何防御这种内部攻击?Vault 重建 Root 令牌的过程被设计成,必须集齐足够的 Unseal Key 才可以执行重建操作,确保内鬼无法独自进行这种攻击,必须获得足够的拥有 Unseal Key 的人的协助(现在明白为什么笔者建议由高管保存 Unseal Key 了吧)。 我们刚才的命令发起了一次重建 Root 令牌的过程,但此时 Root 令牌尚未重建。我们输入第一个 Unseal Key: $ vault operator generate-root Operation nonce: d89404b8-118d-2ce1-4ad4-d09d6398fa8d Unseal Key (will be hidden): Nonce d89404b8-118d-2ce1-4ad4-d09d6398fa8d Started true Progress 1/3 Complete false 命令行中的 \"Unseal Key (will be hidden):\" 后面就是我们输入的第一个 Unseal Key,没有显示出来,此时进度显示为 \"1/3\"。 在非交互式界面下,我们无法这样输入 Unseal Key,那么我们需要借助之前执行 -init 命令得到的的 Nounce 值,使用环境变量来传递这个值: $ echo $UNSEAL_KEY | vault operator generate-root -nonce=d89404b8-118d-2ce1-4ad4-d09d6398fa8d - Nonce d89404b8-118d-2ce1-4ad4-d09d6398fa8d Started true Progress 2/3 Complete false 请董事长亲自输入最后一条 Unseal Key 后: $ vault operator generate-root Operation nonce: d89404b8-118d-2ce1-4ad4-d09d6398fa8d Unseal Key (will be hidden): Nonce d89404b8-118d-2ce1-4ad4-d09d6398fa8d Started true Progress 3/3 Complete true Encoded Token F0YHLX5WBiYtMj4XHgFOAFAgWDlhV111KS4 我们成功地重建了 Root 令牌,但是我们并没有直接得到它,而是得到了 Encoded Token。这是用之前生成的 otp 加密后的 Root 令牌,用以防止董事长是内鬼泄密(陛下何故谋反?)。 $ vault operator generate-root -decode=F0YHLX5WBiYtMj4XHgFOAFAgWDlhV111KS4 -otp=dhox62ecCxYDfKvy3T1k2e7GqC s.hUHdcEnJgSxJ8yctiRS2j2Xm $ vault login s.hUHdcEnJgSxJ8yctiRS2j2Xm Success! You are now authenticated. The token information displayed below is already stored in the token helper. You do NOT need to run \"vault login\" again. Future Vault requests will automatically use this token. Key Value --- ----- token s.hUHdcEnJgSxJ8yctiRS2j2Xm token_accessor 0WzrxaURnBvLSsUUrgth0HKi token_duration ∞ token_renewable false token_policies [\"root\"] identity_policies [] policies [\"root\"] 当我们给出了正确的 Encoded Token 和 otp 后,我们成功地得到了重建的 Root 令牌,经过验证,该 Token 真实有效。 小结 Vault 在设计上极其重视安全性,使用 Master Key 保护保存的所有数据,又专门设计了 Unseal Key 来保护 Master Key。默认情况下 Vault 采用 Shamir 算法,将 Unseal Key 切分成 M 份,要求至少获取 N 份才能正确解密 Master Key,这就将监督的权力分发给了 M 个不同的人,笔者建议这 M 人要选择公司内比较重要岗位的人。 Vault 服务启动后默认将处于 Sealed 状态,这时由于 Master Key 尚未解密,所以 Vault 无法执行任何操作,必须解封。当 Vault 集群重启后也必须执行解封操作。 为了应对 Root 令牌可能的泄密,以及可能的内鬼针对 Root 令牌的重置攻击,Vault 设计了一套非常健壮的 Root 令牌重建机制,确保了只有在拥有了多数 Unseal Key 的情况下才能成功重建 Root 令牌,并且用一次性密码 otp 保障了拥有 Unseal Key 的人无法知晓新的 Root 令牌,实现了 Vault 管理员与 Unseal Key 拥有者的互相制衡与监督,具有极高的安全性。 本文出于演示目的,所以关闭了 tls 链路加密,但读者们可以很容易地理解,由于我们会往 Vault 发送 Root 令牌、Unseal Key 这样的非常机密的信息,所以生产环境部署时请务必使用 tls 加密链路。 "},"1.什么是vault/6.Vault的架构.html":{"url":"1.什么是vault/6.Vault的架构.html","title":"Vault的架构","keywords":"","body":"Vault的架构 Vault是由多个松耦合的部件组成的系统,总体来说它由以下几个部件组成: 后端存储(Storage Backend):后端存储负责将密文数据存储到可靠的持久存储上。Vault 并不假设后端存储上的数据不会被盗取,而只是假设该存储是持久、可靠的。Vault 只会向后端存储写入加密过的数据。后端存储应在 Vault 服务启动前被妥善配置。 屏障(Barrier):屏障是由加密算法所组成的钢筋混凝土防护工事。所有在 Vault 服务与后端存储之间流动的数据都会经过屏障处理。屏障确保了 Vault 写入后端存储的一切数据都是加密的,而从后端存储读取的数据都会经由屏障解密交由 Vault 服务使用。由于屏障的存在,Vault 服务启动后,必须进行“解封”(Unseal)处理,获得与后端存储数据相应的主密钥(Master Key)后才能正常工作。 机密引擎(Secret Engine):机密引擎负责管理各种机密数据。比如“KV“引擎,就是一种简单的保存静态机密数据的机密引擎。某些机密引擎可提供被查询时动态创建机密的动态机密功能,这使得 Vault 可以提供细颗粒度权限配置的一次一密的临时机密。例如为运维与开发配置不同的策略,并对应不同的 AWS 权限,相关人员每次读取相关机密时,由 Vault 动态创建一组拥有有限有效期和预设 AWS 权限的 Access Key 和 Secret Key,并确保在有效期过后由 Vault 在 AWS 上自动删除该Key。 审计设备(Audit Device):审计设备负责管理审计日志。进出 Vault 的每一个请求和响应都会被记录在预设的审计设备上。该部件为 Vault 与多种不同的审计日志存储的集成提供了一种简单的方式。 身份验证方法(Auth Method):身份验证方法被用来认证连接到 Vault 服务的用户或是应用程序的身份信息。一旦验证通过,身份验证组件会返回一组当前身份适用的策略信息。Vault 接受一个通过认证的用户,并返回一个可供将来使用的客户端令牌。举个例子,使用 userpass 认证方式,用户通过提供用户名与密码来进行认证。如果使用 github 认证方式,用户通过 Github 令牌来通过 Vault 的认证。 客户端令牌(Client Token):一个客户端令牌(又称“Vault Token“)类似于网站的会话 Cookie。一旦用户通过认证,Vault 返回一个客户端令牌。该令牌可以被 Vault 用来识别客户端身份并使用相应的访问控制权限约束客户端权限。该令牌通过 HTTP Header 传递。 机密(Secret):机密指的是所有由 Vault 返回的包含机密信息或者密码学原材料(Cryptographic Material)的信息。并不是所有由 Vault 返回的信息都是机密,例如系统配置、服务状态信息、策略配置等就不属于机密范畴。机密都有对应的租约(Lease),这代表客户端不可以假设机密可以无限期地被使用。Vault 会在租约到期后吊销相关机密,Vault 管理员也可以在租约到期之前人工吊销机密。Vault 服务与客户端之间的这种契约是至关重要的,它允许实现全自动的机密凭据和策略的改变。 服务器(Server):Vault 依赖于一个长期运行服务实例的服务器。Vault 服务器对外提供了一组 API 用以与客户端互动,以及管理与各种机密引擎的互动,访问控制权限体系的执行,以及机密租约管理和吊销。服务器的架构设计解除了客户端与机密信息、权限策略的耦合,使得管理员可以轻松实现对审计日志的中央化管控。 架构全览 可以看到,只有后端存储以及 HTTP API 两部分位于屏障外部,其余所有部件都位于屏障内部。 Vault 不信任后端存储,所以只会向后端存储写入加密后的密文数据。当 Vault 服务启动时,必须配置一个后端存储,使得 Vault 重启后仍然可以读取到数据。HTTP API 同样也必须在 Vault 服务启动时被启动,才能使得客户端可以与 Vault 服务交互。 当 Vault 启动后,Vault 处于“封印(Sealed)”状态。在执行任意操作之前,首先要对 Vault 执行“解封(Unseal)”操作。当 Vault 服务初始化时会生成一个加密密钥,所有写入后端存储的数据都会用该密钥加密。该密钥由一个主密钥(Master Key)保护。默认情况下,Vault 使用 Shamir 算法将主密钥拆分成5份,需要至少 3 份才能重建主密钥: 主密钥拆分的份数以及解封要求的最低份数都可以被配置。我们也可以不使用 Shamir 算法,直接用主密钥解封。一旦 Vault 获得了加密密钥,就可以解密后端存储上的数据,服务进入解封状态。一旦解封,Vault 就会加载所有审计设备、认证方式以及机密引擎的配置信息。 当 Vault 解封,发往 Vault 的请求就可以通过 HTTP API 被转往核心(Core)进行处理。核心管理系统接收到的请求,强制执行访问控制权限,以及确保审计信息的成功写入。 当客户端首次与 Vault 连接,它需要经过身份认证。Vault 提供了多种身份认证方式。对人类用户比较友好的有 userpass、github 等,应用程序可以使用证书或是 Token 认证。一个身份认证请求会经由核心抵达相应的认证方式,由认证方式判定认证是否通过,然后返回一组相关的策略。 策略(Policies)是一个命名的访问控制规则,比如说,root 策略是一个内建策略,它允许访问任意资源。你可以创建任意多个命名的策略,在相关路径上定义细致的权限控制。Vault 采用白名单的模式,意为除非策略明确允许某项操作,否则所有操作都是被禁止的。如果一个用户拥有多个策略,对于某项操作,只要任意策略允许就可以执行。策略由内部的策略存储(Policy Store)存储和管理。这个内部的策略存储通过系统后端(System Backend)控制,挂载路径总是为 sys/。 一旦身份认证通过,并且身份认证方式返回一组适用的策略,Token 存储(Token Store)会生成并管理一个新的客户端 Token。该 Token 会被返回给客户端,用来作为后续操作的认证信息,这很像网站在用户登录后通过返回给用户的 Cookie 传递会话信息的机制。根据身份认证方式的配置,该客户端 Token 可能还会关联一个租约。这意味着客户端 Token 可能需要定期续约来避免过期。 一旦身份认证通过,后续请求都要附加客户端 Token。客户端 Token 被用来确认客户端有权加载相关策略。策略被用来验证客户请求的权限。通过验证的请求被路由到相关机密引擎,由机密引擎进行处理。如果机密引擎返回一个机密,核心会将其注册到过期管理器(Expiration Manager)并附加一个租约 ID。租约 ID 可用来续约租约或是吊销相关机密。如果客户端任由租约过期,过期管理器会自动吊销相关机密。 核心还负责将请求与响应的日志写入审计中介(Audit Broker),由审计中介负责将日志写入所有预设的审计设备。除了处理客户端发送的请求,核心还会负责特定的后台活动。租约管理是至关重要的,它允许自动吊销过期的客户端 Token 以及机密信息。另外, Vault 使用预写日志(Write Ahead Logging)和回滚管理器处理特定的失败场景。这些是由核心透明地执行的,对用户不可见。 "},"2.基本概念/基本概念.html":{"url":"2.基本概念/基本概念.html","title":"基本概念","keywords":"","body":"基本概念 本章将介绍一些对日常使用和维护 Vault 来说非常关键的基础概念。建议读者首先读完本章所有内容。 "},"2.基本概念/1.Dev-Server-Mode.html":{"url":"2.基本概念/1.Dev-Server-Mode.html","title":"\"Dev\" 服务模式","keywords":"","body":"\"Dev\" 服务模式 我们可以以 \"dev\" 模式化启动一个 Vault 服务:vault server -dev。这种 \"dev\" 模式的服务不需要任何额外配置,用户可以在本机使用 vault 命令行工具与之交互。这使得可以很方便地启动一个 Vault 的测试服务器来进行学习和实验。在 \"dev\" 模式下,Vault 所有的功能都可以被使用。启动 Vault 服务时指定的 -dev 参数实际上是设置一大堆不安全的默认值的快捷方式。 警告:永远不要再生产环境使用 \"dev\" 模式。这是不安全的,只要重启服务就会导致数据丢失(因为该模式下数据被保存在内存里)。该模式只能作为开发、学习和实验使用。 Dev 模式的配置 \"dev\" 模式服务默认进行了如下配置(有些配置可以通过命令行参数或指定配置文件的方式进行修改): 自动初始化与解封:服务会被自动地执行初始化和解封。用户无需执行 vault operator unseal,开箱即用。 内存存储:所有数据都在加密后被保存在内存中。\"dev\" 模式下 Vault 服务不需要任何文件权限。 不开启 TLS 绑定本机地址:服务监听 127.0.0.1:8200(默认服务地址),并且不开启 TLS。 自动登录:该模式下服务会保存你的 Root 令牌,所以 vault 命令行工具可以直接与 Vault 服务交互。如果用户想用 API 与服务交互,那么需要填入 Vault 服务启动时输出的 Root 令牌。 单一的解封密钥:该模式下服务使用单一的一条解封密钥进行初始化,服务启动时已经是解封的了,不过用户仍然可以使用这一条解封密钥来体验“封印/解封”操作。 自动挂在 Key-Value 引擎:KV-v2 机密引起你过被自动挂载于 secret/ 路径上。请注意 KV-v2 与 KV-v1 有着些许不同。如果想要使用 v1 版本,请在启动服务时指定 -dev-kv-v1 参数。 "},"2.基本概念/2.封印与解封.html":{"url":"2.基本概念/2.封印与解封.html","title":"封印与解封","keywords":"","body":"封印与解封 当 Vault 服务启动时,默认处于封印状态。在该状态下,Vault 知道如何访问物理存储,但无法解密任何存储的数据。 解封时获取 Master Key 明文的过程,通过 Master Key 可以读取并解密存储的数据。 在未解封时,Vault 几乎无法执行任何操作。比方身份认证、管理挂载表等等,都无法执行。唯一可以执行的操作就是解封并检查封印状态。 为什么? Vault 存储的数据时加密的。Vault 需要加密密钥来解密数据。加密密钥与密文数据被一同存储,但加密密钥本身通过 Master Key 加密。 所以,要解密数据,Vault 必须首先使用 Master Key 解密这个加密密钥。解封就是获取 Master Key 的操作流程。Master Key 与其他 Vault 数据被存放在一起,但使用另一种机制进行加密:解封密钥。 总而言之,绝大多数 Vault 数据用加密密钥加密;加密密钥用 Master Key 加密;Master Key 用解封密钥加密。 Shamir 封印 Vault 默认使用 Shamir 算法封印。解封密钥并不是一条单一的密钥,而是使用 Shamir 机密分享算法 将之分割成多个分片。要重建解封密钥,需要特定条分片,然后可以解密 Master Key。 解封 解封通常是通过运行 vault operator unseal 或是调用 HTTP API 完成的。该过程是有状态的:每一条解封 Key 可以被从不同的电脑上以不同的机制输入。这允许构成 Master Key 的分片被保管在不同的机器上以提升安全性。 一旦 Vault 节点被解封,它将维持解封状态,除非发生以下的某种情况: 它被通过 API 重新封印了 服务重启了 Vault 存储层发生不可恢复的错误 解封操作增加了自动化安装 Vault 的难度。自动化工具可以轻松地安装、配置和启动 Vault 服务,但使用 Shamir 算法解封服务依赖手工操作。大多数用户通过使用自动解封机制获得更好的体验。 封印 有特定 API 可以用来封印服务。该操作会将内存中的 Master Key 抛弃,然后必须再执行一次解封操作才能恢复它。封印操作只需要使用 Root 特权进行一次操作即可完成。 这样的话,如果检测到入侵的迹象,可以用最快的速度锁定 Vault 保存的机密来减少损失。如果没有办法恢复 Master Key 是无法访问 Vault 中的数据的。 自动解封 自动解封是一种用来降低解封操作复杂度同时确保解封密钥本身安全的机制。该功能将保管解封密钥的职责从用户这边转移到了一个可信的设备或者服务上。Vault 启动时会连接该设备或者服务,请求解密从存储中读到的 Master Key。 Vault 中除了解封以外有些特定的操作需要一组用户的多数批准才能执行,例如生成 Root 令牌。当使用 Shamir 算法封印时,需要多数用户输入他们的解封密钥。当使用自动解封时,这些操作需要的是“恢复密钥”(recovery keys)。 正如使用 Shamir 密封的初始化过程产生解封密钥一样,使用自动解封的初始化过程会产生恢复密钥。 注意,恢复密钥无法用来解密 Master Key,所以当自动解封机制无法工作时,无法使用恢复密钥解封 Vault。它们只能用来做授权验证。 即使使用自动解封,仍然可以通过调用 API 封印 Vault。一旦封印,Vault 会保持封印状态直到被重启,或是执行了解封操作。执行解封操作需要恢复密钥分片而不是解封密钥,其他流程是一样的。 恢复密钥的更新 恢复密钥可以被更新来更改分片数或是阈值(输入多少份密钥可以成功执行)。使用 Vault CLI 时,可以在执行 vault operator rekey 命令时添加 -target=recovery 参数。 封印迁移 封印迁移流程无法避免停机维护,并且由于实现封印的技术基础,该过程需要用户关闭整个集群。虽然无法避免一段停机,但我们认为更换封印是一种罕见的事件,停机时间带来的不便是一种可以接受的权衡。 执行封印迁移之前请先执行备份以避免出错。 封印迁移会同时需要新旧两套封印的基础设施在迁移期间保持可用。举例来说,如果要从自动解封迁移到 Shamir 封印,那么自动解封所需的服务必须在整个迁移期间保持可用。 从一种自动解封迁移到另一种同类型的自动解封(例如从 AWSKMS 迁移到另一个使用不同密钥的 AWSKMS)从 Vault 1.6.0 开始得到支持。从一种自动解封(例如 AWSKMS)到另一种不同的自动解封(例如 HSM、Azure KSM 等)在更老的版本上也是支持的。 Vault 1.5.1 后的迁移 以下是所有受支持类型和存储后端之间的封印迁移的常见步骤: 设置一台备用节点下线,并更新相关的封印配置。 如果是从 Shamir 封印迁移到自动解封,添加对应的自动解封配置节即可。 如果是从自动解封迁移到 Shamir 封印,在旧的封印配置节中添加 disabled = \"true\"。 如果是从自动解封迁移到其它自动解封,在旧的封印配置节中添加 disabled = \"true\",然后添加新的封印配置节。 现在将备用节点重新上线,然后使用相关密钥运行解封命令,但要记得添加 -migrate 标记。 如果旧的封印机制是 Shamir 封印,那么需要提供 Shamir 解封密钥,然后会迁移到自动解封。 如果旧的封印机制是某种自动解封,那么需要提供恢复密钥,然后会迁移到新的自动解封或是 Shamir 封印。 在所有剩余的备用节点上重复上述步骤,一次操作一个节点。在下线一台节点前要先把之前离线操作的节点重新恢复上线,特别是使用节点集成存储时尤其如此,需要确保在线节点数可以形成集群多数。 下线主节点。这时备用节点之一会成为新的主节点。当使用节点集成存储时,要确保集群活动节点数达到了集群多数,并且选出了新的活动节点。 新的主节点将执行迁移。在主节点监控服务器日志,确认封印迁移过程完成。如果使用节点集成集成存储,请稍等片刻,让迁移信息复制到所有节点。在 Vault 企业版中,切换自动解封意味着被封印保护的数据会被重新加密。监控日志并等待此过程完成。(参见 seal re-wrap completed 命令) 封印迁移到此已经完成。下线旧的主节点,更新它的配置以使用新的封印配置节(完全不顾及旧的封印类型)并将其恢复上线。如果新封印是某种自动解封,它将自动解除封印;如果新封印是 Shamir 封印,则需要解封密钥执行解封。 此时,所有节点的配置文件都可以更新为只有新的封印配置。更新配置后,备用节点可以立即重新启动,主节点可以在更改主节点时重新启动。 Vault 1.5.1 之前的封印迁移 暂时不翻了,如果有需要请提 issue。 "},"2.基本概念/3.名字空间锁.html":{"url":"2.基本概念/3.名字空间锁.html","title":"名字空间锁和解锁","keywords":"","body":"名字空间锁和解锁 Vault 默认解锁了所有的 API,这时 Vault 可以响应所有 API/CLI 请求。 Vault 管理员可以锁定特定命名空间的 API。在这种状态下,Vault 会阻止在锁定命名空间(或锁定命名空间的后代)中,除选定的少数 API 端点之外的所有端点的响应。做出响应的端点(豁免路径)与 Vault 无需身份验证的路径大致相同。它们主要用于检查各种系统的状态(例如,sys/health),或用于解锁 API。 当锁定特定命名空间的 API 时,它也锁定了该命名空间的所有子空间。如果锁定时某个子空间已被锁定,则该锁将被保留,但在解锁该子空间之前必须先解锁父空间。 为什么? 在发生意外事件时,锁定对 Vault 的访问是一种重要的安全机制。 例如使用 HashiCorp 托管的 Vault 服务时,锁定访问 API 提供了与封印 Vault 类似的功能,但不需要 Vault 管理员请求托管服务商来执行封印/解封操作。 此功能对于各种多租户 Vault 部署架构也很有价值。我们可以对组织中特定部分成员访问 Vault 进行限制,而不会影响企业中其他部门。如果在子组织 A 中检测到意外行为,管理员可以禁用子组织 A 下任何实体对 Vault 的访问权限,而不禁用子组织 B 的访问权限。一旦确定并解决了原因,就可以解锁子组织 A 的 API 访问。 锁定 可以用 vault namespace lock 命令(或是调用 API)锁定当前名字空间下的 API。可以提供子路径作为可选项锁定该名字空间下的一个子路径。 执行锁定后会返回一个解锁密钥,可以使用该密钥来解锁该名字空间下的 API。为了将来可以解锁,需要妥善保存该密钥。 当名字空间下的 API 被锁定后,所有子路径都会被锁定。如果一个经过认证的客户端尝试访问一个被锁定的名字空间,会返回一个错误并告知被锁定的名字空间。 解锁 可以用 vault namespace unlock 命令(或是调用 API)解锁当前名字空间下的 API。可以提供子路径作为可选项解锁该名字空间下的一个子路径。 一般来说,解锁 API 需要提供锁定名字空间时返回的解锁密钥。 如果没有解锁密钥,也可以使用 Root 令牌,这时不需要提供解锁密钥。 当 API 解锁后,默认情况下所有子路径都会被解锁。如果某个子路径之前被单独锁定了,那么它仍将保持锁定状态。 API 锁定的响应 如果一个请求被发送到一个被锁定的名字空间,比如 nsA,Vault 会响应 HTTP 503: Service Unavailable,伴随如下错误信息: API access to this namespace has been locked by an administrator - \"nsA\" must be unlocked to gain access. 相似的,如果请求被发送到 nsA 的子路径下时会返回相同的错误。 Vault 复制遇上锁定会发生什么? API 锁定状态会被复制到整个集群中。 "},"2.基本概念/4.租约-续约与吊销.html":{"url":"2.基本概念/4.租约-续约与吊销.html","title":"租约、续约以及吊销","keywords":"","body":"租约、续约以及吊销 对于每个动态机密和 service 类型登录令牌,Vault 都会创建一个租约(lease):包含持续时间、是否可续约等信息的元数据。 Vault 承诺数据在给定的持续时间(Duration)或生存时间 (TTL) 内有效。一旦租约到期,Vault 可以自动吊销数据,过期后机密的使用者无法再确定它是否还是有效(因为吊销机密是一个异步操作,无法预测 Vault 将在何时执行吊销操作)。 这带来一个明显的收益:机密的使用者需要定期与 Vault 通信以续约(如果允许的话),或是请求一个新的机密。这使得 Vault 审计日志更有价值,也使密钥滚动更新变得更加容易。 Vault 中的所有动态机密都需要有租约。即使数据旨在永久有效,也需要租约以强制使用者定期续约。 除了续约,租约也可以吊销。当租约被吊销时,它会立即使该机密无效并阻止任何新的的续订。例如,使用 AWS 机密引擎,一旦租约被吊销,访问密钥就会从 AWS 中删除,这使得访问密钥从那时起变得无效。 吊销可以通过 API 手动进行,也可以通过执行 vault lease revoke 命令进行,也可以由 Vault 自动进行。当租约到期时,Vault 会自动吊销该租约。当令牌被吊销时,Vault 将吊销使用该令牌创建的所有租约。 需要注意的是,Key/Value 机密引擎是不关联租约的,虽然它有时也会返回一个租约期限。 租约 ID 使用比如 vault read 命令读取动态机密时,Vault 总是会返回一个 lease_id。这个 ID 可以被用在 vault lease renew 以及 vault lease revoke 这样的命令上来管理机密的租约。 租约期限与续约 与租约 ID 一起返回的有租约期限。租约期限是生存时间值(Time To Live):租约有效的时间(以秒为单位)。机密的使用者必须在那个时间来临前更新租约。 在续订租约时,用户可以请求他们希望租约保持的有效时长,称为 increment。这不是从当前 TTL 结束时间开始计算的增量,而是从当前时间开始计算的增量。例如,vault lease renew -increment=3600 my-lease-id 命令会将租约的 TTL 调整为 1 小时(3600 秒)。从当前时间开始计算新 TTL 而不是从目前的 TTL 结束时间开始算可以在用户决定减少有效期时简化计算工作,从而使这些凭证更快到期并更快清理资源。 客户端请求的增量完全是建议性的,负责管理机密的后端可以选择完全忽略它。对于大多数机密,后端会尽最大努力尊重用户设定的增量,但通常会对其进行限制以确保机密经常更新。 因此,应仔细检查续约操作的返回值,以确定新租约有效期是多少。 根据前缀的吊销 除了吊销单个机密之外,具有适当访问控制的操作员可以根据租约 ID 前缀撤销多个机密。 租约 ID 的结构是有规律的,它们的前缀始终是请求机密的路径。这使您可以吊销一组机密树。例如,要吊销所有 AWS 访问密钥,您可以执行 vault lease revoke -prefix aws/。有关吊销命令的更多信息,请查看命令行篇章中的吊销租约命令的文档。 如果特定系统发生入侵事件,该功能将非常有用:特定路径中所有机密都可以快速轻松地吊销。 "},"2.基本概念/5.身份验证.html":{"url":"2.基本概念/5.身份验证.html","title":"身份验证","keywords":"","body":"身份验证 Vault 中的身份验证是根据内部或外部系统验证用户或机器提供的信息的过程。 Vault 支持多种身份验证方法,包括 GitHub、LDAP、AppRole 等。每个身份验证方法都有对应的使用场景。 在客户端可以与 Vault 交互之前,必须先使用相关身份验证方法进行身份验证。身份验证后,会生成令牌。该令牌在概念上类似于网站上的会话 ID。令牌可能具有附加的策略,该策略在身份验证时被映射。随后的策略概念章节中会详细描述此过程。 验证方法 Vault 支持多种验证方法。有些是面向人类用户的,而其他的则是面向验证机器身份的。大多数身份验证后端必须在使用前被启用。要启用一个身份验证方法可以用如下命令: $ vault write sys/auth/my-auth type=userpass 该命令在路径 my-auth 上启用了 userpass 认证方法。通常来说我们会看到认证方法的名字就是启用它的路径名,但这并不是强制的。 想要了解某个路径上启用的身份验证方法的详细信息,可以使用内建的 path-help 命令: $ vault path-help auth/my-auth Vault 支持同时使用多种身份验证方法,我们甚至可以在不同路径上挂载相同类型的身份验证方法。只需进行一次身份验证即可访问 Vault,目前无法强制用户必须通过多种身份验证方法才能获得访问权限,尽管某些后端确实支持多因素认证。 令牌 随后会有专门讲述令牌的章节,但在这里我们需要了解的是,身份验证的工作原理就是验证我们的身份,然后生成与该身份相关联的令牌。 举例来说,即使我们可以使用 GitHub 之类的工具进行身份验证,Vault 也会生成一个唯一的访问令牌供我们将来使用。 命令行工具会自动将此令牌附加到请求,但如果我们使用的是 API,则必须手动执行此操作。 任何后端对应的身份验证方式创建的令牌都可以与所有的令牌命令一起使用,例如创建新的子令牌、撤销令牌和更新令牌。这一切都将在随后的令牌概念章节中进行介绍。 执行身份验证 使用命令行 要使用命令行进行身份验证,可以使用 vault login 命令。该命令支持多种内建的验证方式。举一个使用 GitHub 的例子: $ vault login -method=github token= 身份验证完成后,我们就完成了对 Vault 的登录。命令还将输出您的令牌。此令牌用于吊销和续订操作。因为此时用户已经登录,所以令牌主要用来续订租约。 如果想要确定该验证方式需要提供哪些参数,可以只添加 -method 标记,命令会显示帮助信息。 如果使用的验证方式不支持命令行方式,那么必须使用 API。 使用 API API 认证一般用于验证机器身份。每个验证方法都实现了自己的登录端点。使用 vault path-help 命令可以查找正确的端点。 举例来说,GitHub 登录端点位于 auth/github/login。为了确定所需的参数,可以使用命令 vault path-help auth/github/login。 身份租约 和 Vault 管理的机密一样,身份也有与之相关的租约。这意味着我们必须在给定的租约到期后重新进行身份验证才能继续访问 Vault。 要设置与身份关联的租约,请参考所使用的特定身份验证方法的帮助信息。租约机制的具体实施方式取决于每个实现身份验证的后端。 就像机密一样,也可续约身份租约而不是重新验证身份。只需使用 vault token renew 命令来续约与身份关联的令牌的租约。 样例代码 以下代码片段展示了如何续约身份令牌: package main import ( \"context\" \"fmt\" \"log\" vault \"github.com/hashicorp/vault/api\" auth \"github.com/hashicorp/vault/api/auth/userpass\" ) // Once you've set the token for your Vault client, you will need to // periodically renew its lease. // // A function like this should be run as a goroutine to avoid blocking. // // Production applications may also wish to be more tolerant of failures and // retry rather than exiting. // // Additionally, enterprise Vault users should be aware that due to eventual // consistency, the API may return unexpected errors when running Vault with // performance standbys or performance replication, despite the client having // a freshly renewed token. See https://www.vaultproject.io/docs/enterprise/consistency#vault-1-7-mitigations // for several ways to mitigate this which are outside the scope of this code sample. func renewToken(client *vault.Client) { for { vaultLoginResp, err := login(client) if err != nil { log.Fatalf(\"unable to authenticate to Vault: %v\", err) } tokenErr := manageTokenLifecycle(client, vaultLoginResp) if tokenErr != nil { log.Fatalf(\"unable to start managing token lifecycle: %v\", tokenErr) } } } // Starts token lifecycle management. Returns only fatal errors as errors, // otherwise returns nil so we can attempt login again. func manageTokenLifecycle(client *vault.Client, token *vault.Secret) error { renew := token.Auth.Renewable // You may notice a different top-level field called Renewable. That one is used for dynamic secrets renewal, not token renewal. if !renew { log.Printf(\"Token is not configured to be renewable. Re-attempting login.\") return nil } watcher, err := client.NewLifetimeWatcher(&vault.LifetimeWatcherInput{ Secret: token, Increment: 3600, // Learn more about this optional value in https://www.vaultproject.io/docs/concepts/lease#lease-durations-and-renewal }) if err != nil { return fmt.Errorf(\"unable to initialize new lifetime watcher for renewing auth token: %w\", err) } go watcher.Start() defer watcher.Stop() for { select { // `DoneCh` will return if renewal fails, or if the remaining lease // duration is under a built-in threshold and either renewing is not // extending it or renewing is disabled. In any case, the caller // needs to attempt to log in again. case err := "},"2.基本概念/6.令牌.html":{"url":"2.基本概念/6.令牌.html","title":"令牌","keywords":"","body":"令牌 警告:由于令牌的结构被认为是非公开的,它的实现结构没有相关文档,也不保证稳定不变。出于这些原因,请不要在代码或自动化脚本中解析令牌,因为它可能会令牌结构的破坏性变更而出错。 令牌是 Vault 中身份验证的核心方法。可以直接使用已有的令牌,也可以使用身份验证方法根据外部身份动态生成令牌。 如果读者从本书开篇读到这里,那么可能会注意到 vault server -dev(或者 vault operator init 初始化的用于正式场合的服务器)命令输出的 root token。这是 Vault 的第一种身份验证方法。它也是唯一无法禁用的身份验证方法。 正如身份验证章节中所述,所有外部身份验证机制(例如 GitHub)最终都映射到动态创建的令牌上。这些动态创建的令牌与普通的手动创建的令牌具有相同的属性。 在 Vault 中,令牌映射了一组信息。映射到令牌的最重要的信息是一组一个或多个“策略”。这些策略控制着令牌持有者可以在 Vault 中做什么。其他映射的信息包括了一些只读的元数据,例如创建时间、上次更新时间等,并且这些元数据会被记录到审计日志当中。 令牌类型 从 Vault 1.0 开始,有两种类型的令牌:service 令牌和 batch 令牌。本章后续部分将叙述它们之间的差异,但我们可以先略过,了解一下其他有关令牌的概念。以下部分中的功能均适用于 service 令牌,稍后将讨论它们对 batch 令牌的适用性。 令牌存储 通常在文档或帮助中,会引用“令牌存储”。这是一个特殊的后端组件,负责创建和存储令牌,并且不能被禁用。它也是唯一没有登录功能的身份验证方法——所有其他操作都需要一个已存在的经过身份验证的令牌。 根令牌 根(Root)令牌是附加了根策略的令牌。根令牌可以在 Vault 中为所欲为。为所欲为!此外,它们是 Vault 中唯一可以设置为永不过期而无需任何续约的令牌类型。这导致了 Vault 中创建根令牌的流程被有意设计的复杂而困难;实际上只有三种方法可以创建根令牌: 使用 vault operator init 命令时创建的根令牌:此令牌没有过期时间 使用根令牌来创建一个新的根令牌:一个带有过期时间的根令牌无法创建一个无过期时间的根令牌 使用 vault operator generate-root 命令创建一个新的根令牌(我们在之前的篇章中有过叙述) 根令牌在开发过程中很有用,但在生产环境中应该非常小心地保存。事实上,Vault 官方建议根令牌仅用于在创建集群时进行初始设置(通常设置足够的身份验证方法和策略,以允许管理员获取更多的受限制的令牌)或在紧急情况下使用,并在使用完成后应立即吊销。如果需要新的根令牌,可以使用 operator generate-root 命令或相关的 API 来即时生成一个。 在多人监视的状态下使用根令牌进行操作是很好的安全实践。通过这种方式,多个人员可以验证使用根令牌执行的任务,并且在这些任务完成后立即吊销令牌。 令牌的层次结构以及孤儿令牌 通常当令牌的持有者创建新的令牌时,这些新令牌将被创建为原令牌的子令牌;而这些子令牌创建的令牌将是它们的子令牌。当父令牌被吊销时,它的所有子令牌——以及它们创建的所有租约——也会被吊销。这确保用户无法通过简单地生成一个永无止境的子令牌树来逃避吊销。 有时我们不希望这种级联吊销,因此具有适当访问权限的用户可以创建 orphan 令牌。这些令牌没有父令牌——它们是自身令牌树的根。可以用以下方法创建孤儿令牌: 通过对 auth/token/create-orphan 路径执行 write 操作 在对 auth/token/create 路径有 sudo 或是 root 访问权限的前提下,将 no_parent 参数设置为 true 通过设置令牌存储角色 通过其他非 -token 机制的身份验证方法登录 具有适当权限的用户还可以使用 auth/token/revoke-orphan 端点,可以吊销指定的令牌的同时不吊销它的子令牌,而是将令牌的直接子令牌设置为孤儿。谨慎使用! 令牌访问器(Accessor) 创建令牌时,还会创建并返回令牌访问器。此访问器是一个作为对令牌引用的值,只能用于执行以下有限的操作: 查询一个令牌的信息(不包括实际的令牌 ID) 查询一个令牌对指定路径的权限 续约令牌 吊销令牌 执行以上操作时使用的是当前使用的令牌,而不是访问器对应的令牌,所以以上操作的执行者必须拥有相关的权限。 有许多有用的工作流程被设计成使用令牌访问器。举例来说,代表另一个服务(例如 Nomad 调度器)创建令牌的服务可以存储与特定任务 ID 相关的访问器。任务完成后,可以立即使用访问器来吊销授予任务的令牌及其所有租约,从而限制入侵者发现和使用它们的机会。 可以将审计设备设置为不混淆审计日志中的令牌访问器。这使得我们可以在紧急情况下快速吊销令牌。然而,这也意味着获取到审计日志的攻击者可以进行大规模拒绝服务攻击。 最后,枚举令牌的唯一方法是通过 auth/token/accessors 命令,它将返回令牌访问器的列表。虽然这是一个危险的操作(因为列出所有访问器意味着它们可以用来吊销所有令牌),但它也提供了一种审计和吊销当前活动令牌集的方法。 令牌有效期,周期性令牌以及显式的最大有效期限制 每个非根令牌都有一个与之关联的有效期 (TTL),这是自令牌的创建时间或上次续约时间(以较近者为准)以来的当前有效期。(根令牌可能有关联的 TTL,但 TTL 也可能为 0,表示令牌永不过期)。当前 TTL 到期后,令牌将不再起作用——令牌及其相关联的租约将被吊销。 如果令牌是可续约的,则可以要求 Vault 使用 vault token renew 命令或适当的更新端点来延长令牌有效期,这时候要考虑各种因素。命令的结果取决于令牌是否是周期性令牌(可由 root 或 sudo 用户、令牌存储角色或某些身份验证方法创建),是否附加了显式的最大 TTL 限制,或者两者都没有。 通常情况 通常情况下,如果是一个没有显式最大 TTL 限制的非周期性令牌,令牌自创建以来的存活时长会与最大 TTL 进行比较。这个最大 TTL 值是动态生成的,可以随着续约而改变,所以在查询令牌信息时无法显示该值。它基于多种因素的组合: 系统默认最大 TTL 是 32 天,然而这可以在 Vault 配置文件中进行修改 可以为指定挂载点设置最大 TTL。该值允许覆盖系统级别的最大 TTL —— 可以更长也可以更短,挂载点上的配置拥有更高优先级 签发令牌的身份验证方法给出的建议值。这可以基于个别角色、个别组或个别用户进行配置。该值允许小于挂载点上配置的最大 TTL(或者,如果未设置,则为系统最大 TTL),但不允许更长。 请注意,第二条和第三条中的值可能会在任何时间发生更改,这就是为什么在续约时即时读取当前值来最终确定当前允许的最大 TTL 原因,这也是为什么客户端始终要检查从更新操作返回的 TTL 在允许的范围内的一个重要原因;如果有效期未能成功延展,则令牌的 TTL 可能无法扩展到超过其当前值,那么客户端可能需要重新进行身份验证并获取新令牌。但是,除非直接使用相关命令,Vault 永远不会在返回的 TTL 到期之前吊销令牌。 显式的最大 TTL 令牌可以设置一个显式的最大 TTL。该值是令牌生命周期的硬限制——无论一般情况下的三种条件中的值是什么,令牌都不能超过这个明确设置的值,甚至周期性令牌 TTL 也无法摆脱它的限制。 周期性令牌 在某些情况下,吊销令牌会很麻烦——例如,如果长时间运行的服务需要长期维护它的 SQL 连接池。在这种情况下,可以使用定期令牌。可以通过以下几种方式创建定期令牌: 使用有 sudo 权限的令牌,或是根令牌操作 auto/token/create 端点 使用令牌存储角色 使用支持周期令牌的身份验证方法来签发周期性令牌,例如 AppRole 在签发时,周期性令牌的 TTL 将等于设置的周期。每次续约时,TTL 都会重置回这个配置的时长,只要令牌在每个时间段内成功续约,它就永远不会过期。在根令牌之外,它目前是 Vault 中唯一一种具有无限生命周期的令牌。 周期性令牌的设计理念是,使得系统和服务可以很容易地频繁地反复执行某项操作——例如,每两小时,甚至每五分钟。因此,只要系统主动续约此令牌——换句话说,只要系统还活着——系统就可以继续使用令牌和任何相关的租约。但是,如果系统在此期间停止续约(例如,如果它被关闭),令牌将相对较快地过期。设置一个较短的周期是一种很好的做法,同时,一般来说给人类提供周期性的令牌是没有什么益处。 使用周期性令牌有几项重要的事项: 如果使用令牌存储角色创建周期性令牌,那么当前角色设置的周期值将会被用作续约周期 如果一个令牌同时设置了周期以及显式最大 TTL,那么它工作起来就如同一个普通的周期性令牌,但是在到达最大 TTL 时间后会被吊销。 绑定 CIDR 的令牌 某些令牌能够绑定一组限制允许使用它们的客户端 IP 范围的 CIDR。该设置会影响除了无有效期限制的根令牌(TTL 为零的令牌)之外的所有令牌。如果根令牌有过期时间,它也会受到 CIDR 绑定的影响。 令牌类型详解 目前有两种令牌 服务令牌 服务令牌是用户通常认为的“普通” 的 Vault 令牌。它们支持所有功能,例如续订、吊销、创建子令牌等。它们对应了重量级的创建和跟踪流程。 批量令牌 批量令牌是加密的二进制数据,携带了足够的信息供来用于 Vault 操作,但它们不需要 Vault 服务的磁盘存储来记录跟踪它们。因此,它们非常轻量级和可扩展,但缺乏服务令牌的大部分灵活性和功能。 令牌类型对比 下表描述了服务令牌与批量令牌的不同之处: 服务令牌 批量令牌 可以是根令牌吗? 可以 不可以 可以续约吗? 可以 不可以 可以是周期性令牌吗? 可以 不可以 可以设置显式最大 TTL 吗? 可以 不可以(只能使用固定 TTL) 拥有访问器吗? 有 没有 拥有 Cubbyhole 吗? 有 没有 会随着父令牌被级联吊销吗? 会 停止工作 创建的动态机密关联到谁? 自身 父令牌(如果不是孤儿令牌) 可以跨性能复制集群使用吗? 不能 能(如果是孤儿令牌) 创建成本 高:创建每个令牌都要写入多次存储 低:创建令牌不需要访问存储 服务令牌与批量了令牌创建的租约的处理 服务令牌租约 服务令牌创建的租约(包括子令牌创建的租约)会与服务令牌一起被追踪,一旦令牌被吊销,则租约都会被吊销。 批量令牌租约 批量令牌创建的租约受限于批令牌剩余的 TTL,如果批量令牌不是孤儿令牌的,则由父令牌代为跟踪。当批量令牌的 TTL 到期时,或者当批量令牌的父令牌被吊销(此时批量令牌也会被 Vault 拒绝访问)时,它们将被吊销。 可以推论出,批量令牌可以跨性能复制集群使用,但前提是它们是孤儿令牌,因为非孤儿令牌将无法确保父令牌的有效性。 "},"2.基本概念/7.身份.html":{"url":"2.基本概念/7.身份.html","title":"身份","keywords":"","body":"身份(Identity) 本篇包含有关身份的概念信息以及各种术语及其概念的概述。身份在 Vault 中代表经过识别的客户端。因此,Vault 通过身份机密引擎提供身份管理解决方案。有关识别机密引擎及其使用方式的更多信息,请参阅识别机密章节中的机密引擎文档。 实体(Entity)与别名(Alias) 每个用户可能有多个不同的身份提供者提供的账户,并且 Vault 支持其中多种身份提供者使用 Vault 进行身份验证。 Vault 身份机制可以将多种身份验证方法的验证的身份绑定到同一个对象上,这种统一身份的表示称为实体,而与之对应的身份验证提供者的对应帐户可以映射为实体的别名。本质上,每个实体由零个或多个别名组成。一个实体在特定身份验证后端上不能拥有多个别名。 例如,同时在 GitHub 和 LDAP 中有帐户的用户可以映射到 Vault 中具有两个别名的同一个实体,一个是 GitHub 类型,一个是 LDAP 类型。 然而,如果两个别名都是在同一个身份验证方法挂载点上创建的,例如 Github 挂载点,则两个别名不能映射到同一个实体。别名可以具有相同的身份验证类型,只要身份验证挂载的路径不同,就可以与同一实体相关联。下图说明了有效和无效的情况。 当客户端通过任何方式(直接使用令牌除外)进行身份验证时,Vault 会创建一个新实体。如果相应实体没有别名,它会为其附加一个新别名。实体标识符会被绑定到经过身份验证的令牌上。当使用此类令牌时,它们的实体标识符会被审计记录,用以标记特定用户执行的操作踪迹。 实体管理 Vault 中的实体不会从任何地方自动提取身份信息。它需要由系统操作员显式进行管理。这样,就可以灵活地管理控制要与 Vault 同步的实体数量。从某种意义上说,Vault 将充当身份的缓存,而不是身份的来源。 实体策略 Vault 策略可以分派给实体,这将在令牌上的现有策略之上授予令牌额外的权限。如果 API 请求中显示的令牌包含实体的标识符,并且该实体具有一组策略,则该令牌也将能够执行该实体的策略允许的操作。 这对计算令牌所拥有的策略是一种范式转变。在引入身份之前,令牌上附加的策略名称是不可变的(但策略的内容可以修改)。但是由于引入了实体策略,配合令牌上的一组不可变的策略名称,令牌所拥有的策略将在请求发送到 Vault 时根据身份动态计算得出。这大大增强了已签发令牌行为控制流程的灵活性。 需要注意的是,实体上的策略只是一种授予额外权限的手段,而不是令牌上附加的策略的替代品。要计算出关联了实体标识符的令牌的完整权限集合,要把令牌上附加的策略一起考虑进来。 注意:向身份端点授予非只读权限时要小心。如果用户可以修改实体,他们可以通过策略授予它额外的权限。如果用户可以修改他们可以登录的别名,他们可以将该别名绑定到具有更高权限的实体上。如果用户可以修改组成员身份,则他们可以将其实体添加到具有更高权限的组中。 挂载点绑定别名 Vault 支持多种身份验证方式,也允许在不同的挂载路径上启用相同类型的身份验证方式。用户的别名在某一身份验证方式的挂载点上是唯一的。但是身份存储需要区分这些身份提供者的不同挂载点上彼此冲突的别名。因此,别名与身份验证后端挂载点的访问器结合起来就成了区分别名的唯一标识符。 下表显示了每种受支持的身份验证方法用于构造别名所使用的信息。这些识别信息被用于匹配或创建实体。如果没有显式创建或合并任何实体,则在特定身份验证挂载点上进行身份验证时,表中右侧的对象会被用于隐式创建一个对应的实体: 验证方法 验证方法返回的名称 AliCloud Principal ID AppRole Role ID AWS IAM 可以通过 iam_alias 配置项将其设定为 Role ID(默认情况)或是 IAM 的唯一 ID,又或者是 完整的 ARN AWS EC2 可以通过 ec2_alias 配置项将其设定为 Role ID(默认情况)或是 EC2 实例 ID,又或者是 AMI 镜像 ID Azure JWT 中的 Subject 项 Cloud Foundry App ID GitHub 用户 Token 对应的登录名 Google Cloud 可以通过 iam_alias 配置项将其设定为 Role ID(默认情况),或是服务账号的唯一 ID JWT/OIDC 可以通过 user_claim 配置项将其设定为某个属性(没有默认值) Kerberos 用户名 Kubernetes 服务账号 UID LDAP 用户名 OCI 角色名 Okta 用户名 RADIUS 用户名 TLS 证书 Subject CommonName 令牌 entity_alias,如果有的话 用户名(userpass 验证) 用户名 隐式实体 操作员可以为某个身份验证挂载点上的所有用户预先创建对应实体并为其分配策略,以便在用户登录时,通过实体为令牌分配所需的权限。但如果没有这样设置,当用户从任何身份验证方法成功登录后,Vault 将创建一个新实体并之分配一个别名。 请注意,使用令牌身份验证后端创建的令牌通常不会具有任何关联的身份信息。使用配置了 allowed_entity_aliases 列表参数的令牌角色创建令牌时,可以使用 entity_alias 参数分配一个现有实体,或是隐式创建一个新实体。 身份审计 如果关联了实体标识符的令牌被用于进行 API 调用,那么 Vault 会将之记录到审计记录里,这会留下特定用户执行操作的踪迹记录。 身份组 Vault 身份支持组。一个组可以包含多个实体作为其成员。一个组可以有子组。在组上设置的策略会被授予组内所有成员。在 Vault 处理请求时,当计算令牌对应的实体所拥有的策略时,因组成员身份而继承的策略会与实体本身的策略一起被附加到令牌上。 组策略的继承 一个实体可以是一个组的直接成员,这时它将继承所有附加在该组上的策略。实体也可以是组的间接成员。例如,如果 GroupB 是 GroupA 的子组,则 GroupB 的成员是 GroupA 的间接成员。因此,GroupB 的成员将同时拥有附加在 GroupA 和 GroupB 上的策略。 外部组与内部组 默认情况下,在身份存储中创建的组称为内部组。这些组的成员资格是手动管理的。也可以创建外部组,在这种情况下,组中的实体成员的资格是半自动管理的。外部组就像是身份存储之外的组在 Vault 内的一个映射。外部组可以拥有一个(并且只能有一个)别名。此别名应映射到身份存储之外的组的概念,比如说,LDAP 中的组和 GitHub 中的团队。属于某个 LDAP 组的 LDAP 用户名可以在登录和令牌续期时自动将其实体 ID 添加为 Vault 中的组的成员。这种机制只有在 Vault 中的组是外部组并且有一个映射到了 LDAP 中的组的别名时才有效。如果用户被从 LDAP 中的组中删除,则该更改只有在后续执行登录或续约操作时才会被反映在 Vault 中。 "},"2.基本概念/8.响应封装.html":{"url":"2.基本概念/8.响应封装.html","title":"响应封装","keywords":"","body":"响应封装(Response Wrapping) 注意:本节中介绍的一些响应封装机制只有在 Vault 0.8 之后才被引入,更早期的版本无法使用该功能。 总览 在许多 Vault 使用场景中,客户端可以直接访问 Vault 并使用返回的机密,但同时也存在我们希望只有少数受信任的实体能够访问 Vault ,由它负责将机密传递给需要使用机密的客户端的场景。 但是,经手机密的中继越多,意外泄露的可能性就越大,尤其是在机密以明文形式传输的情况下。例如,我们可能希望为一台刚刚冷启动的机器配发一个 TLS 私钥,但由于我们不想在持久存储中保存一个相应的加密密钥,导致我们无法在传输私钥到这台机器的过程中加密这个 TLS 私钥。 为帮助解决这个问题,Vault 引入了一种称为响应封装的功能。收到读取机密的请求时,Vault 可以选择不通过 HTTP 返回响应,而是将其插入一个临时的一次性令牌的 cubbyhole,并且返回这个一次性令牌。有关 cubbyhole 的知识我们会在后面的 Cubbyhole 章节进行详细介绍。 从逻辑上讲,响应被一个临时的令牌封装了,读取其中的机密需要对这个令牌进行解封操作。从功能上讲,该令牌提供了使用 Vault 中存储的的加密密钥来解密数据的授权。 在许多环境中响应封装机制提供了强大的信息共享能力。在此类场景类型中,通常最好的可行方法是为机密信息提供掩护,能够检测不当行为(拦截、篡改机密)的发生,并限制机密暴露的生命周期。响应封装执行所有这三个职责: 它通过确保通过线路传输的值不是实际的机密,而是对机密的引用(即响应封装令牌)来提供掩护。日志或信息传递链路中途被拦截的信息不会直接暴露敏感信息。 它通过确保令牌只能被打开一次来查看其中的内容,这提供了对异常行为的检测。收到无法解封的令牌的客户端可以立即触发安全警告。此外,客户端可以在解封之前检查收到的令牌,以确保其来源来自 Vault 中的预期位置。 它限制了秘密暴露的生命周期,因为响应封装令牌的生命周期与包裹其中的机密的生命周期是分离的(并且通常可以短得多),所以如果客户端未能打开并解封令牌,令牌可可以迅速过期失效。 响应封装令牌 当使用响应封装时,调用 Vault 的普通的 API 的响应不再包含原有的机密,而是与响应封装令牌有关联的一组信息: TTL:响应封装令牌自身的 TTL 令牌:实际的令牌值 创建时间:响应封装令牌的创建时间 创建路径:原请求中的 API 路径 封装访问器:如果封装的响应包含的是 Vault 令牌的身份验证信息,则这是一条指向封装令牌的访问器。这使得一些任务编排系统(例如 Nomad)可以根据他们对任务生命周期的了解来控制对应机密的生命周期非常有用(在任务继续运行时持续续期所需的机密租约),而无需实际解封响应封装令牌或获取封装中的令牌 ID 的信息。 Vault 目前不支持对响应封装令牌进行签名,所以它几乎没有提供额外的保护。如果我们使用的是正确的 Vault 服务器,则通过与服务器本身交互足以验证令牌;对令牌进行签名并不能消除与服务器验证令牌的需要,因为令牌不携带数据而只是一种访问机制,服务器不会在没有验证的情况下返回数据。如果我们受到某种攻击,被欺骗使用了错误的 Vault 服务器,则同一攻击者也可以为这个伪造的 Vault 服务器提供伪造的证书。您可以缓存过去有效的密钥,也可以缓存过去有效的地址(并且在大多数情况下,Vault 地址不会更改,或是将通过服务发现机制进行配置)。因此,我们把令牌设计成本身不携带能够自证身份的数据,并且不对其进行签名。 响应封装令牌的操作 通过 sys/wrapping 路径,我们可以对响应封装令牌进行如下操作: 查询(sys/wrapping/lookup):该操作可以获取响应封装令牌的创建时间、创建路径和 TTL。此路径无需身份验证,可用于查询响应包装令牌。换句话说,响应封装令牌的持有者总是可以查询令牌的信息来验证令牌有效性。 解封(sys/wrapping/unwrap):解封令牌,返回内部包含的响应信息。该操作返回的内容就是原先 API 调用的响应值的原始格式,可以被 API 客户端直接使用。 重新封装(sys/wrapping/rewrap):允许将封装的数据迁移到新的响应封装令牌。该操作适用于长生命周期的机密。例如,组织可能希望(或是合规要求)将 pki 后端返回的根 CA 密钥在长生命周期的响应封装令牌中返回,以确保没有人看到该 CA 密钥(通过查询响应封装信息即可确认这一点),同时也可以在证书吊销列表(CRL)发生变化时重新签署列表,或是防止 pki 挂载点意外丢失。通常来说合规要求定期轮替机密,因此这有助于实现合规目标,而无需暴露实际的内部内容。 封装(sys/wrapping/wrap):一个把发送给它的数据封装进一个响应封装令牌的帮助方法。请注意,屏蔽此端点并不会关闭封装任意数据的能力,因为该功能也可以在 Vault 的其他地方完成。 创建响应封装令牌 响应封装是按请求返回的,通过向 Vault 提供一个该请求的响应封装令牌所需的 TTL 来触发,由客户端使用 X-Vault-Wrap-TTL 标头来设置,可以是整数秒或字符串持续时间,包括秒 (15s)、分钟 (20m) 或小时 (25h)。使用 Vault CLI 时,您可以通过 -wrap-ttl 参数进行设置。当使用 Go API 时,包装是通过设置一个辅助函数来触发的,该函数通过将操作和路径映射到所需的 TTL 来告诉 API 如何封装。 如果客户端请求响应封装: 原来的 HTTP 响应会被序列化 一个一次性的令牌会被创建,其 TTL 被设定为客户端提交的值 原始的 HTTP 响应被序列化后会被保存进这个一次性令牌对应的用户的 cubbyhole 存储中 一个新的 HTTP 响应会被生成,带有这个一次性令牌的 ID、TTL 以及新响应中存储信息的路径 返回这个新的响应 注意,我们可以通过策略来控制封装的最大/最小 TTL。 验证响应封装令牌 对响应封装令牌进行必要的验证可以确保及时发现入侵行为,验证过程非常直接。 验证最好由以下步骤构成: 如果客户端一直等待的传递响应封装令牌始终没有到达,可能是有攻击者拦截了令牌,然后阻止它进一步传播。这应该触发警报并立即调查。 对响应封装令牌执行查找操作。这会立即告诉我们令牌是否已被解封或已过期(或以其他方式吊销)。如果查找表明令牌无效,不一定意味着数据被拦截了(例如可能是客户端启动时间过长且 TTL 已过期),但应触发警报以立即进行调查,可以借助 Vault 的审计日志来查看令牌是否真的已经被解封。 使用所拥有的令牌信息,验证创建路径是否符合预期。如果我们希望在其中得到的是 TLS 密钥/证书,那么路径很可能应该类似于 pki/issue/...。如果创建路径与预期的不一致时(特别是以 cubbyhole 或 sys/wrapping/wrap 开头),有可能是原先的响应已被读取,然后被重新封装进一个响应封装发送给了我们。应特别注意读取 kv 机密引擎的响应,创建路径应精确匹配。例如,如果你期望一个秘密来自于 secret/foo 并且入侵者拦截的响应,并返回了一个以 secret/bar 作为路径的令牌,那么仅仅检查 secret/ 的前缀是不够的。 验证了路径前缀后,解封令牌。如果令牌解封失败,和第一步中查询令牌信息失败一样,应立即出发警报并开始调查。 "},"2.基本概念/9.策略.html":{"url":"2.基本概念/9.策略.html","title":"策略","keywords":"","body":"策略——授权 Vault 模拟了一个文件系统,Vault 中所有的信息,包括机密、配置等,都是依照各自的路径来使用的。使用 Vault 策略,我们可以使用声明式的语法来赋予或者禁止对特定路径的特定操作。 Vault 的策略默认情况下是拒绝一切访问的,所以一个空的策略不会赋予对系统的任何访问权限。 策略——授权流程 在一个人类用户或是应用程序访问 Vault 之前,管理员必须首先为其配置相应的身份认证方式。身份认证方式是 Vault 通过验证人类用户或者应用程序提供的某种信息来确认其身份以及相应权限的过程。 参考下面的图,演示了一个安全团队配置 Vault 使用 LDAP 或是活动目录作为 Vault 的身份认证方式的流程: 安全团队为 Vault 配置身份认证方式,具体的配置项随不同的认证方式而不同。在本例中使用的是 LDAP,Vault 需要知晓 LDAP 的服务的地址,以及是否使用 TLS。需要特别说明,Vault本身并不会保存一份 LDAP 的数据副本,而是将身份认证工作代理给 LDAP 服务。 安全团队编写一个策略(或是复用已有策略)授予对 Vault 中特定路径的访问权限。策略使用 HCL 语法编写,保存在本地磁盘上。 上传策略,保存在 Vault 存储中,每个策略都有一个名字,通过策略名引用策略。可以将策略名理解成指向策略定义的规则集合的指针,或是符号连接。 最重要的是,安全团队将身份认证方式中的数据与一个策略关联起来。例如,安全团队可以创建这样一个关联: 名为“dev”的 OU(Organization Unit)组的成员关联名为“readonly-dev”的Vault策略 或者: 名为“ops”的 OU(Organization Unit)组的成员关联名为“admin”和“auditor”的 Vault 策略 现在 Vault 内部有了一个身份认证系统与内部策略的关联关系。当一个用户请求 Vault 认证身份时,实际的认证工作被代理给了相关的身份认证方式,流程如下图: 一个用户试图使用它的 LDAP 凭证通过 Vault 的身份认证,它向 Vault 提交了它的 LDAP 用户名和密码 Vault 建立与 LDAP 服务的连接,请求 LDAP 服务认证它收到的凭证。假设认证成功,LDAP 服务返回用户相关信息,包括用户所属的OU组 Vault 使用前面叙述的安全团队预先配置的的关联关系,将 LDAP 返回的结果映射到相关策略。Vault 生成一个客户端令牌(Token),并将策略附加到该令牌上 Vault 向客户端返回令牌。该令牌已被赋予正确的策略,一如安全团队先前设定的那样 用户可以使用 Vault 令牌执行后续的操作。如果用户重新执行身份认证操作,他会得到一个新的令牌。这个新的令牌拥有与老令牌一样的权限,但实际的令牌不同。重新执行身份认证不会吊销老令牌。 策略语法 策略使用 HCL 或是 JSON 语法编写,描述了一个人类用户或是应用程序允许访问 Vault 中哪些路径。 一个简单的例子,赋予对 secret/foo 路径的读权限: path \"secret/foo\" { capabilities = [\"read\"] } 当这个策略被附加到一个令牌后,该令牌可以读取 secret/foo,然而无法修改或删除 secret/foo,因为没有授予其相关能力(Capability)。由于策略系统是默认拒绝的,所以令牌在Vault中没有其他权限。 另一个更加丰富的策略,包含注释: # This section grants all access on \"secret/*\". Further restrictions can be # applied to this broad policy, as shown below. path \"secret/*\" { capabilities = [\"create\", \"read\", \"update\", \"delete\", \"list\"] } # Even though we allowed secret/*, this line explicitly denies # secret/super-secret. This takes precedence. path \"secret/super-secret\" { capabilities = [\"deny\"] } # Policies can also specify allowed, disallowed, and required parameters. Here # the key \"secret/restricted\" can only contain \"foo\" (any value) and \"bar\" (one # of \"zip\" or \"zap\"). path \"secret/restricted\" { capabilities = [\"create\"] allowed_parameters = { \"foo\" = [] \"bar\" = [\"zip\", \"zap\"] } } 策略基于路径匹配来验证一个请求所需要的能力。一个策略路径可以精准匹配一个确切的路径,或者可以使用 * 模式指定前缀匹配: # Permit reading only \"secret/foo\". An attached token cannot read \"secret/food\" # or \"secret/foo/bar\". path \"secret/foo\" { capabilities = [\"read\"] } # Permit reading everything under \"secret/bar\". An attached token could read # \"secret/bar/zip\", \"secret/bar/zip/zap\", but not \"secret/bars/zip\". path \"secret/bar/*\" { capabilities = [\"read\"] } # Permit reading everything prefixed with \"zip-\". An attached token could read # \"secret/zip-zap\" or \"secret/zip-zap/zong\", but not \"secret/zip/zap path \"secret/zip-*\" { capabilities = [\"read\"] } 另外,路径当中可以使用 + 代表路径中一个段内任意长度的字符(从 Vault 1.1 开始支持): # Permit reading the \"teamb\" path under any top-level path under secret/ path \"secret/+/teamb\" { capabilities = [\"read\"] } # Permit reading secret/foo/bar/teamb, secret/bar/foo/teamb, etc. path \"secret/+/+/teamb\" { capabilities = [\"read\"] } 我们说过,Vault 模拟了一个文件系统,所有的操作都对应了一个路径,以及对应的能力,即使是 Vault 内部的核心配置信息也挂载于 sys/ 路径下。策略可以定义一个令牌对这些路径和能力的访问权限。 Vault 采用一组具有优先级的判定规则来决定最为具体的路径匹配。如果一个匹配模式被多个策略使用并能匹配上给定路径,Vault 会取其能力的并集。如果一个路径能被多个策略定义的不同的匹配模式所匹配,那么只有最高优先级的匹配会被采用。 假设对给定路径 P,存在两条策略都能够匹配,它们的路径匹配模式分别是 P1 和 P2,Vault 采用如下优先级规则: 如果 P1 中第一个 + 或是 * 出现的位置早于 P2,那么采用 P2 如果 P1 以 * 结尾,而 P2 不是,那么采用 P2 如果 P1 包含更多的 + 段,那么采用 P2 如果 P1 更短,那么采用 P2 如果 P1 得到的字典序更小,那么采用 P2 举个例子,给定两个路径:secret/* 和 secret/+/+/foo/*,由于第一个通配符的位置相同(都在 secret/ 之后),并且都以 * 结尾,而后者拥有更多的通配符段(多了两个 +/ 段),所以结果是使用 secret/* 路径模式的策略。 需要注意的是,* 与正则表达式中的同符号并不同义,Vault仅允许 * 出现在模式的末尾。 如果赋予了 list 能力,需要注意的是因为 list 操作总是作用于一个路径前缀上,所以策略定义的路径匹配模式必须使用前缀匹配(即以 * 结尾)。 能力(Capabilites) 除了路径匹配模式以外,每条规则都必须指定一个或多个能力来定义细颗粒度的允许-禁止规则。能力永远以一个字符串列表的形式定义,哪怕只有一个能力。 需要注意的是,我们在下面列出能力的同时,也会给出该能力相对应的 HTTP 动词。当编写策略时,可以先查阅相关 HTTP API 文档了解路径信息以及相关 HTTP 动词,然后映射到策略定义中的能力。虽然映射关系并不是严格的 1:1,但它们通常非常相似地匹配。 create(POST/PUT)——允许在指定路径创建数据。只有很少的Vault部件会区分 create 和 update,所以大多数操作同时需要 create 以及 update 能力。需要区分二者的部分会在相关文档中说明。 read(GET)——允许读取指定路径的数据 update(POST/PUT)——允许修改指定路径的数据。对多数Vault部件来说,这隐含了在指定位置创建初始值的能力 delete(DELETE)——允许删除指定路径的数据 list(LIST)——允许罗列指定路径的所有值。要注意的是,经由 list 操作返回的键是未经策略过滤的。请勿在键名中编码敏感信息。不是所有后端都支持 list 操作 下面的能力并无对应的 HTTP 动词: sudo——允许访问需要根权限保护的路径。除非拥有 sudo 能力,否则令牌被禁止与这些路径交互(对这种路径的操作可能同时需要其他能力,例如 read 或 delete) 例如,修改审计日志后端配置就需要令牌具有 sudo 特权。 deny——不允许访问。该定义总是优先于其他能力定义,包括 sudo,只要能力中存在 deny,就不允许执行任何操作 需要注意的是,上述的能力映射到的是 HTTP 动词而非实际底层执行的操作,这通常会使人感到困惑。举个例子,通过 Vault 的数据库机密引擎创建一个数据库用户名密码,底层实际执行的操作是创建,但对应的 HTTP 动词却是 GET,所以要在对应路径上配置 read 能力。 细颗粒度控制 除却以上标准的能力,Vault 还提供了对指定路径的更细颗粒度的权限控制。与路径相关联的功能优先于对参数的控制。 参数限制 需要首先说明,Version 2 的 kv 机密引擎不支持参数限制,所以以下例子均假设 secret/ 路径挂载的是 Version 1 的 kv 机密引擎。 Vault 中的数据以键值对的形式表达:key=value。Vault 策略可以进一步限制对指定路径下特定键和值的访问。这些可选的细颗粒度控制项有: required_parameters—— 一组必须指定的参数: # This requires the user to create \"secret/foo\" with a parameter named # \"bar\" and \"baz\". path \"secret/foo\" { capabilities = [\"create\"] required_parameters = [\"bar\", \"baz\"] } 上述例子中,用户想要在 secret/foo 下创建数据,必须包含名为 bar 和 baz 的参数。 allowed_parameters—— 针对指定路径允许操作的键值对的白名单。值为空白列表则代表允许使用任意值: # This allows the user to create \"secret/foo\" with a parameter named # \"bar\". It cannot contain any other parameters, but \"bar\" can contain # any value. path \"secret/foo\" { capabilities = [\"create\"] allowed_parameters = { \"bar\" = [] } } 上述例子允许在 secret/foo 下创建键为 bar,值为任意值的数据。 给定非空列表值则意味着只能使用列表中限定的值: # This allows the user to create \"secret/foo\" with a parameter named # \"bar\". It cannot contain any other parameters, and \"bar\" can only # contain the values \"zip\" or \"zap\". path \"secret/foo\" { capabilities = [\"create\"] allowed_parameters = { \"bar\" = [\"zip\", \"zap\"] } } 上述例子允许在 secret/foo 下创建键为 bar,值为 zip 或 zap 的数据。 如果指定了任意的键,那么所有未被指定的键都会被拒绝,除非同时指定了 * 参数为空列表,这就允许修改其他任意键数据。被指定的键仍然受到给定的值列表的限制: # This allows the user to create \"secret/foo\" with a parameter named # \"bar\". The parameter \"bar\" can only contain the values \"zip\" or \"zap\", # but any other parameters may be created with any value. path \"secret/foo\" { capabilities = [\"create\"] allowed_parameters = { \"bar\" = [\"zip\", \"zap\"] \"*\" = [] } } 上述例子中,只限制对 bar 的值必须是 zip 或 zap,对其他键的值则没有任何限制,可以创建任意键。 重要的一点是,使用 * 可能会造成意外的后果: # This allows the user to create or update \"secret/foo\" with a parameter # named \"bar\". The values passed to parameter \"bar\" must start with \"baz/\" # so values like \"baz/quux\" are fine. However, values like # \"baz/quux,wibble,wobble,wubble\" would also be accepted. The API that # underlies \"secret/foo\" might allow comma delimited values for the \"bar\" # parameter, and if it did, specifying a value like # \"baz/quux,wibble,wobble,wubble\" would result in 4 different values getting # passed along. Seeing values like \"wibble\" or \"wobble\" getting passed to # \"secret/foo\" might surprise someone that expected the allowed_parameters # constraint to only allow values starting with \"baz/\". path \"secret/foo\" { capabilities = [\"create\", \"update\"] allowed_parameters = { \"bar\" = [\"baz/*\"] } } 在上面的例子中,我们限制对 secret/foo 只能写入键为 bar 的数据,并且值必须以 baz/ 为前缀。比如 bar=baz/quux 这样的数据就是合法的。问题是,我们也可以把值设置成 baz/quux,wibble,wobble,wubble,Vault 会接纳这种带有分隔符的值,而这样的值可能会被应用程序解析为长度为 4 的列表,这样的话我们可能会惊讶地发现即使我们限制了 bar 的值必须以 baz/ 为前缀,仍然读取到了诸如 wibble 这样的值。 denied_parameters—— 键值对的黑名单,优先级高于 allowed_parameters。 设置值为空列表会导致拒绝对对应键的任意修改。 # This allows the user to create \"secret/foo\" with any parameters not # named \"bar\". path \"secret/foo\" { capabilities = [\"create\"] denied_parameters = { \"bar\" = [] } } 上面的例子禁止在 secret/foo 下创建键为 bar 的任意键值对。 如果对 denied_parameters 赋值一个非空列表,会导致禁止参数的值包含列表中的任意元素: # This allows the user to create \"secret/foo\" with a parameter named # \"bar\". It can contain any other parameters, but \"bar\" cannot contain # the values \"zip\" or \"zap\". path \"secret/foo\" { capabilities = [\"create\"] denied_parameters = { \"bar\" = [\"zip\", \"zap\"] } } 上述例子禁止设置 secret/foo 的 bar 的值为 zip 或是 zap。 设置 denied_parameters 的键为 *,则禁止操作任意键: # This allows the user to create \"secret/foo\", but it cannot have any # parameters. path \"secret/foo\" { capabilities = [\"create\"] denied_parameters = { \"*\" = [] } } 如果 denied_parameters 配置了任意键,那么默认所有未被指定的键都是允许操作的,除非另有显式的 allowed_parameters 配置。 参数限制中的值也支持前缀与后缀表示: path \"secret/foo\" { capabilities = [\"create\"] allowed_parameters = { \"bar\" = [\"foo-*\"] } } 上面的例子规定对 secret/foo,只能创建键为 bar,值以 foo- 为前缀的键值对。 path \"secret/foo\" { capabilities = [\"create\"] allowed_parameters = { \"bar\" = [\"*-foo\"] } } 而上面的例子则是限制值必须以 -foo 为后缀。 限制响应封装的有效期 我们在前文中介绍过Vault的响应封装机制。我们可以在策略中使用相关参数限制客户端可以申请的响应封装的有效期时限,精确到秒。我们可以通过 s、m 或是 h 后缀来代表秒、分钟和小时。 在实践中,为特定路径指定值为一秒的 min_wrapping_ttl 可以达到强制必须以响应封装的形式返回相应路径数据的目的。 min_wrapping_ttl——客户端可以指定的响应封装有效期的最小值,如果设置该值,则强制必须以响应封装的形式返回相应路径的数据 max_wrapping_ttl——允许设置的响应封装有效期的最大值。 # This effectively makes response wrapping mandatory for this path by setting min_wrapping_ttl to 1 second. # This also sets this path's wrapped response maximum allowed TTL to 90 seconds. path \"auth/approle/role/my-role/secret-id\" { capabilities = [\"create\", \"update\"] min_wrapping_ttl = \"1s\" max_wrapping_ttl = \"90s\" } 上面的例子限制了用户只能以响应封装的形式返回 auth/approle/role/my-role/secret-id 的值,并且响应封装的有效期最大为 90 秒。 当两个参数同时被指定时,最小值必须小于最大值。另外如果当不同区块的路径合并时,最低值会被采纳,目的是使得令牌的有效期尽可能的短。 内建策略 Vault 有两个内建策略:default 和 root。本节来讨论下这两个内建策略。 Default策略 default 策略是一个无法删除的 Vault 内建策略。默认情况下,它被附加到所有令牌上,但可以通过使用身份验证方式创建令牌时显式排除之。 该策略包含了基础的功能,例如准许令牌查询有关自身的数据以及使用 Cubbyhole 数据。然而,Vault 并不限制该策略的内容,你可以根据需要修改它。Vault 永远不会覆盖您的设置。如果您想把 default 策略同步到最新的 Vault 版本的默认值,只要用新版 Vault 执行 vault server -dev 启动一个测试服务,读取它的 default 策略内容,然后写回到原来的 Vault 服务的 default 策略即可: $ vault read sys/policy/default Key Value --- ----- name default rules # Allow tokens to look up their own properties path \"auth/token/lookup-self\" { capabilities = [\"read\"] } # Allow tokens to renew themselves path \"auth/token/renew-self\" { capabilities = [\"update\"] } # Allow tokens to revoke themselves path \"auth/token/revoke-self\" { capabilities = [\"update\"] } # Allow a token to look up its own capabilities on a path path \"sys/capabilities-self\" { capabilities = [\"update\"] } # Allow a token to look up its own entity by id or name path \"identity/entity/id/{{identity.entity.id}}\" { capabilities = [\"read\"] } path \"identity/entity/name/{{identity.entity.name}}\" { capabilities = [\"read\"] } # Allow a token to look up its resultant ACL from all policies. This is useful # for UIs. It is an internal path because the format may change at any time # based on how the internal ACL features and capabilities change. path \"sys/internal/ui/resultant-acl\" { capabilities = [\"read\"] } # Allow a token to renew a lease via lease_id in the request body; old path for # old clients, new path for newer path \"sys/renew\" { capabilities = [\"update\"] } path \"sys/leases/renew\" { capabilities = [\"update\"] } # Allow looking up lease properties. This requires knowing the lease ID ahead # of time and does not divulge any sensitive information. path \"sys/leases/lookup\" { capabilities = [\"update\"] } # Allow a token to manage its own cubbyhole path \"cubbyhole/*\" { capabilities = [\"create\", \"read\", \"update\", \"delete\", \"list\"] } # Allow a token to wrap arbitrary values in a response-wrapping token path \"sys/wrapping/wrap\" { capabilities = [\"update\"] } # Allow a token to look up the creation time and TTL of a given # response-wrapping token path \"sys/wrapping/lookup\" { capabilities = [\"update\"] } # Allow a token to unwrap a response-wrapping token. This is a convenience to # avoid client token swapping since this is also part of the response wrapping # policy. path \"sys/wrapping/unwrap\" { capabilities = [\"update\"] } # Allow general purpose tools path \"sys/tools/hash\" { capabilities = [\"update\"] } path \"sys/tools/hash/*\" { capabilities = [\"update\"] } # Allow checking the status of a Control Group request if the user has the # accessor path \"sys/control-group/request\" { capabilities = [\"update\"] } 创建令牌时排除 default 策略: $ vault token create -no-default-policy 或是调用 API: $ curl \\ --request POST \\ --header \"X-Vault-Token: ...\" \\ --data '{\"no_default_policy\": \"true\"}' \\ https://vault.hashicorp.rocks/v1/auth/token/create 根策略 root 策略是一个无法删除也无法修改的 Vault 内建策略。任何关联了该策略的用户都将是根用户。根用户可以在 Vault 内执行任意操作,强烈建议在生产环境中使用 Vault 前首先吊销所有的根令牌。 每当 Vault 服务被首次初始化时,都会创建一个根用户。这个根用户是用来执行 Vault 的初始化配置的。配置完成后,应创建并使用由细颗粒度策略约束的用户并启用身份认证方式,然后吊销根令牌。 要吊销根令牌可以使用命令行: $ vault token revoke \"\" 或是通过 HTTP API: $ curl \\ --request POST \\ --header \"X-Vault-Token: ...\" \\ --data '{\"token\": \"\"}' \\ https://vault.hashicorp.rocks/v1/auth/token/revoke 管理策略 您可以使用任意编辑器编写策略,策略文件可以采用 HCL 语法或是 JSON 语法。保存策略文件本地磁盘后,需要上传策略文件到 Vault 服务,随后才能使用。 枚举策略 通过命令行列出所有已注册的策略: $ vault read sys/policy 或是通过 HTTP API: $ curl \\ --header \"X-Vault-Token: ...\" \\ https://vault.hashicorp.rocks/v1/sys/policy 创建策略 使用命令行上传并创建策略: $ vault policy write policy-name policy-file.hcl 使用 HTTP API: $ curl \\ --request POST \\ --header \"X-Vault-Token: ...\" \\ --data '{\"policy\":\"path \\\"...\\\" {...} \"}' \\ https://vault.hashicorp.rocks/v1/sys/policy/policy-name 这两个例子里,策略的名称都是 policy-name。您可以把策略名理解成指向策略规则的指针。令牌通过策略名关联相关策略规则。 更新策略 更新已有策略内容与创建策略操作一致,使用的是已有的策略名。使用命令行: $ vault write my-existing-policy updated-policy.json 或者使用 HTTP API: $ curl \\ --request POST \\ --header \"X-Vault-Token: ...\" \\ --data '{\"policy\":\"path \\\"...\\\" {...} \"}' \\ https://vault.hashicorp.rocks/v1/sys/policy/my-existing-policy 删除策略 通过命令行删除一个已有策略: $ vault delete sys/policy/policy-name 或是通过 HTTP API: $ curl \\ --request DELETE \\ --header \"X-Vault-Token: ...\" \\ https://vault.hashicorp.rocks/v1/sys/policy/policy-name 删除是一个幂等操作。删除一个不存在的策略不会导致 Vault 返回错误。 关联策略 Vault 可以通过身份认证方式登录时自动在令牌上关联一组策略,相关配置随具体的身份认证方式类型而不同。简单起见,我们演示一下 Vault 内建的 userpass 认证方式。 Vault 管理员或是安全团队成员可以用如下命令行创建一个关联了一组策略的用户: $ vault write auth/userpass/users/sethvargo \\ password=\"s3cr3t!\" \\ policies=\"dev-readonly,logs\" 如此便创建了名为 sethvargo 的用户,通过用户名密码如果成功过认证了身份,Vault 会返回一个附加了 dev-readonly 和 logs 策略的令牌。 用户可以用命令行执行身份认证,获取令牌: $ vault login -method=\"userpass\" username=\"sethvargo\" Password (will be hidden): ... 如果用户名密码正确,Vault 会创建一个令牌,将预设的策略附加在令牌上,然后返回给用户。 创建令牌时附加策略 可以在通过命令行创建令牌时关联策略: $ vault token create -policy=dev-readonly -policy=logs 子令牌可以关联一组父令牌拥有的策略的子集。根用户可以关联任意策略。 一旦令牌被签发,其关联的策略无法再被修改。必须吊销旧令牌并申请新令牌才能得到更新后的关联策略。 然而,令牌关联的策略内容是实时解析的,也就是说,如果更新了策略内容,附加此策略的令牌下次的请求就会按照新策略内容进行权限检查。 "},"2.基本概念/10.密码策略.html":{"url":"2.基本概念/10.密码策略.html","title":"密码策略","keywords":"","body":"密码策略 密码策略是一组关于如何生成密码的指令,类似于密码生成器。这些密码策略被用于一部分的机密引擎中,以允许我们配置该引擎应该如何生成密码。并非所有机密引擎都使用密码策略,因此在使用引擎前请先阅读相关文档以了解兼容性。 请注意,密码策略与前章所叙述的策略并无关系,只是名字中都有策略二字。 密码策略是从 Vault 1.5.0 开始引入的,可以阅读相关 API 文档。 注意,密码策略是 Vault 的一项高级功能,为外部系统(例如数据库、LDAP、AWS 等)设置生成规则时需要小心使用。 设计 密码策略基本上由两部分组成:密码长度,以及密码必须遵守的一组规则。密码是从一个由所有规则各自生成的字符集合,去重后得到的并集中随机生成,然后根据每个规则检查以确定候选密码是否符合策略约束。 规则是对候选密码字符串的断言,判断密码是否可接受。例如:“charset”规则规定密码中必须至少包含一个小写字母。此规则将拒绝任何不包含任何小写字母的密码。 可以在一个策略中指定多个规则以创建更复杂的规则,例如要求至少一个小写字母、至少一个大写字母和至少一个数字。 该过程的状态机如下: 生成候选密码 如何生成候选密码是非常重要的,必须非常小心,以确保创建密码的过程不会被恶意利用。本节介绍我们如何在密码策略中生成密码,以确保尽可能安全地生成密码。 要生成一个候选密码,我们需要: 一个加密算法意义上安全的随机数生成器( cryptographically secure random number generator,RNG) 一个候选字符集 密码长度 大致说来,我们使用 RNG 生成 N 个数字,这些数字对应于字符集数组中的索引,其中 N 是我们希望创建的密码的长度。然后使用从 RNG 返回的每个值从字符集中提取一个字符到密码中。 举个例子,让我们从字符集 abcdefghij 生成长度为 8 的密码: 我们使用 RNG 生成 8 个随机数,举例来说我们得到的是: [3, 2, 0, 8, 7, 3, 5, 1] 将每个随机数映射成字符集上对应的字符: [3, 2, 0, 8, 7, 3, 5, 1] => [d, c, a, i, h, d, f, b] 我们得到的候选密码就是:dcaihdfb,然后可以用来判断它是否符合密码策略定义的规则限制。 在实际场景中,随机数组中的值将介于 [0-255] 之间,因为这是单个字节可以取值的范围范围。通过使用模运算来防止引用字符集边界之外的字符,该值被限制为不超过字符集数组的大小。然而,这可能会引入偏差(Bias)问题。 防止偏差 使用模运算生成密码时,你必须非常小心地防止引入偏差。当使用长度不能被 256 整除的字符集生成 [0-255] 之间的随机数时,某些字符被选中的概率可能会比其他字符要高。 为了演示这个问题,让我们简化一下数学。假设我们有一个长度为 10 的字符集:abcdefghij。让我们假设 RNG 生成值的范围是 [0-25]。前 10 个值 0-9 对应于我们字符集中的每个字符。接下来的 10 个值 10-19 也对应于我们字符集中的每个字符。但是,接下来的 6 个值 20-25 仅对应于字符集中的前 6 个字符。这意味着前 6 个字符 abcdef可能出现的比比最后 4 个字符 ghij 更加频繁。 为了防止这种情况发生,我们计算了一个索引可以达到的最大值。这个值是基于我们从中选择的字符集的长度计算的。在上面的例子中,我们应该允许的最大索引值是 19,因为它表示字符集数组长度的最大整数倍,它小于我们的 RNG 可以生成的最大值。当我们的 RNG 生成任何大于我们最大允许值的值时,该数字将被忽略,我们继续下一个数字。密码不会丢失任何长度,因为我们会继续生成数字,直到密码完全填写到要求的长度为止。 性能特点 使用此模型生成密码的性能在很大程度上取决于配置的策略。简而言之,策略越严格,生成密码所需的时间就越长。这种概括并不总是正确的,但一般来说如此。性能曲线可以概括为: (生成一个候选密码的时间) * (生成的候选密码数量) 这里面生成的候选密码数量是一个用来评估一个候选密码有多大的可能性无法通过所有策略约束的函数确定的。 下面是一些样例策略及其性能特征。每一个策略都具有相同的用以生成候选密码的字符集(94 个字符)。唯一的区别是各种字符子集中所需要选取的最小字符数。 无最小字符数限制: rule \"charset\" { charset = \"abcdefghijklmnopqrstuvwxyz\" } rule \"charset\" { charset = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" } rule \"charset\" { charset = \"0123456789\" } rule \"charset\" { charset = \"!\\\"#$%&'()*+,-./:;?@[\\\\]^_`{|}~\" } 一个大写字母,一个小写字母,一个数字: rule \"charset\" { charset = \"abcdefghijklmnopqrstuvwxyz\" min-chars = 1 } rule \"charset\" { charset = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" min-chars = 1 } rule \"charset\" { charset = \"0123456789\" min-chars = 1 } rule \"charset\" { charset = \"!\\\"#$%&'()*+,-./:;?@[\\\\]^_`{|}~\" } 一个大写字母,一个小写字母,一个数字,一个符号: rule \"charset\" { charset = \"abcdefghijklmnopqrstuvwxyz\" min-chars = 1 } rule \"charset\" { charset = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" min-chars = 1 } rule \"charset\" { charset = \"0123456789\" min-chars = 1 } rule \"charset\" { charset = \"!\\\"#$%&'()*+,-./:;?@[\\\\]^_`{|}~\" min-chars = 1 } 一个大写字母,一个小写字母,一个数字,!@#$中选择一个字符: rule \"charset\" { charset = \"abcdefghijklmnopqrstuvwxyz\" min-chars = 1 } rule \"charset\" { charset = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" min-chars = 1 } rule \"charset\" { charset = \"0123456789\" min-chars = 1 } rule \"charset\" { charset = \"!@#$\" min-chars = 1 } # Fleshes out the rest of the symbols but doesn't add any required characters rule \"charset\" { charset = \"!\\\"#$%&'()*+,-./:;?@[\\\\]^_`{|}~\" } 随着生成的字符越多,时间越长(如无最少字符中所示)。通过限制字符集可以使这种上升趋势忽略不计。当密码较短时,从字符集中选择特定字符的机会比较小。例如,如果要从字符集 abcde 生成 1 个字符的密码,从中选择 c 的机会是 1/5。但是,如果要生成 2 个字符的密码,则至少选择 c 一次的机会大于 1/5,因为 c 两次机会被选择。 在这些示例中,随着密码长度的增加,生成密码的时间量呈下降趋势,趋于平稳,然后缓慢增加。这是上面列出的两种因素的组合:生成更多字符所增加的时间与某个子集中的字符被选中的概率。当某个子集非常小(例如 !@#$)时,它被选中的机会(4/94)比子集较大时(小写字符为 26/94)要小得多。这可能会导致性能急剧下降(因为更有可能因为缺乏某个子集字符而导致密码不符合规则要求)。 在上面的示例中,用于生成候选密码的字符集长度为 94 个字符。从 94 个字符的字符集中随机选择一个给定的字符有 1/94 的机会。在 N 次尝试后从中选择指定字符(其中 N 是密码的长度)的概率是 1-(1-1/94)^N。 如果我们将其扩展为一个字符集的子集(例如小写字符),则从该子集中选择字符的机会为 1-(1-L/94)^N,其中 L 是子集的长度。选中小写字符的概率是 1-(1-26/94)^N。 如果我们使用大小写字母加数字,那么我们会得到一个组合概率曲线: p = (1-(1-26/94)^N) * (1-(1-26/94)^N) * (1-(1-10/94)^N) 需要注意的是,该概率曲线仅适用于该特定策略。要了解给定策略的性能特征,应该使用 generate 端点运行该策略,以查看策略生成密码所需的时间。 密码策略语法 密码策略使用 HCL 或 JSON 定义,它定义了密码的长度和密码必须遵守的一组规则。 这里有一个简单的生成20位小写字母构成的密码的规则: length = 20 rule \"charset\" { charset = \"abcdefghijklmnopqrstuvwxyz\" } 可以指定多条规则,包括多个相同类型的规则。例如,以下策略将生成一个 20 个字符的密码,其中包含至少一个小写字母、至少一个大写字母、至少一个数字和至少一个来自集合 !@#$%^&* 的符号: length = 20 rule \"charset\" { charset = \"abcdefghijklmnopqrstuvwxyz\" min-chars = 1 } rule \"charset\" { charset = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" min-chars = 1 } rule \"charset\" { charset = \"0123456789\" min-chars = 1 } rule \"charset\" { charset = \"!@#$%^&*\" min-chars = 1 } 必须指定至少一个字符集才能使策略有效。为了生成密码,字符集必须可用于从中选择字符,密码策略不提供默认字符集。以下策略是无效的,将被拒绝: length = 20 配置文件 & 可用策略 length参数 length``(int:)——指定要生成的密码长度。必须 >= 4。 长度并不是一条规则。它是配置中唯一不用遵循“猜测——检查是否符合策略规范”流程的部分。 规则 charset 允许指定从给定的字符集中至少出现的字符数。例如:密码必须至少有一个小写字母。此规则提供了构建密码生成所使用的字符集。为了生成密码,必须指定字符集。 如果指定了多个字符集,则在生成任何候选密码之前,所有字符集都将被组合和去重。一个成功生成密码需要符合每个单独的字符集规则的规定。 需要注意的是,在对字符集进行组合和去重之后,生成候选密码的字符集长度不得超过 256 个字符。 参数 charset``(string:)——此规则遵守的字符集的字符串表示形式。接受兼容 UTF-8 编码的字符串。字符串中的所有字符都必须是可打印的。 请注意,根据 JSON 规范,返回的 JSON 输出可能会为特殊和控制字符(例如 、& 等)进行转义。 min-chars``(int:0)——指定此规则中指定的字符集所需被选中的最少字符数。例如:如果 min-chars = 2,则密码必须至少有 2 个源自 charset 的字符。 例子 length = 20 rule \"charset\" { charset = \"abcde\" min-chars = 1 } rule \"charset\" { charset = \"01234\" min-chars = 1 } 此策略将从字符集 abcde01234 生成密码。但是,密码必须至少有一个来自 abcde 的字符和至少一个来自 01234 的字符。如果字符集在规则之间重叠,字符集将被删除重复以防止重叠部分的字符更容易被选中。例如:如果您有两个字符集规则:abcde & cdefg,则字符集 abcdefg 将用于生成候选密码,但 abcde & cdefg 中的各自有至少一个字符必须出现在密码中。 如果未指定 min-chars(或设置为 0),则此字符集将没有所需的最小字符数,但将用于从中选择字符。例子: length = 8 rule \"charset\" { charset = \"abcde\" } rule \"charset\" { charset = \"01234\" min-chars = 1 } 此策略从字符集 abcde01234 生成 8 个字符的密码,并要求其中至少包含 01234 中的一个字符,但不强制要求 abcde 中的任何字符。此策略可能生成密码 04031945 ,即使其中没有字母。 "},"2.基本概念/11.用户名模板.html":{"url":"2.基本概念/11.用户名模板.html","title":"用户名模板","keywords":"","body":"用户名模板 一些为外部系统生成动态用户的机密引擎允许 Vault 操作员定制如何生成关联的外部系统中的用户名。此自定义功能使用了 Go 模板语言。本节描述了使用这些模板生成用户名的基础知识,但没有深入使用模板语言进行更高级的用法。请参阅对应机密引擎的 API 文档,以确定它是否支持用户名模板,以及在该引擎上使用用户名模板的更多详细信息。 在定制用户名的生成方式时,请注意确保有足够的随机性以确保唯一性,否则创建凭据的多次调用可能会相互干扰。 除了内置在 Go 模板语言中的功能外,还有许多附加功能可用: 可用函数 操作字符串与字符的函数 lowercase——将输入值转小写 样例:{{.FieldName | lowercase}} replace——在输入值中寻找并替换指定字符串 样例:{{.FieldName | replace - _}} truncate——将输入值截断至指定长度 样例:{{.FieldName | truncate 10}} truncate_sha256——将输入值截断为指定的字符数,得到的新值的最后 8 个字符将被截断字符的 SHA256 哈希码的前 8 个字符替换。 样例:{{.FieldName | truncate_sha256 20}}。如果FieldName的值是abcdefghijklmnopqrstuvwxyz,在第 12 个字符 l 之后的字符都会被删除,剩下的部分会被计算 SHA256 哈希码,得到872808ffbf...1886ca6f20。哈希码的头 8 个字符,也就是872808ff会被拼接到原来的 12 子字母之后,得到的结果就是abcdefghijkl872808ff。 uppercase——将输入值转为大写 样例:{{.FieldName | uppercase}} 生成值的函数 random——从小写字母、大写字母和数字生成随机字符串。必须包含一个数字来指定要生成的长度。 样例:{{random 20}} 生成20位长的随机字符串 timestamp——当前的时间戳,后面的参数必须以 Go 语言的时间库规定的格式提供时间格式模板 样例:{{timestamp \"2006-01-02T15:04:05Z\"}} unix_time——当前的 unix 时间戳(从 1970 年 01 月 01 日起到当前时间的秒数) 样例:{{unix_time}} unix_time_millis——毫秒计数的当前 unix 时间戳 样例:{{unix_time_millis}} uuid——生成一个随机的 UUID 样例:{{uuid}} 哈希函数 base64——将输入值进行 Base64 编码 样例:{{.FieldName | base64}} sha256——计算输入值的 SHA256 哈希码 样例:{{.FieldName | sha256}} 例子 不同的机密引擎为模板提供了不同的数据集。有关提供给模板的值的详细信息,请参阅相关的机密引擎的文档。下面的示例以 Database 机密引擎的数据为模型,但是从给定引擎提供的特定字段可能与这些示例不同。此外,假设当前的时间是 2009-02-13 11:31:30PM GMT(unix 时间戳:1234567890),随机字符集是有序的英文字母表:abcdefghijklmnopqrstuvwxyz。 请注意,{{ 与 }}之间的空格是可选的,例如,{{.DisplayName}}和{{ .DisplayName }}是等价的。 下表是我们例子中将会读到的字段的值: 字段名 值 DisplayName token-with-display-name RoleName my_custom_database_role 要引用这些字段中的任何一个,请在字段名称前面加上.:{{.DisplayName}}。本文所述的函数前面不加.:{{random 20}}。 基本例子 模板: {{.DisplayName}}_{{.RoleName}} 生成的用户名是: token-with-display-name_my_custom_database_role 这是引用提供给模板的两个字段的基本示例。简单来说,这是一个简单的字符串替换。 此示例没有任何随机性,不应在生成动态用户名时使用。目的是演示在 Go 模板语言中引用数据。 定制函数例子 模板: FOO_{{.DisplayName | replace \"-\" \"_\" | uppercase}}_{{.RoleName | replace \"-\" \"_\" | uppercase}}_{{timestamp \"2006_01_02T15_04_05Z\" | replace \"-\" \"_\"}} 生成的用户名: FOO_TOKEN_WITH_DISPLAY_NAME_MY_CUSTOM_DATABASE_ROLE_2009_02_13T11_31_30Z_0700 {{.DisplayName | replace \"-\" \"_\" | uppercase}}——将显示名中所有的折线替换成下划线,然后转为大写字母。 {{.RoleName | replace \"-\" \"_\" | uppercase}}——将角色名中所有的折线替换成下划线,然后转为大写字母。 {{timestamp \"2006_01_02T15_04_05Z\" | replace \"-\" \"_\"}}——使用2006_01_02T15_04_05Z这样的格式生成当前时间的时间戳,并将所有的折线替换成下划线。 截断至最大长度的例子 模板: {{printf \"v_%s_%s_%s_%s\" (.DisplayName | truncate 8) (.RoleName | truncate 8) (random 20) (unix_time) | truncate 45}} 生成的用户名: v_token-wi_my_custo_abcdefghijklmnopqrst_1234 .DisplayName | truncate 8:将展示名截断到8位长度(token-wi)。 .RoleName | truncate 8:将角色名截断到8位长度(my_custo)。 random 20:生成的 20 位长的随机字符串(假设是abcdefghijklmnopqrst)。 unix_time:生成当前是时间距离 1970 年 01 月 01 日零时的秒数(1234567890) 上面的每个值都被传递给了 printf \"v_%s_%s_%s_%s\" ,在它们前面加上 v_ 并在每个字段之间放置一个下划线,到这里为止生成了 v_token-wi_my_custo_abcdefghijklmnopqrst_1234567890。然后将此值传递给 truncate 45,其中最后 6 个字符被删除,得到 v_token-wi_my_custo_abcdefghijklmnopqrst_1234。 "},"2.基本概念/12.高可用模式.html":{"url":"2.基本概念/12.高可用模式.html","title":"高可用模式","keywords":"","body":"高可用模式(HA) Vault 支持多服务器部署模式以实现高可用性。此模式通过运行多个 Vault 服务器来防止服务中断。使用支持高可用的数据存储时,会自动启用高可用模式。 用户可以通过启动服务器并查看输出数据存储信息之后是否紧跟着输出“(HA available)”来判断数据存储是否支持高可用性模式(“HA”)。如果是的话,则 Vault 将自动使用 HA 模式。 为了获得高可用性,其中某一个 Vault 服务器节点会在数据存储中成功获取锁。获取到锁的服务器节点将成为主节点;所有其他节点成为备用节点。此时,如果备用节点收到请求,它们将根据集群的当前配置和状态对相关详细信息进行请求转发或客户端重定向,请参阅本文下方的部分。由于这种架构,HA 无法提高性能可扩展性。一般来说,Vault 性能的瓶颈在于数据存储本身,而不是 Vault 内核。例如:为了增加使用 Consul 作为存储的 Vault 集群的性能可扩展性,我们通常需要扩展 Consul 而不是 Vault。 某些存储后端可以支持高可用性模式,这使它们可以同时存储 Vault 的信息以及 HA 锁信息。但是,Vault 也支持分开存储 Vault 数据 和 HA 锁,这可以通过在具有不同后端的配置文件中指定 storage 和 ha_storage 节来完成。例如,可以将 Vault 集群设置为使用 Consul 作为 ha_storage 来管理锁,并使用 Amazon S3 作为所有其他持久数据的存储。 以下部分更详细地解释了服务器通信模式和每种类型的请求处理。至少要满足重定向模式的要求才能使 HA 集群成功运行。 服务器对服务器的通信 两种处理请求的方法都依赖于主节点向其他节点通告自身的信息。该通信并非经由网络,而是通过 Vault 的加密存储完成的;主节点写入自身信息,然后解封了的从节点可以读取到这些信息。 客户端重定向方法是服务器对服务器通信的一种衍生——仅数据存储中的加密数据来传递状态,而不使用直接通信。 如果使用请求转发方法,服务器之间需要进行直接通信。为了安全地执行此操作,主节点还通过加密的数据存储条目通告新生成的用于客户端和服务器之间进行身份验证的私钥 (ECDSA-P521) 和自签名证书。每个备用节点使用私钥和证书通过通告的集群地址,建立连接到主节点的经过相互验证身份的 TLS 1.2 连接。当备用节点收到来自客户端的请求时,请求被序列化,通过这个受 TLS 保护的通信通道发送,并由主节点执行。然后主节点向备用节点返回响应,备用节点将响应发送回请求客户端。 请求转发 如果启用了请求转发(从 0.6.2 开始默认启用),客户端仍然可以通过设置 X-Vault-No-Request-Forwarding 标头为任意非空值来强行使用旧的重定向行为。 要成功设置集群需要一些配置参数,但有些参数可以自动确定。 客户端重定向 如果请求中的 X-Vault-No-Request-Forwarding 标头设置为非空值,备用节点将使用 307 状态代码将客户端重定向到主节点地址上。 这也是关闭请求转发或执行转发出错时使用的备用方法。因此所有高可用设置始终需要设定一个重定向地址。 一些高可用数据存储的驱动可以自动检测重定向地址,但通常需要通过配置文件中的配置项来手动配置它。该值的键是 api_addr,它的值会优先使用环境变量 VAULT_API_ADDR 的值。 api_addr 值应设置成什么取决于 Vault 的配置方式。有两种常见情况:客户端直接访问的 Vault 服务器,以及通过负载均衡器访问的 Vault 服务器。 在这两种情况下,api_addr 都应该是包含 (http/https) 的完整 URL,而不仅仅是 IP 地址和端口。 直接访问 当客户端可以直接访问 Vault 服务器时,每个节点的 api_addr 应该设置成节点的地址。比如说有两个 Vault 节点: A:访问地址为https://a.vault.mycompany.com:8200 B:访问地址为https://b.vault.mycompany.com:8200 那么节点 A 需要将其 api_addr 设置为 https://a.vault.mycompany.com:8200,同时节点 B 需要将其 api_addr 设置为 https://b.vault.mycompany.com:8200。 这样的话,当 A 成为主节点时,B 节点收到的任何请求都会被从定向到 A 节点的地址https://a.vault.mycompany.com:8200,反之亦然。 使用负载均衡 有时客户端一开始通过负载均衡器访问一个 Vault 服务器,但实际上它可以直接访问均衡器后端每个 Vault 节点。在这种情况下,Vault 服务器实际上应该按照上一节所述进行设置,这样客户端可以直接访问服务器时可以被正确地重定向。 但是,如果只能通过负载均衡器访问 Vault 服务器,那么每个节点上的 api_addr 应该是相同的,那就是负载均衡器的地址。发送请求到备用节点的客户端将被重定向回负载均衡器;同时,负载均衡器的后端最好已更新为当前主节点的地址。这种机制可能会导致重定向循环,因此尽量不要这样设置。 每个节点的 listener 地址 Vault 配置文件中的每个 listener 块都包含一个 address 值,Vault 在该地址上侦听请求。同样,每个 listener 块都可以包含一个 cluster_address,Vault 在该地址上侦听服务器对服务器的集群请求。如果不设置这个值,它的 IP 地址将自动设置为与 address 值相同,其端口将自动设置为在 address 使用的端口号上加 1(因此默认为 8201 端口)。 请注意,只有主节点运行了活动的侦听器。当一个节点晋升为主节点状态时,它将启动集群侦听器,当它变为备用节点时,它将停止这些侦听器。 每个节点的 cluster_addr 与 api_addr 类似,每个节点如果处于主节点状态时都应该通过服务器对服务器通信将其 cluster_addr 值通告给其他备用节点,并且 cluster_addr 作为配置文件中的顶层配置项而存在。每个节点上的 cluster_addr 应该设置为主机名或 IP 地址,备用服务器可以使用它来访问 listener 块中设置的该节点的一个或多个 cluster_address 值之一,包括端口号。 (请注意,该地址强制使用 https,因为服务器之间仅使用 TLS 连接。) 如果设置了 VAULT_CLUSTER_ADDR 环境变量,那么该环境变量值会被优先使用。 支持的存储 目前支持高可用模式的存储后端有几种,包括 Consul、ZooKeeper 和 etcd。这些可能会随着时间的推移而改变。 HashiCorp 推荐使用 Consul 作为高可用存储,因为它由 HashiCorp 及其客户在商业支持下用于生产环境。 如果用户有兴趣实施另一个后端或向另一个后端添加高可用支持,HashiCorp 很乐意接受社区的贡献。添加高可用支持需要为存储后端实现 physical.HABackend 接口。 "},"2.基本概念/13.存储.html":{"url":"2.基本概念/13.存储.html","title":"存储","keywords":"","body":"存储 如同先前介绍 Vault 的架构章节中所述,Vault 是不信任的后端存储的,保存在后端存储中的只有加密后的信息。 支持的后端存储类型 对于企业用户,HashiCorp 提供了对 Vault 集成 Consul 作为后端存储的官方支持。 另外有许多社区支持的其他存储类型可用,可以在后续配置文件章节中获取更多信息。 备份 由于 Vault 存储配置具有高度灵活性,为 Vault 的备份提供准确的指导是一件具有挑战性的工作。 要为 Vault 建立备份,需要考虑两种数据: Vault 在后端存储中的加密数据 运行 Vault 服务所需的配置文件以及管理脚本 还有一个需要考虑的大问题——我们想通过备份来防御的故障是什么? 先讨论大问题——为什么要做备份? 在制定持续的备份和灾难恢复策略时,思考“为什么要备份”这个问题很重要。 建议在升级之前进行备份,因为 Vault 存储的数据在出问题后并非总是能够降级回从前的。通常,每当计划对集群进行重大变更前,都建议进行备份。 更具体地说,我们建议在对 /sys API(不包括 /sys/leases、/sys/namespaces、/sys/tools、/sys/wrapping、/sys/policies 和 /sys/pprof 这些端点)进行写操作之前进行备份,而不是在写时备份。对 /sys API 进行写操作的一些工作流会进行升级并且重新生成密钥。未来这份指南可能会针对集成存储后端部分的内容进行修改。 备份还可以帮助处理意外删除或修改数据的情况。如果发生这种情况,事情可能会变得有点棘手。如果我们使用的是早上 5 点的备份数据进行恢复,但当前时间是早上 10 点,我们将丢失在早上 5 点到 10 点之间写入的数据。 Lucy Davinhart 在 HashiConf 做过一个演讲,分享了一个有趣的案例。 我们不建议使用备份来防止单个机器出现的故障。 Vault 服务器可以部署为集群运行,因此为了防止服务器故障,我们建议在高可用模式下运行 Vault。仅仅使用开源版的功能,Vault 集群就可以在同一区域内的多个可用区之间进行扩展。 Vault 企业版支持复制集群和数据中心级别故障的灾难恢复。在高可用模式下使用开源版 Vault 时,备份可以帮助应对数据中心级别的故障。 归根结底,备份不能替代在高可用模式或使用 Vault 企业版进行复制。在制定从灾难恢复或防范故障的计划时,我们应该将备份和高可用都视为该计划的关键组成部分。 备份 Vault 的持久化数据 最好是在 Vault 服务离线时进行备份和还原操作。如果无法进行离线备份,那么我们推荐使用支持原子化快照功能的后端存储(例如 Consul 或是 Vault 集成存储)。 如果使用的后端存储不支持原子化快照,我们推荐只进行离线备份。 要在 HashiCorp 支持的后端存储上对 Vault 存储的加密数据进行备份或者还原操作,请阅读后续命令章节中有关 snapshot 以及 operator raft 命令的部分。 配置 除了备份 Vault 保存在存储后端中的加密数据外,我们可能还希望保存服务器配置文件、用于管理 Vault 服务的所有脚本,并确保可以重新安装任何用户安装的插件。这些文件的位置是由当前 Vault 安装时配置的。 注意:尽管保存在存储后端内的 Vault 数据的备份或快照都是加密的,但某些配置可能是敏感的(例如,用于透明地自动解封的 Vault 令牌,或是配置文件中的 TLS 私钥)。备份中如果存在此种信息意味着我们需要仔细地保护它们。 "},"2.基本概念/14.Vault集成存储.html":{"url":"2.基本概念/14.Vault集成存储.html","title":"Vault 集成存储(Integrated Storage)","keywords":"","body":"Vault 集成存储(Integrated Storage) Vault 支持多种存储选项,用于存储 Vault 的持久化信息。从 Vault 1.4 开始,提供了一个集成存储选项。该存储后端不依赖任何第三方系统,实现高可用性语义,支持企业版复制功能,并提供备份/恢复工作流。 集成存储将 Vault 的数据存储在 Vault 服务器的文件系统上,并使用共识协议将数据复制到集群中的每个服务器。 本节以下部分将详细介绍如何操作 Vault 集成存储。 服务器对服务器通信 一旦节点相互组成集群,它们就开始通过 Vault 的集群端口进行 mTLS 协议通信。集群端口默认为 8201。TLS 信息在加入时交换并定期轮替。 使用集成存储的一个必要条件是已经设置了 cluster_addr 配置。这使得 Vault 在加入集群时会为节点 ID 分配一个地址。 集群成员 本节将概述如何启动和管理一个使用集成存储的 Vault 集群。 集成存储在节点执行 vault operator init 命令的过程中被启动,先组成一个尺寸为 1 的集群。根据所需的部署尺寸,其他节点可以加入到某个已经有主节点的集群成为备用节点。 加入集群 加入集群是初始化一个 Vault 节点并使其成为某个已有集群的成员的过程。为了向集群验证新节点,它必须使用相同的封印机制。如果使用自动解封,则必须将节点配置为使用与其尝试加入的集群相同的 KMS 提供程序和密钥。如果使用 Shamir 封印,则必须在加入过程完成之前将解封密钥提供给新节点。一旦一个节点成功加入,来自主节点的数据就可以向它开始复制。一旦节点加入了一个集群,就不能重新加入不同的集群。 您可以通过配置文件自动加入节点,也可以通过 API 手动加入节点(两种方法在下文叙述)。加入节点时,必须使用 leader 节点的 API 地址。建议在所有节点上设置 api_addr 配置以使加入集群变得更简单。 retry_join 配置 该方法允许在配置文件中设置一个或多个目标领导节点。当一个尚未初始化的 Vault 服务器启动时,它将尝试加入每个定义的潜在领导者的集群,重试直到成功。当指定的领导者之一变为主节点时,该节点即可成功加入集群。节点使用 Shamir 封印时,新加入的节点仍然需要手动解封。使用自动解封时,节点可以自动加入和解封。 下面是一个使用 retry_join 配置的例子: storage \"raft\" { path = \"/var/raft/\" node_id = \"node3\" retry_join { leader_api_addr = \"https://node1.vault.local:8200\" } retry_join { leader_api_addr = \"https://node2.vault.local:8200\" } } 注意,在每个 retry_join 节中,您可以提供一个 leader_api_addr 或 auto_join 值。当提供了一个云平台对应的 auto_join 配置值时,Vault 将使用 go-discover 库尝试自动发现和解析潜在的 Raft 主节点地址。 请查阅 go-discover 的说明文档了解有关 auto-join 值的详细信息。 storage \"raft\" { path = \"/var/raft/\" node_id = \"node3\" retry_join { auto_join = \"provider=aws region=eu-west-1 tag_key=vault tag_value=... access_key_id=... secret_access_key=...\" } } 默认情况下,Vault 将尝试使用 HTTPS 和 8200 端口来访问被发现的节点。操作员可以通过设置 auto_join_scheme 和 auto_join_port 字段覆盖这两个配置。 storage \"raft\" { path = \"/var/raft/\" node_id = \"node3\" retry_join { auto_join = \"provider=aws region=eu-west-1 tag_key=vault tag_value=... access_key_id=... secret_access_key=...\" auto_join_scheme = \"http\" auto_join_port = 8201 } } 通过命令行加入集群 我们可以使用命令行工具的 join 命令或者调用 API 来加入集群。需要指定属于主节点的 API 地址: $ vault operator raft join https://node1.vault.local:8200 从集群中移除节点 当不再需要集群中的某个节点时,需要将其从集群中删除。这可能发生在将一个节点替换为另一个新的节点、节点主机名发生永久性的变更并且无法再被访问、又或者试图缩小集群尺寸等等许多原因时。从集群中删除节点要先确保集群在删除后能够保持所需的最小尺寸,并保持足以形成多数。 可以通过 remove-peer 命令从集群中删除特定节点: $ vault operator raft remove-peer node1 Peer removed successfully! 枚举节点 可以通过 list-peers 命令查询集群中目前存在的节点。所有投票节点都会被列出,为保持集成存储集群的正常运行,集群中的过半多数必须保持在线。 $ vault operator raft list-peers Node Address State Voter ---- ------- ----- ----- node1 node1.vault.local:8201 follower true node2 node2.vault.local:8201 follower true node3 node3.vault.local:8201 leader true 集成存储和 TLS 我们在上面关于引导启动集群的部分中略过了一些细节。对于大多数情况来说上面的说明已经足够了,但是一些用户在使用自动加入集群和 TLS 功能与其他功能例如自动缩放等结合使用时会遇到一些问题。问题是 go-discover 插件在大多数平台上返回的是 IP(而不是主机名),并且这些 IP 是无法提前预知的,导致用于保护 Vault API 端口的 TLS 证书在其 IP SANs 中无法包含这些 IP。 回顾 Vault 的组网 在我们继续探索这些问题的解决方案之前,我们要先回顾一下 Vault 节点之间如何相互交流。 Vault 暴露两个 TCP 端口: API 端口(api_addr)和集群端口(cluster_addr)。 API 端口是接受客户端发给 Vault 服务的 HTTP 请求的端口。 对于单节点 Vault 集群,不需要考虑集群端口,因为它不会被使用。 当部署多个节点时,我们还需要一个集群端口。 Vault 节点使用它来向彼此发出 RPC 调用,例如将客户端发送的请求从备用节点转发到主节点,或者在使用 Raft 协议时,处理领导者选举和存储数据的复制。 集群端口使用 Vault 主节点在其内部生成的 TLS 证书进行保护。在不使用集成存储时(也就是集群节点之间使用共享存储时)它的工作原理很简单:每个节点至少有对存储的读访问权限,因此一旦主节点保存了证书,备用节点就可以获取到该证书,并且就如何加密集群流量达成一致。 启用集成存储时过程就比较复杂了,因为存在先有鸡还是先有蛋的问题。为了交换证书来组建 Raft 集群,我们需要启用存储数据的复制;但是在组成 Raft 集群之前,节点间又无法安全地复制数据!为了解决这个问题,一个 Vault 节点必须使用 API 端口而不是集群端口与另一个 Vault 节点通话。这是目前开源版 Vault 执行这种操作的唯一情况(Vault 企业版在配置复制时也会执行类似操作。) node2 想要加入集群,于是调用了已经加入集群的成员 node1 的 challenge API node1 收到 challenge 请求后返回 (1) 一个加密的随机 UUID (2) 封印配置 node2 必须使用收到的封印配置来解密收到的 UUID;如果节点使用自动解封那么可以直接解密。如果使用的是 Shamir,那么必须等待用户提供足够多的解封密钥来完成解密。 node2 将解密后的 UUID 通过 answer API 回复给 node1 node1 验证后认定 node2 可以信任(因为它证明它可以处理封印),然后返回一个引导启动包,包含了集群 TLS 证书以及私钥 node2 通过集群端口读取一份 Raft 快照 经过这个步骤后,新加入的节点将用不再发送任何请求到 API 端口。所有后续的节点间通信流量都将被发送到集群端口上。 辅助加入 Raft 的技术 最简单的办法就是使用 raft join 命令,显式给定要加入的集群的节点名字或是 IP。在本节中我们将了解其他更适合自动化的兼容 TLS 的选项。 使用 TLS servername 的自动加入 从 Vault 1.6.2 开始,加入集群最简单的方式就是在 retry_join 节中指定 leader_tls_servername,内容要匹配证书中的 DNS SAN。 请注意,证书的 DNS SAN 中的名称实际上不需要实际在 DNS 服务中注册。节点可能在 DNS 中查不到对应名称,但仍在使用在 DNS SAN 中包含此共享的 servername 值的证书。 为自动加入添加 CIDR 限制,在证书中列举所有可能的 IP 如果所有 Vault 节点 IP 都是从一个小子网分配的,例如 /28,将存在于该子网中的所有 IP 放入节点将要共享的 TLS 证书的 IP SAN 中就变得切实可行起来了。 该方案的缺点是集群尺寸可能有一天会超过现在的 CIDR 限制,更改 CIDR 可能会很困难。出于类似的原因,当要使用非投票节点和动态集群伸缩功能时,该解决方案可能不切实际。 使用负载均衡器取代自动加入 大多数 Vault 实例会通过负载均衡器与客户端连接。如果是这样,那么负载均衡器知道如何将流量转发到处于工作状态的 Vault 节点上,这样就不需要使用自动加入了。我们可以把 retry_join 设置成负载均衡器的地址。 一个潜在的问题:一些用户想要一个面向公网的负载均衡器,使得客户端可以连接到 Vault,但同时对从 Vault 节点流出 Vpc 到负载均衡器的公网地址的流量感到担忧。 灾难恢复 集群多数仍在的情况 本节概述了当单个服务器或多个服务器处于故障状态,但集群多数节点仍然维持工作时要采取的步骤。这时剩余的活动服务器仍然可以运行,可以选举领导者,并且能够处理写入请求。 如果发生故障的服务器是可恢复的,最好的选择是将其重新联机并使其重新连接到具有相同主机地址的集群。这将使集群恢复到完全健康的状态。 如果这无法实现,那么就需要删除出现故障的服务器。通常,我们可以使用 remove-peer 命令来删除那些仍然在集群中的故障服务器。 如果无法使用 remove-peer 命令,或者我们更希望手动调整集群成员名单,则可以向配置的数据目录写入一个 raft/peers.json 文件。 集群多数离线的情况 如果多台服务器离线,导致集群多数节点丢失,服务完全中断,部分的恢复仍然是可能的。 如果发生故障的服务器是可恢复的,最好的选择是让它们重新联机并使用相同的主机地址重新连接到集群。这将使集群恢复到完全健康的状态。 如果发生故障的服务器无法恢复,则可以使用集群中其余服务器上的数据进行部分恢复。在这种情况下可能会丢失数据,因为丢失了多数的服务器,因此有关已提交内容的记录可能不完整。恢复过程会隐式提交所有未完成的 Raft 日志条目,因此也可以提交一些在故障前尚未提交的数据。 可以阅读下面通过 peers.json 文件手工恢复集群的内容来获取详细的恢复步骤。我们只在 peers.json 文件中添加还在运行的节点。集群中剩余节点在配置了一致的 peers.json 文件并进行重启后应该就可以重新选举出新的领导者节点。 后续添加的新节点都是全新的没有任何数据的,需要用 Vault 的加入集群命令来添加到集群中。 极端情况下,可以通过在某一台服务器中配置一个只有它自己的 peers.json 恢复文件来将其回程一个单台节点构成的集群。 手动使用 peers.json 文件恢复 使用 raft/peers.json 文件进行恢复可能会导致未提交的 Raft 日志条目被意外提交,因此只有在没有其他选项可用于恢复丢失服务器所引发的服务中断时才可以使用这个方法。请确保没有任何定期新增该文件的自动化流程在运行。 第一步,停止所有剩余的服务器。 下一步是转到每个 Vault 服务器配置的数据路径。在该目录中,会有一个 raft/ 子目录。我们需要创建一个 raft/peers.json 文件。该文件的格式为 JSON 数组,数组中包含我们希望加入到集群的每个 Vault 服务器的节点 ID、地址:端口和选举权信息: [ { \"id\": \"node1\", \"address\": \"node1.vault.local:8201\", \"non_voter\": false }, { \"id\": \"node2\", \"address\": \"node2.vault.local:8201\", \"non_voter\": false }, { \"id\": \"node3\", \"address\": \"node3.vault.local:8201\", \"non_voter\": false } ] id``(string: )——指定服务器的节点 ID。我们可以在配置文件中找到它,或者如果它是自动生成的,则可以在服务器数据目录中的 node-id 文件中找到。 address``(string: )——指定服务器的主机和端口。该端口是服务器的集群端口。 non_voter``(bool: )——该选项表示该服务器是否是无投票权。如果忽略,它将默认为 false,大多数集群都使用 false,这是企业版独有的功能。 为所有剩余的服务器编写相应的条目。我们必须要确定此文件未包含的服务器确实发生了无法恢复的故障,并且以后不会重新加入集群。另外要确保此文件在所有其余服务器节点上都是一样的。 这时,我们可以重新启动所有剩余的服务器。集群应该能够恢复到工作状态。其中一个节点应该被选举为领导节点并成为主节点。 其他恢复方法 对于不涉及 Raft 的恢复方法我们将在其他章节中加以介绍。 "},"2.基本概念/15.Vault集成存储的自动驾驶功能.html":{"url":"2.基本概念/15.Vault集成存储的自动驾驶功能.html","title":"Vault 集成存储的自动驾驶功能","keywords":"","body":"Vault 集成存储的自动驾驶(Autopilot)功能 自动驾驶功能可以自动化管理 Raft 集群。当前包含了 3 个主要功能:稳定服务器(Server Stabilization)、清理死服务器(Dead Server Cleanup)和状态 API(State API)。这三个功能是在 Vault 1.7 中引入的。 稳定服务器 稳定服务器功能通过安全地将新投票节点加入集群来保持 Raft 集群的稳定性。当一个新的投票节点加入现有集群时,自动驾驶会先将其添加为非投票节点,并等待预先配置的时间来监控它的健康状况。如果节点在期间保持健康,则该节点将被提升为投票节点。这个服务器稳定期限可以使用 server_stabilization_time 进行调整(见下文)。 清理死服务器 死服务器清理会自动从 Raft 集群中删除被认为不健康的节点,避免需要操作员人工干预。可以使用 cleanup_dead_servers、dead_server_last_contact_threshold 和 min_quorum(见下文)调整此功能的行为。 状态 API State API 可以通过一次调用返回提供有关 Raft 集群中所有节点的详细信息。该 API 可用于监控集群健康状况。 跟随者(Follower)健康状态 跟随者节点的健康状态由两个因素构成: 它能否定期向领导节点发送心跳。使用 last_contact_threshold 参数调整(见下文)。 它能否及时处理来自领导节点的复制数据。使用 max_trailing_logs 参数调整(见下文)。 默认配置 默认情况下,使用 Vault 1.7 及以上版本的集群将启用自动驾驶功能,但是默认情况下不启用清理死服务器。使用旧版本 Vault 部署的 Raft 集群在升级后也将自动过渡到使用自动驾驶。 自动驾驶功能暴露了一个配置 API 来管理它的行为。自动驾驶初始化时使用以下默认值: cleanup_dead_servers - false dead_server_last_contact_threshold - 24h min_quorum - 此选项没有默认值,并且当 cleanup_dead_servers 设置为 true 时,至少要将其设置为 3。 max_trailing_logs - 1000 last_contact_threshold - 10s server_stabilization_time - 10s 复制 性能辅助集群(Performance secondary clusters)拥有自己的自动驾驶配置,独立于主集群进行管理。 灾难恢复辅助集群(DR secondary clusters)也拥有自己的自动驾驶配置(从 Vault 1.8.0 开始),独立于主集群进行管理。 自动驾驶 API 使用 DR 操作令牌进行授权。 "},"2.基本概念/16.使用PGP_GnuPG和Keybase.html":{"url":"2.基本概念/16.使用PGP_GnuPG和Keybase.html","title":"使用PGP、GnuPG和Keybase","keywords":"","body":"使用 PGP、GnuPG和Keybase Vault 能够兼容 OpenPGP 的程序,例如 GnuPG 以及 Keybase.io 这样的服务进行集成,在执行某些操作时提供额外的安全保护。本节将详细介绍与各种 PGP 集成、操作和使用。 只能通过命令行工具获取 Keybase.io 的支持,而无法通过 Vault HTTP API 使用该服务。在初始化 Vault 服务器时可以通过 Keybase.io 的 API 获取所需的 PGP 公钥来使得初始化过程更安全。 一旦 Vault 被初始化,就可以使用 Keybase 来解密 Shamir 密钥分片并正常执行解封。 使用 PGP 初始化 初始化 Vault 服务早先遇到的一个基本问题是第一个用户(执行初始化的操作员)得到了所有解封密钥的纯文本副本。这违背了 Vault 安全模型的承诺,也使得安全地分发这些密钥变得更加困难。从 Vault 0.3 开始,Vault 可以选择使用 PGP 密钥进行初始化。在这种模式下,Vault 将生成解封密钥,然后立即使用给定用户的 PGP 公钥对其进行加密。只有相应私钥的所有者才能将其解密,从而得到明文解封密钥。 首先,我们必须把适当的密钥创建、获取或导入到正在初始化 Vault 服务的计算机上。本文不会尝试涵盖 PGP 密钥的所有方面,而是提供使用两个流行程序的示例:Keybase 和 GnuPG。 对于初学者,我们建议使用 Keybase.io(“Keybase”),因为它既简单又具有许多与密钥管理相关的有用功能和特性,例如使用许多公共在线服务来验证用户身份。它还让用户能够在他们的服务器上安全地生成、存储和管理 PGP 密钥。我们首先讨论将 Vault 与 Keybase 结合使用,因为这比较简单。 使用 Keybase 初始化 要为 Keybase 用户生成解封密钥,可以在 -pgp-keys 参数的值前面加上 keybase: 前缀: $ vault operator init -key-shares=3 -key-threshold=2 \\ -pgp-keys=\"keybase:jefferai,keybase:vishalnayak,keybase:sethvargo\" 这将需要比使用传统 PGP 程序(例如使用 gpg)少得多的步骤,因为 Keybase 服务处理了一些繁琐的步骤。输出内容大致如下: Key 1: wcBMA37rwGt6FS1VAQgAk1q8XQh6yc... Key 2: wcBMA0wwnMXgRzYYAQgAavqbTCxZGD... Key 3: wcFMA2DjqDb4YhTAARAAeTFyYxPmUd... ... 与通常的解封密钥相比,这里的输出应该更加长。因为这些密钥被加密了,只有持有相应私钥的用户才能解密该值。密钥按照 -pgp-keys 属性中指定的顺序加密。因此,密钥属于 jeffrai、vishalnayak 和 sethvargo 各自的 Keybase 帐户。这些密钥可以使用任意媒介进行分发,但建议也需要使用常识进行判断。加密的密钥经过了 base64 编码后被返回。 使用 Keybase 解封 作为解封密钥的拥有者,得到解封密钥明文的最简单方法是使用 Keybase 命令行工具。我们可以从 Keybase.io 下载页面下载。下载并配置 Keybase 命令行工具后,下面的任务是输入解封密钥。要获得解封密钥的明文,必须解密 Vault 初始化程序返回的值。请运行以下命令获取纯文本明文: $ echo \"wcBMA37...\" | base64 --decode | keybase pgp decrypt 请把 wcBMA37... 替换成实际得到的密文。 命令行此时会提示用户输入 Keybase 密码,输入后得到的输出就是明文形式的解封密钥文本: 6ecb46277133e04b29bd0b1b05e60722dab7cdc684a0d3ee2de50ce4c38a357101 这就是只属于用户个人知晓的解封密钥的明文,应该与日常密码进行同等级的保护。现在我们可以使用它来解封 Vault 服务了: $ vault operator unseal Key (will be hidden): ... 使用 GnuPG 初始化 GnuPG 是 OpenPGP 标准的一个开源实现,可以运行在几乎所有平台上。可以通过 GnuPG 手册了解更多细节。 要创建一个新的 PGP 密钥,运行一下命令: $ gpg --gen-key 要导入一个现存的密钥,下载它的公钥后运行以下命令: $ gpg --import key.asc 导入用户的公钥之后,我们需要将公钥的值以 base64 编码或是二进制形式保存到一个密钥文件: $ gpg --export 348FFC4C | base64 > seth.asc 这些密钥文件必须以 base64(“标准”的 base64 字符集,不能启用 ASCII 加固)或二进制的格式保存在磁盘上。一旦成功存盘,可以用 -pgp-keys 参数指定这些密钥文件: $ vault operator init -key-shares=3 -key-threshold=2 \\ -pgp-keys=\"jeff.asc,vishal.asc,seth.asc\" 命令的输出类似这样过: Key 1: wcBMA37rwGt6FS1VAQgAk1q8XQh6yc... Key 2: wcBMA0wwnMXgRzYYAQgAavqbTCxZGD... Key 3: wcFMA2DjqDb4YhTAARAAeTFyYxPmUd... ... 与通常的解封密钥相比,这里的输出应该更加长。因为这些密钥被加密了,只有持有相应私钥的用户才能解密该值。密钥按照 -pgp-keys 属性中指定的顺序加密。因此,第一把钥匙属于 jeff,第二把属于 vishal,第三把属于 seth。这些密钥可以使用任意媒介进行分发,但建议也需要使用常识进行判断。加密的密钥经过了 base64 编码后被返回。 使用 GnuPG 解封 假设用户得到了一条使用他的 PGP 公钥加密的解封密钥,现在用户要用它来解封 Vault 服务。为了获得解封密钥的明文,用户必须解密 Vault 初始化程序返回的值。运行以下命令以获取解封密钥的明文: $ echo \"wcBMA37...\" | base64 --decode | gpg -dq 请把 wcBMA37... 替换成实际得到的密文。 如果用户使用密码保护了自己的 PGP 私钥,那么这时用户会被要求输入该密码。输入正确密码后,就可以得到明文的解封密钥: 6ecb46277133e04b29bd0b1b05e60722dab7cdc684a0d3ee2de50ce4c38a357101 这就是只属于用户个人知晓的解封密钥的明文,应该与日常密码进行同等级的保护。现在我们可以使用它来解封 Vault 服务了: $ vault operator unseal Key (will be hidden): ... "},"2.基本概念/17.恢复模式.html":{"url":"2.基本概念/17.恢复模式.html","title":"恢复模式","keywords":"","body":"恢复模式 可以使用 -recovery 参数启动 Vault 以进入恢复模式。恢复模式的主要目的是允许直接访问存储,以防 Vault 由于一些新发现的错误而无法启动。一般在没有 Vault 专家在场提供建议的情况下这不太会有什么用。 恢复模式与常规 Vault 操作有如下差异: 所有常规子系统都不会运行,例如:过期、集群、响应他节点的 RPC 调用 与常规解封请求不同,恢复模式下使用一个生成的恢复令牌进行解封 所有请求都被发往 sys/raw 并且使用恢复令牌进行身份验证 恢复流程 一般来说恢复模式按以下流程运行: 封印或停止集群中所有节点 如果使用了集成存储,在每个节点上运行 vault status 来查找序号最高的那一个节点(这需要这些节点处于运行且封印状态。如果处于解封状态那么一个新的领导者节点可能会被选出并进行写操作,这会混淆问题) 以恢复模式重启目标节点 在那个节点上生成一个恢复令牌 使用恢复令牌通过向 sys/raw 发送操作来修复节点 如果使用了集成存储,按照下面的步骤重新组建 Raft 集群 旨在高可用目的的集成存储(ha_storage) 如果以混合模式(即 ha_storage)使用集成存储,那么恢复模式将不允许更改 Raft 数据,只能修改与 Vault 存储后端关联的底层物理数据。这意味着本文档中有关集成存储的说明不适用。 集成存储 使用集成存储时,并非所有节点都是一致的。一些节点的状态可能更落后 - 它尚有还未同步到的 Raft 日志。在选择用于恢复的节点时,必须要确定它具有在集群中找到的最高 AppliedIndex。 每个节点的 AppliedIndex 值可以通过在关闭集群后对集群内已封印节点运行 vault status 来获取。 恢复令牌 恢复令牌的签发方式与生成根令牌的方式大致相同,只是使用不同的端点,并且必须首先封印 Vault 节点。与根令牌不同,恢复令牌不会持久化,因此如果 Vault 重新启动进入恢复模式,则必须生成一个新的恢复令牌。 只能生成一个恢复令牌。如果丢失,请重新启动 Vault 并生成一个新的。 Raw 请求 发往 sys/raw 端点的请求与常规模式下发到 Vault 服务的请求一样,唯一的区别是在恢复模式下,X-Vault-Token 包含的必须是恢复令牌而不是服务令牌或者批量令牌。 重组 Raft 集群 恢复模式下 Vault 会自动将集群大小调整为 1。这是必须的,因为 Raft 协议不允许在没有仲裁的情况下进行更改,并且在恢复模式下我们希望使用单个节点进行更改。 这意味着在使用了恢复模式后,要返回正常服务必须包括先重组 Raft 集群。有两种方法可以做到这一点:删除其他节点上的 Vault 数据目录并将它们重新加入恢复完的节点,或者使用 peers.json 文件手动恢复的方法让所有节点就哪些节点是集群的组成部分达成一致。 "},"2.基本概念/18.资源配额.html":{"url":"2.基本概念/18.资源配额.html","title":"资源配额","keywords":"","body":"资源配额(Quota) 与 Vault 的每一次交互,无论是将机密放入键/值存储中还是为 MySQL 数据库生成新的数据库用户名密码,都需要调用 Vault 的 API。 这种通过 API 驱动的模型的一个副作用是,应用程序和用户可能会发送一系列高频的 API 请求使系统资源不堪重负,从而导致某些 Vault 节点甚至整个 Vault 集群出现拒绝服务问题。当 Vault API 端点暴露于部署在全球基础设施中的数千或数百万个服务时,这种风险会显着增加,尤其是为内部开发人员的服务而部署的 Vault 服务。 Vault 提供了资源配额功能,允许 Vault 操作员指定对 Vault 中使用的资源的限制。具体来说,Vault 允许维护者创建和配置 API 速率限制。 速率限制配额(Rate Limit Quotas) Vault 允许操作员创建速率限制配额,使用令牌桶算法强制执行 API 速率限制。创建配额时可以指定路径,可以在根级别、命名空间级别或挂载点上定义速率限制配额。速率限制器基于每个 Vault 节点应用于每个唯一的客户端 IP 地址(速率限制配额的消耗信息不会再集群内复制)。客户端可以在任意 1 秒内发起 rate 次请求,每秒都是如此。 在根级别(也就是 path 为空)定义的速率限制配额会被所有命名空间和挂载点继承。它将充当整个 Vault API 的单一速率限制器。在命名空间级别定义的限速配额优先于全局限速配额,为挂载点定义的限速配额优先于全局和命名空间级别的限速配额。换句话说,更具体的配额规则更优先。 可以使用可选的 block_interval 创建速率限制,如果设置为非零值时,任何达到速率限制阈值的客户端都将在 block_interval 秒的持续时间内被屏蔽所有后续请求。 Vault 还允许通过公开的各种指标和启用可选的审计日志来检查 Vault 节点中的速率限制状态。 豁免路径(Exempt Routes) 默认情况下,以下路径不会被速率限制策略所限制。然而,Vault 操作员可以通过修改配置文件中的rate_limit_exempt_paths来修改这组路径的内容: /v1/sys/generate-recovery-token/attempt /v1/sys/generate-recovery-token/update /v1/sys/generate-root/attempt /v1/sys/generate-root/update /v1/sys/health /v1/sys/seal-status /v1/sys/unseal "},"3.命令行/命令行.html":{"url":"3.命令行/命令行.html","title":"命令行","keywords":"","body":"Vault 命令行 Vault 提供了一个命令行工具,该工具包含常用功能和格式输出。 Vault 命令行是一个单一的静态二进制文件。它是一个对 HTTP API 的简单封装。每个命令行的命令在其内部都能直接映射到 HTTP API。 每个命令都表示为一个命令或子命令。本文档对应于最新版本的 Vault(本文写作时为 1.9.2)。如果读者运行的是旧版本,命令的行为可能会有所不同。运行 vault -h 或 vault -h 以查看与当前使用的版本相对应的帮助输出。 运行以下命令获得帮助信息: $ vault -h 运行以下命令获得子命令的帮助信息: $ vault -h 命令结构 Vault 有许多命令和子命令选项可用:HTTP 选项、输出选项和各命令特有的选项。 Vault 命令行的命令中,命令在前,路径和参数(如果有)在后: $ vault [options] [path] [args] options —— 设置额外配置的标识 args —— 传递给 API 的参数 提示:可以运行 vault path-help 命令来查询可用参数。 例子: 这个例子里使用 write 命令在 userpass 身份验证方法中创建一个新用户 (bob)。它通过 -address 标志指定使用的 Vault 服务器地址,然后 (auth/userpass/users/bob)指定了写入的路径, 最后是其参数 (password=\"long-password\")。 $ vault write -address=\"http://127.0.0.1:8200\" auth/userpass/users/bob password=\"long-password\" 如果指定了多个选项(-address和-namespace)以及多个参数(password和policies)被设置了,那么命令看起来就是这样的: $ vault write -address=\"http://127.0.0.1:8200\" -namespace=\"my-organization\" \\ auth/userpass/users/bob password=\"long-password\" policies=\"admin\" 选项(标志)位于命令(或子命令)之后,路径之前;路径后是用来设置发往 API 请求的参数。 返回码 Vault 命令行的设计目标是要保持一致性以及遵守社区的约定与习惯,除非文档中有另外的说明。 发生在本机的错误(例如标志不正确、验证失败或参数数量错误)返回退出代码 1。 所有远程错误(例如 API 故障、TLS 错误 或 API 参数不正确)都会返回退出状态 2 某些命令会在合理的情况下不遵守上面的约定。相关文档中会记录这种特殊情况。 自动补全(Autocompletion) Vault 命令可以对标志、子命令和参数(如果支持的话)的选项进行自动补全。 启用自动补全: $ vault -autocomplete-install 启用后需要重启 shell 会话才能生效。 开始键入 Vault 命令时,按 键以显示可用自动补全的列表。输入 - 以显示可用自动补全的标识。 如果设置了 VAULT_* 环境变量,自动补全将自动查询 Vault 服务器并返回有用的参数建议。 读写数据 Vault 中最常见的四种操作是 read、write、delete 和 list。这些操作适用于 Vault 中的大多数路径。一些路径将包含机密,其他路径可能包含配置。不管包含的是什么,向 Vault 读写数据的主要接口是相似的。 为了演示基本的读写操作,将使用内置的 Key Value 机密引擎。这个引擎是自动挂载的,没有外部依赖。请注意,kv 使用不同的命令进行读写:分别为 kv get 和 kv put。 早先的 Key Value 引擎使用 read 和 write 命令。更先进的 Key Value Version 2 引擎引入了新的 kv get 和 kv put 命令。 写入数据 使用 vault kv put 命令向 Vault 写入数据: $ vault kv put secret/password value=itsasecret 某些机密引擎可以使用任意键/值对,另一些则有严格限制。 Vault 内置的帮助将在适当的情况下指导用户了解这些限制。 stdin Vault 中的某些命令可以使用 - 作为值从标准输入读取数据。如果参数是个 - ,Vault 希望从 stdin 读取 JSON 对象: $ echo -n '{\"value\":\"itsasecret\"}' | vault kv put secret/password - 除了读取完整的 JSON 对象,Vault 也可以从 stdin 读取一个值: $ echo -n \"itsasecret\" | vault kv put secret/password value=- 文件 有些命令可以从磁盘上的文件中读取数据。用法与刚才读取 stdin 的差不多。如果参数值以@开头,Vault 会将它作为文件来读取: $ vault kv put secret/password @data.json 或者可以将文件内容作为参数值: $ vault kv put secret/password value=@data.txt 请注意,如果参数以 @key=value 格式提供,Vault 会将其视为一个键为 @key 的键值对,而不是名为 key=value 的文件。这也意味着 Vault 不支持包含 = 的文件名。 读取数据 数据被保存后,可以通过 vault kv get 命令读取: $ vault kv get secret/password Key Value --- ----- refresh_interval 768h0m0s value itsasecret 令牌助手(Token Helper) 默认情况下,Vault 命令行使用“令牌助手”在通过身份验证后缓存令牌。这在概念上类似于网站如何将您的会话信息通过 Cookie 安全地存储在浏览器中。令牌助手是可定制的,甚至可以构建自己的令牌助手。 默认情况下令牌助手将令牌存储在 ~/.vault-token 中。随时可以通过删除该文件来“注销”Vault。 环境变量 命令行会读取以下环境变量作为默认配置使用。这可以减轻重复输入标志的工作量。命令中设置的标志总是优先于环境变量。 VAULT_TOKEN Vault 身份验证令牌。在概念上类似于网站上的会话令牌,VAULT_TOKEN 环境变量保存令牌的内容。 VAULT_ADDR 用 URL 和端口号组成的 Vault 服务器地址,例如:https://127.0.0.1:8200/。 VAULT_CACERT 一个本机磁盘上的 PEM 编码的 CA 证书文件路径。该文件被用以验证 Vault 服务器的 SSL 证书。本环境变量优先级高于 VAULT_CAPATH。 VAULT_CAPATH 一个本机磁盘上包含有一组 PEM 编码的 CA 证书文件的文件夹路径。这些证书被用以验证 Vault 服务器的 SSL 证书 VAULT_CLIENT_CERT 一个本机磁盘上的 PEM 编码的客户端证书文件路径。该文件被用以与 Vault 服务器进行 TLS 通信。 VAULT_CLIENT_KEY 一个本机磁盘上的未加密、PEM 编码的私钥文件路径,对应上面的客户端证书。 VAULT_CLIENT_TIMEOUT 控制超时时间的变量。默认值为 60 秒。 VAULT_CLUSTER_ADDR 在高可用性模式下,其他集群成员用于连接到本节点的地址。 VAULT_FORMAT 设置 Vault 命令(read/status/write)的输出格式。合法格式有:table、json 或 yaml。 VAULT_MAX_RETRIES 当遇到 5xx 错误码时最大重试次数。默认值为 2,也就是会尝试 3 次。将该值设置为 0 即可关闭重试。 VAULT_REDIRECT_ADDR 在高可用性模式下将客户端重定向到此节点时使用的地址。 VAULT_SKIP_VERIFY 在与 Vault 服务器进行通信之前不对服务器端提供的证书进行验证。该配置将违反 Vault 的安全模型,不建议使用。 VAULT_TLS_SERVER_NAME 通过 TLS 连接时用作 SNI 主机的名称。 VAULT_CLI_NO_COLOR 如果设置该变量,Vault 的输出将不包含 ANSI 颜色转义序列字符。 VAULT_RATE_LIMIT 该环境变量将限制 vault 命令向 Vault 服务发送请求的速率。 该环境变量的格式为 rate[:burst](其中 [] 中的项目是可选的)。如果没有设定,则突发值默认为 rate。速率和突发(burst)都按“每秒操作数”计算。如果未设定本环境变量,则速率和突发将不受限制,即默认情况下速率限制处于关闭状态。 注意:每次调用 vault 命令行的速率都受到限制。由于每次调用 vault CLI 通常只会发出几个请求,因此在使用 Go Vault 客户端 API 时,该环境变量最有用。 VAULT_NAMESPACE 命令所使用的命名空间。设置此项不是必需的,但允许使用相对路径。 VAULT_HTTP_PROXY 用于访问 Vault 的 HTTP 代理地址。当设定该变量时,会覆盖在环境中找到的任何其他代理。格式应为 http://server:port。 命令行标志 不同的子命令有不同的标志可用。某些标志(例如用于设置 HTTP 和输出选项的标志)是全局可用的,而其他标志则只能用于特定的子命令。有关可用标志的完整列表,请运行: $ vault -h "},"3.命令行/1.agent.html":{"url":"3.命令行/1.agent.html","title":"agent","keywords":"","body":"agent 有关 agent 命令的信息,请查阅后续的 Vault Agent 章节。 "},"3.命令行/2.audit.html":{"url":"3.命令行/2.audit.html","title":"audit","keywords":"","body":"audit audit 命令有一组用于和 Vault 审计设备进行交互的子命令。用户可以列出、启用和禁用审计设备。 例子 启用审计设备: $ vault audit enable file file_path=/tmp/my-file.txt Success! Enabled the file audit device at: file/ 列出审计设备: $ vault audit list Path Type Description ---- ---- ----------- file/ file n/a 禁用一个审计设备: $ vault audit disable file/ Success! Disabled audit device (if it was enabled) at: file/ 可用子命令 Usage: vault audit [options] [args] # ... Subcommands: disable Disables an audit device enable Enables an audit device list Lists enabled audit devices 每种子命令的详细使用方法,请阅读对应的章节。 "},"3.命令行/2.1.audit_disable.html":{"url":"3.命令行/2.1.audit_disable.html","title":"audit disable","keywords":"","body":"audit disable audit disable 命令可以禁用给定路径上的审计设备(如果存在的话)。该命令是幂等的,这意味着即使指定路径上不存在启用的审计设备,它也会成功。 一旦审计设备被禁用,之后的审计日志就不会被发送给它。已经与审计设备关联的数据不受影响。例如,如果禁用了记录到文件的审计设备,该文件以及存储的内容将依然存在。 例子 禁用 file/ 路径上的审计设备: $ vault audit disable file/ Success! Disabled audit device (if it was enabled) at: file/ 可用标志 该命令只有标准的全局标志位可用。 "},"3.命令行/2.2.audit_enable.html":{"url":"3.命令行/2.2.audit_enable.html","title":"audit enable","keywords":"","body":"audit enable audit enable 命令在指定的路径上启用审计设备。如果指定的路径中已存在审计设备,则返回错误。用于配置审计设备的其他选项以 KEY=VALUE 的形式提供。每种审计设备都声明了自己的一组配置选项。 一旦启用审核设备,几乎每个请求和响应都会记录到设备中。 例子 在 file/ 路径上启用 file 审计设备: $ vault audit enable file file_path=/tmp/my-file.txt Success! Enabled the file audit device at: file/ 每种审计设备完整的可配置参数请阅读“审计设备”章中的相关部分。 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: -description (string:\"\") - 关于此审计设备的易于阅读的描述 -local (bool: false) - 将此审计设备标记为本地设备。本地设备的记录不会被复制或是被复制机制删除。 -path (string: \"\") - 可以访问审计设备的路径。所有审计设备的路径都必须是唯一的。默认值为审计设备的类型名称。 "},"3.命令行/2.3.audit_list.html":{"url":"3.命令行/2.3.audit_list.html","title":"audit list","keywords":"","body":"audit list audit list 命令列出了已启用的审计设备列表。输出已启用的审计设备以及这些设备的配置参数。 例子 列出所有审计设备: $ vault audit list Path Type Description ---- ---- ----------- file/ file n/a 列出审计设备的明细信息: $ vault audit list -detailed Path Type Description Replication Options ---- ---- ----------- ----------- ------- file/ file n/a replicated file_path=/var/log/audit.log 可用标志 除全局可用的标准标志以外,该命令可以使用以下标志: 输出选项 -format (string: \"table\") —— 以给定格式打印输出。合法的格式有:table、json 或者 yaml。该标志也可以通过环境变量 VAULT_FORMAT 来设定 命令选项 -detailed (bool:false) —— 是否打印每种审计设备的诸如配置参数以及复制状态等详细信息 "},"3.命令行/3.auth.html":{"url":"3.命令行/3.auth.html","title":"auth","keywords":"","body":"auth auth 命令有一组子命令于和 Vault 的身份验证方法进行交互。用户可以列出、启用、禁用身份验证方法,或是获取相关身份验证方法的帮助信息。 更多有关信息,请参阅对应子命令文档或身份验证概念页面。 要以用户或计算机身份向 Vault 进行身份验证,请改用 vault login 命令。此命令用于与身份验证方法本身进行交互,而不是向 Vault 进行身份验证。 例子 启用一个身份验证方法: $ vault auth enable userpass Success! Enabled userpass auth method at: userpass/ 列出所有的身份验证方法: $ vault auth list Path Type Description ---- ---- ----------- token/ token token based credentials userpass/ userpass n/a 获取如何使用特定身份验证方法进行登录的帮助信息: $ vault auth help userpass/ Usage: vault login -method=userpass [CONFIG K=V...] # ... 禁用一个身份验证方法: $ vault auth disable userpass/ Success! Disabled the auth method (if it existed) at: userpass/ 调整一个身份验证方法: $ vault auth tune -max-lease-ttl=30m userpass/ Success! Tuned the auth method at: userpass/ 可用子命令 Usage: vault auth [options] [args] # ... Subcommands: disable Disables an auth method enable Enables a new auth method help Prints usage for an auth method list Lists enabled auth methods tune Tunes an auth method configuration "},"3.命令行/3.1.auth_disable.html":{"url":"3.命令行/3.1.auth_disable.html","title":"auth disable","keywords":"","body":"auth disable auth disable 命令可以禁用指定路径上的身份验证方法(如果存在的话)。该命令是幂等的,这意味着即使指定路径上不存在启用的身份验证方法,它也会成功。 一旦一个身份验证方法被禁用了,它将无法再被用于身份验证。所有由这个被禁用的身份验证方法生成的令牌都将被立即吊销。该命令将阻塞至所有令牌都被吊销后返回。 例子 禁用 userpass/ 路径上启用的身份验证方法: $ vault auth disable userpass/ Success! Disabled the auth method (if it existed) at: userpass/ 可用标志 该命令只有标准的全局标志位可用。 "},"3.命令行/3.2.auth_enable.html":{"url":"3.命令行/3.2.auth_enable.html","title":"auth enable","keywords":"","body":"auth enable auth enable 命令在指定路径上启用身份验证方法。如果指定路径中已存在身份验证方法,则返回错误。启用身份验证方法后,通常需要进行配置。每个身份验证方法的配置方法各有不同。 身份验证方法负责对用户或机器进行身份验证,并为他们分配策略和可以访问 Vault 的令牌。身份验证通常映射到策略。 例子 在路径 userpass/ 上启用\"userpass\"身份验证方法: $ vault auth enable userpass Success! Enabled the userpass auth method at: userpass/ 创建一个用户: $ vault write auth/userpass/users/sethvargo password=secret Success! Data written to: auth/userpass/users/sethvargo 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: -audit-non-hmac-request-keys (string: \"\") - 指定在请求的数据对象中的哪些键不会被审计设备 HMAC 哈希。请注意,可以通过多次使用该选项来指定多个键,每次指定一个键 -audit-non-hmac-response-keys (string: \"\") - 指定在响应的数据对象中的哪些键不会被审计设备 HMAC 哈希。请注意,可以通过多次使用该选项来指定多个键,每次指定一个键 -default-lease-ttl (duration: \"\") - 该身份验证方法生成的令牌租约的默认 TTL。如果未指定,则默认为 Vault 服务器全局配置的默认租约 TTL,或先前为该验证方法配置过的值 -passthrough-request-headers (string: \"\") - 将要发送到身份验证方法的请求标头值。请注意,可以通过多次使用该选项来指定多个键,每次指定一个键 -allowed-response-headers (string: \"\") - 允许身份验证方法设置在响应上的标头值。请注意,可以通过多次使用该选项来指定多个键,每次指定一个键 -description (string: \"\") - 关于此身份验证方法的易于阅读的描述 -listing-visibility (string: \"\") - 是否在列出身份验证方法的 UI 界面上展示挂载点信息的开关 -local (bool: false) - 将此身份验证方法标记为本地方法。本地方法的数据不会被复制或是被复制机制删除 -max-lease-ttl (string: \"\") - 租约的最大 TTL 约束,使用类似 \"5s\" 或是 \"30m\" 这样的字符串定义 -path (string: \"\") - 可以访问身份验证方法的路径。所有身份验证方法的路径都必须是唯一的。默认值为验证方法的类型名称 -seal-wrap (bool: false) - 是否为挂载点启用封印封装,启用后保存在该挂载点的值会使用封印的加密功能进行封装 "},"3.命令行/3.3.auth_help.html":{"url":"3.命令行/3.3.auth_help.html","title":"auth help","keywords":"","body":"auth help auth help 命令打印该身份验证方法的使用方法和帮助信息。 如果给定的是一个类型,那么该方法打印该类型身份验证方法的默认帮助信息。 如果给定的是一个路径,那么该方法打印的是该路径上启用的身份验证方法的帮助信息。给定路径上必须已经启用了一个身份验证方法。 每一种身份验证方法都可以打印自己的帮助信息。 例子 以 userpass 身份验证方法为例演示获取使用方法的帮助: $ vault auth help userpass Usage: vault login -method=userpass [CONFIG K=V...] The userpass auth method allows users to authenticate using Vault's internal user database. # ... 打印启用在 my-method/ 路径上的身份验证方法的使用说明: $ vault auth help my-method/ # ... 可用标志 该命令只有标准的全局标志位可用。 "},"3.命令行/3.4.auth_list.html":{"url":"3.命令行/3.4.auth_list.html","title":"auth list","keywords":"","body":"auth list auth list 命令列出已启用的身份验证方法。 例子 列出所有身份验证方法: $ vault auth list Path Type Description ---- ---- ----------- token/ token token based credentials userpass/ userpass n/a 列出身份验证方法的详细信息: $ vault auth list -detailed Path Type Accessor Plugin Default TTL Max TTL Replication Description ---- ---- -------- ------ ----------- ------- ----------- ----------- token/ token auth_token_b2166f9e n/a system system replicated token based credentials userpass/ userpass auth_userpass_eea6507e n/a system system replicated n/a 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 命令选项 -detailed (bool:false) - 是否打印每种身份验证方法的诸如配置参数以及复制状态等详细信息。 "},"3.命令行/3.5.auth_tune.html":{"url":"3.命令行/3.5.auth_tune.html","title":"auth tune","keywords":"","body":"auth tune auth tune 命令调整指定路径上的身份验证方法的配置选项。传递给命令的参数是身份验证方法启用的路径,而非方法的类型。 例子 调整身份验证方法配置之前,先查看启用于 github/ 路径上的身份验证方法的配置: $ vault read sys/auth/github/tune Key Value --- ----- default_lease_ttl 768h description n/a force_no_cache false max_lease_ttl 768h token_type default-service github/ 路径上启用的身份验证方法的默认租约 TTL 被设置为 768 小时。将它调整为 72 小时: $ vault auth tune -default-lease-ttl=72h github/ Success! Tuned the auth method at: github/ 让我们指定多个请求对象中不在审计记录中进行 HMAC 哈希处理的字段: $ vault auth tune -audit-non-hmac-request-keys=value1 -audit-non-hmac-request-keys=value2 github/ Success! Tuned the auth method at: github/ 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: -allowed-response-headers (string: \"\") - 允许身份验证方法设置在响应上的标头值。 -audit-non-hmac-request-keys (string: \"\") - 指定在请求的数据对象中的哪些键不会被审计设备 HMAC 哈希。请注意,可以通过多次使用该选项来指定多个键,每次指定一个键 -audit-non-hmac-response-keys (string: \"\") - 指定在响应的数据对象中的哪些键不会被审计设备 HMAC 哈希。请注意,可以通过多次使用该选项来指定多个键,每次指定一个键 -default-lease-ttl (duration: \"\") - 该身份验证方法生成的令牌租约的默认 TTL。如果未指定,则默认为 Vault 服务器全局配置的默认租约 TTL,或先前为该验证方法配置过的值 -description (string: \"\") - 设置该身份验证方法的描述。会覆盖当前可能已经存储的值。 -listing-visibility (string: \"\") - 是否在列出身份验证方法的 UI 界面上展示挂载点信息的开关 -max-lease-ttl (duration: \"\") - 该身份验证方法签发的租约最大的 TTL 。默认值为 Vault 服务器全局配置的最大租约 TTL,或是先前身份验证方法设置的值。该参数将会覆盖服务器的全局最大 TTL,可以设置成更长或者更短 -passthrough-request-headers (string: \"\") - 将要发送到身份验证方法的请求标头值。请注意,可以通过多次使用该选项来指定多个键,每次指定一个键 -token-type (string: \"\") - 设置验证方法返回的令牌类型。请注意,可以通过多次使用该选项来指定多个键,每次指定一个键 "},"3.命令行/4.debug.html":{"url":"3.命令行/4.debug.html","title":"debug","keywords":"","body":"debug debug 命令启动一个监控 Vault 服务的进程,在一段时间内侦测有关的信息。 收集有关 Vault 集群状态的信息通常需要操作员通过各种 API 调用和终端命令访问所有必要的信息。 debug 命令旨在提供一个简单的工作流程,产生一致的输出,以帮助操作员检索和共享出问题的服务器的信息。 debug 命令遵循基本命令传统,接受的相同变量,例如通过先前登录存储的令牌,或是环境变量 VAULT_TOKEN 和 VAULT_ADDR。使用的令牌决定了权限,进而决定了 debug 能够收集的信息。指定的地址决定了将被探测的目标服务器。 如果命令收到中断信号,在收到信号的时间点上已经收集到的信息会被保存到一个输出目录中。 权限 无论是否提供特定收集目标,debug 获取要收集的目标数据的能力取决于提供的令牌。一些目标,例如 server-status,无需身份验证即可查询,这意味着它总能够被查询。其他目标需要所使用的令牌具有 ACL 权限来查询指定的端点,以便获得正确的响应。由于权限或其他原因在收集期间遇到的所有错误都将记录在索引文件中。 收集目标 -target 标志可以重复定义来定义调试运行期间所要收集的信息。默认情况下会收集所有信息。 Target Description config 配置状态的脱敏版本 host 运行服务的实例的信息,例如 CPU、内存以及磁盘 metrics 遥测信息 pprof 运行时剖析(profiling)数据,包括堆、CPU、gouroutine以及追踪剖析(trace profiling)数据 replication-status 复制状态 server-status 健康及封印状态 请注意,config、host、metrics 和 pprof 目标仅在主节点和性能备用节点上查询,因为信息属于节点本身,不应转发请求。 此外,由于在不启用 cgo 的情况下无法获取数据,在 OpenBSD 平台上无法获取主机信息。 输出信息的布局 输出的一组信息解压缩后是一个文件夹。除了运行时剖析数据外,其他目标都被捕获在各自独立的文件中。这些收集目标每个都以一个 JSON 数组对象的形式表达,数组中每一个成员都是在特定间隔周期内以JSON 对象格式收集到的数据。 $ tree vault-debug-2019-10-15T21-44-49Z/ vault-debug-2019-10-15T21-44-49Z/ ├── 2019-10-15T21-44-49Z │ ├── goroutine.prof │ ├── heap.prof │ ├── profile.prof │ └── trace.out ├── 2019-10-15T21-45-19Z │ ├── goroutine.prof │ ├── heap.prof │ ├── profile.prof │ └── trace.out ├── 2019-10-15T21-45-49Z │ ├── goroutine.prof │ ├── heap.prof │ ├── profile.prof │ └── trace.out ├── 2019-10-15T21-46-19Z │ ├── goroutine.prof │ ├── heap.prof │ ├── profile.prof │ └── trace.out ├── 2019-10-15T21-46-49Z │ ├── goroutine.prof │ └── heap.prof ├── config.json ├── host_info.json ├── index.json ├── metrics.json ├── replication_status.json └── server_status.json 例子 使用默认值启动调试: $ vault debug 使用自定义配置启动调试: $ vault debug -duration=1m -interval=10s -metrics-interval=5s -compress=false 仅针对特定目标启动调试: $ vault debug -target=host -target=metrics 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 命令选项: -compress (bool: true) - 是否压缩输出包。默认为 true -duration (int or time string: \"2m\") - 运行时长。默认为 2 分钟 -interval (int or time string: \"30s\") - 拉取性能剖析数据和服务器状态的间隔时间。默认为 30 秒 -metrics-interval (int or time string: \"10s\") - 拉取遥测数据的间隔时间。默认为 10 秒 -output (string) - 设置调试信息输出路径。默认为基于时间生成的文件名 -target (string: all targets) - 收集目标,如果未设置则默认收集所有信息。该标志可重复定义以指定收集多个目标。可收集的目标有:config、host、metrics、pprof、replicatioin-status、server-status "},"3.命令行/5.delete.html":{"url":"3.命令行/5.delete.html","title":"delete","keywords":"","body":"delete delete 命令从 Vault 中删除指定路径上的机密和配置。删除操作的具体实现是委托给具体路径上挂载的后端实现的。 例子 从静态机密引擎中删除数据: $ vault delete secret/my-secret 删除加密服务(transit)后端中的密钥: $ vault delete transit/keys/my-key 删除一个 IAM 角色: $ vault delete aws/roles/ops 可用标志 该命令只有标准的全局标志位可用。 "},"3.命令行/6.kv.html":{"url":"3.命令行/6.kv.html","title":"kv","keywords":"","body":"kv kv 命令包含一组与 Vault 的 Key/Value 机密引擎交互的子命令(同时包含 Version 1 以及 Version 2): 例子 在 secret 路径上启用的 K/V Version 2 引擎上创建或更新键为 \"creds\" 的记录,值为 \"passcode=my-long-passcode\": $ vault kv put secret/creds passcode=my-long-passcode Key Value --- ----- created_time 2019-06-28T15:53:30.395814Z deletion_time n/a destroyed false version 1 读取该值: $ vault kv get secret/creds ====== Metadata ====== Key Value --- ----- created_time 2019-06-28T15:53:30.395814Z deletion_time n/a destroyed false version 1 ====== Data ====== Key Value --- ----- passcode my-long-passcode 读取键 \"creds\" 的元数据: $ vault kv metadata get secret/creds ========== Metadata ========== Key Value --- ----- cas_required false created_time 2019-06-28T15:53:30.395814Z current_version 1 delete_version_after 0s max_versions 0 oldest_version 0 updated_time 2019-06-28T15:53:30.395814Z ====== Version 1 ====== Key Value --- ----- created_time 2019-06-28T15:53:30.395814Z deletion_time n/a destroyed false 读取键 \"creds\" 的指定版本: $ vault kv get -version=1 secret/creds ====== Metadata ====== Key Value --- ----- created_time 2019-06-28T15:53:30.395814Z deletion_time n/a destroyed false version 1 ====== Data ====== Key Value --- ----- passcode my-long-passcode 可用子命令 Usage: vault kv [options] [args] # ... Subcommands: delete Deletes versions in the KV store destroy Permanently removes one or more versions in the KV store enable-versioning Turns on versioning for a KV store get Retrieves data from the KV store list List data or secrets metadata Interact with Vault's Key-Value storage patch Sets or updates data in the KV store without overwriting put Sets or updates data in the KV store rollback Rolls back to a previous version of data undelete Undeletes versions in the KV store 每种子命令的详细使用方法,请阅读对应的章节。 "},"3.命令行/6.1.kv_delete.html":{"url":"3.命令行/6.1.kv_delete.html","title":"kv delete","keywords":"","body":"kv delete kv delete 命令删除 Key/Value 机密引擎指定路径上的数据。如果使用 K/V Version 2,它的版本化数据不会被完全删除,而是标记为已删除并且不会在正常的读取请求中返回。 例子 删除键 \"creds\" 的最新版本: $ vault kv delete secret/creds Success! Data deleted (if it existed) at: secret/creds [K/V Version 2] 专有 删除键 \"creds\" 的版本 11: $ vault kv delete -versions=11 secret/creds Success! Data deleted (if it existed) at: secret/creds 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 命令选项 -versions ([]int: ) - 要删除的版本。该版本的数据并不会被删除,而是不会在正常的读取请求中返回。 注意,该命令选项仅适用于 K/V Version 2。 "},"3.命令行/6.2.kv_destroy.html":{"url":"3.命令行/6.2.kv_destroy.html","title":"kv destroy","keywords":"","body":"kv delete 注意,该命令仅适用于 K/V Version 2 机密引擎,无法使用于 Versoin 1。 kv destroy 命令永久删除 Key/Value 机密引擎指定路径上特定版本的数据。如果指定路径上不存在要删除的键,那么不会执行任何操作。 例子 永久删除键 \"creds\" 的 version 11: $ vault kv destroy -versions=11 secret/creds Success! Data written to: secret/destroy/creds 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 命令选项 -versions ([]int: ) - 要彻底删除的版本。该版本的数据会被永久删除。 "},"3.命令行/6.3.kv_enable-versioning.html":{"url":"3.命令行/6.3.kv_enable-versioning.html","title":"kv enable-versioning","keywords":"","body":"kv enable-versioning kv enable-versioning 开启指定路径上挂载的 Key/Value Version 1 机密引擎的版本控制功能。 例子 以下命令开启挂载于 \"secret\" 路径上的 Key/Value Version 1 机密引擎的版本控制功能: $ vault kv enable-versioning secret Success! Tuned the secrets engine at: secret/ 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -format string: \"table\" - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 "},"3.命令行/6.4.kv_get.html":{"url":"3.命令行/6.4.kv_get.html","title":"kv get","keywords":"","body":"kv get kv get 命令读取挂载于指定路径上的 K/V 机密引擎中指定键的值。如果读取不存在的键会返回错误。如果键存在但没有数据,则不返回任何内容。 例子 读取键 \"creds\" 的数据: $ vault kv get secret/creds ====== Metadata ====== Key Value --- ----- created_time 2019-06-06T06:03:26.595978Z deletion_time n/a destroyed false version 5 ====== Data ====== Key Value --- ----- passcode my-long-passcode 如果 \"secret“ 上启用的是 K/V Version 1 机密引擎,那么输出信息不会有元数据,因为该引擎的数据没有相关的版本信息。 $ vault kv get secret/creds ====== Data ====== Key Value --- ----- passcode my-long-passcode 返回 \"creds\" 键的 \"passcode\" 字段: $ vault kv get -field=passcode secret/creds my-long-passcode 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -field (string: \"\") 只返回指定字段的数据。此选项优先级高于其他格式指令。结果的尾部没有换行符,使其适合通过管道与其他进程集成使用。 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 命令选项 -version (int: 0) - 指定要读取的版本。如果省略则返回最新版本的数据。 "},"3.命令行/6.5.kv_list.html":{"url":"3.命令行/6.5.kv_list.html","title":"kv list","keywords":"","body":"kv list kv list 命令返回指定位置的键名列表。文件夹的名字以 / 为后缀。输入值必须对应文件夹;列出一个文件路径不会返回值数据。请注意,不会对列出的键执行基于策略的过滤;不要在键名中编入敏感信息。无法通过此命令访问值本身。 例子 列出键 \"my-app\" 下所有的值: $ vault kv list secret/my-app/ Keys ---- admin_creds domain eng_creds qa_creds release 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 "},"3.命令行/6.6.kv_metadata.html":{"url":"3.命令行/6.6.kv_metadata.html","title":"kv metadata","keywords":"","body":"kv metadata 注意,该命令仅适用于 K/V Version 2 机密引擎,无法使用于 Versoin 1。 kv metadata 命令包含一组与 Vault 的 Key/Value 机密引擎交互的子命令,用以获取挂载于指定路径上的 K/V Version 2 机密引擎的元数据信息以及版本信息。 可用标志 Usage: vault kv metadata [options] [args] # ... Subcommands: delete Deletes all versions and metadata for a key in the KV store get Retrieves key metadata from the KV store put Sets or updates key settings in the KV store kv metadata delete kv metadata delete 命令删除指定键的所有版本数据以及元数据。 例子 删除键 \"creds\" 的所有版本数据以及元数据: $ vault kv metadata delete secret/creds Success! Data deleted (if it existed) at: secret/metadata/creds kv metadata get kv metadata get 命令读取指定键机密的版本化元数据。如果指定键不存在会返回错误。 例子 读取键 \"creds\" 的元数据: $ vault kv metadata get secret/creds ========== Metadata ========== Key Value --- ----- cas_required false created_time 2019-06-28T15:53:30.395814Z current_version 5 delete_version_after 0s max_versions 0 oldest_version 0 updated_time 2019-06-28T16:01:47.40064Z ====== Version 1 ====== Key Value --- ----- created_time 2019-06-28T15:53:30.395814Z deletion_time n/a destroyed false ====== Version 2 ====== Key Value --- ----- created_time 2019-06-28T16:01:36.676912Z deletion_time n/a destroyed false ... kv metadata put kv metadata put 命令可以用来在 K/V v2 机密引擎上创建一个空白的新键或是更新键配置。 例子 在 K/V v2 引擎上创建一个名为 \"creds\" 的空白键: $ vault kv metadata put secret/creds Success! Data written to: secret/metadata/creds 设置键 \"creds\" 的最大版本数: $ vault kv metadata put -max-versions=5 secret/creds Success! Data written to: secret/metadata/creds 注意:如果未设置,则使用引擎配置的最大版本。一旦机密的版本超过配置的最大允许数,最旧的版本将被永久删除。 对键 \"creds\" 启用 Check-and-Set 约束: $ vault kv metadata put -cas-required secret/creds 注意:一旦 check-and-set 被启用,对该键的写操作都需要添加 cas 参数。否则,会默认使用引擎配置的值。 设置键 \"creds\" 的生存时长: $ vault kv metadata put -delete-version-after=\"3h25m19s\" secret/creds 注意:如果没有设置该值,那么会使用引擎配置的 Delete-Version-After 值。如果显式将键的 Delete-Version-After 值设置为大于引擎设置的值,那么会使用引擎设置的值。对 Delete-Version-After 设置的变更只会应用于新版本的键数据。 输出选项 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、 json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 子命令选项 -cas-required (bool: false) - 如果设置为 true 那么所有写操作都要指定 cas 参数。如果设置为 false 那么会使用引擎配置的值。默认为 false -max-versions (int: 0) - 每个键允许保留的版本数。如果忽略,那么会使用引擎配置的最大版本数。一旦一个键的版本数超过了配置的允许值那么最旧的版本将被永久删除 -delete-version-after (string: \"0s\") - 通过设置该值配置所有写入这个键的新版本经过多长时间后将被删除。如果忽略,那么将使用引擎配置的 delete_version_after 值。可使用 Go 语言时长格式 -custom-metedata (string: \"\") - 使用一个键值对设置 custom_metadata 字段。可以通过多次使用该选项来指定多个键,每次指定一个键 "},"3.命令行/6.7.kv_patch.html":{"url":"3.命令行/6.7.kv_patch.html","title":"kv patch","keywords":"","body":"kv patch 注意,该命令仅适用于 K/V Version 2 机密引擎,无法使用于 Versoin 1。 kv patch 命令将数据写入 K/V v2 机密引擎中的指定路径,可以是任何类型的数据。与 kv put 命令不同,patch 命令结合现有数据进行部分更新,而不是整体替换。 例子 如果想要对键 \"creds\" 上已有的数据添加一个额外的键值对(ttl=48h): $ vault kv patch secret/creds ttl=48h Key Value --- ----- created_time 2019-06-06T16:46:22.090654Z deletion_time n/a destroyed false version 6 请注意:如果使用的是 kv put 命令,需要同时指定现有数据以及想要额外添加的新数据才能达到于上述 patch 命令等价的效果: $ vault kv put secret/creds ttl=48h passcode=my-long-passcode 要写入的数据也可以从一个磁盘上的文件读取,文件名前加一个 \"@\" 前缀,例如: $ vault kv patch secret/creds @data.json 或者也可以通过 \"-\" 符号从 stdin 读取: $ echo \"abcd1234\" | vault kv patch secret/foo bar=- 可用标志 输出选项 -field (string: \"\") 只返回指定字段的数据。此选项优先级高于其他格式指令。结果的尾部没有换行符,使其适合通过管道与其他进程集成使用。 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 命令选项 -method (string: \"patch\") - 设置使用的更新方法。可用的选项有 patch 和 rw。patch 方法使用 HTTP PATCH 请求执行部分更新。rw 方法会首先读取机密数据,在内存中进行更新后再写回新版本数据。 -cas (int: 0) - 配置是否要求使用 Check-And-Set 操作。该标志仅在使用 patch 方法时有效。如果机密或是机密引擎上设置了 cas_required 为 true 那么该标志必须使用。为使 patch 操作成功执行,-cas 必须设定成机密当前的版本号。如果使用 rw 方法那么该标志设定的值将被忽略,取而代之的是使用读取机密时得到的机密版本号。 "},"3.命令行/6.8.kv_put.html":{"url":"3.命令行/6.8.kv_put.html","title":"kv put","keywords":"","body":"kv put kv put 命令将数据写入 K/V 机密引擎中的指定路径。 如果使用 K/V v2,该命令会在指定位置创建新版本的机密。如果使用 K/V v1,此命令将给定的秘密存储在指定位置。 无论使用哪个版本的 K/V 引擎,如果指定路径上尚不存在该值,则调用者使用的令牌必须具有“创建”功能的 ACL 策略。如果该值已存在,则调用者使用的令牌必须具有“更新”功能的 ACL 策略。 例子 向键 \"creds\" 写入数据: $ vault kv put secret/creds passcode=my-long-passcode 要写入的数据也可以从一个磁盘上的文件读取,文件名前加一个 \"@\" 前缀,例如: $ vault kv put secret/foo @data.json 或者也可以通过 \"-\" 符号从 stdin 读取: $ echo \"abcd1234\" | vault kv put secret/foo bar=- 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -field (string: \"\") 只返回指定字段的数据。此选项优先级高于其他格式指令。结果的尾部没有换行符,使其适合通过管道与其他进程集成使用。 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 命令选项 -cas (int: 0) - 配置是否要求使用 Check-And-Set 操作。如果忽略则允许进行写操作。如果设置为 0 那么只有在数据不存在时允许写入。如果设置为非零值那么只有当键的当前版本与指定值一致时允许写入。默认值为 -1 "},"3.命令行/6.9.kv_rollback.html":{"url":"3.命令行/6.9.kv_rollback.html","title":"kv rollback","keywords":"","body":"kv rollback 注意,该命令仅适用于 K/V Version 2 机密引擎,无法使用于 Versoin 1。 kv rollback 命令指定路径上机密恢复到的一个早期版本,指定的旧版本的值将作为新版本写入;比如当前版本是 5,想要回滚到版本 2,那么版本 2 的数据就作为版本 6 写入。这个命令可以很容易地恢复不小心被覆盖的数据。 例子 将键 \"creds\" 恢复到版本 2: $ vault kv rollback -version=2 secret/creds Key Value --- ----- created_time 2019-06-06T17:07:19.299831Z deletion_time n/a destroyed false version 6 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 命令选项 -version (int: 0) - 指定要恢复哪个版本的数据到最新版本 "},"3.命令行/6.10.kv_undelete.html":{"url":"3.命令行/6.10.kv_undelete.html","title":"kv undelete","keywords":"","body":"kv undelete 注意,该命令仅适用于 K/V Version 2 机密引擎,无法使用于 Versoin 1。 kv undelete 命令撤销对 K/V 存储中指定路径上的指定版本的数据删除。它可以恢复数据,允许它在获取请求时返回。 例子 恢复键 \"creds\" 的版本 3: $ vault kv undelete -versions=3 secret/creds Success! Data written to: secret/undelete/creds 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 命令选项 -versions ([]int: ) - 指定要恢复哪些版本的数据到最新版本 "},"3.命令行/7.lease.html":{"url":"3.命令行/7.lease.html","title":"lease","keywords":"","body":"lease lease 命令包含一组操作机密所关联的租约的子命令。想要操作租约上附加的令牌,请使用 vault token 子命令。 例子 查找一条租约: $ vault lease lookup database/creds/readonly/27e1b9a1-27b8-83d9-9fe0-d99d786bdc83 Key Value --- ----- expire_time 2021-03-17T11:55:50.755313-05:00 id database/creds/readonly/27e1b9a1-27b8-83d9-9fe0-d99d786bdc83 issue_time 2021-03-17T11:45:50.755312-05:00 last_renewal renewable true ttl 9m52s 续约: $ vault lease renew database/creds/readonly/27e1b9a1-27b8-83d9-9fe0-d99d786bdc83 Key Value --- ----- lease_id database/creds/readonly/27e1b9a1-27b8-83d9-9fe0-d99d786bdc83 lease_duration 5m lease_renewable true 吊销: $ vault lease revoke database/creds/readonly/27e1b9a1-27b8-83d9-9fe0-d99d786bdc83 Success! Revoked lease: database/creds/readonly/27e1b9a1-27b8-83d9-9fe0-d99d786bdc83 可用子命令 Usage: vault lease [options] [args] # ... Subcommands: lookup Lookup lease information by lease id renew Renews the lease of a secret revoke Revokes leases and secrets 每种子命令的详细使用方法,请阅读对应的章节。 "},"3.命令行/7.1.lease_lookup.html":{"url":"3.命令行/7.1.lease_lookup.html","title":"lease lookup","keywords":"","body":"lease lookup lease lookup 命令读取一个机密租约的信息。 Vault 中每个机密都关联了一个租约。用户可以通过租约 ID 查询租约信息。 例子 查询租约: $ vault lease lookup database/creds/readonly/27e1b9a1-27b8-83d9-9fe0-d99d786bdc83 Key Value --- ----- expire_time 2021-03-17T11:55:50.755313-05:00 id database/creds/readonly/27e1b9a1-27b8-83d9-9fe0-d99d786bdc83 issue_time 2021-03-17T11:45:50.755312-05:00 last_renewal renewable true ttl 9m52s 可用标志 该命令只有标准的全局标志位可用。 "},"3.命令行/7.2.lease_renew.html":{"url":"3.命令行/7.2.lease_renew.html","title":"lease renew","keywords":"","body":"lease renew lease renew 命令续约机密的租约,延长它在被 Vault 吊销之前可以使用的时间。 Vault 中的每个秘密都有一个与之关联的租约。如果机密的所有者想要使用它的时间比租约长,则必须续约。续约租约不会更改机密的内容。 例子 续约: $ vault lease renew database/creds/readonly/27e1b9a1-27b8-83d9-9fe0-d99d786bdc83 Key Value --- ----- lease_id database/creds/readonly/27e1b9a1-27b8-83d9-9fe0-d99d786bdc83 lease_duration 5m lease_renewable true 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: -increment (duration: \"\") - 申请延长租约有效期的秒数。Vault 没有义务必须按此申请来延长租约 "},"3.命令行/7.3.lease_revoke.html":{"url":"3.命令行/7.3.lease_revoke.html","title":"lease revoke","keywords":"","body":"lease revoke lease revoke 命令吊销一个机密的租约,使得底层对应的机密变得不再有效。 例子 吊销租约: $ vault lease revoke database/creds/readonly/27e1b9a1-27b8-83d9-9fe0-d99d786bdc83 Success! Revoked lease: database/creds/readonly/27e1b9a1-27b8-83d9-9fe0-d99d786bdc83 使用前缀吊销租约: $ vault lease revoke -prefix database/creds Success! Revoked any leases with prefix: database/creds 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: -force (bool: false) - 机密引擎删除机密失败时是否要从 Vault 中删除租约。可以在要删除的机密已经被从 Vault 外部用手动方式删除的情况下使用(因为机密已经被手动删除了,所以机密引擎再想删除机密会失败,此时加上该标志可以删除 Vault 中已经没用了的租约)。如果设置了该标志,-prefix 标志也必须设置。可以使用别名 -f 代替 -force。默认值为 false -prefix (bool:false) - 使用 ID 前缀而不是精准的租约 ID。这使得我们可以同时吊销多个租约。默认值为 false -sync (bool:false) - 在后台使用同步调用而不是插入一个待吊销的异步队列等待吊销 "},"3.命令行/8.list.html":{"url":"3.命令行/8.list.html","title":"list","keywords":"","body":"list list 命令列出 Vault 指定路径上的数据。它可以用来列出指定机密引擎中的键。 例子 列出 K/V 机密引擎中 \"my-app\" 文件夹下的值: $ vault list secret/my-app/ 可用标志 该命令只有标准的全局标志位可用。 "},"3.命令行/9.login.html":{"url":"3.命令行/9.login.html","title":"login","keywords":"","body":"login login 命令使用提供的参数向 Vault 验证用户或机器。身份验证成功会返回一个 Vault 令牌 - 在概念上类似于网站上的会话令牌。默认情况下,此令牌会被缓存在本地以供将来发起请求时使用。 -method 标志允许使用其他身份验证方法,例如 userpass、github 或 cert。使用这些验证方法时可能需要添加额外的键值对。有关可用于给定身份验证方法的配置参数列表的更多信息,请使用 vault auth help TYPE 命令获取。您还可以使用 vault auth list 命令查看已启用的身份验证方法列表。 如果在非默认路径上启用了身份验证方法,则仍然使用 -method 标志指定验证方法类型,同时使用 -path 标志指定启用的路径。 如果身份验证请求启用了响应封装(通过 -wrap-ttl 标志),返回的令牌会自动解封,除非: 设置了 -token-only 标志,这样的话命令只会输出封装令牌。 设置了 -no-store 标志,这样的话命令会输出封装令牌的详细信息。 例子 login 默认使用 token 身份验证方法: $ vault login s.3jnbMAKl1i4YS3QoKdbHzGXq Success! You are now authenticated. The token information displayed below is already stored in the token helper. You do NOT need to run \"vault login\" again. Future Vault requests will automatically use this token. Key Value --- ----- token s.3jnbMAKl1i4YS3QoKdbHzGXq token_accessor 7Uod1Rm0ejUAz77Oh7SxpAM0 token_duration 767h59m49s token_renewable true token_policies [\"admin\" \"default\"] identity_policies [] policies [\"admin\" \"default\"] 要使用不同身份验证方法,使用 -method 标志: $ vault login -method=userpass username=my-username Password (will be hidden): Success! You are now authenticated. The token information below is already stored in the token helper. You do NOT need to run \"vault login\" again. Future requests will use this token automatically. Key Value --- ----- token s.2y4SU3Sk46dK3p2Y8q2jSBwL token_accessor 8J125x9SZyB76MI9uF2jSJZf token_duration 768h token_renewable true token_policies [\"default\"] identity_policies [] policies [\"default\"] token_meta_username my-username 请注意命令选项 (-method=userpass) 要放在命令参数之前(username=my-username)。 如果在路径 github-prod 上启用了 github 身份验证方法: $ vault login -method=github -path=github-prod Success! You are now authenticated. The token information below is already stored in the token helper. You do NOT need to run \"vault login\" again. Future requests will use this token automatically. Key Value --- ----- token s.2f3c5L1MHtnqbuNCbx90utmC token_accessor JLUIXJ6ltUftTt2UYRl2lTAC token_duration 768h token_renewable true token_policies [\"default\"] identity_policies [] policies [\"default\"] token_meta_org hashicorp token_meta_username my-username 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项s -field (string: \"\") 只返回指定字段的数据。此选项优先级高于其他格式指令。结果的尾部没有换行符,使其适合通过管道与其他进程集成使用。 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 命令选项 -method (string \"token\") - 使用的身份验证方法,例如 userpass 或是 ldap。注意要使用类型,而不是启用引擎的路径。使用 -path 指定身份验证方法启用的路径。 -no-print (bool: false) - 不输出令牌。令牌仍然会被令牌助手保存在本地。默认为 false。 -no-store (bool: false) - 不使用令牌助手在身份验证成功后将令牌保存到本地(通常使用本地文件系统)以供将来的请求使用。令牌只会通过命令行输出打印。 -path (string: \"\") - 使用的身份验证方法在 Vault 中启用的路径。默认为身份验证方法的类型名称(例如:userpass 方法启用在 userpass/ 上)。 -token-only (bool: false) - 只输出未经验证的令牌。该标志为等同于 -field=token -no-store。使用本标志位将导致 -field 和 -no-store 标志被忽略。 "},"3.命令行/10.monitor.html":{"url":"3.命令行/10.monitor.html","title":"monitor","keywords":"","body":"monitor monitor 命令显示 Vault 服务器的服务器的实时日志。此命令可以用参数指定日志级别,该级别可以与 Vault 服务器启动时使用的日志级别不同。 monitor 命令可以读取 VAULT_ADDR 环境变量。该地址决定了监控的目标服务器地址。 请注意本命令被设计成持续运行不会自行结束。类似于 Unix 世界中的 tail -f。该命令只要没有遇到意外错误就不会停止运行。用户必须手动终结该进程。 如果 Vault 输出日志的速度快于接收者可以处理的速度,某些日志行会被丢弃。 例子 监视服务器 debug 级别的日志: $ vault monitor -log-level=debug 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -log-level (string: \"info\") - 要监视的 Vault 服务日志级别。可用级别有(按照细节程度排序):trace、debug、info、warn、error。如果忽略该参数,默认使用 info。 "},"3.命令行/11.namespace.html":{"url":"3.命令行/11.namespace.html","title":"namespace","keywords":"","body":"namespace namespace 命令包含一组操作名字空间的子命令。 例子 列出所有名字空间: $ vault namespace list 在 ns/ 路径上创建一个名字空间: $ vault namespace create ns1/ 删除 ns/ 上的名字空间: $ vault namespace delete ns1/ 查找 ns1/ 路径上的名字空间的信息: $ vault namespace lookup ns1/ 锁定当前的名字空间的 API: $ vault namespace lock 锁定路径 current/namespace/ns1/ 子路径的 API $ vault namespace lock ns1/ 解锁当前名字空间的 API: $ vault namespace unlock -unlock-key 解锁路径 current/namespace/ns1/ 子路径的 API: $ vault namespace unlock -unlock-key ns1/ 可用标志 Usage: vault namespace [options] [args] This command groups subcommands for interacting with Vault namespaces. These set of subcommands operate on the context of the namespace that the current logged in token belongs to. Subcommands: create Create a new namespace delete Delete an existing namespace list List child namespaces lookup Look up an existing namespace lock Lock the API for a namespace unlock Unlock the API for a namespace "},"3.命令行/12.operator.html":{"url":"3.命令行/12.operator.html","title":"operator","keywords":"","body":"operator operator 命令包含一组操作系统管理员管理 Vault 服务的子命令。大多数用户不会需要使用这些命令。 例子 初始化一个新的 Vault 集群: $ vault operator init Unseal Key 1: sP/4C/fwIDjJmHEC2bi/1Pa43uKhsUQMmiB31GRzFc0R Unseal Key 2: kHkw2xTBelbDFIMEgEC8NVX7NDSAZ+rdgBJ/HuJwxOX+ Unseal Key 3: +1+1ZnkQDfJFHDZPRq0wjFxEuEEHxDDOQxa8JJ/AYWcb Unseal Key 4: cewseNJTLovmFrgpyY+9Hi5OgJlJgGGCg7PZyiVdPwN0 Unseal Key 5: wyd7rMGWX5fi0k36X4e+C4myt5CoTmJsHJ0rdYT7BQcF Initial Root Token: 6662bb4a-afd0-4b6b-faad-e237fb564568 # ... 强制 Vault 节点辞任集群领导者节点: $ vault operator step-down Success! Stepped down: https://127.0.0.1:8200 轮替 Vault 底层的加密密钥: $ vault operator rotate Success! Rotated key Key Term 2 Install Time 01 Jan 07 12:30 UTC 可用子命令 Usage: vault operator [options] [args] # ... Subcommands: generate-root Generates a new root token init Initializes a server key-status Provides information about the active encryption key rekey Generates new unseal keys rotate Rotates the underlying encryption key seal Seals the Vault server step-down Forces Vault to resign active duty unseal Unseals the Vault server 每种子命令的详细使用方法,请阅读对应的章节。 "},"3.命令行/12.1.operator_diagnose.html":{"url":"3.命令行/12.1.operator_diagnose.html","title":"operator diagnose","keywords":"","body":"operator diagnose operator diagnose 命令应主要在 Vault 宕机或部分无法运行时使用。无论 Vault 处于何种状态,都可以安全地使用该命令,但如果 Vault 服务正在运行,则可能会因为某些测试用例而返回无意义的结果。 注意:如果您在服务启动之前或运行时主动运行诊断命令,请查阅有关以下各项检查的文档以查看哪些检查会返回假的错误消息或警告。 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 输出布局 operator diagnose 命令将在命令行中输出一组行。每一行将以中括号中的前缀开头。它们是: [ success ] - 标记成功的检查 [ warning ] - 标记虽然通过,但可能存在与 Vault 遇到的问题有关的需要进一步分析的潜在问题。诊断会产生频繁的警告。这些警告旨在作为调试过程的起点 [ failure ] - 标记该检查失败。从诊断命令的角度看,故障是关键问题 除了这些带前缀的输出行之外,可能还有一些没有前缀的紫色的输出行。这些是来自诊断程序的建议,旨在提供有关如何修复可能出现的潜在警告或故障的一般性指导。 如果警告或故障嵌套在其他信息内部并带有父级的前缀,那么检查结果前缀将会冒泡至父级。故障会覆盖警告,警告覆盖成功。例如,如果 Storage 检查下的 TLS 检查失败,则 [ failure ] 前缀将冒泡到存储检查(子项的失败将直接体现在父项的前缀上)。 命令选项 -config (string: \"\") - 指向用以启动 Vault 服务的配置文件的路径 诊断检查 以下部分详细介绍了诊断程序运行的各种检查。文档中的检查名称将用斜杠分隔以表示它们是嵌套的。例如,记录为 A / B 的检查将在operator diagnose 输出中显示为 B,并将嵌套(缩进)在 A 下。 Vault Diagnose Vault Diagnose 是包含其余检查的顶级检查。它将报告检查的状态 Check Operating System / Check Open File Limit Check Open File Limit 验证打开文件限制值是否设置得足够高以使得 Vault 能够正常运行。建议将这些限制至少设置为 1024768 及以上。 在 openbsd、arm 和 windows 上将跳过此检查。 Check Operating System / Check Disk Usage Check Disk Usage 将报告每个分区的磁盘使用情况。对于生产环境主机上的每个分区,我们建议至少分区有 5% 的空间可用,以及至少 1 GB 的空间。 在 openbsd 和 arm 上将跳过此检查。 Parse Configuration Parse Configuration 将检查 Vault 服务器配置文件是否存在语法错误。它将检查配置文件中是否有多余的值、重复的节和不属于配置文件的节(例如tcpp listener 而不是 tcp listener)。 目前,storage 节不会被检查。 Check Storage / Create Storage Backend Create Storage Backend 确保在 Vault 服务配置中配置的存储节有足够的信息在内部创建存储对象。常见错误与 storage 节中错误配置的字段有关。 Check Storage / Check Consul TLS 如果存储类型是 consul,则 Check Consul TLS 验证 storage 节中包含的 TLS 信息。如果提供了证书链,诊断程序会解析根证书、中间证书和叶证书,并检查每个证书的正确性。 Check Storage / Check Consul Direct Storage Access Check Consul Direct Storage Access 是一项针对 Consul 的检查,可确保 Vault 不是直接访问 Consul 服务,而是通过本地代理。 Check Storage / Check Raft Folder Permissions Check Raft Folder Permissions 计算 Raft 文件夹的权限,检查之前是否在该文件夹内初始化了一个 boltDB 文件,并确保该文件夹配置的权限不是过于宽松,但同时又足够使用。Raft 文件夹不应具有 other 权限,但应具有 group rw 或 owner rw,具体取决于不同的配置。此检查还会在检测到使用符号链接时发出警告。 请注意,如果在先前没有运行过服务的情况下运行诊断,则此检查将警告尚未创建 Raft 文件。 在 Windows 上将跳过此检查。 Check Storage / Check Raft Folder Permissions Check Raft Folder Permissions 计算 Raft 文件夹的权限,检查之前是否在该文件夹内初始化了一个 boltDB 文件,该文件夹配置的权限不是过于宽松,但同时又足够使用。 Raft 文件夹不应具有 other 权限,但应具有 group rw 或所有 owner rw,具体取决于不同的设置。此检查还会在检测到使用符号链接时发出警告。 请注意,如果在先前没有运行过服务的情况下运行诊断,则此检查将警告尚未创建 Raft 文件。 在 Windows 上将跳过此检查。 Check Storage / Check For Raft Quorum Check For Raft Quorum 使用 FSM 来确保在 Vault 最近运行时在 Raft 集群中有奇数个选民。 请注意,如果在先前没有运行过服务的情况下运行诊断,则此检查将警告有 0 个投票者。 Check Storage / Check Storage Access Check Storage Access 将尝试将一个名为 diagnose/latency/ 的哑值写入存储。在运行诊断之前确保此位置没有重要数据,因为此检查将覆盖该数据。然后,该检查将尝试列出并读取它写入的值,以确保名称和值符合预期。 如果任何操作的时间超过 100 毫秒,Check Storage Access 将发出警告,如果整个检查时间超过 30 秒,则会出错。 Check Service Discovery / Check Consul Service Discovery TLS 如果存储类型是 consul,那么 Check Consul Service Discovery TLS 将验证包含在 service discovery 节中的 TLS 信息。如果提供了证书链,诊断程序会解析根证书、中间证书和叶证书,并检查每个证书的正确性。 Check Service Discovery / Check Consul Direct Service Discovery Check Consul Direct Service Discovery 是一项针对于 Consul 的检查,可确保 Vault 不直接访问 Consul 服务,而是通过本地代理访问。 Create Vault Server Configuration Seals Create Vault Server Configuration Seals 从 Vault 配置节创建封印并验证它们可以被初始化和解封。 Check Transit Seal TLS Check Transit Seal TLS 检查在 transit seal 节(如果存在)中提供的 TLS 客户端证书、密钥和 CA 证书的正确性。 Create Core Configuration / Initialize Randomness for Core Initialize Randomness for Core 确保 Vault 内核可以使用 randReader。 HA Storage 此检查以及它的嵌套检查将与 Check Storage 检查相同。唯一的区别是该检查将针对 Vault 配置中的 ha_storage 部分运行,而不是 storage 部分。 Determine Redirect Address 确保设置了 VAULT_API_ADDR、VAULT_REDIRECT_ADDR 或 VAULT_ADVERTISE_ADDR 环境变量之一,或者在 Vault 配置中指定了重定向地址。 Check Cluster Address 从 VAULT_CLUSTER_ADDR 环境变量,或从 Vault 配置中指定的重定向地址或集群地址解析集群地址,并检查地址的格式是否为host:port。 Check Core Creation Check Core Creation 验证 Vault 在创建核心对象时所做的逻辑配置检查。这些检查是运行时检查,这意味着此诊断测试抛出的任何错误也将在运行时由 Vault 服务器本身抛出。 Check For Autoloaded License Check For Autoloaded License 是一项企业版诊断检查,用于验证 Vault 是否在未来 30 天内能够访问到一个不会过期的有效的自动加载许可证。 Start Listeners / Check Listener TLS Check Listener TLS 验证服务器证书文件和密钥是否有效且匹配。它还会检查客户端 CA 文件(如果有的话)的有效证书,并对 listener 配置节执行标准运行时 listener 检查,例如验证最小和最大 TLS 版本是否在 Vault 支持的范围内。 与所有其他诊断 TLS 的检查一样,如果提供的任何证书将在一个月内到期,它将发出警告。 Start Listeners / Create Listeners Create Listeners 使用 listener 配置来初始化侦听器,如果出现任何问题,则会出现服务器错误。 Check Autounseal Encryption Check Autounseal Encryption 将使用封印节初始化加密屏障,如果封印类型不是 Shamir,并使用它来加密和解密一个哑值。 Check Server Before Runtime Check Server Before Runtime 在服务器初始化之前运行运行时代码检查以确保没有任何故障。如果没有另一个诊断检查失败,此检查将永远不会失败。 "},"3.命令行/12.2.operator_generate-root.html":{"url":"3.命令行/12.2.operator_generate-root.html","title":"operator generate","keywords":"","body":"operator generate-root operator generate-root 命令通过组合多个密钥持有人的密钥(例如 Shamir)来生成新的根令牌。使用 -dr-token 或 -recovery-token 选项时,可以以相同的方式生成 DR(灾难恢复)操作令牌或恢复令牌。 要开始生成根令牌,需要满足一下任意一个条件: 一个通过 -otp 标志提供的 base64 编码的一次性密码 (OTP)。使用 -generate-otp 标志生成一个可用值。返回的令牌会与该一次性密码进行按位异或(XOR)后返回。使用 -decode 标志输出最终值。 在 -pgp-key 标志中包含 PGP 密钥或 keybase 用户名的文件。生成的令牌使用此公钥加密。 可以在命令行上直接提供解封密钥作为命令的参数。如果 key 指定为“-”,则命令将从 stdin 读取。如果 TTY 可用,该命令将提示输入文本。 例子 为生成根令牌预先生成一个一次性密钥(OTP): $ vault operator generate-root -generate-otp 开始生成根令牌: $ vault operator generate-root -init -otp=\"...\" 输入一个解封密钥来继续根令牌生成的过程: $ vault operator generate-root -otp=\"...\" 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 命令选项 -cancel (bool: false) - 重置根令牌生成进度。这将抛弃所有已提交的解封密钥以及配置项。 -decode (string: \"\") - 解码并输出生成的根令牌。该选项需要将 -otp 标志设置为初始化期间使用的一次性密钥。如果值为“-”,则从 stdin 读取未解码的令牌。 -generate-otp (bool: false) - 生成并打印一个高熵的适合用作 -init 标志参数值的一次性密钥。 -nonce (string: \"\") - 初始化阶段使用的随机值。输入每个解封密钥时必须输入相同的随机值。 -otp (string: \"\") - 与 -decode 或是 -init 标志一同使用的一次性密码 -pgp-key (keybase or pgp) - 指向磁盘上一个包含二进制或 base64 编码 PGP 公钥的文件路径。这也可以使用格式 keybase: 指定为 Keybase 用户名。设置该标记时,生成的根令牌将使用给定的公钥进行加密和 base64 编码。 -status (bool: false) - 在不提供解封密钥的情况下打印当前尝试的状态。默认为 false。 -dr-token (bool: false) - 生成 DR (灾难恢复)操作令牌 -recovery-token (bool: false) - 生成恢复操作令牌 "},"3.命令行/12.3.operator_init.html":{"url":"3.命令行/12.3.operator_init.html","title":"operator init","keywords":"","body":"operator init operator init 命令对一个 Vault 服务节点执行初始化操作。初始化是 Vault 的存储后端做好接收数据的准备的过程。由于 Vault 服务在高可用模式下共享同一存储后端,因此这时只需初始化一个 Vault 节点即可初始化存储后端。 在初始化期间,Vault 生成内存中的主密钥并应用 Shamir 的机密共享算法将该主密钥分解为执行数量个密钥分片,以便这些密钥分片的一定数量的子集聚集在一起时可以重新生成主密钥。这些密钥在 Vault 的文档中通常称为“解封密钥”。 无法针对已经初始化的 Vault 集群运行此命令。 例子 使用默认设置开始初始化流程: $ vault operator init 使用 pgp 密钥加密解封密钥来初始化: $ vault operator init \\ -key-shares=3 \\ -key-threshold=2 \\ -pgp-keys=\"keybase:hashicorp,keybase:jefferai,keybase:sethvargo\" 使用非默认阈值和恢复密钥数量初始化自动解封,并使用 pgp 密钥加密恢复密钥: $ vault operator init \\ -recovery-shares=7 \\ -recovery-threshold=4 \\ -recovery-pgp-keys=\"keybase:jeff,keybase:chris,keybase:brian,keybase:calvin,keybase:matthew,keybase:vishal,keybase:nick\" 使用 pgp 密钥加密初始的根令牌: vault operator init -root-token-pgp-key=\"keybase:hashicorp\" 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 命令选项 -key-shares (int: 5) - 将生成的 Master Key 拆分成多少份分片。该值即生成的解封密钥的个数。该标志的别名为 -n。 -key-threshold (int: 3) - 重建 Master Key 所需要的解封密钥个数。该值必须小于等于 -key-shares。该标志的别名为 -t。 -pgp-keys (string: \"...\") - 包含 PGP 公钥的磁盘文件路径的列表,或是 Keybase 用户名的列表(使用格式 keybase:),均由逗号分隔。如果指定该参数,生成的解封密钥将按照此列表中指定的顺序进行加密和 base64 编码。条目数必须与 -key-shares 匹配,除非使用 -stored-shares。 -root-token-pgp-key (string: \"\") - 包含PGP 公钥的磁盘文件路径,或是 Keybase 用户名(使用格式 keybase:)。如果指定该参数,生成的根令牌将使用指定的公钥进行加密和 base64 编码。 -status (bool: false) - 打印当前初始化的状态。返回码 0 代表 Vault 已经初始化。返回码 1 代表发生了错误。返回码 2 代表 Vault 尚未初始化完成。 Consul 选项 -consul-auto (bool: false) - 在高可用模式下使用 Consul 进行自动的服务发现。当 Vault 高可用集群中的所有节点都向 Consul 注册时,启用此选项将根据提供的 -consul-service 值触发自动服务发现。当 Consul 是 Vault 的高可用后端存储时,此功能会自动启用。使用前请先确保设置了正确的 Consul 环境变量(CONSUL_HTTP_ADDR 等)。当仅发现一台 Vault 服务器时,它将自动初始化。当发现多个 Vault 服务器时,它们将分别输出以供选择。默认值为 fasle。 -consul-service (string: \"vault\") - Vault 服务在 Consul 中注册的服务名称。 HSM(硬件加密模块)和 KMS(Key Management System)选项 -recovery-pgp-key (string: \"...\") - 行为类似 -pgp-keys,但针对的是恢复密钥分片。该参数仅用于自动解封封印(HSM、KMS、Transit)。 -recovery-shares (int: 5) - 将恢复密钥拆分成的分片数。该参数仅用与自动解封封印(HSM、KMS、Transit)。 -recovery-threshold (int: 3) - 将恢复密钥拆分成的分片数。该参数仅用与自动解封封印(HSM、KMS、Transit)。 -stored-shares (int: 0) - 存储在 HSM 上的解封密钥分片数。必须与 -key-shares 一致。 "},"3.命令行/12.4.operator_key-status.html":{"url":"3.命令行/12.4.operator_key-status.html","title":"operator key-status","keywords":"","body":"operator key-status operator key-status 提供有关使用的加密密钥的信息。具体来说是当前密钥期限和密钥安装时间。 例子 获取密钥状态: $ vault operator key-status Key Term 1 Install Time 01 Jan 22 12:31 UTC Encryption Count 35 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 "},"3.命令行/12.5.operator_migrate.html":{"url":"3.命令行/12.5.operator_migrate.html","title":"operator migrate","keywords":"","body":"operator migrate operator migrate 命令在存储后端之间复制数据,以切换 Vault 配置。它直接在存储级别运行,不涉及解密。目标存储后端中的键将被覆盖,并且在迁移操作完成之前不应初始化目标存储后端。除了迁移期间添加的用作同步锁的键之外,源数据不会被修改。 该操作为了确保数据一致性设计为离线操作,如果迁移正在进行,Vault 将不允许启动服务。 例子 迁移所有键: $ vault operator migrate -config migrate.hcl 2018-09-20T14:23:23.656-0700 [INFO ] copied key: data/core/seal-config 2018-09-20T14:23:23.657-0700 [INFO ] copied key: data/core/wrapping/jwtkey 2018-09-20T14:23:23.658-0700 [INFO ] copied key: data/logical/fd1bed89-ffc4-d631-00dd-0696c9f930c6/31c8e6d9-2a17-d98f-bdf1-aa868afa1291/archive/metadata 2018-09-20T14:23:23.660-0700 [INFO ] copied key: data/logical/fd1bed89-ffc4-d631-00dd-0696c9f930c6/31c8e6d9-2a17-d98f-bdf1-aa868afa1291/metadata/5kKFZ4YnzgNfy9UcWOzxxzOMpqlp61rYuq6laqpLQDnB3RawKpqi7yBTrawj1P ... 迁移以一致的、有序的顺序完成。如果迁移在完成之前停止或退出(例如,由于与存储后端的连接故障),它可以从任意键前缀恢复: $ vault operator migrate -config migrate.hcl -start \"data/logical/fd\" 配置文件 operator migrate 命令使用专用配置文件来指定源和目标存储后端。存储节的格式与用于配置 Vault 的格式相同,唯一的区别是需要两个节:storage_source 和 storage_destination。 storage_source \"mysql\" { username = \"user1234\" password = \"secret123!\" database = \"vault\" } storage_destination \"consul\" { address = \"127.0.0.1:8500\" path = \"vault\" } 迁移到集成 Raft 存储 以下配置文件将从 Consul 存储迁移到集成 Raft 存储。Raft 数据将存储在服务器本地文件系统中定义的path的位置上。node_id 来设置节点 ID。cluster_addr 必须设置为该节点的集群主机名。 如果原始后端存储配置的 ha_storage 为“raft”,则需要为 storage_destination 中的 path 和迁移后节点的新配置声明不同的 path。 storage_source \"consul\" { address = \"127.0.0.1:8500\" path = \"vault\" } storage_destination \"raft\" { path = \"/path/to/raft/data\" node_id = \"raft_node_1\" } cluster_addr = \"http://127.0.0.1:8201\" 运行迁移 Vault 在迁移过程中需要处于离线状态。首先,停止 Vault 服务。然后,在希望设置为新 Vault 节点的服务器上运行迁移。 $ vault operator migrate -config migrate.hcl 2018-09-20T14:23:23.656-0700 [INFO ] copied key: data/core/seal-config 2018-09-20T14:23:23.657-0700 [INFO ] copied key: data/core/wrapping/jwtkey 2018-09-20T14:23:23.658-0700 [INFO ] copied key: data/logical/fd1bed89-ffc4-d631-00dd-0696c9f930c6/31c8e6d9-2a17-d98f-bdf1-aa868afa1291/archive/metadata 2018-09-20T14:23:23.660-0700 [INFO ] copied key: data/logical/fd1bed89-ffc4-d631-00dd-0696c9f930c6/31c8e6d9-2a17-d98f-bdf1-aa868afa1291/metadata/5kKFZ4YnzgNfy9UcWOzxxzOMpqlp61rYuq6laqpLQDnB3RawKpqi7yBTrawj1P ... 迁移完成后,数据将存储在本地文件系统上。要让 Vault 使用新的存储后端,请按照 Raft 存储配置文档中的说明更新 Vault 的配置文件。然后启动并解封 Vault 服务。 添加新节点 迁移后,Raft 集群将只有一个节点。其他节点需要一一加入此集群。 如果集群之前 ha_storage 配置为 \"raft\" 而启用了高可用,则节点必须在解封之前重新加入迁移后的节点。 可用标志 operator migrate 命令可以使用一下标志: -config (string: ) - 迁移配置文件的路径 -start (string: \"\") - 迁移的起始键前缀。只有拥有该前缀或是排序其后的键会被拷贝。 -reset - 重置迁移锁。迁移过程中会添加一个迁移锁文件防止在迁移过程中 Vault 服务被启动或是启用另一个迁移过程。如果设置 -reset 标志会删除该锁文件(如果它还存在的话)。 "},"3.命令行/12.6.operator_raft.html":{"url":"3.命令行/12.6.operator_raft.html","title":"operator raft","keywords":"","body":"operator raft 该命令包含一组管理集成 Raft 存储后端的子命令。 Usage: vault operator raft [options] [args] This command groups subcommands for operators interacting with the Vault integrated Raft storage backend. Most users will not need to interact with these commands. Here are a few examples of the Raft operator commands: Subcommands: join Joins a node to the Raft cluster list-peers Returns the Raft peer set remove-peer Removes a node from the Raft cluster snapshot Restores and saves snapshots from the Raft cluster join 该命令用于加入一个新节点作为 Raft 集群的节点。要成功加入一个集群,集群中必须至少有一个成员。如果正在使用 Shamir 封印,则在加入过程前后需要提供解封密钥,具体过程取决于它是否专用于高可用模式。 如果将 storage 设置为 raft,解封前必须加入集群,并且必须提供 leader-api-addr 参数。如果将 ha_storage 设置为 raft,加入集群前必须先解封节点,且不得提供 leader-api-addr。 Usage: vault operator raft join [options] Join the current node as a peer to the Raft cluster by providing the address of the Raft leader node. $ vault operator raft join \"http://127.0.0.2:8200\" join 命令还允许使用 cloud auto-join 配置,而不是静态 IP 地址或主机名。设置后,Vault 将尝试根据提供的自动加入配置自动发现和解析潜在的领导者地址。 Vault 使用 go-discover 来支持自动加入功能。相关详细信息,请参阅 go-discover README。 默认情况下,Vault 将尝试使用 HTTPS 协议和 8200 端口访问发现的对等点。管理员可以分别通过 --auto-join-scheme 和 --auto-join-port 命令行标志覆盖这些设置。 Usage: vault operator raft join [options] Join the current node as a peer to the Raft cluster by providing cloud auto-join metadata configuration. $ vault operator raft join \"provider=aws region=eu-west-1 ...\" 参数 operator raft join 命令可以使用一下标志: -leader-ca-cert (string: \"\") - 与 Raft 领导者通信使用的 CA 证书 -leader-client-cert (string: \"\") - 与 Raft 领导者进行身份验证所使用的客户端证书 -leader-client-key (string: \"\") - 与 Raft 领导者进行身份验证所使用的客户端密钥 -non-voter (bool: false)(企业版专用) - 该标志用于使服务器不参与 Raft 投票,只接收数据复制流。在需要对服务器进行大量读取的情况下,这可用于为集群增加读取可扩展性。默认值为 false。 -retry (bool: false) - 失败时不断重试加入 Raft 集群。默认值为 false。 list-peers 该命令用于列出 Raft 集群中的节点集合。 Usage: vault operator raft list-peers Provides the details of all the peers in the Raft cluster. $ vault operator raft list-peers 输出样例 { ... \"data\": { \"config\": { \"index\": 62, \"servers\": [ { \"address\": \"127.0.0.2:8201\", \"leader\": true, \"node_id\": \"node1\", \"protocol_version\": \"3\", \"voter\": true }, { \"address\": \"127.0.0.4:8201\", \"leader\": false, \"node_id\": \"node3\", \"protocol_version\": \"3\", \"voter\": true } ] } } } remove-peer 该命令从 Raft 集群中删除一个节点。在某些情况下,即使服务器不再存在并且集群已经知晓这一情况,在 Raft 配置中也可能会留下该节点,此命令可用于删除故障服务器,使其不再影响 Raft 仲裁。 Usage: vault operator raft remove-peer Removes a node from the Raft cluster. $ vault operator raft remove-peer node1 snapshot 该命令提供了一组与集成 Raft 存储后端的快照功能交互的子命令。有两个子命令:save 和 restore。 Usage: vault operator raft snapshot [options] [args] This command groups subcommands for operators interacting with the snapshot functionality of the integrated Raft storage backend. Subcommands: restore Installs the provided snapshot, returning the cluster to the state defined in it save Saves a snapshot of the current state of the Raft cluster into a file snapshot save 为 Vault 数据创建一份快照。快照可以将 Vault 数据精准还原到快照被创建的那个时刻。 Usage: vault operator raft snapshot save Saves a snapshot of the current state of the Raft cluster into a file. $ vault operator raft snapshot save raft.snap 注意,当仅将 ha_storage 设置为 raft 时无法使用快照。 snapshot restore 还原 vault operator raft snapshot save 命令保存的快照。 Usage: vault operator raft snapshot restore Installs the provided snapshot, returning the cluster to the state defined in it. $ vault operator raft snapshot restore raft.snap autopilot 该命令提供了一组与集成 Raft 存储后端的自动驾驶功能交互的子命令。支持三个子命令:get-config、set-config 和 state。 有关自动驾驶仪功能的更详细概述,请参阅相关基本概念页面。 Usage: vault operator raft autopilot [options] [args] This command groups subcommands for operators interacting with the autopilot functionality of the integrated Raft storage backend. Subcommands: get-config Returns the configuration of the autopilot subsystem under integrated storage set-config Modify the configuration of the autopilot subsystem under integrated storage state Displays the state of the raft cluster under integrated storage as seen by autopilot autopilot state 显示自动驾驶所观察到的集成存储下 Raft 集群的状态。它显示自动驾驶认为集群是否健康,以及有多少个节点发生故障将会导致集群变得不健康(“Failure Tolerance”)。 状态包括按 nodeID 和 IP 地址列出的所有服务器的列表。 Last Index 指示每个节点上的状态与领导者的状态有多接近。 一个节点可以具有“leader”、“voter”和“non-voter”的状态。 Usage: vault operator raft autopilot state Displays the state of the raft cluster under integrated storage as seen by autopilot. $ vault operator raft autopilot state 输出样例 Healthy: true Failure Tolerance: 1 Leader: raft1 Voters: raft1 raft2 raft3 Servers: raft1 Name: raft1 Address: 127.0.0.1:8201 Status: leader Node Status: alive Healthy: true Last Contact: 0s Last Term: 3 Last Index: 38 raft2 Name: raft2 Address: 127.0.0.2:8201 Status: voter Node Status: alive Healthy: true Last Contact: 2.514176729s Last Term: 3 Last Index: 38 autopilot get-config 返回集成存储下自动驾驶子系统的配置。 Usage: vault operator raft autopilot get-config Returns the configuration of the autopilot subsystem under integrated storage. $ vault operator raft autopilot get-config autopilot set-config 修改集成存储的自动驾驶子系统的配置: Usage: vault operator raft autopilot set-config [options] Modify the configuration of the autopilot subsystem under integrated storage. $ vault operator raft autopilot set-config -server-stabilization-time 10s 该命令可以使用以下标志: cleanup-dead-servers (bool) - 控制是否定期或在新服务器加入时从 Raft 集群节点列表中删除死服务器。这要求设置 min-quorum last-contact-threshold (string) - 限制在多久没有联系过领导者节点后服务节点可被认为不健康 dead-server-last-contact-threshold (string) - 限制在多久没有联系过领导者节点后服务节点可被认为故障。只有在设置了 cleanup_dead_servers 标志时才有用 max-trailing-logs (int) - 服务实例的 Raft 记录落后于领导者多少条时可以被认为不健康 min-quorum (int) - 集群在清理死服务器前集群需要保持的最小节点数。该值至少为 3。只适用于投票节点 server-stabilization-time (string) - 服务器在成为投票者之前必须处于稳定、健康状态的最短时间。在此之前,它将作为集群中的对等节点可见,但作为非投票者,这意味着它不可参加仲裁 "},"3.命令行/12.7.operator_rekey.html":{"url":"3.命令行/12.7.operator_rekey.html","title":"opeartor rekey","keywords":"","body":"operator rekey operator rekey 命令生成一组新的解封密钥。这可以选择性地更改密钥分片的总数或重建主密钥所需的密钥分片的阈值。此操作不需要停机,但它需要解封 Vault 并提供足够数量个现有的解封密钥。 可以在命令行上直接提供解封密钥作为命令的参数。如果密钥指定为 \"-\",则命令将从 stdin 读取。如果 TTY 可用,该命令将提示输入文本。 例子 开始重新生成解封密钥: $ vault operator rekey \\ -init \\ -key-shares=15 \\ -key-threshold=9 开始重新生成密钥,并且启动验证程序: $ vault operator rekey \\ -init \\ -key-shares=15 \\ -key-threshold=9 为自动解封的 Vault 重新生成恢复密钥,并且使用 PGP 加密生成的恢复密钥: $ vault operator rekey \\ -target=recovery \\ -init \\ -pgp-keys=keybase:grahamhashicorp -key-shares=1 -key-threshold=1 将 PGP 加密过的密钥保存在 Vault 内核: $ vault operator rekey \\ -init \\ -pgp-keys=\"...\" \\ -backup 取回备份的解封密钥: ``shell $ vault operator rekey -backup-retrieve 删除备份的解封密钥: ```shell $ vault operator rekey -backup-delete 使用验证随机字符来验证正在重新生成密钥: $ vault operator rekey -verify -nonce=\"...\" 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 命令选项 -cancel (bool: false) - 重置重新生成密钥的进程。这会丢弃已提交的解封密钥以及配置。默认为 false。 -init (bool: false) - 开始重新生成密钥的操作。可以在没有其他重新生成密钥操作进行的时候启动。可以用 -key-shares 以及 -key-threshold 标志定制新的密钥分片数以及阈值。 -key-shares (int: 5) - 主密钥拆分的密钥分片数。该值就是生成的解封密钥的个数。该参数别名是 -n。 -key-threshold (int: 3) - 重建主密钥所需的密钥分片数。该值必须小于等于 -key-shares。该参数别名是 -t。 -nonce (string: \"\") - 重建过程需使用的随机字符串。输入每个解封密钥时都必须输入相同的随机字符串。 -pgp-keys (string: \"...\") - 逗号分隔的含有 PGP 公钥文件的路径或是 Keybase 用户名(使用 keybase: 的格式)列表。如果给定该参数,生成的解封密钥会按照列表顺序使用对应的公钥进行加密后进行 base64 编码返回。 -status (bool: false) - 在不提供解封密钥的情况下打印当前尝试的状态。默认为 false。 -target (string: \"barrier\") - 重新生成的密钥类型。只有在启用了 HSM 功能或是使用了自动解封的情况下可以设置为 recovery。 -verify (bool: false) - 表明在 -init 阶段重建密钥的验证过程已经启动。配合 -nonce 选项可以判定给定的随机字符串元被用于了验证过程。 备份选项 -backup (bool: false) - 将 PGP 加密过的解封密钥备份在 Vault 内核中。当发生失败时可以找回备份的密钥,或是成功后抛弃备份的密钥。该选项只有在现存的解封密钥是通过 PGP 加密的情况下有效。 -backup-delete (bool: false) - 删除备份的解封密钥 -backup-retrieve (bool: false) - 找回备份的解封密钥。该选项只有在现存的解封密钥是通过 PGP 加密的情况下有效。 "},"3.命令行/12.8.operator_rotate.html":{"url":"3.命令行/12.8.operator_rotate.html","title":"operator rotate","keywords":"","body":"operator rotate operator rotate 命令通过轮替底层加密密钥来保护写入存储后端的数据。它将会在密钥环中安装一个新密钥。这个新密钥用于加密新数据,而环中的旧密钥用于解密旧数据。 这是在线操作,不会导致停机。此命令按集群(而不是按服务器)运行,因为高可用模式下的 Vault 服务器共享相同的存储后端。 例子 轮替 Vault 的加密密钥: $ vault operator rotate Key Term 3 Install Time 01 May 17 10:30 UTC 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 "},"3.命令行/12.9.operator_seal.html":{"url":"3.命令行/12.9.operator_seal.html","title":"operator seal","keywords":"","body":"operator seal operator seal 命令封印 Vault 服务器。封印使得 Vault 服务器停止响应任何操作,直到它被解封。封印后,Vault 服务器会丢弃其内存中用来解锁数据的主密钥,因此它在物理上无法响应请求,直到解封。 如果正在进行解封,封印 Vault 将重置解封进度。用户将不得不再次重新输入他们的密钥分片。 如果 Vault 服务器已封印,则此命令不执行任何操作。 更多有关封印/解封的详情,请阅读相关章节。 例子 封印一个 Vault 服务: $ vault operator seal Success! Vault is sealed. 可用标志 该命令只有标准的全局标志位可用。 "},"3.命令行/12.10.operator_step-down.html":{"url":"3.命令行/12.10.operator_step-down.html","title":"operator step-down","keywords":"","body":"operator step-down operator step-down 会强制高可用集群中的主节点从主动辞职下台。当针对非主节点(即备用或性能备用节点)执行时,请求将转发到主节点。虽然受影响的节点在尝试再次获取领导锁之前会有延迟,但如果没有其他 Vault 节点事先获取锁,则同一节点可能会重新获取锁并再次变为领导者。 例子 强迫 Vault 节点辞任领导者: $ vault operator step-down Success! Stepped down: http://127.0.0.1:8200 可用标志 该命令只有标准的全局标志位可用。 "},"3.命令行/12.11.operator_unseal.html":{"url":"3.命令行/12.11.operator_unseal.html","title":"operator unseal","keywords":"","body":"operator unseal operator unseal 允许用户提供主密钥的一份分片来解封 Vault 服务器。 Vault 启动时处于封印状态。在解封之前,它无法执行操作。此命令接受一部分主密钥(“解封密钥”)。 解封密钥可以作为命令的参数提供,但不建议这样做,因为解封密钥将在命令行的历史记录中可见: $ vault operator unseal IXyR0OJnSFobekZMMCKCoVEpT7wI6l+USMzE3IcyDyo= 与之相反的是运行不带参数的命令,它会提示输入密钥: $ vault operator unseal Key (will be hidden): IXyR0OJnSFobekZMMCKCoVEpT7wI6l+USMzE3IcyDyo= 例子 提供一份解封密钥: $ vault operator unseal Key (will be hidden): Sealed: false Key Shares: 1 Key Threshold: 1 Unseal Progress: 0 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 命令选项 -migrate (bool: false) - 表明提供密钥的意图是用来迁移封印的。 -reset (bool: false) - 抛弃先前输入解封密钥 "},"3.命令行/12.12.operator_usage.html":{"url":"3.命令行/12.12.operator_usage.html","title":"operator usage","keywords":"","body":"operator usage operator usage 命令允许管理员检索默认报告周期或特定月份时间范围内的客户端计数报告。 命令输出将按不同实体、非实体令牌和每个命名空间的活跃客户端总数以及整个集群的总数列出客户端。有可能显示在请求的时间范围内没有可用的数据,这可能是因为客户端计数报告已禁用、时间范围过去太远,或者自启用该功能以来尚未收集到任何数据。 例子 获取默认报告周期内的客户端计数: $ vault operator usage Period start: 2019-11-01T00:00:00Z Period end: 2020-10-31T23:59:59Z Namespace path Distinct entities Non-Entity tokens Active clients -------------- ----------------- ----------------- -------------- [root] 1581 332 1913 Total 1581 332 1913 获取指定月份的客户端计数: $ vault operator usage -start-time=2020-01 -end-time=2020-01 Period start: 2020-01-01T00:00:00Z Period end: 2020-01-31T23:59:59Z Namespace path Distinct entities Non-Entity tokens Active clients -------------- ----------------- ----------------- -------------- [root] 954 176 1130 Total 954 176 1130 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 命令选项 -start-time (date) - 生成报告的开始月份。必须按照 YYYY-MM-DD, YYYY-MM-DD的格式,或是完整的 RFC3339 时间戳,或是 Unix 纪元时间戳来输入。默认为 -end-time 之前可配置 -default_report_months。 -end-time (date: previous month) - 生成报告的结束月份。默认为上个月的结束时间。 输出显示报告的确切时间范围,如果不足一月,或者可用报告是所请求月份的子集,则该时间范围可能与输入参数不匹配。 "},"3.命令行/13.path-help.html":{"url":"3.命令行/13.path-help.html","title":"path-help","keywords":"","body":"path-help path-help 命令查询指定路径对应的 API 的帮助信息。 Vault 中的所有端点都提供 Markdown 格式的内置帮助。这包括系统、机密引擎和身份验证方法的路径。 帮助系统是学习如何使用 Vault 中各种系统的最简单方法,还允许我们发现新路径。 在使用 path-help 之前,了解 Vault 中的“路径”很重要。路径是用于 vault read、vault write 等的参数,比方说 secret/foo 或者 aws/config/root。可用的路径取决于使用的机密引擎。因此,交互式帮助是查找可用路径不可或缺的工具。 要了解有哪些可用路径,请使用 vault path-help PATH。例如,如果您启用了 AWS 机密引擎,则可以使用 vault path-help aws 来查找该后端支持的路径。路径用正则表达式显示,这会使它们难以解析,但它们也非常精确。 例子 获取 K/V 机密引擎的帮助信息: $ vault path-help secret ## DESCRIPTION The KV backend reads and writes arbitrary secrets to the backend. The secrets are encrypted/decrypted by Vault: they are never stored unencrypted in the backend and the backend never has an opportunity to see the unencrypted value. Leases can be set on a per-secret basis. These leases will be sent down when that secret is read, and it is assumed that some outside process will revoke and/or replace the secret at that path. ## PATHS The following paths are supported by this backend. To view help for any of the paths below, use the help command with any route matching the path pattern. Note that depending on the policy of your auth token, you may or may not be able to access certain paths. ^.*$ Pass-through secret storage to the storage backend, allowing you to read/write arbitrary data into secret storage. 找到感兴趣的路径后,您可以使用 vault path-help 了解有关它的更多信息,其中“path”是与输出的帮助信息中的正则表达式之一匹配的路径。 可用标志 该命令只有标准的全局标志位可用。 "},"3.命令行/14.plugin.html":{"url":"3.命令行/14.plugin.html","title":"plugin","keywords":"","body":"plugin plugin 命令包含一组与 Vault 插件以及插件目录交互的子命令。 例子 列出目录中所有可用插件: $ vault plugin list Plugins ------- my-custom-plugin # ... 将一个新插件注册到目录中 $ vault plugin register \\ -sha256=d3f0a8be02f6c074cf38c9c99d4d04c9c6466249 \\ my-custom-plugin Success! Registered plugin: my-custom-plugin 获取目录中某个插件的信息: $ vault plugin info my-custom-plugin Key Value --- ----- command my-custom-plugin name my-custom-plugin sha256 d3f0a8be02f6c074cf38c9c99d4d04c9c6466249 可用子命令 Usage: vault plugin [options] [args] # ... Subcommands: deregister Deregister an existing plugin in the catalog info Read information about a plugin in the catalog list Lists available plugins register Registers a new plugin in the catalog reload Reload mounted plugin backend 每种子命令的详细使用方法,请阅读对应的章节。 "},"3.命令行/14.1.plugin_deregister.html":{"url":"3.命令行/14.1.plugin_deregister.html","title":"plugin deregister","keywords":"","body":"plugin deregister plugin deregister 命令从 Vault 的插件目录中卸载一个现有插件。如果插件不存在,不会返回错误。插件类型必须是 auth、database 或 secret 中的一种。 例子 卸载一个插件: $ vault plugin deregister auth my-custom-plugin Success! Deregistered plugin (if it was registered): my-custom-plugin 可用标志 该命令只有标准的全局标志位可用。 "},"3.命令行/14.2.plugin_info.html":{"url":"3.命令行/14.2.plugin_info.html","title":"plugin info","keywords":"","body":"plugin info plugin info 显示相关目录中某一插件的信息。插件类型必须是 auth、database 或 secret 中的一种。 例子 展示某一插件的信息: $ vault plugin info auth my-custom-plugin Key Value --- ----- args [] builtin false command my-custom-plugin name my-custom-plugin sha256 d3f0a8be02f6c074cf38c9c99d4d04c9c6466249 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -field (string: \"\") 只返回指定字段的数据。此选项优先级高于其他格式指令。结果的尾部没有换行符,使其适合通过管道与其他进程集成使用。 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 "},"3.命令行/14.3.plugin_list.html":{"url":"3.命令行/14.3.plugin_list.html","title":"plugin list","keywords":"","body":"plugin list plugin list 命令列出插件目录中的所有可用插件。它可以单独使用,也可以搭配 auth、database 或 secret 等类型一起使用。 例子 列出目录中所有可用插件 $ vault plugin list Plugins ------- my-custom-plugin # ... $ vault plugin list database Plugins ------- cassandra-database-plugin # ... 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 "},"3.命令行/14.4.plugin_register.html":{"url":"3.命令行/14.4.plugin_register.html","title":"plugin register","keywords":"","body":"plugin register plugin register 命令在 Vault 的插件目录中注册一个新插件。插件类型必须是 auth、database 或 secret 中的一种。 例子 注册一个插件 $ vault plugin register \\ -sha256=d3f0a8be02f6c074cf38c9c99d4d04c9c6466249 \\ auth my-custom-plugin Success! Registered plugin: my-custom-plugin 使用定制化参数注册插件: $ vault plugin register \\ -sha256=d3f0a8be02f6c074cf38c9c99d4d04c9c6466249 \\ -args=--with-glibc,--with-curl-bindings \\ auth my-custom-plugin 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 命令选项 -sha256 (string: ) - 插件二进制文件的 SHA256 校验码 -args (string: \"\") - 每次调用时传递给插件二进制文件的一组参数。使用逗号分隔多个参数。 -command (string: \"\") - 要调用的二进制文件的名字。该名字默认情况下就是插件的名字。 "},"3.命令行/14.5.plugin_reload.html":{"url":"3.命令行/14.5.plugin_reload.html","title":"plugin reload","keywords":"","body":"plugin reload plugin reload 命令用来重新加载已挂载的插件后端。需要提供插件名(plugin)或者是想要挂载的路径(mounts),但只能提供其一。 例子 重载插件: $ vault plugin reload -plugin my-custom-plugin Success! Reloaded plugin: my-custom-plugin 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 命令选项 -plugin (string: \"\") - 要重载的插件的名字,就是在插件目录中注册的名字 -mounts (array: []) - 一组逗号分隔的挂载路径字符串,记录的是要重载的插件后端挂载的路径 -scope (string: \"\") - 重新加载的范围。如果只重新加载本机的插件,请忽略此参数;如果要重载多个 Vault 集群的插件,请使用 global。 "},"3.命令行/15.policy.html":{"url":"3.命令行/15.policy.html","title":"policy","keywords":"","body":"policy policy 命令包含一组与 Vault 交互操作策略的子命令。用户可以编写、读取和列出 Vault 中的策略。 有关策略的详细信息,请阅读相关章节。 例子 列出所有启用的策略: $ vault policy list 从本地磁盘文件读取内容后创建名为 \"my-policy\" 的策略: $ vault policy write my-policy ./my-policy.hcl 删除名为 \"my-policy\" 的策略: $ vault policy delete my-policy 可用子命令 Usage: vault policy [options] [args] # ... Subcommands: delete Deletes a policy by name list Lists the installed policies read Prints the contents of a policy write Uploads a named policy from a file 每种子命令的详细使用方法,请阅读对应的章节。 "},"3.命令行/15.1.policy_delete.html":{"url":"3.命令行/15.1.policy_delete.html","title":"policy delete","keywords":"","body":"policy delete policy delete 命令从 Vault 服务中删除指定名称的策略。一旦策略被删除,所有关联该策略的令牌都将立即受到影响。 请注意,无法删除 \"default\" 和 \"root\" 策略。它们是内建策略。 例子 删除名为 \"my-policy\" 的策略: $ vault policy delete my-policy 可用标志 该命令只有标准的全局标志位可用。 "},"3.命令行/15.2.policy_fmt.html":{"url":"3.命令行/15.2.policy_fmt.html","title":"policy fmt","keywords":"","body":"policy fmt policy fmt 命令将本地策略文件格式化为符合规范的格式。该命令将输出格式正确的策略文件来覆盖指定路径上的文件。 例子 格式化本地文件 \"my-policy.hcl\": $ vault policy fmt my-policy.hcl 可用标志 该命令只有标准的全局标志位可用。 "},"3.命令行/15.3.policy_list.html":{"url":"3.命令行/15.3.policy_list.html","title":"policy list","keywords":"","body":"policy list policy list 命令列出 Vault 服务器上的策略名。 例子 列出可用策略: $ vault policy list default root 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 "},"3.命令行/15.4.policy_read.html":{"url":"3.命令行/15.4.policy_read.html","title":"policy read","keywords":"","body":"policy read policy read 命令打印 Vault 中指定名称的策略的内容以及元数据。如果策略不存在则返回错误。 例子 读取名为 \"my-policy\" 的策略: $ vault policy read my-policy 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 "},"3.命令行/15.5.policy_write.html":{"url":"3.命令行/15.5.policy_write.html","title":"policy write","keywords":"","body":"policy write policy write 命令将从本机指定路径文件或是 stdin 读取的策略上传至 Vault 中指定名称的策略中。如果路径为 \"-\",那么将从 stdin 读取策略。否则就从本机磁盘的指定路径上读取文件。 有关策略语法的详细信息,请阅读相关章节。 例子 从本机 \"/tmp/policy.hcl\" 读取策略上传至 \"my-policy\": $ vault policy write my-policy /tmp/policy.hcl 从 stdin 读取策略上传: $ cat my-policy.hcl | vault policy write my-policy - 可用标志 该命令只有标准的全局标志位可用。 "},"3.命令行/16.read.html":{"url":"3.命令行/16.read.html","title":"read","keywords":"","body":"read read 命令读取 Vault 中指定路径的数据。该命令可以用来读取机密、生成动态凭据、获取配置细节以及其他操作。 要获取有关路径和例子的完整列表,请读取相关机密引擎的文档。 例子 从静态机密引擎中读取机密: $ vault read secret/my-secret Key Value --- ----- refresh_interval 768h foo bar 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -field (string: \"\") 只返回指定字段的数据。此选项优先级高于其他格式指令。结果的尾部没有换行符,使其适合通过管道与其他进程集成使用。 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 "},"3.命令行/17.secrets.html":{"url":"3.命令行/17.secrets.html","title":"secrets","keywords":"","body":"secrets secrets 命令包含一组与 Vault 机密引擎互动的子密令。每一种机密引擎的行为各不相同。请阅读相关机密引擎的文档获取更多信息。 有些机密引擎会保存持久化数据,而有些则只会在内存中短暂地处理数据,还有一些会创建动态凭据。机密引擎挂载后一般需要进行配置。详细的配置信息请阅读相关机密引擎文档。 例子 启用机密引擎: $ vault secrets enable database Success! Enabled the database secrets engine at: database/ 列出所有机密引擎: $ vault secrets list Path Type Description ---- ---- ----------- cubbyhole/ cubbyhole per-token private secret storage database/ database n/a secret/ kv key/value secret storage sys/ system system endpoints used for control, policy and debugging 将一个机密引擎移动到新地址: $ vault secrets move database/ db-prod/ Success! Moved secrets engine database/ to: db-prod/ 调整一个机密引擎: $ vault secrets tune -max-lease-ttl=30m db-prod/ Success! Tuned the secrets engine at: db-prod/ 禁用一个机密引擎: $ vault secrets disable db-prod/ Success! Disabled the secrets engine (if it existed) at: db-prod/ 可用子命令 Usage: vault secrets [options] [args] # ... Subcommands: disable Disable a secrets engine enable Enable a secrets engine list List enabled secrets engines move Move a secrets engine to a new path tune Tune a secrets engine configuration 每种子命令的详细使用方法,请阅读对应的章节。 "},"3.命令行/17.1.secrets_disable.html":{"url":"3.命令行/17.1.secrets_disable.html","title":"secrets disable","keywords":"","body":"secrets disable secrets disable 命令禁用指定路径上的机密引擎。使用的参数对应的是启用引擎的路径而不是引擎类型。所有该引擎创建的机密在引擎被禁用时都会被吊销,数据会被删除。 由于禁用引擎时所有相关机密都将被立即吊销,所以在吊销一个关联了大量机密的机密引擎时要格外小心,这有可能对系统造成很大的负载。 例子 禁用 aws/ 上的机密引擎: $ vault secrets disable aws/ 可用标志 该命令只有标准的全局标志位可用。 "},"3.命令行/17.2.secrets_enable.html":{"url":"3.命令行/17.2.secrets_enable.html","title":"secrets enable","keywords":"","body":"secrets enable secrets enable 命令在指定路径上启用一个机密引擎。如果指定路径上已挂载机密引擎,则返回错误。机密引擎在启用后,通常需要进行配置。不同的机密引擎配置各异。 默认情况下,机密引擎在与其类型对应的路径上启用,但用户可以使用 -path 选项自定义路径。 有些机密引擎会保存持久化数据,而有些则只会在内存中短暂地处理数据,还有一些会创建动态凭据。机密引擎挂载后一般需要进行配置。详细的配置信息请阅读相关机密引擎文档。 例子 在 aws/ 上启用 AWS 机密引擎: $ vault secrets enable aws Success! Enabled the aws secrets engine at: aws/ 在 ssh-prod/ 上启用 SSH 机密引擎: $ vault secrets enable -path=ssh-prod ssh 使用显式设定 30 分钟的 TTL 的配置启用 database 机密引擎: $ vault secrets enable -max-lease-ttl=30m database 启用一个自定义插件(在该插件被注册到插件中心后): $ vault secrets enable -path=my-secrets my-plugin 可用标志 -audit-non-hmac-request-keys (string: \"\") - 指定在请求的数据对象中的哪些键不会被审计设备 HMAC 哈希。请注意,可以通过多次使用该选项来指定多个键,每次指定一个键 -audit-non-hmac-response-keys (string: \"\") - 指定在响应的数据对象中的哪些键不会被审计设备 HMAC 哈希。请注意,可以通过多次使用该选项来指定多个键,每次指定一个键 -default-lease-ttl (duration: \"\") - 该机密引擎生成的令牌租约的默认 TTL。如果未指定,则默认为 Vault 服务器全局配置的默认租约 TTL,或先前为该机密引擎配置过的值 -description (string: \"\") - 关于此身份验证方法的易于阅读的描述 -force-no-cache (bool: false) - 强制机密引擎禁用缓存。如果未加指定,则默认为 Vault 服务器全局配置的缓存设置。该参数不会影响底层存储对加密数据的缓存 -local (bool: false) - 将此机密引擎标记为本地可用。本地可用的引擎数据不会被复制或是被复制机制删除 -max-lease-ttl (duration: \"\") - 该机密引擎租约的最大 TTL 约束,如果未加指定,则默认为 Vault 服务器全局配置的最大租约 TTL 设置 -path (string: \"\") - 可以访问机密引擎的路径。所有机密引擎的路径都必须是唯一的。默认值为机密引擎的类型名称 -passthrough-request-headers (string: \"\") - 将要发送到机密引擎的请求中的标头值。请注意,可以通过多次使用该选项来指定多个键,每次指定一个键 -allowed-response-headers (string: \"\") - 允许机密引擎设置在响应上的标头值。请注意,可以通过多次使用该选项来指定多个键,每次指定一个键 "},"3.命令行/17.3.secrets_list.html":{"url":"3.命令行/17.3.secrets_list.html","title":"secrets list","keywords":"","body":"secrets list secrets list 命令列出 Vault 服务器上启用了的机密引擎。该命令同样输出相关启用路径的信息,包括配置的 TTL 和易于阅读的描述。TTL 值为 system 表示正在使用系统默认值。 例子 列出所有启用了的机密引擎: $ vault secrets list Path Type Description ---- ---- ----------- cubbyhole/ cubbyhole per-token private secret storage secret/ kv key/value secret storage sys/ system system endpoints used for control, policy and debugging 列出所有启用了的机密引擎及其详细信息: $ vault secrets list -detailed Path Type Accessor Plugin Default TTL Max TTL Force No Cache Replication Description ---- ---- -------- ------ ----------- ------- -------------- ----------- ----------- cubbyhole/ cubbyhole cubbyhole_10fbb584 n/a n/a n/a false local per-token private secret storage secret/ kv kv_167ce199 n/a system system false replicated key/value secret storage sys/ system system_a9fd745d n/a n/a n/a false replicated system endpoints used for control, policy and debugging 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -format (string: \"table\") - 按照指定格式打印输出。合法的格式有 table、json 或 yaml。也可以通过环境变量 VAULT_FORMAT 设定该值。 命令选项 -detailed (bool: false) - 是否打印如配置以及复制状态等的机密引擎详细信息 "},"3.命令行/17.4.secrets_move.html":{"url":"3.命令行/17.4.secrets_move.html","title":"secrets move","keywords":"","body":"secrets move secrets move 命令将现有的机密引擎移动到新路径。旧机密引擎相关的所有租约都将被吊销,但与机密引擎关联的所有配置都将保留。 移动现有的机密引擎将吊销旧引擎的所有租约。 例子 将现有机密引擎从 secret/ 移动到 kv/: $ vault secrets move secret/ kv/ 可用标志 该命令只有标准的全局标志位可用。 "},"3.命令行/17.5.secrets_tune.html":{"url":"3.命令行/17.5.secrets_tune.html","title":"secrets tune","keywords":"","body":"secrets tune secrets tune 命令调整指定路径上的机密引擎的配置选项。参数使用的是启用机密引擎的路径,而不是引擎类型。 例子 在调整配置之前,首先读取路径 pki/ 上当前的配置信息: $ vault read sys/mounts/pki/tune Key Value --- ----- default_lease_ttl 12h description Example PKI mount force_no_cache false max_lease_ttl 24h 调整 PKI 机密引擎的默认租约,从审计设备的 HMAC 字段中排除掉 common_name 和 serial_number : $ vault secrets tune -default-lease-ttl=18h -audit-non-hmac-request-keys=common_name -audit-non-hmac-response-keys=serial_number pki/ Success! Tuned the secrets engine at: pki/ $ vault read sys/mounts/pki/tune Key Value --- ----- audit_non_hmac_request_keys [common_name] audit_non_hmac_response_keys [serial_number] default_lease_ttl 18h description Example PKI mount force_no_cache false max_lease_ttl 24h 指定多个不执行 HMAC 哈希的字段: $ vault secrets tune -audit-non-hmac-request-keys=common_name -audit-non-hmac-request-keys=ttl pki/ 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: -audit-non-hmac-request-keys (string: \"\") - 指定在请求的数据对象中的哪些键不会被审计设备 HMAC 哈希。请注意,可以通过多次使用该选项来指定多个键,每次指定一个键 -audit-non-hmac-response-keys (string: \"\") - 指定在响应的数据对象中的哪些键不会被审计设备 HMAC 哈希。请注意,可以通过多次使用该选项来指定多个键,每次指定一个键 -default-lease-ttl (duration: \"\") - 该机密引擎生成的租约的默认 TTL。如果未指定,则默认为 Vault 服务器全局配置的默认租约 TTL,或先前为该机密引擎配置过的值 -description (string: \"\") - 设定关于该挂载点的说明。如果设定将会覆盖现有的值 -listing-visibility (string: \"\") - 是否在列出挂载点信息的 UI 界面上展示挂载点信息的开关 -max-lease-ttl (duration: \"\") - 该机密引擎签发的租约最大的 TTL 。默认值为 Vault 服务器全局配置的最大租约 TTL,或是先前机密引擎设置的值。该参数将会覆盖服务器的全局最大 TTL,可以设置成更长或者更短 -passthrough-request-headers (string: \"\") - 将要发送到机密引擎的请求标头值。请注意,可以通过多次使用该选项来指定多个键,每次指定一个键 "},"3.命令行/18.server.html":{"url":"3.命令行/18.server.html","title":"server","keywords":"","body":"server server 命令启动Vault 服务来响应 API 请求。默认情况下,Vault 将在封印状态下启动。 Vault 集群在使用前必须进行初始化,通常是通过 vault operator init 命令。每个 Vault 服务器还必须使用 vault operator unseal 命令或 API 解封,然后服务器才能响应请求。 相关命令信息: operator init operator unseal 例子 使用指定配置文件启动 Vault 服务: $ vault server -config=/etc/vault/config.hcl 使用自定义的根令牌启动 \"dev\" 模式服务: $ vault server -dev -dev-root-token-id=\"root\" 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 命令选项 -config (string: \"\") - 指向配置文件或是包含配置文件的文件夹的路径。该标志可以多次指定加载多个配置文件。如果路径指向的是文件夹,其中所有以 .hcl 或是 .json 结尾的文件都将被加载。 -log-level (string: \"info\") - 日志级别。可以设置的值有:trace、debug、* info、warn 和 err。也可以通过设置环境变量 VAULT_LOG_LEVEL 来设置该值。 -log-format (string: \"standard\") - 日志格式。可以设置的值有 standard 和 json。也可以通过设置环境变量 VAULT_LOG_FORMAT 来设置该值。 开发选项 -dev (bool: false) - 启用开发模式。在该模式下,Vault 启动时就处于解封状态,数据均存储在内存中。如同该模式名称所暗示的那样,请勿在生产环境使用 \"dev\" 模式。 -dev-listen-address (string: \"127.0.0.1:8200\") - 绑定到 \"dev\" 模式的地址。也可以通过设置环境变量 VAULT_DEV_LIESTEN_ADDRESS 来设置该值。 -dev-root-token-id (string: \"\") - 初始的根令牌。该标志只有在 \"dev\" 模式下有效。也可以通过设置环境变量 VAULT_DEV_ROOT_TOKEN_ID 来设置该值。 注意:根令牌 ID 不可以 以 s. 开头。 -dev-no-store-token (string: \"\") - 设置令牌助手不保存根令牌。根令牌只会被展示在命令行的输出中。 dev-plugin-dir (string: \"\") - 允许加载的插件存放的目录路径。该标志仅在 \"dev\" 模式下有效,会自动将目录中所有插件注册到 Vault 服务中。 "},"3.命令行/19.ssh.html":{"url":"3.命令行/19.ssh.html","title":"ssh","keywords":"","body":"ssh ssh 命令建立一条到指定机器的 SSH 连接。 该命令使用某个指定的 SSH 机密引擎进行身份验证并自动建立与主机的 SSH 连接。该操作需要挂载和配置 SSH 机密引擎。 用户必须在本地安装 ssh —— 该命令将传递正确的参数给它,以提供类似 SSH 的一致体验。 例子 使用一次性密码(One Time Password, OTP)模式(需要安装 sshpass 实现全自动化) $ vault ssh -mode=otp -role=my-role user@1.2.3.4 使用 CA 模式的 SSH: $ vault ssh -mode=ca -role=my-role user@1.2.3.4 使用主机密钥验证的 CA 模式的 SSH: $ vault ssh \\ -mode=ca \\ -role=my-role \\ -host-key-mount-point=host-signer \\ -host-key-hostnames=example.com \\ user@example.com 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -field (string: \"\") 只打印指定字段的数据。此选项优先级高于其他格式指令。信息的尾部没有换行符,使其适合通过管道与其他进程集成使用。 -format (string: \"table\") —— 以给定格式打印输出。合法的格式有:table、json 或者 yaml。该标志也可以通过环境变量 VAULT_FORMAT 来设定。 SSH 选项 -mode (string: \"\") - 身份验证模式(例如 ca、dynamic、otp)。 -mount-point (string: \"ssh/\") - SSH 机密引擎的挂载点 -no-exec (bool: false) - 只打印生成的凭据,但不建立连接 -role (string: \"\") - 用来生成密钥的角色名 -strict-host-key-checking (string: \"\") - 传递给 SSH 配置选项 \"StrictHostKeyChecking\" 的值。默认为 ask。也可以通过设置环境变量 VAULT_SSH_STRICT_HOST_KEY_CHECKING 来设置该值 -user-known-hosts-file (string: \"~/.ssh/known_hosts\") - 传递给 SSH 配置选项 \"UserKnownHostsFile\" 的值。也可以通过设置环境变量 VAULT_SSH_USER_KNOWN_HOSTS_FILE 来设置该值 CA 模式选项 -host-key-hostnames (string: \"\") - 为 CA 委派的主机名列表。默认值允许所有域名和 IP。值为使用逗号分隔的列表。也可以通过设置环境变量 VAULT_SSH_HOST_KEY_HOSTNAMES 来设置该值 -host-key-mount-point (string: \"\") - 签发主机密钥的 SSH 机密引擎的挂载点。如果设定该标志,Vault 将生成一个自定义的 \"known_hosts\" 文件并委托给指定路径上 CA 证书,使用该 CA 证书验证 SSH 连接的主机密钥。默认情况下,主机密钥通过使用用户的本地\"known_hosts\" 文件进行验证。此标志强制对密钥执行严格的主机检查并忽略自定义的 \"known_hosts\" 文件。也可以通过设置环境变量 VAULT_SSH_HOST_KEY_MOUNT_POINT 来设置该值 -private-key-path (string: \"~/.ssh/id_rsa\") - 用以执行身份验证的 SSH 私钥文件的路径。该标志必须使用 -public-key-path 指定的公钥对应的私钥文件 -public-key-path (string: \"~/.ssh/id_rsa.pub\") - 发往 Vault 用以签名的 SSH 公钥文件的地址 "},"3.命令行/20.status.html":{"url":"3.命令行/20.status.html","title":"status","keywords":"","body":"status status 命令打印 Vault 当前状态,包括有是否处于封印状态,是否启用了高可用等。该命令无论 Vault 是否封印均可执行。 返回码代表着封印状态: 0 - 已解封 1 - 错误 2 - 封印中 例子 检查状态: $ vault status Sealed: false Key Shares: 5 Key Threshold: 3 Unseal Progress: 0 Unseal Nonce: Version: x.y.z Cluster Name: vault-cluster-49ffd45f Cluster ID: d2dad792-fb99-1c8d-452e-528d073ba205 High-Availability Enabled: false 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -format (string: \"table\") —— 以给定格式打印输出。合法的格式有:table、json 或者 yaml。该标志也可以通过环境变量 VAULT_FORMAT 来设定。 "},"3.命令行/21.token.html":{"url":"3.命令行/21.token.html","title":"token","keywords":"","body":"token token 命令包含一组与 Vault 交互操作令牌的子命令。用户可以创建、查询、续约以及吊销令牌。 有关令牌的更多介绍,请阅读相关章节。 例子 创建一个新令牌: $ vault token create 吊销一个令牌: $ vault token revoke 96ddf4bc-d217-f3ba-f9bd-017055595017 续约令牌; $ vault token renew 96ddf4bc-d217-f3ba-f9bd-017055595017 可用子命令 Usage: vault token [options] [args] # ... Subcommands: capabilities Print capabilities of a token on a path create Create a new token lookup Display information about a token renew Renew a token lease revoke Revoke a token and its children 每种子命令的详细使用方法,请阅读对应的章节。 "},"3.命令行/21.1.token_capabilities.html":{"url":"3.命令行/21.1.token_capabilities.html","title":"token capabilities","keywords":"","body":"token capabilities token capabilities 命令查询某令牌对指定路径拥有的权限。 如果通过参数传递了一个令牌,那么该命令就会使用 \"/sys/capabilities\" 端点和权限。如果没有传递令牌,该命令会使用 \"/sys/capabilities-self\" 端点以及当前登录令牌的权限。 例子 列举本地令牌在 \"secret/foo\" 上的权限: $ vault token capabilities secret/foo read 列举某个令牌在 \"cubbyhole/foo\" 上的权限: $ vault token capabilities 96ddf4bc-d217-f3ba-f9bd-017055595017 database/creds/readonly deny 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -format (string: \"table\") —— 以给定格式打印输出。合法的格式有:table、json 或者 yaml。该标志也可以通过环境变量 VAULT_FORMAT 来设定。 "},"3.命令行/21.2.token_create.html":{"url":"3.命令行/21.2.token_create.html","title":"token create","keywords":"","body":"token create token create 命令创建一个可用于身份验证的新令牌。这个新令牌将成为当前使用的已经身份验证的令牌的子代。生成的令牌将继承当前使用的令牌的所有策略和权限,除非显式定义了要分配给令牌的策略列表的子集。 令牌也可以关联 ttl。如果令牌未关联 ttl,则无法续约。如果令牌关联了 ttl,它将在该时间之后过期,除非被续约。 使用令牌时,与令牌关联的元数据(用 -metadata 指定)会被写入审计日志。 如果指定了角色,则该角色可以覆盖该命令指定的参数。 例子 创建关联了指定策略的令牌: $ vault token create -policy=my-policy -policy=other-policy Key Value --- ----- token 95eba8ed-f6fc-958a-f490-c7fd0eda5e9e token_accessor 882d4a40-3796-d06e-c4f0-604e8503750b token_duration 768h token_renewable true token_policies [default my-policy other-policy] 创建一个周期性(periodic)令牌: $ vault token create -period=30m Key Value --- ----- token fdb90d58-af87-024f-fdcd-9f95039e353a token_accessor 4cd9177c-034b-a004-c62d-54bc56c0e9bd token_duration 30m token_renewable true token_policies [my-policy] 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -field (string: \"\") 只打印指定字段的数据。此选项优先级高于其他格式指令。信息的尾部没有换行符,使其适合通过管道与其他进程集成使用。 -format (string: \"table\") —— 以给定格式打印输出。合法的格式有:table、json 或者 yaml。该标志也可以通过环境变量 VAULT_FORMAT 来设定。 命令选项 -display-name (string: \"\") - 关联此令牌的名字。这是一个非敏感信息,可以用来识别创建的机密(例如前缀)。 -entity-alias (string: \"\") - 关联此令牌的实体别名的名称。只能和 -role 参数结合使用,使用的实体别名必须列在 allowed_entity_aliases 中。如果已指定,则不会从父级继承实体。 -explicit-max-ttl (duration: \"\") - 显式设置令牌的最大生命周期。它与普通 TTL 不同,最大 TTL 是硬限制,无法超越(意为该参数与最大 TTL 取其小者)。该参数的值需要是后缀为“30s”或“5m”的字符串。 -id (string: \"\") - 令牌的值。默认情况下会自动生成一个值。设定该标志需要有 sudo 权限。 -no-default-policy (bool: false) - 不给该令牌分配 \"default\" 策略。 -orphan (bool: false) - 创建没有父令牌的新令牌。该参数可以防止创建新令牌的令牌过期时新令牌被级联吊销。设定该标志需要有 sudo 权限。 -period (duration: \"\") - 如果指定该标志,每次续订都将使用给定的时长。只要定期主动续约令牌,它们就不会过期(除非还提供了 -explicit-max-ttl)。设置此值需要 sudo 权限。该参数的值需要是后缀为“30s”或“5m”的字符串。 -policy (string: \"\") - 关联此令牌的策略名称。可以通过多次使用该选项来指定多个策略。 -renewable (bool: true) - 是否允许续约该令牌直至它的最大 TTL。 -role (string: \"\") - 要创建的令牌配置的角色的名称。指定 -role 可能会覆盖其他参数。创建令牌所使用的本地的经过身份验证的 Vault 令牌必须具有 auth/token/create/ 的权限。 -ttl (duration: \"\") - 令牌关联的初始 TTL。令牌实际续约时间可能会超出此值,具体取决于配置的最大 TTL。该参数的值需要是后缀为“30s”或“5m”的字符串。 -type (string: \"service\") - 创建的令牌的类型。可以是 service 或是 batch。 -use-limit (int: 0) - 该参数可被使用的次数。过了该次数后,令牌会被自动吊销。默认情况下创建的令牌可以无限制地使用直至过期。 "},"3.命令行/21.3.token_lookup.html":{"url":"3.命令行/21.3.token_lookup.html","title":"token lookup","keywords":"","body":"token lookup token lookup 命令显示一个令牌或令牌访问器的信息。如果没有指定令牌,默认使用经身份验证的本地令牌。 例子 获取经身份验证的本地令牌的信息(使用 /auth/token/lookup-self 端点以及权限): $ vault token lookup 获取指定令牌的信息(使用 auth/token/lookup 端点以及权限): $ vault token lookup 96ddf4bc-d217-f3ba-f9bd-017055595017 通过访问器获取令牌信息: $ vault token lookup -accessor 9793c9b3-e04a-46f3-e7b8-748d7da248da 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -format (string: \"table\") —— 以给定格式打印输出。合法的格式有:table、json 或者 yaml。该标志也可以通过环境变量 VAULT_FORMAT 来设定。 命令选项 -accessor (bool: false) - 使用访问器而非令牌。如果使用该标志,那么令牌将不会被输出。 "},"3.命令行/21.4.token_renew.html":{"url":"3.命令行/21.4.token_renew.html","title":"token renew","keywords":"","body":"token renew token renew 命令续约令牌的租约,延长令牌的可用时间。如果没有指定令牌,那么就续约已经通过身份验证的本地令牌。如果令牌不可续约、已被吊销或是达到了最大 TTL,续约命令将会失败。 例子 续约一个令牌(使用 /auth/token/renew 端点和权限): $ vault token renew 96ddf4bc-d217-f3ba-f9bd-017055595017 续约当前登录的令牌(使用 auth/token/renew-self 端点和权限): $ vault token renew 使用特定延长时间续约令牌: $ vault token renew -increment=30m 96ddf4bc-d217-f3ba-f9bd-017055595017 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -format (string: \"table\") —— 以给定格式打印输出。合法的格式有:table、json 或者 yaml。该标志也可以通过环境变量 VAULT_FORMAT 来设定。 命令选项 -increment (duration: \"\") - 请求延长特定时间。 Vault 不会无条件采纳该参数。如果没有设置,Vault 将使用默认 TTL。该参数的值需要是后缀为“30s”或“5m”的字符串。该参数可简写为 -i。 "},"3.命令行/21.5.token_revoke.html":{"url":"3.命令行/21.5.token_revoke.html","title":"token revoke","keywords":"","body":"token revoke token revoke 命令吊销已经通过身份验证的令牌及其子令牌。如果没有指定令牌,则吊销已经通过身份验证的本地令牌。可以使用 -mode 标志控制吊销操作的行为。 例子 吊销一个令牌及其子令牌: $ vault token revoke 96ddf4bc-d217-f3ba-f9bd-017055595017 Success! Revoked token (if it existed) 吊销一个令牌但保留其子令牌: $ vault token revoke -mode=orphan 96ddf4bc-d217-f3ba-f9bd-017055595017 Success! Revoked token (if it existed) 使用令牌访问器吊销令牌: $ vault token revoke -accessor 9793c9b3-e04a-46f3-e7b8-748d7da248da Success! Revoked token (if it existed) 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: -accessor (bool: false) - 使用访问器而非令牌。 -mode (string: \"\") - 吊销操作的类型。如果未指定,Vault 将吊销该令牌及其子令牌。如果是 orphan,Vault 将只撤销令牌,而让子令牌成为孤儿令牌。如果为 path,则以指定路径为前缀的令牌与其子令牌都将被吊销。 -self - 吊销当下使用的已经通过身份验证的令牌。 "},"3.命令行/22.unwrap.html":{"url":"3.命令行/22.unwrap.html","title":"unwrap","keywords":"","body":"unwrap unwrap 命令使用给定的令牌从 Vault 中通过拆除封装读取机密。结果与从 Vault 读取未使用响应封装的机密相同。如果没有指定令牌,则使用当前已经通过身份验证的令牌拆除封装后读取数据。 例子 读取指定令牌的 cubbyhole 机密引擎中封装的数据: $ vault unwrap 3de9ece1-b347-e143-29b0-dc2dc31caafd 使用当前使用令牌读取封装数据: $ vault login 848f9ccf-7176-098c-5e2b-75a0689d41cd $ vault unwrap # unwraps 848f9ccf... 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -field (string: \"\") 只打印指定字段的数据。此选项优先级高于其他格式指令。结果的尾部没有换行符,使其适合通过管道与其他进程集成使用。 -format (string: \"table\") —— 以给定格式打印输出。合法的格式有:table、json 或者 yaml。该标志也可以通过环境变量 VAULT_FORMAT 来设定。 "},"3.命令行/23.version.html":{"url":"3.命令行/23.version.html","title":"version","keywords":"","body":"version version 命令打印 Vault 的版本信息。 $ vault version Vault v1.2.3 也可以在 vault 命令后添加 --version 或是 -v 标志打印版本信息: $ vault -v Vault v1.2.3 "},"3.命令行/24.write.html":{"url":"3.命令行/24.write.html","title":"write","keywords":"","body":"write write 命令将数据写入 Vault 的指定路径。数据可以是凭证、机密、配置或其他任意数据。该命令的具体行为由路径上挂载的后端实现决定。 数据以 \"key=value\" 键值对形式提供。如果该值以 \"@\" 开头,则从文件加载数据。如果值为 \"-\",Vault 将从 stdin 读取该值。 有关示例和路径的完整列表,请参阅相关机密引擎的文档。 例子 将数据保存到 K/V 机密引擎中: $ vault write secret/my-secret foo=bar 命令 transit 机密引擎创建一个新的加密密钥: $ vault write -f transit/keys/my-key 从磁盘文件读取并上传一个 AWS IAM 策略: $ vault write aws/roles/ops policy=@policy.json 设置访问 Consul 所使用的访问令牌: $ echo $MY_TOKEN | vault write consul/config/access token=- 可用标志 除了全局可用的标准标志以外,也可以配置下列标志: 输出选项 -field (string: \"\") 只打印指定字段的数据。此选项优先级高于其他格式指令。结果的尾部没有换行符,使其适合通过管道与其他进程集成使用。 -format (string: \"table\") —— 以给定格式打印输出。合法的格式有:table、json 或者 yaml。该标志也可以通过环境变量 VAULT_FORMAT 来设定。 命令选项 -force (bool: false) - 允许在没有提供 \"key-value\" 键值对时执行操作。使用该标志可以写入那些不需要数据的键。可以简写为 -f。 "},"4.配置文件/overview.html":{"url":"4.配置文件/overview.html","title":"配置文件","keywords":"","body":"Vault 配置文件 除非使用的是 \"dev\" 模式,否则 Vault 服务使用配置文件进行配置。配置文件使用 HCL 或是 JSON 格式。以下是一个配置文件的例子: storage \"consul\" { address = \"127.0.0.1:8500\" path = \"vault\" } listener \"tcp\" { address = \"127.0.0.1:8200\" tls_disable = 1 } telemetry { statsite_address = \"127.0.0.1:8125\" disable_hostname = true } 配置文件编写好以后,使用 vault server 命令的 -config 标志指定使用的配置文件路径。 参数 storage ([StorageBackend][storage-backend]: ) - 配置存储 Vault 数据的存储后端。在高可用模式下运行 Vault 需要后端支持协调(coordination)语义。如果存储后端支持高可用,也可以在此参数块中指定高可用后端选项。如果不支持,则应使用支持高可用的后端以及相应的高可用选项配置独立的 ha_storage 参数。 ha_storage ([StorageBackend][storage-backend]: nil) - 配置 Vault 高可用集群协调所使用的后端存储。这必须是一个支持高可用集群的后端。如果没有设置该标志,那么将会尝试在 storage 配置的位置上进行高可用集群协调。如果 storage 后端支持高可用集群协调,那么不需要配置该标志。 listener ([Listener][listener]: ) - 配置 Vault 如何侦听 API 请求。 seal ([Seal][seal]: nil) - 配置自动解封的类型。 cluster_name (string: ) – 设置 Vault 集群的标志符。如果忽略,Vault 会生成一个值。 cache_size (string: \"131072\") – 设定物理存储子系统所使用的读缓存的大小。该值设定的是缓存的条目数,所以实际缓存的大小取决于存储的条目的大小。 disable_cache (bool: false) – 禁止 Vault 使用缓存,包括物理存储子系统的读缓存。这将会显著影响性能。 disable_mlock (bool: false) – 禁止服务调用 mlock 系统调用。mlock 防止内存数据被交换(swap)到磁盘上。不建议禁用 mlock ,除非使用集成存储。禁用 mlock 时,请遵循下面概述的其他安全预防措施。该参数也可以通过 环境变量 VAULT_DISABLE_MLOCK 来设定。 除非运行 Vault 的系统仅使用加密虚拟内存或根本不使用虚拟内存,否则不建议禁用 mlock。 Vault 只会在支持 mlock() 系统调用(Linux、FreeBSD 等)的类 UNIX 系统上支持内存锁定。非类 UNIX 系统(例如 Windows、NaCL、Android)缺乏防止进程的整个内存地址空间被交换到磁盘的原语(primitive),因此在不受支持的平台上会自动禁用。 如果使用集成存储,强烈建议禁用 mlock,因为 mlock 无法与内存映射文件(例如由BoltDB 创建的用于跟踪 Raft 集群状态的文件)配合好。使用 mlock 时,内存映射文件会加载到常驻内存中,这会导致 Vault 的整个数据集加载到内存中,如果 Vault 的数据变得超出可用内存范围,则会引发内存不足问题。在这种情况下,即使 BoltDB 中的数据保持加密存储状态,也应禁用虚拟内存以防止 Vault 内存中其他的敏感数据转储到磁盘中。 在 Linux 中,要给予 Vault 可执行文件以使用 mlock 系统调用同时不给予 root 权限的话,运行以下命令: sudo setcap cap_ipc_lock=+ep $(readlink -f $(which vault)) 注意,由于 Vault 中所有的插件都是单独的进程,我们需要对插件目录中所有插件都执行该操作。 如果使用的 Linux 发行版搭配有 systemd 的现代化版本,我们可以将以下指令添加到配置文件中的 \"[Service]\" 部分: LimitMEMLOCK=infinity plugin_directory (string: \"\") – 存放有允许加载的插件的目录路径。Vault 必须拥有读取该目录的权限来成功加载插件,该路径不可以是符号链接。 telemetry ([Telemetry][telemetry]: ) – 设置遥测(telemetry)报告系统。 log_level (string: \"\") – 设定使用的日志级别,可以使用命令行参数或环境变量覆盖配置文件中的配置。收到 SIGHUP (sudo kill -s HUP pid of vault) 信号时,Vault 会将日志级别更新为此处指定的当前值(包括命令行参数或环境变量覆盖的值)。并非 Vault 日志记录的所有部分都可以通过这种方式动态更改级别;特别是,secrets/auth 插件目前不支持动态更新。支持的日志级别: Trace, Debug, Error, Warn, Info。 log_format (string: \"\") – 设置使用的日志格式。可以使用命令行参数或环境变量覆盖配置文件中的配置。支持的格式有:standard 和 json。 default_lease_ttl (string: \"768h\") – 设置令牌与机密的默认租约时长。该参数的值需要是后缀为 \"30s\" 或 \"5m\" 的字符串。该值不可以超过 max_lease_ttl。 max_lease_ttl (string: \"768h\") – 设置令牌与机密租约的最大时长。该参数的值需要是后缀为 \"30s\" 或 \"5m\" 的字符串。具体挂载点可以通过调整配置 max-lease-ttl 标志覆盖此处的配置:auth、secret default_max_request_duration (string: \"90s\") – 设置 Vault 处理请求所使用的最大时长。该参数可以被侦听器配置的 max_request_duration 覆盖。 raw_storage_endpoint (bool: false) – 启用 sys/raw 端点,该端点允许透过加密屏障读写原始数据。这是一个需要极高特权的端点。 ui (bool: false) – 是否启用 Web UI 界面,启用后可以从任意侦听地址加上 /ui 后缀来访问 UI 界面。从浏览器中访问 Vault API 地址会被自动重定向到 UI 地址上。该参数也可以通过 环境变量 VAULT_UI 来设定。 pid_file (string: \"\") - Vault 服务用以保存进程 ID (PID)的文件路径 enable_response_header_hostname (bool: false) - 在所有 Vault 的 HTTP 响应中添加 HTTP 标头:X-Vault-Hostname,值为响应 HTTP 请求的 Vault 节点的主机名。此信息是尽力而为,不保证存在。如果启用此配置选项并且响应中不存在 X-Vault-Hostname 标头,则意味着从操作系统检索主机名时出现了错误。 enable_response_header_raft_node_id (bool: false) - 在所有 Vault 的 HTTP 响应中添加 HTTP 标头:X-Vault-Raft-Node-ID。如果 Vault 是 Raft 集群的组成节点(即使用集成存储),则此标头将包含响应 HTTP 请求的 Vault 节点的 Raft 节点 ID。如果 Vault 不参与 Raft 集群,则无论是否启用此配置选项,此标头都将被省略。 高可用参数 后端存储支持高可用时可以使用以下参数: api_addr (string: \"\") - 设定要向集群中的其他 Vault 服务器通告以进行客户端重定向的地址(完整 URL)。该值也用于插件后端。也可以通过设置环境变量 VAULT_API_ADDR 设置该参数。通常该参数要设置为指向侦听器地址值的完整 URL。 cluster_addr (string: \"\") - 设定要向集群中的其他 Vault 服务器通告以进行请求转发的地址。也可以通过设置环境变量 VAULT_CLUSTER_ADDR 设置该参数。这是一个像 api_addr 那样完整的 URL,但 Vault 将忽略协议(所有集群成员始终使用带有私钥/证书的 TLS)。 disable_clustering (bool: false) – 设定是否启用请求转发等集群功能。如果在一个 Vault 节点上将此设置为 true ,那么只会在该节点是主节点时禁用这些功能。如果存储类型是 raft 则不能将该参数设置为 true。 "},"4.配置文件/1.listener.html":{"url":"4.配置文件/1.listener.html","title":"listener","keywords":"","body":"listener 配置节 listener 配置节配置了 Vault 接受并响应请求所使用的地址和端口。目前,唯一支持的 listener 类型是 TCP "},"4.配置文件/1.1.listener_tcp.html":{"url":"4.配置文件/1.1.listener_tcp.html","title":"tcp","keywords":"","body":"tcp 侦听器 TCP 侦听器配置 Vault 侦听的 TCP 地址/端口: listener \"tcp\" { address = \"127.0.0.1:8200\" } listener 配置节可以设定多次使得 Vault 可以在多个接口上进行侦听。如果我们配置了多个侦听器,那么我们也必须设定 api_addr 和 cluster_addr 使得 Vault 可以传播正确的地址给其他节点。 侦听器的自定义响应标头 从 1.9 版开始,Vault 支持为根路径 (/) 以及 API 端点 (/v1/*) 定义自定义 HTTP 响应标头。标头是根据返回的状态代码定义的。例如,用户可以为 200 状态代码定义一组自定义响应标头,并为 307 状态代码定义另一组自定义响应标头。有一个 \"/sys/config/ui\" API 端点允许用户设置 UI 特定的自定义标头。如果在配置文件中配置了标头,则不允许通过 \"/sys/config/ui\" API 端点重新配置。在需要修改自定义标头或需要移除自定义标头的时,需要修改相应的 Vault 配置文件,并且需要向 Vault 进程发送 SIGHUP 信号。 如果在配置文件中定义了一个标头,并且 Vault 的内部进程使用了相同的 header,则不接受配置的标头。例如,具有 X-Vault- 前缀的自定义标头将不会被接受。启动时将在 Vault 的日志中记录一条消息,表明不接受带有 X-Vault- 前缀的标头。 优先级顺序 如果在配置文件和 \"/sys/config/ui\" API 端点中配置了相同的标头,则配置文件中的标头优先。例如,在 \"/sys/config/ui\" API 端点中定义了 \"Content-Security-Policy\" 标头,并且在配置文件中也定义了该标头,则在响应标头中设置配置文件中的值,而不是 \"/sys/config/ui\" API 端点中的值。 tcp 侦听器参数 address (string: \"127.0.0.1:8200\") – 设置要绑定的侦听地址。该值可以使用 go-sockaddr template 定义成动态值,运行时解析实际地址。 cluster_address (string: \"127.0.0.1:8201\") – 设置要绑定到集群服务器对服务器请求的地址。该端口号默认为比 address 值高 1。通常不需要设置该参数,但在 Vault 服务器相互隔离的情况下很有用,那时它们需要通过 TCP 负载均衡器或其他一些方案进行通信。该值可以使用 go-sockaddr template 定义成动态值,运行时解析实际地址。 http_idle_timeout (string: \"5m\") - 设置启用连接 keep-alives 时等待下一个请求的最长时间。如果 http_idle_timeout 为零,则使用 http_read_timeout 的值。如果两者都为零,则使用 http_read_header_timeout 的值。该参数的值需要是后缀为 \"30s\" 或 \"1h\" 的字符串。 http_read_header_timeout (string: \"10s\") - 设置允许读取请求标头的时间。该参数的值需要是后缀为 \"30s\" 或 \"1h\" 的字符串。 http_read_timeout (string: \"30s\") - 设置允许读取整个请求,包括 body 的时间。该参数的值需要是后缀为 \"30s\" 或 \"1h\" 的字符串。 http_write_timeout string: \"0\") - 设置允许对响应进行写入的最大持续时间,该值会在读取新请求的标头时重置。默认值 \"0\" 表示无穷大。该参数的值需要是后缀为 \"30s\" 或 \"1h\" 的字符串。 max_request_size (int: 33554432) – 设置请求的最大尺寸限制,以字节计。默认为 32 MB(如果没有设置或是设置为 0)。为该值设置一个小于 0 的数字将完全关闭限制。 max_request_duration (string: \"90s\") – 设置 Vault 取消请求前允许的最大时间。该值将覆盖本节的 default_max_request_duration 配置。 proxy_protocol_behavior (string: \"\") – 如果设置该参数,将为本侦听器启用 PROXY 协议版本 1 行为。可以配置的值有: use_always - 始终使用 client IP 地址(理解为使用 LB 等中间代理的地址) allow_authorized - 如果源 IP 地址在 proxy_protocol_authorized_addrs 列表中,那么就使用 client IP;如果源 IP 不在该列表中,那么使用源 IP deny_unauthorized - 如果源 IP 地址不在 proxy_protocol_authorized_addrs 列表中则拒绝连接 proxy_protocol_authorized_addrs (string: or array: ) – 设定允许使用 PROXY 协议的源 IP 地址列表。如果 proxy_protocol_behavior 为 use_always 那么不需要设置该参数。源 IP 应该以逗号分隔的字符串形式给定。至少需要设置一个源 IP,proxy_protocol_authorized_addrs 不可以是空字符串或是空列表。 tls_disable (string: \"false\") – 是否禁用 TLS。 Vault 默认启用 TLS,因此必须明确禁用 TLS 才能选择使用不安全的通信。 tls_cert_file (string: , reloads-on-SIGHUP) – 指定 TLS 证书的路径。证书文件必须是一个 PEM 编码的文件。要将侦听器配置为使用 CA 证书,请将主证书和 CA 证书拼接在一起。主证书应首先出现在拼接后的文件中。收到 SIGHUP 消息时, Vault 将使用该路径重新加载证书;在 Vault 运行时修改此值对 SIGHUP 无效。 tls_key_file (string: , reloads-on-SIGHUP) – 指定证书私钥的路径。私钥文件必须是一个 PEM 编码的文件。如果密钥文件有密码保护,系统会在服务启动时提示输入密码。使用 SIGHUP 信号重新加载配置时,密钥文件的密码必须保持不变。收到 SIGHUP 消息时, Vault 将使用该路径重新加载证书;在 Vault 运行时修改此值对 SIGHUP 无效。 tls_min_version (string: \"tls12\") – 设置支持的 TLS 的最低版本。可以配置的值有:\"tls10\"、\"tls11\"、\"tls12\"或者\"tls13\" 警告: TLS 1.1 及更低版本被认为是不安全的。 tls_cipher_suites (string: \"\") – 设置支持的 ciphersuites,该值是一个是用逗号分隔的列表,可以在 Golang TLS 文档中查询所有可用的 ciphersuites。 注意,Go 只针对 TLSv1.2 及更早版本参考此列表;密码的顺序并不重要。要使此参数生效,必须将 tls_max_version 属性设置为 tls12 以防止协商使用 TLSv1.3,我们不推荐这样做。有关更多信息,请参阅 Go 博客文章。 tls_prefer_server_cipher_suites (string: \"false\") – 设置服务器的 ciphersuite 覆盖客户端的 ciphersuite。 警告:该参数已经废弃,设置该参数没有任何作用。 tls_require_and_verify_client_cert (string: \"false\") – 为此侦听器开启对客户端的身份验证。开启后侦听器会要求客户端提供证书,然后使用系统 CA 证书进行验证。 tls_client_ca_file (string: \"\") – PEM 编码的 CA 证书文件,用来检查客户端的身份。 tls_disable_client_certs (string: \"false\") – 为此侦听器关闭对客户端的身份验证。默认情况(默认是 false)下 Vault 会在可用时请求客户端证书。 x_forwarded_for_authorized_addrs (string: ) – 设置 X-Forwarded-For 标头中可信源 IP 地址的 CIDR,使用逗号分隔的列表或是 JSON 数组。这将开启 X-Forwarded-For 支持。 x_forwarded_for_hop_skips (string: \"0\") – 将从 X-Forwarded-For-Hop 尾部跳过的地址数。例如,对于 1.2.3.4,2.3.4.5,3.4.5.6,4.5.6.7 的标头值,如果此值设置为\"1\",则将用作原始客户端 IP 的地址为 3.4.5.6。 x_forwarded_for_reject_not_authorized (string: \"true\") – 如果设置为 false,则如果来自未授权地址的连接中存在 X-Forwarded-For 标头,则该标头将被忽略并按原样使用客户端连接,而不是拒绝连接。 x_forwarded_for_reject_not_present (string: \"true\") – 如果设置为 false,则如果 X-Forwarded-For 标头不存在或为空,则将按原样使用客户端地址,而不是拒绝连接。 telemetry 参数 unauthenticated_metrics_access (bool: false) - 如果设置为 true,将允许未经身份验证的用户访问 /v1/sys/metrics 端点。 profiling 参数 unauthenticated_pprof_access (bool: false) - 如果设置为 true,将允许未经身份验证的用户访问 /v1/sys/pprof 端点。 unauthenticated_in_flight_request_access (bool: false) - 如果设置为 true,将允许未经身份验证的用户访问 /v1/sys/in-flight-req 端点。 custom_response_headers 参数 default (key-value-map: {}) - 一个标头名称到字符串值数组的映射,代表无论响应状态代码值是多少,都会在所有端点上设置的默认标头。 (key-value-map: {}) - 一个标头名称到字符串值数组的映射,只有在返回特定状态码的响应时设置这些标头。例如 \"200\" = {\"Header-A\": [\"Value1\", \"Value2\"]},当返回的 HTTP 响应状态吗为 \"200\" 时添加标头 \"Header-A\"。 (key-value-map: {}) - 一个标头名称到字符串值数组的映射,只有在返回特定范围的状态码的响应时设置这些标头。例如 \"2xx\" = {\"Header-A\": [\"Value1\", \"Value2\"]},当返回的 HTTP 响应状态吗为 \"200\" 或 \"204\" 等时添加标头 \"Header-A\"。 例子 配置 TLS 以下配置演示如何启用 TLS 侦听: listener \"tcp\" { tls_cert_file = \"/etc/certs/vault.crt\" tls_key_file = \"/etc/certs/vault.key\" } 在多个接口上进行侦听 以下配置演示如何在多个接口上进行侦听,包括 localhost: listener \"tcp\" { address = \"127.0.0.1:8200\" } listener \"tcp\" { address = \"10.0.0.5:8200\" } # Advertise the non-loopback interface api_addr = \"https://10.0.0.5:8200\" cluster_addr = \"https://10.0.0.5:8201\" 配置未经身份验证的用户访问 metrics 端点 以下配置演示如何允许未经身份验证的用户访问 metrics 端点: listener \"tcp\" { telemetry { unauthenticated_metrics_access = true } } 配置未经身份验证的用户访问 profiling 端点 以下配置演示如何允许未经身份验证的用户访问 profiling 端点: listener \"tcp\" { profiling { unauthenticated_pprof_access = true unauthenticated_in_flight_request_access = true } } 配置自定义 HTTP 响应标头 注意:此功能需要 Vault v1.9.0 及以上版本。该例子显示配置自定义 HTTP 响应标头。管理员可以在侦听器节中配置 \"custom_response_headers\" 子节,以设置适合其应用程序的自定义 HTTP 标头。本示例演示的是 \"Strict-Transport-Security\" 和 \"Content-Security-Policy\",它们是已知的 HTTP 标头,可以用来加强与 Vault 端点通信的应用程序的安全性。请注意,漏洞扫描通常会检查此类与安全相关的 HTTP 标头。此外,还可以配置应用程序特定的自定义标头。例如,在下面的示例中配置了 \"X-Custom-Header\"。 listener \"tcp\" { custom_response_headers { \"default\" = { \"Strict-Transport-Security\" = [\"max-age=31536000\",\"includeSubDomains\"], \"Content-Security-Policy\" = [\"connect-src https://clusterA.vault.external/\"], \"X-Custom-Header\" = [\"Custom Header Default Value\"], }, \"2xx\" = { \"Content-Security-Policy\" = [\"connect-src https://clusterB.vault.external/\"], \"X-Custom-Header\" = [\"Custom Header Value 1\", \"Custom Header Value 2\"], }, \"301\" = { \"Strict-Transport-Security\" = [\"max-age=31536000\"], “Content-Security-Policy\" = [\"connect-src https://clusterC.vault.external/\"], }, } } 当一个标头在多个状态码配置节中被定义时,将返回与最具体的响应代码匹配的标头。例如,对于下面的配置示例,307 响应将返回 307 Custom header value,而 306 将返回 3xx Custom header value。 listener \"tcp\" { custom_response_headers { \"default\" = { \"X-Custom-Header\" = [\"default Custom header value\"] }, \"3xx\" = { \"X-Custom-Header\" = [\"3xx Custom header value\"] }, \"307\" = { \"X-Custom-Header\" = [\"307 Custom header value\"] } } } 侦听所有 IPv6 & IPv4 接口 该例子演示了 Vault 对所有 IPv6 & IPv4 接口启用侦听,包括 localhost: listener \"tcp\" { address = \"[::]:8200\" cluster_address = \"[::]:8201\" } 侦听特定 IPv6 地址 该例子演示了如何让 Vault 仅使用 IPv6 并绑定到 IP 地址为 2001:1c04:90d:1c00:a00:27ff:fefa:58ec 的接口: listener \"tcp\" { address = \"[2001:1c04:90d:1c00:a00:27ff:fefa:58ec]:8200\" cluster_address = \"[2001:1c04:90d:1c00:a00:27ff:fefa:58ec]:8201\" } # Advertise the non-loopback interface api_addr = \"https://[2001:1c04:90d:1c00:a00:27ff:fefa:58ec]:8200\" cluster_addr = \"https://[2001:1c04:90d:1c00:a00:27ff:fefa:58ec]:8201\" "},"4.配置文件/2.replication.html":{"url":"4.配置文件/2.replication.html","title":"replication","keywords":"","body":"replication 配置节 replication 配置节可以设置与复制相关的多种配置项。 replication { resolver_discover_servers = true logshipper_buffer_length = 1000 logshipper_buffer_size = \"5gb\" allow_forwarding_via_header = false best_effort_wal_wait_duration = \"2s\" } replication 参数 resolver_discover_servers (boolean: true) - 应尽量始终开启该功能,它使辅助(secondary)集群节点能够与主集群中的节点联系,以请求有关主节点是谁的信息。这解决了辅助集群可能只知道主集群中的单个节点的问题,如果该节点不是主节点,则复制连接将不会成功。禁用该解析功能后,解决此问题的唯一方法是通过繁重的 UpdatePrimary 工作流程,或者在主集群上执行降级,直到辅助集群知道的一个节点成为主节点。 logshipper_buffer_length (integer: 16384) - 日志传输缓冲最大可以存储的条目数。如果没有指定,默认为 16000 条。 logshipper_buffer_size (string: \"\") - 日志传输缓冲最大可以存储的数据大小。该大小以整数表示,代表字节数,或是表示容量的字符串。容量字符串包含一个数字,后跟一个可选的空格,最后是代表单位的后缀。可用的单位后缀有:kb, kib, mb, mib, gb, gib, tb, tib。后缀不分大小写。如果没有指定,Vault 会尝试读取运行服务的主机的总内存数。如果成功获得总内存大小,Vault 会将之设定为总内存的 10%。如果无法读取主机总内存,那么默认设置为 1GB。 allow_forwarding_via_header (boolean: false) - 启用客户端控制的一致性转发选项,例如: X-Vault-Inconsistent: forward-active-node 和 X-Vault-Forward: active-node。 best_effort_wal_wait_duration (duration: \"2s\") - 尝试防止读取超时的缓解措施:当通过 RPC 进行写入时,比如处理请求的节点自己不执行写入,等待该参数设定的时间以查看本地 WAL(预写日志)中的结果,然后再返回响应给客户端。 "},"4.配置文件/3.seal.html":{"url":"4.配置文件/3.seal.html","title":"seal","keywords":"","body":"seal 配置节 seal 配置节配置了用于对数据提供额外保护的封印的类型,例如使用 HSM 或 Cloud KMS 方案来加解密主密钥。此配置节是可选的,对于主密钥,如果未配置,Vault 将使用 Shamir 算法以加密和拆分主密钥。 更多的例子,请参阅具体类型的自动解封章节。 配置 可以通过配置 seal 配置节来配置封印相关的配置: seal [NAME] { # ... } 比如: seal \"pkcs11\" { # ... } 对于也可以从环境变量读取的配置项,环境变量将优先于配置文件中的值。 "},"4.配置文件/3.1.seal_AliCloud.html":{"url":"4.配置文件/3.1.seal_AliCloud.html","title":"AliCloud","keywords":"","body":"alicloudkms 封印 阿里云 KMS 封印配置了 Vault 如何使用阿里云 KMS 封装封印密钥。阿里云 KMS 封印可以由以下任一方式激活: Vault 配置文件中设置了 seal \"alicloudkms\" 块 环境变量 VAULT_SEAL_TYPE 的值被设置为 alicloudkms。如果通过环境变量启用该功能,必须通过环境变量提供所有阿里云 KMS 所需要的其他值和所有有助进行身份验证的环境变量。(例如 VAULT_ALICLOUDKMS_SEAL_KEY_ID) alicloudkms 例子 下面的例子显示了通过 Vault 配置文件提供所有必要的值来配置阿里云 KMS 封印: seal \"alicloudkms\" { region = \"us-east-1\" access_key = \"0wNEpMMlzy7szvai\" secret_key = \"PupkTg8jdmau1cXxYacgE736PJj4cA\" kms_key_id = \"08c33a6f-4e0a-4a1b-a3fa-7ddfa1d4fb73\" } alicloudkms 参数 以下参数可以在 Vault 配置文件的 seal 配置节当中使用: region (string: \"us-east-1\"): 加密密钥所处的阿里云区域。可以通过设置环境变量 ALICLOUD_REGION 来配置。 domain (string: \"kms.us-east-1.aliyuncs.com\"): 如果设置该参数,将会覆盖通常连接特定区域的 KMS 服务所使用的端点地址。可以通过设置环境变量 ALICLOUD_DOMAIN 来配置。 access_key (string: ): 使用的阿里云 access key。可以通过设置环境变量 ALICLOUD_ACCESS_KEY 来配置。 secret_key (string: ): 使用的阿里云 secret key。可以通过设置环境变量 ALICLOUD_SECRET_KEY 来配置。 kms_key_id (string: ): 加解密密钥的 KMS key ID。可以通过设置环境变量 VAULT_ALICLOUDKMS_SEAL_KEY_ID 来配置。 身份验证 必须设定身份验证相关的参数,通过配置文件参数或者环境变量二者选一。 注意,虽然我们可以用配置文件来配置阿里云的 access key 和 secret key,但我们强烈建议使用环境变量来配置。 AliCloud authentication values: * `ALICLOUD_REGION` * `ALICLOUD_ACCESS_KEY` * `ALICLOUD_SECRET_KEY` 请注意,客户端使用的是官方的阿里云 SDK,它会根据环境变量中的凭据、指定凭据,或是 RAM 角色凭据的顺序来加载凭据。 alicloudkms 环境变量 可以通过设置环境变量来启用阿里云 KMS 封印: Vault Seal specific values: * `VAULT_SEAL_TYPE` * `VAULT_ALICLOUDKMS_SEAL_KEY_ID` "},"4.配置文件/3.2.seal_AWS.html":{"url":"4.配置文件/3.2.seal_AWS.html","title":"AWS","keywords":"","body":"awskms 封印 AWS KMS 封印配置了 Vault 如何使用 AWS KMS 封装封印密钥。AWS KMS 封印可以由以下任一方式激活: Vault 配置文件中设置了 seal \"awskms\" 块 环境变量 VAULT_SEAL_TYPE 的值被设置为 awskms。如果通过环境变量启用该功能,必须通过环境变量提供所有 AWS KMS 所需要的其他值和所有有助进行身份验证的环境变量。(例如 AWS_ACCESS_KEY_ID) awskms 例子 下面的例子显示了通过 Vault 配置文件提供所有必要的值来配置 AWS KMS 封印: seal \"awskms\" { region = \"us-east-1\" access_key = \"AKIAIOSFODNN7EXAMPLE\" secret_key = \"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\" kms_key_id = \"19ec80b0-dfdd-4d97-8164-c6examplekey\" endpoint = \"https://vpce-0e1bb1852241f8cc6-pzi0do8n.kms.us-east-1.vpce.amazonaws.com\" } awskms 参数 以下参数可以在 Vault 配置文件的 seal 配置节当中使用: region (string: \"us-east-1\"): 加密密钥所处的 AWS 区域。可以通过设置环境变量 AWS_REGION 或是 AWS_DEFAULT_REGION 来配置,也可以在 ~/.aws/config 文件中配置,或是使用实例元信息。 access_key (string: ): 使用的 AWS access key。可以通过设置环境变量 AWS_ACCESS_KEY_ID 来配置,或是在 AWS 命令行配置文件或实例配置文件中配置。 session_token (string: \"\"): 配置 AWS 会话(session)令牌。可以通过设置环境变量 AWS_SESSION_TOKEN 来配置。 secret_key (string: ): 使用的 AWS secret key。可以通过设置环境变量 AWS_SECRET_ACCESS_KEY 来配置,或是在 AWS 命令行配置文件或实例配置文件中配置。 kms_key_id (string: ): 加解密密钥的 KMS key ID 或是 ARN。可以通过设置环境变量 VAULT_AWSKMS_SEAL_KEY_ID 来配置。 endpoint (string: \"\"): 用于接收 AWS KMS 请求的 KMS API 终端节点。也可以由 AWS_KMS_ENDPOINT 环境变量指定。有时,比如说需要通过 VPC 端点走内网连接 KMS 时该参数会很有用。如果没有设置该参数,Vault 将使用您所在地区的默认 API 端点。 身份验证 必须设置身份验证参数,可以通过环境变量或是配置文件中的参数。 注意:虽然我们可以在配置文件中配置 AWS_ACCESS_KEY_ID 和 AWS_SECRET_ACCESS_KEY,但我们强烈推荐使用环境变量配置这两个参数。 AWS 身份验证参数有: AWS_REGION 或是 AWS_DEFAULT_REGION AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY 注意:客户端使用官方 AWS SDK,如果没有提供上面的 AWS 参数,,将按顺序使用指定的凭证、环境变量中的凭证、共享文件凭证或 IAM 角色/ECS 任务凭证。 Vault 需要对 KMS 密钥具有以下权限: kms:Encrypt kms:Decrypt kms:DescribeKey 以上权限可以通过 Vault 使用的委托(principal)的对指定 KMS 密钥的密钥策略的 IAM 权限授予,也可以通过密钥的 KMS Grants配置。 awskms 环境变量 AWS KMS 封印也可以通过设置以下环境变量来启用 VAULT_SEAL_TYPE VAULT_AWSKMS_SEAL_KEY_ID 密钥轮换 AWS KMS 封印支持轮换 AWS KMS 文档中定义的主密钥。因为密钥信息与加密数据一起存储,所以KMS 支持自动轮换和手动轮换。旧密钥不得禁用或删除,并用于解密旧数据。任何新增或更新的数据都将使用封印配置中定义的当前密钥,或者利用密钥别名设置为当前密钥的密钥进行加密。 AWS 实例元数据超时 影响 Vault 1.4 及以后的版本 Vault 在 EC2 实例上使用实例元数据服务的时候,例如从实例配置文件中获取凭据,使用到实例元数据服务 (IMDSv2) 的 v2 版本时可能会出现延迟。 Vault 使用的 AWS SDK 首先尝试连接到 IMDSv2,如果超时,则回退到 v1。在 Vault 1.4 中,此超时最多可能需要 2 分钟。在 Vault 1.5.5 及更高版本中最多可能需要 2 秒钟。 超时会发生在 Vault 和 IMDSv2 之间存在代理,并且实例 hop 限制设置为小于 Vault 和 IMDSv2 之间的“hop”数的时候。例如,如果 Vault 在 EC2 实例上的 docker 中运行,并且实例 hop 限制设置为 1,AWS SDK 客户端将尝试连接到 IMDSv2,超时并回退到 IMDSv1,因为 docker 和 IMDS 之间存在额外的网络 hop. 为避免超时行为,可以在底层 EC2 实例上调整 hop 限制。对于 docker 示例,将 hop 限制设置为 2 就可以让 Vault 中的 AWS SDK 无延迟地连接到 IMDSv2。 "},"4.配置文件/3.3.seal_Azure.html":{"url":"4.配置文件/3.3.seal_Azure.html","title":"Azure Key Valut","keywords":"","body":"azurekeyvault 配置节 Azure Key Vault 封印配置了 Vault 如何使用 Azure Key Vault 封装封印密钥。Azure Key Vault 封印可以由以下任一方式激活: Vault 配置文件中设置了 seal \"azurekeyvault\" 块 环境变量 VAULT_SEAL_TYPE 的值被设置为 azurekeyvault。如果通过环境变量启用该功能,必须通过环境变量提供所有 Azure Key Vault 所需要的其他值和所有有助进行身份验证的环境变量(例如 VAULT_AZUREKEYVAULT_VAULT_NAME 以及 AZURE_TENANT_ID 等)。 azurekeyvault 例子 下面的例子显示了通过 Vault 配置文件提供所有必要的值来配置 Azure Key Vault 封印: seal \"azurekeyvault\" { tenant_id = \"46646709-b63e-4747-be42-516edeaf1e14\" client_id = \"03dc33fc-16d9-4b77-8152-3ec568f8af6e\" client_secret = \"DUJDS3...\" vault_name = \"hc-vault\" key_name = \"vault_key\" } azurekeyvault 参数 以下参数可以在 Vault 配置文件的 seal 配置节当中使用: tenant_id (string: ): 使用的 Azure Active Directory 组织的 tenant id。可以通过设置环境变量 AZURE_TENANT_ID 来配置。 client_id (string: ): 用来调用 Azure API 的凭据的 client id。可以通过设置环境变量 AZURE_CLIENT_ID 来配置。 client_secret (string: ): 用来调用 Azure API 的凭据的 client secret。可以通过设置环境变量 AZURE_CLIENT_SECRET 来配置。 environment (string: \"AZUREPUBLICCLOUD\"): 使用的 Azure 云环境的 API 端点。可以通过设置环境变量 AZURE_ENVIRONMENT 来配置。 vault_name (string: ): 加解密密钥所在的 Key Vault 名称。可以通过设置环境变量 VAULT_AZUREKEYVAULT_VAULT_NAME 来配置。 key_name (string: ): 加解密密钥的名称。可以通过设置环境变量 VAULT_AZUREKEYVAULT_KEY_NAME 来配置。 resource (string: \"vault.azure.net\"): 要连接的 Azure KeyVault 资源的 DNS 后缀。可以通过设置环境变量 AZURE_AD_RESOURCE 来配置。如果要使用 Azure 托管 HSM KeyVault 实例时需要修改该参数。 身份验证 必须设置身份验证参数,可以通过环境变量或是配置文件中的参数。 Azure 身份验证参数有: AZURE_TENANT_ID AZURE_CLIENT_ID AZURE_CLIENT_SECRET AZURE_ENVIRONMENT AZURE_AD_RESOURCE 注意:如果 Vault 服务运行于 Azure 上,Vault 可以使用 Managed Service Identities (MSI) 来访问 Azure 服务,而不使用环境变量或是共享的的 client id 和 client secret。必须在运行 Vault 服务的虚拟机上启用 MSI。我们推荐使用 MSI 来避免通过明文保存 Azure 凭证。 另外需要注意的是,如果使用 Managed HSM KeyVault,必须设置 resource 配置参数或是 AZURE_AD_RESOURCE 环境变量。一般来说设置为 managedhsm.azure.net,但也可以工具使用的 Azure 环境指向其他后缀。 azurekeyvault 环境变量 Azure Key Vault 封印也可以通过设置以下环境变量来启用: VAULT_AZUREKEYVAULT_VAULT_NAME VAULT_AZUREKEYVAULT_KEY_NAME 密钥轮换 Azure Key Vault 封印支持轮换密钥。密钥元数据与加密数据一起存储,以确保在解密操作期间使用正确的密钥。只需使用 Azure Automation Account 设置 Azure Key Vault,Vault 的密钥轮换,Vault 就能识别出新轮换的密钥。 "},"4.配置文件/3.4.seal_transit.html":{"url":"4.配置文件/3.4.seal_transit.html","title":"Vault Transit","keywords":"","body":"transit 封印 Transit 封印配置了 Vault 如何使用 Transit 机密引擎封装封印密钥。Transit 封印可以由以下任一方式激活: Vault 配置文件中设置了 seal \"transit\" 块 环境变量 VAULT_SEAL_TYPE 的值被设置为 transit transit 例子 下面的例子显示了通过 Vault 配置文件提供所有必要的值来配置 Transit 封印: seal \"transit\" { address = \"https://vault:8200\" token = \"s.Qf1s5zigZ4OX6akYjQXJC1jY\" disable_renewal = \"false\" // Key configuration key_name = \"transit_key_name\" mount_path = \"transit/\" namespace = \"ns1/\" // TLS Configuration tls_ca_cert = \"/etc/vault/ca_cert.pem\" tls_client_cert = \"/etc/vault/client_cert.pem\" tls_client_key = \"/etc/vault/ca_cert.pem\" tls_server_name = \"vault\" tls_skip_verify = \"false\" } transit 参数 以下参数可以在 Vault 配置文件的 seal 配置节当中使用: address (string: ): 用以解封的 Vault 集群的完整地址。可以通过设置环境变量 VAULT_ADDR 来配置。 token (string: ): 使用的 Vault 令牌。可以通过设置环境变量 VAULT_TOKEN 来配置。 key_name (string: ): 用以加解密的 transit 引擎 key。可以通过设置环境变量 VAULT_TRANSIT_SEAL_KEY_NAME 来配置。 mount_path (string: ): transit 机密引擎的挂载路径。可以通过设置环境变量 VAULT_TRANSIT_SEAL_MOUNT_PATH 来配置。 namespace (string: \"\"): transit 机密引擎的名字空间。可以通过设置环境变量 VAULT_NAMESPACE 来配置。 disable_renewal (string: \"false\"): 禁用自动续约令牌,以应对令牌是由 Vault 之外管理生命周期的场景。可以通过设置环境变量 VAULT_TRANSIT_SEAL_DISABLE_RENEWAL 来配置。 tls_ca_cert (string: \"\"): 设置用以与 Vault 服务通信的 CA 证书文件的地址。可以通过设置环境变量 VAULT_CACERT 来配置。 tls_client_cert (string: \"\"): 设置用以与 Vault 服务通信的客户端证书文件的地址。可以通过设置环境变量 VAULT_CLIENT_CERT 来配置。 tls_client_key (string: \"\"): 设置用以与 Vault 服务通信的私钥文件的地址。可以通过设置环境变量 VAULT_CLIENT_KEY 来配置。 tls_server_name (string: \"\"): 通过 TLS 连接 Vault 服务时使用的 SNI 主机名。可以通过设置环境变量 VAULT_TLS_SERVER_NAME 来配置。 tls_skip_verify (bool: \"false\"): 关闭验证 TLS 证书。极不推荐使用该选项,它将削弱和 Vault 服务之间数据传输的安全性。可以通过设置环境变量 VAULT_SKIP_VERIFY 来配置。 身份验证 必须设置身份验证参数,可以通过环境变量或是配置文件中的参数。 注意:虽然我们可以在配置文件中配置 VAULT_TOKEN,但我们强烈推荐使用环境变量配置该参数。 配置的 Vault 令牌需要拥有以下 transit key 的权限: path \"/encrypt/\" { capabilities = [\"update\"] } path \"/decrypt/\" { capabilities = [\"update\"] } 密钥轮换 Transit 封印支持轮换 Transit 机密引擎中的密钥。旧密钥不得禁用或删除,并用于解密旧数据。 "},"4.配置文件/4.service_registration.html":{"url":"4.配置文件/4.service_registration.html","title":"service_registration","keywords":"","body":"service_registration 配置节 可选的 service_registration 节配置 Vault 的服务注册机制。 当后端存储配置为使用 Consul 时不需要配置 service_registration 节。如果想用其他的后端存储,例如 Raft 集成存储,但又想使用服务注册的话,可以使用 service_registration 配置节: service_registration \"consul\" { address = \"127.0.0.1:8500\" } storage \"raft\" { path = \"/path/to/raft/data\" node_id = \"raft_node_1\" } 具体的服务发现配置细节,请参阅本章对应的节。 配置 可以通过在 Vault 配置文件中使用 service_registration 配置节来配置服务注册: service_registration [NAME] { [PARAMETERS...] } 例如: service_registration \"consul\" { address = \"127.0.0.1:8500\" } 配置参数也可以通过环境变量配置,环境变量的优先级高于配置文件。 "},"4.配置文件/4.1.consul_service_registration.html":{"url":"4.配置文件/4.1.consul_service_registration.html","title":"Consul","keywords":"","body":"Consul 服务注册 Consul 服务注册将 Vault 注册为 Consul 中的一个服务,并配置默认服务检查。 HashiCorp 官方支持 —— Consul 服务注册由 HashiCorp 提供官方支持 service_registration \"consul\" { address = \"127.0.0.1:8500\" } 正确配置后,应该可以在以下位置使用和访问解封的 Vault 服务: active.vault.service.consul 可以通过以下地址访问到解封后处于待机状态的 Vault 实例: standby.vault.service.consul 可以通过以下地址访问所有解封后的 Vault 实例: vault.service.consul 封印的 Vault 实例会将他们自己标记为不健康,以避免 Consul 服务发现层将它们返回。 consul 参数 address (string: \"127.0.0.1:8500\") – 设置用来通信的 Consul agent 地址。它可以是一个 IP 地址、DNS 记录,或是 unix socket。推荐使用本地 Consul agent 而不是直接与 Consul 服务通信。 check_timeout (string: \"5s\") – 设置发回健康检查数据的间隔。该参数的后缀类似于 \"30s\" 或是 \"1h\"。 disable_registration (string: \"false\") – 设置是否禁止 Vault 该将其注册到 Consul 中。 scheme (string: \"http\") – 设置与 Consul 通信使用的协议。可以设置为 http 或是 https。与非本机 Consul 通信时强烈建议使用 https。当使用 unix socket 时,该参数将被忽略。 service (string: \"vault\") – 设置注册到 Consul 中的服务名称。 service_tags (string: \"\") – 设置以逗号分隔的附加在注册到 Consul 服务上的一组标签。 service_address (string: nil) – 设置一个特定的在 Consul 中注册的服务地址。如果没有设置该参数,Vault 将使用它所知道的 HA 重定向地址——这通常是推荐使用的地址。将此参数设置为 \"\" 将告诉 Consul 利用动态注册服务的节点的配置。如果打算利用 Consul 的 translate_wan_addrs 参数时该参数可能会有所帮助。 token (string: \"\") – 设置注册 Consul 服务时使用的 Consul ACL 令牌。该令牌并不是 Vault 令牌。 通过加密连接与 Consul 通信时可以使用下面的设置。可以在 Consul 加密页面上阅读有关加密 Consul 连接的更多信息。 tls_ca_file (string: \"\") – 设置用以与 Consul 通信使用的 CA 证书。默认使用系统捆绑包。该参数应根据 Consul 的 ca_file 配置来设定。 tls_cert_file (string: \"\") (optional) – 设置用以与 Consul 通信使用的证书。该参数应根据 Consul 的 cert_file 配置来设定。 tls_key_file (string: \"\") – 设置用以与 Consul 通信所使用的私钥文件的路径。该参数应根据 Consul 的 key_file 配置来设定。 tls_min_version (string: \"tls12\") – 设置所使用的 TLS 最低版本。可以设置的值有:\"tls10\"、\"tls11\"、\"tls12\"以及\"tls13\"。 tls_skip_verify (string: \"false\") – 关闭对 TLS 证书的验证。强烈不推荐使用该选项。 ACLs 如果启用了 Consul 的 ACL,我们必须为 Vault 服务注册配置合适的权限。如果服务名称为 vault,下列权限将会适用于大多数场景: { \"service\": { \"vault\": { \"policy\": \"write\" } } } consul 例子 本地 Agent 该例子演示了通过运行在 127.0.0.1:8500 上的本地 Consul Agent 与 Consul 通信的简单配置: service_registration \"consul\" {} 自定义细节 该例子演示了使用自定义地址以及 ACL 令牌与 Consul 通信: service_registration \"consul\" { address = \"10.5.7.92:8194\" token = \"abcd1234\" } 通过 Unix Socket 与 Consul 通信 该例子演示了如何通过本地 unix socket 与 Consul 通信: service_registration \"consul\" { address = \"unix:///tmp/.consul.http.sock\" } 自定义 TLS 该例子演示了如何使用自定义 CA、证书以及私钥文件基于 TLS 安全地与 Consul 通信: service_registration \"consul\" { scheme = \"https\" tls_ca_file = \"/etc/pem/vault.ca\" tls_cert_file = \"/etc/pem/vault.cert\" tls_key_file = \"/etc/pem/vault.key\" } "},"4.配置文件/4.2.k8s_service_registration.html":{"url":"4.配置文件/4.2.k8s_service_registration.html","title":"Kubernetes","keywords":"","body":"Kubernetes 服务注册 Kubernetes 服务注册将 Vault pod 的当前状态更新到它们的 tag 上以便 selector 可以用来筛选。Vault 只有以高可用模式运行时才可以使用服务注册。 HashiCorp 官方支持 —— Kubernetes 服务注册由 HashiCorp 提供官方支持。 配置 service_registration \"kubernetes\" { namespace = \"my-namespace\" pod_name = \"my-pod-name\" } 另外,namespace 和 pod name 也可以通过环境变量设置: VAULT_K8S_NAMESPACE VAULT_K8S_POD_NAME 我们可以该机制通过 Downward API 设置这些参数。 如果只使用环境变量,那么仍然需要声明服务注册配置节以表明我们的意图: service_registration \"kubernetes\" {} 要成功使用服务注册,Vault 必须能够在 Kubernetes 中相关 Pod 上应用标签(label)。Vault pod 需要以下 RBAC 规则来获取更新自身定义(specification)的 service account 权限: kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: namespace: mynamespace name: vault-service-account rules: - apiGroups: [\"\"] resources: [\"pods\"] verbs: [\"get\", \"update\"] 例子 经过正确配置后,启用服务注册会导致 Kubernetes 的 pod 被应用以下标签: apiVersion: v1 kind: Pod metadata: name: vault labels: vault-active: \"false\" vault-initialized: \"true\" vault-perf-standby: \"false\" vault-sealed: \"false\" vault-version: 1.3.0 集群关闭后,Vault 的 pod 会被打上以下标签: apiVersion: v1 kind: Pod metadata: name: vault labels: vault-active: \"false\" vault-initialized: \"false\" vault-perf-standby: \"false\" vault-sealed: \"true\" vault-version: 1.3.0 标签定义 vault-active (string: \"true\"/\"false\") – 该标签状态在 Vault 集群主从关系发生变化时会动态更新。true 代表该 pod 为集群领导者。false 代表该 pod 当前为从节点。 vault-initialized (string: \"true\"/\"false\") – 该标签状态在 Vault 的初始化状态发生变化时会动态更新。true 代表 Vault 已经初始化。false 代表 Vault 目前未经初始化。 vault-perf-standby (string: \"true\"/\"false\") – 该标签状态在 Vault 的性能备用集群的 领导者/从节点 状态发生变化时会动态更新。该标签只有在 pod 是一个性能备用集群成员时才有意义,否则它将被设置为 fasle。true 代表该 pod 目前是性能备用集群从节点。false 代表该 pod 目前是性能备用集群主节点。 vault-sealed (string: \"true\"/\"false\") – 该标签状态在 Vault 的封印状态发生变化时会动态更新。true 代表 Vault 目前处于封印状态。false 代表 Vault 已经解封。 vault-version (string: \"1.3.0\") – 该标签显示了 Vault 版本,在 pod 的生命周期内不会变化。 使用 Vault 的服务发现标签 服务的例子 通过将标签应用于 pod,创建服务时可以使用选择器来过滤具有特定 Vault 高可用角色的 pod,从而使得可以通过服务与 Vault pod 的子集进行直接通信。注意下面例子中的 vault-active: \"true\": apiVersion: v1 kind: Service metadata: labels: app.kubernetes.io/instance: vault app.kubernetes.io/name: vault helm.sh/chart: vault-0.1.2 name: vault-active-us-east namespace: default spec: clusterIP: 10.7.254.51 ports: - name: http port: 8200 protocol: TCP targetPort: 8200 - name: internal port: 8201 protocol: TCP targetPort: 8201 publishNotReadyAddresses: false selector: app.kubernetes.io/instance: vault app.kubernetes.io/name: vault component: server vault-active: \"true\" type: ClusterIP 同样,通过设置 publishNotReadyAddresses: false,宕机的 pod 会被从服务中移除。 通过设置指向主节点的服务,我们始终可以通过特定端点访问到集群主节点。在设置 Vault 复制时,这将会是一个非常有用的主地址: $ vault write -f sys/replication/performance/primary/enable \\ primary_cluster_addr='https://vault-active-us-east:8201' 更新的例子 结合 pod 标签以及 OnDelete 更新策略,更新将变得更加容易编排: $ helm upgrade vault --set='server.image.tag=1.4.0' $ kubectl delete pod --selector=vault-active=false \\ --selector=vault-version=1.2.3 $ kubectl delete pod --selector=vault-active=true \\ --selector=vault-version=1.2.3 删除特定 pod 实例时,定义了集群所需状态的 Statefulset 将使用最新镜像重新调度已删除的 Pod。 "},"4.配置文件/5.storage.html":{"url":"4.配置文件/5.storage.html","title":"storage","keywords":"","body":"storage 配置节 storage 节配置存储后端,代表了 Vault 信息的持久化存储位置。每个后端都有优点、缺点、优势和权衡。例如,一些后端支持高可用性,而另一些则提供更强大的备份和恢复功能。有关特定后端的信息,阅读相关章节。 配置 可以通过 Vault 配置文件中的 storage 配置节来设置存储后端配置: storage [NAME] { [PARAMETERS...] } 比如说: storage \"file\" { path = \"/mnt/vault/data\" } 某些配置选项也可以通过环境变量来配置,这种时候环境变量的优先级高于配置文件。 "},"4.配置文件/5.1.storage_AlicloudOss.html":{"url":"4.配置文件/5.1.storage_AlicloudOss.html","title":"Alicloud OSS","keywords":"","body":"阿里云 OSS 存储后端 阿里云 OSS 存储后端可以用来将 Vault 的数据保存到阿里云 OSS 存储桶中。 无高可用集群支持 —— 阿里云 OSS 存储后端不支持高可用集群。 仅有社区支持 —— 阿里云 OSS 存储后端由社区支持。虽然它已经过 HashiCorp 员工的审查,但他们可能对该技术并不了解。如果遇到问题,该问题可能会被转交给原作者。 storage \"alicloudoss\" { access_key = \"abcd1234\" secret_key = \"defg5678\" endpoint = \"oss-us-west-1.aliyuncs.com\" bucket = \"my-bucket\" } alicoudoss 参数 bucket (string: ) – 设置使用的 OSS 存储桶名称。该参数也可以通过环境变量 ALICLOUD_OSS_BUCKET 来设置。 endpoint (string: \"\") – 设置使用的 OSS 端点。该参数也可以通过环境变量 ALICLOUD_OSS_ENDPOINT 来设置。 access_key – 设置使用的阿里云 access key。该参数也可以通过环境变量 ALICLOUD_ACCESS_KEY 来设置。 secret_key – 设置使用的阿里云 secret key。该参数也可以通过环境变量 ALICLOUD_SECRET_KEY 来设置。 max_parallel (string: \"128\") – 设置与阿里云 OSS 通信的最大并行请求数。 alicloudoss 例子 默认例子 该例子演示了如何使用阿里云 OSS 作为存储后端: storage \"alicloudoss\" { access_key = \"abcd1234\" secret_key = \"defg5678\" endpoint = \"oss-us-west-1.aliyuncs.com\" bucket = \"my-bucket\" } "},"4.配置文件/5.2.storage_Azure.html":{"url":"4.配置文件/5.2.storage_Azure.html","title":"Azure","keywords":"","body":"Azure 存储后端 Azure 存储后端可以用来将 Vault 的数据保存到 Azure 存储容器中。存储容器必须已经存在,并且配置的帐户凭据必须对存储容器具有读写权限。存储帐户类型必须支持 block blob。目前可以使用 Standard 性能(Premium 将不起作用)的通用目的 \"Storage V2\" 类型,或是 Premium 的 \"Block Blob Storage\"(仅在少数区域可用)。 无高可用集群支持 —— Azure 存储后端不支持高可用集群。 仅有社区支持 —— Azure 存储后端由社区支持。虽然它已经过 HashiCorp 员工的审查,但他们可能对该技术并不了解。如果遇到问题,该问题可能会被转交给原作者。 storage \"azure\" { accountName = \"my-storage-account\" accountKey = \"abcd1234\" container = \"container-efgh5678\" environment = \"AzurePublicCloud\" } 当前实现将每个 blob 的最大尺寸限制在 4Mb。 azure 参数 accountName (string: ) – 设置使用的 Azure Storage Account 名称。 accountKey (string: ) – 设置使用的 Azure Account Key。如果没有设置该参数,将使用 Managed Identity 身份验证。 container (string: ) – 设置使用的 Azure Storage Blob Container 名称。 environment (string: \"AzurePublicCloud\") - 设置使用的 Storage Account 所属的云环境,以 Azure GO SDK 中规定的大小写敏感的名字定义。 arm_endpoint (string: \"\") - 通过 Azure Resource Manager 端点 URL 的形式设置使用的 Storage Account 所属的云环境 max_parallel (string: \"128\") – 设置与 Azure 通信的最大并行请求数。 azure 例子 该例子演示了如何使用自定义的最大并发连接数配置 Azure 存储后端: storage \"azure\" { accountName = \"my-storage-account\" accountKey = \"abcd1234\" container = \"container-efgh5678\" max_parallel = 512 } "},"4.配置文件/5.3.storage_consul.html":{"url":"4.配置文件/5.3.storage_consul.html","title":"Consul","keywords":"","body":"Consul 存储后端 Consul 存储后端可以用来将 Vault 的数据保存到Consul 键值存储中。除了提供持久存储之外,使用此存储后端还将在 Consul 中将 Vault 注册为具有默认健康检查的服务。 支持高可用集群 —— Consul 存储后端支持高可用集群。 HashiCorp 官方支持 —— HashiCorp 提供对 Consul 存储后端的官方支持 storage \"consul\" { address = \"127.0.0.1:8500\" path = \"vault\" } 正确配置后,应该可以在以下位置使用和访问解封的 Vault 服务: active.vault.service.consul 可以通过以下地址访问到解封后处于待机状态的 Vault 实例: standby.vault.service.consul 可以通过以下地址访问所有解封后的 Vault 实例: vault.service.consul 封印的 Vault 实例会将他们自己标记为不健康,以避免 Consul 服务发现层将它们返回。 需要注意的是如果为 Vault 配置了多个侦听器,我们必须通过 api_addr 和 cluster_addr 指定 Consul 通告哪一个地址 (例子)。 consul 参数 address (string: \"127.0.0.1:8500\") – 设置用来通信的 Consul agent 地址。它可以是一个 IP 地址、DNS 记录,或是 unix socket。推荐使用本地 Consul agent 而不是直接与 Consul 服务通信。 check_timeout (string: \"5s\") – 设置发回健康检查数据的间隔。该参数的后缀类似于 \"30s\" 或是 \"1h\"。 consistency_mode (string: \"default\") – 设置 Consul 的一致性模式。可以设置的值有 \"default\" 或是 \"strong\"。 disable_registration (string: \"false\") – 设置是否禁止 Vault 该将其注册到 Consul 中。 max_parallel (string: \"128\") – 设置与 Consul 通信的最大并行请求数。需要提前确认 Consul agent 被设置为支持这种程度的并发度,请参照 http_max_conns_per_client。 path (string: \"vault/\") – 设置 Vault 数据要存储的 Consul 键值存储路径。 scheme (string: \"http\") – 设置与 Consul 通信使用的协议。可以设置为 http 或是 https。与非本机 Consul 通信时强烈建议使用 https。当使用 unix socket 时,该参数将被忽略。 service (string: \"vault\") – 设置注册到 Consul 中的服务名称。 service_tags (string: \"\") – 设置以逗号分隔的附加在注册到 Consul 服务上的一组标签。 service_address (string: nil) – 设置一个特定的在 Consul 中注册的服务地址。如果没有设置该参数,Vault 将使用它所知道的 HA 重定向地址——这通常是推荐使用的地址。将此参数设置为 \"\" 将告诉 Consul 利用动态注册服务的节点的配置。如果打算利用 Consul 的 translate_wan_addrs 参数时该参数可能会有所帮助。 token (string: \"\") – 设置读写 Consul 键值存储中 path 位置的数据时使用的 Consul ACL 令牌。该令牌并不是 Vault 令牌。也可以通过设置环境变量 CONSUL_HTTP_TOKEN 来设置该参数。 session_ttl (string: \"15s\") - 设置允许的最小的会话(session) TTL。Consul 服务每一个会话默认有 10s 的 TTL 下限。这里配置的 session_ttl 不可以比 10s 小,除非 Consul 服务配置中的 session_ttl_min 配置了一个更小的值。 lock_wait_time (string: \"15s\") - 设置获取锁操作的等待时间。该参数会影响取消锁定获取所需的最短时间。 通过加密连接与 Consul 通信时可以使用下面的设置。可以在 Consul 加密页面上阅读有关加密 Consul 连接的更多信息。 tls_ca_file (string: \"\") – 设置用以与 Consul 通信使用的 CA 证书。默认使用系统捆绑包。该参数应根据 Consul 的 ca_file 配置来设定。 tls_cert_file (string: \"\") (optional) – 设置用以与 Consul 通信使用的证书。该参数应根据 Consul 的 cert_file 配置来设定。 tls_key_file (string: \"\") – 设置用以与 Consul 通信所使用的私钥文件的路径。该参数应根据 Consul 的 key_file 配置来设定。 tls_min_version (string: \"tls12\") – 设置所使用的 TLS 最低版本。可以设置的值有:\"tls10\"、\"tls11\"、\"tls12\"以及\"tls13\"。 tls_skip_verify (string: \"false\") – 关闭对 TLS 证书的验证。强烈不推荐使用该选项。 ACLs 如果启用了 Consul 的 ACL,我们必须为 Vault 服务注册配置合适的权限。如果使用 Consul 0.8,服务名称为 vault 并且使用的前缀是 vault/,下列权限将会适用于大多数场景: { \"key\": { \"vault/\": { \"policy\": \"write\" } }, \"service\": { \"vault\": { \"policy\": \"write\" } }, \"agent\": { \"\": { \"policy\": \"read\" } }, \"session\": { \"\": { \"policy\": \"write\" } } } 如果使用的是 Consul 1.4+,请使用以下例子来配置账户权限: { \"key_prefix\": { \"vault/\": { \"policy\": \"write\" } }, \"service\": { \"vault\": { \"policy\": \"write\" } }, \"agent_prefix\": { \"\": { \"policy\": \"read\" } }, \"session_prefix\": { \"\": { \"policy\": \"write\" } } } consul 例子 使用本机 Agent 该例子演示了使用运行在 127.0.0.1:8500 上的本机 Consul Agent 作为物理存储后端: storage \"consul\" {} 自定义细节 该例子演示了使用自定义地址与 ACL 令牌与 Consul 通信: storage \"consul\" { address = \"10.5.7.92:8194\" token = \"abcd1234\" } 自定义存储路径 该例子演示了将数据存储在 Consul 键值存储的自定义路径上。如果 Consul 启用了 ACL 那么使用的 ACL 令牌必须对该路径拥有读写权限: storage \"consul\" { path = \"vault/\" } 使用 Unix Socket 与 Consul 通信 该例子演示了通过本机 unix socket 与 Consul 通信: storage \"consul\" { address = \"unix:///tmp/.consul.http.sock\" } 自定义 TLS 该例子演示了如何使用自定义 CA、证书以及私钥文件基于 TLS 安全地与 Consul 通信: storage \"consul\" { scheme = \"https\" tls_ca_file = \"/etc/pem/vault.ca\" tls_cert_file = \"/etc/pem/vault.cert\" tls_key_file = \"/etc/pem/vault.key\" } "},"4.配置文件/5.4.storage_dynamodb.html":{"url":"4.配置文件/5.4.storage_dynamodb.html","title":"DynamoDB","keywords":"","body":"DynamoDB 存储后端 DynamoDB 存储后端可以用来将 Vault 的数据保存到 DynamoDB 表中。 支持高可用集群 —— DynamoDB 存储后端支持高可用集群。由于 DynamoDB 使用 Vault 节点上的时间来实现锁的会话生命周期,所以 Vault 节点之间显著的时钟偏差可能会导致锁上的争用问题。 仅有社区支持 —— DynamoDB 存储后端由社区支持。虽然它已经过 HashiCorp 员工的审查,但他们可能对该技术并不了解。如果遇到问题,该问题可能会被转交给原作者。 storage \"dynamodb\" { ha_enabled = \"true\" region = \"us-west-2\" table = \"vault-data\" } 更多关于 DynamoDB 表的读写权限的信息,请参阅 AWS DynamoDB 官方文档。 DynamoDB 参数 endpoint (string: \"\") – 指定一个非默认的,兼容 AWS 规范的 DynamoDB 端点。该参数也可以通过设置环境变量 AWS_DYNAMODB_ENDPOINT 来设置。(可用来配置使用兼容 DynamoDB 协议的其他存储服务) ha_enabled (string: \"false\") – 配置该后端是否被用以运行在 Vault 高可用模式下。可选参数为 true 或是 false。该参数也可以通过设置环境变量 DYNAMODB_HA_ENABLED 来设置。 max_parallel (string: \"128\") – 设置最大并行请求数。 region (string \"us-east-1\") – 设置 AWS region。该参数也可以通过设置环境变量 AWS_DEFAULT_REGION 来设置。 read_capacity (int: 5) – 设置表上每秒可以执行多少次读操作,该参数在 Vault 创建 DynamoDB 表时生效,如果 table 已经存在则不起作用。该参数也可以通过设置环境变量 AWS_DYNAMODB_READ_CAPACITY 来设置。 table (string: \"vault-dynamodb-backend\") – 设置用来存储 Vault 数据的 DynamoDB 表名称。如果指定的表并不存在,那么在初始化时会创建该表。该参数也可以通过设置环境变量 AWS_DYNAMODB_TABLE 来设置。请阅读下方表结构 获取表结构信息。 write_capacity (int: 5) – 设置表上每秒可以执行多少次写操作,该参数在 Vault 创建 DynamoDB 表时生效,如果 table 已经存在则不起作用。该参数也可以通过设置环境变量 AWS_DYNAMODB_WRITE_CAPACITY 来设置。 下列参数用于向 AWS 进行身份验证。如果您在 EC2 实例上运行 Vault 服务器,您还可以使用 EC2 实例配置文件服务来提供 Vault 用于调用 DynamoDB API 的凭据。将 access_key 和 secret_key 字段留空将导致 Vault 尝试从 AWS 元数据服务检索凭证。 access_key (string: ) – 设置 AWS access key。该参数也可以通过设置环境变量 AWS_ACCESS_KEY_ID 来设置。 secret_key (string: ) – 设置 AWS secret key。该参数也可以通过设置环境变量 AWS_SECRET_ACCESS_KEY 来设置。 session_token (string: \"\") – 设置 AWS session token。该参数也可以通过设置环境变量 AWS_SESSION_TOKEN 来设置。 必要的 AWS 权限 Vault 所使用的 IAM 用户或是 EC2 实例配置文件所拥有的策略必须包含以下权限以使 Vault 可以顺利地对 DynamDB 表执行必要的操作: \"Statement\": [ { \"Action\": [ \"dynamodb:DescribeLimits\", \"dynamodb:DescribeTimeToLive\", \"dynamodb:ListTagsOfResource\", \"dynamodb:DescribeReservedCapacityOfferings\", \"dynamodb:DescribeReservedCapacity\", \"dynamodb:ListTables\", \"dynamodb:BatchGetItem\", \"dynamodb:BatchWriteItem\", \"dynamodb:CreateTable\", \"dynamodb:DeleteItem\", \"dynamodb:GetItem\", \"dynamodb:GetRecords\", \"dynamodb:PutItem\", \"dynamodb:Query\", \"dynamodb:UpdateItem\", \"dynamodb:Scan\", \"dynamodb:DescribeTable\" ], \"Effect\": \"Allow\", \"Resource\": [ \"arn:aws:dynamodb:us-east-1:... dynamodb table ARN\" ] }, 表结构 如果要在 Vault 初始化之前预先创建 DynamoDB 表,我们需要创建包含以下属性(attribute)的表: 主分区键(Primary partition key): \"Path\",string 类型 主排序键(Primary sort key):\"Key\",string 类型 我们可以用 Terraform 来创建这样的表: resource \"aws_dynamodb_table\" \"dynamodb-table\" { name = \"${var.dynamoTable}\" read_capacity = 1 write_capacity = 1 hash_key = \"Path\" range_key = \"Key\" attribute = [ { name = \"Path\" type = \"S\" }, { name = \"Key\" type = \"S\" } ] tags { Name = \"vault-dynamodb-table\" Environment = \"prod\" } } 如果要创建的表已经存在,那么 Vault 不会修改它 —— Vault 配置文件中的 read_capacity 和 write_capacity 值将不生效。 如果要创建的表尚不存在,Vault 会尝试创建它,并且分别应用Vault 配置文件中的 read_capacity 和 write_capacity 值。 AWS 读取实例元数据超时 影响 Vault 1.4 及以后的版本 Vault 在 EC2 实例上使用实例元数据服务的时候,例如从实例配置文件中获取凭据,使用到实例元数据服务 (IMDSv2) 的 v2 版本时可能会出现延迟。 Vault 使用的 AWS SDK 首先尝试连接到 IMDSv2,如果超时,则回退到 v1。在 Vault 1.4 中,此超时最多可能需要 2 分钟。在 Vault 1.5.5 及更高版本中最多可能需要 2 秒钟。 超时会发生在 Vault 和 IMDSv2 之间存在代理,并且实例 hop 限制设置为小于 Vault 和 IMDSv2 之间的“hop”数的时候。例如,如果 Vault 在 EC2 实例上的 docker 中运行,并且实例 hop 限制设置为 1,AWS SDK 客户端将尝试连接到 IMDSv2,超时并回退到 IMDSv1,因为 docker 和 IMDS 之间存在额外的网络 hop. 为避免超时行为,可以在底层 EC2 实例上调整 hop 限制。对于 docker 示例,将 hop 限制设置为 2 就可以让 Vault 中的 AWS SDK 无延迟地连接到 IMDSv2。 Vault 配置使用 DynamoDB 的例子 使用自定义的表读写限制 该例子演示了使用自定义的读/写能力配置创建表: storage \"dynamodb\" { table = \"my-vault-data\" read_capacity = 10 write_capacity = 15 } 启用高可用 该例子演示了为 DynamoDB 存储后端启用高可用: api_addr = \"https://vault-leader.my-company.internal\" storage \"dynamodb\" { ha_enabled = \"true\" ... } "},"4.配置文件/5.5.storage_etcd.html":{"url":"4.配置文件/5.5.storage_etcd.html","title":"Etcd","keywords":"","body":"Etcd 存储后端 Etcd 存储后端可以用来将 Vault 的数据保存到Etcd 中。该存储后端同时支持 v2 和 v3 版本的 Etcd API,并且会根据集群的版本自动判定使用的协议版本。 注意:随着 Etcd v3.5 的发布, Etcd v2 API 已被废弃,并且在 Etcd v3.6 中退役。它将在 Vault 1.1.0 中被移除。Vault Etcd 存储后端的用户需要在使用 Vault 1.1.0 之前准备将 Vault 存储迁移到 Etcd v3 集群中。执行迁移之前需要先执行备份。 支持高可用集群 —— Etcd 存储后端支持高可用集群。v2 版本 API 在搭配高可用时存在问题,不可以用于高可用场景。 仅有社区支持 —— Etcd 存储后端由 CoreOS 提供支持。虽然它已经过 HashiCorp 员工的审查,但他们可能对该技术并不了解。如果遇到问题,该问题可能会被转交给原作者。 storage \"etcd\" { address = \"http://localhost:2379\" etcd_api = \"v3\" } etcd 参数 address (string: \"http://localhost:2379\") – 使用逗号分隔的列表来配置使用的 Etcd 实例地址。该参数也可以通过环境变量 ETCD_ADDR 来设置。 discovery_srv (string: \"example.com\") - 设置用以查询描述集群端点的 SRV 记录的域名。该参数也可以通过环境变量 ETCD_DISCOVERY_SRV 来设置。 discovery_srv_name (string: \"vault\") - 设置用以描述集群端点的 SRV 记录的服务名。该参数也可以通过环境变量 ETCD_DISCOVERY_SRV_NAME 来设置。 etcd_api (string: \"\") – 设置通信所使用的 API 版本。默认情况下这是自动派生的。如果集群版本是 3.1+ 并且尚无数据通过 v2 API 被写入过,那么将自动判定使用 v3。 ha_enabled (string: \"false\") – 设置是否自动启用高可用。该参数也可以通过环境变量 ETCD_HA_ENABLED 来设置。 path (string: \"/vault/\") – 设置 Vault 数据在 Etcd 中存储的路径。 sync (string: \"true\") – 设置是否在启动时同步可用 Etcd 服务的列表。该参数是一个强制转换成 boolean 值的字符串。如果集群处于代理服务器之后并且同步导致 Vault 服务失败,您可能需要将此设置为 false。 username (string: \"\") – 设置 Etcd 服务身份验证时使用的用户名。该参数也可以通过环境变量 ETCD_USERNAME 来设置。 password (string: \"\") – 设置 Etcd 服务身份验证时使用的密码。该参数也可以通过环境变量 ETCD_PASSWORD 来设置。 tls_ca_file (string: \"\") – 设置与 Etcd 通信使用的 CA 证书的路径。默认使用系统捆绑包。 tls_cert_file (string: \"\") – 设置与 Etcd 通信使用的证书的路径。 tls_key_file (string: \"\") – 设置与 Etcd 通信使用的私钥文件的路径。 request_timeout (string: \"5s\") – 设置发往 Etcd 的请求的超时时间。大多数场景下 5 秒钟足以,即使是考虑到内部重试的场景。 lock_timeout (string: \"15s\") – 设置竞选 Vault 主节点的锁超时时间。如果不需要快速恢复集群,可以设置一个比较大的值。 etcd 例子 使用 DNS 发现机制发现集群成员 该例子演示了配置 Vault 根据 DNS 发现协议文档中相关记录来使用 SRV 记录发现 Etcd 集群成员: storage \"etcd\" { discovery_srv = \"example.com\" } 自定义身份验证 该例子演示了使用用户名和密码连接 Etcd 集群: storage \"etcd\" { username = \"user1234\" password = \"pass5678\" } 自定义路径 该例子演示了将数据存储在自定义路径上: storage \"etcd\" { path = \"my-vault-data/\" } 启用高可用 该例子演示了为 Etcd 存储后端启用高可用: api_addr = \"https://vault-leader.my-company.internal\" storage \"etcd\" { ha_enabled = \"true\" ... } "},"4.配置文件/5.6.storage_filesystem.html":{"url":"4.配置文件/5.6.storage_filesystem.html","title":"Filesystem","keywords":"","body":"Filesystem 存储后端 Filesystem 存储后端可以用来将 Vault 的数据保存到标准目录结构的文件系统中。该后端可用于单机持久化存储场景,或是用作非关键的本地开发服务器。 无高可用集群支持 —— Filesystem 存储后端不支持高可用集群。 HashiCorp 官方支持 —— HashiCorp 提供对 Filesystem 存储后端的官方支持。 storage \"file\" { path = \"/mnt/vault/data\" } 尽管 Vault 存储的数据都是加密的,我们仍然需要采取合适的措施来限制对文件系统的访问权限。 file 参数 path (string: ) - 在磁盘上保存数据的目录的绝对路径。如果该目录不存在,Vault 会创建一个。 file 例子 该例子演示了将数据存储在 /mnt/vault/data: storage \"file\" { path = \"/mnt/vault/data\" } "},"4.配置文件/5.7.storage_inmem.html":{"url":"4.配置文件/5.7.storage_inmem.html","title":"In-Memory","keywords":"","body":"In-Memory 存储后端 In-Memory 存储后端将 Vault 数据完全存储在运行 Vault 服务的机器内存中。该后端仅供开发测试及实验使用,强烈不推荐应用于生产环境。当 Vault 服务或是机器重启后所有数据都将丢失。 无高可用集群支持集群 —— In-Memory 存储后端不支持高可用集群。 不推荐应用于生产环境 —— 我们不推荐在生产环境使用 In-Memory 后端,因为重启后数据会全部丢失。 HashiCorp 官方支持 —— HashiCorp 提供对 In-Memory 存储后端的官方支持。 storage \"inmem\" {} inmem 参数 In-Memory 存储后端没可配置的参数。 inmem 例子 以下例子演示了启用 In-Memory 存储后端: storage \"inmem\" {} "},"4.配置文件/5.8.storage_mssql.html":{"url":"4.配置文件/5.8.storage_mssql.html","title":"MSSQL","keywords":"","body":"MSSQL 存储后端 MSSQL 存储后端可以用来将 Vault 的数据保存到微软 SQL Server中。 无高可用集群支持 —— MSSQL 存储后端不支持高可用集群。 仅有社区支持 —— MSSQL 存储后端由社区提供支持。虽然它已经过 HashiCorp 员工的审查,但他们可能对该技术并不了解。如果遇到问题,该问题可能会被转交给原作者。 storage \"mssql\" { server = \"localhost\" port = 1433 username = \"user1234\" password = \"secret123!\" database = \"vault\" table = \"vault\" appname = \"vault\" schema = \"dbo\" connectionTimeout = 30 logLevel = 0 } mssql 参数 server (string: ) – 主机或主机/实例。 port (int: 1433) – 设置 MSSQL 端口号。 username (string: \"\") - 设置用以进行 SQL Server 身份验证的用户 id 或是以 DOMAIN\\User 格式提供的 Windows 身份验证用户 id。在 Windows 平台上,如果启用了单点登录,则用户 id 可以不设置或是设置为空字符串。 password (string: \"\") – 设置用来连接数据库的 MSSQL 密码。 database (string: \"Vault\") – 设置使用的数据库名称。如果该数据库尚不存在,那么 Vault 会尝试创建一个。 table (string: \"Vault\") – 设置使用的表名。如果该表尚不存在,Vault 会尝试创建一个。 schema (string: \"dbo\") – 设置使用的 schema。如果该 schema 尚不存在,Vault 会尝试创建一个。 appname (string: \"Vault\") – 应用名称。 connectionTimeout (int: 30) – 连接超时时间,以秒计数(默认为 30 秒)。 logLevel (int: 0) – 日志标志(默认为 0/不记录日志,63 为完整日志)。 max_parallel (string: \"128\") – 设置发往 MSSQL 的最大并行请求数。 mssql 例子 自定义数据库、表和 Schema 该例子演示了使用自定义数据库和表名配置 MSSQL 后端: storage \"mssql\" { database = \"my-vault\" table = \"vault-data\" schema = \"vlt\" username = \"user1234\" password = \"pass5678\" } "},"4.配置文件/5.9.storage_mysql.html":{"url":"4.配置文件/5.9.storage_mysql.html","title":"MySQL","keywords":"","body":"MySQL 存储后端 MySQL 存储后端可以用来将 Vault 的数据保存到 MySQL 服务器或是集群中。 支持高可用集群 —— MySQL 存储后端支持高可用集群。请注意由于 MySQL 锁函数的工作原理,如果连接中断则锁也会丢失。如果不希望选举的领导者频繁发生变更,我们可以大幅增加 MySQL 配置文件中的 interactive_timeout 和 wait_timeout 的值,远大于默认的 8 小时。 仅有社区支持 —— MySQL 存储后端由社区提供支持。虽然它已经过 HashiCorp 员工的审查,但他们可能对该技术并不了解。如果遇到问题,该问题可能会被转交给原作者。 storage \"mysql\" { username = \"user1234\" password = \"secret123!\" database = \"vault\" } mysql 参数 address (string: \"127.0.0.1:3306\") – 设置 MySQL 主机的地址。 database (string: \"vault\") – 设置使用的数据库名称。如果该数据库尚不存在,Vault 会尝试创建一个。 table (string: \"vault\") – 设置使用的表名。如果该表尚不存在,Vault 会尝试创建一个。 tls_ca_file (string: \"\") – 设置使用 TLS 连接所需的 CA 证书路径 plaintext_credentials_transmission (string: \"\") - 设置通过明文发送凭据的授权。如果没有设置该参数并且没有设置 TLS CA 证书会触发关于通过纯文本发送凭据的警告。在将来的版本中,未能确认或使用 TLS 将导致 Vault 服务无法启动。这样做是为了确保凭据不会意外泄露。 max_parallel (string: \"128\") – 设置发往 MySQL 的最大并行请求数。 max_idle_connections (string: \"0\") – 设置与服务器之间最大的闲置连接数。设置为 0 将配置成默认的 2 个闲置连接,设置为负数则将禁用闲置连接。该参数的值如果大于 max_parallel 将被减少到与其相同。 max_connection_lifetime (string: \"0\") – 设置一个连接可被重用的最大秒数。如果设置为 另外,Vault 要求必须设置以下身份验证相关信息: username (string: ) – 设置连接数据库使用的 MySQL 用户名。 password (string: ) – 设置连接数据库使用的 MySQL 密码。 高可用参数 ha_enabled (string: \"true\") - 设置是否启用高可用。该参数使用 boolean 值,但必须以字符串形式设置,比如 \"true\" 或是 \"false\"。 lock_table (string: \"vault_lock\") – 设置用来存储高可用信息的表名。默认情况下值为 table 参数的值搭配 _lock 的后缀。如果该表尚不存在,Vault 会尝试创建一个。 mysql 例子 自定义数据库以及表 该例子演示了使用自定义数据库及表名配置 MySQL 后端: storage \"mysql\" { database = \"my-vault\" table = \"vault-data\" username = \"user1234\" password = \"pass5678\" } "},"4.配置文件/5.10.storage_postgres.html":{"url":"4.配置文件/5.10.storage_postgres.html","title":"PostgreSQL","keywords":"","body":"PostgreSQL 存储后端 PostgreSQL 存储后端可以用来将 Vault 的数据保存到 PostgreSQL 服务器或是集群中。 支持高可用集群 —— PostgreSQL 存储后端支持高可用集群。需要 PostgreSQL 9.5 或更高版本。 仅有社区支持 —— PostgreSQL 存储后端由社区提供支持。虽然它已经过 HashiCorp 员工的审查,但他们可能对该技术并不了解。如果遇到问题,该问题可能会被转交给原作者。 storage \"postgresql\" { connection_url = \"postgres://user123:secret123!@localhost:5432/vault\" } 请注意,PostgreSQL 存储后端插件会尝试使用 SSL 连接数据库。如果没有启用 SSL 那么必须配置 connection_url 参数来配置关闭 SSL。请参阅下方内容中关于关闭 SSL 的部分。 PostgreSQL 存储后端不会自动创建表。这里有一份样例 SQL 代码演示如何创建表以及索引: CREATE TABLE vault_kv_store ( parent_path TEXT COLLATE \"C\" NOT NULL, path TEXT COLLATE \"C\", key TEXT COLLATE \"C\", value BYTEA, CONSTRAINT pkey PRIMARY KEY (path, key) ); CREATE INDEX parent_path_idx ON vault_kv_store (parent_path); 启用高可用所需的表: CREATE TABLE vault_ha_locks ( ha_key TEXT COLLATE \"C\" NOT NULL, ha_identity TEXT COLLATE \"C\" NOT NULL, ha_value TEXT COLLATE \"C\", valid_until TIMESTAMP WITH TIME ZONE NOT NULL, CONSTRAINT ha_key PRIMARY KEY (ha_key) ); 如果使用的是 PostgreSQL 9.5 之前的版本,需要创建以下函数: CREATE FUNCTION vault_kv_put(_parent_path TEXT, _path TEXT, _key TEXT, _value BYTEA) RETURNS VOID AS $$ BEGIN LOOP -- first try to update the key UPDATE vault_kv_store SET (parent_path, path, key, value) = (_parent_path, _path, _key, _value) WHERE _path = path AND key = _key; IF found THEN RETURN; END IF; -- not there, so try to insert the key -- if someone else inserts the same key concurrently, -- we could get a unique-key failure BEGIN INSERT INTO vault_kv_store (parent_path, path, key, value) VALUES (_parent_path, _path, _key, _value); RETURN; EXCEPTION WHEN unique_violation THEN -- Do nothing, and loop to try the UPDATE again. END; END LOOP; END; $$ LANGUAGE plpgsql; postgresql 参数 connection_url (string: ) – 设置连接 PostgreSQL 并进行身份验证的连接字符串。该参数也可以通过环境变量 VAULT_PG_CONNECTION_URL 来设置。完整的可用参数列表可以在 pg 库文档中找到。可以阅读本章后续的例子了解连接字符串的例子。 table (string: \"vault_kv_store\") – 设置 Vault 数据存储的表名。该表必须已经存在(Vault 不会尝试创建它)。 max_idle_connections (int) - 该参数默认情况下未经设置。设置空闲连接池的中最多可以容纳多少个空闲连接。请阅读 golang 关于 SetMaxIdleConns 的文档获取更多信息。 该功能需要使用 1.2 及更高版本。 max_parallel (string: \"128\") – 设置发往 PostgreSQL 的最大并行请求数。 ha_enabled (string: \"true|false\") – 是否启用高可用。默认不启用。需要 PostgreSQL 9.5 或更高版本。 ha_table (string: \"vault_ha_locks\") – 设置用以存储高可用信息的表名。该表必须已经存在(Vault 不会尝试创建它)。 postgresql 例子 自定义 SSL 验证 该例子演示了使用完整的 SSL 验证机制连接一个 PostgreSQL 集群(我们推荐这样做): storage \"postgresql\" { connection_url = \"postgres://user:pass@localhost:5432/database?sslmode=verify-full\" } 要关闭 SSL 验证(不推荐这样做),请把 verify-full 替换为 disable: storage \"postgresql\" { connection_url = \"postgres://user:pass@localhost:5432/database?sslmode=disable\" } "},"4.配置文件/5.11.storage_raft.html":{"url":"4.配置文件/5.11.storage_raft.html","title":"Integrated Storage(Raft)","keywords":"","body":"集成存储后端(Raft) 集成存储后端可以用来保存 Vault 的数据。与其他存储后端不同,集成存储并非将数据保存在一个地方,相反,Vault 集群中的所有节点都将拥有 Vault 数据的复制副本。数据通过 Raft 共识算法在所有节点上复制。 支持高可用集群 —— 集成存储后端支持高可用集群。 HashiCorp 官方支持 —— HashiCorp 提供对集成存储后端的官方支持 storage \"raft\" { path = \"/path/to/raft/data\" node_id = \"raft_node_1\" } cluster_addr = \"http://127.0.0.1:8201\" 注意,使用集成存储后端时: 需要提供 cluster_addr 来指定 Raft 集群通信所使用的地址与端口。 不能设置 ha_storage 参数。 强烈建议将 disable_mlock 设置为 true ,并且关闭操作系统的内存换页。 raft 参数 path (string: \"\") – Vault 数据存储的路径。该参数也可以通过环境变量 VAULT_RAFT_PATH 来设置。 node_id (string: \"\") - Raft 集群中本节点的 ID。该参数也可以通过环境变量 VAULT_RAFT_NODE_ID 来设置。 performance_multiplier (integer: 0) - 服务器用来缩放关键 Raft 时间参数的整数乘数。调整该参数会影响 Vault 检测领导者宕机随后进行新领导者选举的时间,代价是使用更多网络和 CPU 资源获取更好的性能。忽略该参数或将参数设置为 0 代表使用下面描述的默认倍数。该参数值越小代表时间约束越紧张,这会增加检测的敏感度,而越大的值则会降低检测的敏感度。 默认情况下,Vault 会使用适合于最小 Vault 服务集群的较低性能的倍数设置,目前相当于将该值设置为 5 (该默认值可能会在 Vault 未来的版本中被改变,取决于目标最小服务器配置是否发生变化)。将该参数设置为 1 会使得 Raft 以最高性能模式工作,推荐在生产环境 Vault 服务器上进行这样的配置。该参数允许设置的最大值为 10。 trailing_logs (integer: 10000) - 该参数控制了在快照创建后磁盘上的日志存储中保留多少条日志条目。该参数只在由于快照很大,写入吞吐量太高导致在快照被完全写完之前日志就被截断进而导致集群追随者无法跟上领导者的日志进度时才需要被调整。如果需要用该参数来恢复集群,请考虑降低吸入吞吐量或是减少 Vault 中保存的数据量。默认值 10000 适用于大多数正常的工作负载。 snapshot_threshold (integer: 8192) - 该参数控制了在两次创建保存到磁盘的快照之间 Raft 提交日志的最小条目数。这是一个低优先级的参数,应该极少需要被调整。在非常繁忙,IO 过载的集群上可以增加该参数的值以减少磁盘 IO 并最大限度地减少所有服务器同时创建快照的可能性。增加该参数的值是以磁盘空间为代价换取降低磁盘 IO,因为日志将会变得更大,并且 raft.db 文件所占用的空间在下一次创建快照之前无法被回收。如果显著增加该参数的值会导致服务器宕机或故障转移后需要更多的时间恢复,因为需要重放更多的日志来恢复状态。 retry_join (list: []) - retry_join 配置节可以被反复声明。每当 Raft 集群引导启动时,如果如果实现知道所有节点的连接细节,那么指定该配置节可以使得节点自动加入 Raft 集群。集群的所有节点都应该通过该配置节引用要加入的集群中的所有其他节点。当节点中有一个节点完成了初始化,它将成为领导者并且其他节点会以它为领导者组建集群。当使用的是 Shamir 封印时,加入了集群的节点仍然需要手工解封。 max_entry_size (integer: 1048576) - 该参数配置了每条 Raft 条目的最大字节数。该限制同时适用于 Put 操作和 Transaction 操作。任何超过该值的 Put 或 Transaction 操作都将导致相应的操作失败。Raft 对于 Raft 日志条目的最大尺寸有一个建议值,该建议值时更近当前的架构、默认时间决定的。集成存储还使用了块尺寸来将大块数据分割成小块序列。默认情况下,块尺寸与 Raft 的最大日志尺寸相同。该参数的默认值是 1048576——是块尺寸的两倍。 autopilot_reconcile_interval (string: \"10s\") - 该参数配置了自动驾驶系统接收所有状态变更的时间间隔。状态变更可能意味着各种事:例如,一个新加入的投票者节点;最初通过自动驾驶作为非投票者添加到 Raft 集群已成功度过稳定期,从而有资格被提升为投票者;该节点已变得不健康,需要通过状态 API 体现出来;节点已被标记为死亡,需要从 Raft 配置中驱逐,等等。 retry_join 配置节 leader_api_addr (string: \"\") - 可能的领导者节点地址。 auto_join (string: \"\") - 云端自动加入集群配置,使用 go-discover 语法。 auto_join_scheme (string: \"\") - 可选的自动加入地址协议。可配置的项有 http 或者 https。 auto_join_port (uint: \"\") - 可选的自动加入地址的端口号。 leader_tls_servername (string: \"\") - 使用 HTTPS 连接时使用的 TLS 服务器名称。该名称应该要与远程服务器证书的 DNS SANs 中的名称之一相匹配。 leader_ca_cert_file (string: \"\") - 潜在领导者节点的 CA 证书的路径。 leader_client_cert_file (string: \"\") - 作为追随者节点与潜在的领导者节点建立连接进行身份验证时所用的客户端证书的路径。 leader_client_key_file (string: \"\") - 作为追随者节点与潜在的领导者节点建立连接进行身份验证时所用的客户端私钥的路径。 leader_ca_cert (string: \"\") - 潜在领导者节点的 CA 证书 leader_client_cert (string: \"\") - 作为追随者节点与潜在的领导者节点建立连接进行身份验证时所用的客户端证书。 leader_client_key (string: \"\") - 作为追随者节点与潜在的领导者节点建立连接进行身份验证时所用的客户端私钥。 每一个 retry_join 块都可以通过文件路径或是单行证书字符串(以 \\n 代表换行)设置 TLS 证书,但不能同时设置两者。每一个 retry_join 块都可以包含 leader_api_addr 参数或是 auto-join 参数,但不能同时设置两者。如果设置了 auto_join 参数,Vault 会自动尝试使用 go-discover 发现并解析潜在的 Raft 领导者地址。请阅读 go-discover 项目的 README 获取更多信息。 默认情况下,Vault 会尝试使用 HTTPS 协议和 8200 端口访问被发现的节点。系统管理员可以通过配置 auto_join_scheme 参数和 auto_join_port 参数修改这两个值。 配置样例: storage \"raft\" { path = \"/Users/foo/raft/\" node_id = \"node1\" retry_join { leader_api_addr = \"http://127.0.0.2:8200\" leader_ca_cert_file = \"/path/to/ca1\" leader_client_cert_file = \"/path/to/client/cert1\" leader_client_key_file = \"/path/to/client/key1\" } retry_join { leader_api_addr = \"http://127.0.0.3:8200\" leader_ca_cert_file = \"/path/to/ca2\" leader_client_cert_file = \"/path/to/client/cert2\" leader_client_key_file = \"/path/to/client/key2\" } retry_join { leader_api_addr = \"http://127.0.0.4:8200\" leader_ca_cert_file = \"/path/to/ca3\" leader_client_cert_file = \"/path/to/client/cert3\" leader_client_key_file = \"/path/to/client/key3\" } retry_join { auto_join = \"provider=aws region=eu-west-1 tag_key=vault tag_value=... access_key_id=... secret_access_key=...\" } } "},"4.配置文件/5.12.storage_s3.html":{"url":"4.配置文件/5.12.storage_s3.html","title":"S3","keywords":"","body":"S3 存储后端 S3 存储后端可以用来将 Vault 的数据保存到 Amazon S3 存储桶中。 无高可用集群支持 —— S3 存储后端不支持高可用集群。 仅有社区支持 —— S3 存储后端由社区支持。虽然它已经过 HashiCorp 员工的审查,但他们可能对该技术并不了解。如果遇到问题,该问题可能会被转交给原作者。 storage \"s3\" { access_key = \"abcd1234\" secret_key = \"defg5678\" bucket = \"my-bucket\" } s3 参数 bucket (string: ) – 设置使用的 S3 存储桶名称。该参数也可以通过设置环境变量 AWS_S3_BUCKET 来设置。 endpoint (string: \"\") – 指定一个非默认的,兼容 AWS 规范的 S3 端点。该参数也可以通过设置环境变量 AWS_S3_ENDPOINT 来设置。(可用来配置使用兼容 S3 协议的其他存储服务) region (string \"us-east-1\") – 指定使用的 AWS region。该参数也可以通过设置环境变量 AWS_REGION 或 AWS_DEFAULT_REGION 来设置,前者的优先级高于后者。 下列参数用于向 AWS 进行身份验证。如果您在 EC2 实例上运行 Vault 服务器,您还可以使用 EC2 实例配置文件服务来提供 Vault 用于调用 S3 API 的凭据。将 access_key 和 secret_key 字段留空将导致 Vault 尝试从 AWS 元数据服务检索凭证。 access_key (string: ) – 设置 AWS access key。该参数也可以通过设置环境变量 AWS_ACCESS_KEY_ID 来设置,或者使用 AWS 凭据文件,又或者是 IAM 角色。 secret_key (string: ) – 设置 AWS secret key。该参数也可以通过设置环境变量 AWS_SECRET_ACCESS_KEY 来设置,或者使用 AWS 凭据文件,又或者是 IAM 角色。 session_token (string: \"\") – 设置 AWS session token。该参数也可以通过设置环境变量 AWS_SESSION_TOKEN 来设置。 max_parallel (string: \"128\") – 设置与 S3 通信的最大并行请求数。 s3_force_path_style (string: \"false\") - 设置是否使用虚拟托管风格域名访问配置的端点。 disable_ssl (string: \"false\") - 设置是否为到端点的连接启用 SSL (强烈不推荐在生产环境关闭 SSL)。 kms_key_id (string: \"\") - 设置用以加密 S3 后端中数据所使用的 KMS 密钥的 ID 或别名。Vault 必须对此密钥拥有 kms:Encrypt 以及 kms:Decrypt 权限。我们可以设置该参数为 alias/aws/s3 来使用账号的默认密钥。 path (string: \"\") - 设置 Vault 数据在 S3 存储桶中的存储路径。 s3 例子 默认例子 该例子演示使用 S3 作为存储后端: storage \"s3\" { access_key = \"abcd1234\" secret_key = \"defg5678\" bucket = \"my-bucket\" } 使用 S3 KMS 默认密钥加密 该例子演示了使用 Amazon S3 作为存储后端,搭配账号默认的 S3 KMS 密钥加密: storage \"s3\" { access_key = \"abcd1234\" secret_key = \"defg5678\" bucket = \"my-bucket\" kms_key_id = \"alias/aws/s3\" } 使用自定义的 S3 KMS 密钥加密 该例子演示了使用 Amazon S3 作为存储后端,搭配用户管理的 KMS 密钥加密: This example shows using Amazon S3 as a storage backend using KMS encryption with a customer managed KMS key. storage \"s3\" { access_key = \"abcd1234\" secret_key = \"defg5678\" bucket = \"my-bucket\" kms_key_id = \"001234ac-72d3-9902-a3fc-0123456789ab\" } AWS 读取实例元数据超时 影响 Vault 1.4 及以后的版本 Vault 在 EC2 实例上使用实例元数据服务的时候,例如从实例配置文件中获取凭据,使用到实例元数据服务 (IMDSv2) 的 v2 版本时可能会出现延迟。 Vault 使用的 AWS SDK 首先尝试连接到 IMDSv2,如果超时,则回退到 v1。在 Vault 1.4 中,此超时最多可能需要 2 分钟。在 Vault 1.5.5 及更高版本中最多可能需要 2 秒钟。 超时会发生在 Vault 和 IMDSv2 之间存在代理,并且实例 hop 限制设置为小于 Vault 和 IMDSv2 之间的“hop”数的时候。例如,如果 Vault 在 EC2 实例上的 docker 中运行,并且实例 hop 限制设置为 1,AWS SDK 客户端将尝试连接到 IMDSv2,超时并回退到 IMDSv1,因为 docker 和 IMDS 之间存在额外的网络 hop. 为避免超时行为,可以在底层 EC2 实例上调整 hop 限制。对于 docker 示例,将 hop 限制设置为 2 就可以让 Vault 中的 AWS SDK 无延迟地连接到 IMDSv2。 "},"4.配置文件/6.telemetry.html":{"url":"4.配置文件/6.telemetry.html","title":"telemetry","keywords":"","body":"telemetry 配置节 telemetey 配置节设置各种 Vault 用以向上游系统公布指标(metric)信息的参数。 telemetry { statsite_address = \"statsite.company.local:8125\" } telemetry 参数 鉴于 telemetry 配置节可配置参数众多,本节中的参数将根据类别进行分组。 通用 下列参数适用于所有遥测配置: usage_gauge_period (string: \"10m\") - 设置收集高基数(cardinality)使用数据的时间间隔,例如令牌总数、实体总数、机密总数等。配置为 \"none\" 将关闭数据收集。 maximum_gauge_cardinality (int: 500) - 仪表(Gauge)标签的最大基数。 disable_hostname (bool: false) - 设置仪表数据是否添加本地主机名作为前缀。 enable_hostname_label (bool: false) - 设置是否要为所有指标数据添加值为本地主机名的 host 标签。启用该功能时推荐关闭 disable_hostname。 lease_metrics_epsilon (string: \"1hr\") - 设置用以预测未来到期租约的存储桶尺寸。例如,默认值为 1 小时,那么 vault.expire.leases.by_expiration 将会统计从现在开始将来 1 小时内过期的租约的总数。请注意租约会被分流后放入存储桶中。例如,如果 lease_metrics_epsilon 被设置成 1 小时,租约 A 会在 25 分钟后到期,而租约 B 会在 35 分钟后到期,那么租约 A 会被放在第一个桶内,对应 0-30 分钟,租约 B 会被放在第二个桶中,对应 31-90 分钟。 num_lease_metrics_buckets (int: 168) - 设置租约过期的存储桶数量。以默认值 168 举例来说,vault.expire.leases.by_expiration 指标数据会有 168 种不同的标签值,每个标签值对应了 lease_metrics_epsilon 参数切分出的一个存储桶。如果把 lease_metrics_epsilon 配置为默认值 1 小时并且配置 num_lease_metrics_buckets 使用默认值,vault.expire.leases.by_expiration 将会统计从现在开始一周内每小时会过期的租约的总数。 add_lease_metrics_namespace_labels (bool: false) - 如果将该参数设置为 true,那么 vault.expire.leases.by_expiration 会同时按照时间以及名字空间进行分割。该参数默认关闭因为开启该功能会导致指标基数变大。 filter_default (bool: true) - 该参数控制是否允许过滤器上没有设置的指标。默认为 true,在没有设置过滤器时允许接收所有指标。当设置为 false 且没有配置过滤器时,将不会发送任何指标。 prefix_filter (string array: []) - 这是一个过滤规则列表,适用于通过前缀允许/阻止指标,格式如下 [\"+vault.token\", \"-vault.expire\", \"+vault.expire.num_leases\"] 前缀为\"+\"将允许具有给定前缀的任何指标,而前缀为\"-\"将阻止它们。如果两个规则之间存在重叠,则更具体的规则将优先。如果相同的前缀多次出现,则阻止规则有更高优先级。 statsite 以下参数适用于 statsite: statsite_address (striing: \"\") - 设置将指标数据转发到的 statsite 服务地址: telemetry { statsite_address = \"statsite.company.local:8125\" } statsd 以下参数适用于 statsd telemetry { statsd_address = \"statsd.company.local:8125\" } circonus 以下参数适用于 Circonus: circonus_api_token (string: \"\") - 设置一个用以创建、管理检查的合法的 Circonus API 令牌。如果设置该参数,将启用指标管理。 circonus_api_app (string: \"nomad\") - 设置一个与 API 令牌关联的应用名称。 circonus_api_url (string: \"https://api.circonus.com/v2\") - 设置连接 Circonus API 的基础 URL。 circonus_submission_interval (string: \"10s\") - 设置提交指标到 Circonus 的时间间隔。 circonus_submission_url (string: \"\") - 设置先前创建的 HTTPRAP 检查对象的 check.config.submission_url 字段。 circonus_check_id (string: \"\") - 设置来自先前创建的 HTTPTRAP 检查的检查 ID(不是 check bundle)。 Check API 对象中 check._cid 字段的数字部分。 circonus_check_force_metric_activation (bool: false) - 设置是否强制激活已经存在但当前未启用的指标。如果启用了检查管理,则默认行为是在遇到新指标时添加它们。如果检查中已经存在该指标,则它不会被激活。此参数的配置会覆盖该默认行为。 circonus_check_instance_id (string: \":\") - 设置一个唯一标识用以区分来自此实例的指标。它可用于短暂运行的实例在基础架构内移动时保持指标的连续性。默认情况下,该参数被设置为 hostname:application name(例如\"host123:nomad\")。 circonus_check_search_tag (string: :) - 设置一个特殊标签,当它与实例 ID 结合使用时,可以帮助我们在拥有 Submission URL 或 Check ID 时缩小搜索结果的范围。默认情况下,该参数被设置为 service:app(例如\"service:nomad\")。 circonus_check_display_name (string: \"\") - 设置检查在创建时的名称。该名称将被显示在 Circonus UI Check 列表中。 circonus_check_tags (string: \"\") - 以逗号分隔的附加标签列表,以在创建检查时添加到检查中。 circonus_broker_id (string: \"\") - 设置创建新 Check 时要使用的特定 Circonus Broker 的 ID。对应 Broker API 对象中 broker._cid 字段的数字部分。如果启用了指标管理并且知晓了 Submission URL 或 Check ID,则将尝试使用 Instance ID 和 Search Tag 搜索现有 Check。如果没有找到,将创建一个新的 HTTPTRAP 检查。默认情况下,会使用随机选择的一个 Enterprise Broker,或者默认的 Circonus Public Broker。 circonus_broker_select_tag (string: \"\") - 指定一个特殊标签,当没有提供 Broker ID 时,该标签将用于选择 Circonus Broker。最好的用法是根据此特定实例的运行位置(例如,特定的地理位置或数据中心,dc:sfo)作为参考决定应该使用哪个代理 dogstatsd 以下参数适用于 DogStatsD: dogstatsd_addr (string: \"\") - 该参数设置了 DogStatsD 实例的地址。 DogStatsD 是一种兼容 statsd 风格的协议,增加了用标签和事件信息装饰(decorate)指标的能力。如果设置该参数,Vault 将向该实例发送各种遥测信息以进行聚合。这可用于捕获运行时信息。 dogstatsd_tags (string array: []) - 该参数设置了一个全局标签列表,这些标签将添加到发送到 DogStatsD 的所有遥测数据包中。它是一个字符串列表,其中每个字符串格式类似:\"my_tag_name:my_tag_value\"。 prometheus 下列 telemetry 参数适用于 prometheus: prometheus_retention_time (string: \"24h\") - 设置 Prometheus 指标在内存中驻留的时间。将该参数设置为 0 将会关闭 Prometheus 遥测。 disable_hostname (bool: false) - 我们推荐将该参数设置为 true 来防止为指标数据添加主机名前缀。 /v1/sys/metrics 端点只能在主节点上被访问,在备用节点上会自动被禁用。我们可以通过启用未经身份验证的用户访问来开启备用节点上的 /v1/sys/metrics 端点。 Vault 并不使用默认的 Prometheus 路径,所以 Prometheus 必须按照以下路径进行配置。请注意在路径中无法使用 ?format=prometheus 因为 \"?\" 会被转义,所以必须以参数的方式进行设定。 使用的 Vault 令牌需要对 /v1/sys/metrics 路径有 capabilities = [\"read\", \"list\"] 的权限。必须将 Prometheus 的 bearer_token 或 bearer_token_file 参数添加到抓取作业中。 以下是一个 Prometheus 配置文件所需要的样例 job_name 配置节: # prometheus.yml scrape_configs: - job_name: 'vault' metrics_path: \"/v1/sys/metrics\" params: format: ['prometheus'] scheme: https tls_config: ca_file: your_ca_here.pem bearer_token: \"your_vault_token_here\" static_configs: - targets: ['your_vault_server_here:8200'] Vault 的配置文件中需要添加以下样例遥测配置: telemetry { prometheus_retention_time = \"30s\" disable_hostname = true } "},"4.配置文件/7.ui.html":{"url":"4.配置文件/7.ui.html","title":"ui","keywords":"","body":"Vault UI Vault 提供了用于与 Vault 交互的用户界面(Web 界面)。使用 Vault UI 可以轻松创建、读取、更新和删除机密、身份验证、解封等。 要使用 UI 需要使用 Vault 0.10 或更高版本,或是使用 Vault 企业版。 启用 Vault UI Vault 在默认情况下开启 UI。要开启 UI,请在 Vault 服务器配置中设置 ui 配置选项。 Vault 客户端不需要设置此选项,因为它们不有 UI 服务。 ui = true listener \"tcp\" { # ... } 更多信息,请阅读 ui 配置节 访问 Vault UI UI 与 Vault 侦听器在同一端口上运行。因此,您必须配置至少一个 listener 配置节才能访问 UI。 listener \"tcp\" { address = \"10.0.1.35:8200\" # If bound to localhost, the Vault UI is only # accessible from the local machine! # address = \"127.0.0.1:8200\" } 按照这种配置,可以从子网中的任何计算机通过以下 URL 访问 UI(假设没有网络防火墙): https://10.0.1.35:8200/ui/ 也可以通过任意解析到该 IP 地址的 DNS 名来访问,例如使用 Consul 服务地址(如果配置使用了 Consul 的话): https://vault.service.consul:8200/ui/ 关于 TLS 的注意事项 使用 TLS(推荐)时,证书必须对将通过它访问的 Vault UI 的所有 DNS 条目以及 SAN 上的任何 IP 地址有效。如果您使用自签名证书运行 Vault,则任何访问 Vault UI 的浏览器都需要安装根 CA。不这样做可能会导致浏览器显示该站点“不受信任”的警告。强烈建议访问 Vault UI 的客户端浏览器安装正确的 CA 根证书以进行验证,以减少 MITM 攻击的机会。 "},"5.身份验证方法/身份验证方法.html":{"url":"5.身份验证方法/身份验证方法.html","title":"身份验证方法","keywords":"","body":"认证方式 认证方式是 Vault 用来执行身份认证以及赋予用于一组带有策略权限的身份的组件。 我们可以根据使用 Vault 的不同场景以及所属组织的需求,同时启用多种不同的认证方式。 例如,当要为开发人员提供日常的认证服务时,我们可以使用 GitHub 认证方式;而对于部署在生产环境中的应用服务来说, AppRole 会是更好的选择。 "},"5.身份验证方法/1.AppRole.html":{"url":"5.身份验证方法/1.AppRole.html","title":"AppRole","keywords":"","body":"AppRole approle 身份验证方法允许机器或应用程序使用 Vault 定义的角色进行身份验证。AppRole 的开放式设计支持使用不同的工作流和配置来应对大量应用程序。这种身份验证方法主要是面向自动化工作流程(机器和服务)设计的,对人类操作者不太有用。 “AppRole”代表一组 Vault 策略和登录约束,必须满足这些策略才能使用这些策略获取令牌。范围可以根据需要进行调整。可以为特定机器、甚至该机器上的特定用户或跨机器的服务创建 AppRole。成功登录所需的凭据取决于相关联的 AppRole 所设置的约束。 身份验证 通过命令行工具 默认路径是/approle。如果该引擎被挂载了其他挂载点上,请使用 auth/my-path/login 替代: $ vault write auth/approle/login \\ role_id=db02de05-fa39-4855-059b-67221c5c2f63 \\ secret_id=6a174c20-f6de-a53c-74d2-6018fcceff64 Key Value --- ----- token 65b74ffd-842c-fd43-1386-f7d7006e520a token_accessor 3c29bc22-5c72-11a6-f778-2bc8f48cea0e token_duration 20m0s token_renewable true token_policies [default] 通过 API 默认端点是 auth/approle/login。如果该引擎被挂载到了其他挂载点上,请使用其他值代替 approle。 $ curl \\ --request POST \\ --data '{\"role_id\":\"988a9df-...\",\"secret_id\":\"37b74931...\"}' \\ http://127.0.0.1:8200/v1/auth/approle/login 响应中 auth.client_token 字段会包含令牌: { \"auth\": { \"renewable\": true, \"lease_duration\": 2764800, \"metadata\": {}, \"policies\": [\"default\", \"dev-policy\", \"test-policy\"], \"accessor\": \"5d7fb475-07cb-4060-c2de-1ca3fcbf0c56\", \"client_token\": \"98a4c7ab-b1fe-361b-ba0b-e307aacfd587\" } } 集成到自己的应用程序:请参阅代码示例部分,了解使用 AppRole 身份验证方法对 Vault 进行身份验证的代码片段。 配置 在用户或机器进行身份验证之前,必须预先配置身份验证方法。这些步骤通常由系统操作员或配置管理工具完成。 通过命令行工具配置 启用 AppRole 身份验证方法: $ vault auth enable approle 创建一个命名角色: $ vault write auth/approle/role/my-role \\ secret_id_ttl=10m \\ token_num_uses=10 \\ token_ttl=20m \\ token_max_ttl=30m \\ secret_id_num_uses=40 注意:如果您的 approle 签发的令牌需要能够创建子令牌,您需要将 token_num_uses 设置为 0。 有关配置选项的完整列表,请参阅 API 文档。 获取 AppRole 的 RoleID: $ vault read auth/approle/role/my-role/role-id role_id db02de05-fa39-4855-059b-67221c5c2f63 为指定 AppRole 签发一个 SecretID: $ vault write -f auth/approle/role/my-role/secret-id secret_id 6a174c20-f6de-a53c-74d2-6018fcceff64 secret_id_accessor c454f7e5-996e-7230-6074-6ef26b7bcf86 secret_id_ttl 10m 通过 API 配置 启用 AppRole 身份验证方法: $ curl \\ --header \"X-Vault-Token: ...\" \\ --request POST \\ --data '{\"type\": \"approle\"}' \\ http://127.0.0.1:8200/v1/sys/auth/approle 创建包含指定策略的 AppRole: $ curl \\ --header \"X-Vault-Token: ...\" \\ --request POST \\ --data '{\"policies\": \"dev-policy,test-policy\"}' \\ http://127.0.0.1:8200/v1/auth/approle/role/my-role 获取 AppRole 的 ID: $ curl \\ --header \"X-Vault-Token: ...\" \\ http://127.0.0.1:8200/v1/auth/approle/role/my-role/role-id 响应看起来大概是这样的: { \"data\": { \"role_id\": \"988a9dfd-ea69-4a53-6cb6-9d6b86474bba\" } } 用该角色创建一个新的 SecretID: curl \\ --header \"X-Vault-Token: ...\" \\ --request POST \\ http://127.0.0.1:8200/v1/auth/approle/role/my-role/secret-id 响应看起来可能是这样的: { \"data\": { \"secret_id_accessor\": \"45946873-1d96-a9d4-678c-9229f74386a5\", \"secret_id\": \"37b74931-c4cd-d49a-9246-ccc62d682a25\", \"secret_id_ttl\": 600 } } 凭据和约束 RoleID RoleID 是一个标识符,指定用来计算拥有的凭据的 AppRole。在针对此身份验证方法的登录端点进行身份验证时,RoleID 始终是必需的参数(通过 role_id)。默认情况下,RoleID 是一个全局唯一的 UUID,但是,它们可以被设置为特定值。 SecretID SecretID 是默认情况下任何登录(通过 secret_id)都需要的凭据,并且应该始终保密。 (一种进阶用法,可以通过设置 AppRole 的 bind_secret_id 参数来避免传递 SecretID,允许只知道 RoleID 或匹配其他设置约束的机器获取令牌)。可以通过角色本身生成 128 位纯随机 UUID(Pull模式),或通过特定的自定义值(Push 模式)针对 AppRole 创建 SecretID。与令牌类似,SecretID 具有使用限制、TTL 和过期等属性。 Pull 和 Push 模式 如果用于登录的 SecretID 是从 AppRole 获取的,则这是在 Pull 模式下运行。如果客户端针对 AppRole 设置了“自定义”SecretID,则称为 Push 模式。Push 模式模仿已弃用的 App-ID 身份验证方法的行为;然而,在大多数情况下,Pull 模式是更好的方法。原因是 Push 模式需要其他一些系统了解完整的客户端凭据集(RoleID 和 SecretID)才能创建条目,即使这些凭据是通过不同路径分发的。但是,在 Pull 模式下,即使必须知道 RoleID 才能将其分发给客户端,也可以通过使用响应封装对除终端用户以外的所有各方保密 SecretID。 Push 模式可用于和 App-ID 工作流保持兼容性,在某些特定情况下更可取,但在大多数情况下,Pull 模式更安全,应该首选 Pull 模式。 进一步的约束 role_id 是登录端点所需的凭据。 role_id 指向的 AppRole 将对其设置约束。这决定了登录所需要的其他 required 的凭据。 bind_secret_id 约束要求在登录端点使用 secret_id 的值。展望未来,此身份验证方法可以支持更多约束参数来支持不同的应用集合。某些约束不需要凭据,但仍会强制执行登录约束。例如,secret_id_bound_cidrs 将只允许 IP 在 CIDR 块范围内的 AppRole 的登录。 "},"5.身份验证方法/2.GitHub.html":{"url":"5.身份验证方法/2.GitHub.html","title":"GitHub","keywords":"","body":"GitHub github 验证方法可使用 GitHub 个人访问令牌对 Vault 进行身份验证。这种身份验证方法对人类最有用:运维或开发人员直接通过命令行登录 Vault 。 重要说明:Vault 不支持通过 OAuth 工作流程来生成 GitHub 令牌,因此它不是一个 GitHub 应用程序。所以该验证方法使用个人访问令牌。一个重要的结果是,通过 Vault 配置 GitHub 组织中的任何用户的有效 GitHub 访问令牌,只要具有 read:org 权限,都可以用于身份验证。如果此类令牌从第三方服务中被盗,并且攻击者能够通过网络对 Vault 进行调用,他们将能够以生成访问令牌的用户的身份登录 Vault。使用本方法时,最好禁止通过公网对 Vault 进行访问。如果这种风险是不可承受的,则应使用不同的方法。 身份验证 通过命令行 默认挂载路径是/github。如果在其他挂载点启用该方法,请替代命令中的-path=/my-path部分: $ vault login -method=github token=\"MY_TOKEN\" 通过 API 默认端点是 auth/github/login。如果在其他挂载点启用该方法,请替代路径中的 github: $ curl \\ --request POST \\ --data '{\"token\": \"MY_TOKEN\"}' \\ http://127.0.0.1:8200/v1/auth/github/login 响应会在 auth.client_token 包含令牌数据: { \"auth\": { \"renewable\": true, \"lease_duration\": 2764800, \"metadata\": { \"username\": \"my-user\", \"org\": \"my-org\" }, \"policies\": [\"default\", \"dev-policy\"], \"accessor\": \"f93c4b2d-18b6-2b50-7a32-0fecf88237b8\", \"client_token\": \"1977fceb-3bfa-6c71-4d1f-b64af98ac018\" } } 配置 在用户或者及其可以使用该验证方法之前必须预先进行必要的配置。需要执行以下步骤: 启用 GitHub 验证方法: $ vault auth enable github 通过 /config 端点来配置 Vault 如何验证 GitHub 的令牌: $ vault write auth/github/config organization=hashicorp 请访问相关 API 文档获取完整的配置项列表。 将 GitHub 组织里的用户名或者组名映射到 Vault 策略上。组名必须经过 ASCII 编码处理(slugify)。 $ vault write auth/github/map/teams/dev value=dev-policy 在上面的例子里,属于 \"hashicorp\" 组织的 \"dev\" 组的用户的 GitHub 个人访问令牌可以被用来登录 Vault,他们会得到一个包含 \"dev-policy\" 策略的令牌。 我们也可以通过 map/users/ 端点把特定用户映射到指定策略上: $ vault write auth/github/map/users/sethvargo value=sethvargo-policy 在上面的例子里,除了现存的团队策略以外,名为 sethvargo 的 GitHub 用户还会被授予 sethvargo-policy 策略。 "},"5.身份验证方法/3.Token.html":{"url":"5.身份验证方法/3.Token.html","title":"Token","keywords":"","body":"Token token方法是内置的,在 /auth/token 自动启用。它允许用户使用令牌进行身份验证,以及创建新令牌、通过令牌吊销机密等。 当任何其他身份验证方法返回身份时,Vault 内核会调用 token 方法为该身份创建一个新的唯一令牌。 token 存储还可用于绕过任何其他身份验证方法:您可以直接创建令牌,对令牌执行各种其他操作,例如续约和吊销。 身份验证 通过命令行 $ vault login token= 通过 API 直接将令牌设置进 HTTP API 的标头。标头应该是 X-Vault-Token: 或 Authorization: Bearer 。 "},"5.身份验证方法/4.Userpass.html":{"url":"5.身份验证方法/4.Userpass.html","title":"用户名密码","keywords":"","body":"用户名密码 userpass 身份验证方法允许用户使用一组用户名密码登录 Vault。 这组用户名密码直接通过 users/ 路径配置进该验证方法。本方法无法从外部数据源读取用户名密码。 用户名不区分大小写,例如 Mary 与 mary 是一样的。 身份验证 通过命令行 $ vault login -method=userpass \\ username=mitchellh \\ password=foo 通过 API $ curl \\ --request POST \\ --data '{\"password\": \"foo\"}' \\ http://127.0.0.1:8200/v1/auth/userpass/login/mitchellh 响应中的 auth.client_token 包含了返回的令牌: { \"lease_id\": \"\", \"renewable\": false, \"lease_duration\": 0, \"data\": null, \"auth\": { \"client_token\": \"c4f280f6-fdb2-18eb-89d3-589e2e834cdb\", \"policies\": [\"admins\"], \"metadata\": { \"username\": \"mitchellh\" }, \"lease_duration\": 0, \"renewable\": false } } 配置 本验证方法需要在用户或者机器可以使用它进行身份验证之前进行相关配置。以下步骤通常被用来配置: 启用用户名密码身份验证方法: $ vault auth enable userpass 配置允许登录的用户名和密码: $ vault write auth/userpass/users/mitchellh \\ password=foo \\ policies=admins 以上命令创建了名为 mitchellh 的新用户,他的密码是 foo,关联了 admins 策略。这些就是所有需要配置的内容了。 "},"6.机密引擎/机密引擎.html":{"url":"6.机密引擎/机密引擎.html","title":"机密引擎","keywords":"","body":"机密引擎(Secrets Engines) 机密引擎是存储、生成或加密数据的组件。 机密引擎非常灵活可扩展,功能强大。 向机密引擎发送一些数据,它们会对这些数据执行一些操作,然后返回一个结果。 一些机密引擎只是存储和读取数据——就像是加密存储数据的 Redis/Memcached 那样。另一些机密引擎会连接到其他服务并按需生成动态凭证。还有一些机密引擎提供加密即服务、totp 生成、证书等等。 机密引擎在 Vault 中被挂载在“路径”上启用。当一个请求发送到 Vault,路由器会负责将所有符合路径前缀的请求发送到该路径上挂载的机密引擎里。通过这样的机制,每一个机密引擎都可以定义属于它自己的路径和属性。对于用户来说,机密引擎的行为就像是虚拟文件系统,提供了增删改查功能。 机密引擎生命周期 大多数秘密引擎可以通过命令行工具或 API 启用、禁用、调整和移动。过去版本的 Vault 中将这些称为“挂载点”(mounts),但该术语已被过度使用。 启用 —— 给定路径上启用秘密引擎。除了少数例外之外,机密引擎可以同时在多个路径上启用。每个机密引擎都按照路径隔离。默认情况下,它们在其“类型”名对应路径上启用(例如,aws 启用在 aws/ 路径上) 禁用 —— 禁用一个现存的机密引擎。当一个机密引擎被禁用时,它的所有机密都会被吊销(如果这些机密支持吊销的话),并且在物理存储层中该引擎存储的所有数据都会被删除 移动 —— 将一个现存机密引擎移动到一个新路径上。该过程会吊销引擎的所有机密,因为这些机密租约都已经在创建时与特定路径相绑定了。已存储的该引擎相关的配置信息会被移动到新路径上。 调整 —— 调整该引擎的全局配置,例如 TTL 机密引擎启用之后,就可以在启用路径上通过 API 直接与其交互。使用 vault path-help 确定它响应的路径。 请注意,Vault 中的挂载点不能相互冲突。这有两个广泛的含义。第一个是要挂载的路径不能以一个现存挂载点路径为前缀。第二个是要挂载的路径本身不能是某个现存挂载点路径的前缀。例如,挂载 foo/bar 和 foo/baz 可以和平共处,而 foo 和 foo/baz 不能 屏障视图 机密引擎只能透过一个包含屏障的视图访问配置的 Vault 物理存储。这很像是 chroot。 启用秘密引擎时,会生成一个随机 UUID,这将成为该引擎的数据根。每当该引擎写入物理存储层时,它的写入路径都会以该 UUID 文件夹为前缀。由于 Vault 存储层不支持相对访问(例如 ../),这使得启用的机密引擎无法访问其他引擎的数据。 这是 Vault 中的一项重要安全功能 - 即使是恶意的引擎也无法访问来自任何其他引擎的数据。 "},"6.机密引擎/1.AliCloud.html":{"url":"6.机密引擎/1.AliCloud.html","title":"AliCloud","keywords":"","body":"AliCloud 阿里云 Secrets 引擎基于 RAM 策略动态生成阿里云访问令牌,或者基于 RAM 角色动态生成阿里云 STS 凭证。这通常简化了阿里云的使用,因为它不需要在 Web 界面中点击。阿里云 access token 是基于时间的,并且会在 Vault 租约到期时自动吊销。 STS 凭证是短暂的、不可更新的,并且会自行过期。 配置 大部分机密引擎在工作之前必须要先进行配置。这些步骤通常是由系统管理员来完成。 启用阿里云机密引擎: $ vault secrets enable alicloud Success! Enabled the alicloud secrets engine at: alicloud/ 默认情况下,机密引擎会挂载到名字对应的路径上。要在不同路径上启用机密引擎,请使用 -path 参数。 在阿里云中创建一个自定义策略,生成的 access key 将拥有该策略。 在阿里云中创建一个用户,名字是\"hashicorp-valut\",在用户的 \"User Authorization Policies\" 节里直接授予刚才新建的策略。 在阿里云中为刚才的用户创建一个 access key,这可以在阿里云 Web UI 的用户页面中完成。 在 Vault 中配置用以调用阿里云来生成凭据的 access key: $ vault write alicloud/config \\ access_key=0wNEpMMlzy7szvai \\ secret_key=PupkTg8jdmau1cXxYacgE736PJj4cA 或者,阿里云机密引擎可以使用设置在环境变量里的凭据,或通过实例元数据获取可用的凭据。由于它会在每次 API 调用时读取当前凭据,因此凭据更改后立即生效,无需重新启动 Vault。 如果可用,建议使用实例元数据作为凭据,因为它们是最安全的选项。这样的话,只需要确保运行 Vault 的实例具有足够的权限而不要添加任何额外的配置。 配置新建的凭据所拥有的角色。 使用阿里云已有的策略生成 access token: $ vault write alicloud/role/policy-based \\ remote_policies='name:AliyunOSSReadOnlyAccess,type:System' \\ remote_policies='name:AliyunRDSReadOnlyAccess,type:System' 生成 access token 时动态创建策略: $ vault write alicloud/role/policy-based \\ inline_policies=- inline_policies 和 remote_policies 两者可以混合使用。他们都不可以用来生成 STS 凭据,例如: $ vault write alibaba/role/role-based \\ role_arn='acs:ram::5138828231865461:role/hastrustedactors' 任何指定的 role_arn 在创建时都必须添加“trusted actors”。这只能在角色创建时添加。可信参与者是可以代入(assume)角色的实体。由于我们将代入角色来获取凭据,因此配置中的 access_key 和 secret_key 必须有资格作为受信任的参与者。 为 Vault 设置 RAM 策略的例子 虽然阿里云凭证可以由环境变量、alicloud/config 中的显式的设置或读取实例元数据提供,但该凭证需要有足够的权限来签发机密。不同的角色配置方式需要不同的权限。 这是一个示例 RAM 策略,它允许您使用任何类型的角色创建凭据: { \"Statement\": [ { \"Action\": [ \"ram:CreateAccessKey\", \"ram:DeleteAccessKey\", \"ram:CreatePolicy\", \"ram:DeletePolicy\", \"ram:AttachPolicyToUser\", \"ram:DetachPolicyFromUser\", \"ram:CreateUser\", \"ram:DeleteUser\", \"sts:AssumeRole\" ], \"Effect\": \"Allow\", \"Resource\": \"*\" } ], \"Version\": \"1\" } 然而,实际使用的策略应只包含根据配置的角色所必须的权限。 如果有任意角色使用了 inline_policies,我们需要以下权限: \"ram:CreateAccessKey\" \"ram:DeleteAccessKey\" \"ram:AttachPolicyToUser\" \"ram:DetachPolicyFromUser\" \"ram:CreateUser\" \"ram:DeleteUser\" 如果有角色使用了 remote_policies,那么需要以下权限: 所有 inline_policies 需要的权限 \"ram:CreatePolicy\" \"ram:DeletePolicy\" 如果有任何权限使用了 role_arn,我们需要以下权限: \"sts:AssumeRole\" 可用标志 配置完机密引擎后拥有合适权限的 Vault 令牌的用户或者机器就可以用它来生成凭据了。 通过读取 /creds 端点加上角色名的路径来生成一个新的 access key: $ vault read alicloud/creds/policy-based Key Value --- ----- lease_id alicloud/creds/policy-based/f3e92392-7d9c-09c8-c921-575d62fe80d8 lease_duration 768h lease_renewable true access_key 0wNEpMMlzy7szvai secret_key PupkTg8jdmau1cXxYacgE736PJj4cA 返回的 access_key 和 secret_key 就是阿里云文档中提到的 \"AccessKeyId\" 和 \"AccessKeySecret\"。 使用 role_arn 获取的凭据会带有额外的字段:expiration 和 security_token,就像这样: $ vault read alicloud/creds/role-based Key Value --- ----- lease_id alicloud/creds/role-based/f3e92392-7d9c-09c8-c921-575d62fe80d9 lease_duration 59m59s lease_renewable false access_key STS.L4aBSCSJVMuKg5U1vFDw secret_key wyLTSmsyPGP1ohvvw8xYgB29dlGI8KMiH2pKCNZ9 security_token CAESrAIIARKAAShQquMnLIlbvEcIxO6wCoqJufs8sWwieUxu45hS9AvKNEte8KRUWiJWJ6Y+YHAPgNwi7yfRecMFydL2uPOgBI7LDio0RkbYLmJfIxHM2nGBPdml7kYEOXmJp2aDhbvvwVYIyt/8iES/R6N208wQh0Pk2bu+/9dvalp6wOHF4gkFGhhTVFMuTDRhQlNDU0pWTXVLZzVVMXZGRHciBTQzMjc0KgVhbGljZTCpnJjwySk6BlJzYU1ENUJuCgExGmkKBUFsbG93Eh8KDEFjdGlvbkVxdWFscxIGQWN0aW9uGgcKBW9zczoqEj8KDlJlc291cmNlRXF1YWxzEghSZXNvdXJjZRojCiFhY3M6b3NzOio6NDMyNzQ6c2FtcGxlYm94L2FsaWNlLyo= expiration 2018-08-15T21:58:00Z "},"6.机密引擎/2.AWS.html":{"url":"6.机密引擎/2.AWS.html","title":"AWS","keywords":"","body":"AWSs AWS 机密引擎根据 IAM 策略动态生成 AWS 访问凭据。这通常简化了 AWS IAM 的使用,因为它不需要在 Web 界面中点击。此外,该过程可以被编码并映射到内部身份验证方法(例如 LDAP)。基于 AWS IAM 生成凭据具有有效期,会在 Vault 租约到期时自动撤销。 Vault 支持从 AWS 获取三种不同类型的凭据: iam_user: Vault 将为每个租约创建一个 IAM 用户,将角色中指定的托管和内联 IAM 策略附加到用户,如果在角色上指定了权限边界,那么这个权限边界也会被附加。然后 Vault 将为 IAM 用户生成 access key 和 secret key,并将它们返回给调用者。 IAM 用户没有会话令牌,所以不会返回会话令牌。 Vault 将在 TTL 到期时删除 IAM 用户。 assumed_role: Vault 会调用 sts:AssumeRole 并返回 access key、secret key 和会话令牌给调用者 federation_token: Vault 会调用 sts:GetFederationToken 传入配置的 AWS 策略文档并将 access key、secret key 和会话令牌返回给调用者。 配置 大部分机密引擎在工作之前必须要先进行配置。这些步骤通常是由系统管理员来完成。 启用 AWS 机密引擎: $ vault secrets enable aws Success! Enabled the aws secrets engine at: aws/ 默认情况下,机密引擎会挂载到名字对应的路径上。要在不同路径上启用机密引擎,请使用 -path 参数。 配置 Vault 用来与 AWS 通信生成 IAM 凭据的凭据: $ vault write aws/config/root \\ access_key=AKIAJWVN5Z4FOFT7NLNA \\ secret_key=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 \\ region=us-east-1 Vault 使用这里配置的凭据连接到 AWS。因此,这些凭据本身拥有的策略必须是要生成的 IAM 凭据所拥有策略的超集。由于 Vault 使用官方 AWS SDK,因此它将使用 SDK 指定使用的凭据,也可以使用标准 AWS 环境变量、共享文件凭据或 IAM 角色/ECS 任务凭据作为凭据。 (请注意,如果想要使用 STS 联合令牌,Vault 就不能使用 IAM 角色凭据,因为与角色关联的临时安全凭据无权使用 GetFederationToken。) 注意:虽然上面配置的 Vault 的路径是 aws/config/root,也不要使用 AWS 根账户凭据,而是生成专用用户或角色。 配置一个映射到 AWS 中的一组权限以及 AWS 凭据类型的 Vault 角色。当用户生成凭据时,该凭据会根据使用的 Vault 角色生成。一个例子: $ vault write aws/roles/my-role \\ credential_type=iam_user \\ policy_document=- 上面的例子会创建一个名为“my-role”的角色。当用户使用这个角色生成凭据时,Vault 将创建一个 IAM 用户并将指定的策略文档附加到 IAM 用户。然后 Vault 将为 IAM 用户创建访问密钥和秘密密钥并返回这些凭据。也可以指定用户内联策略,外加可选的对现有 AWS 策略的完整 ARN 或一组 IAM 组的引用: $ vault write aws/roles/my-other-role \\ policy_arns=arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess,arn:aws:iam::aws:policy/IAMReadOnlyAccess \\ iam_groups=group1,group2 \\ credential_type=iam_user \\ policy_document=- 可以阅读 AWS IAM 策略文档获取更多有关 IAM 策略的信息。 可用标志 配置完机密引擎后拥有合适权限的 Vault 令牌的用户或者机器就可以用它来生成凭据了。 通过读取 /creds 端点加上角色名的路径来生成一个新的凭据: $ vault read aws/creds/my-role Key Value --- ----- lease_id aws/creds/my-role/f3e92392-7d9c-09c8-c921-575d62fe80d8 lease_duration 768h lease_renewable true access_key AKIAIOWQXTLW36DV7IEA secret_key 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 security_token 每次执行该命令都会生成一个新的凭据。 遗憾的是,与 AWS 其他服务一样,IAM 凭据创建后需要等待 5-10 秒(有时更长)才能正常使用,如果是在自动化流程中使用该命令需要注意这一点。 想要立即可用的凭据,请考虑使用 STS 获取密钥的方法。 STS 令牌支持的 IAM 凭据在生成后立即可用。 轮替 Vault 用来与 AWS 通信使用的凭据: $ vault write -f aws/config/rotate-root Key Value --- ----- access_key AKIA3ALIVABCDG5XC8H4 注意:由于 AWS 的最终一致的特点,调用此端点后,从 Vault 到 AWS 的后续调用可能会失败几秒钟,直到 AWS 再次变得一致。 为 Vault 设置 IAM 策略的例子 配置在 aws/config/root 的凭据需要管理动态 IAM 用户的权限。这里有一份 AWS IAM 策略的例子,该策略授予了大多数 Vault 所需要的权限: { \"Version\": \"2012-10-17\", \"Statement\": [ { \"Effect\": \"Allow\", \"Action\": [ \"iam:AttachUserPolicy\", \"iam:CreateAccessKey\", \"iam:CreateUser\", \"iam:DeleteAccessKey\", \"iam:DeleteUser\", \"iam:DeleteUserPolicy\", \"iam:DetachUserPolicy\", \"iam:ListAccessKeys\", \"iam:ListAttachedUserPolicies\", \"iam:ListGroupsForUser\", \"iam:ListUserPolicies\", \"iam:PutUserPolicy\", \"iam:AddUserToGroup\", \"iam:RemoveUserFromGroup\" ], \"Resource\": [\"arn:aws:iam::ACCOUNT-ID-WITHOUT-HYPHENS:user/vault-*\"] } ] } Vault 也支持在创建 IAM 用户时应用权限边界。如果希望强制 Vault 始终将权限边界附加到 IAM 用户,可以使用如下策略: { \"Version\": \"2012-10-17\", \"Statement\": [ { \"Effect\": \"Allow\", \"Action\": [ \"iam:CreateAccessKey\", \"iam:DeleteAccessKey\", \"iam:DeleteUser\", \"iam:ListAccessKeys\", \"iam:ListAttachedUserPolicies\", \"iam:ListGroupsForUser\", \"iam:ListUserPolicies\", \"iam:AddUserToGroup\", \"iam:RemoveUserFromGroup\" ], \"Resource\": [\"arn:aws:iam::ACCOUNT-ID-WITHOUT-HYPHENS:user/vault-*\"] }, { \"Effect\": \"Allow\", \"Action\": [ \"iam:AttachUserPolicy\", \"iam:CreateUser\", \"iam:DeleteUserPolicy\", \"iam:DetachUserPolicy\", \"iam:PutUserPolicy\" ], \"Resource\": [\"arn:aws:iam::ACCOUNT-ID-WITHOUT-HYPHENS:user/vault-*\"], \"Condition\": { \"StringEquals\": { \"iam:PermissionsBoundary\": [ \"arn:aws:iam::ACCOUNT-ID-WITHOUT-HYPHENS:policy/PolicyName\" ] } } } ] } 这里的“iam:PermissionsBoundary”条件包含希望确保 Vault 使用的权限边界策略列表。此策略将确保 Vault 使用给定的权限边界中的一个(不是全部)。 STS 凭据 上面我们演示了如何使用 iam_user 凭据。如前所述,Vault 也支持 assumed_role 和 federation_token 凭据。 STS 联合(Federation)令牌 注意:由于 AWS 中的限制,为了使用 federation_token 凭据,Vault 必须配置为使用 IAM 用户凭据。 AWS 不允许使用临时凭据(例如那些来自 IAM 实例配置文件的凭据)。 STS 联合令牌继承了一组权限,他们是四组权限的组合(交集): 配置在 aws/config/root 的凭据所拥有的权限 配置在 Vault 的角色中的内联策略 配置在 Vault 的角色的托管策略的 ARN 一个 IAM 或是 STS 操作上的隐式的拒绝策略 credential_type 为 federation_token 的角色可以在 Vault 角色中指定一个或多个 policy_document、policy_arns 和 iam_groups 参数。 配置在 aws/config/root 的凭据需要有 sts:GetFederationToken 的 IAM 权限以及委托给 STS 联合令牌的权限。例如,aws/config/root 凭据上的策略将允许创建具有委托给 ec2:* 的权限(或任意 ec2:* 权限的子集)的 STS 联合令牌: { \"Version\": \"2012-10-17\", \"Statement\": { \"Effect\": \"Allow\", \"Action\": [ \"ec2:*\", \"sts:GetFederationToken\" ], \"Resource\": \"*\" } } 然后 ec2_admin 角色将分配一个具有相同的 ec2:* 权限的内联策略。 $ vault write aws/roles/ec2_admin \\ credential_type=federation_token \\ policy_document=@policy.json 这里的 policy.json 文件将包含具有类似权限的内联策略,但没有 sts:GetFederationToken 权限。 (我们可以授予 sts:GetFederationToken 权限,但 STS 附加了一个隐式拒绝,覆盖了我们授予的权限。) { \"Version\": \"2012-10-17\", \"Statement\": { \"Effect\": \"Allow\", \"Action\": \"ec2:*\", \"Resource\": \"*\" } } 要生成一组新的 STS 联合令牌凭据,我们只需向 aws/sts 路径写入: $ vault write aws/sts/ec2_admin ttl=60m Key Value lease_id aws/sts/ec2_admin/31d771a6-fb39-f46b-fdc5-945109106422 lease_duration 60m0s lease_renewable false access_key ASIAJYYYY2AA5K4WIXXX secret_key HSs0DYYYYYY9W81DXtI0K7X84H+OVZXK5BXXXX security_token AQoDYXdzEEwasAKwQyZUtZaCjVNDiXXXXXXXXgUgBBVUUbSyujLjsw6jYzboOQ89vUVIehUw/9MreAifXFmfdbjTr3g6zc0me9M+dB95DyhetFItX5QThw0lEsVQWSiIeIotGmg7mjT1//e7CJc4LpxbW707loFX1TYD1ilNnblEsIBKGlRNXZ+QJdguY4VkzXxv2urxIH0Sl14xtqsRPboV7eYruSEZlAuP3FLmqFbmA0AFPCT37cLf/vUHinSbvw49C4c9WQLH7CeFPhDub7/rub/QU/lCjjJ43IqIRo9jYgcEvvdRkQSt70zO8moGCc7pFvmL7XGhISegQpEzudErTE/PdhjlGpAKGR3d5qKrHpPYK/k480wk1Ai/t1dTa/8/3jUYTUeIkaJpNBnupQt7qoaXXXXXXXXXX STS AssumeRole assumed_role 凭据通常用于跨账户身份验证或单点登录 (SSO) 场景。为了使用 assumed_role 凭据,必须在 Vault 之外进行配置: 一个 IAM 角色 IAM 内联策略以及可选的附加到 IAM 角色的托管策略 IAM 信任策略附加到 IAM 角色以授予 Vault 代入角色的权限 与使用 federation_token 凭据相比,使用 assumed_role 凭据有如下的优势: 如果角色的 IAM 策略允许的话,assumed_role 凭据可以调用 IAM 和 STS 操作。 assumed_role 支持跨账户身份验证 临时凭据(例如在使用了 IAM 实例配置文件的 EC2 实例上运行 Vault 使用的凭据)可以读取assumed_role 凭据(但不能检索 federation_token 凭据)。 配置在 aws/config/root 的凭据必须有一个允许指定角色执行 sts:AssumeRole 的 IAM 策略: { \"Version\": \"2012-10-17\", \"Statement\": { \"Effect\": \"Allow\", \"Action\": \"sts:AssumeRole\", \"Resource\": \"arn:aws:iam::ACCOUNT-ID-WITHOUT-HYPHENS:role/RoleNameToAssume\" } } 我们必须为要代入的 IAM 角色附加一个信任策略,从而允许配置在 aws/root/config 凭据代入该角色。 { \"Version\": \"2012-10-17\", \"Statement\": [ { \"Effect\": \"Allow\", \"Principal\": { \"AWS\": \"arn:aws:iam::ACCOUNT-ID-WITHOUT-HYPHENS:user/VAULT-AWS-ROOT-CONFIG-USER-NAME\" }, \"Action\": \"sts:AssumeRole\" } ] } 在设置一个 Vault 角色的 credential_type 为 assumed_role 的时候,可以设置不止一个 IAM 角色 ARN。这样做的话,Vault 客户端可以在从该角色检索凭据时选择他们想要代入的角色 ARN。 此外,我们可以同时设定 policy_document 和 policy_arns 参数;如果这样设定的话,这两个参数同时对要授予代入角色的 IAM 权限进行过滤。如果设置了 iam_groups,则在调用 sts:AssumeRole 时,每个 IAM 组的内联和附加策略都会被添加到 policy_document 和 policy_arns 参数中。对于要放行的操作,它必须得到要代入的 AWS 角色上的 IAM 策略,以及 Vault 角色上指定的 policy_document(如果设置了的话)以及 policy_arns 参数指定的托管策略的许可。 (policy_document 参数被作为 Policy 参数传入 sts:AssumeRole API 调用,而 policy_arns 参数被作为 PolicyArns 参数传入同一调用。) 注意:当设置了多个 role_arns 时,请求凭据的客户端可以指定在 Vault 角色上定义的某个角色 ARN 以获得凭据。但是,当指定了 policy_document、policy_arns 或 iam_groups 时,这将适用于从 AWS 检索的所有角色凭据。 让我们使用我们要代入的角色的 ARN 来创建一个“deploy”策略: $ vault write aws/roles/deploy \\ role_arns=arn:aws:iam::ACCOUNT-ID-WITHOUT-HYPHENS:role/RoleNameToAssume \\ credential_type=assumed_role 为了生成一组新的代入角色 STS 凭据,我们再次对 aws/sts 路径进行写操作: $ vault write aws/sts/deploy ttl=60m Key Value lease_id aws/sts/deploy/31d771a6-fb39-f46b-fdc5-945109106422 lease_duration 60m0s lease_renewable false access_key ASIAJYYYY2AA5K4WIXXX secret_key HSs0DYYYYYY9W81DXtI0K7X84H+OVZXK5BXXXX security_token AQoDYXdzEEwasAKwQyZUtZaCjVNDiXXXXXXXXgUgBBVUUbSyujLjsw6jYzboOQ89vUVIehUw/9MreAifXFmfdbjTr3g6zc0me9M+dB95DyhetFItX5QThw0lEsVQWSiIeIotGmg7mjT1//e7CJc4LpxbW707loFX1TYD1ilNnblEsIBKGlRNXZ+QJdguY4VkzXxv2urxIH0Sl14xtqsRPboV7eYruSEZlAuP3FLmqFbmA0AFPCT37cLf/vUHinSbvw49C4c9WQLH7CeFPhDub7/rub/QU/lCjjJ43IqIRo9jYgcEvvdRkQSt70zO8moGCc7pFvmL7XGhISegQpEzudErTE/PdhjlGpAKGR3d5qKrHpPYK/k480wk1Ai/t1dTa/8/3jUYTUeIkaJpNBnupQt7qoaXXXXXXXXXX 故障排查 动态 IAM 用户错误 如果看到类似于以下任一内容的错误消息,则写入 aws/config/root 的根凭据权限不足: $ vault read aws/creds/deploy * Error creating IAM user: User: arn:aws:iam::000000000000:user/hashicorp is not authorized to perform: iam:CreateUser on resource: arn:aws:iam::000000000000:user/vault-root-1432735386-4059 $ vault revoke aws/creds/deploy/774cfb27-c22d-6e78-0077-254879d1af3c Revoke error: Error making API request. URL: PUT http://127.0.0.1:8200/v1/sys/revoke/aws/creds/deploy/774cfb27-c22d-6e78-0077-254879d1af3c Code: 400. Errors: * invalid request 任何时候如果遇到问题,都可以运行 vault path-help aws 或是使用子路径来获取交互式帮助信息。 STS 联合令牌错误 Vault 使用配置在 aws/config 的 IAM 凭据生成 STS 令牌。 这些凭据必须具有两个属性: 他们必须有权限调用 sts:GetFederationToken 他们所拥有的权限必须至少与要附加到待生成的 STS 凭据的权限相等 如果上述任一一个条件得不到满足,就会返回 \"403 not-authorized\"。 查阅 http://docs.aws.amazon.com/STS/latest/APIReference/API_GetFederationToken.html 获取更多信息。 使用 STS 令牌时,建议使用 Vault 0.5.1 或更高版本,以避免超过 AWS 对 STS 令牌名称的 32 个字符的限制而导致验证错误。 AWS 读取实例元数据超时 影响 Vault 1.4 及以后的版本 Vault 在 EC2 实例上使用实例元数据服务的时候,例如从实例配置文件中获取凭据,使用到实例元数据服务 (IMDSv2) 的 v2 版本时可能会出现延迟。 Vault 使用的 AWS SDK 首先尝试连接到 IMDSv2,如果超时,则回退到 v1。在 Vault 1.4 中,此超时最多可能需要 2 分钟。在 Vault 1.5.5 及更高版本中最多可能需要 2 秒钟。 超时会发生在 Vault 和 IMDSv2 之间存在代理,并且实例 hop 限制设置为小于 Vault 和 IMDSv2 之间的“hop”数的时候。例如,如果 Vault 在 EC2 实例上的 docker 中运行,并且实例 hop 限制设置为 1,AWS SDK 客户端将尝试连接到 IMDSv2,超时并回退到 IMDSv1,因为 docker 和 IMDS 之间存在额外的网络 hop. 为避免超时行为,可以在底层 EC2 实例上调整 hop 限制。对于 docker 示例,将 hop 限制设置为 2 就可以让 Vault 中的 AWS SDK 无延迟地连接到 IMDSv2。 "},"6.机密引擎/3.Azure.html":{"url":"6.机密引擎/3.Azure.html","title":"Azure","keywords":"","body":"Azure Azure 机密引擎能够动态生成 Azure 服务主体(principal),并为其分配角色和所属的组。 Vault 角色可以映射到一个或多个 Azure 角色,还可以选择是否分配到组,从而提供一种简单、灵活的方式来管理授予给生成的服务主体的权限。 每个服务主体都关联了一个 Vault 租约。当租约过期时(正常到期吊销或提前主动吊销),服务主体将被自动删除。 如果将现有服务主体设置为角色配置的一部分,那么将动态生成新密码而不是新服务主体。当租约被吊销时,密码将被删除。 注意,微软将关闭他们的 Azure Active Directory API,并将于 2022 年停用。如果当前正在使用本机密引擎,需要更新凭据以包含 Microsoft Graph API 权限并将 use_microsoft_graph_api 配置项设置为 true。有关更多详细信息,请参阅 API 文档。 配置 大部分机密引擎在工作之前必须要先进行配置。这些步骤通常是由系统管理员来完成。 启用 Azure 机密引擎: $ vault secrets enable azure Success! Enabled the azure secrets engine at: azure/ 默认情况下,机密引擎会挂载到名字对应的路径上。要在不同路径上启用机密引擎,请使用 -path 参数。 使用账号凭据配置机密引擎: $ vault write azure/config \\ subscription_id=$AZURE_SUBSCRIPTION_ID \\ tenant_id=$AZURE_TENANT_ID \\ client_id=$AZURE_CLIENT_ID \\ client_secret=$AZURE_CLIENT_SECRET \\ use_microsoft_graph_api=true Success! Data written to: azure/config 如果在启用了托管服务身份(Managed Service Identity, MSI)的 Azure 虚拟机上运行 Vault,可以省略 client_id 和 client_secret。 配置一个 Vault 角色。一个 Vault 角色要么被配置使用一个现存的服务主体,要么是一组将要被赋予动态创建的服务主体上的 Azure 角色。 可以用以下命令配置一个名为 \"my-role\" 的角色使用一个现存的服务主体: $ vault write azure/roles/my-role \\ application_object_id= \\ ttl=1h 或者,把 Vault 角色配置成为新建的服务主体赋予一些 Azure 角色: $ vault write azure/roles/my-role ttl=1h azure_roles=-/resourceGroups/Website\" } ] EOF 角色也可以将其 TTL 配置成为挂载点上配置的 TTL 不同的值。 可用标志 配置完机密引擎后拥有合适权限的 Vault 令牌的用户或者机器就可以用它来生成凭据了。使用现存服务主体或是创建新的服务主体在创建凭据时的用法时一样的。 要用\"my-role\"角色创建凭据: $ vault read azure/creds/my-role Key Value --- ----- lease_id azure/creds/sp_role/1afd0969-ad23-73e2-f974-962f7ac1c2b4 lease_duration 60m lease_renewable true client_id 408bf248-dd4e-4be5-919a-7f6207a307ab client_secret ad06228a-2db9-4e0a-8a5d-e047c7f32594 该路径生成一组可更新的凭据。应用程序可以使用 client_id/client_secret 登录,并且将拥有由配置的服务主体或在“my-role”配置中设置的 Azure 角色集合提供的访问权限。 角色 Vault 角色允许配置成使用一个现存的服务主体或使用一组 Azure 角色,每个角色都可以设置自己的 TTL 参数。如果没有设置现存的服务主体,那么配置的 Azure 角色会被分配给新创建的服务主体。 Vault 角色可以配置它自己的 ttl 和/或 max_ttl 值。在创建租约时,会使用更靠近角色的位置上配置的 TTL 值(角色上的配置优于挂载点上的配置)。 Application Object IDs 如果要使用现存的服务主体,则必须在 Vault 角色上设置 Application Object ID。可以通过使用 az 命令行工具或通过 Azure 门户网站查找所需的应用程序来找到此 ID。请注意,必须提供 Application Object ID,而不是 Application ID。 Azure 角色 如果使用动态服务主体,那么必须在 Vault 角色上配置要使用的 Azure 角色。Azure 角色通过一个 JSON 格式列表配置,每个元素都描述了要分配的 Azure 角色和范围。可以使用 role_name 参数(比如 “Owner”)或 role_id 参数(“/subscriptions/.../roleDefinitions/...”)指定 Azure 角色。 role_id 是 Vault 最终使用的 ID; role_name 在角色管理操作时很方便。写入配置时所有角色都必须存在,否则操作将失败。角色查找优先级是: 如果配置了 role_id,则对其进行验证后更新相应的 role_name。 如果只配置了 role_name,会根据角色名进行一次大小写敏感的搜索,只有查询到正好一个匹配记录才算成功。role_id 字段会在成功匹配到角色 ID 时更新。 不管使用哪种方式定义角色,都必须要设置相应的 scope 参数。 Azure 组 如果使用动态服务主体,可以在 Vault 角色上配置一组 Azure 组。当服务主体被创建时,它会被分派到这些组里。与用于指定 Azure 角色的格式类似,Azure 组可以通过它们的 group_name 或 object_id 进行引用。按名称设置的组必须返回一个匹配的组。 角色配置示例: $ vault write azure/roles/my-role \\ ttl=1h \\ max_ttl=24h \\ azure_roles=@az_roles.json \\ azure_groups=@az_groups.json $ cat az_roles.json [ { \"role_name\": \"Contributor\", \"scope\": \"/subscriptions//resourceGroups/Website\" }, { \"role_id\": \"/subscriptions//providers/Microsoft.Authorization/roleDefinitions/\", \"scope\": \"/subscriptions/\" }, { \"role_name\": \"This won't matter as it will be overwritten\", \"role_id\": \"/subscriptions//providers/Microsoft.Authorization/roleDefinitions/\", \"scope\": \"/subscriptions//resourceGroups/Database\" } ] $ cat az_groups.json [ { \"group_name\": \"foo\", }, { \"group_name\": \"This won't matter as it will be overwritten\", \"object_id\": \"a6a834a6-36c3-4575-8e2b-05095963d603\" } ] 身份验证 Azure 机密引擎必须具有足够的权限才能读取 Azure 角色信息以及管理服务主体。身份验证参数可以在引擎中设置或通过环境变量读取。优先读取环境变量。 如果没有配置 client id 和 client secret,并且 Vault 在 Azure VM 上运行,则 Vault 将尝试使用托管服务标识 (MSI) 来访问 Azure。请注意,使用 MSI 时,仍必须在配置或环境变量中明确提供 tenet id 和 subscription id。 MS Graph 权限 配置由 Vault 使用来管理 Azure 的服务主体需要具备以下 Azure 权限: Permission Name Type Application.Read.All Application Application.ReadWrite.All Application Application.ReadWrite.OwnedBy Application Directory.Read.All Application Directory.ReadWrite.All Application Group.Read.All Application Group.ReadWrite.All Application GroupMember.Read.All Application GroupMember.ReadWrite.All Application Permission Name Type Application.Read.All Delegated Application.ReadWrite.All Delegated Directory.AccessAsUser.All Delegated Directory.Read.All Delegated Directory.ReadWrite.All Delegated Group.Read.All Delegated Group.ReadWrite.All Delegated GroupMember.Read.All Delegated GroupMember.ReadWrite.All Delegated 此外,需要下列额外的 IAM 角色,并且需要使用 Azure 门户添加到服务主体: \"Owner\" 角色 在动态创建服务主体或是使用现存服务主体之间进行选择 如果可以通过 RBAC 系统和 Vault 角色中定义的 Azure 角色创建所需的 Azure 资源,则首选动态创建服务主体。这种形式的凭证与任何其他客户端完全分离,发布后不受权限变更的影响,并提供最佳的审计粒度。 但是,某些 Azure 服务无法通过 RBAC 系统提供访问权限。在这些情况下,可以使用必要的访问权限设置现存的服务主体,并且 Vault 可以为该服务主体创建新密码。对服务主体权限的任何变更都会影响所有使用该服务主体的客户端。此外,Azure 无法提供有关操作使用哪个凭据的任何日志记录。 使用现存服务主体时的一个重要限制是 Azure 限制单个 Application 的密码数量。此限制基于应用程序对象大小,并没有明确规定,但实际上每个 Application 可以创建数百个密码。如果达到对象大小,将返回错误。可以通过减少 Vault 角色配置的 TTL 或使用一个权限完全相同的其他 Azure 服务主体创建另一个 Vault 角色来绕过该限制。 额外的注意事项 如果引用的 Azure 角色不存在,则不会生成凭据。只有在所有角色的分配都成功时才会生成服务主体。如果使用的自定义 Azure 角色定义可能会在某些时候被删除,请务必注意这一点。 Azure 角色只会在创建服务主体时分配一次。如果 Vault 角色变更了使用的 Azure 角色列表,这些变更将不会反映在任何现存服务主体中,即使在令牌续约之后也是如此。 签发凭据所需的时间与必须分配的 Azure 角色数大致成正比。此操作需要一些时间(通常需要 10 秒,并且可能超过一分钟)。 生成的 Azure 服务主体凭据不会在 Azure 端设置超时属性。 Vault 将通过删除服务主体来吊销访问权限。 动态创建的服务主体的应用程序名称将以 vault- 为前缀。同样,添加到现有服务主体的任何密码的 keyId 都将以 ffffff 开头。这些可用于使用 az 命令行工具或门户搜索 Vault 创建的凭据。 "},"6.机密引擎/4.Consul.html":{"url":"6.机密引擎/4.Consul.html","title":"Consul","keywords":"","body":"Consul Consul 机密引擎可以根据 Consul ACL 策略动态生成 Consul API 令牌。 配置 大部分机密引擎在工作之前必须要先进行配置。这些步骤通常是由系统管理员或是配置管理工具来完成。 启用 Consul 机密引擎: $ vault secrets enable consul Success! Enabled the consul secrets engine at: consul/ 默认情况下,机密引擎会挂载到名字对应的路径上。要在不同路径上启用机密引擎,请使用 -path 参数。 在 Consul 1.4 之前的版本中,需要从 Consul 获取一个管理令牌,使用 Cosnul 配置文件中的 acl_master_token 或是其他管理令牌: $ curl \\ --header \"X-Consul-Token: my-management-token\" \\ --request PUT \\ --data '{\"Name\": \"sample\", \"Type\": \"management\"}' \\ https://consul.rocks/v1/acl/create Vault 必须配置一个管理类型的令牌使得它可以创建并吊销 ACL 令牌。上述命令的响应会返回一个新令牌: { \"ID\": \"7652ba4c-0f6e-8e75-5724-5e083d72cfe4\" } 对 Vault 1.4 及以上的版本,可以使用命令行生成具有合适策略的令牌: CONSUL_HTTP_TOKEN=d54fe46a-1f57-a589-3583-6b78e334b03b consul acl token create -policy-name=global-management 配置 Vault 连接到 Consul 并进行身份验证: $ vault write consul/config/access \\ address=127.0.0.1:8500 \\ token=7652ba4c-0f6e-8e75-5724-5e083d72cfe4 Success! Data written to: consul/config/access 配置将 Vault 中的名称映射到 Consul ACL 策略的角色。根据使用的 Consul 版本,需要设置一个策略文档和一个 token_type,或一组策略。当用户生成凭据时,这些凭据是根据此角色生成的。对于 1.4 以下的 Consul 版本: $ vault write consul/roles/my-role policy=$(base64 策略需要以 base64 编码。请参照 Consul 策略文档。 对于 Consul 1.4 及以上版本,可以在 Consul 中生成一个策略,然后将策略与 Vault 角色关联起来: $ vault write consul/roles/my-role policies=readonly Success! Data written to: consul/roles/my-role 令牌租约期限:如果没有设置 ttl 值(或是在 Consul 1.4 及以下版本中的 lease),那么 Vault 的 Consul 机密引擎创建的令牌默认会有 30 天的 TTL。我们可以通过向命令传递 -ttl= 参数来修改租约的期限。\"duration\" 参数是一个以类似 \"30s\" 或是 \"1h\" 结尾的字符串。 使用方法 配置了机密引擎后,一个拥有搭配了合适权限的 Vault 令牌的用户或是机器可以生成 Consul 凭据。 通过读取搭配角色名的 /creds 端点来创建一个新的凭据: $ vault read consul/creds/my-role Key Value --- ----- lease_id consul/creds/my-role/b2469121-f55f-53c5-89af-a3ba52b1d6d8 lease_duration 768h lease_renewable true token 642783bf-1540-526f-d4de-fe1ac1aed6f0 如果使用的是 Consul 1.4,响应会包括令牌的访问器: $ vault read consul/creds/my-role Key Value --- ----- lease_id consul/creds/my-role/7miMPnYaBCaVWDS9clNE0Nv3 lease_duration 768h lease_renewable true accessor 6d5a0348-dffe-e87b-4266-2bec03800abb token bc7a42c0-9c59-23b4-8a09-7173c474dc42 令牌的过期轮换:一旦令牌的 TTL 过期,Consul 将不再允许使用该令牌进行操作。这要求我们必须通过外部进程来轮换令牌。目前推荐的方式是相关管理员通过手动执行 vault read consul/creds/my-role 命令来轮换成创建的新令牌。一旦通过 Consul 同步到了新令牌,将新令牌应用到使用 Consul API 的代理或是命令行工具中。 "},"6.机密引擎/5.Cubbyhole.html":{"url":"6.机密引擎/5.Cubbyhole.html","title":"Cubbyhole","keywords":"","body":"Cubbyhole cubbyhole 机密引擎是一个每个令牌私有的 Vault 物理存储空间,可以用来存储令牌专属的任意数据。在 cubbyhole 引擎中,路径是根据令牌隔离的。没有令牌可以访问其他令牌的 cubbyhole。当令牌过期时,它的 cubbyhole 也会被销毁。 另外不同于 kv 机密引擎,由于 cubbyhole 的生命周期是与经身份验证的令牌绑定的,所以 cubbyhole 中的值并没有 TTL 或是刷新周期的概念。 向 cubbyhole 机密引擎写入一个键会彻底覆盖旧值。 配置 大部分机密引擎在工作之前必须要先进行配置。这些步骤通常是由系统管理员或是配置管理工具来完成。 cubbyhole 机密引擎默认启用。它无法被禁用、移动或是重复多次启用。 使用方法 配置了机密引擎后,一个拥有搭配了合适权限的 Vault 令牌的用户或是机器可以生成凭据。cubbyhole 机密引擎允许写入任意键值数据。 写入任意数据: $ vault write cubbyhole/my-secret my-value=s3cr3t Success! Data written to: cubbyhole/my-secret 读取任意数据: $ vault read cubbyhole/my-secret Key Value --- ----- my-value s3cr3t "},"6.机密引擎/6.Database.html":{"url":"6.机密引擎/6.Database.html","title":"Database","keywords":"","body":"Databases 数据库机密引擎根据配置的角色动态生成数据库凭据。它通过插件接口与许多不同的数据库协同工作。有许多内置的数据库类型,以及一个用于运行自定义数据库类型以实现可扩展性的公开框架。这意味着需要访问数据库的服务不再需要使用硬编码的凭据:它们可以从 Vault 请求凭据,并使用 Vault 的租约机制来更轻松地轮换密钥。这些被称为“动态角色”或“动态机密”。 由于每个服务都使用与众不同的凭据访问数据库,因此当发现有问题的数据访问时,审计会变得更加容易。我们可以通过 SQL 用户名跟踪到服务的特定实例。 Vault 使用内建的吊销系统来确保用户的凭据在租约到期后的合理时间内失效。 静态角色 数据库机密引擎支持“静态角色”的概念,即 Vault 角色与数据库中的用户名的一对一映射。数据库用户的当前密码由 Vault 在可配置的时间段内存储和自动轮换。这与动态机密不同,动态机密的每次请求凭据都会生成唯一的用户名和密码对。当为角色请求凭据时,Vault 会返回已配置数据库用户的当前密码,允许任何拥有适当 Vault 策略的人访问数据库中的用户帐户。 目前并不是所有数据库类型都支持静态角色,请阅读数据库功能文档了解该数据库后端是否支持静态角色。 配置 大部分机密引擎在工作之前必须要先进行配置。这些步骤通常是由系统管理员或是配置管理工具来完成。 启用数据库机密引擎: vault secrets enable database Success! Enabled the database secrets engine at: database/ 默认情况下,机密引擎会挂载到名字对应的路径上。要在不同路径上启用机密引擎,请使用 -path 参数。 使用合适的插件以及连接信息来配置 Vault: $ vault write database/config/my-database \\ plugin_name=\"...\" \\ connection_url=\"...\" \\ allowed_roles=\"...\" \\ username=\"...\" \\ password=\"...\" 我们强烈建议在数据库中为 Vault 创建一个专属用户。该用户会被用来操控数据库中的动态和静态用户。在文档中该用户被称为“根”用户。 Vault 将我们在这里指定的用户来创建/更新/吊销数据库凭据。该用户必须具有适当的权限才能对其他数据库用户执行操作(创建、更新凭据、删除等)。 该秘密引擎可以配置多个数据库连接。有关特定配置选项的详细信息,请参阅相关数据库类型的文档。 配置了根用户后,我们强烈建议轮换用户密码,使得 Vault 以外的用户无法使用该账号: vault write -force database/rotate-root/my-database 当上述命令完成后,之前为用户配置的密码将无法再使用。所以我们强烈推荐在数据库中为 Vault 创建一个专户用户来管理其他数据库用户。 配置一个角色,将 Vault 中的名称映射到一组创建语句以创建数据库凭据: $ vault write database/roles/my-role \\ db_name=my-database \\ creation_statements=\"...\" \\ default_ttl=\"1h\" \\ max_ttl=\"24h\" Success! Data written to: database/roles/my-role 语句中使用模板语法 {{username}} 以及 {{password}},这些自动断会被插件使用动态生成的值替换。某些插件还支持 {{expiration}} 字段。 使用方法 配置了机密引擎后,一个拥有搭配了合适权限的 Vault 令牌的用户或是机器可以生成数据库凭据。 通过读取搭配角色名的 /creds 端点来创建一个新的凭据: $ vault read database/creds/my-role Key Value --- ----- lease_id database/creds/my-role/2f6a614c-4aa2-7b19-24b9-ad944a8d4de6 lease_duration 1h lease_renewable true password FSREZ1S0kFsZtLat-y94 username v-vaultuser-e2978cd0-ugp7iqI2hdlff5hfjylJ-1602537260 数据库功能 在 Vault 1.6 开始,所有数据库引擎都支持动态角色和静态角色。除 MongoDB Atlas 以外所有引擎都支持轮换根用户的凭据。 数据库 根用户凭据轮换 动态角色 静态角色 自定义用户名 ElasticSearch Yes Yes Yes(1.6+) Yes(1.8+) MongoDB Yes Yes Yes Yes(1.7+) MSSQL Yes Yes Yes Yes(1.7+) MySQL/MariaDB Yes Yes Yes Yes(1.7+) PostgreSQ Yes Yes Yes Yes(1.7+) 自定义插件 数据库机密引擎支持通过实现一个公开的插件框架来支持自定义数据库类型。相关章节本书不作翻译。 密码生成 密码根据密码策略生成。数据库可以选择设置密码策略以供该数据库的所有角色使用。换句话说,每一次我们调用 vault write database/config/my-database 时我们可以使用 my-database 为所有角色指定密码策略。每一个数据库都有一个如下的默认密码策略:20 位字符,至少一个大写字母,至少一个小写字母,至少一个数字,是少一个破折号。 我们无法为特定角色指定密码策略,因为密码策略的目的是遵守系统(例如数据库)的密码要求,而不是为特定用户设置密码。 默认密码生成策略可以表示为以下密码策略: length = 20 rule \"charset\" { charset = \"abcdefghijklmnopqrstuvwxyz\" min-chars = 1 } rule \"charset\" { charset = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" min-chars = 1 } rule \"charset\" { charset = \"0123456789\" min-chars = 1 } rule \"charset\" { charset = \"-\" min-chars = 1 } "},"6.机密引擎/6.1.Database_ElasticSearch.html":{"url":"6.机密引擎/6.1.Database_ElasticSearch.html","title":"Elasticsearch","keywords":"","body":"Elasticsearch Elasticsearch 是数据库机密引擎所支持的插件之一。该插件可以根据为 Elasticsearch 配置的角色动态生成数据库凭据。 能力 数据库插件名称 根用户凭据轮换 动态角色 静态角色 自定义用户名 elasticsearch-database-plugin Yes Yes Yes(1.6+) Yes(1.8+) 开始使用 要使用该插件,我们必须先必须首先通过激活 X-Pack 启用 Elasticsearch 的本机安全领域(native realm of security)。这些说明将引导我们使用 Elasticsearch 7.1.1 完成此操作。 启用 Elasticsearch 的 X-Pack 安全机制 请阅读 Elastic Stack的安全加固并执行启用 X-Pack 安全机制的步骤。 启用加密通信 该插件与 Elasticsearch 的安全 API 进行通信。Elasticsearch 要求这些通信启用 TLS 来加密通信流量。 要设置 Elaticsearch 的 TLS,请先阅读加密通信并执行加密 HTTP 客户端通信中的步骤。 启用了 Elasticsearch 端的 TLS 后,我们需要将生成的 .p12 证书转换成其他格式使得 Vault 可以使用。这里有一个使用 OpenSSL 将我们的 .p12 证书转换为 pem 格式的例子。 此外,在运行 Elasticsearch 的实例上,我们需要安装新生成的 CA 证书,该证书最初为 .p12 格式。为此,我们将 .p12 CA 证书转换为 pem,然后进一步将该 pem 转换为 crt,将该 crt 添加到 /usr/share/ca-certificates/extra,并执行 sudo dpkg-reconfigure ca-certificates 命令。 如果我们使用的不是 Ubuntu 系统,上述操作步骤可能会有所不同。请确保我们使用的是特定于当前操作环境的方法,我们不在此做一一说明。 设置密码 完成上述步骤后,通过运行 $ $ES_HOME/bin/elasticsearch-setup-passwords interactive 来验证是否已启用 X-Pack。如果它引导您完成多个密码输入步骤,就说明它已被成功设置。 为 Vault 创建一个角色 下面,我们推荐在 Elasticsearch 中为 Vault 创建一个专门用于管理机密的用户。 为此,首先创建一个角色,通过对 Elasticsearch 执行 POST,允许 Vault 获得管理用户和密码所需的最低权限。为实现该目标,我们使用名为 elastic 的 Vault 用户,密码就是之前通过 $ $ES_HOME/bin/elasticsearch-setup-passwords interactive 命令创建得到的: $ curl \\ -X POST \\ -H \"Content-Type: application/json\" \\ -d '{\"cluster\": [\"manage_security\"]}' \\ http://vaultuser:$PASSWORD@localhost:9200/_xpack/security/role/vault 然后我们在 Vault 中创建一个关联该角色的新用户: curl \\ -X POST \\ -H \"Content-Type: application/json\" \\ -d @data.json \\ http://vaultuser:$PASSWORD@localhost:9200/_xpack/security/user/vault 例子中的 data.json 文件的内容是: { \"password\" : \"myPa55word\", \"roles\" : [ \"vault\" ], \"full_name\" : \"Hashicorp Vault\", \"metadata\" : { \"plugin_name\": \"Vault Plugin Database Elasticsearch\", \"plugin_url\": \"https://github.com/hashicorp/vault-plugin-database-elasticsearch\" } } 现在,Elasticsearch 已经完成了配置,可以与 Vault 一起使用了。 设置 如果数据库机密引擎尚未启用,先启用该机密引擎: $ vault secrets enable database Success! Enabled the database secrets engine at: database/ 默认情况下,机密引擎会挂载到名字对应的路径上。要在不同路径上启用机密引擎,请使用 -path 参数。 使用合适的插件以及连接信息来配置 Vault: $ vault write database/config/my-elasticsearch-database \\ plugin_name=\"elasticsearch-database-plugin\" \\ allowed_roles=\"internally-defined-role,externally-defined-role\" \\ username=vault \\ password=myPa55word \\ url=http://localhost:9200 \\ ca_cert=/usr/share/ca-certificates/extra/elastic-stack-ca.crt.pem \\ client_cert=$ES_HOME/config/certs/elastic-certificates.crt.pem \\ client_key=$ES_HOME/config/certs/elastic-certificates.key.pem 配置一个角色,将 Vault 中的名称映射到 Elasticsearch 中的角色定义。这被认为是最安全的角色类型,因为没有人可以通过在 Elasticsearch 中编辑角色的带外(out-of-band)权限来实现特权提升: $ vault write database/roles/internally-defined-role \\ db_name=my-elasticsearch-database \\ creation_statements='{\"elasticsearch_role_definition\": {\"indices\": [{\"names\":[\"*\"], \"privileges\":[\"read\"]}]}}' \\ default_ttl=\"1h\" \\ max_ttl=\"24h\" Success! Data written to: database/roles/internally-defined-role 或者配置一个角色,将 Vault 中的名称映射到 Elasticsearch 中预先存在的角色定义: $ vault write database/roles/externally-defined-role \\ db_name=my-elasticsearch-database \\ creation_statements='{\"elasticsearch_roles\": [\"pre-existing-role-in-elasticsearch\"]}' \\ default_ttl=\"1h\" \\ max_ttl=\"24h\" Success! Data written to: database/roles/externally-defined-role 使用方法 配置了机密引擎后,一个拥有搭配了合适权限的 Vault 令牌的用户或是机器可以生成数据库凭据。 通过读取搭配角色名的 /creds 端点来创建一个新的凭据: $ vault read database/creds/my-role Key Value --- ----- lease_id database/creds/my-role/2f6a614c-4aa2-7b19-24b9-ad944a8d4de6 lease_duration 1h lease_renewable true password 0ZsueAP-dqCNGZo35M0n username v-vaultuser-my-role-AgIViC5TdQHBdeiCxae0-1602541724 "},"6.机密引擎/6.2.Database_MongoDB.html":{"url":"6.机密引擎/6.2.Database_MongoDB.html","title":"MongoDB","keywords":"","body":"MongoDB MongoDB 是数据库机密引擎所支持的插件之一。该插件可以根据为 MongoDB 配置的角色动态生成数据库凭据,也支持静态角色。 能力 数据库插件名称 根用户凭据轮换 动态角色 静态角色 自定义用户名 mongodb-database-plugin Yes Yes Yes Yes(1.7+) 配置 如果数据库机密引擎尚未启用,先启用该机密引擎: $ vault secrets enable database Success! Enabled the database secrets engine at: database/ 默认情况下,机密引擎会挂载到名字对应的路径上。要在不同路径上启用机密引擎,请使用 -path 参数。 使用合适的插件以及连接信息来配置 Vault: $ vault write database/config/my-mongodb-database \\ plugin_name=mongodb-database-plugin \\ allowed_roles=\"my-role\" \\ connection_url=\"mongodb://{{username}}:{{password}}@mongodb.acme.com:27017/admin?tls=true\" \\ username=\"vaultuser\" \\ password=\"vaultpass!\" 在 Vault 中配置一个映射到一条创建数据库凭据的 MongoDB 命令的 Vault 角色: $ vault write database/roles/my-role \\ db_name=my-mongodb-database \\ creation_statements='{ \"db\": \"admin\", \"roles\": [{ \"role\": \"readWrite\" }, {\"role\": \"read\", \"db\": \"foo\"}] }' \\ default_ttl=\"1h\" \\ max_ttl=\"24h\" Success! Data written to: database/roles/my-role 使用方法 配置了机密引擎后,一个拥有搭配了合适权限的 Vault 令牌的用户或是机器可以生成数据库凭据。 通过读取搭配角色名的 /creds 端点来创建一个新的凭据: $ vault read database/creds/my-role Key Value --- ----- lease_id database/creds/my-role/2f6a614c-4aa2-7b19-24b9-ad944a8d4de6 lease_duration 1h lease_renewable true password LEm-lcDJ2k0Hi05FvizN username v-vaultuser-my-role-ItceCZHlp0YGn90Puy9Z-1602542024 客户端 x509 证书身份验证 该插件支持使用 MongoDB 的 x509 客户端证书身份验证。 要配置插件使用该身份验证机制: $ vault write database/config/my-mongodb-database \\ plugin_name=mongodb-database-plugin \\ allowed_roles=\"my-role\" \\ connection_url=\"mongodb://@mongodb.acme.com:27017/admin\" \\ tls_certificate_key=@/path/to/client.pem \\ tls_ca=@/path/to/client.ca 请注意,tls_certificate_key 和 tls_ca 对应了 MongoDB 配置中的 tlsCertificateKeyFile 以及 tlsCAFile 配置项,但 Vault 参数应配置为这些文件的内容而非文件名。有鉴于此,这两个选项彼此独立。请阅读 MongoDB 配置选项 获取更多相关信息。 "},"6.机密引擎/6.3.Database_MSSQL.html":{"url":"6.机密引擎/6.3.Database_MSSQL.html","title":"MSSQL","keywords":"","body":"MSSQL MSSQL 是数据库机密引擎所支持的插件之一。该插件可以根据为 MSSQL 配置的角色动态生成数据库凭据。 能力 数据库插件名称 根用户凭据轮换 动态角色 静态角色 自定义用户名 mssql-database-plugin Yes Yes Yes Yes(1.7+) 配置 如果数据库机密引擎尚未启用,先启用该机密引擎: $ vault secrets enable database Success! Enabled the database secrets engine at: database/ 默认情况下,机密引擎会挂载到名字对应的路径上。要在不同路径上启用机密引擎,请使用 -path 参数。 使用合适的插件以及连接信息来配置 Vault: $ vault write database/config/my-mssql-database \\ plugin_name=mssql-database-plugin \\ connection_url='sqlserver://{{username}}:{{password}}@localhost:1433' \\ allowed_roles=\"my-role\" \\ username=\"vaultuser\" \\ password=\"yourStrong(!)Password\" 请注意,上面的例子中演示的 SQL Server 连接使用的用户名是 vaultuser,尽管用户 vaultuser 可能是活动目录域中的 Windows 身份验证用户,例如 DOMAIN\\vaultuser。 上面的例子中,我们配置 Vault 使用名为 \"sa\" 的用户名以及 \"yourStrong(!)Password\" 作为密码,连接到运行于 \"localhost\" 1433 端口的实例上。Vault 并不一定需要以 sa 登录,但配置使用的用户必须拥有创建登录名及管理 SQL Server 进程的特权。作为例子,内建的 securityadmin 以及 processadmin 角色拥有这些权限。用户同时必须对 Vault 管理的数据库拥有创建数据库用户以及授予数据库的权限。作为例子,内建的 db_accessadmin 以及 db_securityadmin 角色拥有这些权限。 在 Vault 中配置一个映射到一条创建数据库凭据的 SQL 命令的 Vault 角色: $ vault write database/roles/my-role \\ db_name=my-mssql-database \\ creation_statements=\"CREATE LOGIN [{{name}}] WITH PASSWORD = '{{password}}';\\ CREATE USER [{{name}}] FOR LOGIN [{{name}}];\\ GRANT SELECT ON SCHEMA::dbo TO [{{name}}];\" \\ default_ttl=\"1h\" \\ max_ttl=\"24h\" Success! Data written to: database/roles/my-role 要小心,如果没有设置 revocation_statement,Vault 会执行默认的吊销过程。在大型数据库中,这有可能引发连接超时。这种情况下请指定一个吊销语句。 使用方法 配置了机密引擎后,一个拥有搭配了合适权限的 Vault 令牌的用户或是机器可以生成数据库凭据。 通过读取搭配角色名的 /creds 端点来创建一个新的凭据: $ vault read database/creds/my-role Key Value --- ----- lease_id database/creds/my-role/2f6a614c-4aa2-7b19-24b9-ad944a8d4de6 lease_duration 1h lease_renewable true password wJKpk9kg-T1Ma7qQfS8y username v-vaultuser-my-role-r7kCtKGGr3eYQP1OGR6G-1602542258 Azure SQL Database 的例子 这里有一个使用 Azure SQL Database 的完整例子。注意 Azure SQL Database 中的数据库是 contained database,我们不为用户创建登录名。取而代之的是,我们将密码直接关联到用户自身。另请注意,对于要为其生成动态凭据的每个 Azure SQL 数据库,都需要一个单独的连接和角色。我们可以为所有这些数据库使用单个数据库后端挂载,也可以为其中的一个使用单独的挂载。在此示例中,我们为数据库后端使用自定义路径。 首先,我们通过 vault secrets enable -path=azuresql database 命令将数据库后端挂载到 azuresql 路径上。然后我们配置一个名为 testvault 的连接到 test-vault 数据库的连接,在路径的开头我们使用 “azuresql”: $ vault write azuresql/config/testvault \\ plugin_name=mssql-database-plugin \\ connection_url='server=hashisqlserver.database.windows.net;port=1433;user id=admin;password=pAssw0rd;database=test-vault;app name=vault;' \\ allowed_roles=\"test\" 现在,我们为 \"testvault\" 连接添加一个名为 \"test\" 的角色: $ vault write azuresql/roles/test \\ db_name=testvault \\ creation_statements=\"CREATE USER [{{name}}] WITH PASSWORD = '{{password}}';\" \\ revocation_statements=\"DROP USER IF EXISTS [{{name}}]\" \\ default_ttl=\"1h\" \\ max_ttl=\"24h\" 我们现在可以使用该角色为 Azure SQL 数据库 test-vault 动态生成凭据: $ vault read azuresql/creds/test Key Value --- ----- lease_id azuresql/creds/test/2e5b1e0b-a081-c7e1-5622-39f58e79a719 lease_duration 1h0m0s lease_renewable true password cZ-BJy-SqO5tKwazAuUP username v-token-test-tr2t4x9pxvq1z8878s9s-1513446795 当我们不再需要该后端时,我们可以使用命令 vault unmount azuresql 摘除它。现在,我们可以通过 MSSQL 数据库插件管理我们的 Azure SQL Database 了。 Amazon RDS MSSQL 插件支持管理运行在 Amazon RDS 上的数据库,但需要适应一些差异。最主要的限制是 Amazon RDS 不支持 \"sysadmin\" 角色,这是 Vault 用来吊销 MSSQL 进程的默认角色。解决方法是为角色添加以下自定义吊销语句: vault write database/roles/my-role revocation_statements=\"\\ USE my_database; \\ IF EXISTS \\ (SELECT name \\ FROM sys.database_principals \\ WHERE name = N'{{name}}') \\ BEGIN \\ DROP USER [{{name}}] \\ END \\ IF EXISTS \\ (SELECT name \\ FROM master.sys.server_principals \\ WHERE name = N'{{name}}') \\ BEGIN \\ DROP LOGIN [{{name}}] \\ END\" "},"6.机密引擎/6.4.Database_MYSQL.html":{"url":"6.机密引擎/6.4.Database_MYSQL.html","title":"MySQL/MariaDB","keywords":"","body":"MySQL/MariaDB MySQL 是数据库机密引擎所支持的插件之一。该插件可以根据为 MySQL 配置的角色动态生成数据库凭据,也支持静态角色。 该插件在 Vault 中内置了几个不同的实例,每个实例用于稍微不同的 MySQL 驱动程序。这些插件之间的唯一区别是插件生成的用户名长度,因为不同版本的 mysql 接受不同的长度。可用的插件有: mysql-database-plugin mysql-aurora-database-plugin mysql-rds-database-plugin mysql-legacy-database-plugin 能力 数据库插件名称 根用户凭据轮换 动态角色 静态角色 自定义用户名 视情况而定(见上方) Yes Yes Yes Yes(1.7+) 配置 如果数据库机密引擎尚未启用,先启用该机密引擎: $ vault secrets enable database Success! Enabled the database secrets engine at: database/ 默认情况下,机密引擎会挂载到名字对应的路径上。要在不同路径上启用机密引擎,请使用 -path 参数。 使用合适的插件以及连接信息来配置 Vault: $ vault write database/config/my-mysql-database \\ plugin_name=mysql-database-plugin \\ connection_url=\"{{username}}:{{password}}@tcp(127.0.0.1:3306)/\" \\ allowed_roles=\"my-role\" \\ username=\"vaultuser\" \\ password=\"vaultpass\" 在 Vault 中配置一个映射到一条创建数据库凭据的 SQL 命令的 Vault 角色: $ vault write database/roles/my-role \\ db_name=my-mysql-database \\ creation_statements=\"CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}';GRANT SELECT ON *.* TO '{{name}}'@'%';\" \\ default_ttl=\"1h\" \\ max_ttl=\"24h\" Success! Data written to: database/roles/my-role 使用方法 配置了机密引擎后,一个拥有搭配了合适权限的 Vault 令牌的用户或是机器可以生成数据库凭据。 通过读取搭配角色名的 /creds 端点来创建一个新的凭据: $ vault read database/creds/my-role Key Value --- ----- lease_id database/creds/my-role/2f6a614c-4aa2-7b19-24b9-ad944a8d4de6 lease_duration 1h lease_renewable true password yY-57n3X5UQhxnmFRP3f username v_vaultuser_my-role_crBWVqVh2Hc1 客户端 x509 证书身份验证 该插件支持使用 MySQL 的 x509 客户端证书身份验证。 要配置插件使用该身份验证机制: $ vault write database/config/my-mysql-database \\ plugin_name=mysql-database-plugin \\ allowed_roles=\"my-role\" \\ connection_url=\"user:password@tcp(localhost:3306)/test\" \\ tls_certificate_key=@/path/to/client.pem \\ tls_ca=@/path/to/client.ca 请注意,tls_certificate_key 和 tls_ca 对应了 MySQL 配置中的 ssl-cert (结合了 ssl-key) 以及 ssl-ca 配置项,但 Vault 参数应配置为这些文件的内容而非文件名。有鉴于此,这两个选项彼此独立。请阅读 MySQL 连接配置选项 获取更多相关信息。 例子 在授权语句中使用通配符 MySQL 支持在授权语句中使用通配符。如果应用程序需要访问 MySQL 中大量数据库则需要该功能。这可以通过在授权语句中使用通配符来实现。例如,如果希望 Vault 创建的用户能够访问以 fooapp_ 开头的所有数据库,可以使用以下创建语句: CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}'; GRANT SELECT ON `fooapp\\_%`.* TO '{{name}}'@'%'; MySQL 期望将通配符放在反引号内的部分。如果想通过 Vault CLI 将此创建语句添加到 Vault,则不能简单地将上述语句粘贴到 CLI 上,因为 shell 会将反引号之间的文本解释为必须执行的内容。解决此问题的最简单方法是将创建语句编码为 Base64 并将其提供给 Vault。例如: vault write database/roles/my-role \\ db_name=mysql \\ creation_statements=\"Q1JFQVRFIFVTRVIgJ3t7bmFtZX19J0AnJScgSURFTlRJRklFRCBCWSAne3twYXNzd29yZH19JzsgR1JBTlQgU0VMRUNUIE9OIGBmb29hcHBcXyVgLiogVE8gJ3t7bmFtZX19J0AnJSc7\" \\ default_ttl=\"1h\" \\ max_ttl=\"24h\" 在 MySQL 5.6 中轮换根凭据 默认的 MySQL 根凭据了轮换使用了 MySQL 5.7 开始出现的 ALTER USER 语法。对 MySQL 5.6,轮换根密钥语句必须配置为使用旧的 SET PASSWORD 语法,举例来说: $ vault write database/config/my-mysql-database \\ plugin_name=mysql-database-plugin \\ connection_url=\"{{username}}:{{password}}@tcp(127.0.0.1:3306)/\" \\ root_rotation_statements=\"SET PASSWORD = PASSWORD('{{password}}')\" \\ allowed_roles=\"my-role\" \\ username=\"root\" \\ password=\"mysql\" "},"6.机密引擎/6.5.Database_Postgres.html":{"url":"6.机密引擎/6.5.Database_Postgres.html","title":"PostgreSQL","keywords":"","body":"PostgreSQL PostgreSQL 是数据库机密引擎所支持的插件之一。该插件可以根据为 PostgreSQL 配置的角色动态生成数据库凭据,也支持静态角色。 PostgreSQL 机密引擎使用 pq,与 PostgreSQL 存储后端使用的是同一个数据库库。连接字符串信息,包括 SSL 配置项,可以参考这篇文档。 能力 数据库插件名称 根用户凭据轮换 动态角色 静态角色 自定义用户名 postgresql-database-plugin Yes Yes Yes Yes(1.7+) 配置 如果数据库机密引擎尚未启用,先启用该机密引擎: $ vault secrets enable database Success! Enabled the database secrets engine at: database/ 默认情况下,机密引擎会挂载到名字对应的路径上。要在不同路径上启用机密引擎,请使用 -path 参数。 使用合适的插件以及连接信息来配置 Vault: $ vault write database/config/my-postgresql-database \\ plugin_name=postgresql-database-plugin \\ allowed_roles=\"my-role\" \\ connection_url=\"postgresql://{{username}}:{{password}}@localhost:5432/\" \\ username=\"vaultuser\" \\ password=\"vaultpass\" 在 Vault 中配置一个映射到一条创建数据库凭据的 SQL 命令的 Vault 角色: $ vault write database/roles/my-role \\ db_name=my-postgresql-database \\ creation_statements=\"CREATE ROLE \\\"{{name}}\\\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \\ GRANT SELECT ON ALL TABLES IN SCHEMA public TO \\\"{{name}}\\\";\" \\ default_ttl=\"1h\" \\ max_ttl=\"24h\" Success! Data written to: database/roles/my-role 使用方法 配置了机密引擎后,一个拥有搭配了合适权限的 Vault 令牌的用户或是机器可以生成数据库凭据。 通过读取搭配角色名的 /creds 端点来创建一个新的凭据: $ vault read database/creds/my-role Key Value --- ----- lease_id database/creds/my-role/2f6a614c-4aa2-7b19-24b9-ad944a8d4de6 lease_duration 1h lease_renewable true password SsnoaA-8Tv4t34f41baD username v-vaultuse-my-role-x "},"6.机密引擎/7.KV.html":{"url":"6.机密引擎/7.KV.html","title":"Key / Value","keywords":"","body":"Key / Value kv 机密引擎是一个通用的键值存储,用于在 Vault 使用的物理存储中存储任意秘密。该后端可以以两种模式之一运行;可以将其配置为存储密钥的单个值,或者可以开启版本控制并存储每个键的一定数量版本的值。 KV 版本 1 当运行非版本化的 kv 机密引擎时,只有最近写入的值会被保存下来。非版本化 kv 的好处是减少了每个键的存储大小,因为不需要存储额外的元数据或历史记录。此外,发送到以这种方式配置的引擎的请求将具有更高的性能,因为对于任何给定的请求,存储调用更少并且不需要用锁。 KV 版本 2 在运行 kv 引擎的 v2 时,每个键可以保留可配置的版本数的值。默认为 10 个版本。可以检索旧版本的元数据和数据。此外,Check-and-Set 操作可用于避免无意中覆盖数据。 当一个版本被删除时,底层数据不会被删除,而是被标记为已删除。可以撤销删除已删除的版本。要永久删除版本的数据,可以使用 destroy 命令或调用 API。此外,可以通过删除元数据命令或调用 API 来删除键的所有版本和元数据。这些操作中的每一个都可以进行单独的访问权限控制,从而限制谁有权软删除、取消删除或完全删除数据。 我们将用两个独立的节来分别讲述两个版本的 kv 引擎。 "},"6.机密引擎/7.1.KV_v1.html":{"url":"6.机密引擎/7.1.KV_v1.html","title":"Key / Value v1","keywords":"","body":"Key / Value —— Version 1 kv 机密引擎用于在 Vault 使用的物理存储中存储任意机密。 写入 kv 引擎中的键将用新值替换旧值;子字段不会合并在一起。 键名必须始终是字符串。如果您直接通过命令行编写非字符串类型的值,它们将被转换为字符串。但是,您可以通过从 JSON 文件或使用 HTTP API 写入键值数据来保留非字符串类型的值。 这个机密引擎能够区分 ACL 策略内的create和update功能之间的区别。 注意:路径名和键名没有被混淆或加密;只有在键上设置的值是加密的。不要将敏感信息存储为机密路径的一部分。 配置 启用 version 1 的 kv 存储: $ vault secrets enable -version=1 kv 可用标志 配置完机密引擎后拥有合适权限的 Vault 令牌的用户或者机器就可以用它来读写数据了。kv 机密引擎允许在键值对的值中写入任意数据。 写入一些数据: $ vault kv put kv/my-secret my-value=s3cr3t Success! Data written to: kv/my-secret 读取一些数据: $ vault kv get kv/my-secret Key Value --- ----- my-value s3cr3t 列出键: $ vault kv list kv/ Keys ---- my-secret 删除一个键: $ vault kv delete kv/my-secret Success! Data deleted (if it existed) at: kv/my-secret TTL 与其他机密引擎不同,kv 机密引擎不会强制执行 TTL 过期。取而代之的是,lease_duration 提示消费者应该多久检查一次新值。 如果设置了一个键的 ttl,kv 机密引擎将使用此值作为租约期限: $ vault kv put kv/my-secret ttl=30m my-value=s3cr3t Success! Data written to: kv/my-secret 即使设置了 ttl,机密引擎也不会自行删除数据。 ttl 只是一个参考。 "},"6.机密引擎/7.2.KV_v2.html":{"url":"6.机密引擎/7.2.KV_v2.html","title":"Key / Value v2","keywords":"","body":"Key / Value —— Version 2 kv 机密引擎用于在 Vault 使用的物理存储中存储任意机密。 键名必须始终是字符串。如果您直接通过命令行编写非字符串类型的值,它们将被转换为字符串。但是,您可以通过从 JSON 文件或使用 HTTP API 写入键值数据来保留非字符串类型的值。 这个机密引擎能够区分 ACL 策略内的 create 和 update 功能之间的区别。还支持 patch 功能,用于表示部分更新,而 update 功能表示完全覆盖。 配置 大部分机密引擎在工作之前必须要先进行配置。这些步骤通常是由系统管理员来完成。 用以下命令启用一个 v2 版本的 kv 机密引擎: $ vault secrets enable -version=2 kv 或者可以用 kv-v2 作为机密引擎类型: $ vault secrets enable kv-v2 此外,在运行\"dev\"模式的测试服务器时,默认会在路径 secret/ 处启用 v2 kv 机密引擎(对于非\"dev\"服务器,当前为 v1)。可以在多个不同的路径禁用、移动或启用它。 kv 机密引擎在每个挂载点上启用的实例都是隔离且唯一的。 从 Version 1 升级到 Version 2 现有的 Version 1 kv 存储可以通过命令行或 API 升级到 Version 2 kv 存储。这将启动升级过程,将现有的键/值数据升级为版本化的格式。在此过程中将无法访问相关路径。此过程可能需要很长时间,因此提前做好计划。 升级到 Version 2 后,以前用来访问数据的路径将不再适用。您将需要调整用户策略以添加对 Version 2 路径的访问,如下面的 ACL 规则部分所述。同样,一旦升级到 Version 2,用户/应用程序将需要更新他们与 kv 数据交互的路径。 可以使用下面的命令将 Version 1 升级到 Version 2: $ vault kv enable-versioning secret/ Success! Tuned the secrets engine at: secret/ 或者调用 API: $ cat payload.json { \"options\": { \"version\": \"2\" } } $ curl \\ --header \"X-Vault-Token: ...\" \\ --request POST \\ --data @payload.json \\ http://127.0.0.1:8200/v1/sys/mounts/secret/tune ACL 规则 Version 2 kv 存储使用带前缀的 API,这与 Version 1 API 不同。在从 Version 1 kv 升级之前,要更改 ACL 规则。Version 2 API 中的不同路径也可以采用不同的 ACL。 读写 Version 2 数据都要以 data/ 为前缀。以下策略适用于版本 1 kv: path \"secret/dev/team-1/*\" { capabilities = [\"create\", \"update\", \"read\"] } 到 Version 2 就应该写成: path \"secret/data/dev/team-1/*\" { capabilities = [\"create\", \"update\", \"read\"] } 数据删除的权限路径也不一样,要让策略授予删除键的最新版本的权限: path \"secret/data/dev/team-1/*\" { capabilities = [\"delete\"] } 要让策略允许删除一个键的任意版本: path \"secret/delete/dev/team-1/*\" { capabilities = [\"update\"] } 要让策略允许撤销删除数据: path \"secret/undelete/dev/team-1/*\" { capabilities = [\"update\"] } 要让策略允许彻底删除一个版本的数据: path \"secret/destroy/dev/team-1/*\" { capabilities = [\"update\"] } 要让策略允许列出键: path \"secret/metadata/dev/team-1/*\" { capabilities = [\"list\"] } 要让策略允许查询每个版本的元数据: path \"secret/metadata/dev/team-1/*\" { capabilities = [\"read\"] } 要让策略允许删除指定键的所有版本和元数据: path \"secret/metadata/dev/team-1/*\" { capabilities = [\"delete\"] } 不能为 Version 2 的 kv 存储设置包含 allowed_parameters、 denied_parameters 和 required_parameters 字段的策略。 可用标志 配置完机密引擎后拥有合适权限的 Vault 令牌的用户或者机器就可以用它来读写数据了。kv 机密引擎允许在键值对的值中写入任意数据。 读写任意数据 写入数据 $ vault kv put secret/my-secret foo=a bar=b Key Value --- ----- created_time 2019-06-19T17:20:22.985303Z custom_metadata map[bar:123 foo:abc] deletion_time n/a destroyed false version 1 读取数据: $ vault kv get secret/my-secret ====== Metadata ====== Key Value --- ----- created_time 2019-06-19T17:20:22.985303Z custom_metadata map[bar:123 foo:abc] deletion_time n/a destroyed false version 1 ====== Data ====== Key Value --- ----- foo a 再次向同一键写入新版本的数据,之前的版本还是可以访问的。可以选择传递 -cas 标志来执行 check-and-set 操作。如果设置为 -cas=0,则仅当密钥不存在时才允许写入。如果 -cas 不为零,则仅当密钥的当前版本与 cas 参数中指定的版本一致时才允许写入。 $ vault kv put -cas=1 secret/my-secret foo=aa bar=bb Key Value --- ----- created_time 2019-06-19T17:22:23.369372Z custom_metadata map[bar:123 foo:abc] deletion_time n/a destroyed false version 2 再读一次就可以得到最新版本的数据: $ vault kv get secret/my-secret ====== Metadata ====== Key Value --- ----- created_time 2019-06-19T17:22:23.369372Z custom_metadata map[bar:123 foo:abc] deletion_time n/a destroyed false version 2 ====== Data ====== Key Value --- ----- foo aa bar bb 可以使用 vault kv patch 命令实现部分更新。命令将首先尝试发送一个需要 patch ACL 权限的 HTTP PATCH 请求。如果使用的令牌关联的策略中不包含 patch 权限,那么 PATCH 请求将失败。在这种情况下,该命令将读取最新数据,在本地更新后写回,这一系列操作需要在 ACL 中拥有 read 和 update 权限。 可以选择使用 -cas 参数来执行 check-and-set 操作。它只会在一开始的 PATCH 请求中使用。 读取后完整更新的流程将使用读取返回的机密中的 version 值来在后续写入中执行 check-and-set 操作。 $ vault kv patch -cas=2 secret/my-secret bar=bbb Key Value --- ----- created_time 2019-06-19T17:23:49.199802Z custom_metadata map[bar:123 foo:abc] deletion_time n/a destroyed false version 3 vault kv patch 命令还支持 -method 标志,可用于指定是使用 HTTP PATCH 还是先读后写。支持的值分别是 patch 和 rw。 使用 patch 方法执行部分更新: $ vault kv patch -method=patch -cas=2 secret/my-secret bar=bbb Key Value --- ----- created_time 2019-06-19T17:23:49.199802Z custom_metadata map[bar:123 foo:abc] deletion_time n/a destroyed false version 3 使用 rw 方法进行更新: $ vault kv patch -method=rw secret/my-secret bar=bbb Key Value --- ----- created_time 2019-06-19T17:23:49.199802Z custom_metadata map[bar:123 foo:abc] deletion_time n/a destroyed false version 3 部分更新后读取机密会可以从返回的最新版本的数据中看到,只有部分字段发生了变更: $ vault kv get secret/my-secret ====== Metadata ====== Key Value --- ----- created_time 2019-06-19T17:23:49.199802Z custom_metadata map[bar:123 foo:abc] deletion_time n/a destroyed false version 3 ====== Data ====== Key Value --- ----- foo aa bar bbb 可以使用 -version 标志查看先前的版本: $ vault kv get -version=1 secret/my-secret ====== Metadata ====== Key Value --- ----- created_time 2019-06-19T17:20:22.985303Z custom_metadata map[bar:123 foo:abc] deletion_time n/a destroyed false version 1 ====== Data ====== Key Value --- ----- foo a bar b 删除 / 彻底删除(Destroy)数据 删除数据时,普通的 vault kv delete 命令将执行软删除。它将将该版本标记为已删除并填充一个 deletion_time 时间戳。软删除不会从存储中删除底层版本数据,这使我们可以撤销删除版本。 vault kv undelete 命令可以撤销删除版本。 仅当键的版本数超过 max-versions 设置允许的数量时,或使用 vault kv destroy 命令时,才会永久删除版本数据。当使用 destroy 命令时,底层版本数据将被删除,并且键值对的元数据将被标记为已销毁。如果一个版本是通过 max-versions 机制被清理的,那么它的版本元数据也将从键值对中删除。 以下操作可以提供进一步的说明: 可以使用 delete 命令删除键值对的最新版本,也可以通过 -versions 标志来删除过去的版本: $ vault kv delete secret/my-secret 可以撤销删除的版本: $ vault kv undelete -versions=2 secret/my-secret Success! Data written to: secret/undelete/my-secret $ vault kv get secret/my-secret ====== Metadata ====== Key Value --- ----- created_time 2019-06-19T17:23:21.834403Z custom_metadata map[bar:123 foo:abc] deletion_time n/a destroyed false version 2 ====== Data ====== Key Value --- ----- my-value short-lived-s3cr3t 彻底删除一个版本的数据: $ vault kv destroy -versions=2 secret/my-secret Success! Data written to: secret/destroy/my-secret 键的元数据 可以使用元数据命令和 API 跟踪键的所有版本和元数据。删除键的元数据将导致该键值对的所有元数据和版本被永久删除。 查询一个键的所有元数据和版本: $ vault kv metadata get secret/my-secret ========== Metadata ========== Key Value --- ----- cas_required false created_time 2019-06-19T17:20:22.985303Z current_version 2 custom_metadata map[bar:123 foo:abc] delete_version_after 0s max_versions 0 oldest_version 0 updated_time 2019-06-19T17:22:23.369372Z ====== Version 1 ====== Key Value --- ----- created_time 2019-06-19T17:20:22.985303Z deletion_time n/a destroyed false ====== Version 2 ====== Key Value --- ----- created_time 2019-06-19T17:22:23.369372Z deletion_time n/a destroyed true 可以设置一个键的元数据配置: $ vault kv metadata put -max-versions 2 -delete-version-after=\"3h25m19s\" secret/my-secret Success! Data written to: secret/metadata/my-secret -delete-version-after 设置只对新版本数据有效。下次写入的数据就会应用 -max-versions 的约束: $ vault kv put secret/my-secret my-value=newer-s3cr3t Key Value --- ----- created_time 2019-06-19T17:31:16.662563Z custom_metadata map[bar:123 foo:abc] deletion_time 2019-06-19T20:56:35.662563Z destroyed false version 4 当一个键的版本超过 -max-versions 设置的限制,旧版本会被清理: $ vault kv metadata get secret/my-secret ========== Metadata ========== Key Value --- ----- cas_required false created_time 2019-06-19T17:20:22.985303Z current_version 4 custom_metadata map[bar:123 foo:abc] delete_version_after 3h25m19s max_versions 2 oldest_version 3 updated_time 2019-06-19T17:31:16.662563Z ====== Version 3 ====== Key Value --- ----- created_time 2019-06-19T17:23:21.834403Z deletion_time n/a destroyed true ====== Version 4 ====== Key Value --- ----- created_time 2019-06-19T17:31:16.662563Z deletion_time 2019-06-19T20:56:35.662563Z destroyed false 机密键值对的元数据可以包含用于描述机密的自定义元数据。数据将存储为字符串到字符串的键值对。如果设置了 -custom-metadata 标志,则 custom_metadata 的值将被覆盖。 可以重复使用 -custom-metadata 标志添加多个键值对: $ vault kv metadata put -custom-metadata=foo=abc -custom-metadata=bar=123 secret/my-secret 永久删除一个键的所有元数据和版本: $ vault kv metadata delete secret/my-secret Success! Data deleted (if it existed) at: secret/metadata/my-secret "},"6.机密引擎/8.RabbitMQ.html":{"url":"6.机密引擎/8.RabbitMQ.html","title":"RabbitMQ","keywords":"","body":"RabbitMQ RabbitMQ 机密引擎根据配置的权限以及虚拟主机(virtual host)来动态生成用户凭据。这意味着需要访问虚拟主机的服务不再需要硬编码凭据。 由于每个服务都使用与众不同的凭据访问数据库,因此当发现有问题的数据访问时,审计会变得更加容易。我们可以通过 RabbitMQ 用户名跟踪到服务的特定实例。 Vault 在创建的用户对应的租约到期而变得无效时,会同时调用其自身内部的吊销系统以及删除 RabbitMQ 用户以确保该用户不再有效。 配置 大部分机密引擎在工作之前必须要先进行配置。这些步骤通常是由系统管理员或是配置管理工具来完成。 启用 RabbitMQ 机密引擎: $ vault secrets enable rabbitmq Success! Enabled the rabbitmq secrets engine at: rabbitmq/ 默认情况下,机密引擎会挂载到名字对应的路径上。要在不同路径上启用机密引擎,请使用 -path 参数。 配置 Vault 用来与 RabbitMQ 通信以生成凭据的凭据: $ vault write rabbitmq/config/connection \\ connection_uri=\"http://localhost:15672\" \\ username=\"admin\" \\ password=\"password\" Success! Data written to: rabbitmq/config/connection 必须要确保 Vault 用户有管理用户的管理员权限。 在 Vault 中配置一个映射到 RabbitMQ 虚拟主机权限的 Vault 角色: $ vault write rabbitmq/roles/my-role \\ vhosts='{\"/\":{\"write\": \".*\", \"read\": \".*\"}}' Success! Data written to: rabbitmq/roles/my-role 通过写入 roles/my-role 路径,我们定义了 my-role 角色。该角色将通过评估给定的 vhosts、vhost_topics 和 tags 语句来创建。默认情况下,不会为角色分配标签、虚拟主机或主题权限。如果未定义主题权限并使用默认授权后端(authorisation backend),则始终可以发布到主题交换或从主题消费。可以阅读有关 RabbitMQ 管理标签和 RabbitMQ 主题授权的更多信息。 使用方法 配置了机密引擎后,一个拥有搭配了合适权限的 Vault 令牌的用户或是机器可以生成 RabbitMQ 凭据。 通过读取搭配角色名的 /creds 端点来创建一个新的凭据: $ vault read rabbitmq/creds/my-role Key Value --- ----- lease_id rabbitmq/creds/my-role/I39Hu8XXOombof4wiK5bKMn9 lease_duration 768h lease_renewable true password 3yNDBikgQvrkx2VA2zhq5IdSM7IWk1RyMYJr username root-39669250-3894-8032-c420-3d58483ebfc4 通过使用 ACL,可以限制使用 rabbitmq 机密引擎,以便受信任的操作员可以管理角色定义,并且用户和应用程序都被限制只能使用他们被允许读取的凭据。 "},"6.机密引擎/9.SSH.html":{"url":"6.机密引擎/9.SSH.html","title":"SSH","keywords":"","body":"SSH 名称:ssh Vault SSH 机密引擎为通过 SSH 协议访问机器提供安全的身份验证和授权功能。Vault SSH 机密引擎有助于管理对机器基础设施的访问,提供多种颁发 SSH 凭据的方法。 Vault SSH 机密引擎支持以下模式: 签名的 SSH 证书 一次性 SSH 密码 阅读上述内容需要了解 SSH 协议的基础知识。 "},"6.机密引擎/9.1.SSH_SignedCertificates.html":{"url":"6.机密引擎/9.1.SSH_SignedCertificates.html","title":"签名的证书","keywords":"","body":"签名的 SSH 证书 就设置复杂性和平台无关性而言,签名的 SSH 证书是最简单和最强大的。通过利用 Vault 强大的 CA 能力和 OpenSSH 内置的功能,客户端可以使用自己的本地 SSH 密钥通过 SSH 连接到目标主机。 本节中的“客户端”指的是执行 SSH 操作的人或者机器。“服务端”指的是目标机器。客户端也可以看作“用户”。 本节仅演示如何快速使用该机密引擎。要获取所有可用路径的详细文档,请使用 vault path-help 命令,命令后方输入机密引擎的挂载地点。 客户端密钥签名 在客户端可以请求对他们的 SSH 密钥进行签名之前,必须配置 Vault SSH 机密引擎。通常由 Vault 管理员或安全团队执行这些步骤。也可以使用 Chef、Puppet、Ansible 或 Salt 等配置管理工具来自动化这些操作。 签名密钥 & 配置角色 以下步骤由 Vault 管理员、安全团队或配置管理工具预先执行。 挂载机密引擎。就像 Vault 中所有的机密引擎一样,SSH 机密引擎在使用前必须先挂载: $ vault secrets enable -path=ssh-client-signer ssh Successfully mounted 'ssh' at 'ssh-client-signer'! 该命令在路径 ssh-client-signer 上启用了 SSH 机密引擎。可以通过使用不同的 -path 参数多次挂载同一个机密引擎。这里的名字 ssh-client-signer 并没有什么特殊的——它可以是任何名字,但本节假设挂载点是 ssh-client-signer。 透过 /config/ca 端点给 Vault 配置一个 CA ,用以对客户端密钥进行签名。如果我们没有内部 CA,Vault 会为我们生成一个密钥对(keypair)。 $ vault write ssh-client-signer/config/ca generate_signing_key=true Key Value --- ----- public_key ssh-rsa AAAAB3NzaC1yc2EA... 如果我们已经有了密钥对,请将公钥和私钥部分指定为有效载荷的一部分: $ vault write ssh-client-signer/config/ca \\ private_key=\"...\" \\ public_key=\"...\" 无论是生成的还是上传的,用以对客户端进行签名的公钥都可以通过 API 的 /public_key 端点访问到。 将公钥添加到所有目标主机的 SSH 配置中。该进程可以手工完成,或是借助于配置管理工具。该公钥可以通过 API 得到,不需要经过身份验证: $ curl -o /etc/ssh/trusted-user-ca-keys.pem http://127.0.0.1:8200/v1/ssh-client-signer/public_key $ vault read -field=public_key ssh-client-signer/config/ca > /etc/ssh/trusted-user-ca-keys.pem 将存有公钥的文件路径保存在 SSH 配置文件中的 TrustedUserCAKeys 配置项上: # /etc/ssh/sshd_config # ... TrustedUserCAKeys /etc/ssh/trusted-user-ca-keys.pem 重启 SSH 服务以加载变更。 创建用于签署客户端密钥的命名 Vault 角色。 重要提示:在 Vault 1.9 之前,如果角色没有配置 \"allowed_extensions\" 或是配置为空白,Vault 会采用允许的默认值:分配给该角色的任何用户都可以指定任意扩展值作为对 Vault 服务器的证书请求的一部分。这可能会对依赖 extensions 字段来获取安全关键信息的第三方系统产生重大影响。在这些情况下,请考虑使用模板来指定默认扩展,并在字段为空或未设置时将 allowed_extensions 显式设置为任意非空字符串。 由于一些 SSH 证书功能实现的方式,选项以字典的方式传递。下面的例子中为证书添加了 permit-pty 扩展,并允许用户在请求证书时为 permit-pty 和 permit-port-forwarding 自行设置值: $ vault write ssh-client-signer/roles/my-role - 客户端 SSH 身份验证 希望通过身份验证连接到由 Vault 管理的机器的客户端(用户)需要执行以下步骤。这些命令通常从过年客户端运行的本地工作站运行: 定位或生成 SSH 公钥。通常公钥在 ~/.ssh/id_rsa.pub。如果我们没有 SSH 密钥对,可以生成一个: $ ssh-keygen -t rsa -C \"user@example.com\" 请求 Vault 签名公钥。该文件通常以 .pub 为后缀名,内容的开头为 ssh-rsa ...。 $ vault write ssh-client-signer/sign/my-role \\ public_key=@$HOME/.ssh/id_rsa.pub Key Value --- ----- serial_number c73f26d2340276aa signed_key ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1... 返回结果包含了序列号以及签名后的密钥。该密钥也是一个公钥。 要自定义签名参数,请使用 JSON 载荷: $ vault write ssh-client-signer/sign/my-role - 将经过签名的公钥保存到磁盘上。可以根据需要限制权限: $ vault write -field=signed_key ssh-client-signer/sign/my-role \\ public_key=@$HOME/.ssh/id_rsa.pub > signed-cert.pub 如果我们将证书直接保存在 SSH 密钥旁边,在文件名后添加 -cert.pub 后缀(!/.ssh/id_rsa-cert.pub)。如果按照这种规范命名,OpenSSH 会自动在进行身份验证时使用经签名的公钥。 (可选)查看经签名的密钥的已启用的扩展(extnsions)、主体(principals)以及元数据: $ ssh-keygen -Lf ~/.ssh/signed-cert.pub 使用经签名的密钥 SSH 连接至主机。我们需要同时提供从 Vault 获取的经签名的公钥以及相对应的私钥作为身份验证信息来发起 SSH 调用: $ ssh -i signed-cert.pub -i ~/.ssh/id_rsa username@10.0.23.5 对主机密钥签名 为了增加额外的安全性,我们建议启用主机密钥签名。与客户端密钥签名结合使用可以提供额外的完整性层。启用后,SSH 代理将在尝试 SSH 之前验证目标主机是否有效且受信任。这将减少用户通过 SSH 意外连接到非托管或恶意机器的可能性。 签名密钥配置 挂载机密引擎。为了最大程度的安全,请挂载到与签名客户端时不同的路径上: $ vault secrets enable -path=ssh-host-signer ssh Successfully mounted 'ssh' at 'ssh-host-signer'! 通过 Vault 的 /config/ca 端点配置一个对主机密钥进行签名用的 CA 证书。如果我们没有内部的 CA,Vault 可以为我们生成一个密钥对。 $ vault write ssh-host-signer/config/ca generate_signing_key=true Key Value --- ----- public_key ssh-rsa AAAAB3NzaC1yc2EA... 如果我们已经有了密钥对,请将公钥和私钥部分指定为有效载荷的一部分: $ vault write ssh-host-signer/config/ca \\ private_key=\"...\" \\ public_key=\"...\" 无论是生成的还是上传的,用以对主机进行签名的公钥都可以通过 API 的 /public_key 端点访问到。 延展主机密钥证书的 TTL: $ vault secrets tune -max-lease-ttl=87600h ssh-host-signer 创建用于对主机密钥进行签名的角色。一定要填写 allowed domains 列表,或设置 allow_bare_domains,或同事设置两者: $ vault write ssh-host-signer/roles/hostrole \\ key_type=ca \\ ttl=87600h \\ allow_host_certificates=true \\ allowed_domains=\"localdomain,example.com\" \\ allow_subdomains=true 对主机的 SSH 公钥进行签名: $ vault write ssh-host-signer/sign/hostrole \\ cert_type=host \\ public_key=@/etc/ssh/ssh_host_rsa_key.pub Key Value --- ----- serial_number 3746eb17371540d9 signed_key ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1y... 将返回的经签名的证书保存到磁盘: $ vault write -field=signed_key ssh-host-signer/sign/hostrole \\ cert_type=host \\ public_key=@/etc/ssh/ssh_host_rsa_key.pub > /etc/ssh/ssh_host_rsa_key-cert.pub 将证书文件的权限设置为 0640: $ chmod 0640 /etc/ssh/ssh_host_rsa_key-cert.pub 将主机密钥及证书添加到 SSH 配置文件中: # /etc/ssh/sshd_config # ... # For client keys TrustedUserCAKeys /etc/ssh/trusted-user-ca-keys.pem # For host keys HostKey /etc/ssh/ssh_host_rsa_key HostCertificate /etc/ssh/ssh_host_rsa_key-cert.pub 重启 SSH 服务以加载变更。 客户端对主机进行验证 获取主机使用的 CA 公钥来验证目标机器的签名: $ curl http://127.0.0.1:8200/v1/ssh-host-signer/public_key $ vault read -field=public_key ssh-host-signer/config/ca 将返回的公钥添加到 known_hosts 文件的 authority 中: # ~/.ssh/known_hosts @cert-authority *.example.com ssh-rsa AAAAB3NzaC1yc2EAAA... 像往常一样 SSH 连接到目标机器。 故障排查 当开始配置此类密钥签名时,开启 SSH 登录的 VERBOSE 级别日志可以帮助在日志中定位各种错误: # /etc/ssh/sshd_config # ... LogLevel VERBOSE 重启 SSH 服务以加载变更。 默认情况下,SSH 将日志记录到 /var/log/auth.log,但还有其他许多程序也会将日志记录于此。如果要把 SSH 的日志提取出来,可以这样做: $ tail -f /var/log/auth.log | grep --line-buffered \"sshd\" 如果我们无法连接到主机,服务端的 SSH 日志可能可以提供指引和洞见。 name is not a listed principal 如果 auth.log 显示一下信息: # /var/log/auth.log key_cert_check_authority: invalid certificate Certificate invalid: name is not a listed principal 该证书不允许将用户名作为列出的主体对系统进行身份验证。这很可能是由于 OpenSSH 错误(有关更多信息,请参阅已知问题)。此错误导致 allowed_users 选项值如果为“*”将不起作用。以下是解决此问题的方法: 设置角色中的 default_user。如果我们只使用同一个用户进行身份验证,可以将角色的 default_user 设置为 SSH 到目标机器时使用的用户名。 $ vault write ssh/roles/my-role - 在签名过程总设置 valid_principals。如果有多个用户需要通过 Vault 进行 SSH 身份验证,在密钥签名期间设置合法的主体列表,其中要包括当前用户名: $ vault write ssh-client-signer/sign/my-role - 登录后没有提示(prompt) 如果我们在通过登录到目标机器后没有看到提示,可能是因为经签名的证书没有配置 permit-pty 扩展。有两种方法为证书添加该扩展: 在创建角色时添加: $ vault write ssh-client-signer/roles/my-role - 在签名时添加: $ vault write ssh-client-signer/sign/my-role - 没有端口转发 如果从客户端到主机的端口转发不起作用,经签名的证书可能没有配置 permit-port-forwarding 扩展。可以参考上面没有提示部分的流程,在创建角色时添加该扩展: { \"default_extensions\": [ { \"permit-port-forwarding\": \"\" } ] } 没有 X11 转发 如果从客户端到主机的 X11 转发不起作用,经签名的证书可能没有配置 permit-X11-forwarding 扩展。可以参考上面没有提示部分的流程,在创建角色时添加该扩展: { \"default_extensions\": [ { \"permit-X11-forwarding\": \"\" } ] } 没有代理(agent)转发 如果从客户端到主机的代理转发不起作用,经签名的证书可能没有配置 permit-agent-forwarding 扩展。可以参考上面没有提示部分的流程,在创建角色时添加该扩展: { \"default_extensions\": [ { \"permit-agent-forwarding\": \"\" } ] } 已知的问题 在启用了 SELinux 的系统上,我们可能需要调整相关类型使得 SSH 守护进程可以读取到它。流入,将经签名的主机证书设置成 sshd_key_t 类型。 在某些版本的 SSH 上我们可能会看到以下错误: no separate private key for certificate 这是一个 OpenSSH 7.2 版本引入的 bug,在 7.5 被修复了。请阅读 OpenSSH bug 2617获取详细信息。 在某些版本的 SSH 上,我们可能会在目标主机上看到以下错误: userauth_pubkey: certificate signature algorithm ssh-rsa: signature algorithm not supported [preauth] 可以通过向 etc/ssh/sshd_config 追加以下内容来修复该错误: CASignatureAlgorithms ^ssh-rsa 从 OpenSSH 8.2开始不再支持 ssh-rsa 算法。 "},"6.机密引擎/9.2.SSH_OTP.html":{"url":"6.机密引擎/9.2.SSH_OTP.html","title":"一次性 SSH 密码","keywords":"","body":"一次性 SSH 密码 一次性 SSH 密码(One-Time Password,OTP)是一种使得 Vault 服务可以在客户端想要 SSH 到一台安装有帮助程序的远程服务器上时为其签发一条一次性 SSH 密码的 SSH 机密引擎。 经过身份验证的客户端从 Vault 申请一条一次性密码。当客户端建立起一条到远程服务器的 SSH 连接时,服务端在执行 SSH 身份验证时会将收到的密码交给 Vault 帮助程序,由其交由 Vault 服务进行验证。Vault 在成功验证后会删除该密码,以保证它只能被使用一次。 由于建立 SSH 连接时需要联络 Vault 服务,每一次尝试登录 SSH 以及相关的 Vault 租约信息都会被记录到机密引擎的日志中。 可以在这里 阅读 Vault 的 SSH 帮助程序的详细信息。 缺点 OTP 机密引擎类型的主要风险点是远程主机与 Vault 的连接;如果受到攻击,攻击者可以伪造 Vault 服务器返回的成功响应。可以通过使用 TLS 连接到 Vault 服务并检查证书有效性来减轻这种风险;未来对这个机密引擎的增强可能会在 TLS 的基础上提供额外的安全性。 挂载机密引擎 $ vault secrets enable ssh Successfully mounted 'ssh' at 'ssh'! 创建角色 将 key_type 设置为 otp 来创建一个角色。角色的 cidr_list 参数所代表的 CIDR 范围内所有的主机都应该预先正确安装并配置好 SSH 帮助程序。 $ vault write ssh/roles/otp_key_role \\ key_type=otp \\ default_user=username \\ cidr_list=x.x.x.x/y,m.m.m.m/n Success! Data written to: ssh/roles/otp_key_role 创建凭据 为一个属于 otp_key_role 的远程主机的 IP 创建一个 OTP 凭据: $ vault write ssh/creds/otp_key_role ip=x.x.x.x Key Value lease_id ssh/creds/otp_key_role/73bbf513-9606-4bec-816c-5a2f009765a5 lease_duration 600 lease_renewable false port 22 username username ip x.x.x.x key 2f7e25a2-24c9-4b7b-0d35-27d5e5203a5c key_type otp 建立 SSH 会话 $ ssh username@x.x.x.x Password: username@x.x.x.x:~$ 自动化该流程 可以用一条命令来创建新的一次性密码并使用正确参数调用 SSH 连接到主机: $ vault ssh -role otp_key_role -mode otp username@x.x.x.x OTP for the session is `b4d47e1b-4879-5f4e-ce5c-7988d7986f37` [Note: Install `sshpass` to automate typing in OTP] Password: 一次性密码会被自动交给 sshpass 输入(如果安装了该工具的话): $ vault ssh -role otp_key_role -mode otp -strict-host-key-checking=no username@x.x.x.x username@:~$ 注意:sshpass 无法检查主机证书。可以通过设置 -strict-host-key-checking=no 来关闭该功能。 "},"6.机密引擎/10.Transit.html":{"url":"6.机密引擎/10.Transit.html","title":"Transit","keywords":"","body":"Transit Transit 机密引擎对数据进行加密并不会持久化保存(所以它们被称为传输中的数据)。Vault 不会存储引擎接收到的数据。它也可以被视为“加密即服务”。Transit 机密引擎还可以对数据进行签名和验证;生成数据的散列和 HMAC;并充当随机数据的来源。 transit 主要用途是帮助加密应用程序发来的数据,然后应用程序在其主要数据存储中保存经过加密的数据。该机密引擎将正确加解密数据的重担从应用程序的开发者身上转移到了 Vault 的管理员这里。 该引擎支持派生密钥,这允许通过基于用户提供的上下文值派生新密钥来将相同的密钥用于多种目的。在这种模式下,可以选择支持收敛加密,它允许相同的输入值产生相同的密文。 生成数据密钥功能允许应用程序请求将给定位长度的高熵密钥返回给它们,并使用命名密钥进行加密。通常也可以以明文形式返回密钥使得请求方可以立即使用,但可以禁用此功能以适应审计要求。 工作集合(Working Set)管理 Transit 机密引擎支持密钥的版本化管理。早于 min_decryption_version 参数限制的老版本密钥将被归档,其余版本的密钥则属于工作集合。这是出于保持密钥快速加载的性能考虑,也是安全考虑:通过禁止解密旧版本的密钥,可以通过由于无法被大多数用户解密来发现哪些数据对应的密钥已经过期,但在紧急情况下 min_decryption_version 可以回拨以允许合法解密。 目前,这些被归档的老密钥被存储在单个存储条目中。对于一些存储后端,尤其是那些使用 Raft 或 Paxos 来实现高可用功能的后端,频繁轮换密钥可能会导致归档的存储条目大小大于存储后端可以处理的大小。对于频繁的轮换需求,使用与时间限制相对应的命名键(例如,五分钟为一个周期,计算出处于哪个周期数)可能会提供一个很好的选择,允许同时使用多个密钥以及什么时候使用哪个密钥。 NIST(美国国家标准技术研究所) 密钥轮换指引 推荐定期轮换加密密钥。如果使用的是 AES-GCM 密钥,每个密钥在执行了大约 232 次加密前需要根据 NIST 出版物 800-38D 进行轮换。建议管理员评估密钥的加密频率,并根据该频率来确定一个可以防止达到指导限制的轮换频率。例如,如果评估的速率是每天 4000 万次操作,那么每三个月轮换一次密钥就足够了。 密钥类型 目前 Transit 机密引擎支持以下密钥类型(所有这些密钥类型都支持生成独立的 HMAC 密钥): aes128-gcm96: 使用 128 位 AES 密钥以及 96 位随机数的 AES-GCM;支持加密、解密、密钥派生以及收敛加密 aes256-gcm96: 具有 256 位 AES 密钥和 96 位随机数的 AES-GCM;支持加密、解密、密钥派生和收敛加密(默认) chacha20-poly1305: 具有 256 位密钥的 ChaCha20-Poly1305;支持加密、解密、密钥派生和收敛加密 ed25519: Ed25519;支持签名、签名验证和密钥派生 ecdsa-p256: 使用 P-256 椭圆曲线的ECDSA;支持签名和签名验证 ecdsa-p384: 使用 P-384 椭圆曲线的ECDSA;支持签名和签名验证 ecdsa-p521: 使用 P-521 椭圆曲线的ECDSA;支持签名和签名验证 rsa-2048: 2048 位 RSA 密钥;支持加密、解密、签名和签名验证 rsa-3072: 3072 位 RSA 密钥;支持加密、解密、签名和签名验证 rsa-4096: 4096 位 RSA 密钥;支持加密、解密、签名和签名验证 收敛加密 收敛加密是一种相同的明文+上下文集合总是产生相同密文的模式。它通过使用密钥派生函数派生密钥以及确定性地派生随机数来做到这一点。因为在 2256 大小的密钥空间上,明文和密文的任何组合的这些属性都不同,所以随机数重用的风险接近于零。 该功能有很多实际用途。一种常见的使用模式是允许将值加密存储在数据库中,同时提供了有限的查找和查询的支持,以便可以从查询中返回特定字段为特定值的行。 为了适应任何对算法升级的需求,历史上存在过不同版本的收敛加密: Version 1 要求客户提供他们自己的随机数,使用起来非常灵活,但如果做得不正确可能会很危险。这仅在 Vault 0.6.1 中存在,使用此版本的密钥无法升级。 Version 2 使用算法来派生参数。但是,所使用的算法容易受到离线已知明文攻击,如果明文太短,攻击者可能会暴力破解。使用 Version 2 的密钥可以通过简单地执行轮换操作升级到新的密钥版本;然后可以根据新的密钥版本使用 Version 3 算法重新加密现有值。 Version 3 使用了专门用以抵御离线已知明文攻击的不同算法。该算法类似于 AES-SIV ,使用一个 PRF 以从明文中生成随机数。 配置 大部分机密引擎在工作之前必须要先进行配置。这些步骤通常是由系统管理员或是配置管理工具来完成。 启用 Transit 机密引擎 $ vault secrets enable transit Success! Enabled the transit secrets engine at: transit/ 默认情况下,机密引擎会挂载到名字对应的路径上。要在不同路径上启用机密引擎,请使用 -path 参数。 创建一个命名的加密密钥: $ vault write -f transit/keys/my-key Success! Data written to: transit/keys/my-key 一般来说每个应用程序都有其自己的加密密钥。 用法 配置了机密引擎后,一个拥有搭配了合适权限的 Vault 令牌的用户或是机器使用该机密引擎。 使用 /encrypt 端点搭配一个命名密钥加密一段明文数据: 注意:所有明文必须进行 base64 编码,这是因为 Vault 并不要求明文数据必须是文本,它也可以是一个二进制文件,例如一个 PDF 或是图片。把这种数据作为 JSON 载荷的一部分的最简单的安全传输机制是对其进行 base64 编码。 $ vault write transit/encrypt/my-key plaintext=$(base64 返回的密文以 vault:v1 开头。前缀 vault 表示该密文由 Vault 加密。v1 表示使用的是密钥的 version 1 进行加密的,这样当我们要轮换密钥时,Vault 就知道使用哪个版本的密钥来解密。剩下的部分就是初始化向量(IV)以及密文的 base64 编码的拼接值。 要注意 Vault 不会存储这些数据。调用者有责任存储加密的密文。如果调用者希望获得明文,必须将密文传回 Vault 进行解密。 要注意 Vault HTTP API 限制请求最大的尺寸为 32MB 来防范拒绝服务攻击。我们可以通过调整 Vault 配置中的 listener 块来修改该限制。 使用 /decrypt 端点搭配一个命名密钥解密这段密文: $ vault write transit/decrypt/my-key ciphertext=vault:v1:8SDd3WHDOjf7mq69CyCqYjBXAiQQAVZRkFM13ok481zoCmHnSeDX9vyf7w== Key Value --- ----- plaintext bXkgc2VjcmV0IGRhdGEK 返回结果也是经过 base64 编码的(原因前文已述)。将其解码为文本: $ base64 --decode 通过使用 ACL,可以限制使用 Transit 机密引擎,以便受信任的管理员可以管理命名密钥,并且应用程序只能使用他们需要访问的命名密钥进行加密或解密。 轮换使用的密钥。这将生成一个新密钥并将其添加至命名密钥的密钥环中: $ vault write -f transit/keys/my-key/rotate Success! Data written to: transit/keys/my-key/rotate 使用新密钥更新已经加密的密文。Vault 会使用密钥环中对应的密钥解密密文,再使用密钥环中最新的密钥重新加密: $ vault write transit/rewrap/my-key ciphertext=vault:v1:8SDd3WHDOjf7mq69CyCqYjBXAiQQAVZRkFM13ok481zoCmHnSeDX9vyf7w== Key Value --- ----- ciphertext vault:v2:0VHTTBb2EyyNYHsa3XiXsvXOQSLKulH+NqS4eRZdtc2TwQCxqJ7PUipvqQ== 该过程不会暴露明文数据。因此,Vault 策略可以授予几乎不受信任的进程“重新加密”密文数据的权限,因为该过程中无法访问明文数据。 "},"8.审计设备/审计设备.html":{"url":"8.审计设备/审计设备.html","title":"审计设备","keywords":"","body":"审计设备 审计设备是 Vault 中保存所有对 Vault 的请求和响应的详细日志的组件。由于所有对 Vault 的操作都是通过 API 请求/响应,因此审计日志包含与 Vault 的每个经过身份验证的交互,包括错误。 可以启用多个审核设备,Vault 会将审计日志发送给所有这些设备。这使得我们不仅可以拥有冗余副本,还可以拥有第二个副本,以防第一个副本被篡改。 格式 每行审计日志都是一个 JSON 对象。type 字段指定了对象的类型。目前,只有两种类型存在:request 和 response。该行包含一次请求和响应的所有信息。所有敏感信息在被记录进审计日志之前首先被哈希。 敏感信息 审计日志包含每次与 Vault 交互的完整请求和响应对象。可以使用分配给每个请求的唯一标识符来匹配请求和响应。 请求和响应中包含的大多数字符串都使用 HMAC-SHA256 加盐进行散列。散列的目的是使机密在审计日志中不以明文形式存在。但是,您仍然可以通过自己生成 HMAC 来检查机密的值,这可以通过使用 /sys/audit-hash API 端点调用审计设备的哈希函数和盐来完成。 请注意,目前只有来自 JSON 或以 JSON 返回的字符串会被 HMAC 的。其他数据类型,如整数、布尔值等,以明文形式传递。 虽然大多数字符串都被散列,但 Vault 确实会有一些例外,例如身份验证方法和机密引擎,用户可以使用 secrets enable 命令启用其他例外,然后再对其进行调整。 启用 / 禁用审计设备 第一次初始化 Vault 服务时,不会启用任何审计设备。必须由 root 用户使用 vault audit enable 启用审计设备。 启用审计设备时,可以将选项传递给它以对其进行配置。例如,用下面的命令启用文件审计设备: $ vault audit enable file file_path=/var/log/vault_audit.log 在上面的命令中,我们通过传递 “file_path” 参数来指定审计日志将写入的路径。每个审计设备都有自己的一组参数。 注意:审核设备配置默认复制到集群内的所有节点,并复制到 Vault 企业版集群的性能/灾难恢复辅助节点。在启用审计设备之前,请确保集群内的所有节点都能够成功登录到审计设备,以避免 Vault 节点无法处理请求。一个审计设备的数据可以被限制在具有 local 参数的集群节点内部。 当审计设备被禁用时,它将立即停止接收日志。它存储的现有日志不会受到影响。 阻塞的审计设备 如果启用了审计设备,Vault 在完成一个请求之前会要求至少一个审计设备成功地保存了日志。 如果只启用了一台审计设备,并且它处于阻塞状态(网络阻塞等),则 Vault 将无法响应请求。在审计设备变得可以写入之前,Vault 不会完成任何请求。 如果启用了多个审计设备,那么只要一台审计设备成功地保存了日志,Vault 就会完成请求。 如果所有审计设备被阻塞,Vault 将不会响应任何请求,因为审计日志非常重要,而忽略被阻塞的日志记录请求会打开攻击途径。使用者必须明白使用的审计设备不可被阻塞。 "},"8.审计设备/1.file.html":{"url":"8.审计设备/1.file.html","title":"File","keywords":"","body":"File 审计设备 file 审计设备将审计日志写入文件。这是一个非常简单的审计设备:它将日志添加到文件中。 该设备目前不进行任何日志文件轮换。已经有非常稳定且功能丰富的日志轮换工具,因此我们建议使用现有工具。 向 Vault 进程发送 SIGHUP 信号将导致文件审计设备关闭并重新打开它们的底层文件,这可以协助实现日志轮换。 例子 使用默认路径启用文件审计设备: $ vault audit enable file file_path=/var/log/vault_audit.log 使用不同路径启用。可以使用不同路径启用同一类型审计设备的多个副本: $ vault audit enable -path=\"vault_audit_1\" file file_path=/home/user/vault_audit.log 在 stdout 上启用文件审计设备。使用容器运行 Vault 时这很有用: $ vault audit enable file file_path=stdout 可用标志 请注意 audit enable 命令选项和 file 审计设备配置选项之间的区别。使用 vault audit enable -help 查看命令选项。以下是该审计设备可用的配置选项: file_path (string: ) - 写入审计日志的路径。如果给定路径中已存在文件,则审计日志将添加到该文件末尾。有一些特殊的关键字: stdout 将深究日志写入标准输出流 discard 写入输出(output)(测试场景下很有用) log_raw (bool: false) - 如果启用,安全敏感的信息将会不经散列,以原始格式直接记录 hmac_accessor (bool: true) - 如果启用,将令牌访问器(token accessor)散列 mode (string: \"0600\") - 一个包含八进制数的字符串,表示文件模式的位模式,类似于 chmod。设置为“0000”以防止 Vault 修改文件模式。 format (string: \"json\") - 允许选择输出格式。有效值为“json”和“jsonx”(它将普通日志条目格式化为 XML)。 prefix (string: \"\") - 在实际日志行之前配置一个可自定义字符串前缀。 日志文件轮换 要在运行于 BSD、Darwin 或 Linux 的 Vault 服务器上正确轮换 Vault 文件审计设备日志文件,需要配置日志轮换软件,在每次日志文件轮换后向 Vault 进程发送 SIGHUP 信号。 "},"8.审计设备/2.syslog.html":{"url":"8.审计设备/2.syslog.html","title":"Syslog","keywords":"","body":"Syslog 审计设备 syslog 审计设备将日志写入 syslog。 它目前不支持可配置的 syslog 目标,并且始终发送到本地代理。此设备仅在 Unix 系统上受支持,如果集群中有任何备用 Vault 实例不支持,则不应启用该设备。 警告:为某些操作生成的审计消息可能非常大,并且可能大于单个 UDP 数据包的最大尺寸。如果可能,请为 syslog 守护程序配置 TCP 侦听器。否则,请考虑使用 file 审计设备并配置 syslog 以从文件中读取条目;或者,同时启用 file 和 syslog,以便将特定消息直接记录到 syslog 发生失败时不会导致 Vault 被阻塞。 例子 使用以下命令启用 syslog 审计设备: $ vault audit enable syslog 使用 K=V 键值对设定配置参数: $ vault audit enable syslog tag=\"vault\" facility=\"AUTH\" 可用标志 facility (string: \"AUTH\") - 使用的 syslog facility。 tag (string: \"vault\") - 使用的 syslog tag。 log_raw (bool: false) - 如果启用,安全敏感的信息将会不经散列,以原始格式直接记录 hmac_accessor (bool: true) - 如果启用,将令牌访问器(token accessor)散列 mode (string: \"0600\") - 一个包含八进制数的字符串,表示文件模式的位模式,类似于 chmod format (string: \"json\") - 允许选择输出格式。有效值为“json”和“jsonx”(它将普通日志条目格式化为 XML) prefix (string: \"\") - 在实际日志行之前配置一个可自定义字符串前缀 "},"8.审计设备/3.socket.html":{"url":"8.审计设备/3.socket.html","title":"Socket","keywords":"","body":"Socket 审计设备 socket 审计设备将日志写入一个 TCP、UDP 或是 UNIX 套接字(Socket)。 警告:由于此设备中使用的底层协议的性质,有时与套接字的连接会丢失,日志中可能会省略单个审计条目,但请求仍将成功。将此设备与其他审计设备结合使用将有助于提高准确性,但如果审计日志需要强有力的保证,则不应使用套接字设备。 启用 在默认路径上启用: $ vault audit enable socket 使用 K=V 键值对设定配置参数: $ vault audit enable socket address=127.0.0.1:9090 socket_type=tcp 可用标志 address (string: \"\") - 使用的套接字服务器地址。例如 127.0.0.1:9090 或是 /tmp/aduit.sock socket_type (string: \"tcp\") - 使用的套接字类型,可以使用任何与 net.Dial 兼容的类型 log_raw (bool: false) - 如果启用,安全敏感的信息将会不经散列,以原始格式直接记录 hmac_accessor (bool: true) - 如果启用,将令牌访问器(token accessor)散列 mode (string: \"0600\") - 一个包含八进制数的字符串,表示文件模式的位模式,类似于 chmod format (string: \"json\") - 允许选择输出格式。有效值为“json”和“jsonx”(它将普通日志条目格式化为 XML) prefix (string: \"\") - 在实际日志行之前配置一个可自定义字符串前缀 "},"9.实际案例/overview.html":{"url":"9.实际案例/overview.html","title":"实际案例","keywords":"","body":"实际案例 我们将在本章中介绍一些 Vault 比较实用的用法作为参考。 "},"9.实际案例/1.加密即服务.html":{"url":"9.实际案例/1.加密即服务.html","title":"加密即服务","keywords":"","body":"加密即服务 我们有时会编写一些与敏感信息(PII,Personally Identifiable Information)打交道的服务,例如存储用户的姓名、联系方式、住址等隐私信息。为防止由于意外数据库泄漏,或是减少内鬼违规查阅、下载数据的可能性,将 PII 数据加密后存储到数据库是一个比较好的实践。 一般我们会使用对称加密算法来加密该类数据,一个对称加密算法的关键是加密密钥。拥有加密密钥的人可以解密对应的密文数据。如果我们在应用程序代码中调用加密库加解密信息,那么不可避免地就要与密钥打交道,密钥的管理、访问密钥的权限、密钥的轮替和紧急状态下吊销密钥这些工作就都落在应用程序开发者身上了。所以 Vault 提供了名为 transit 的机密引擎,对外提供了“加密即服务”功能(Encryption as a Service)。 之所以叫 transit 引擎,是因为 Vault 只保存密钥,不保存明文与密文数据,这与 Vault 其他的机密引擎是不同的。 简单把玩加解密 让我们来把玩一下该功能。首先启动 Vault 测试服务: $ vault server -dev ==> Vault server configuration: Api Address: http://127.0.0.1:8200 Cgo: disabled Cluster Address: https://127.0.0.1:8201 Go Version: go1.17.5 Listener 1: tcp (addr: \"127.0.0.1:8200\", cluster address: \"127.0.0.1:8201\", max_request_duration: \"1m30s\", max_request_size: \"33554432\", tls: \"disabled\") Log Level: info Mlock: supported: false, enabled: false Recovery Mode: false Storage: inmem Version: Vault v1.9.2 Version Sha: f4c6d873e2767c0d6853b5d9ffc77b0d297bfbdf+CHANGES ==> Vault server started! Log data will stream in below: ... WARNING! dev mode is enabled! In this mode, Vault runs entirely in-memory and starts unsealed with a single unseal key. The root token is already authenticated to the CLI, so you can immediately begin using Vault. You may need to set the following environment variable: $ export VAULT_ADDR='http://127.0.0.1:8200' The unseal key and root token are displayed below in case you want to seal/unseal the Vault or re-authenticate. Unseal Key: 9ZWOerPfTUx+8O22+nxANhpJMDYSSD+piObTgCZGkQQ= Root Token: s.XaRNCe4e3g5UA5z7g1gRDUaL Development mode should NOT be used in production installations! 然后用得到的 Root 令牌登录: $ export VAULT_ADDR='http://127.0.0.1:8200' $ vault login s.XaRNCe4e3g5UA5z7g1gRDUaL Success! You are now authenticated. The token information displayed below is already stored in the token helper. You do NOT need to run \"vault login\" again. Future Vault requests will automatically use this token. Key Value --- ----- token s.XaRNCe4e3g5UA5z7g1gRDUaL token_accessor kdF6LVdST93xnFDeQ3M5sN46 token_duration ∞ token_renewable false token_policies [\"root\"] identity_policies [] policies [\"root\"] 然后我们启用 transit 引擎: $ vault secrets enable transit Success! Enabled the transit secrets engine at: transit/ Vault 模拟了一个文件系统,各个机密引擎的启用是被挂载到相应文件路径下,默认挂载路径是引擎名称,所以我们启用的 transit 引擎被挂载到了默认的 transit/ 路径下。 下面,我们要创建一个密钥环,才能开始使用加密服务: $ vault write -f transit/keys/orders Success! Data written to: transit/keys/orders 我们成功地在 transit/keys/orders 这个路径下创建了一个密钥环,后续的加解密我们会通过这个路径来执行。 到目前为止我们都是使用 Root 用户进行的操作,下面我们要模拟一个普通的应用程序如何访问 Vault 来执行加解密操作。首先让我们为应用程序配置访问密钥环的权限: $ vault policy write app-orders - 我们新增了一个名为 app-oerders 的 Policy,赋予了对 transit 引擎下名为 orders 的密钥环加密与解密的操作权限。然后我们为应用程序创建一个 Vault Token: $ vault token create -policy=app-orders Key Value --- ----- token s.Nn1C0fK0XTF8TCQlfh1LFNDJ token_accessor SDgg8iUVAoZVNZ1aQ5mtLlrN token_duration 768h token_renewable true token_policies [\"app-orders\" \"default\"] identity_policies [] policies [\"app-orders\" \"default\"] 可以看到,这个新 Token 拥有两个 Policy:app-orders 与 default。default 是默认所有用户都拥有的策略。让我们使用新 Token 登录: $ vault login s.Nn1C0fK0XTF8TCQlfh1LFNDJ Success! You are now authenticated. The token information displayed below is already stored in the token helper. You do NOT need to run \"vault login\" again. Future Vault requests will automatically use this token. Key Value --- ----- token s.Nn1C0fK0XTF8TCQlfh1LFNDJ token_accessor SDgg8iUVAoZVNZ1aQ5mtLlrN token_duration 767h59m27s token_renewable true token_policies [\"app-orders\" \"default\"] identity_policies [] policies [\"app-orders\" \"default\"] 然后尝试加密一段数据: $ vault write transit/encrypt/orders \\ > plaintext=$(base64 可以看到,给出明文 4111 1111 1111 1111,我们得到的了密文 vault:v1:lnvn6fiOjBgpTUlYw1Oqx2uBT8dq2LfrAn2r/fFf+W8Hp12b5WFj/EDRstRBX5LO。在这里我们还可以看到,输出结果中 key_version 为 1,并且密文头部也有 vault:v1: 的前缀。这是因为 transit 引擎支持多版本密钥管理以及密钥轮替操作。 让我们试试解密数据: $ vault write transit/decrypt/orders \\ ciphertext=\"vault:v1:lnvn6fiOjBgpTUlYw1Oqx2uBT8dq2LfrAn2r/fFf+W8Hp12b5WFj/EDRstRBX5LO\" Key Value --- ----- plaintext NDExMSAxMTExIDExMTEgMTExMQo= $ base64 --decode 可以看到,解密后得到的结果,就是明文的 base64 编码形式。 密钥版本 transit 允许同时保存一个密钥环的多个版本的密钥。我们使用 Root 令牌登录,再查看一下当前密钥环的信息: $ vault login s.XaRNCe4e3g5UA5z7g1gRDUaL Success! You are now authenticated. The token information displayed below is already stored in the token helper. You do NOT need to run \"vault login\" again. Future Vault requests will automatically use this token. Key Value --- ----- token s.XaRNCe4e3g5UA5z7g1gRDUaL token_accessor kdF6LVdST93xnFDeQ3M5sN46 token_duration ∞ token_renewable false token_policies [\"root\"] identity_policies [] policies [\"root\"] $ vault read transit/keys/orders Key Value --- ----- allow_plaintext_backup false deletion_allowed false derived false exportable false keys map[1:1641036346] latest_version 1 min_available_version 0 min_decryption_version 1 min_encryption_version 0 name orders supports_decryption true supports_derivation true supports_encryption true supports_signing false type aes256-gcm96 可以看到,transit/keys/orders 密钥环采用的是 aes256-gcm96 算法,当前最新密钥版本是 1,保存的最久远的密钥版本 min_decryption_version 也是 1。 轮替密钥 让我们执行一次密钥轮替: $ vault write -f transit/keys/orders/rotate Success! Data written to: transit/keys/orders/rotate 这时再查看密钥环信息: $ vault read transit/keys/orders Key Value --- ----- allow_plaintext_backup false deletion_allowed false derived false exportable false keys map[1:1641036346 2:1641036686] latest_version 2 min_available_version 0 min_decryption_version 1 min_encryption_version 0 name orders supports_decryption true supports_derivation true supports_encryption true supports_signing false type aes256-gcm96 可以看到,最新密钥版本变成了 2,而 min_decryption_version 还是 1,这代表轮替后将默认使用版本号为 2 的密钥加密数据,但使用版本号为 1 的密钥加密的数据仍然可以正常解密。让我们验证一下现在我们是否还可以解密刚才的密文: $ vault write -format=json transit/decrypt/orders ciphertext=\"vault:v1:lnvn6fiOjBgpTUlYw1Oqx2uBT8dq2LfrAn2r/fFf+W8Hp12b5WFj/EDRstRBX5LO\" | jq -r .data.plaintext | base64 -d 4111 1111 1111 1111 使用版本 1 的密钥加密的密文目前仍然可以解密。让我们再次加密同一段明文试试看: $ vault write transit/encrypt/orders plaintext=$(base64 可以看到,同样的明文,现在得到的密文发生了变化: vault:v1:lnvn6fiOjBgpTUlYw1Oqx2uBT8dq2LfrAn2r/fFf+W8Hp12b5WFj/EDRstRBX5LO vault:v2:pLwQJs39PWFGHvnvX/5qrBoHBi7Ly5l4bC6ouYX9SYWgOMLlOxm+HTGhTcvpW3o8 新的密文是使用版本 2 的密钥加密的。目前这两个版本的密文都可以被正常解密: $ vault write -format=json transit/decrypt/orders ciphertext=\"vault:v2:pLwQJs39PWFGHvnvX/5qrBoHBi7Ly5l4bC6ouYX9SYWgOMLlOxm+HTGhTcvpW3o8\" | jq -r .data.plaintext | base64 -d 4111 1111 1111 1111 更新密文版本 如果我们数据库中目前存储的是 v1 版本的密文,在轮替密钥后,我们希望把旧版本密文全部更新成新版本,可以使用 Vault 的 rewrap 来完成: $ vault write transit/rewrap/orders ciphertext=\"vault:v1:lnvn6fiOjBgpTUlYw1Oqx2uBT8dq2LfrAn2r/fFf+W8Hp12b5WFj/EDRstRBX5LO\" Key Value --- ----- ciphertext vault:v2:o8XaRyDyyj2+ai46DVW2SYssGBMdvYxPPxrC7y+UaEWA1zAf+iaIfWaIEqtU3hL5 key_version 2 通过 rewrap,我们在不知晓明文的前提下,将密文的密钥版本从 v1 更新到了 v2。 假设我们在数据库中存放了大量密文数据,一种比较好的实践是定期用轮替生成新的密钥,并且编写定时任务,定时将密文全部更新到最新密钥版本,这样即使密文意外泄漏,存放在 Vault 中的密钥可能也已经被抛弃了。 抛弃旧密钥 在生产环境中我们定期轮替生成新密钥,但老密钥还是被保存在 Vault 中,长此以往会在 Vault 中留下大量的无用密钥(旧密文已被定期更新到新密钥版本)。我们可以设置密钥环的最低解密密钥版本: $ vault write transit/keys/orders/config min_decryption_version=2 Success! Data written to: transit/keys/orders/config $ vault read transit/keys/orders Key Value --- ----- allow_plaintext_backup false deletion_allowed false derived false exportable false keys map[2:1641036686] latest_version 2 min_available_version 0 min_decryption_version 2 min_encryption_version 0 name orders supports_decryption true supports_derivation true supports_encryption true supports_signing false type aes256-gcm96 如此,当前的 min_decryption_version 就被设置为 2。我们现在试试解密 v1 版本的密文: $ vault write -format=json transit/decrypt/orders ciphertext=\"vault:v1:lnvn6fiOjBgpTUlYw1Oqx2uBT8dq2LfrAn2r/fFf+W8Hp12b5WFj/EDRstRBX5LO\" Error writing data to transit/decrypt/orders: Error making API request. URL: PUT http://127.0.0.1:8200/v1/transit/decrypt/orders Code: 400. Errors: * ciphertext or signature version is disallowed by policy (too old) Vault 拒绝解密 v1 版本的密文,原因是 too old。v1 版本的密钥已经被 Vault 删除。需要注意的是,抛弃密钥前必须确保所有旧版本密文都已被更新到新密钥版本,否则抛弃旧密钥等同于删除旧版本密文。 生成加密密钥 在刚才的例子中,加解密操作以及密钥都位于 Vault 内部,这在处理大部分加解密场景时都已经足够;但是如果我们要加解密一个体积很大的数据,例如用户上传的视频,那么把整个视频数据传输到 Vault 服务内加密就显得不合时宜了。 我们可以用 Vault 生成一段随机的加密密钥,包含明文与密文;使用明文加密数据后,抛弃明文密钥,将密文密钥与密文数据存放在一起;解密时,先用 Vault 解密密文密钥得到明文密钥,就可以解密密文数据了。 $ vault write -f transit/datakey/plaintext/orders Key Value --- ----- ciphertext vault:v2:BD4fCUsL0xXfwWDbf+6+gwIGu3zdaFRpx0t1WQVrayiWqnSNHSCFNszKM3wxyec2e6wY2n+ABo+c2cYg key_version 2 plaintext vZZYeErrdmsWOl/3864lRVOTl40aQa5QRcC8o6Wgisc= 通过写入 transit/datakey/plaintext/orders 路径,我们得到了一个随机密钥,明文是 vZZYeErrdmsWOl/3864lRVOTl40aQa5QRcC8o6Wgisc=,密文是 vault:v2:BD4fCUsL0xXfwWDbf+6+gwIGu3zdaFRpx0t1WQVrayiWqnSNHSCFNszKM3wxyec2e6wY2n+ABo+c2cYg。 假设我们用 vZZYeErrdmsWOl/3864lRVOTl40aQa5QRcC8o6Wgisc= 这个密钥加密了一段视频数据,并且把 vault:v2:BD4fCUsL0xXfwWDbf+6+gwIGu3zdaFRpx0t1WQVrayiWqnSNHSCFNszKM3wxyec2e6wY2n+ABo+c2cYg 和密文视频数据存放在一起;需要解密时,我们先解密密文密钥: $ vault write -format=json transit/decrypt/orders ciphertext=\"vault:v2:BD4fCUsL0xXfwWDbf+6+gwIGu3zdaFRpx0t1WQVrayiWqnSNHSCFNszKM3wxyec2e6wY2n+ABo+c2cYg\" | jq -r .data.plaintext vZZYeErrdmsWOl/3864lRVOTl40aQa5QRcC8o6Wgisc= 成功地从 Vault 得到了密钥的明文以后,我们就可以进一步解密视频数据了。同样的,该密钥也收到 transit 引擎密钥环的版本管理,我们也可以定期轮替密钥、更新与视频密文一同存储的密文密钥。 "},"9.实际案例/2.使用vault提供可搜索的密文.html":{"url":"9.实际案例/2.使用vault提供可搜索的密文.html","title":"使用 Vault 提供可搜索的密文","keywords":"","body":"使用 Vault 提供可搜索的密文 近年来世界各国都开始密切关注信息安全,特别是数据安全、隐私安全问题,2021 年 06 月 10 日国家更是正式颁布了《中华人民共和国数据安全法》,从法律层面上规定了开展数据处理活动的主体必须对数据进行有效的安全保障,切实保护敏感隐私数据。 数据的保护有多种方法,例如现在云端和绝大多数商业数据库都提供的标配功能,数据的静态存储加密(Encryption at Rest): 数据在内存中以明文方式保存,但在落盘前会经过加密屏障处理,以密文形式写入磁盘等外部存储。这种加密机制可以有效对抗未经授权的第三方直接读取磁盘数据(例如盗取磁盘,或是未经授权对磁盘建立快照),但它存在其他的软肋,例如数据库账号被黑客盗取,黑客直接访问数据库;又或者企业通过ELT将数据同步到多个大数据分析平台,我们很难保证所有拥有数据的平台都能以最高规格保护数据不会外泄。 在静态存储加密的基础上我们还可以对敏感数据进行传输中加密(Encryption in Transit): 这里的传输中加密不止是利用 TLS、SSL 等对链路进行加密,我们还可以在应用程序接收到敏感的明文数据后,在应用内加密后再存入数据库,这样即使在数据库的内存中,数据仍然是加密的,而密钥是存储在数据库之外的安全存储内,这样即使黑客获取了数据库连接,或是拖库,得到的也是密文数据,只要黑客没有同时获得密钥,那么机密信息就仍然是安全的。 我们在前一节介绍了 Vault 的加密即服务,使用 Vault 来加解密数据,密钥由 Vault 保管,应用程序服务即使被入侵黑客也得不到密钥。而且借由 Vault 的权限控制能力,我们可以将加密与解密权限分开管理,某些面向 C 端用户的应用只能单向将数据加密,但无权调用解密;借助 Vault Transit 引擎的密钥版本控制以及密文 Re-Wrap 功能,我们可以实现定期强制执行的密钥轮替,进一步加固机密数据的安全保障。 对密文进行搜索 前不久被人问到一个这样的问题,启用 Vault 的 Transit 加密服务,把应用中的机密数据,例如用户姓名、手机号、信用卡号等信息加密存储到数据库之后,如果业务上要求要对这些列进行匹配搜索怎么办?因为数据库中的数据已经是密文了,所以直接用明文去匹配是不行的。 一个符合直觉的做法是,把提交的搜索条件也加密,用密文去匹配密文。可惜直接这样做会有一个问题:Vault Transit 默认的加密算法不是收敛(Convergent)的,也就是说,同一个密钥,同一段明文,在加密时 Vault 也会混入一个随机的 Nonce 值,导致生成的密文是不同的。我们来做个实验,启动一个 Vault 测试服务: $ vault server -dev ==> Vault server configuration: Api Address: http://127.0.0.1:8200 Cgo: disabled Cluster Address: https://127.0.0.1:8201 Go Version: go1.17.5 Listener 1: tcp (addr: \"127.0.0.1:8200\", cluster address: \"127.0.0.1:8201\", max_request_duration: \"1m30s\", max_request_size: \"33554432\", tls: \"disabled\") Log Level: info Mlock: supported: false, enabled: false Recovery Mode: false Storage: inmem Version: Vault v1.9.2 Version Sha: f4c6d873e2767c0d6853b5d9ffc77b0d297bfbdf+CHANGES ==> Vault server started! Log data will stream in below: ... WARNING! dev mode is enabled! In this mode, Vault runs entirely in-memory and starts unsealed with a single unseal key. The root token is already authenticated to the CLI, so you can immediately begin using Vault. You may need to set the following environment variable: $ export VAULT_ADDR='http://127.0.0.1:8200' The unseal key and root token are displayed below in case you want to seal/unseal the Vault or re-authenticate. Unseal Key: 03Gmq22/VnZPwpZDp8Voo82jQ+5e9qvChUVfUq0mTv8= Root Token: s.BeNIwE8yEJFVKmyay1CcbFPV Development mode should NOT be used in production installations! 然后用 Root 令牌登录,启用 Transit 引擎: $ export VAULT_ADDR='http://127.0.0.1:8200' && vault login s.BeNIwE8yEJFVKmyay1CcbFPV Success! You are now authenticated. The token information displayed below is already stored in the token helper. You do NOT need to run \"vault login\" again. Future Vault requests will automatically use this token. Key Value --- ----- token s.BeNIwE8yEJFVKmyay1CcbFPV token_accessor MhOj7Rb8oMnVwgjK64bQyG5Z token_duration ∞ token_renewable false token_policies [\"root\"] identity_policies [] policies [\"root\"] $ vault secrets enable transit Success! Enabled the transit secrets engine at: transit/ 生成一个新的密钥: $ vault write -f transit/keys/testkey Success! Data written to: transit/keys/testkey 然后我们尝试对同一段明文加密两次: $ vault write transit/encrypt/testkey plaintext=$(base64 可以看到,对同一段明文,生成的密文是不同的。这就使得使用密文匹配数据库中密文的想法无法成功。 Vault 的收敛加密 Vault 后来根据用户提出的需求,提供了收敛加密,基于收敛加密,我们可以对相同的明文生成相同的密文。我们利用 path-help 查看一下 Transit 引擎密钥的功能: $ vault path-help transit/keys/key Request: keys/key Matching Route: ^keys/(?P\\w(([\\w-.]+)?\\w)?)$ Managed named encryption keys ## PARAMETERS allow_plaintext_backup (bool) Enables taking a backup of the named key in plaintext format. Once set, this cannot be disabled. context (string) Base64 encoded context for key derivation. When reading a key with key derivation enabled, if the key type supports public keys, this will return the public key for the given context. convergent_encryption (bool) Whether to support convergent encryption. This is only supported when using a key with key derivation enabled and will require all requests to carry both a context and 96-bit (12-byte) nonce. The given nonce will be used in place of a randomly generated nonce. As a result, when the same context and nonce are supplied, the same ciphertext is generated. It is *very important* when using this mode that you ensure that all nonces are unique for a given context. Failing to do so will severely impact the ciphertext's security. derived (bool) Enables key derivation mode. This allows for per-transaction unique keys for encryption operations. exportable (bool) Enables keys to be exportable. This allows for all the valid keys in the key ring to be exported. name (string) Name of the key type (string) The type of key to create. Currently, \"aes128-gcm96\" (symmetric), \"aes256-gcm96\" (symmetric), \"ecdsa-p256\" (asymmetric), \"ecdsa-p384\" (asymmetric), \"ecdsa-p521\" (asymmetric), \"ed25519\" (asymmetric), \"rsa-2048\" (asymmetric), \"rsa-3072\" (asymmetric), \"rsa-4096\" (asymmetric) are supported. Defaults to \"aes256-gcm96\". ## DESCRIPTION This path is used to manage the named keys that are available. Doing a write with no value against a new named key will create it using a randomly generated key. 可以看到,创建密钥时我们可以指定 convergent_encryption、derived 参数,这两个参数可以让我们启用收敛加密。 $ vault path-help transit/encrypt/convergent_key Request: encrypt/convergent_key Matching Route: ^encrypt/(?P\\w(([\\w-.]+)?\\w)?)$ Encrypt a plaintext value or a batch of plaintext blocks using a named key ## PARAMETERS context (string) Base64 encoded context for key derivation. Required if key derivation is enabled convergent_encryption (bool) This parameter will only be used when a key is expected to be created. Whether to support convergent encryption. This is only supported when using a key with key derivation enabled and will require all requests to carry both a context and 96-bit (12-byte) nonce. The given nonce will be used in place of a randomly generated nonce. As a result, when the same context and nonce are supplied, the same ciphertext is generated. It is *very important* when using this mode that you ensure that all nonces are unique for a given context. Failing to do so will severely impact the ciphertext's security. key_version (int) The version of the key to use for encryption. Must be 0 (for latest) or a value greater than or equal to the min_encryption_version configured on the key. name (string) Name of the policy nonce (string) Base64 encoded nonce value. Must be provided if convergent encryption is enabled for this key and the key was generated with Vault 0.6.1. Not required for keys created in 0.6.2+. The value must be exactly 96 bits (12 bytes) long and the user must ensure that for any given context (and thus, any given encryption key) this nonce value is **never reused**. plaintext (string) Base64 encoded plaintext value to be encrypted type (string) This parameter is required when encryption key is expected to be created. When performing an upsert operation, the type of key to create. Currently, \"aes128-gcm96\" (symmetric) and \"aes256-gcm96\" (symmetric) are the only types supported. Defaults to \"aes256-gcm96\". ## DESCRIPTION This path uses the named key from the request path to encrypt a user provided plaintext or a batch of plaintext blocks. The plaintext must be base64 encoded. 可以看到,有一个名为 context 的参数,只要 context 的值相同,同样的明文就会生成同样的密文。 让我们首先创建一个用于收敛加密的密钥,记得要开启 convergent_encryption 和 derived(不要添加-前缀): $ vault write -f transit/keys/convergent_key convergent_encryption=true derived=true Success! Data written to: transit/keys/convergent_key 然后使用该密钥,搭配同一个 context 把同一段明文加密两次看看: $ vault write transit/encrypt/convergent_key plaintext=$(base64 可以看到,同样的 plaintext 和 context 产生的密文是一样的。我们试试不同的 context 值: $ vault write transit/encrypt/convergent_key plaintext=$(base64 可以看到,context 变化后密文也会发生变化。 那么我们是不是可以把机密数据用收敛加密算法加密后存进数据库,搜索时把搜索条件的值也用同样的 context 值加密去匹配呢?很遗憾,不行,这会遇到问题。 Re-Wrap 的问题 为了对抗已知明文的暴力破解攻击,每个密钥在使用数十亿次之后都应该进行更换。在生成了新版密钥后,我们应该逐步将所有旧密钥加密的密文,用新密钥重新加密存储。让我们对刚才创建的收敛密钥进行一次轮替操作: $ vault write -f transit/keys/convergent_key/rotate Success! Data written to: transit/keys/convergent_key/rotate $ vault read transit/keys/convergent_key Key Value --- ----- allow_plaintext_backup false convergent_encryption true convergent_encryption_version -1 deletion_allowed false derived true exportable false kdf hkdf_sha256 keys map[1:1641040308 2:1641040506] latest_version 2 min_available_version 0 min_decryption_version 1 min_encryption_version 0 name convergent_key supports_decryption true supports_derivation true supports_encryption true supports_signing false type aes256-gcm96 可以看到目前存在两个版本的密钥了。让我们重新用相同的 plaintext 和 context 值执行一次加密: $ vault write transit/encrypt/convergent_key plaintext=$(base64 第一个版本的密钥加密后的密文是 vault:v1:y3eR4qJugpNg9Aqt4/JMfthcen2IYHKyDlKBRKeahbVNJH5puKnAMw==,与当前版本密钥的密文不同。想象一下我们执行了密钥轮替,然后使用一个后台进程逐行读取数据库中的存量数据,将旧版密文替换成新版密文,那么在这个过程中必然会有一段时间同时存在新旧两个版本的密文,这时用户提交的搜索请求,究竟是用哪个版本的密钥加密搜索值就是一个大问题。 盲索引 我们除了收敛加密以外,也可以用其他方法来解决针对密文的匹配搜索问题,比如盲索引。盲索引简单来讲就是为明文生成一段哈希码,与密文一起存储;当我们需要对密文进行匹配搜索时,我们生成明文的哈希码,然后用哈希码去数据库匹配搜索,这样就可以在没有密钥的情况下,直接针对密文进行搜索了。 但是我们不能直接使用诸如 MD5、SHA-1 这样的哈希算法生成盲索引,因为一个成功拖库的攻击者可以针对密文进行猜测来匹配,例如手机号、姓名等,黑客可以轻松生成他感兴趣的数据的哈希码,然后快速在盲索引中匹配到记录是否存在。 所以盲索引的生成算法应该使用散列消息认证码(HMAC)算法。HMAC 是一种结合了哈希算法以及加密密钥的算法,生成明文的哈希码时必须同时提供一个密钥,不同的密钥会导致同一段明文生成的哈希码不同。 Transit引擎同样也提供了生成HMAC哈希码的功能: $ vault write transit/hmac/testkey/sha2-256 input=$(base64 针对明文,我们用 Vault 中保存的 testkey 密钥生成了HMAC哈希码,全程 testkey 密钥值都没有流出 Vault。我们换一个密钥试试: $ vault write transit/hmac/convergent_key/sha2-256 input=$(base64 不同的密钥生成的哈希码不同,黑客拖库后如果没有得到 Vault 中保存的密钥,他也无法猜测明文来生成哈希码,从而无法判定指定明文是否存在于数据库内。 用这种方法保存的密文,想要进行搜索,或是获得明文,都必须在有权操作Vault的环境下,这就直接杜绝了拖库后对利用数据的可能性。 "},"9.实际案例/3.vault_consul_secret_in_env_win.html":{"url":"9.实际案例/3.vault_consul_secret_in_env_win.html","title":"用Vault 与 Consul-Template 管理 Windows 环境变量中的机密","keywords":"","body":"用Vault 与 Consul-Template 管理 Windows 环境变量中的机密 最近参与的一个开源项目中配有大量的验收测试,在提交代码之前需要运行这些验收测试,把测试结果与代码一并提交给社区,而这些验收测试要求在系统环境变量中配置使用的 Azure 的凭据(Credential)信息。这鼓励了团队成员申请一个长生命周期的凭据,并手工硬编码配置在自己开发环境的环境变量中。实际上这并不是一个最佳实践,因为即使在团队成员休息、休假的时候,这些凭据仍然是有效的,仍然存在被有意无意地泄漏的风险。近年来机密信息被错误地推送到公开的 Github 仓库而被恶意利用的案例屡见不鲜。 Azure CLI 提供了另一种身份认证方式,即直接执行: $ az login 该命令会打开一个 Azure 登录页面,要求用户登录。登录成功后 Azure CLI 会获得一个临时的用户凭据,后续的 CLI 或是 Terraform 操作在默认情况下都会使用该凭据,不需要显式设置各种机密。 但是这种方法存在两个局限: 很难控制凭据的有效期,有时我们还在使用,但它过期了;有时我们已经下班回家了,但凭据其实还有效(这似乎并不是什么问题) 该认证方式依赖于人类交互,无法应用于自动化环境(例如 CI 流水线中) 有没有办法为自动化环境构建一种同样安全的认证方式,它应该能够: 使用短生命周期的凭据,使得凭据仅在我们使用开发环境的时候有效 适用于自动化环境,以环境变量或是凭据文件的形式存在 设置凭据的过程尽可能简单 在这里我们要搭配 HashiCorp 开发的另一个开源工具 Consul-Template 来构建一种这样的方案。 Consul-Template Consul-Template 仓库的说明中对该项目的描述如下: This project provides a convenient way to populate values from Consul into the file system using the consul-template daemon. The daemon consul-template queries a Consul or Vault cluster and updates any number of specified templates on the file system. As an added bonus, it can optionally run arbitrary commands when the update process completes. Please see the examples folder for some scenarios where this functionality might prove useful. 简而言之该项目可以把存储在 Consul 或是 Vault 中的信息“渲染”成一个本机的文件,并且在依赖的信息发生变化时可以重新渲染。这对于想要把一些依赖于配置文件的遗留系统也纳入云原生的架构来说有时会很有用,例如可以使用配置文件来做服务发现等。现在我们先按下不表。 实验环境 本次实验使用的是 Windows 10,我们需要安装好 Vault 和 Consul-Template 以及 Azure CLI。Vault 和 Azure CLI 建议使用 Chocolaty 进行安装,而 Consul-Template 则有些特殊,Chocolaty 中的版本太旧,而目前最新版本(v0.27.x)又有问题(具体的问题后面会叙述),所以推荐阅读 Consul-Template 仓库的说明文档中关于安装的章节,使用 v0.26.0 版本。 另外为了运行本次实验,需要配置一个高权限的 Azure 账号,要获取 client_id、client_secret、tenant_id、subscripton_id 这四个机密,具体步骤我们可以阅读 Terraform AzureRM Provider 的帮助文档,写的非常详细。需要说明的是,这组机密必须是一个长生命周期的机密,但它并不是我们日常使用的,而是配置在 Vault 里,通过该账号去操作我们日常使用的短生命周期凭据的。由于我们使用的这个账号的权限比较高,请妥善保存,确保该机密仅存在于 Vault 当中。 启动和配置 Vault Server 在 Windows 命令行中执行: >vault server -dev ==> Vault server configuration: Api Address: http://127.0.0.1:8200 Cgo: disabled Cluster Address: https://127.0.0.1:8201 Go Version: go1.16.7 Listener 1: tcp (addr: \"127.0.0.1:8200\", cluster address: \"127.0.0.1:8201\", max_request_duration: \"1m30s\", max_request_size: \"33554432\", tls: \"disabled\") Log Level: info Mlock: supported: false, enabled: false Recovery Mode: false Storage: inmem Version: Vault v1.8.4 Version Sha: 925bc650ad1d997e84fbb832f302a6bfe0105bbb ==> Vault server started! Log data will stream in below: ... WARNING! dev mode is enabled! In this mode, Vault runs entirely in-memory and starts unsealed with a single unseal key. The root token is already authenticated to the CLI, so you can immediately begin using Vault. You may need to set the following environment variable: PowerShell: $env:VAULT_ADDR=\"http://127.0.0.1:8200\" cmd.exe: set VAULT_ADDR=http://127.0.0.1:8200 The unseal key and root token are displayed below in case you want to seal/unseal the Vault or re-authenticate. Unseal Key: UlwuYJgN2VDDcejBRaY8Pqd+Pga4YugV2VXa2wLbXs8= Root Token: s.nbMzfLlBfAb5S4O2zb3DzeFJ Development mode should NOT be used in production installations! 然后我们启用 Azure Secret Engine,配置 Vault 使用的 Azure 账号凭据。请在一个新的命令行窗口中输入: >set VAULT_ADDR=http://127.0.0.1:8200 >vault login s.nbMzfLlBfAb5S4O2zb3DzeFJ Success! You are now authenticated. The token information displayed below is already stored in the token helper. You do NOT need to run \"vault login\" again. Future Vault requests will automatically use this token. Key Value --- ----- token s.nbMzfLlBfAb5S4O2zb3DzeFJ token_accessor g12y5M7RMuXNNSqrGzQCRayZ token_duration ∞ token_renewable false token_policies [\"root\"] identity_policies [] policies [\"root\"] >vault secrets enable azure Success! Enabled the azure secrets engine at: azure/ >vault write azure/config subscription_id= client_id= client_secret= tenant_id= Success! Data written to: azure/config 然后我们新建一个名为 azure.hcl 的文件,内容如下: [ { \"role_name\": \"Contributor\", \"scope\": \"/subscriptions//resourceGroups/vault-education\" } ] 这里需要指出的是,我们假设后续创建的短生命周期凭据的作用范围都是在名为 vault-education 的 ResourceGroup 下的。我们需要在 Azure 上创建这个 ResourceGroup。如果想要调整作用范围(例如使得短生命周期凭据作用于整个 Subscription 范围),那么请自行修改文件中的 scope。 随后我们向 Vault 提交该策略: >vault write azure/roles/edu-app ttl=2m azure_roles=@./azure.hcl Success! Data written to: azure/roles/edu-app 该策略将创建一个在 vault-education 这个 ResourceGroup 下有效的 Contributor 角色的账号,其有效期为2分钟。 至此 Vault Server 部分我们已经配置完成了。 配置 Azure Active Directory 权限 目前的 Vault Server 使用 Azure Active Directory Graph API 来操作 Azure 上的账号,所以需要配置相应 API 权限。但是由于 Azure 有意淘汰 Azure Active Directory Graph API,督促用户使用新的 Microsoft Graph API,所以 HashiCorp 官方的 Learn 教程中描述的通过 Portal 设置 API 权限的方式已不可行(界面上已经无法配置了),这里我们需要使用 Azure CLI 来进行配置。 首先通过 Azure CLI 登录: >az login The default web browser has been opened at https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize. Please continue the login in the web browser. If no web browser is available or if the web browser fails to open, use device code flow with `az login --use-device-code`. ... 然后用以下命令添加权限: >az ad app permission add --id --api 00000002-0000-0000-c000-000000000000 --api-permissions 1cda74f2-2616-4834-b122-5cb1b07f8a59=Role Invoking \"az ad app permission grant --id --api 00000002-0000-0000-c000-000000000000\" is needed to make the change effective >az ad app permission add --id --api 00000002-0000-0000-c000-000000000000 --api-permissions 78c8a3c8-a07e-4b9e-af1b-b5ccab50a175=Role Invoking \"az ad app permission grant --id --api 00000002-0000-0000-c000-000000000000\" is needed to make the change effective >az ad app permission grant --id --api 00000002-0000-0000-c000-000000000000 ... 这里面的 00000002-0000-0000-c000-000000000000 代表 Azure Active Directory Graph API,而 1cda74f2-2616-4834-b122-5cb1b07f8a59 和 78c8a3c8-a07e-4b9e-af1b-b5ccab50a175 代表着 Application.ReadWrite.All 和 Directory.ReadWrite.All 这两个我们需要的权限(可以在这篇博客中查到)。 配置好权限,让我们测试一下 Vault 是否可以正确地创建 Azure 账号: >vault read azure/creds/edu-app Key Value --- ----- lease_id azure/creds/edu-app/HqusX5LF7s1D87DiQpRnR1sv lease_duration 2m lease_renewable true client_id 055edb07-7b86-4569-b221-c07a43fa092e client_secret 5N9g4hAvw8pX4hDJ5c2zPpcw0Civcwnht9HT 我们也可以在 Azure Active Directory 的 App registrations 中确认: 需要特别指出的是,我敢在这里直接输出结果中的 client_id 和 client_secret,是因为它的有效期只有 2 分钟,并且我确认了它已经被 Vault 正确吊销了。请大家一定要确认不要泄漏自己的长生命周期凭据。 配置 Consul-Template 先确认一下当前版本: >consul-template -version v0.26.0 (3b7f233a) 请一定要注意不要使用最新的 v.0.27.0 或以上的版本。 创建一个 ct_config.hcl 文件,内容如下: vault { address = \"http://127.0.0.1:8200\" token = \"s.nbMzfLlBfAb5S4O2zb3DzeFJ\" renew_token = false } 配置文件中使用了 Vault 的 Root Token,这是因为我们在做实验。生产环境下这里的 Token 最好是使用 Token 文件,内容是每个用户自己登录自己 Vault 账号后得到的 Token。renew_token = false 也是因为我们使用的 Root Token 是不可续期的,所以显式设置为 false,生产环境使用时请根据自身情况进行配置。 再创建一个名为 config_env.ps1.tplt 的文件,内容如下: [Environment]::SetEnvironmentVariable(\"ARM_SUBSCRIPTION_ID\", \"\", 'Machine') [Environment]::SetEnvironmentVariable(\"ARM_TENANT_ID\", \"\", 'Machine') {{ with secret \"azure/creds/edu-app\" }} [Environment]::SetEnvironmentVariable(\"ARM_CLIENT_ID\", \"{{ .Data.client_id }}\", 'Machine') [Environment]::SetEnvironmentVariable(\"ARM_CLIENT_SECRET\", \"{{ .Data.client_secret }}\", 'Machine') {{ end }} while ($true) { Start-Sleep -Seconds 1 } 本质上这就是一个 PowerShell 脚本,调用 [Environment]::SetEnvironmentVariable 方法设置四个环境变量:ARM_SUBSCRIPTION_ID、ARM_TENANT_ID、ARM_CLIENT_ID 和 ARM_CLIENT_SECRET。唯一和普通 PowerShell 脚本不同的地方在于,设置 ARM_CLIENT_ID 和 ARM_CLIENT_SECRET 的部分使用了 Golang Template 语法,这就是 Consul-Template 支持的模板语法。这段模板的意思是读取 Vault 的 azure/creds/edu-app 这个路径下的机密(等同于刚才测试时通过 Valut CLI 执行的 read),然后把结果中的 client_id 和 client_secret 填充进脚本。 脚本的最后是一个永远运行的循环,只有接收到退出信号时脚本才会终止执行,这是 Consul-Template 要求的,我们将会以 Exec 模式运行 Consul-Template ,在它渲染该脚本文件后启动一个 PowerShell 进程来执行它,该进程如果结束运行会导致 Consul-Template 也结束运行,那这样的话就没有办法为我们自动执行续期了,所以我们要确保它永远执行(直到我们关闭它为止)。 让我们启动 Consul-Template: >consul-template -template \"./config_env.ps1.tplt:./config_env.ps1\" -config \"./ct_config.hcl\" -exec \"powershell.exe -noexit -file ./config_env.ps1\" Windows PowerShell 版权所有 (C) Microsoft Corporation。保留所有权利。 尝试新的跨平台 PowerShell https://aka.ms/pscore6 这段命令意思是,以 ct_config.hcl 文件为 Consul-Template 使用的配置,渲染 config_env.ps1.tplt 这个模板文件,把渲染的结果存放在 config_env.ps1 这个文件里,然后执行 powershell.exe -noexit -file ./config_env.ps1 命令,也就是执行我们刚刚渲染出来的 PowerShell 脚本。Consul-Template 从 v.0.27.0 开始不再支持 Windows 平台上的多行命令,所以如果你使用的是 v0.27.0 或更新的版本,你可能会遇到一个错误。我个人认为该问题只是暂时的,将来应该是会被解决的。 等我们成功看到 PowerShell 的输出后,我们可以查看一下 config_env.ps1 的内容: [Environment]::SetEnvironmentVariable(\"ARM_SUBSCRIPTION_ID\", \"\", 'Machine') [Environment]::SetEnvironmentVariable(\"ARM_TENANT_ID\", \"\", 'Machine') [Environment]::SetEnvironmentVariable(\"ARM_CLIENT_ID\", \"58484968-1d18-4a6c-8335-28c98e491dc3\", 'Machine') [Environment]::SetEnvironmentVariable(\"ARM_CLIENT_SECRET\", \"sSpY2gst2o8RO6L0VRfobBNHjWvGI8g4vxQz\", 'Machine') while ($true) { Start-Sleep -Seconds 1 } 同样的我们也可以通过 Portal 确认该账号的存在。让我们打开设置系统环境变量窗口来观察一下环境变量: 与刚才用 Vault CLI 创建的只有2分钟生命的账号不同,你会发现 Consul-Template 创建的账号在 2 分钟以后仍然活蹦乱跳的,这是因为 Consul-Template 会检查它创建出来的租约的有效期,会在有效期只剩一半时向 Vault 提交续期申请,也就是说,只要这个 Consul-Template 和它启动的 PowerShell 窗体持续运行,那么环境变量里配置的凭据就持续有效。如果我们停止进程、关闭主机或进入睡眠状态、或是简单地合上笔记本盖子,由于 Consul-Template 无法再执行续期操作,所以该账号就只剩 2 分钟的生命了(前提是 Vault Server 运行在另外独立的主机上,因为吊销必须由 Vault Server 来执行,所以关机等操作在本次实验中是不适用的)。 我们可以尝试用 Ctrl + C 关闭进程: >consul-template -template \"./config_env.ps1.tplt:./config_env.ps1\" -config \"./ct_config.hcl\" -exec \"powershell.exe -noexit -file ./config_env.ps1\" Windows PowerShell 版权所有 (C) Microsoft Corporation。保留所有权利。 尝试新的跨平台 PowerShell https://aka.ms/pscore6 Cleaning up... 过大约 2 分钟后,我们可以在 Vault Server 的输出中看到这样的信息: 2021-11-07T15:49:18.327+0800 [INFO] expiration: revoked lease: lease_id=azure/creds/edu-app/5Gx0PjUjtORAW16stpEhBCta 这就代表 Vault 吊销了这个过期的租约,这时我们在 Azure Active Directory 中会发现账号被正确删除了。 用这种方法我们就可以达到最初的三个目的: 使用短生命周期的凭据,使得凭据仅在我们使用开发环境的时候有效 适用于自动化环境,以环境变量或是凭据文件的形式存在 设置凭据的过程尽可能简单 另外如果是进入睡眠状态或是合上笔记本盖子后,在租约被吊销、账号被删除以后,如果我们重新打开系统,Consul-Template 恢复运行时会发现自己创建的租约已经被吊销了,它会重新向 Vault 申请一个租约,重新渲染 PowerShell 文件,然后重新设置环境变量,我们往往会意识不到环境变量其实已经变了,这时有可能还要执行 refreshenv 命令重新加载环境变量。 "},"9.实际案例/4.ssh_otp.html":{"url":"9.实际案例/4.ssh_otp.html","title":"配置使用一次性密码登录 SSH","keywords":"","body":"生成并使用 SSH OPT 密码 我们将在本节介绍使用 Vault 生成一次性的 Linux 登陆密码(One Time Password, OTP)的方案。 管理用户在生产服务器上的用户名、密码是一件很枯燥而又关键的事,Vault 提供了一种创建一次性登陆密码的方式,它的工作原理如图: 首先我们需要在被登陆的服务器上配置 vault-ssh-helper 程序,它可以取代 Linux 默认的登陆验证程序,在用户传递了登陆用户名密码后,转而向 Vault 服务器请求验证用户名密码的正确性。用户首先登陆 Vault,通过 Vault 创建一个属于目标服务器的 otp,随后远程连接目标服务器,给出这组 otp,在 vault-ssh-helper 验证通过后成功登陆,同时 Vault 服务器会在成功验证后删除这个 otp,确保密码的确是一次性的。 实验环境 实验环境为: 我个人的 Macbook,运行 Vault 以及 Consul 服务,IP 为 10.25.239.240。 一台 Ubuntu Server 20.04.2 LTS,IP 为 10.25.81.248。 安装配置 vault-ssh-helper 目前 vault-ssh-helper 官方支持 Ubuntu 操作系统,在 CentOS 上由于一些原因,官方的教程还无法直接工作。本文后续都是在 Ubuntu 上进行试验。 安装本身很简单,以 vault-ssh-helper 0.2.1 为例: # Download the vault-ssh-helper $ wget https://releases.hashicorp.com/vault-ssh-helper/0.2.1/vault-ssh-helper_0.2.1_linux_amd64.zip # Unzip the vault-ssh-helper in /user/local/bin $ sudo unzip -q vault-ssh-helper_0.2.1_linux_amd64.zip -d /usr/local/bin $ sudo chmod 0755 /usr/local/bin/vault-ssh-helper 下面是创建配置文件 /etc/vault-ssh-helper.d/config.hcl: vault_addr = \"\" ssh_mount_point = \"ssh\" tls_skip_verify = true allowed_roles = \"*\" 我们为了演示,所以没有为 Vault 配置 tls 和证书,所以我们要设置 tls_skip_verify 为 true,并且 vault_addr 要配置 http 而非 https 的地址。生产环境必须配置 tls 验证,使用 https 地址,因为 vault-ssh-helper 会将用户提供的 OTP 通过网络传递给 Vault,非加密链路存在被中间人拦截的可能性。 然后是修改 /etc/pam.d/sshd: # PAM configuration for the Secure Shell service # Standard Un*x authentication. #@include common-auth auth requisite pam_exec.so quiet expose_authtok log=/tmp/vaultssh.log /usr/local/bin/vault-ssh-helper -dev -config=/etc/vault-ssh-helper.d/config.hcl auth optional pam_unix.so not_set_pass use_first_pass nodelay ... 在这段配置里,我们把原有的 include common-auth 这一行注释了,common-auth 是 linux 提供标准的身份验证模块,我们必须将它注释,才能使用我们后面定义的 vault-ssh-helper 模块。 注意我们配置的模块 /usr/local/bin/vault-ssh-helper 后面跟了 -dev 参数。这是因为我们在前面的配置文件中将 tls_skip_verify 设置为 true,并使用了 http 而非 https 地址,只有在开启 -dev 模式后 vault-ssh-helper 才可以使用 http 地址。生产环境的配置中绝对不要开启 -dev 模式。 然后我们修改 /etc/ssh/sshd_config,确保其中的三项配置设置如下: ChallengeResponseAuthentication yes PasswordAuthentication no UsePAM yes 最后,我们重启一下 sshd 服务: $ sudo systemctl restart sshd 至此,在待登陆服务器上的配置已经完成,让我们继续配置 Vault 服务器。 配置Vault服务器 OTP SSH 需要 vault-ssh-helper 远程访问 Vault 进行密码的验证,所以我们不可以使用 -dev 模式启动 Vault 服务,因为 -dev 模式下的 Vault 仅接收来自 127.0.0.1 的访问。我们需要如上一篇文章介绍的那样,以常规模式启动 Vault 服务,使用 consul 作为存储,并且正确配置侦听的 IP,比如: storage \"consul\" { address = \"127.0.0.1:8500\" path = \"vault/\" } listener \"tcp\" { address = \"10.25.239.240:8200\" tls_disable = 1 } listener \"tcp\" { address = \"127.0.0.1:8200\" tls_disable = 1 } 在这里侦听的 ip 地址就是 Vault 服务器的地址 10.25.239.240,由于是演示,我们设置 tls_disable 为 1,生产环境中务必不要如此。 按照上一篇文章的步骤,我们成功启动 Vault,并且解封,用 Root Token 登陆后,首先启用 ssh 插件: $ vault secrets enable ssh Success! Enabled the ssh secrets engine at: ssh/ 然后我们写入一条角色,允许使用 otp 登陆 ssh: $ vault write ssh/roles/otp_key_role key_type=otp default_user=ubuntu cidr_list=0.0.0.0/0 Success! Data written to: ssh/roles/otp_key_role 这里我们指定了生成的 otp 的默认用户名是 ubuntu,生成 otp 时要确保对应的用户名在要登陆的服务器上已经存在,否则即使 Vault 验证通过,也是无法正常登陆的。这里的 cidr_list 可以限制试图登陆的来访 ip 范围,在这里我们姑且放开,任意来源都可以使用 otp 登陆。 随后我们生成一条 otp: $ vault write ssh/creds/otp_key_role ip=10.25.81.248 Key Value --- ----- lease_id ssh/creds/otp_key_role/Kxkvuy5IgRw0uKk3K8UzPMAH lease_duration 768h lease_renewable false ip 10.25.81.248 key f4d7dbf5-cef2-efc1-3adb-62ffdfc666e2 key_type otp port 22 username ubuntu 在这里 ip=10.25.81.248 中的这个 ip 就是我们想要登陆的服务器 ip(就是前文配置了 vault-ssh-helper 的服务器),返回的信息中标明 username 是 ubuntu,而形如 uuid 的 key 则是对应的 otp。让我们尝试登陆 10.25.81.248: $ ssh ubuntu@10.25.81.248 ubuntu@10.25.81.248's password: Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-96-generic x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage System information as of Sun 30 Jan 2022 12:55:01 PM UTC System load: 0.0 Usage of /: 14.4% of 30.88GB Memory usage: 13% Swap usage: 0% Processes: 133 Users logged in: 1 IPv4 address for enp0s5: 10.25.81.248 IPv6 address for enp0s5: fdb2:2c26:f4e4:0:21c:42ff:fe36:186f 97 updates can be installed immediately. 1 of these updates is a security update. To see these additional updates run: apt list --upgradable Last login: Sun Jan 30 10:38:42 2022 from 10.25.81.248 ubuntu@roger:~$ 登陆的用户名是 ubuntu,密码就是 f4d7dbf5-cef2-efc1-3adb-62ffdfc666e2,我们看到的确可以成功登陆。如果我们退出后重复用同一个密码登陆会怎么样? ssh ubuntu@10.25.81.248 Password: Password: 我们会发现,该密码再次使用就无法正确登陆了。如果我们生成一个新的 otp,再次登陆到 10.25.81.248,去看一下 vault-ssh-helper 的日志: *** Sun Jan 30 12:55:01 2022 2022/01/30 12:55:01 ==> WARNING: Dev mode is enabled! 2022/01/30 12:55:01 [INFO] using SSH mount point: ssh 2022/01/30 12:55:01 [INFO] using namespace: 2022/01/30 12:55:01 [INFO] ubuntu@10.25.81.248 authenticated! *** Sun Jan 30 12:55:08 2022 2022/01/30 12:55:08 ==> WARNING: Dev mode is enabled! 2022/01/30 12:55:08 [INFO] using SSH mount point: ssh 2022/01/30 12:55:08 [INFO] using namespace: 2022/01/30 12:55:08 [ERROR]: Error making API request. URL: PUT http://10.25.239.240:8200/v1/ssh/verify Code: 400. Errors: * OTP not found 我们会看到两条记录,第一条是我们第一次成功的 otp 登陆,同样的密码第二次登陆引发了一个 400 错误,回复找不到 OTP,这是因为第一次成功登陆后 Vault 服务器就删除了这条 otp。 自动化申请 otp 并登陆 我们目前使用 otp 登陆,需要先用 vault write 生成一条 otp,然后再用 ssh 登陆。Vault 也提供了更简单的登陆方法: $ vault ssh -role otp_key_role -mode otp -strict-host-key-checking=no ubuntu@10.25.81.248 Vault could not locate \"sshpass\". The OTP code for the session is displayed below. Enter this code in the SSH password prompt. If you install sshpass, Vault can automatically perform this step for you. OTP for the session is: 930d8bdc-4d73-c450-d57d-8b27e9a84d36 ubuntu@10.25.81.248's password: Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-96-generic x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage System information as of Sun 30 Jan 2022 01:08:04 PM UTC System load: 0.0 Usage of /: 14.4% of 30.88GB Memory usage: 13% Swap usage: 0% Processes: 125 Users logged in: 1 IPv4 address for enp0s5: 10.25.81.248 IPv6 address for enp0s5: fdb2:2c26:f4e4:0:21c:42ff:fe36:186f 97 updates can be installed immediately. 1 of these updates is a security update. To see these additional updates run: apt list --upgradable Last login: Sun Jan 30 12:55:02 2022 from 10.25.239.240 ubuntu@roger:~$ 由于我的 Mac 没有安装 sshpass ,所以 Vault 会帮助我申请到 OTP,然后要求我自己复制粘贴该 OTP 执行登录,但即使这样也极大地简化了使用 OTP 登录服务器的工作。如果客户端安装了 sshpass,则 Vault 会替我们填写 OTP,直接实现登录。我们可以将该命令封装为脚本进行进一步简化,在此不再赘述。 小结 通过配置 vault-ssh-helper 和 Vault 服务器的 ssh 插件,我们可以实现强制用户使用自己的 Vault 账号生成对应服务器的一次性密码来登陆生产服务器。由于密码仅能使用一次,所以也就大大降低了泄密和管理的风险。值得注意的是,由于 vault-ssh-helper 的开发团队目前只给出了 Ubuntu 下 pam 的配置,所以目前 ssh otp 我只在 Ubuntu 下测试成功,vault-ssh-helper在其他发行版上的运行还在摸索中。 "},"9.实际案例/5.routate_db_root_credential.html":{"url":"9.实际案例/5.routate_db_root_credential.html","title":"轮换数据库根凭证","keywords":"","body":"轮换数据库根凭证 Vault 的数据库机密引擎 代表数据库管理员来管理各种数据库凭据,所以我们必须在 Vault 中设置一个拥有很高权限的账号凭证,使得 Vaut 可以用它来签发或是吊销其他数据库凭证。因此我们一般会在 Vault 中配置数据库的“根账户”。 然而,这些配置在 Vault 中的长生命周期的凭据永远不会被修改,这将违反一些有关于数据库保存数据的合规要求(例如 Governance, Risk and Compliance, GRC)。 解决方案 使用 Vault 的 /database/rotate-root/:name API 端点来轮换存储的根凭据: 最佳实践:每次初始化好一个数据库机密引擎后,都使用该功能轮换该数据库的根凭据。 前置条件 一个已经初始化并解封的 Vault 服务器 一个 PostgreSQL 数据库(本实验使用 Docker 运行 Postgres,所以请确保已安装 Docker) 拥有如下 Vault 权限的 Vault 令牌: # Mount secrets engines path \"sys/mounts/*\" { capabilities = [ \"create\", \"read\", \"update\", \"delete\", \"list\" ] } # Configure the database secrets engine and create roles path \"database/*\" { capabilities = [ \"create\", \"read\", \"update\", \"delete\", \"list\" ] } 请注意,如果是进行实验或者学习,那么可以使用 Vault 的 root 令牌。然而我们不推荐在生产环境使用 root 令牌,而应该根据工作需要配置拥有合适权限的令牌。 步骤一:启用数据库机密引擎 用以下命令启用数据库引擎 $ vault secrets enable database 步骤二:启动 PostgreSQL 数据库 使用 Docker 启动一个 PostgreSQL 数据库实例: $ docker run --rm -p 5432:5432 -e POSTGRES_PASSWORD=mysecretpassword postgres 这时在 localhost:5432 可以访问到该数据库,默认用户名为 postgres,密码为 mysecretpassword,读者可以连接一下数据库进行确认。 步骤三:配置 PostgreSQL 机密引擎 使用一下命令配置 PostgreSQL 机密引擎: $ vault write database/config/postgresql \\ plugin_name=postgresql-database-plugin \\ connection_url=\"postgresql://{{username}}:{{password}}@localhost:5432/postgres?sslmode=disable\" \\ allowed_roles=\"*\" \\ username=\"postgres\" \\ password=\"mysecretpassword\" 要注意的是,由于本实验中没有为数据库启用 ssl,所以在 connection_url 连接字符串的末尾特意加上了 ?sslmode=disable。请勿在生产环境这样做。 步骤四:轮换根凭据 使用以下命令轮换根凭据: $ vault write -force database/rotate-root/postgresql Success! Data written to: database/rotate-root/postgresql 可以使用一下命令确认根凭据已被轮换: $ psql -h postgres.host.address -p 5432 -U root postgres Password for user root: 使用初始的用户名 postgres 以及密码 mysecretpassword 将无法连接到数据库,说明数据库根凭据已被轮换。 我们可以定期调用该命令来实现对根凭据的定期轮换。 警告:一旦轮换了根凭据,那么就只有 Vault 知道新的根账号密码了。这一点对该 Vault 机密引擎中配置的所有的数据库根凭据都是一样的。所以,应当配置一个 Vault 专用的根账号。 步骤五:验证配置 我们可以创建一个角色来验证数据库机密引擎可以正确动态生成一个凭据。 创建一个名为 readonly.sql 的文件,包含如下语句: $ tee readonly.sql 创建一个名为 readonly 的角色,TTL 设置为 1 小时: $ vault write database/roles/readonly db_name=postgresql \\ creation_statements=@readonly.sql \\ default_ttl=1h max_ttl=24h 现在,获取一个新的数据库凭据: $ vault read database/creds/readonly Key Value --- ----- lease_id database/creds/readonly/999c43f0-f79e-ba90-24a8-4de5af33a2e9 lease_duration 1h lease_renewable true password A1a-u7wxtrpx09xp40yq username v-root-readonly-x6q809467q98yp4yx4z4-1525378026e 验证我们可以使用该凭据连接到数据库: $ psql -h postgres.host.address -p 5432 \\ -U v-root-readonly-x6q809467q98yp4yx4z4-1525378026e postgres Password for user v-root-readonly-x6q809467q98yp4yx4z4-1525378026: postgres=> \\du Role name | Attributes | Member of ------------------------------------------------+------------------------------------------------------------+---------- postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} v-root-readonly-x6q809467q98yp4yx4z4-1525378026 | Password valid until 2018-05-03 21:07:11+00 | {} v-root-readonly-x7v65y1xuprzxv9vpt80-1525378873 | Password valid until 2018-05-03 21:21:18+00 | {} postgres=> \\q 以上步骤可以确认 Vault 成功地连接到了 PostgreSQL 数据库并依据 readonly.sql 文件中的语句创建了一个拥有相应权限的新用户。 "},"9.实际案例/6.postgres_dynamic_secrets.html":{"url":"9.实际案例/6.postgres_dynamic_secrets.html","title":"生成动态数据库凭证","keywords":"","body":"生成动态数据库凭证 数据保护是重中之重,数据库凭证轮换是任何数据保护计划的关键部分。每个角色都有一组不同的权限来访问数据库。为抵御黑客攻击,自动化的连续凭证轮换是非常必要的。 解决方案 应用程序向 Vault 请求数据库凭据,而不是将它们设置为环境变量。管理员为数据库凭据设置 TTL 以限制其有效期,以便在不再使用时自动撤销它们。 每个应用程序实例都可以获得他们独享的凭据。通过使这些凭据的生命周期限制在一个较短的时间内,我们可以减少它们可能被泄露的机会。如果应用程序遭到入侵,可以撤销应用程序使用的凭据,而不是更改更多的全局凭据集合。 参与实验的角色 admin:拥有 Vault 以及数据库管理员特权的管理员 app:从 Vault 读取数据库动态凭据的应用程序 前置条件 一个已经初始化并解封的 Vault 服务器 一个 PostgreSQL 数据库(本实验使用 Docker 运行 Postgres,所以请确保已安装 Docker) admin 需要拥有如下 Vault 权限的 Vault 令牌: # Mount secrets engines path \"sys/mounts/*\" { capabilities = [ \"create\", \"read\", \"update\", \"delete\", \"list\" ] } # Configure the database secrets engine and create roles path \"database/*\" { capabilities = [ \"create\", \"read\", \"update\", \"delete\", \"list\" ] } # Manage the leases path \"sys/leases/+/database/creds/readonly/*\" { capabilities = [ \"create\", \"read\", \"update\", \"delete\", \"list\", \"sudo\" ] } path \"sys/leases/+/database/creds/readonly\" { capabilities = [ \"create\", \"read\", \"update\", \"delete\", \"list\", \"sudo\" ] } # Write ACL policies path \"sys/policies/acl/*\" { capabilities = [ \"create\", \"read\", \"update\", \"delete\", \"list\" ] } # Manage tokens for verification path \"auth/token/create\" { capabilities = [ \"create\", \"read\", \"update\", \"delete\", \"list\", \"sudo\" ] } app 需要拥有如下 Vault 权限的 Vault 令牌: # Get credentials from the database secrets engine 'readonly' role. path \"database/creds/readonly\" { capabilities = [ \"read\" ] } 启动 Postgres 我们使用 Docker 启动一个用户名为 root,密码为 rootpassword 的 Postgres 数据库实例: $ docker run \\ --name learn-postgres \\ -e POSTGRES_USER=root \\ -e POSTGRES_PASSWORD=rootpassword \\ -p 5432:5432 \\ --rm \\ postgres 确认数据库容器正常运行: $ docker ps -f name=learn-postgres --format \"table {{.Names}}\\t{{.Status}}\" NAMES STATUS learn-postgres Up 5 seconds 在确认容器正常运行后,创建相关数据库角色。该数据库角色会在后续的设置中被 Vault 用来签发数据库动态凭据。 创建一个名为 ro 的数据库角色: $ docker exec -i \\ learn-postgres \\ psql -U root -c \"CREATE ROLE \\\"ro\\\" NOINHERIT;\" CREATE ROLE 赋予角色 ro 读取所有表的权限: $ docker exec -i \\ learn-postgres \\ psql -U root -c \"GRANT SELECT ON ALL TABLES IN SCHEMA public TO \\\"ro\\\";\" GRANT 至此,该角色已经搭配所需的权限被正确创建完了。 启动 Vault 我们在新的命令行终端中启动一个 -dev 模式的 Vault 服务,使用 root 作为根令牌: $ vault server -dev -dev-root-token-id root 该 Vault 实例目前运行于 127.0.0.1:8200 上,服务已被初始化并解封。 注意,我们在这里是为了教学目的所以使用了 -dev 模式。请不要在生产环境这样做。 通过环境变量设置命令行使用的 Vault 服务地址: $ export VAULT_ADDR=http://127.0.0.1:8200 通过环境变量设置命令行使用的 Vault 令牌: $ export VAULT_TOKEN=root 注意,我们在这里是为了教学目的所以使用了 root 令牌。请不要在生产环境这样做,而是使用配置了合适权限的令牌。 至此,Vault 服务已经正常运行。 启用数据库机密引擎 (操作者:admin) 通过命令行在 database/ 路径上启用数据库机密引擎: $ vault secrets enable database 配置 PostgreSQL 机密引擎 (操作者:admin) 通过命令行配置使用的 Postgres 连接凭据: $ vault write database/config/postgresql \\ plugin_name=postgresql-database-plugin \\ connection_url=\"postgresql://{{username}}:{{password}}@localhost:5432/postgres?sslmode=disable\" \\ allowed_roles=readonly \\ username=\"root\" \\ password=\"rootpassword\" 要注意的是,由于本实验中没有为数据库启用 ssl,所以在 connection_url 连接字符串的末尾特意加上了 ?sslmode=disable。请勿在生产环境这样做。 创建角色 (操作者:admin) 在前面的步骤中,我们配置 Postgres 机密引擎允许名为 readonly 的角色调用。Vault 中的角色是一个映射到数据库凭据的逻辑名称。这些附加到 Vault 角色的凭据以 SQL 语句的形式表达。 定义用来创建凭据的 SQL 语句: $ tee readonly.sql SQL 语句包含了模板化的字段 {{name}}、{{password}} 以及 {{expiration}}。这些字段会在 Vault 创建凭据时被填充。这将创建一个新角色,并将先前在 Postgres 中创建的名为 ro 的角色拥有的权限赋予这个新角色。 我们用以下命令来创建使用 readonly.sql 文件的内容创建凭据的名为 readonly 的角色: $ vault write database/roles/readonly \\ db_name=postgresql \\ creation_statements=@readonly.sql \\ default_ttl=1h \\ max_ttl=24h 该角色创建出来的数据库凭据拥有默认的长度为 1 小时的 TTL 以及长度为 24 小时的最大 TTL。 请求 Postgres 凭据 (操作者:app) 应用程序通过以下命令,从 readonly 数据库角色读取数据库凭据: $ vault read database/creds/readonly Key Value --- ----- lease_id database/creds/readonly/fyF5xDomnKeCHNZNQgStwBKD lease_duration 1h lease_renewable true password A1a-ckirtymYaXACpIHn username v-token-readonly-6iRIcGv8tLpu816oblPY-1556567086 Postgres 凭据通过 username 和 password 字段展示出来。凭据在 Vault 中的租约就是 lease_id。 验证 连接到 Postgres 数据库,列出所有数据库用户: $ docker exec -i \\ learn-postgres \\ psql -U root -c \"SELECT usename, valuntil FROM pg_user;\" 输出展示了所有生成的数据库凭据的表。之前 app 生成的凭据也在其中: usename | valuntil --------------------------------------------------+------------------------ root | v-token-readonly-ExP3fop3xpzCoZkzdiT7-1635943853 | 2021-11-04 12:50:58+00 (2 rows) 管理租约 (操作者:admin) Vault 创建的所有凭据都关联了对应的租约 ID,在 TTL 到期前或是被吊销前凭据有效。一旦租约被吊销,则凭据也将不再有效。 列出存在的租约: $ vault list sys/leases/lookup/database/creds/readonly Keys ---- IQKUMCTg3M5QTRZ0abmLKjTX 输出列出了所有有效的数据库凭据的租约。 将第一个租约 ID 保存在一个环境变量中: $ LEASE_ID=$(vault list -format=json sys/leases/lookup/database/creds/readonly | jq -r \".[0]\") 传递租约 ID 续约租约及相应的数据库凭据: $ vault lease renew database/creds/readonly/$LEASE_ID Key Value --- ----- lease_id database/creds/readonly/IQKUMCTg3M5QTRZ0abmLKjTX lease_duration 1h lease_renewable true 续约后的租约的 TTL 为 1h。 在租约过期前吊销租约: $ vault lease revoke database/creds/readonly/$LEASE_ID All revocation operations queued successfully! 尝试再列出存在的租约: $ vault list sys/leases/lookup/database/creds/readonly No value found at sys/leases/lookup/database/creds/readonly/ 无效的租约不会被显示出来。重新读取一个新的数据库凭据: $ vault read database/creds/readonly Key Value --- ----- lease_id database/creds/readonly/P6tTTiWsR1fVCp0btLktU0Dm lease_duration 1m lease_renewable true password A1a-pfgGk7Ptb0TxGBJI username v-token-readonly-9blxDY3dIKXsFMkv8kvH-1600278284 下面我们吊销一个路径上所有的租约。让我们吊销所有前缀为 database/creds/readonly 的租约: $ vault lease revoke -prefix database/creds/readonly prefix 标志匹配了所有路径前缀为 database/creds/readonly 的有效租约。 尝试列出存在的租约: $ vault list sys/leases/lookup/database/creds/readonly No value found at sys/leases/lookup/database/creds/readonly/ 所有该路径下的租约都已经被吊销了。 定义密码策略 要执行以下步骤需要 Vault 1.6 或是更新的版本。 数据库机密引擎生成的密码遵循默认模式,可以用新密码策略覆盖。策略定义了密码必须遵守的规则和要求,并且可以直接通过新端点或在机密引擎中生成该密码。 假如我们要生成的密码必须要符合以下要求: 至少 20 位字符长度 至少一个大写字母 至少一个小写字母 至少一个数字 至少一个符号 通过名为 example_policy.hcl 的文件定义一个密码策略: $ tee example_policy.hcl 策略使用 HCL 编写。length 字段设置了密码长度为 20 位字符。每个 rule 配置节定义一个字符集以及这些字符需要出现在生成的密码中的最少出现次数。这些规则是累积的,因此每个规则都对生成的密码增加了更多要求。 使用 example_policy.hcl 创建名为 example 的密码策略: $ vault write sys/policies/password/example policy=@example_policy.hcl Success! Data written to sys/polices/password/example_policy 现在可以直接访问此策略以生成密码或在配置支持的机密引擎时通过它的名字 example 引用该策略。 使用 example 密码策略生成一个密码: $ vault read sys/policies/password/example/generate Key Value --- ----- password #v!RQDHxHunJ1TUmCyys 生成的密码符合上面的规定。 应用密码策略 配置数据库机密引擎,使用 example 密码策略: $ vault write database/config/postgresql \\ password_policy=\"example\" 与数据库建立连接的其他信息没有变化,唯一的变化是 password_policy 被设置为使用 example 策略。 使用 readonly 数据库角色读取凭据: $ vault read database/creds/readonly Key Value --- ----- lease_id database/creds/readonly/ZxoKlbklsliYA4hZs7umoPIz lease_duration 1h lease_renewable true password 9MSegMz7N1Fr69ZTyb#D username v-token-readonly-wGLPkpDyc6AgqBfMZTD3-1604195404 定义用户名模板 要执行以下步骤需要 Vault 1.7 或是更新的版本。 数据库机密引擎生成符合默认模式的用户名。为满足组织和团队的合规要求,我们可以使用自定义的用户名模板。 请确保自定义用户名模板包含足够的随机性,以防止多次生成相同的用户名。 使用 readonly 数据库角色读取凭据: $ vault read database/creds/readonly Key Value --- ----- lease_id database/creds/readonly/ZxoKlbklsliYA4hZs7umoPIz lease_duration 1h lease_renewable true password 9MSegMz7N1Fr69ZTyb#D username v-token-readonly-wGLPkpDyc6AgqBfMZTD3-1604195404 生成的用户名,v-token-readonly-wGLPkpDyc6AgqBfMZTD3-1604195404,使用的默认模式可以用如下 Go 模板表达:{{ printf \"v-%s-%s-%s-%s\" (.DisplayName | truncate 8) (.RoleName | truncate 8) (random 20) (unix_time) | truncate 63 }}。 使用用户名模板配置数据库机密引擎: $ vault write database/config/postgresql \\ username_template=\"myorg-{{.RoleName}}-{{unix_time}}-{{random 8}}\" 用户名以 myorg- 为前缀,使用角色名 readonly,然后是以秒计的 unix 时间戳,最后是一个 8 位字符长的随机序列。 从 readonly 数据库角色读取凭据: $ vault read database/creds/readonly Key Value --- ----- lease_id database/creds/readonly/NOCGtSbz7g4FFjcztX6Bqh3S lease_duration 1h lease_renewable true password -h3B-JteYjgOPYIC6dGQ username myorg-readonly-1616447348-af9eHMWD 生成的用户名满足我们先前通过模板定义的规则。 "},"9.实际案例/7.production-hardening.html":{"url":"9.实际案例/7.production-hardening.html","title":"生产环境的安全加固","keywords":"","body":"生产环境的安全加固 本节将讨论在生产环境部署 Vault 的最佳实践。 这些关于最佳实践的建议被分为两个部分:在任何生产环境中部署 Vault 时都必须尽全力遵守的基线建议,以及提供了额外的安全性,却由于需要额外的管理开销而可能不适用于某些环境的扩展建议。 基线建议 不要用 root 账号运行:使用专用的非特权服务帐户运行 Vault,而不是使用 root 或管理员帐户运行。 Vault 旨在由非特权用户运行,这样做可以显著防御各种提权攻击。 仅允许最小化的写特权:非特权 Vault 服务帐户不应拥有可覆盖其可执行二进制文件或任何 Vault 配置文件的权限。Vault 用户只能写入本地 Vault 存储(例如,用于集成存储后端)或审计日志的目录和文件。 端到端的 TLS:Vault 在生产环境中应始终启用 TLS。如果在 Vault 前方还部署了中间负载均衡器或是反向代理,则应将 TLS 用于系统的每个组件(包括存储后端)之间的所有网络连接,以确保在进出 Vault 的传输过程中对所有流量进行加密。如果可能,应使用 Vault 的自定义响应标头功能设置 HTTP 严格传输安全 (HSTS) 标头。 禁用虚拟内存:Vault 的数据在传输时或是落盘时都是加密的,但是在内存中仍然保有敏感数据的明文。应通过禁用虚拟内存来最小化数据泄露风险,以防止操作系统将敏感数据换出到磁盘。这在使用集成存储后端时尤其重要。 禁用核心转储(Core Dump):可以强制执行核心转储并有权访问转储文件的用户或管理员可能可以访问到 Vault 加密密钥。禁用核心转储是一个特定于平台的过程;在 Linux 平台上可以通过将资源的 RLIMIT_CORE 限制设置为 0 来禁用核心转储。如果使用的是 systemd 服务,可以通过设置 LimitCORE=0 来强制 Vault 服务禁用核心转储。 单租户:Vault 应该是机器上运行的唯一主进程。这降低了在同一台机器上运行的另一个进程被破坏并通过它与 Vault 交互的风险。同样,在裸机上运行应该优于在 VM 上运行,在 VM 中运行应该优于在容器中运行。 使用防火墙控制流量:使用云服务商的本地防火墙或网络安全功能来限制 Vault 和 NTP 等基本系统服务的传入和传出流量。这包括限制传入流量到允许的子网和传出流量到 Vault 需要连接到的服务,例如数据库。 避免使用根令牌:Vault 在首次初始化时会返回一个根令牌。此令牌应用于最初设置系统,特别是设置身份验证方法以便用户可以进行身份验证。我们建议使用代码来编排 Vault 配置,并对 Vault 策略进行版本化控制。设置后,应撤销根令牌以消除暴露风险。根令牌可以在需要时生成,并应尽快撤销。 启用审计日志:Vault 支持数种审计设备。启用审计日志记录可提供 Vault 执行的所有操作的历史记录,并在误用或泄露的情况下提供取证跟踪。审计日志中敏感数据会被安全地哈希,但仍应限制访问日志文件以防止任何意外泄露。 禁用 Shell 命令历史记录:我们最好能够将 vault 命令排除在 Shell 命令历史记录之外以防止命令中的机密信息泄漏。在 bash 中我们可以通过设置环境变量来实现该目标: $ export HISTIGNORE=\"&:vault*\" 频繁升级:Vault 开发非常活跃,并且频繁更新对于合并安全修复和默认设置(例如密钥长度或密码套件)中的任何更改非常重要。请订阅 HashiCorp 公告邮件列表来获取新版本发布的通告,并访问 Vault 变更记录 以获取每次版本更新的变更详情。 同步时钟:使用 NTP 或任何适合运行环境的机制来确保所有 Vault 节点有一致的时间。 Vault 将时钟用于执行 TTL 和在 PKI 证书中设置日期等事情,如果节点有明显的时钟偏差,故障转移可能会引发严重破坏。 限制对存储的访问:无论使用的是哪种存储后端,Vault 都会加密所有静态数据。尽管数据是加密的,但具有最高控制权的攻击者可以通过修改或删除密钥来导致数据损坏或丢失。对存储后端的访问应仅限于 Vault,以避免未经授权的访问或操作。 不要用明文存储凭据:Vault 配置文件中的 seal 配置节可以用来配置使用的封印类型以提供额外的数据保护,例如使用 HSM 或是云端 KMS 方案来加解密主密钥。不要用明文形式在 seal 配置节中存储云的凭据或是 HSM 的 pin 码。如果 Vault 服务器与 KMS 服务托管在同一云平台上,请使用特定于平台的身份解决方案: 阿里云上的 Resource Access Management (RAM) AWS 上的实例配置文件 Azure 上的 Managed Service Identities (MSI) 如果以上都不可用,可以将凭据设置在环境变量中(例如 VAULT_HSM_PIN)。 尽可能使用最安全的算法:Vault 的 TLS 侦听器支持多种遗留算法以实现向后兼容性。虽然这些算法可用,但不建议使用它们,尤其是在有更强大的替代方案可用时。如果可能,使用 TLS 1.3 可确保使用现代加密算法来加密传输中的数据和前向保密。 遵循插件的最佳实践:虽然 HashiCorp 开发的插件通常默认为安全配置,但应该注意是否有配置错误或恶意的 Vault 插件。这些可能会损害 Vault 部署的安全状况。 非确定性的文件合并:Vault 的配置文件合并是不确定的,文件之间的设置不一致可能会导致 Vault 设置不一致。确保所有文件(以及由 -config 表示的任何合并在一起的文件)的设置配置一致。 使用正确的文件系统权限:始终确保在启动 Vault 之前对文件应用适当的权限,尤其是那些包含敏感信息的文件。 扩展建议 禁用 SSH/远程桌面:将 Vault 作为单租户应用程序运行时,用户不应直接访问计算机。相反,他们应该通过网络上的 API 访问 Vault。使用集中式日志记录和遥测解决方案进行调试。请务必根据需要限制对日志的访问。 开启 systemd 安全功能:Systemd 提供了许多可用于锁定对文件系统和管理功能的访问的功能。官方 Vault Linux 软件包提供的服务单元文件默认设置了其中的一些,包括: ProtectSystem=full PrivateTmp=yes CapabilityBoundingSet=CAP_SYSLOG CAP_IPC_LOCK AmbientCapabilities=CAP_IPC_LOCK ProtectHome=read-only PrivateDevices=yes NoNewPrivileges=yes 请阅读 systemd.exec 手册获取更多详细信息。 采用不可变风格的更新方式:Vault 依赖于外部存储后端来实现持久性,这种解耦允许使用不可变的风格来管理运行 Vault 的服务器。升级到新版本时,带有升级版 Vault 的新服务器将联机。它们连接到同一个共享存储后端并且解封。然后旧服务器被销毁。这减少了对可能引入安全漏洞的远程访问和升级编排的需求。 配置使用 SELinux / AppArmor:在使用 Vault 时,使用 SELinux 和 AppArmor 等额外机制有助于提供额外的安全层。虽然 Vault 可以在许多操作系统上运行,但由于此处提到的各种安全原语,我们建议使用 Linux。 调整 ulimits:我们使用的 Linux 发行版可能对进程的 ulimits 有严格的约束。在部署到生产环境之前,应考虑审查 ulimits 规定的打开文件数、连接数等,该限制数可能需要增大。 Docker 容器:为了在 Vault 容器内部使用“内存锁定”功能,我们可能需要使用类似 overlayfs2 或是其他支持该功能的驱动。 "},"9.实际案例/8.entities_groups.html":{"url":"9.实际案例/8.entities_groups.html","title":"身份:实体与组","keywords":"","body":"身份:实体与组 Vault 支持多种身份验证方法,还允许在不同的挂载路径上启用相同类型的身份验证方法。每个 Vault 客户端可能有多个帐户,这些帐户具有在 Vault 服务器上启用的各种身份提供者。 Vault 客户端可以映射为实体,并且它们与身份验证提供程序的对应帐户可以映射为别名。本质上,每个实体都由零个或多个别名组成。Vault 内部使用身份机密引擎维护 Vault 识别的客户端。 举例来说,Bob 在 Github 和 LDAP 中都有帐户。 Github 和 LDAP 身份验证方法都在 Vault 服务器上启用,他可以使用他的任一帐户进行身份验证。虽然两个账户都属于 Bob,但是两个账户之间并没有关联来设置一些共同的属性。 解决方案 创建一个代表 Bob 的实体,并将代表他的每个帐户的别名关联为实体成员。我们可以在实体级别设置其他策略和元数据,以便两个帐户都可以继承。 当 Bob 使用他的任一帐户进行身份验证时,实体标识符将与经过身份验证的令牌相关联。当使用此类令牌时,它们的实体标识符会被审计记录,标记特定用户执行的操作的踪迹。 准备工作 要执行本教程的实验,我们需要: 一个已经初始化并解封的 Vault 服务器 安装 jq 启动一个 dev 服务 在命令行终端中,使用 root 作为根令牌启动一个 Vault dev 服务: $ vault server -dev -dev-root-token-id root Vault dev 服务默认运行于 127.0.0.1:8200。该服务启动后已被初始化并解封。 开启一个新的终端,使用环境变量设置 Vault 命令行使用的 Vaul 服务地址: $ export VAULT_ADDR=http://127.0.0.1:8200 使用环境变量设置 Vault 命令行使用的 Vaul 根令牌: $ export VAULT_TOKEN=root 步骤一:创建一个带别名的实体 我们将创建一个分配了基本策略的新实体。该实体定义了两个实体别名,每个别名都分配了不同的策略。 场景:一个 ACME Inc. 的用户 Bob Smith 碰巧有两组凭据:bob 和 bsmith。 Bob 可以使用他的任一帐户向 Vault 进行身份验证。要管理用户的帐户并将其链接到 QA 团队中的身份 Bob Smith,您将为 Bob 创建一个实体。 请注意,为简化本教程,我们将使用 userpass 身份验证方法。在实际场景中,用户 bob 可能是一个存在于活动目录中的用户名,而 bsmith 可能是 Bob 在 GitHub 的用户名。为了模拟该行为,本教程中将在两个独立路径上启用 userpass 身份验证方法:userpass-test 以及 userpass-qa,来模拟两种不同的身份验证方法。 场景策略 base.hcl: path \"secret/data/training_*\" { capabilities = [\"create\", \"read\"] } test.hcl: path \"secret/data/test\" { capabilities = [ \"create\", \"read\", \"update\", \"delete\" ] } team-qa.hcl: path \"secret/data/team-qa\" { capabilities = [ \"create\", \"read\", \"update\", \"delete\" ] } 请注意该场景假设已在 secret 路径上启用了 K/V v2 机密引擎。如果不了解 K/V 机密引擎,请先阅读相关文档。 现在,让我们使用合适的权限来创建 bob 和 bsmith 用户: 创建 base 策略: $ vault policy write base - 创建 test 策略: $ vault policy write test - 创建 team-qa 策略: $ vault policy write team-qa - 列出所有策略,确认 base、test 以及 team-qa 策略已就绪: $ vault policy list base default team-qa test root 在 userpass-test 路径上启用 userpass 身份验证方法: $ vault auth enable -path=\"userpass-test\" userpass 在 userpass-test 上创建一个名为 bob 的新用户,密码为 training,附加 test 策略: $ vault write auth/userpass-test/users/bob password=\"training\" policies=\"test\" 在另一个路径 userpass-qa 上启用 userpass 身份验证方法: $ vault auth enable -path=\"userpass-qa\" userpass 在 userpass-qa 上创建一个名为 bsmith 的新用户,密码为 training,附加 team-qa 策略: $ vault write auth/userpass-qa/users/bsmith password=\"training\" policies=\"team-qa\" 执行以下命令获取 userpass 身份验证方法挂载点的访问器: $ vault auth list -detailed Path Type Accessor ... ---- ---- -------- ... token/ token auth_token_c5943123 ... userpass-qa/ userpass auth_userpass_8c7b8e0f ... userpass-test/ userpass auth_userpass_264d4705 ... ... 每一个 userpass 身份验证方法都有一个唯一的 Accessor 值作为标志符。 运行以下命令将 userpass-test 身份验证方法的访问器保存在名为 access_test.txt 的文件里: $ vault auth list -format=json | jq -r '.[\"userpass-test/\"].accessor' > accessor_test.txt 类似的,运行以下命令将 userpass-qa 身份验证方法的访问器保存在名为 access_qa.txt 的文件里: $ vault auth list -format=json | jq -r '.[\"userpass-qa/\"].accessor' > accessor_qa.txt 创建名为 bob-smith 的实体,将返回的实体 ID 保存在名为 entity_id.txt 的文件内: $ vault write -format=json identity/entity name=\"bob-smith\" policies=\"base\" \\ metadata=organization=\"ACME Inc.\" \\ metadata=team=\"QA\" \\ | jq -r \".data.id\" > entity_id.txt 现在,通过创建一个实体别名来将用户 bob 添加到实体 bob-smith 中。在名为 bob 的实体的别名上设置自定义元数据“account”,并将其值设置为“Tester Account”: $ vault write identity/entity-alias name=\"bob\" \\ canonical_id=$(cat entity_id.txt) \\ mount_accessor=$(cat accessor_test.txt) \\ custom_metadata=account=\"Tester Account\" 请注意:要在实体别名上设置自定义元数据,需要运行 Vault 1.9 或更高版本。如果我们的 Vault 版本低于 v1.9,那么执行上述命令时请不要添加 custom_metadata=account=\"Tester Account\"。 输出样例: Key Value --- ----- canonical_id 24204b50-22a6-61f5-bd4b-803f1a4e4726 id ae2cdd0f-9807-7336-2265-5575c71837e7 重复以上步骤,通过创建一个实体别名来将用户 bsmith 添加到实体 bob-smith 中。在名为 bob 的实体的别名上设置自定义元数据“account”,并将其值设置为“QA Eng Account”: $ vault write identity/entity-alias name=\"bsmith\" \\ canonical_id=$(cat entity_id.txt) \\ mount_accessor=$(cat accessor_qa.txt) \\ custom_metadata=account=\"QA Eng Account\" 输出样例: Key Value --- ----- canonical_id 24204b50-22a6-61f5-bd4b-803f1a4e4726 id 6066f6af-bb1c-5310-58a1-fd9c8f151573 审查实体明细: $ vault read -format=json identity/entity/id/$(cat entity_id.txt) | jq -r \".data\" 样例输出:输出应包括实体别名、元数据(组织和团队)和基本策略: { \"aliases\": [ { \"canonical_id\": \"73503625-abcd-db22-08c3-c121d682d550\", \"creation_time\": \"2021-11-17T05:33:48.040506Z\", \"custom_metadata\": { \"account\": \"Tester Account\" }, \"id\": \"cf073e2e-41af-852f-848d-f67533c8a610\", \"last_update_time\": \"2021-11-17T05:33:48.040506Z\", \"local\": false, \"merged_from_canonical_ids\": null, \"metadata\": null, \"mount_accessor\": \"auth_userpass_0a6936a7\", \"mount_path\": \"auth/userpass-test/\", \"mount_type\": \"userpass\", \"name\": \"bob\" }, { \"canonical_id\": \"73503625-abcd-db22-08c3-c121d682d550\", \"creation_time\": \"2021-11-17T05:33:48.107834Z\", \"custom_metadata\": { \"account\": \"QA Eng Account\" }, \"id\": \"7add6763-ce53-d92a-c795-c8ae529ce6e7\", \"last_update_time\": \"2021-11-17T05:33:48.107834Z\", \"local\": false, \"merged_from_canonical_ids\": null, \"metadata\": null, \"mount_accessor\": \"auth_userpass_ef4f8068\", \"mount_path\": \"auth/userpass-qa/\", \"mount_type\": \"userpass\", \"name\": \"bsmith\" } ], \"creation_time\": \"2021-11-17T05:33:47.966585Z\", \"direct_group_ids\": [ \"49e8b9e8-8933-1e14-f05c-e1c9674b142b\" ], \"disabled\": false, \"group_ids\": [ \"49e8b9e8-8933-1e14-f05c-e1c9674b142b\" ], \"id\": \"73503625-abcd-db22-08c3-c121d682d550\", \"inherited_group_ids\": [], \"last_update_time\": \"2021-11-17T05:33:47.966585Z\", \"merged_entity_ids\": null, \"metadata\": { \"organization\": \"ACME Inc.\", \"team\": \"QA\" }, \"mfa_secrets\": {}, \"name\": \"bob-smith\", \"namespace_id\": \"root\", \"policies\": [ \"base\" ] } 安全注意事项:避免在实体元数据中存储任何敏感的个人身份信息 (PII)。如果配置了性能复制,实体元数据将复制到其他集群。这可能严重违反 GDPR 规定。 Vault 1.9 引入了在不与 Vault 设置的元数据重叠的每个实体别名上设置自定义元数据的功能。如果身份验证方法是集群本地的,则元数据不会被复制到同一性能复制组中的其他集群。因此,建议改用实体别名上的自定义元数据。 步骤二:测试实体 为了更好地理解令牌如何从实体的策略继承功能,您将通过以 bob 身份登录来测试它。 首先以 bob 的身份登录,并将生成的客户端令牌保存在 bob_token.txt 文件中: $ vault login -format=json -method=userpass -path=userpass-test \\ username=bob password=training \\ | jq -r \".auth.client_token\" > bob_token.txt test 策略授予了对 secret/test 路径的 CRUD 操作权限。测试确认我们可以向该路径写入机密: $ VAULT_TOKEN=$(cat bob_token.txt) vault kv put secret/test owner=\"bob\" Key Value --- ----- created_time 2021-11-06T02:12:02.104146Z custom_metadata deletion_time n/a destroyed false version 1 虽然用户名 bob 没有附加 base 策略,但它的令牌集成了 base 策略,因为 bob 是 bob-smith 实体的成员之一,而该实体附加了 base 策略。检查 bob 的令牌是否集成了这些权限: $ VAULT_TOKEN=$(cat bob_token.txt) vault token capabilities secret/data/training_test create, read base 策略授予了对 secret/training_* 路径的创建与读取权限;所以 bob 也可以对任意以 secret/training_* 为前缀的路径执行创建以及读取操作。 那么 secret/team-qa 路径呢? $ VAULT_TOKEN=$(cat bob_token.txt) vault token capabilities secret/data/team-qa deny 用户 bob 只集成了关联实体的策略。用户只可以通过以 bsmith 的凭据登录才能访问 secret/team-qa 路径。 步骤三:创建一个内部组 请注意,请在执行步骤三的操作前,先使用先前配置实体所使用的令牌重新登录。 现在,我们要创建一个名为 engineers 的内部组,它的成员是我们在步骤一中创建的实体 bob-smith: 名为 team-eng.hcl 的文件中定义了名为 team-eng 的组策略: team-eng.hcl: path \"secret/data/team/eng\" { capabilities = [ \"create\", \"read\", \"update\", \"delete\"] } 注意:在继续下面的操作前,请确认环境变量 VAULT_TOKEN 被设置为了 root。 创建名为 team-eng 的新策略: $ vault policy write team-eng - 创建名为 engineers 的内部组,将 bob-smith 实体添加为组成员,并附加 team-eng 策略: $ vault write identity/group name=\"engineers\" \\ policies=\"team-eng\" \\ member_entity_ids=$(cat entity_id.txt) \\ metadata=team=\"Engineering\" \\ metadata=region=\"North America\" 输出样例: Key Value --- ----- id 0cd76d41-fe36-8c7b-4758-d36bbc212650 name engineers 现在,当我们用 bob 或是 bsmith 登录时,生成的令牌都继承了组级别的策略 team-eng。 总结 默认情况下,Vault 创建内部组。当我们创建一个内部组时,我们可以设置组成员而不是组别名。组别名时 Vault 与外部身份提供者(例如 LDAP、GitHub 等)之间的映射。因此,只有在创建外部组时才定义组别名。对于内部组,我们指定 member_entity_ids 和/或 member_group_ids。 步骤四:创建一个外部组 组织通常使用 LDAP、Okta 和 GitHub 等身份验证方法来处理 Vault 用户身份验证,并且在这些身份提供程序中定义个人用户的组成员身份。 为了管理组级别的授权,我们可以创建一个外部组以将 Vault 与外部身份提供者(身份验证提供者)链接起来,并将适当的策略附加到该组。 样例场景 所有属于 Github 组织 example-inc 中 training 团队的用户都被允许在 secret/education 路径上执行任意操作。 请注意,该场景假设 GitHub 上已经存在了名为 example-inc 的组织以及名为 training 的团队。 education.hcl: path \"secret/data/education\" { capabilities = [ \"create\", \"read\", \"update\", \"delete\", \"list\" ] } 创建名为 education 的新策略: $ vault policy write education - 启用 GitHub 身份验证方法: $ vault auth enable github 获取 GitHub 身份验证方法的挂载点访问器,并保存在名为 accessor_github.txt 的文件内: $ vault auth list -format=json | jq -r '.[\"github/\"].accessor' > accessor_github.txt 配置指向我们的 GitHub 组织: $ vault write auth/github/config organization=example-inc 创建一个外部组,将组 ID 保存至文件 group_id.txt 中: $ vault write -format=json identity/group name=\"education\" \\ policies=\"education\" \\ type=\"external\" \\ metadata=organization=\"Product Education\" | jq -r \".data.id\" > group_id.txt 创建一个组别名,其中 canonical_id 是组 ID,名称必须是存在的实际 GitHub 团队名称。: $ vault write identity/group-alias name=\"training\" \\ mount_accessor=$(cat accessor_github.txt) \\ canonical_id=\"$(cat group_id.txt)\" 样例输出: Key Value --- ----- canonical_id 66818a45-ef85-0ff3-6c1e-37faf12ea55e id 578944f0-dcfd-29fd-a763-d3f9431512d7 "},"9.实际案例/9.Cubbyhole.html":{"url":"9.实际案例/9.Cubbyhole.html","title":"Cubbyhole","keywords":"","body":"Cubbyhole 我们之前介绍了 Vault 的响应封装。要正确使用响应封装,我们需要使用一种名为 Cubbyhole 的特殊存储引擎。 Cubbyhole 是美语,意为一个舒适的小房间,或是一个储物柜。每一个 Token 都会拥有一个独立的 Cubbyhole 存储空间,彼此相互隔离;每当一个 Token 被吊销或是过期时,Vault 会清空相应的 Cubbyhole 存储。 Cubbyhole 对外也是提供了一个 key-value 格式的存储形式,但与 Key-Value 机密引擎不同的是,任何 Token 无权访问其他 Token 的 Cubbyhole(即使是 Root 令牌),而 Key-Value 机密引擎提供的则是一个全局的 Key-Value 存储,凡是配置了相应路径读写 Policy 的用户都可以读写同一个路径下的机密数据。 创建实验环境 我们假想的场景是,管理员为自动化工具创建一个对应的 Vault 策略,然后创建一个拥有该策略权限的 Token,并且利用 Cubbyhole Response Wrap 机制,安全地将 Token 分发给自动化工具,自动化工具拿到封装着 Token 的信息后,通过 Vault 解封得自己要使用的 Token,并利用该 Token 完成与 Vault 的交互。 那为什么不直接传递 Token ,而是传递 Token 的封装呢?这是因为一个 Token 封装只能被解封一次,重复申请解封会被 Vault 拒绝。也就是说,假设传递给工具的 Token 封装被攻击者截获了,如果工具率先解封得到了 Token,那么攻击者随后试图解封将会失败;反过来如果攻击者率先成功解封得到了 Token,那么会造成工具解封失败,无法得到 Token,而这种类型的错误足以拉响运维系统的警报,督促管理员在第一时间吊销泄漏的 Token,并开始追查泄漏途径。 我们还是使用 Vault 的测试服务器: $ vault server -dev ==> Vault server configuration: Api Address: http://127.0.0.1:8200 Cgo: disabled Cluster Address: https://127.0.0.1:8201 Go Version: go1.17.5 Listener 1: tcp (addr: \"127.0.0.1:8200\", cluster address: \"127.0.0.1:8201\", max_request_duration: \"1m30s\", max_request_size: \"33554432\", tls: \"disabled\") Log Level: info Mlock: supported: false, enabled: false Recovery Mode: false Storage: inmem Version: Vault v1.9.2 Version Sha: f4c6d873e2767c0d6853b5d9ffc77b0d297bfbdf+CHANGES ==> Vault server started! Log data will stream in below: ... WARNING! dev mode is enabled! In this mode, Vault runs entirely in-memory and starts unsealed with a single unseal key. The root token is already authenticated to the CLI, so you can immediately begin using Vault. You may need to set the following environment variable: $ export VAULT_ADDR='http://127.0.0.1:8200' The unseal key and root token are displayed below in case you want to seal/unseal the Vault or re-authenticate. Unseal Key: uKMxO//ttcdRqQDQuesylq2vAmquIwo9SBbdf9IreI8= Root Token: s.iydnufld9coAyr1CMcAQsvyl Development mode should NOT be used in production installations! 使用 Root 令牌登录: $ export VAULT_ADDR='http://127.0.0.1:8200' $ vault login s.iydnufld9coAyr1CMcAQsvyl Success! You are now authenticated. The token information displayed below is already stored in the token helper. You do NOT need to run \"vault login\" again. Future Vault requests will automatically use this token. Key Value --- ----- token s.iydnufld9coAyr1CMcAQsvyl token_accessor dFDhtueTVZ4L3jU4TnGf8jsl token_duration ∞ token_renewable false token_policies [\"root\"] identity_policies [] policies [\"root\"] 首先,我们使用 Root 用户添加一个新策略,允许应用读取 kv 引擎的特定路径: $ vault policy write apps - 拥有 apps 策略的用户,可以读取 secret/data/dev 下的数据。 这里要特别说明一下,一般情况下,包括 secret 在内的机密引擎在一个新的 Vault 集群上,都不是默认启用的,需要操作员显式启用,而我们使用的 -dev 模式的测试服务则会为我们自动启用 secret 路径;另外,secret 路径默认挂载的是 Vault 的 K-V Version 2 机密引擎,该引擎我们会在后续的文章中单独介绍,现在我们只需要了解到,我们可以在 secret/dev 路径下读写 Key-Value 格式的数据,例如,利用 Root 令牌在 secret/dev 路径下写入两条 Key-Value 数据: $ vault kv put secret/dev username=\"webapp\" password=\"my-long-password\" Key Value --- ----- created_time 2021-12-28T03:16:05.350108Z custom_metadata deletion_time n/a destroyed false version 1 下面,我们要创建一个包含 apps 策略的 Token,并利用 Cubbyhole Response Wrap 机制将其封装,传递给工具: $ vault token create -policy=apps -wrap-ttl=120 Key Value --- ----- wrapping_token: s.7zHZJEXCbN4mbgq0ODudaEzm wrapping_accessor: uzc6SiSLXZM8zh3mQskPeuQk wrapping_token_ttl: 2m wrapping_token_creation_time: 2021-12-28 11:26:41.152364 +0800 CST wrapping_token_creation_path: auth/token/create wrapped_accessor: YkhPxMxs73MXZVkqIQfUgqcD 我们在创建给工具使用的 Token 时,指定了 -wrap-ttl 参数,值为 120,Vault 创建了一个包含 apps 策略的 Token,但没有直接返回给我们,而是又创建了另一个 Token:s.rsqdLYcPWzPCRxKFGbwmrFKt,这个 Token 被称为\"Wrapping Token\";我们为工具创建的 Token 是被存储在这个 Wrapping Token 的 Cubbyhole 中的,而这个 Wrapping Token 的有效期为 120 秒。 下面,我们假设我们位于运行着工具的环境中。假设我们得到了 Wrapping Token,为了能够顺利解封,我们首先要一个最低权限的 Token: $ vault token create -policy=default Key Value --- ----- token s.AXjSluO2ui19eB769fc1Lv5b token_accessor fQ1NxDuGFrXEduMNnt8IBwvz token_duration 768h token_renewable true token_policies [\"default\"] identity_policies [] policies [\"default\"] 该 Token 只拥有 default 策略,默认情况下没有任何权限,但足以让我们进行后续的交互。我们先用该 Token 登录,然后解封 Wrapping Token: $ vault login s.AXjSluO2ui19eB769fc1Lv5b Success! You are now authenticated. The token information displayed below is already stored in the token helper. You do NOT need to run \"vault login\" again. Future Vault requests will automatically use this token. Key Value --- ----- token s.AXjSluO2ui19eB769fc1Lv5b token_accessor fQ1NxDuGFrXEduMNnt8IBwvz token_duration 767h59m50s token_renewable true token_policies [\"default\"] identity_policies [] policies [\"default\"] $ $ VAULT_TOKEN=s.7zHZJEXCbN4mbgq0ODudaEzm vault unwrap Key Value --- ----- token s.fK43de9uwneJXz0jYPWwcoya token_accessor YkhPxMxs73MXZVkqIQfUgqcD token_duration 768h token_renewable true token_policies [\"apps\" \"default\"] identity_policies [] policies [\"apps\" \"default\"] 我们首先使用含有 default 策略的 Token 执行了登录,然后将 VAULT_TOKEN 环境变量设置为 Wrapping Token 的值,并执行 vault unwrap 操作,结果得到了值为 s.fK43de9uwneJXz0jYPWwcoya 的 Token,这就是刚才 Root 用户为工具创建的,包含 apps策略的 Token。我们试试看该 Token 是否能够顺利读取 secret/dev 下的数据: $ VAULT_TOKEN=s.fK43de9uwneJXz0jYPWwcoya vault kv get secret/dev ======= Metadata ======= Key Value --- ----- created_time 2021-12-28T03:16:05.350108Z custom_metadata deletion_time n/a destroyed false version 1 ====== Data ====== Key Value --- ----- password my-long-password username webapp 该 Token 如我们预期一般可用。 我们试试如果再执行一次解封操作会发生什么: $ VAULT_TOKEN=s.7zHZJEXCbN4mbgq0ODudaEzm vault unwrap Error unwrapping: Error making API request. URL: PUT http://localhost:8200/v1/sys/wrapping/unwrap Code: 400. Errors: * wrapping token is not valid or does not exist Vault 拒绝了解封,给出的原因是 Wrapping Token 已经不存在了,这是因为 Cubbyhole Response Wrap 机制只允许我们解封一次。如果我们在正常执行流程中看到这样的错误,就说明传递的 Wrapping Token 已经泄漏了,同时为工具创建的 Token 也已经泄漏了,这时该怎么办?让我们重新看一下创建的 Wrapping Token: $ vault token create -policy=apps -wrap-ttl=120 Key Value --- ----- wrapping_token: s.7zHZJEXCbN4mbgq0ODudaEzm wrapping_accessor: uzc6SiSLXZM8zh3mQskPeuQk wrapping_token_ttl: 2m wrapping_token_creation_time: 2021-12-28 11:26:41.152364 +0800 CST wrapping_token_creation_path: auth/token/create wrapped_accessor: YkhPxMxs73MXZVkqIQfUgqcD 返回结果中除了 wrapping_token 外,还有一个 wrapped_accessor,值为 YkhPxMxs73MXZVkqIQfUgqcD,这个 wrapped_accessor 可以理解为一个指向被封装的 Token 的指针或者引用。我们可以通过该引用吊销这个 Token: $ VAULT_TOKEN=s.iydnufld9coAyr1CMcAQsvyl vault token revoke -accessor YkhPxMxs73MXZVkqIQfUgqcD Success! Revoked token (if it existed) 这样一来,原先创建的包含 apps 策略的 Token 就被我们吊销了,攻击者即使抢先解封得到了该 Token,也无法继续使用了: $ VAULT_TOKEN=s.fK43de9uwneJXz0jYPWwcoya vault kv get secret/dev Error making API request. URL: GET http://localhost:8200/v1/sys/internal/ui/mounts/secret/dev Code: 403. Errors: * permission denied "},"10.后记/1.关于翻译.html":{"url":"10.后记/1.关于翻译.html","title":"后记 1 —— 翻译本书的心路历程","keywords":"","body":"后记 1 —— 翻译本书的心路历程 说实话本书的翻译工作量之大、之高完全是出乎我最初的意料的,原以为 7 天假期肝一把基本可以完成主体工作,没有想到真的开始翻译了才越来越发现,这次开了一个很深的坑。 这次的翻译工作量已经超过了之前的《Terrform 入门教程》的两倍,这是因为与 Terraform 相比,Vault 有以下明显的不同: Vault 是一个要部署在生产环境的服务,所以需要翻译大量的与配置、部署有关的内容,才能使读者有一个基本的、安全的在生产环境可用的 Vaut 服务。我们当然可以只挑选部分有关使用的内容翻译而减少、忽略配置、运维的内容,但这样的内容对帮助读者建立起一个可以实际使用的 Vault 服务没有太大帮助 Vault 涉及到大量的三方系统,不像 Terraform 其自身就有大量高价值的内容可以学习,Vault 中高价值的内容几乎大多数都是围绕着各种第三方系统展开的,试想一下如果我们的内容中没有如何与 AWS/Azure/阿里云 或是 MySQL/PostgreSQL 等成熟的三方系统的集成,那么这本书对读者还有多少的价值?这就迫使笔者需要花费大量时间去学习相关三方系统的各种知识,虽是倾尽全力却也只能是浮光掠影地了解一番,并且很难保证没有错漏 Vault 需要考虑到大量日常的运维以及安全防护问题,Terraform 的入门教程可以将注意力集中在如何创建相关资源上,而 Vault 则还要兼顾到如何确保 Vault 服务和机密在长期的使用中不泄漏、不丢失 虽说是咬牙坚持翻了下来,但在翻译的过程中如履薄冰,越来越感到心虚和胆战心惊,自身知识雷达中的盲区一个接一个地暴露了出来,让我愈发认清了自己能力的界限和不足。 再加上整个 HashiCorp 产品线在近些年大额融资,特别是 IPO 之后火力全开,更新的频率日新月异,以笔者一人之力想要覆盖到这庞大的知识网络实在是如同蚍蜉撼树不自量力了,所以笔者只能中途砍掉了许多原本雄心勃勃想要翻译的内容。 为什么要翻译 事实上翻译这本书的真正初心可能就在这里:我个人认为目前中国的云计算产业,或是信息基数产业,正面临着一个古老而熟悉,并且迫在眉睫的威胁,那就是固步自封和闭关锁国。 1840 年的第一次鸦片战争,英国舰队用坚船利炮轰开了古老的中华帝国的大门,中国由此开始了长达百余年的屈辱历史。在鸦片战争之前,中国人对世界的想象是我们处于世界的中心,其他国家根据距离中国的远近和外交关系的亲疏,其文明和先进等级递减,处于中华文明影响范围之外的地区没有像样的文明,只有茹毛饮血的蛮夷。我们的祖先曾经固执的关闭国门,仅从广州一个口岸与全球打交道,并顽固地抵制一切外国人发明的奇技淫巧。 结果就是大炮打碎了想象,蒸汽机和现代科学、工业碾碎了四书五经,中国人终于从中央帝国的迷梦中醒来,百余年的屈辱历史同时也是百余年的自强图存之路。这条路我们的先辈探索了很久很久,这条路是非常的艰辛且曲折的,直到伟大的改革开放,我们终于确立了拥抱世界,努力学习各种先进技术和经验,并且开始探索自己的创新之路。 云计算发展到今天,在中国,在世界,似乎走上了两条不同的道路;我们会观察到国内有越来越多的“混合云”、“私有云”、“政企云”、“专有云”,地方政府大量的数字化建设大单,几万几十万台服务器的大手笔;大厂也基本都选择自建数据中心,而不是使用公有云。另一方面,公有云理解的“产品设计”大多是指 GUI 界面上的设计,或是发力于单个产品的某些性能指标的优化,但其产品之间的关联能力非常薄弱。 中国的云用户似乎大多停留在将原先 IDC 中的架构和管理模式 Lift And Shift 到云端,就止步于此;大量的应用还是使用静态的架构,使用大量虚拟机,并没有太多云的特色。而许多所谓“云原生”厂商,则着眼于某些 K8s 生态中的点进行发力,比如专注于服务网格、API 网关、或是提供商业化的私有 K8s 发行版等等,但并没有形成一个像 AWS、Azure、GCP 这样围绕着公有云为基础设施,Kubernetes 作为应用层调度平台的庞大的工具生态圈。 国内外对云计算的不同理解和分歧在“基础设施即代码”时代变得更加明显,国外越来越多的团队已经将越来越多的日常工作转由各种 XXX as Code 工具来完成,高度自动化、工程化,而国内还停留在脚本、流程和堆人。整个中国云计算产业的发展被需求端拉向了一个与国际趋势完全不同的方向。 关天培将军战死虎门炮台之前,面对英国舰队的大炮,不知心中是何滋味。我等在今天面对洋人新的坚船利炮,面对 XXX as Code 和公有云在研发效率上的降维打击时,是要重复乾隆皇帝的天朝上国迷梦?还是勇敢地学习、消化、发展并创新? 这就是我这些年致力于宣传 HashiCorp 产品的初心,我希望将这种先进的生产力和其背后的思想介绍给国内的技术同仁,以及企业的管理者们,在发展中国云计算产业和信息技术产业这件事上,我们都是肩负历史使命的,我们接过的是中华民族伟大复兴的使命,这份使命要求我们不能重复祖先的错误,而是要继续勇敢地拥抱世界上的先进思想和技术,为我所用。 关起门来搞自己的小圈子是没有出路的,只有灭绝和被蹂躏的结局。我今天所做的只是尽力将一扇窗推开一条缝隙,多一点新鲜空气和光线进来。 仅以此篇自勉。 "},"10.后记/2.云与安全.html":{"url":"10.后记/2.云与安全.html","title":"后记 2 —— 云与安全","keywords":"","body":"后记 2 —— 云与安全 我经常在想,为什么 HashiCorp 没有诞生在中国,或者反过来说,为什么中国没有诞生 HashiCorp 这样的公司?就拿 Vault 这样的工具来说,其理念在国内仍然很少有人实践,这是为什么? 和大多数人一样,在很多平台注册账号时我也要填写个人联系方式,包括姓名、手机号等等。我在填写姓名时时常会设法将平台名编码进姓名里,比如肯德基,就叫“X肯生”,麦当劳?“X麦生”;美团?“X美生”...... 最后结果就是,我有时会在某些社工数据中查到自己的数据,通过这些数据中的姓名,我就能大致了解到数据泄漏的源头是哪里。 结论就是许多我们日常信任的品牌都存在过数据泄漏问题。这是为什么?为什么用户的隐私数据没有做好加密?为什么内部数据访问权限没有经过严格控制?为什么不花时间去学习并使用类似 Vault 这样健壮且成熟的安全工具? 原因是,在中国信息安全出问题的成本其实很低,以至于企业并没有什么动力去做好这件事。欧美在信息安全和隐私保护方面都有严格的立法,一旦发生数据泄漏,伴随着大额罚单的往往还有各种行政乃至刑事处罚。在我国目前这方面终于在立法方面有所进展,但目前看力度仍显不足。 兜售“大数据”概念的人往往喜欢说:“数据是新的石油”。我们甚至发明了“数据挖掘”这样的名词来形容对数据的分析。但是我们对真正的各种煤矿、油田等进行严格的安全生产立法的同时,对数据生产安全的处罚力度却显得非常稀松。假如某地发生矿难,数百人伤亡,那么不但企业负责人要负刑事责任,当地主政的一把手仕途可能也会受到严重影响。如果一家企业发生了严重的隐私泄漏事件,我们可能会看到具体涉事的底层员工被开除,发动攻击的黑客被抓捕,企业缴纳罚款,但我们似乎从来没有见到过有企业高管因玩忽职守而负刑事责任的。 在企业跑马圈地野蛮生长的时候,信息安全看起来似乎没有那么重要,安全人员和运维看起来就是可有可无的角色,企业管理层可以用各种手段减少团队在信息安全方面的投入。但如果我们严格立法并且严格执法,像管理煤矿那样管理信息安全,规定企业高管要对大规模隐私泄漏负刑事责任的话,我想中国企业才能正确评估信息安全、软件工程和自动化的真正价值,以及他们当下野蛮生长的真实成本。 多一些玩忽职守蒙眼狂奔的 CXO 唱铁窗泪,大众的隐私安全就多一份保障,中国的信息技术产业的发展也就更加健康一点。 "},"10.后记/3.中国云计算的革命尚未开始.html":{"url":"10.后记/3.中国云计算的革命尚未开始.html","title":"后记 3 —— 中国云计算的革命尚未开始","keywords":"","body":"中国云计算的革命尚未开始 最近一位朋友在他的朋友圈发了这样一张T恤的图,并且怒喷某头部云平台的运维平台产品仍然提供了代码脚本分组管理功能,并且喜滋滋地将其作为一大功能大书特书的行为,将其斥为云计算的反动派,宣传腐朽落后的技术理念。没成想因为忘记说明是T云,结果惹来一众A云大佬的关注;由于对号入座的人太多,不得不郑重声明喷的是T云而非A云(A云这种对号入座的心态也非常值得玩味) 我觉得这件事一下子勾动了心中这些年一些零零碎碎的想法。最近两年公有云领域的竞争情况是,中小玩家逐渐被挤出竞争,纷纷开始想在私有云方面寻找一线生机;而头部大厂似乎也放慢了对公有云市场的耕耘,也开始在私有云、智慧城市、地方政务云这些“大单”上发力,看起来公有云的增长与竞争格局似乎已经接近终局一般。打开公众号“云头条”,基本新闻都是某地的政务云教育云智慧城市云的招投标情况,都是多少台服务器,多少的显卡,多少套虚拟化管理系统,多少个数据库、消息队列等平台,云的竞争似乎变成了数据中心规模的竞争。 针对云的测评,无论是厂商还是第三方的,基本也都是着眼于性能测试,比的是CPU谁快,IOPS谁高,集群规模谁大等等。 云真的变成了一个单纯竞争规模、尺寸、成本和性能的行业了吗?这就是我们想要的云计算革命吗? 我并不这样认为。 电气化的曲折道路 在这里我想要先介绍BBC的一篇文章:《Why didn't electricity immediately change manufacturing?》 ,以下是我的一点意会的译文: 1870年托马斯爱迪生与约瑟夫斯旺两人分别独立发明了实用的灯泡,1881年爱迪生在纽约和伦敦分别部署了直流电发电站,并且开始对外出售电力。一年以后,爱迪生有了第一个使用电力来带动机器进行生产制造的客户。 但是直到1900年,全美仍然只有不到5%的工厂使用电力作为主要能源,大部分工厂坚持使用蒸汽机,这还是属于蒸汽机的时代。 以蒸汽机作为能源的工厂一般都有一个巨大的蒸汽机提供动力,同时通过一根非常长的,通常与厂房一样长的传动轴穿过整个厂房来分配动力。有时传动轴还会伸出厂房,进入其他厂房传输动力。 副轴、皮带以及齿轮环环相扣,驱动着工厂里的锤子、冲床、压床以及织布机,甚至可以通过垂直齿轮和轴,通过地板上的洞在楼层之间传输动力。这一切的动力机械被封装起来,以防止火灾通过缝隙蔓延开来。整套传动系统要持续地用大量润滑油持续润滑。 蒸汽机极少停机。只要工厂里有一部机器需要工作,那么蒸汽机就要保持运转,就要持续燃煤烧水。 这个由齿轮、传动轴、皮带搅动着润滑油、灰尘的复杂系统同时也是一个非常危险的机器,时不时就会有倒霉的工人因为袖子或者鞋带被绞住而被拖进这个无情而又吞噬一切的机器里。 有些工厂主尝试将蒸汽机替换成了电动机,使用附近发电站提供的更为清洁且现代化的电力驱动机器。但尽管付出了巨大的改造成本,这些工厂主大多数无法得到令人满意的收益。直到1910年,绝大多数的企业家虽然关注电力系统方面的进展,但仍然会选择老式的蒸汽机。 这是为什么?因为如果想要发挥出电气化的优势,工厂主需要一种全然不同以往的思维方式。他们当然可以直接把旧式工厂的蒸汽机直接替换成电动机,而其他部分不变,这使他们可以用最低的成本将工厂动力源替换成电力。 但问题是电动机实际上可以做的更多。电气化的关键在于可以把电力轻松传递到它所需要被使用的地方,并且可以弹性使用。不用机器时只需要关掉开关,就不会产生电力消耗。 小号的蒸汽机完全没有效率,它无法提供足够整个工厂使用的动力,但是小号的电动机却可以驱动一定数量的机器。所以新式工厂可以设置一系列的电动机,每一个电动机驱动一个小号传动轴。随着技术的发展,每个工作台都可以配备一个独立的小型电动机驱动自己的机器,动力的传输也由轴传动改为由电线传输电力。 旧式的蒸汽机厂房必须修建的足够坚固以承载巨大的钢制传动轴,而新式电气化工厂却可以修建的更加轻便与透气通风。 旧式的蒸汽机工厂在设计布局时必须围绕着传动轴来做设计,一切都是为了能够简单地获取动能;电气化意味着我们设计工厂时更多地考虑的是提升合作效率和生产力。 旧式的蒸汽机工厂昏暗且密不透风,所有的设备都围绕在传动轴和动力井周围;新式的电气化工厂可以自由展开,并且拥有更多的窗户,使得更多的阳光和新鲜空气可以进入厂房。 旧式的蒸汽机工厂里蒸汽机是核心;而在新式的电气化工厂,工人才是主角。 工厂从此变得更加整洁与安全,同时也更高效,因为机器需要运转时才打开开关运转,停机时关上开关,就不再消耗电力。 我们无法简单地把蒸汽机换成电动机而不动其余就能够获得这些进步,我们必须重新设计一切——从工厂的布局,到生产流程,都需要适应新的动力源。 同时由于工人的生产方式变得更加自治与弹性,我们也要改变招工、培训流程,甚至是薪酬体系也要做相应的改变。 面对这些改变,有些工厂主犹豫了。他们的犹豫是完全合理的。他们当然不想把自己既有的投资扔进垃圾堆,但也许他们只是需要更多时间去思考这样一个需要改变一切来适应这种新技术的全新世界对既有规则的冲击。 有赖于电力系统越来越廉价并且可靠,同时因为一系列限制欧洲移民的新法律的出台,美国工人成本变得更加昂贵,最终,电气化革命还是无可避免地发生了。 平均工资的飙升使得招聘员工越来越多地开始关注质量而非数量。训练有素的工人可以充分利用电气化给予他们的自治性,同时越来越多的工厂主学会了如何最大化电动机的优点,关于生产制造的新理念得到了大范围传播。 时间来到1920年,美国制造业的生产效率以一种前所未见的方式飙升。 我们可能将生产效率的大幅提升归功于某种新技术的的诞生,但其实并非如此,我们发现,有时这种进步会大幅晚于新技术的诞生,有时会晚50年。 译文到此结束。 直接迁移(Lift And Shift)——云计算领域的传动轴 早期推广企业上云时为了降低上云的难度,云厂商和企业有时会采用一种名为直接迁移(Lift And Shift)的策略。这种策略是指将应用程序以及其数据存储、操作系统和架构的精确副本从本地环境迁移到云端的过程。 直接迁移相对来说实施成本较低,风险较小,实施周期较短,作为企业上云的初始阶段具有明显的优势。但是将本地数据中心的应用原封不动地迁移到公有云上,就如同将旧式工厂的蒸汽机替换成了电动机而不动其余一样,并不能最大化云计算所带来的收益;有时企业经过核算之后甚至会发现,长期使用的场景下,公有云的服务器资源使用成本相比起自有机房内采购的服务器,不但没有降低,反而可能更高。 蒸汽机工厂简单地把蒸汽机替换成电动机并不能享受到电动机可以弹性工作的优势(可以随时开关机,关机时不消耗电力),简单地直接迁移应用上云也无法享受到云计算带来的弹性计算的优势。 甚至很多中国公有云厂商设计的定价模型对公有云的弹性特征都是不友好的。举一个例子,同样是包年包月的方式付费,AWS采用的计费方式属于后付费方式,比如客户购买了一台16核32G内存的服务器一年的使用权限,那么在这一年的时间内客户可以随时开启这样一台16核32G的服务器,不会有额外费用。客户可以随时删除这台服务器,重新建立一个同尺寸的新服务器,可以安装不同的镜像,执行不同的计算任务,机器的ID可以不同,AWS不管这些,只要是同一尺寸的机器,就不会有额外的费用。假如客户开了两台这样的机器呢?那么这个月的账单里会出现一台机器的按需使用实例的费用,另一台的费用已经被年付的预付费包括了。 同样的包年包月付费方式,国内大部分公有云都是要求客户提前付费,随后直接分配一台服务器实例给客户,这笔年费与这台服务器实例是绑定的。如果我们删除了这台服务器呢?那么会产生一个退费逻辑,会将这台服务器实例尚未使用的时间折算成费用退还到客户的账户内。那这种退费会带来什么样的后果呢?比如我年初年付购买了一台服务器,此时账户余额为0,但我有了一台可以运行一年的服务器实例;年中的时候我删除了这台服务器,然后想重新创建一台同尺寸的服务器。我想以年付的价格继续使用半年到年底(这样我总共以年付的价格使用这样一台服务器一整年的时间),我会发现这是不行的,因为我的余额不足以让我再购买一年的时长,同时不买足一年的时长我就不能以年付的价格购买六个月。 更不要说有时云厂商会在某一天搞活动,在这一天可以以非常优惠的价格购买年付费的服务器实例,但如果购买的实例在日后被删除了,那么这个优惠也就随之而去了。 诸如此类的各种细节问题限制了我们对云的看法,我们仍然是以看待和管理自有数据中心的方式来对待公有云;购买的预留实例我们会紧紧地握在手中,这一切的阻碍都影响到我们进一步解放公有云的弹性和可编排能力。 目前有大量的企业对云的使用就如同把蒸汽机拆掉换成电动机,然后说:你看,我们成功上云了。 噢并非如此我的朋友,并非如此。 盲人摸象——割裂的云原生运动 为了使得更好地解放云的生产力,业界近年来开始提出云原生(Cloud Native)运动。云原生并不是某一种或几种具体的技术,而更多的是一种文化。根据云原生计算基金会(CNCF)的定义: 云原生技术有利于各组织在公有云、私有云和混合云等新型动态环境中,构建和运行可弹性扩展的应用。云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式 API。 这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段,云原生技术使工程师能够轻松地对系统作出频繁和可预测的重大变更。 经过数年的发展,云原生的生态圈有了爆炸式的发展,就像当年的大数据全景图(Big Data Landscape),现如今要完整加载云原生基金会的全景图也已经是一件比较缓慢的过程了,并且很难从全景图上快速找到一个目标项目的图标——如果CNCF基金会的目的是展示其生态的庞大和繁荣,那么CNCF全景图已经超额完成了这一目标。 然而在云原生时代,我们交付云端应用有没有变得更加简单和快速呢?目前看效果有限。 交付一个生产环境的云端应用需要考虑多少环节?我们要考虑业务逻辑、应用架构、服务发现、故障转移、服务可观测、安全控制、背压熔断与优雅降级服务网格、Dockerfile、镜像存储与版本化管理、安全扫描、K8s容器编排与服务编排、各种排查故障的工具、脚本,日志系统等等等等。 一个简单的问题,我该如何从无到有地在目标环境中部署一个生产就绪的应用,并且确保它的维护与更新是可持续的?由于CNCF生态给了我们太多的选择与答案,而每个选择与答案又有它们所适用的范围与优劣势,再搭配以高速的发展和演进,我们很容易面临两种问题: 分析瘫痪 NIH综合症 分析瘫痪是指由于选择过多,我们无法获得足够多的信息使得我们有足够的信心做出具体的决策,导致我们总是在对比与分析,不敢付诸生产环境的实践。 NIH综合症意为非我所创综合症(Not Invented Here),由于全面分析开源社区的所有选择与答案是不现实的,所以为了最大化地掌握自身的命运,往往会选择自研。我们经常会看到一些加拉帕戈斯式的工具、项目以及集群(一个典型的例子是遇到过一个团队选择在AWS上使用Rancher管理一组EC2虚拟机组建K8s集群,而非使用EKS集群,原因似乎也是为了“最大化的控制力”)。 使得问题变得更麻烦的是CNCF生态中的各个项目与厂商,大部分厂商会将自身的目标定位在CNCF划定的某一个具体的领域内——云原生网络、云原生存储、云原生数据库、云原生服务网格、服务网关、Serverless运行时等等等等,他们会沉迷于对局部问题的深度优化。 然而云的使用者在旧式的思维指导下用云观念落后,导致局部优化对总体效率的提升相当有限,反而是增加了更多的复杂度。如今的云原生运动面临着盲人摸象式的困境,每个人对云原生到底哪个是重点都有着自己独特的理解和答案,而作为整体,理应带给我们更高生产力的那种新时代的云计算应用交付方式,仍然像是佛经中提到的林中象一般扑朔迷离,极少有人理解它是什么,它在哪里。 云计算革命的目标应该是人 云原生革命的目标应该是解放IT从业者的生产力,减少价值流动中的障碍,提升价值的交付速度和频率,应该是提升人的自治性,提升人的综合素质和效率。 目前阻碍技术团队最大化地发挥交付能力的最大障碍,并不是哪个局部细节没有最优化,而是作为整体,我们并没有一种适用于云原生时代的新的研发交付范式。 新的技术已经到来,而我们目前使用的范式仍然偏向旧有的,静态的、割裂的时代,我们就是在电动机驱动传动轴厂房中工作的工人、工厂主。生产关系不改变,工厂不重新设计,针对发电厂做再多的优化和改良对生产效率的提升都是有限的。蒸汽机时代的工人要从蒸汽机直接获取动力是十分困难的,更遑论要根据情况对自己的工作台进行个性化的改造;而目前技术团队要直接获取基础设施及服务也是十分困难(假如还要借助于电子邮件或者工单的话),进行个性化改造更是难上加难。 我们需要的是全新的工厂,全新的工人,全新的生产关系和工具链。我们需要的是认清这样一个事实:只有优化整体,彻底解放人的生产力才能带来总体收益的提升。 云计算革命仍然处于一阶状态 电气化与云计算有着许多的相似点。就比如云计算厂商口中一句高频率的口号,就是要把计算做成像水电这样的模式,用时打开龙头开关就可以获得,用完了关上就不再计费。 云原生运动帮助客户像使用水电一样使用云计算资源了吗?部分上是的,在许多具体的问题上我们的确获得了一些进展,但是作为整体而言还远远不足。比如直到现在,要编写一个可以多云部署,并且能够把目标云平台的各种优势发挥到极致的云端应用,对技术团队来说仍然是一件困难的工作。我们的从业人员仍然在各种由不同产品、不同服务、不同职能、不同部门和组织所构成的筒仓之间挣扎,价值的流动仍然受到各种各样的阻碍。 电气化革命带给云计算革命的启示是,至少在中国,云计算的革命远未结束,它甚至尚未开始。云计算理念的诞生到今天也不过短短十来年,我们的组织结构和社会文化尚未适应这种水电化的弹性,以及与微服务架构所适应的分布式、小规模自治团队的协作方式。 云计算革命在中国目前仍然处于一阶状态,也就是我们会把最终生产的系统部署在公有云或者私有云上交付给用户,但是我们本身的生产过程却没有充分利用云的特性。例如现在依然有不少团队的CI Agent不是使用Serverless容器,或者部署的Artifact仓库使用的存储不是例如S3这样的Serverless对象存储,即使公有云提供了各种简便快捷的服务,许多公司和团队仍然倾向于重复发明自己的加拉帕戈斯轮子。什么时候我们的生产过程本身也是大量使用到云的特性,复用成熟的公有云服务,那么云计算革命就算是进入了二阶状态。 那么有没有三阶状态呢?我想是有的。软件工程相较于其他的传统工程来说有一个显著的不同点,就是软件工程包含了对自身效率提升的工程实践,例如编写生成代码的代码,编写自动检查合规的工具等等,我们可以通过编写软件来提升我们编写软件的效率,这也就是元编程(Metaprogramming)思想的威力。同样的道理,我们也可以探索如何使用云计算的能力来提升我们使用云计算的能力,例如将复杂的云应用部署流程用基础设施即代码(IaC)技术进行封装后,将其封装为一个SAM(Serverless Application Model)风格的云端应用,通过API暴露其能力,使得我们可以通过调用简单的自助服务来获得各种复杂的云端环境和能力。这种云计算的元编程应用本身又可以组成更高阶的云计算元编程服务。 回想电气化革命的过程,就是把动力和人的关系进行反转;传统的蒸汽机工厂之所以是这样设计的,是因为动力是难以获得的,所以工厂必须以蒸汽机和传动系统为核心,人从属于机器;而电气化时代,通过电线传递的电力是易于获得的,并且通过开关控制可以弹性获得电力,所以电气时代是以人为核心,机器从属于人。只有当人和动力、人和机器的从属关系进行了适当调整之后,电气化的高效率才能被完整释放出来。 同样的,云计算时代人和基础设施的关系也要进行调整。传统数据中心时代,基础设施是难以获得的,是昂贵的,所以人要从属于基础设施;云计算时代基础设施是易于获得的,是廉价的,是弹性的,所以基础设施要从属于人。只有每个人都能像拧开水龙头洗手那样方便地随时随地获取所需要的基础设施时,才能解放人的生产力,释放云计算真正的效率。 "},"10.后记/4.加拉帕戈斯云上不会有千里马.html":{"url":"10.后记/4.加拉帕戈斯云上不会有千里马.html","title":"后记 4 —— 加拉帕戈斯云上不会有千里马","keywords":"","body":"加拉帕戈斯云上没有千里马 缘起达尔文与小猎犬 1835年,26岁的查尔斯·达尔文跟随一艘名为\"小猎犬\"号的英国海军船来到了太平洋东部的加拉帕戈斯群岛进行环球考察。 加拉帕戈斯群岛(西班牙语:Islas Galápagos,官方名称Archipiélago de Colón),位于太平洋东部,接近赤道,为厄瓜多尔领土,属火山群岛,面积7976平方公里,离厄瓜多尔本土1100公里,是加拉帕戈斯省所在地,在西班牙语中,“Galápagos”意为“陆龟”。 达尔文在岛上观察到了大量只有该岛才有的珍奇物种,大为震惊,并且发现即使是生活在不同岛上的同一种类的鸟类和海龟,形态和习性都大不相同,一个念头在达尔文心中逐渐成型。在岛上停留了采集了大量标本与数据后,继而与他在南美大陆、澳大利亚采集的标本和数据进行了对比,随着不断的观察和思考,达尔文逐渐总结出“自然选择”是造成生物多样性的原因,生物并不是神创造的不变的形态,而是会为了适应自然环境而发生变化的。这次考察推动了达尔文编写并发表的震撼世界的《物种起源》,科学取代了上帝的位置,人类也回答了“我们从何而来”这个问题。 福斯特法则 1964年英国科学家约翰·布里斯托尔·福斯特(John Bristol Foster)在自然杂志上发表了一篇题为《哺乳动物在岛屿上的演化》(The evolution of mammals on islands)的文章,在对116座岛上的物种进行研究后福斯特发现,与它们在大陆上的同类相比,一些动物的体型变得更大(岛屿巨型化),而另一些则变得更小(岛屿侏儒化)。 福斯特法则最著名的例子是塞舌尔群岛的巨型陆龟、印度尼西亚的科摩多巨蜥和伯利兹Snake Cayes的蟒蛇。至于那些已灭绝的生物,最好的例子则是钝齿鼠,这种豚鼠的体积与黑熊差不多,它们之前生活在加勒比海的安圭拉岛和圣马丁岛。 福斯特认为,由于缺乏天敌,岛屿上小型动物倾向于变得越来越大以争夺更多的资源,大型动物则因为食物资源的缺乏而变得越来越小。 公有云与私有云,开源技术与自造轮子 近年来我们经常看到各地各种政务云、智慧城市、城市大脑以及大型企业发包私有云的大单,大手笔采购各类服务器、高端存储以及显卡等硬件,再由供应商提供管理系统,组建行业云、地方云、企业私有云等。 这与国外市场动向形成了鲜明对比。国内建设私有云往往出于“安全”、“自主可控”等等名义,但这些要求实际上国外也存在,但我们还是能观察到国外重要敏感部门使用公有云技术(注意,不一定直接使用公有云,而是由公有云为其部署一个私有版本,但仍然采用与公有云一致的技术) 假如这些顶级敏感部门在进行评估后都认为使用公有云技术来构建自己的云平台,那么还有什么企业认为自己对“安全”和“自主可控”的需求等级比这种类型的部门还要高呢? 闭关锁国使人落后 使用私有云看起来可控性高,但也会失去改变和进化的动力;对于企业内有志于钻研技术工作的“大型动物”来说,由于在封闭环境内积累的技术经验很难在别处使用,这样的人才要么选择及时离开,要么如同岛屿上的大型动物一样,逐渐“缩小”自己的关注点,逐渐变得过于适应这个环境而失去了对主流技术的嗅觉。而对于那些原本就乐于混日子的平庸之类来说,巴不得一个高度封闭、可控的系统,变更尽可能少,让他们可以当一天和尚撞一天钟,所以使用封闭式私有云平台的环境里,往往“啮齿类”动物们悠然自得长得肥肥大大的。 私有云平台的真正问题 私有云平台的真正的问题在于他们的人力是不可能全方位紧跟主流技术发展前沿的,这就会产生一种不喜欢更新迭代和变化的倾向,这种倾向有两种后果,一是无法享受到蓬勃发展的社区工具链的成果,二是有心钻研技术的优秀技术人员要么离开,要么被迫变得平庸,而组织中平庸之辈成长的越来越大。 我们总认为技术和工具是为业务服务的,这话倒也没错;但是反过来,技术进步和工具的革新也会反过来促进业务和组织结构形式的发展,蒸汽机的出现催生出了现代工业,也催生了现代资本主义的发展。中国自改革开放以来化身基建狂魔,投入惊人的资源和力量提升基础设施建设水平,也是希望优秀的基础设施可以降低经济运行的成本,从而催生出原先无法生存的新技术、新业态。国内众多的小小的私有云平台,就如同一个个小小的岛屿,岛屿上会出现各种珍奇异兽,各种大陆上难得一见的生态环境,但如果以这种难得一见和特殊性为荣的话,那么绝大多数岛屿生物在人类到来以后都不是灭绝就是濒危的现实可以给持有这样想法的人敲响警钟。关起门来搞自己的一套,在真正“船坚炮利”的人来了以后,只有灭绝一条路,要想在残酷的竞争和进化战争中持续生存下去,唯有在最广阔的大陆上与最先进的对手交流、交战,互相学习,才能立于不败之地。 "},"10.后记/5.致谢.html":{"url":"10.后记/5.致谢.html","title":"后记 5 —— 致谢","keywords":"","body":"致谢 过去的一年对我个人而言可谓否极泰来,终于回到了软件开发的本行,新的工作感觉也很舒适,同时充满了令人兴奋的挑战,可以专心从事自己喜欢的基础设施即代码方面的工作真的是让人感到心满意足。在这里要首先感谢新的领导和同事们,给了我这样一个令人满意的工作环境。 一些自下而上的力量往往会迸发出令人惊讶的韧劲,自从加入了一个以美剧《Billions》影迷为主的群后,认识了各行各业不少天南海北的朋友,发现,如果将“学习”局限在自己的专业里那将是坐井观天,莫大的损失。各行各业的群友提出的各种有趣有深度的想法交织在一起,逐步解除了我盲人摸象的状态,或者至少说,我们这次是把多角度盲人摸到的情况整合在一起,试图还原出关于大象的高维度真相。该群由于各种原因被二向箔拍平了两次,但每次都能顽强地自发地自我重建。这个群的实践又让我开始对一个去中心化的自组织协作组织的可能性产生了兴趣,也许未来真的是属于类似 DAO 这样的协作模式的?不管如何,非常感谢各位群友的指教!(有兴趣的朋友可以订阅微信公众号:Intellectual honesty) 最后,最需要感谢的仍然是我的家人,感谢你们对任性的我的包容,感谢你们接受这样一个没有世俗意义成功的我。没有你们的支持,我无法完成本书的工作。 "}}