和很多其他系统一样,Vault的access control也可以简化为一个问题:谁(who)在什么情况下(condition)能去哪(path)干什么(capabilities)?上文说过Vault中这个谁(who)的概念相对模糊,并不会具体指出是什么用户,又或者是一个应用程序,那么在我们设计Vault中的权限分级时可以先放下这个问题,不去纠结具体的who,而是设计出Vault在使用中会涉及哪些角色(role)。所谓角色,即被赋予了某个策略(policy)子集的一类个体,这个角色只能执行这个权限子集内的策略。而一个策略中则会定义条件(condition)+ 路径(path)+ 能力(capability)。这些term看起来很冰冷晦涩,下面我们通过一个简单的场景来解释Vault中的policy。
设想,我们现在要用Vault来管理我们学校的CS系的机密信息,比如期末考试题(exam),教授工资信息(salary),学生成绩(grade)等。这里还有一些非机密的信息,比如课程的名字和编号(course name and code),教授的基本profile等。那这个故事中可以有哪些角色呢?明面上来看,教授和学生就是很好的例子,也有着很明显的信息可见度的分级,又不缺乏特殊可见度的情形,故事中假设如下:
- 学校CS系提供了多种多样的CPSC课程,如cpsc210,课程的信息都是公开的,包括课件,上课学生的ID和姓名。
- 每个教授可以教0到多节课,并可以查看课上学生所有信息包括成绩,但是对于其他课的学生则不能查看任何信息。
- CS系的HR可以查看所有学生信息,所有教授的信息包括工资。
- 教授不可以查看其他教授或HR的私人信息,例如工资,家庭住址等。
- 学生只能查看自己的信息,以及和自己相关的信息,例如自己上的某节课的成绩。
我们先来生成故事角色的信息:
vault secrets enable -path=course/ kv
vault secrets enable -path=student/ kv
vault secrets enable -path=professor/ kv
vault secrets list
vault kv put professor/Ruby_Swift salary=150000 address='3301 SchoolSide Drive'
vault kv put professor/Lua_Haskell salary=160000 address='214 Campus Plaza'
vault kv list professor/
vault kv put course/cpsc/cpsc210 \
instructor_name='Ruby Swift' \
course_title='Hello Object' \
course_code=CPSC210 \
num_students=129
vault kv put course/cpsc/cpsc317 \
instructor_name='Lua Haskell' \
course_title='Network Intro' \
course_code=CPSC317 \
num_students=76
vault kv list course/cpsc/
vault kv put student/Alice_Perl student_num=20200001
vault kv put student/Bob_Java student_num=20200002
vault kv put student/Charlie_Kotlin student_num=20200003
vault kv list student/
接下来我们根据故事中的假设来创建关于这些路径的policy。我们这里可以根据角色来一一创建对应的policy,即professor_policy, student_policy和hr_policy。举个例子Alice Perl的student_policy可以向如下定义:
path "student/Alice_Perl" {
capabilities = ["read", "list"]
}
把这个policy存成一个文件,如alice_perl.hcl,然后将这条policy写入Vault:
vault policy list
vault policy write student_alice_perl alice_perl.hcl
vault policy list
vault list sys/policy/
vault policy read student_alice_perl
vault read sys/policy/student_alice_perl
可以用同样的方式为其他student创建类似的policy。Vault中所有资源都是path-based,即会存在某一个路径下,policy也属于Vault的资源,固定存在sys/policy/下,所以这里也可以通过vault read和list直接对其访问,换句话说vault policy这个subcommand和kv一样只是为了方便,其本质还是对于sys/policy/的子路径进行read,write,list等操作。
那么这里就出现了下一个问题:新建的policy如何绑定给一个“角色”呢?因为这样我们才能从不同的角色的角度来测试我们的policy。在我们还没有讲解AuthN之前,我们可以把这个问题简化一下,即我们如何证明正在跟Vault互动的用户是某个角色呢?答案是通过出示一个Token。这个用户的角色对于Vault来说没有意义,Vault在意的是这个Token绑定了哪些policy,以及如何判断Token是否有效。那我们来看一下如何出示一个Token:
vault login s.3jnbMAKl1i4YS3QoKdbHzGXq # 向Vault展示这个Token
这就是最直接的AuthN,把Token展示给Vault,忽略这Token是怎么来的,从哪来的。Vault这时就会记录下来这个Token并且赋予这个正在使用的用户以Token绑定的policy。上面的例子我用了一个Root Token(回忆,Root Token是在启动server时显示的),那也就是说我接下来的操作只要在root policy的范围内都是被允许的(Allowed)。但是我们并不想测试root policy,而是要测试我们刚创建的student_alice_perl policy,这时该怎么办呢?这里有两种方式,一种是通过正确的AuthN获取Token,二是生成一个新的绑定了policy的Token,第一种方式我们放到后面讲,这里先来看一下如何生成一个Token:
vault token create -policy=student_alice_perl
然后会看到类似这样的信息:
其中的token一项便是新生成的token,而且也会看到token_policie和policies都显示了student_alice_perl,其他信息会在后面章节详述,这里先记下token的string,然后就可以出示这个token来登录:
vault login s.I2neLob1a8x01JXVh7iIc4Uu
Vault这时会提示你已经成功登录,当然如果这个token过期了或者被revoke了,那自然是不会成功。那我们怎么来证明自己现在是在使用student_alice_perl而不是root呢?可以通过实践来检测,我们知道这个policy只能访问student/Alice_Perl这个路径,那我们可以尝试如下:
vault kv get student/Alice_Perl # ok
vault kv get student/Bob_Java # denied
vault kv get course/cpsc/cpsc210 #denied
对于student/Bob_Java和course/cpsc/cpsc210的访问以及对于Alice_Perl的更改(write),你会得到类似下面的403:
另外一种简单的方式来确认当前用户的policy可用:
vault token capabilities student/Alice_Perl # read,list
vault token capabilities student/Bob_Java # deny
vault print token # 显示正在使用的token