rbac_test.go 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package auth
  14. import (
  15. "context"
  16. "fmt"
  17. "io"
  18. "io/ioutil"
  19. "net/http"
  20. "net/http/httputil"
  21. gopath "path"
  22. "reflect"
  23. "strings"
  24. "testing"
  25. "time"
  26. rbacapi "k8s.io/api/rbac/v1"
  27. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  28. "k8s.io/apimachinery/pkg/runtime/schema"
  29. "k8s.io/apimachinery/pkg/types"
  30. "k8s.io/apimachinery/pkg/watch"
  31. "k8s.io/apiserver/pkg/authentication/request/bearertoken"
  32. "k8s.io/apiserver/pkg/authentication/token/tokenfile"
  33. "k8s.io/apiserver/pkg/authentication/user"
  34. "k8s.io/apiserver/pkg/authorization/authorizer"
  35. genericfeatures "k8s.io/apiserver/pkg/features"
  36. "k8s.io/apiserver/pkg/registry/generic"
  37. utilfeature "k8s.io/apiserver/pkg/util/feature"
  38. clientset "k8s.io/client-go/kubernetes"
  39. restclient "k8s.io/client-go/rest"
  40. watchtools "k8s.io/client-go/tools/watch"
  41. "k8s.io/client-go/transport"
  42. featuregatetesting "k8s.io/component-base/featuregate/testing"
  43. "k8s.io/klog"
  44. "k8s.io/kubernetes/pkg/api/legacyscheme"
  45. rbachelper "k8s.io/kubernetes/pkg/apis/rbac/v1"
  46. "k8s.io/kubernetes/pkg/master"
  47. "k8s.io/kubernetes/pkg/registry/rbac/clusterrole"
  48. clusterrolestore "k8s.io/kubernetes/pkg/registry/rbac/clusterrole/storage"
  49. "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding"
  50. clusterrolebindingstore "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/storage"
  51. "k8s.io/kubernetes/pkg/registry/rbac/role"
  52. rolestore "k8s.io/kubernetes/pkg/registry/rbac/role/storage"
  53. "k8s.io/kubernetes/pkg/registry/rbac/rolebinding"
  54. rolebindingstore "k8s.io/kubernetes/pkg/registry/rbac/rolebinding/storage"
  55. "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac"
  56. "k8s.io/kubernetes/test/integration/framework"
  57. )
  58. func clientForToken(user string) *http.Client {
  59. return &http.Client{
  60. Transport: transport.NewBearerAuthRoundTripper(
  61. user,
  62. transport.DebugWrappers(http.DefaultTransport),
  63. ),
  64. }
  65. }
  66. func clientsetForToken(user string, config *restclient.Config) (clientset.Interface, clientset.Interface) {
  67. configCopy := *config
  68. configCopy.BearerToken = user
  69. return clientset.NewForConfigOrDie(&configCopy), clientset.NewForConfigOrDie(&configCopy)
  70. }
  71. type testRESTOptionsGetter struct {
  72. config *master.Config
  73. }
  74. func (getter *testRESTOptionsGetter) GetRESTOptions(resource schema.GroupResource) (generic.RESTOptions, error) {
  75. storageConfig, err := getter.config.ExtraConfig.StorageFactory.NewConfig(resource)
  76. if err != nil {
  77. return generic.RESTOptions{}, fmt.Errorf("failed to get storage: %v", err)
  78. }
  79. return generic.RESTOptions{StorageConfig: storageConfig, Decorator: generic.UndecoratedStorage, ResourcePrefix: resource.Resource}, nil
  80. }
  81. func newRBACAuthorizer(t *testing.T, config *master.Config) authorizer.Authorizer {
  82. optsGetter := &testRESTOptionsGetter{config}
  83. roleRest, err := rolestore.NewREST(optsGetter)
  84. if err != nil {
  85. t.Fatalf("unexpected error from REST storage: %v", err)
  86. }
  87. roleRegistry := role.AuthorizerAdapter{Registry: role.NewRegistry(roleRest)}
  88. rolebindingRest, err := rolebindingstore.NewREST(optsGetter)
  89. if err != nil {
  90. t.Fatalf("unexpected error from REST storage: %v", err)
  91. }
  92. roleBindingRegistry := rolebinding.AuthorizerAdapter{Registry: rolebinding.NewRegistry(rolebindingRest)}
  93. clusterroleRest, err := clusterrolestore.NewREST(optsGetter)
  94. if err != nil {
  95. t.Fatalf("unexpected error from REST storage: %v", err)
  96. }
  97. clusterRoleRegistry := clusterrole.AuthorizerAdapter{Registry: clusterrole.NewRegistry(clusterroleRest)}
  98. clusterrolebindingRest, err := clusterrolebindingstore.NewREST(optsGetter)
  99. if err != nil {
  100. t.Fatalf("unexpected error from REST storage: %v", err)
  101. }
  102. clusterRoleBindingRegistry := clusterrolebinding.AuthorizerAdapter{Registry: clusterrolebinding.NewRegistry(clusterrolebindingRest)}
  103. return rbac.New(roleRegistry, roleBindingRegistry, clusterRoleRegistry, clusterRoleBindingRegistry)
  104. }
  105. // bootstrapRoles are a set of RBAC roles which will be populated before the test.
  106. type bootstrapRoles struct {
  107. roles []rbacapi.Role
  108. roleBindings []rbacapi.RoleBinding
  109. clusterRoles []rbacapi.ClusterRole
  110. clusterRoleBindings []rbacapi.ClusterRoleBinding
  111. }
  112. // bootstrap uses the provided client to create the bootstrap roles and role bindings.
  113. //
  114. // client should be authenticated as the RBAC super user.
  115. func (b bootstrapRoles) bootstrap(client clientset.Interface) error {
  116. for _, r := range b.clusterRoles {
  117. _, err := client.RbacV1().ClusterRoles().Create(context.TODO(), &r, metav1.CreateOptions{})
  118. if err != nil {
  119. return fmt.Errorf("failed to make request: %v", err)
  120. }
  121. }
  122. for _, r := range b.roles {
  123. _, err := client.RbacV1().Roles(r.Namespace).Create(context.TODO(), &r, metav1.CreateOptions{})
  124. if err != nil {
  125. return fmt.Errorf("failed to make request: %v", err)
  126. }
  127. }
  128. for _, r := range b.clusterRoleBindings {
  129. _, err := client.RbacV1().ClusterRoleBindings().Create(context.TODO(), &r, metav1.CreateOptions{})
  130. if err != nil {
  131. return fmt.Errorf("failed to make request: %v", err)
  132. }
  133. }
  134. for _, r := range b.roleBindings {
  135. _, err := client.RbacV1().RoleBindings(r.Namespace).Create(context.TODO(), &r, metav1.CreateOptions{})
  136. if err != nil {
  137. return fmt.Errorf("failed to make request: %v", err)
  138. }
  139. }
  140. return nil
  141. }
  142. // request is a test case which can.
  143. type request struct {
  144. // The bearer token sent as part of the request
  145. token string
  146. // Resource metadata
  147. verb string
  148. apiGroup string
  149. resource string
  150. namespace string
  151. name string
  152. // The actual resource.
  153. body string
  154. // The expected return status of this request.
  155. expectedStatus int
  156. }
  157. func (r request) String() string {
  158. return fmt.Sprintf("%s %s %s", r.token, r.verb, r.resource)
  159. }
  160. type statusCode int
  161. func (s statusCode) String() string {
  162. return fmt.Sprintf("%d %s", int(s), http.StatusText(int(s)))
  163. }
  164. // Declare a set of raw objects to use.
  165. var (
  166. writeJobsRoleBinding = `
  167. {
  168. "apiVersion": "rbac.authorization.k8s.io/v1",
  169. "kind": "RoleBinding",
  170. "metadata": {
  171. "name": "pi"%s
  172. },
  173. "roleRef": {
  174. "apiGroup": "rbac.authorization.k8s.io",
  175. "kind": "ClusterRole",
  176. "name": "write-jobs"
  177. },
  178. "subjects": [{
  179. "apiGroup": "rbac.authorization.k8s.io",
  180. "kind": "User",
  181. "name": "admin"
  182. }]
  183. }`
  184. aJob = `
  185. {
  186. "apiVersion": "batch/v1",
  187. "kind": "Job",
  188. "metadata": {
  189. "name": "pi"%s
  190. },
  191. "spec": {
  192. "template": {
  193. "metadata": {
  194. "name": "a",
  195. "labels": {
  196. "name": "pijob"
  197. }
  198. },
  199. "spec": {
  200. "containers": [
  201. {
  202. "name": "pi",
  203. "image": "perl",
  204. "command": [
  205. "perl",
  206. "-Mbignum=bpi",
  207. "-wle",
  208. "print bpi(2000)"
  209. ]
  210. }
  211. ],
  212. "restartPolicy": "Never"
  213. }
  214. }
  215. }
  216. }
  217. `
  218. aLimitRange = `
  219. {
  220. "apiVersion": "v1",
  221. "kind": "LimitRange",
  222. "metadata": {
  223. "name": "a"%s
  224. }
  225. }
  226. `
  227. podNamespace = `
  228. {
  229. "apiVersion": "v1",
  230. "kind": "Namespace",
  231. "metadata": {
  232. "name": "pod-namespace"%s
  233. }
  234. }
  235. `
  236. jobNamespace = `
  237. {
  238. "apiVersion": "v1",
  239. "kind": "Namespace",
  240. "metadata": {
  241. "name": "job-namespace"%s
  242. }
  243. }
  244. `
  245. forbiddenNamespace = `
  246. {
  247. "apiVersion": "v1",
  248. "kind": "Namespace",
  249. "metadata": {
  250. "name": "forbidden-namespace"%s
  251. }
  252. }
  253. `
  254. limitRangeNamespace = `
  255. {
  256. "apiVersion": "v1",
  257. "kind": "Namespace",
  258. "metadata": {
  259. "name": "limitrange-namespace"%s
  260. }
  261. }
  262. `
  263. )
  264. // Declare some PolicyRules beforehand.
  265. var (
  266. ruleAllowAll = rbachelper.NewRule("*").Groups("*").Resources("*").RuleOrDie()
  267. ruleReadPods = rbachelper.NewRule("list", "get", "watch").Groups("").Resources("pods").RuleOrDie()
  268. ruleWriteJobs = rbachelper.NewRule("*").Groups("batch").Resources("*").RuleOrDie()
  269. )
  270. func TestRBAC(t *testing.T) {
  271. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
  272. superUser := "admin/system:masters"
  273. tests := []struct {
  274. bootstrapRoles bootstrapRoles
  275. requests []request
  276. }{
  277. {
  278. bootstrapRoles: bootstrapRoles{
  279. clusterRoles: []rbacapi.ClusterRole{
  280. {
  281. ObjectMeta: metav1.ObjectMeta{Name: "allow-all"},
  282. Rules: []rbacapi.PolicyRule{ruleAllowAll},
  283. },
  284. {
  285. ObjectMeta: metav1.ObjectMeta{Name: "read-pods"},
  286. Rules: []rbacapi.PolicyRule{ruleReadPods},
  287. },
  288. },
  289. clusterRoleBindings: []rbacapi.ClusterRoleBinding{
  290. {
  291. ObjectMeta: metav1.ObjectMeta{Name: "read-pods"},
  292. Subjects: []rbacapi.Subject{
  293. {Kind: "User", Name: "pod-reader"},
  294. },
  295. RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "read-pods"},
  296. },
  297. },
  298. },
  299. requests: []request{
  300. // Create the namespace used later in the test
  301. {superUser, "POST", "", "namespaces", "", "", podNamespace, http.StatusCreated},
  302. {superUser, "GET", "", "pods", "", "", "", http.StatusOK},
  303. {superUser, "GET", "", "pods", "pod-namespace", "a", "", http.StatusNotFound},
  304. {superUser, "POST", "", "pods", "pod-namespace", "", aPod, http.StatusCreated},
  305. {superUser, "GET", "", "pods", "pod-namespace", "a", "", http.StatusOK},
  306. {"bob", "GET", "", "pods", "", "", "", http.StatusForbidden},
  307. {"bob", "GET", "", "pods", "pod-namespace", "a", "", http.StatusForbidden},
  308. {"pod-reader", "GET", "", "pods", "", "", "", http.StatusOK},
  309. {"pod-reader", "POST", "", "pods", "pod-namespace", "", aPod, http.StatusForbidden},
  310. },
  311. },
  312. {
  313. bootstrapRoles: bootstrapRoles{
  314. clusterRoles: []rbacapi.ClusterRole{
  315. {
  316. ObjectMeta: metav1.ObjectMeta{Name: "write-jobs"},
  317. Rules: []rbacapi.PolicyRule{ruleWriteJobs},
  318. },
  319. {
  320. ObjectMeta: metav1.ObjectMeta{Name: "create-rolebindings"},
  321. Rules: []rbacapi.PolicyRule{
  322. rbachelper.NewRule("create").Groups("rbac.authorization.k8s.io").Resources("rolebindings").RuleOrDie(),
  323. },
  324. },
  325. {
  326. ObjectMeta: metav1.ObjectMeta{Name: "bind-any-clusterrole"},
  327. Rules: []rbacapi.PolicyRule{
  328. rbachelper.NewRule("bind").Groups("rbac.authorization.k8s.io").Resources("clusterroles").RuleOrDie(),
  329. },
  330. },
  331. },
  332. clusterRoleBindings: []rbacapi.ClusterRoleBinding{
  333. {
  334. ObjectMeta: metav1.ObjectMeta{Name: "write-jobs"},
  335. Subjects: []rbacapi.Subject{{Kind: "User", Name: "job-writer"}},
  336. RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "write-jobs"},
  337. },
  338. {
  339. ObjectMeta: metav1.ObjectMeta{Name: "create-rolebindings"},
  340. Subjects: []rbacapi.Subject{
  341. {Kind: "User", Name: "job-writer"},
  342. {Kind: "User", Name: "nonescalating-rolebinding-writer"},
  343. {Kind: "User", Name: "any-rolebinding-writer"},
  344. },
  345. RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "create-rolebindings"},
  346. },
  347. {
  348. ObjectMeta: metav1.ObjectMeta{Name: "bind-any-clusterrole"},
  349. Subjects: []rbacapi.Subject{{Kind: "User", Name: "any-rolebinding-writer"}},
  350. RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "bind-any-clusterrole"},
  351. },
  352. },
  353. roleBindings: []rbacapi.RoleBinding{
  354. {
  355. ObjectMeta: metav1.ObjectMeta{Name: "write-jobs", Namespace: "job-namespace"},
  356. Subjects: []rbacapi.Subject{{Kind: "User", Name: "job-writer-namespace"}},
  357. RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "write-jobs"},
  358. },
  359. {
  360. ObjectMeta: metav1.ObjectMeta{Name: "create-rolebindings", Namespace: "job-namespace"},
  361. Subjects: []rbacapi.Subject{
  362. {Kind: "User", Name: "job-writer-namespace"},
  363. {Kind: "User", Name: "any-rolebinding-writer-namespace"},
  364. },
  365. RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "create-rolebindings"},
  366. },
  367. {
  368. ObjectMeta: metav1.ObjectMeta{Name: "bind-any-clusterrole", Namespace: "job-namespace"},
  369. Subjects: []rbacapi.Subject{{Kind: "User", Name: "any-rolebinding-writer-namespace"}},
  370. RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "bind-any-clusterrole"},
  371. },
  372. },
  373. },
  374. requests: []request{
  375. // Create the namespace used later in the test
  376. {superUser, "POST", "", "namespaces", "", "", jobNamespace, http.StatusCreated},
  377. {superUser, "POST", "", "namespaces", "", "", forbiddenNamespace, http.StatusCreated},
  378. {"user-with-no-permissions", "POST", "batch", "jobs", "job-namespace", "", aJob, http.StatusForbidden},
  379. {"user-with-no-permissions", "GET", "batch", "jobs", "job-namespace", "pi", "", http.StatusForbidden},
  380. // job-writer-namespace cannot write to the "forbidden-namespace"
  381. {"job-writer-namespace", "GET", "batch", "jobs", "forbidden-namespace", "", "", http.StatusForbidden},
  382. {"job-writer-namespace", "GET", "batch", "jobs", "forbidden-namespace", "pi", "", http.StatusForbidden},
  383. {"job-writer-namespace", "POST", "batch", "jobs", "forbidden-namespace", "", aJob, http.StatusForbidden},
  384. {"job-writer-namespace", "GET", "batch", "jobs", "forbidden-namespace", "pi", "", http.StatusForbidden},
  385. // job-writer can write to any namespace
  386. {"job-writer", "GET", "batch", "jobs", "forbidden-namespace", "", "", http.StatusOK},
  387. {"job-writer", "GET", "batch", "jobs", "forbidden-namespace", "pi", "", http.StatusNotFound},
  388. {"job-writer", "POST", "batch", "jobs", "forbidden-namespace", "", aJob, http.StatusCreated},
  389. {"job-writer", "GET", "batch", "jobs", "forbidden-namespace", "pi", "", http.StatusOK},
  390. {"job-writer-namespace", "GET", "batch", "jobs", "job-namespace", "", "", http.StatusOK},
  391. {"job-writer-namespace", "GET", "batch", "jobs", "job-namespace", "pi", "", http.StatusNotFound},
  392. {"job-writer-namespace", "POST", "batch", "jobs", "job-namespace", "", aJob, http.StatusCreated},
  393. {"job-writer-namespace", "GET", "batch", "jobs", "job-namespace", "pi", "", http.StatusOK},
  394. // cannot bind role anywhere
  395. {"user-with-no-permissions", "POST", "rbac.authorization.k8s.io", "rolebindings", "job-namespace", "", writeJobsRoleBinding, http.StatusForbidden},
  396. // can only bind role in namespace where they have explicit bind permission
  397. {"any-rolebinding-writer-namespace", "POST", "rbac.authorization.k8s.io", "rolebindings", "forbidden-namespace", "", writeJobsRoleBinding, http.StatusForbidden},
  398. // can only bind role in namespace where they have covering permissions
  399. {"job-writer-namespace", "POST", "rbac.authorization.k8s.io", "rolebindings", "forbidden-namespace", "", writeJobsRoleBinding, http.StatusForbidden},
  400. {"job-writer-namespace", "POST", "rbac.authorization.k8s.io", "rolebindings", "job-namespace", "", writeJobsRoleBinding, http.StatusCreated},
  401. {superUser, "DELETE", "rbac.authorization.k8s.io", "rolebindings", "job-namespace", "pi", "", http.StatusOK},
  402. // can bind role in any namespace where they have covering permissions
  403. {"job-writer", "POST", "rbac.authorization.k8s.io", "rolebindings", "forbidden-namespace", "", writeJobsRoleBinding, http.StatusCreated},
  404. {superUser, "DELETE", "rbac.authorization.k8s.io", "rolebindings", "forbidden-namespace", "pi", "", http.StatusOK},
  405. // cannot bind role because they don't have covering permissions
  406. {"nonescalating-rolebinding-writer", "POST", "rbac.authorization.k8s.io", "rolebindings", "job-namespace", "", writeJobsRoleBinding, http.StatusForbidden},
  407. // can bind role because they have explicit bind permission
  408. {"any-rolebinding-writer", "POST", "rbac.authorization.k8s.io", "rolebindings", "job-namespace", "", writeJobsRoleBinding, http.StatusCreated},
  409. {superUser, "DELETE", "rbac.authorization.k8s.io", "rolebindings", "job-namespace", "pi", "", http.StatusOK},
  410. {"any-rolebinding-writer-namespace", "POST", "rbac.authorization.k8s.io", "rolebindings", "job-namespace", "", writeJobsRoleBinding, http.StatusCreated},
  411. {superUser, "DELETE", "rbac.authorization.k8s.io", "rolebindings", "job-namespace", "pi", "", http.StatusOK},
  412. },
  413. },
  414. {
  415. bootstrapRoles: bootstrapRoles{
  416. clusterRoles: []rbacapi.ClusterRole{
  417. {
  418. ObjectMeta: metav1.ObjectMeta{Name: "allow-all"},
  419. Rules: []rbacapi.PolicyRule{ruleAllowAll},
  420. },
  421. {
  422. ObjectMeta: metav1.ObjectMeta{Name: "update-limitranges"},
  423. Rules: []rbacapi.PolicyRule{
  424. rbachelper.NewRule("update").Groups("").Resources("limitranges").RuleOrDie(),
  425. },
  426. },
  427. },
  428. clusterRoleBindings: []rbacapi.ClusterRoleBinding{
  429. {
  430. ObjectMeta: metav1.ObjectMeta{Name: "update-limitranges"},
  431. Subjects: []rbacapi.Subject{
  432. {Kind: "User", Name: "limitrange-updater"},
  433. },
  434. RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "update-limitranges"},
  435. },
  436. },
  437. },
  438. requests: []request{
  439. // Create the namespace used later in the test
  440. {superUser, "POST", "", "namespaces", "", "", limitRangeNamespace, http.StatusCreated},
  441. {"limitrange-updater", "PUT", "", "limitranges", "limitrange-namespace", "a", aLimitRange, http.StatusForbidden},
  442. {superUser, "PUT", "", "limitranges", "limitrange-namespace", "a", aLimitRange, http.StatusCreated},
  443. {superUser, "PUT", "", "limitranges", "limitrange-namespace", "a", aLimitRange, http.StatusOK},
  444. {"limitrange-updater", "PUT", "", "limitranges", "limitrange-namespace", "a", aLimitRange, http.StatusOK},
  445. },
  446. },
  447. {
  448. bootstrapRoles: bootstrapRoles{
  449. clusterRoles: []rbacapi.ClusterRole{
  450. {
  451. ObjectMeta: metav1.ObjectMeta{Name: "allow-all"},
  452. Rules: []rbacapi.PolicyRule{ruleAllowAll},
  453. },
  454. {
  455. ObjectMeta: metav1.ObjectMeta{Name: "patch-limitranges"},
  456. Rules: []rbacapi.PolicyRule{
  457. rbachelper.NewRule("patch").Groups("").Resources("limitranges").RuleOrDie(),
  458. },
  459. },
  460. },
  461. clusterRoleBindings: []rbacapi.ClusterRoleBinding{
  462. {
  463. ObjectMeta: metav1.ObjectMeta{Name: "patch-limitranges"},
  464. Subjects: []rbacapi.Subject{
  465. {Kind: "User", Name: "limitrange-patcher"},
  466. },
  467. RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "patch-limitranges"},
  468. },
  469. },
  470. },
  471. requests: []request{
  472. // Create the namespace used later in the test
  473. {superUser, "POST", "", "namespaces", "", "", limitRangeNamespace, http.StatusCreated},
  474. {"limitrange-patcher", "PATCH", "", "limitranges", "limitrange-namespace", "a", aLimitRange, http.StatusForbidden},
  475. {superUser, "PATCH", "", "limitranges", "limitrange-namespace", "a", aLimitRange, http.StatusCreated},
  476. {superUser, "PATCH", "", "limitranges", "limitrange-namespace", "a", aLimitRange, http.StatusOK},
  477. {"limitrange-patcher", "PATCH", "", "limitranges", "limitrange-namespace", "a", aLimitRange, http.StatusOK},
  478. },
  479. },
  480. }
  481. for i, tc := range tests {
  482. // Create an API Server.
  483. masterConfig := framework.NewIntegrationTestMasterConfig()
  484. masterConfig.GenericConfig.Authorization.Authorizer = newRBACAuthorizer(t, masterConfig)
  485. masterConfig.GenericConfig.Authentication.Authenticator = bearertoken.New(tokenfile.New(map[string]*user.DefaultInfo{
  486. superUser: {Name: "admin", Groups: []string{"system:masters"}},
  487. "any-rolebinding-writer": {Name: "any-rolebinding-writer"},
  488. "any-rolebinding-writer-namespace": {Name: "any-rolebinding-writer-namespace"},
  489. "bob": {Name: "bob"},
  490. "job-writer": {Name: "job-writer"},
  491. "job-writer-namespace": {Name: "job-writer-namespace"},
  492. "nonescalating-rolebinding-writer": {Name: "nonescalating-rolebinding-writer"},
  493. "pod-reader": {Name: "pod-reader"},
  494. "limitrange-updater": {Name: "limitrange-updater"},
  495. "limitrange-patcher": {Name: "limitrange-patcher"},
  496. "user-with-no-permissions": {Name: "user-with-no-permissions"},
  497. }))
  498. masterConfig.GenericConfig.OpenAPIConfig = framework.DefaultOpenAPIConfig()
  499. _, s, closeFn := framework.RunAMaster(masterConfig)
  500. defer closeFn()
  501. clientConfig := &restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{NegotiatedSerializer: legacyscheme.Codecs}}
  502. // Bootstrap the API Server with the test case's initial roles.
  503. superuserClient, _ := clientsetForToken(superUser, clientConfig)
  504. if err := tc.bootstrapRoles.bootstrap(superuserClient); err != nil {
  505. t.Errorf("case %d: failed to apply initial roles: %v", i, err)
  506. continue
  507. }
  508. previousResourceVersion := make(map[string]float64)
  509. for j, r := range tc.requests {
  510. path := "/"
  511. if r.apiGroup == "" {
  512. path = gopath.Join(path, "api/v1")
  513. } else {
  514. path = gopath.Join(path, "apis", r.apiGroup, "v1")
  515. }
  516. if r.namespace != "" {
  517. path = gopath.Join(path, "namespaces", r.namespace)
  518. }
  519. if r.resource != "" {
  520. path = gopath.Join(path, r.resource)
  521. }
  522. if r.name != "" {
  523. path = gopath.Join(path, r.name)
  524. }
  525. var body io.Reader
  526. if r.body != "" {
  527. sub := ""
  528. if r.verb == "PUT" {
  529. // For update operations, insert previous resource version
  530. if resVersion := previousResourceVersion[getPreviousResourceVersionKey(path, "")]; resVersion != 0 {
  531. sub += fmt.Sprintf(",\"resourceVersion\": \"%v\"", resVersion)
  532. }
  533. }
  534. body = strings.NewReader(fmt.Sprintf(r.body, sub))
  535. }
  536. req, err := http.NewRequest(r.verb, s.URL+path, body)
  537. if r.verb == "PATCH" {
  538. // For patch operations, use the apply content type
  539. req.Header.Add("Content-Type", string(types.ApplyPatchType))
  540. q := req.URL.Query()
  541. q.Add("fieldManager", "rbac_test")
  542. req.URL.RawQuery = q.Encode()
  543. }
  544. if err != nil {
  545. t.Fatalf("failed to create request: %v", err)
  546. }
  547. func() {
  548. reqDump, err := httputil.DumpRequest(req, true)
  549. if err != nil {
  550. t.Fatalf("failed to dump request: %v", err)
  551. return
  552. }
  553. resp, err := clientForToken(r.token).Do(req)
  554. if err != nil {
  555. t.Errorf("case %d, req %d: failed to make request: %v", i, j, err)
  556. return
  557. }
  558. defer resp.Body.Close()
  559. respDump, err := httputil.DumpResponse(resp, true)
  560. if err != nil {
  561. t.Fatalf("failed to dump response: %v", err)
  562. return
  563. }
  564. if resp.StatusCode != r.expectedStatus {
  565. // When debugging is on, dump the entire request and response. Very helpful for
  566. // debugging malformed test cases.
  567. //
  568. // To turn on debugging, use the '-args' flag.
  569. //
  570. // go test -v -tags integration -run RBAC -args -v 10
  571. //
  572. klog.V(8).Infof("case %d, req %d: %s\n%s\n", i, j, reqDump, respDump)
  573. t.Errorf("case %d, req %d: %s expected %q got %q", i, j, r, statusCode(r.expectedStatus), statusCode(resp.StatusCode))
  574. }
  575. b, _ := ioutil.ReadAll(resp.Body)
  576. if r.verb == "POST" && (resp.StatusCode/100) == 2 {
  577. // For successful create operations, extract resourceVersion
  578. id, currentResourceVersion, err := parseResourceVersion(b)
  579. if err == nil {
  580. key := getPreviousResourceVersionKey(path, id)
  581. previousResourceVersion[key] = currentResourceVersion
  582. } else {
  583. t.Logf("error in trying to extract resource version: %s", err)
  584. }
  585. }
  586. }()
  587. }
  588. }
  589. }
  590. func TestBootstrapping(t *testing.T) {
  591. superUser := "admin/system:masters"
  592. masterConfig := framework.NewIntegrationTestMasterConfig()
  593. masterConfig.GenericConfig.Authorization.Authorizer = newRBACAuthorizer(t, masterConfig)
  594. masterConfig.GenericConfig.Authentication.Authenticator = bearertoken.New(tokenfile.New(map[string]*user.DefaultInfo{
  595. superUser: {Name: "admin", Groups: []string{"system:masters"}},
  596. }))
  597. _, s, closeFn := framework.RunAMaster(masterConfig)
  598. defer closeFn()
  599. clientset := clientset.NewForConfigOrDie(&restclient.Config{BearerToken: superUser, Host: s.URL})
  600. watcher, err := clientset.RbacV1().ClusterRoles().Watch(context.TODO(), metav1.ListOptions{ResourceVersion: "0"})
  601. if err != nil {
  602. t.Fatalf("unexpected error: %v", err)
  603. }
  604. ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
  605. defer cancel()
  606. _, err = watchtools.UntilWithoutRetry(ctx, watcher, func(event watch.Event) (bool, error) {
  607. if event.Type != watch.Added {
  608. return false, nil
  609. }
  610. return true, nil
  611. })
  612. if err != nil {
  613. t.Fatalf("unexpected error: %v", err)
  614. }
  615. clusterRoles, err := clientset.RbacV1().ClusterRoles().List(context.TODO(), metav1.ListOptions{})
  616. if err != nil {
  617. t.Fatalf("unexpected error: %v", err)
  618. }
  619. if len(clusterRoles.Items) == 0 {
  620. t.Fatalf("missing cluster roles")
  621. }
  622. for _, clusterRole := range clusterRoles.Items {
  623. if clusterRole.Name == "cluster-admin" {
  624. return
  625. }
  626. }
  627. t.Errorf("missing cluster-admin: %v", clusterRoles)
  628. healthBytes, err := clientset.Discovery().RESTClient().Get().AbsPath("/healthz/poststarthook/rbac/bootstrap-roles").DoRaw(context.TODO())
  629. if err != nil {
  630. t.Error(err)
  631. }
  632. t.Errorf("error bootstrapping roles: %s", string(healthBytes))
  633. }
  634. // TestDiscoveryUpgradeBootstrapping is primarily meant to test the behavior of
  635. // primePublicInfoClusterRoleBinding in storage_rbac.go during cluster upgrades.
  636. func TestDiscoveryUpgradeBootstrapping(t *testing.T) {
  637. var tearDownFn func()
  638. defer func() {
  639. if tearDownFn != nil {
  640. tearDownFn()
  641. }
  642. }()
  643. superUser := "admin/system:masters"
  644. masterConfig := framework.NewIntegrationTestMasterConfig()
  645. masterConfig.GenericConfig.Authorization.Authorizer = newRBACAuthorizer(t, masterConfig)
  646. masterConfig.GenericConfig.Authentication.Authenticator = bearertoken.New(tokenfile.New(map[string]*user.DefaultInfo{
  647. superUser: {Name: "admin", Groups: []string{"system:masters"}},
  648. }))
  649. _, s, tearDownFn := framework.RunAMaster(masterConfig)
  650. client := clientset.NewForConfigOrDie(&restclient.Config{BearerToken: superUser, Host: s.URL})
  651. // Modify the default RBAC discovery ClusterRoleBidnings to look more like the defaults that
  652. // existed prior to v1.14, but with user modifications.
  653. t.Logf("Modifying default `system:discovery` ClusterRoleBinding")
  654. discRoleBinding, err := client.RbacV1().ClusterRoleBindings().Get(context.TODO(), "system:discovery", metav1.GetOptions{})
  655. if err != nil {
  656. t.Fatalf("Failed to get `system:discovery` ClusterRoleBinding: %v", err)
  657. }
  658. discRoleBinding.Annotations["rbac.authorization.kubernetes.io/autoupdate"] = "false"
  659. discRoleBinding.Annotations["rbac-discovery-upgrade-test"] = "pass"
  660. discRoleBinding.Subjects = []rbacapi.Subject{
  661. {
  662. Name: "system:authenticated",
  663. Kind: "Group",
  664. APIGroup: "rbac.authorization.k8s.io",
  665. },
  666. }
  667. if discRoleBinding, err = client.RbacV1().ClusterRoleBindings().Update(context.TODO(), discRoleBinding, metav1.UpdateOptions{}); err != nil {
  668. t.Fatalf("Failed to update `system:discovery` ClusterRoleBinding: %v", err)
  669. }
  670. t.Logf("Modifying default `system:basic-user` ClusterRoleBinding")
  671. basicUserRoleBinding, err := client.RbacV1().ClusterRoleBindings().Get(context.TODO(), "system:basic-user", metav1.GetOptions{})
  672. if err != nil {
  673. t.Fatalf("Failed to get `system:basic-user` ClusterRoleBinding: %v", err)
  674. }
  675. basicUserRoleBinding.Annotations["rbac.authorization.kubernetes.io/autoupdate"] = "false"
  676. basicUserRoleBinding.Annotations["rbac-discovery-upgrade-test"] = "pass"
  677. if basicUserRoleBinding, err = client.RbacV1().ClusterRoleBindings().Update(context.TODO(), basicUserRoleBinding, metav1.UpdateOptions{}); err != nil {
  678. t.Fatalf("Failed to update `system:basic-user` ClusterRoleBinding: %v", err)
  679. }
  680. t.Logf("Deleting default `system:public-info-viewer` ClusterRoleBinding")
  681. if err = client.RbacV1().ClusterRoleBindings().Delete(context.TODO(), "system:public-info-viewer", &metav1.DeleteOptions{}); err != nil {
  682. t.Fatalf("Failed to delete `system:public-info-viewer` ClusterRoleBinding: %v", err)
  683. }
  684. // Stop the first API server.
  685. tearDownFn()
  686. tearDownFn = nil
  687. // Check that upgraded API servers inherit `system:public-info-viewer` settings from
  688. // `system:discovery`, and respect auto-reconciliation annotations.
  689. _, s, tearDownFn = framework.RunAMaster(masterConfig)
  690. client = clientset.NewForConfigOrDie(&restclient.Config{BearerToken: superUser, Host: s.URL})
  691. newDiscRoleBinding, err := client.RbacV1().ClusterRoleBindings().Get(context.TODO(), "system:discovery", metav1.GetOptions{})
  692. if err != nil {
  693. t.Fatalf("Failed to get `system:discovery` ClusterRoleBinding: %v", err)
  694. }
  695. if !reflect.DeepEqual(newDiscRoleBinding, discRoleBinding) {
  696. t.Errorf("`system:discovery` should have been unmodified. Wanted: %v, got %v", discRoleBinding, newDiscRoleBinding)
  697. }
  698. newBasicUserRoleBinding, err := client.RbacV1().ClusterRoleBindings().Get(context.TODO(), "system:basic-user", metav1.GetOptions{})
  699. if err != nil {
  700. t.Fatalf("Failed to get `system:basic-user` ClusterRoleBinding: %v", err)
  701. }
  702. if !reflect.DeepEqual(newBasicUserRoleBinding, basicUserRoleBinding) {
  703. t.Errorf("`system:basic-user` should have been unmodified. Wanted: %v, got %v", basicUserRoleBinding, newBasicUserRoleBinding)
  704. }
  705. publicInfoViewerRoleBinding, err := client.RbacV1().ClusterRoleBindings().Get(context.TODO(), "system:public-info-viewer", metav1.GetOptions{})
  706. if err != nil {
  707. t.Fatalf("Failed to get `system:public-info-viewer` ClusterRoleBinding: %v", err)
  708. }
  709. if publicInfoViewerRoleBinding.Annotations["rbac.authorization.kubernetes.io/autoupdate"] != "false" {
  710. t.Errorf("publicInfoViewerRoleBinding.Annotations[\"rbac.authorization.kubernetes.io/autoupdate\"] should be %v, got %v", publicInfoViewerRoleBinding.Annotations["rbac.authorization.kubernetes.io/autoupdate"], "false")
  711. }
  712. if publicInfoViewerRoleBinding.Annotations["rbac-discovery-upgrade-test"] != "pass" {
  713. t.Errorf("publicInfoViewerRoleBinding.Annotations[\"rbac-discovery-upgrade-test\"] should be %v, got %v", publicInfoViewerRoleBinding.Annotations["rbac-discovery-upgrade-test"], "pass")
  714. }
  715. if !reflect.DeepEqual(publicInfoViewerRoleBinding.Subjects, newDiscRoleBinding.Subjects) {
  716. t.Errorf("`system:public-info-viewer` should have inherited Subjects from `system:discovery` Wanted: %v, got %v", newDiscRoleBinding.Subjects, publicInfoViewerRoleBinding.Subjects)
  717. }
  718. }