auth_test.go 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290
  1. /*
  2. Copyright 2014 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. // This file tests authentication and (soon) authorization of HTTP requests to a master object.
  15. // It does not use the client in pkg/client/... because authentication and authorization needs
  16. // to work for any client of the HTTP interface.
  17. import (
  18. "bytes"
  19. "encoding/json"
  20. "fmt"
  21. "io/ioutil"
  22. "net/http"
  23. "net/http/httptest"
  24. "net/url"
  25. "os"
  26. "strconv"
  27. "strings"
  28. "testing"
  29. "time"
  30. authenticationv1beta1 "k8s.io/api/authentication/v1beta1"
  31. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  32. "k8s.io/apiserver/pkg/authentication/authenticator"
  33. "k8s.io/apiserver/pkg/authentication/group"
  34. "k8s.io/apiserver/pkg/authentication/request/bearertoken"
  35. "k8s.io/apiserver/pkg/authentication/serviceaccount"
  36. "k8s.io/apiserver/pkg/authentication/token/cache"
  37. "k8s.io/apiserver/pkg/authentication/user"
  38. "k8s.io/apiserver/pkg/authorization/authorizer"
  39. "k8s.io/apiserver/pkg/authorization/authorizerfactory"
  40. "k8s.io/apiserver/plugin/pkg/authenticator/token/tokentest"
  41. "k8s.io/apiserver/plugin/pkg/authenticator/token/webhook"
  42. "k8s.io/client-go/tools/clientcmd/api/v1"
  43. "k8s.io/kubernetes/pkg/api/testapi"
  44. "k8s.io/kubernetes/pkg/apis/autoscaling"
  45. api "k8s.io/kubernetes/pkg/apis/core"
  46. "k8s.io/kubernetes/pkg/apis/extensions"
  47. "k8s.io/kubernetes/pkg/auth/authorizer/abac"
  48. "k8s.io/kubernetes/test/integration"
  49. "k8s.io/kubernetes/test/integration/framework"
  50. )
  51. const (
  52. AliceToken string = "abc123" // username: alice. Present in token file.
  53. BobToken string = "xyz987" // username: bob. Present in token file.
  54. UnknownToken string = "qwerty" // Not present in token file.
  55. )
  56. func getTestTokenAuth() authenticator.Request {
  57. tokenAuthenticator := tokentest.New()
  58. tokenAuthenticator.Tokens[AliceToken] = &user.DefaultInfo{Name: "alice", UID: "1"}
  59. tokenAuthenticator.Tokens[BobToken] = &user.DefaultInfo{Name: "bob", UID: "2"}
  60. return group.NewGroupAdder(bearertoken.New(tokenAuthenticator), []string{user.AllAuthenticated})
  61. }
  62. func getTestWebhookTokenAuth(serverURL string) (authenticator.Request, error) {
  63. kubecfgFile, err := ioutil.TempFile("", "webhook-kubecfg")
  64. if err != nil {
  65. return nil, err
  66. }
  67. defer os.Remove(kubecfgFile.Name())
  68. config := v1.Config{
  69. Clusters: []v1.NamedCluster{
  70. {
  71. Cluster: v1.Cluster{Server: serverURL},
  72. },
  73. },
  74. }
  75. if err := json.NewEncoder(kubecfgFile).Encode(config); err != nil {
  76. return nil, err
  77. }
  78. webhookTokenAuth, err := webhook.New(kubecfgFile.Name(), nil)
  79. if err != nil {
  80. return nil, err
  81. }
  82. return bearertoken.New(cache.New(webhookTokenAuth, false, 2*time.Minute, 2*time.Minute)), nil
  83. }
  84. func path(resource, namespace, name string) string {
  85. return testapi.Default.ResourcePath(resource, namespace, name)
  86. }
  87. func pathWithPrefix(prefix, resource, namespace, name string) string {
  88. return testapi.Default.ResourcePathWithPrefix(prefix, resource, namespace, name)
  89. }
  90. func pathWithSubResource(resource, namespace, name, subresource string) string {
  91. return testapi.Default.SubResourcePath(resource, namespace, name, subresource)
  92. }
  93. func timeoutPath(resource, namespace, name string) string {
  94. return addTimeoutFlag(testapi.Default.ResourcePath(resource, namespace, name))
  95. }
  96. // Bodies for requests used in subsequent tests.
  97. var aPod = `
  98. {
  99. "kind": "Pod",
  100. "apiVersion": "` + testapi.Groups[api.GroupName].GroupVersion().String() + `",
  101. "metadata": {
  102. "name": "a",
  103. "creationTimestamp": null%s
  104. },
  105. "spec": {
  106. "containers": [
  107. {
  108. "name": "foo",
  109. "image": "bar/foo"
  110. }
  111. ]
  112. }
  113. }
  114. `
  115. var aRC = `
  116. {
  117. "kind": "ReplicationController",
  118. "apiVersion": "` + testapi.Groups[api.GroupName].GroupVersion().String() + `",
  119. "metadata": {
  120. "name": "a",
  121. "labels": {
  122. "name": "a"
  123. }%s
  124. },
  125. "spec": {
  126. "replicas": 2,
  127. "selector": {
  128. "name": "a"
  129. },
  130. "template": {
  131. "metadata": {
  132. "labels": {
  133. "name": "a"
  134. }
  135. },
  136. "spec": {
  137. "containers": [
  138. {
  139. "name": "foo",
  140. "image": "bar/foo"
  141. }
  142. ]
  143. }
  144. }
  145. }
  146. }
  147. `
  148. var aService = `
  149. {
  150. "kind": "Service",
  151. "apiVersion": "` + testapi.Groups[api.GroupName].GroupVersion().String() + `",
  152. "metadata": {
  153. "name": "a",
  154. "labels": {
  155. "name": "a"
  156. }%s
  157. },
  158. "spec": {
  159. "ports": [
  160. {
  161. "protocol": "TCP",
  162. "port": 8000
  163. }
  164. ],
  165. "selector": {
  166. "name": "a"
  167. },
  168. "clusterIP": "10.0.0.100"
  169. }
  170. }
  171. `
  172. var aNode = `
  173. {
  174. "kind": "Node",
  175. "apiVersion": "` + testapi.Groups[api.GroupName].GroupVersion().String() + `",
  176. "metadata": {
  177. "name": "a"%s
  178. },
  179. "spec": {
  180. "externalID": "external"
  181. }
  182. }
  183. `
  184. func aEvent(namespace string) string {
  185. return `
  186. {
  187. "kind": "Event",
  188. "apiVersion": "` + testapi.Groups[api.GroupName].GroupVersion().String() + `",
  189. "metadata": {
  190. "name": "a"%s
  191. },
  192. "involvedObject": {
  193. "kind": "Pod",
  194. "namespace": "` + namespace + `",
  195. "name": "a",
  196. "apiVersion": "v1"
  197. }
  198. }
  199. `
  200. }
  201. var aBinding = `
  202. {
  203. "kind": "Binding",
  204. "apiVersion": "` + testapi.Groups[api.GroupName].GroupVersion().String() + `",
  205. "metadata": {
  206. "name": "a"%s
  207. },
  208. "target": {
  209. "name": "10.10.10.10"
  210. }
  211. }
  212. `
  213. var emptyEndpoints = `
  214. {
  215. "kind": "Endpoints",
  216. "apiVersion": "v1",
  217. "metadata": {
  218. "name": "a"%s
  219. }
  220. }
  221. `
  222. var aEndpoints = `
  223. {
  224. "kind": "Endpoints",
  225. "apiVersion": "` + testapi.Groups[api.GroupName].GroupVersion().String() + `",
  226. "metadata": {
  227. "name": "a"%s
  228. },
  229. "subsets": [
  230. {
  231. "addresses": [
  232. {
  233. "ip": "10.10.1.1"
  234. }
  235. ],
  236. "ports": [
  237. {
  238. "port": 1909,
  239. "protocol": "TCP"
  240. }
  241. ]
  242. }
  243. ]
  244. }
  245. `
  246. var deleteNow = `
  247. {
  248. "kind": "DeleteOptions",
  249. "apiVersion": "` + testapi.Groups[api.GroupName].GroupVersion().String() + `",
  250. "gracePeriodSeconds": 0%s
  251. }
  252. `
  253. // To ensure that a POST completes before a dependent GET, set a timeout.
  254. func addTimeoutFlag(URLString string) string {
  255. u, _ := url.Parse(URLString)
  256. values := u.Query()
  257. values.Set("timeout", "60s")
  258. u.RawQuery = values.Encode()
  259. return u.String()
  260. }
  261. func getTestRequests(namespace string) []struct {
  262. verb string
  263. URL string
  264. body string
  265. statusCodes map[int]bool // allowed status codes.
  266. } {
  267. requests := []struct {
  268. verb string
  269. URL string
  270. body string
  271. statusCodes map[int]bool // Set of expected resp.StatusCode if all goes well.
  272. }{
  273. // Normal methods on pods
  274. {"GET", path("pods", "", ""), "", integration.Code200},
  275. {"GET", path("pods", namespace, ""), "", integration.Code200},
  276. {"POST", timeoutPath("pods", namespace, ""), aPod, integration.Code201},
  277. {"PUT", timeoutPath("pods", namespace, "a"), aPod, integration.Code200},
  278. {"GET", path("pods", namespace, "a"), "", integration.Code200},
  279. // GET and POST for /exec should return Bad Request (400) since the pod has not been assigned a node yet.
  280. {"GET", path("pods", namespace, "a") + "/exec", "", integration.Code400},
  281. {"POST", path("pods", namespace, "a") + "/exec", "", integration.Code400},
  282. // PUT for /exec should return Method Not Allowed (405).
  283. {"PUT", path("pods", namespace, "a") + "/exec", "", integration.Code405},
  284. // GET and POST for /portforward should return Bad Request (400) since the pod has not been assigned a node yet.
  285. {"GET", path("pods", namespace, "a") + "/portforward", "", integration.Code400},
  286. {"POST", path("pods", namespace, "a") + "/portforward", "", integration.Code400},
  287. // PUT for /portforward should return Method Not Allowed (405).
  288. {"PUT", path("pods", namespace, "a") + "/portforward", "", integration.Code405},
  289. {"PATCH", path("pods", namespace, "a"), "{%v}", integration.Code200},
  290. {"DELETE", timeoutPath("pods", namespace, "a"), deleteNow, integration.Code200},
  291. // Non-standard methods (not expected to work,
  292. // but expected to pass/fail authorization prior to
  293. // failing validation.
  294. {"OPTIONS", path("pods", namespace, ""), "", integration.Code405},
  295. {"OPTIONS", path("pods", namespace, "a"), "", integration.Code405},
  296. {"HEAD", path("pods", namespace, ""), "", integration.Code405},
  297. {"HEAD", path("pods", namespace, "a"), "", integration.Code405},
  298. {"TRACE", path("pods", namespace, ""), "", integration.Code405},
  299. {"TRACE", path("pods", namespace, "a"), "", integration.Code405},
  300. {"NOSUCHVERB", path("pods", namespace, ""), "", integration.Code405},
  301. // Normal methods on services
  302. {"GET", path("services", "", ""), "", integration.Code200},
  303. {"GET", path("services", namespace, ""), "", integration.Code200},
  304. {"POST", timeoutPath("services", namespace, ""), aService, integration.Code201},
  305. // Create an endpoint for the service (this is done automatically by endpoint controller
  306. // whenever a service is created, but this test does not run that controller)
  307. {"POST", timeoutPath("endpoints", namespace, ""), emptyEndpoints, integration.Code201},
  308. // Should return service unavailable when endpoint.subset is empty.
  309. {"GET", pathWithSubResource("services", namespace, "a", "proxy") + "/", "", integration.Code503},
  310. {"PUT", timeoutPath("services", namespace, "a"), aService, integration.Code200},
  311. {"GET", path("services", namespace, "a"), "", integration.Code200},
  312. {"DELETE", timeoutPath("endpoints", namespace, "a"), "", integration.Code200},
  313. {"DELETE", timeoutPath("services", namespace, "a"), "", integration.Code200},
  314. // Normal methods on replicationControllers
  315. {"GET", path("replicationControllers", "", ""), "", integration.Code200},
  316. {"GET", path("replicationControllers", namespace, ""), "", integration.Code200},
  317. {"POST", timeoutPath("replicationControllers", namespace, ""), aRC, integration.Code201},
  318. {"PUT", timeoutPath("replicationControllers", namespace, "a"), aRC, integration.Code200},
  319. {"GET", path("replicationControllers", namespace, "a"), "", integration.Code200},
  320. {"DELETE", timeoutPath("replicationControllers", namespace, "a"), "", integration.Code200},
  321. // Normal methods on endpoints
  322. {"GET", path("endpoints", "", ""), "", integration.Code200},
  323. {"GET", path("endpoints", namespace, ""), "", integration.Code200},
  324. {"POST", timeoutPath("endpoints", namespace, ""), aEndpoints, integration.Code201},
  325. {"PUT", timeoutPath("endpoints", namespace, "a"), aEndpoints, integration.Code200},
  326. {"GET", path("endpoints", namespace, "a"), "", integration.Code200},
  327. {"DELETE", timeoutPath("endpoints", namespace, "a"), "", integration.Code200},
  328. // Normal methods on nodes
  329. {"GET", path("nodes", "", ""), "", integration.Code200},
  330. {"POST", timeoutPath("nodes", "", ""), aNode, integration.Code201},
  331. {"PUT", timeoutPath("nodes", "", "a"), aNode, integration.Code200},
  332. {"GET", path("nodes", "", "a"), "", integration.Code200},
  333. {"DELETE", timeoutPath("nodes", "", "a"), "", integration.Code200},
  334. // Normal methods on events
  335. {"GET", path("events", "", ""), "", integration.Code200},
  336. {"GET", path("events", namespace, ""), "", integration.Code200},
  337. {"POST", timeoutPath("events", namespace, ""), aEvent(namespace), integration.Code201},
  338. {"PUT", timeoutPath("events", namespace, "a"), aEvent(namespace), integration.Code200},
  339. {"GET", path("events", namespace, "a"), "", integration.Code200},
  340. {"DELETE", timeoutPath("events", namespace, "a"), "", integration.Code200},
  341. // Normal methods on bindings
  342. {"GET", path("bindings", namespace, ""), "", integration.Code405},
  343. {"POST", timeoutPath("pods", namespace, ""), aPod, integration.Code201}, // Need a pod to bind or you get a 404
  344. {"POST", timeoutPath("bindings", namespace, ""), aBinding, integration.Code201},
  345. {"PUT", timeoutPath("bindings", namespace, "a"), aBinding, integration.Code404},
  346. {"GET", path("bindings", namespace, "a"), "", integration.Code404}, // No bindings instances
  347. {"DELETE", timeoutPath("bindings", namespace, "a"), "", integration.Code404},
  348. // Non-existent object type.
  349. {"GET", path("foo", "", ""), "", integration.Code404},
  350. {"POST", path("foo", namespace, ""), `{"foo": "foo"}`, integration.Code404},
  351. {"PUT", path("foo", namespace, "a"), `{"foo": "foo"}`, integration.Code404},
  352. {"GET", path("foo", namespace, "a"), "", integration.Code404},
  353. {"DELETE", timeoutPath("foo", namespace, ""), "", integration.Code404},
  354. // Special verbs on nodes
  355. {"GET", pathWithSubResource("nodes", namespace, "a", "proxy"), "", integration.Code404},
  356. {"GET", pathWithPrefix("redirect", "nodes", namespace, "a"), "", integration.Code404},
  357. // TODO: test .../watch/..., which doesn't end before the test timeout.
  358. // TODO: figure out how to create a node so that it can successfully proxy/redirect.
  359. // Non-object endpoints
  360. {"GET", "/", "", integration.Code200},
  361. {"GET", "/api", "", integration.Code200},
  362. {"GET", "/healthz", "", integration.Code200},
  363. {"GET", "/version", "", integration.Code200},
  364. {"GET", "/invalidURL", "", integration.Code404},
  365. }
  366. return requests
  367. }
  368. // The TestAuthMode* tests a large number of URLs and checks that they
  369. // are FORBIDDEN or not, depending on the mode. They do not attempt to do
  370. // detailed verification of behaviour beyond authorization. They are not
  371. // fuzz tests.
  372. //
  373. // TODO(etune): write a fuzz test of the REST API.
  374. func TestAuthModeAlwaysAllow(t *testing.T) {
  375. // Set up a master
  376. masterConfig := framework.NewIntegrationTestMasterConfig()
  377. _, s, closeFn := framework.RunAMaster(masterConfig)
  378. defer closeFn()
  379. ns := framework.CreateTestingNamespace("auth-always-allow", s, t)
  380. defer framework.DeleteTestingNamespace(ns, s, t)
  381. transport := http.DefaultTransport
  382. previousResourceVersion := make(map[string]float64)
  383. for _, r := range getTestRequests(ns.Name) {
  384. var bodyStr string
  385. if r.body != "" {
  386. sub := ""
  387. if r.verb == "PUT" {
  388. // For update operations, insert previous resource version
  389. if resVersion := previousResourceVersion[getPreviousResourceVersionKey(r.URL, "")]; resVersion != 0 {
  390. sub += fmt.Sprintf(",\r\n\"resourceVersion\": \"%v\"", resVersion)
  391. }
  392. sub += fmt.Sprintf(",\r\n\"namespace\": %q", ns.Name)
  393. }
  394. bodyStr = fmt.Sprintf(r.body, sub)
  395. }
  396. r.body = bodyStr
  397. bodyBytes := bytes.NewReader([]byte(bodyStr))
  398. req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes)
  399. if err != nil {
  400. t.Logf("case %v", r)
  401. t.Fatalf("unexpected error: %v", err)
  402. }
  403. if r.verb == "PATCH" {
  404. req.Header.Set("Content-Type", "application/merge-patch+json")
  405. }
  406. func() {
  407. resp, err := transport.RoundTrip(req)
  408. defer resp.Body.Close()
  409. if err != nil {
  410. t.Logf("case %v", r)
  411. t.Fatalf("unexpected error: %v", err)
  412. }
  413. b, _ := ioutil.ReadAll(resp.Body)
  414. if _, ok := r.statusCodes[resp.StatusCode]; !ok {
  415. t.Logf("case %v", r)
  416. t.Errorf("Expected status one of %v, but got %v", r.statusCodes, resp.StatusCode)
  417. t.Errorf("Body: %v", string(b))
  418. } else {
  419. if r.verb == "POST" {
  420. // For successful create operations, extract resourceVersion
  421. id, currentResourceVersion, err := parseResourceVersion(b)
  422. if err == nil {
  423. key := getPreviousResourceVersionKey(r.URL, id)
  424. previousResourceVersion[key] = currentResourceVersion
  425. } else {
  426. t.Logf("error in trying to extract resource version: %s", err)
  427. }
  428. }
  429. }
  430. }()
  431. }
  432. }
  433. func parseResourceVersion(response []byte) (string, float64, error) {
  434. var resultBodyMap map[string]interface{}
  435. err := json.Unmarshal(response, &resultBodyMap)
  436. if err != nil {
  437. return "", 0, fmt.Errorf("unexpected error unmarshaling resultBody: %v", err)
  438. }
  439. metadata, ok := resultBodyMap["metadata"].(map[string]interface{})
  440. if !ok {
  441. return "", 0, fmt.Errorf("unexpected error, metadata not found in JSON response: %v", string(response))
  442. }
  443. id, ok := metadata["name"].(string)
  444. if !ok {
  445. return "", 0, fmt.Errorf("unexpected error, id not found in JSON response: %v", string(response))
  446. }
  447. resourceVersionString, ok := metadata["resourceVersion"].(string)
  448. if !ok {
  449. return "", 0, fmt.Errorf("unexpected error, resourceVersion not found in JSON response: %v", string(response))
  450. }
  451. resourceVersion, err := strconv.ParseFloat(resourceVersionString, 64)
  452. if err != nil {
  453. return "", 0, fmt.Errorf("unexpected error, could not parse resourceVersion as float64, err: %s. JSON response: %v", err, string(response))
  454. }
  455. return id, resourceVersion, nil
  456. }
  457. func getPreviousResourceVersionKey(url, id string) string {
  458. baseURL := strings.Split(url, "?")[0]
  459. key := baseURL
  460. if id != "" {
  461. key = fmt.Sprintf("%s/%v", baseURL, id)
  462. }
  463. return key
  464. }
  465. func TestAuthModeAlwaysDeny(t *testing.T) {
  466. // Set up a master
  467. masterConfig := framework.NewIntegrationTestMasterConfig()
  468. masterConfig.GenericConfig.Authorization.Authorizer = authorizerfactory.NewAlwaysDenyAuthorizer()
  469. _, s, closeFn := framework.RunAMaster(masterConfig)
  470. defer closeFn()
  471. ns := framework.CreateTestingNamespace("auth-always-deny", s, t)
  472. defer framework.DeleteTestingNamespace(ns, s, t)
  473. transport := http.DefaultTransport
  474. for _, r := range getTestRequests(ns.Name) {
  475. bodyBytes := bytes.NewReader([]byte(r.body))
  476. req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes)
  477. if err != nil {
  478. t.Logf("case %v", r)
  479. t.Fatalf("unexpected error: %v", err)
  480. }
  481. func() {
  482. resp, err := transport.RoundTrip(req)
  483. defer resp.Body.Close()
  484. if err != nil {
  485. t.Logf("case %v", r)
  486. t.Fatalf("unexpected error: %v", err)
  487. }
  488. if resp.StatusCode != http.StatusForbidden {
  489. t.Logf("case %v", r)
  490. t.Errorf("Expected status Forbidden but got status %v", resp.Status)
  491. }
  492. }()
  493. }
  494. }
  495. // Inject into master an authorizer that uses user info.
  496. // TODO(etune): remove this test once a more comprehensive built-in authorizer is implemented.
  497. type allowAliceAuthorizer struct{}
  498. func (allowAliceAuthorizer) Authorize(a authorizer.Attributes) (authorizer.Decision, string, error) {
  499. if a.GetUser() != nil && a.GetUser().GetName() == "alice" {
  500. return authorizer.DecisionAllow, "", nil
  501. }
  502. return authorizer.DecisionNoOpinion, "I can't allow that. Go ask alice.", nil
  503. }
  504. // TestAliceNotForbiddenOrUnauthorized tests a user who is known to
  505. // the authentication system and authorized to do any actions.
  506. func TestAliceNotForbiddenOrUnauthorized(t *testing.T) {
  507. // This file has alice and bob in it.
  508. // Set up a master
  509. masterConfig := framework.NewIntegrationTestMasterConfig()
  510. masterConfig.GenericConfig.Authentication.Authenticator = getTestTokenAuth()
  511. masterConfig.GenericConfig.Authorization.Authorizer = allowAliceAuthorizer{}
  512. _, s, closeFn := framework.RunAMaster(masterConfig)
  513. defer closeFn()
  514. ns := framework.CreateTestingNamespace("auth-alice-not-forbidden", s, t)
  515. defer framework.DeleteTestingNamespace(ns, s, t)
  516. previousResourceVersion := make(map[string]float64)
  517. transport := http.DefaultTransport
  518. for _, r := range getTestRequests(ns.Name) {
  519. token := AliceToken
  520. var bodyStr string
  521. if r.body != "" {
  522. sub := ""
  523. if r.verb == "PUT" {
  524. // For update operations, insert previous resource version
  525. if resVersion := previousResourceVersion[getPreviousResourceVersionKey(r.URL, "")]; resVersion != 0 {
  526. sub += fmt.Sprintf(",\r\n\"resourceVersion\": \"%v\"", resVersion)
  527. }
  528. sub += fmt.Sprintf(",\r\n\"namespace\": %q", ns.Name)
  529. }
  530. bodyStr = fmt.Sprintf(r.body, sub)
  531. }
  532. r.body = bodyStr
  533. bodyBytes := bytes.NewReader([]byte(bodyStr))
  534. req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes)
  535. if err != nil {
  536. t.Fatalf("unexpected error: %v", err)
  537. }
  538. req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
  539. if r.verb == "PATCH" {
  540. req.Header.Set("Content-Type", "application/merge-patch+json")
  541. }
  542. func() {
  543. resp, err := transport.RoundTrip(req)
  544. defer resp.Body.Close()
  545. if err != nil {
  546. t.Logf("case %v", r)
  547. t.Fatalf("unexpected error: %v", err)
  548. }
  549. b, _ := ioutil.ReadAll(resp.Body)
  550. if _, ok := r.statusCodes[resp.StatusCode]; !ok {
  551. t.Logf("case %v", r)
  552. t.Errorf("Expected status one of %v, but got %v", r.statusCodes, resp.StatusCode)
  553. t.Errorf("Body: %v", string(b))
  554. } else {
  555. if r.verb == "POST" {
  556. // For successful create operations, extract resourceVersion
  557. id, currentResourceVersion, err := parseResourceVersion(b)
  558. if err == nil {
  559. key := getPreviousResourceVersionKey(r.URL, id)
  560. previousResourceVersion[key] = currentResourceVersion
  561. }
  562. }
  563. }
  564. }()
  565. }
  566. }
  567. // TestBobIsForbidden tests that a user who is known to
  568. // the authentication system but not authorized to do any actions
  569. // should receive "Forbidden".
  570. func TestBobIsForbidden(t *testing.T) {
  571. // This file has alice and bob in it.
  572. masterConfig := framework.NewIntegrationTestMasterConfig()
  573. masterConfig.GenericConfig.Authentication.Authenticator = getTestTokenAuth()
  574. masterConfig.GenericConfig.Authorization.Authorizer = allowAliceAuthorizer{}
  575. _, s, closeFn := framework.RunAMaster(masterConfig)
  576. defer closeFn()
  577. ns := framework.CreateTestingNamespace("auth-bob-forbidden", s, t)
  578. defer framework.DeleteTestingNamespace(ns, s, t)
  579. transport := http.DefaultTransport
  580. for _, r := range getTestRequests(ns.Name) {
  581. token := BobToken
  582. bodyBytes := bytes.NewReader([]byte(r.body))
  583. req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes)
  584. if err != nil {
  585. t.Fatalf("unexpected error: %v", err)
  586. }
  587. req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
  588. func() {
  589. resp, err := transport.RoundTrip(req)
  590. defer resp.Body.Close()
  591. if err != nil {
  592. t.Logf("case %v", r)
  593. t.Fatalf("unexpected error: %v", err)
  594. }
  595. // Expect all of bob's actions to return Forbidden
  596. if resp.StatusCode != http.StatusForbidden {
  597. t.Logf("case %v", r)
  598. t.Errorf("Expected not status Forbidden, but got %s", resp.Status)
  599. }
  600. }()
  601. }
  602. }
  603. // TestUnknownUserIsUnauthorized tests that a user who is unknown
  604. // to the authentication system get status code "Unauthorized".
  605. // An authorization module is installed in this scenario for integration
  606. // test purposes, but requests aren't expected to reach it.
  607. func TestUnknownUserIsUnauthorized(t *testing.T) {
  608. // This file has alice and bob in it.
  609. // Set up a master
  610. masterConfig := framework.NewIntegrationTestMasterConfig()
  611. masterConfig.GenericConfig.Authentication.Authenticator = getTestTokenAuth()
  612. masterConfig.GenericConfig.Authorization.Authorizer = allowAliceAuthorizer{}
  613. _, s, closeFn := framework.RunAMaster(masterConfig)
  614. defer closeFn()
  615. ns := framework.CreateTestingNamespace("auth-unknown-unauthorized", s, t)
  616. defer framework.DeleteTestingNamespace(ns, s, t)
  617. transport := http.DefaultTransport
  618. for _, r := range getTestRequests(ns.Name) {
  619. token := UnknownToken
  620. bodyBytes := bytes.NewReader([]byte(r.body))
  621. req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes)
  622. if err != nil {
  623. t.Fatalf("unexpected error: %v", err)
  624. }
  625. req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
  626. func() {
  627. resp, err := transport.RoundTrip(req)
  628. defer resp.Body.Close()
  629. if err != nil {
  630. t.Logf("case %v", r)
  631. t.Fatalf("unexpected error: %v", err)
  632. }
  633. // Expect all of unauthenticated user's request to be "Unauthorized"
  634. if resp.StatusCode != http.StatusUnauthorized {
  635. t.Logf("case %v", r)
  636. t.Errorf("Expected status %v, but got %v", http.StatusUnauthorized, resp.StatusCode)
  637. b, _ := ioutil.ReadAll(resp.Body)
  638. t.Errorf("Body: %v", string(b))
  639. }
  640. }()
  641. }
  642. }
  643. type impersonateAuthorizer struct{}
  644. // alice can't act as anyone and bob can't do anything but act-as someone
  645. func (impersonateAuthorizer) Authorize(a authorizer.Attributes) (authorizer.Decision, string, error) {
  646. // alice can impersonate service accounts and do other actions
  647. if a.GetUser() != nil && a.GetUser().GetName() == "alice" && a.GetVerb() == "impersonate" && a.GetResource() == "serviceaccounts" {
  648. return authorizer.DecisionAllow, "", nil
  649. }
  650. if a.GetUser() != nil && a.GetUser().GetName() == "alice" && a.GetVerb() != "impersonate" {
  651. return authorizer.DecisionAllow, "", nil
  652. }
  653. // bob can impersonate anyone, but that it
  654. if a.GetUser() != nil && a.GetUser().GetName() == "bob" && a.GetVerb() == "impersonate" {
  655. return authorizer.DecisionAllow, "", nil
  656. }
  657. // service accounts can do everything
  658. if a.GetUser() != nil && strings.HasPrefix(a.GetUser().GetName(), serviceaccount.ServiceAccountUsernamePrefix) {
  659. return authorizer.DecisionAllow, "", nil
  660. }
  661. return authorizer.DecisionNoOpinion, "I can't allow that. Go ask alice.", nil
  662. }
  663. func TestImpersonateIsForbidden(t *testing.T) {
  664. // Set up a master
  665. masterConfig := framework.NewIntegrationTestMasterConfig()
  666. masterConfig.GenericConfig.Authentication.Authenticator = getTestTokenAuth()
  667. masterConfig.GenericConfig.Authorization.Authorizer = impersonateAuthorizer{}
  668. _, s, closeFn := framework.RunAMaster(masterConfig)
  669. defer closeFn()
  670. ns := framework.CreateTestingNamespace("auth-impersonate-forbidden", s, t)
  671. defer framework.DeleteTestingNamespace(ns, s, t)
  672. transport := http.DefaultTransport
  673. // bob can't perform actions himself
  674. for _, r := range getTestRequests(ns.Name) {
  675. token := BobToken
  676. bodyBytes := bytes.NewReader([]byte(r.body))
  677. req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes)
  678. if err != nil {
  679. t.Fatalf("unexpected error: %v", err)
  680. }
  681. req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
  682. func() {
  683. resp, err := transport.RoundTrip(req)
  684. defer resp.Body.Close()
  685. if err != nil {
  686. t.Logf("case %v", r)
  687. t.Fatalf("unexpected error: %v", err)
  688. }
  689. // Expect all of bob's actions to return Forbidden
  690. if resp.StatusCode != http.StatusForbidden {
  691. t.Logf("case %v", r)
  692. t.Errorf("Expected not status Forbidden, but got %s", resp.Status)
  693. }
  694. }()
  695. }
  696. // bob can impersonate alice to do other things
  697. for _, r := range getTestRequests(ns.Name) {
  698. token := BobToken
  699. bodyBytes := bytes.NewReader([]byte(r.body))
  700. req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes)
  701. if err != nil {
  702. t.Fatalf("unexpected error: %v", err)
  703. }
  704. req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
  705. req.Header.Set("Impersonate-User", "alice")
  706. func() {
  707. resp, err := transport.RoundTrip(req)
  708. defer resp.Body.Close()
  709. if err != nil {
  710. t.Logf("case %v", r)
  711. t.Fatalf("unexpected error: %v", err)
  712. }
  713. // Expect all the requests to be allowed, don't care what they actually do
  714. if resp.StatusCode == http.StatusForbidden {
  715. t.Logf("case %v", r)
  716. t.Errorf("Expected status not %v, but got %v", http.StatusForbidden, resp.StatusCode)
  717. }
  718. }()
  719. }
  720. // alice can't impersonate bob
  721. for _, r := range getTestRequests(ns.Name) {
  722. token := AliceToken
  723. bodyBytes := bytes.NewReader([]byte(r.body))
  724. req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes)
  725. if err != nil {
  726. t.Fatalf("unexpected error: %v", err)
  727. }
  728. req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
  729. req.Header.Set("Impersonate-User", "bob")
  730. func() {
  731. resp, err := transport.RoundTrip(req)
  732. defer resp.Body.Close()
  733. if err != nil {
  734. t.Logf("case %v", r)
  735. t.Fatalf("unexpected error: %v", err)
  736. }
  737. // Expect all of bob's actions to return Forbidden
  738. if resp.StatusCode != http.StatusForbidden {
  739. t.Logf("case %v", r)
  740. t.Errorf("Expected not status Forbidden, but got %s", resp.Status)
  741. }
  742. }()
  743. }
  744. // alice can impersonate a service account
  745. for _, r := range getTestRequests(ns.Name) {
  746. token := BobToken
  747. bodyBytes := bytes.NewReader([]byte(r.body))
  748. req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes)
  749. if err != nil {
  750. t.Fatalf("unexpected error: %v", err)
  751. }
  752. req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
  753. req.Header.Set("Impersonate-User", serviceaccount.MakeUsername("default", "default"))
  754. func() {
  755. resp, err := transport.RoundTrip(req)
  756. defer resp.Body.Close()
  757. if err != nil {
  758. t.Logf("case %v", r)
  759. t.Fatalf("unexpected error: %v", err)
  760. }
  761. // Expect all the requests to be allowed, don't care what they actually do
  762. if resp.StatusCode == http.StatusForbidden {
  763. t.Logf("case %v", r)
  764. t.Errorf("Expected status not %v, but got %v", http.StatusForbidden, resp.StatusCode)
  765. }
  766. }()
  767. }
  768. }
  769. func newAuthorizerWithContents(t *testing.T, contents string) authorizer.Authorizer {
  770. f, err := ioutil.TempFile("", "auth_test")
  771. if err != nil {
  772. t.Fatalf("unexpected error creating policyfile: %v", err)
  773. }
  774. f.Close()
  775. defer os.Remove(f.Name())
  776. if err := ioutil.WriteFile(f.Name(), []byte(contents), 0700); err != nil {
  777. t.Fatalf("unexpected error writing policyfile: %v", err)
  778. }
  779. pl, err := abac.NewFromFile(f.Name())
  780. if err != nil {
  781. t.Fatalf("unexpected error creating authorizer from policyfile: %v", err)
  782. }
  783. return pl
  784. }
  785. type trackingAuthorizer struct {
  786. requestAttributes []authorizer.Attributes
  787. }
  788. func (a *trackingAuthorizer) Authorize(attributes authorizer.Attributes) (authorizer.Decision, string, error) {
  789. a.requestAttributes = append(a.requestAttributes, attributes)
  790. return authorizer.DecisionAllow, "", nil
  791. }
  792. // TestAuthorizationAttributeDetermination tests that authorization attributes are built correctly
  793. func TestAuthorizationAttributeDetermination(t *testing.T) {
  794. trackingAuthorizer := &trackingAuthorizer{}
  795. // Set up a master
  796. masterConfig := framework.NewIntegrationTestMasterConfig()
  797. masterConfig.GenericConfig.Authentication.Authenticator = getTestTokenAuth()
  798. masterConfig.GenericConfig.Authorization.Authorizer = trackingAuthorizer
  799. _, s, closeFn := framework.RunAMaster(masterConfig)
  800. defer closeFn()
  801. ns := framework.CreateTestingNamespace("auth-attribute-determination", s, t)
  802. defer framework.DeleteTestingNamespace(ns, s, t)
  803. transport := http.DefaultTransport
  804. requests := map[string]struct {
  805. verb string
  806. URL string
  807. expectedAttributes authorizer.Attributes
  808. }{
  809. "prefix/version/resource": {"GET", "/api/v1/pods", authorizer.AttributesRecord{APIGroup: api.GroupName, Resource: "pods"}},
  810. "prefix/group/version/resource": {"GET", "/apis/extensions/v1/pods", authorizer.AttributesRecord{APIGroup: extensions.GroupName, Resource: "pods"}},
  811. "prefix/group/version/resource2": {"GET", "/apis/autoscaling/v1/horizontalpodautoscalers", authorizer.AttributesRecord{APIGroup: autoscaling.GroupName, Resource: "horizontalpodautoscalers"}},
  812. }
  813. currentAuthorizationAttributesIndex := 0
  814. for testName, r := range requests {
  815. token := BobToken
  816. req, err := http.NewRequest(r.verb, s.URL+r.URL, nil)
  817. if err != nil {
  818. t.Logf("case %v", testName)
  819. t.Fatalf("unexpected error: %v", err)
  820. }
  821. req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
  822. func() {
  823. resp, err := transport.RoundTrip(req)
  824. defer resp.Body.Close()
  825. if err != nil {
  826. t.Logf("case %v", r)
  827. t.Fatalf("unexpected error: %v", err)
  828. }
  829. found := false
  830. for i := currentAuthorizationAttributesIndex; i < len(trackingAuthorizer.requestAttributes); i++ {
  831. if trackingAuthorizer.requestAttributes[i].GetAPIGroup() == r.expectedAttributes.GetAPIGroup() &&
  832. trackingAuthorizer.requestAttributes[i].GetResource() == r.expectedAttributes.GetResource() {
  833. found = true
  834. break
  835. }
  836. t.Logf("%#v did not match %#v", r.expectedAttributes, trackingAuthorizer.requestAttributes[i].(*authorizer.AttributesRecord))
  837. }
  838. if !found {
  839. t.Errorf("did not find %#v in %#v", r.expectedAttributes, trackingAuthorizer.requestAttributes[currentAuthorizationAttributesIndex:])
  840. }
  841. currentAuthorizationAttributesIndex = len(trackingAuthorizer.requestAttributes)
  842. }()
  843. }
  844. }
  845. // TestNamespaceAuthorization tests that authorization can be controlled
  846. // by namespace.
  847. func TestNamespaceAuthorization(t *testing.T) {
  848. // This file has alice and bob in it.
  849. a := newAuthorizerWithContents(t, `{"namespace": "auth-namespace"}
  850. `)
  851. // Set up a master
  852. masterConfig := framework.NewIntegrationTestMasterConfig()
  853. masterConfig.GenericConfig.Authentication.Authenticator = getTestTokenAuth()
  854. masterConfig.GenericConfig.Authorization.Authorizer = a
  855. _, s, closeFn := framework.RunAMaster(masterConfig)
  856. defer closeFn()
  857. ns := framework.CreateTestingNamespace("auth-namespace", s, t)
  858. defer framework.DeleteTestingNamespace(ns, s, t)
  859. previousResourceVersion := make(map[string]float64)
  860. transport := http.DefaultTransport
  861. requests := []struct {
  862. verb string
  863. URL string
  864. namespace string
  865. body string
  866. statusCodes map[int]bool // allowed status codes.
  867. }{
  868. {"POST", timeoutPath("pods", ns.Name, ""), "foo", aPod, integration.Code201},
  869. {"GET", path("pods", ns.Name, ""), "foo", "", integration.Code200},
  870. {"GET", path("pods", ns.Name, "a"), "foo", "", integration.Code200},
  871. {"DELETE", timeoutPath("pods", ns.Name, "a"), "foo", "", integration.Code200},
  872. {"POST", timeoutPath("pods", "foo", ""), "bar", aPod, integration.Code403},
  873. {"GET", path("pods", "foo", ""), "bar", "", integration.Code403},
  874. {"GET", path("pods", "foo", "a"), "bar", "", integration.Code403},
  875. {"DELETE", timeoutPath("pods", "foo", "a"), "bar", "", integration.Code403},
  876. {"POST", timeoutPath("pods", metav1.NamespaceDefault, ""), "", aPod, integration.Code403},
  877. {"GET", path("pods", "", ""), "", "", integration.Code403},
  878. {"GET", path("pods", metav1.NamespaceDefault, "a"), "", "", integration.Code403},
  879. {"DELETE", timeoutPath("pods", metav1.NamespaceDefault, "a"), "", "", integration.Code403},
  880. }
  881. for _, r := range requests {
  882. token := BobToken
  883. var bodyStr string
  884. if r.body != "" {
  885. sub := ""
  886. if r.verb == "PUT" && r.body != "" {
  887. // For update operations, insert previous resource version
  888. if resVersion := previousResourceVersion[getPreviousResourceVersionKey(r.URL, "")]; resVersion != 0 {
  889. sub += fmt.Sprintf(",\r\n\"resourceVersion\": \"%v\"", resVersion)
  890. }
  891. namespace := r.namespace
  892. // FIXME: Is that correct?
  893. if len(namespace) == 0 {
  894. namespace = "default"
  895. }
  896. sub += fmt.Sprintf(",\r\n\"namespace\": %q", namespace)
  897. }
  898. bodyStr = fmt.Sprintf(r.body, sub)
  899. }
  900. r.body = bodyStr
  901. bodyBytes := bytes.NewReader([]byte(bodyStr))
  902. req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes)
  903. if err != nil {
  904. t.Logf("case %v", r)
  905. t.Fatalf("unexpected error: %v", err)
  906. }
  907. req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
  908. func() {
  909. resp, err := transport.RoundTrip(req)
  910. defer resp.Body.Close()
  911. if err != nil {
  912. t.Logf("case %v", r)
  913. t.Fatalf("unexpected error: %v", err)
  914. }
  915. b, _ := ioutil.ReadAll(resp.Body)
  916. if _, ok := r.statusCodes[resp.StatusCode]; !ok {
  917. t.Logf("case %v", r)
  918. t.Errorf("Expected status one of %v, but got %v", r.statusCodes, resp.StatusCode)
  919. t.Errorf("Body: %v", string(b))
  920. } else {
  921. if r.verb == "POST" {
  922. // For successful create operations, extract resourceVersion
  923. id, currentResourceVersion, err := parseResourceVersion(b)
  924. if err == nil {
  925. key := getPreviousResourceVersionKey(r.URL, id)
  926. previousResourceVersion[key] = currentResourceVersion
  927. }
  928. }
  929. }
  930. }()
  931. }
  932. }
  933. // TestKindAuthorization tests that authorization can be controlled
  934. // by namespace.
  935. func TestKindAuthorization(t *testing.T) {
  936. // This file has alice and bob in it.
  937. a := newAuthorizerWithContents(t, `{"resource": "services"}
  938. `)
  939. // Set up a master
  940. masterConfig := framework.NewIntegrationTestMasterConfig()
  941. masterConfig.GenericConfig.Authentication.Authenticator = getTestTokenAuth()
  942. masterConfig.GenericConfig.Authorization.Authorizer = a
  943. _, s, closeFn := framework.RunAMaster(masterConfig)
  944. defer closeFn()
  945. ns := framework.CreateTestingNamespace("auth-kind", s, t)
  946. defer framework.DeleteTestingNamespace(ns, s, t)
  947. previousResourceVersion := make(map[string]float64)
  948. transport := http.DefaultTransport
  949. requests := []struct {
  950. verb string
  951. URL string
  952. body string
  953. statusCodes map[int]bool // allowed status codes.
  954. }{
  955. {"POST", timeoutPath("services", ns.Name, ""), aService, integration.Code201},
  956. {"GET", path("services", ns.Name, ""), "", integration.Code200},
  957. {"GET", path("services", ns.Name, "a"), "", integration.Code200},
  958. {"DELETE", timeoutPath("services", ns.Name, "a"), "", integration.Code200},
  959. {"POST", timeoutPath("pods", ns.Name, ""), aPod, integration.Code403},
  960. {"GET", path("pods", "", ""), "", integration.Code403},
  961. {"GET", path("pods", ns.Name, "a"), "", integration.Code403},
  962. {"DELETE", timeoutPath("pods", ns.Name, "a"), "", integration.Code403},
  963. }
  964. for _, r := range requests {
  965. token := BobToken
  966. var bodyStr string
  967. if r.body != "" {
  968. bodyStr = fmt.Sprintf(r.body, "")
  969. if r.verb == "PUT" && r.body != "" {
  970. // For update operations, insert previous resource version
  971. if resVersion := previousResourceVersion[getPreviousResourceVersionKey(r.URL, "")]; resVersion != 0 {
  972. resourceVersionJSON := fmt.Sprintf(",\r\n\"resourceVersion\": \"%v\"", resVersion)
  973. bodyStr = fmt.Sprintf(r.body, resourceVersionJSON)
  974. }
  975. }
  976. }
  977. r.body = bodyStr
  978. bodyBytes := bytes.NewReader([]byte(bodyStr))
  979. req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes)
  980. if err != nil {
  981. t.Logf("case %v", r)
  982. t.Fatalf("unexpected error: %v", err)
  983. }
  984. req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
  985. {
  986. resp, err := transport.RoundTrip(req)
  987. defer resp.Body.Close()
  988. if err != nil {
  989. t.Logf("case %v", r)
  990. t.Fatalf("unexpected error: %v", err)
  991. }
  992. b, _ := ioutil.ReadAll(resp.Body)
  993. if _, ok := r.statusCodes[resp.StatusCode]; !ok {
  994. t.Logf("case %v", r)
  995. t.Errorf("Expected status one of %v, but got %v", r.statusCodes, resp.StatusCode)
  996. t.Errorf("Body: %v", string(b))
  997. } else {
  998. if r.verb == "POST" {
  999. // For successful create operations, extract resourceVersion
  1000. id, currentResourceVersion, err := parseResourceVersion(b)
  1001. if err == nil {
  1002. key := getPreviousResourceVersionKey(r.URL, id)
  1003. previousResourceVersion[key] = currentResourceVersion
  1004. }
  1005. }
  1006. }
  1007. }
  1008. }
  1009. }
  1010. // TestReadOnlyAuthorization tests that authorization can be controlled
  1011. // by namespace.
  1012. func TestReadOnlyAuthorization(t *testing.T) {
  1013. // This file has alice and bob in it.
  1014. a := newAuthorizerWithContents(t, `{"readonly": true}`)
  1015. // Set up a master
  1016. masterConfig := framework.NewIntegrationTestMasterConfig()
  1017. masterConfig.GenericConfig.Authentication.Authenticator = getTestTokenAuth()
  1018. masterConfig.GenericConfig.Authorization.Authorizer = a
  1019. _, s, closeFn := framework.RunAMaster(masterConfig)
  1020. defer closeFn()
  1021. ns := framework.CreateTestingNamespace("auth-read-only", s, t)
  1022. defer framework.DeleteTestingNamespace(ns, s, t)
  1023. transport := http.DefaultTransport
  1024. requests := []struct {
  1025. verb string
  1026. URL string
  1027. body string
  1028. statusCodes map[int]bool // allowed status codes.
  1029. }{
  1030. {"POST", path("pods", ns.Name, ""), aPod, integration.Code403},
  1031. {"GET", path("pods", ns.Name, ""), "", integration.Code200},
  1032. {"GET", path("pods", metav1.NamespaceDefault, "a"), "", integration.Code404},
  1033. }
  1034. for _, r := range requests {
  1035. token := BobToken
  1036. bodyBytes := bytes.NewReader([]byte(r.body))
  1037. req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes)
  1038. if err != nil {
  1039. t.Fatalf("unexpected error: %v", err)
  1040. }
  1041. req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
  1042. func() {
  1043. resp, err := transport.RoundTrip(req)
  1044. defer resp.Body.Close()
  1045. if err != nil {
  1046. t.Logf("case %v", r)
  1047. t.Fatalf("unexpected error: %v", err)
  1048. }
  1049. if _, ok := r.statusCodes[resp.StatusCode]; !ok {
  1050. t.Logf("case %v", r)
  1051. t.Errorf("Expected status one of %v, but got %v", r.statusCodes, resp.StatusCode)
  1052. b, _ := ioutil.ReadAll(resp.Body)
  1053. t.Errorf("Body: %v", string(b))
  1054. }
  1055. }()
  1056. }
  1057. }
  1058. // TestWebhookTokenAuthenticator tests that a master can use the webhook token
  1059. // authenticator to call out to a remote web server for authentication
  1060. // decisions.
  1061. func TestWebhookTokenAuthenticator(t *testing.T) {
  1062. authServer := newTestWebhookTokenAuthServer()
  1063. defer authServer.Close()
  1064. authenticator, err := getTestWebhookTokenAuth(authServer.URL)
  1065. if err != nil {
  1066. t.Fatalf("error starting webhook token authenticator server: %v", err)
  1067. }
  1068. // Set up a master
  1069. masterConfig := framework.NewIntegrationTestMasterConfig()
  1070. masterConfig.GenericConfig.Authentication.Authenticator = authenticator
  1071. masterConfig.GenericConfig.Authorization.Authorizer = allowAliceAuthorizer{}
  1072. _, s, closeFn := framework.RunAMaster(masterConfig)
  1073. defer closeFn()
  1074. ns := framework.CreateTestingNamespace("auth-webhook-token", s, t)
  1075. defer framework.DeleteTestingNamespace(ns, s, t)
  1076. transport := http.DefaultTransport
  1077. for _, r := range getTestRequests(ns.Name) {
  1078. // Expect Bob's requests to all fail.
  1079. token := BobToken
  1080. bodyBytes := bytes.NewReader([]byte(r.body))
  1081. req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes)
  1082. if err != nil {
  1083. t.Fatalf("unexpected error: %v", err)
  1084. }
  1085. req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
  1086. func() {
  1087. resp, err := transport.RoundTrip(req)
  1088. defer resp.Body.Close()
  1089. if err != nil {
  1090. t.Logf("case %v", r)
  1091. t.Fatalf("unexpected error: %v", err)
  1092. }
  1093. // Expect all of Bob's actions to return Forbidden
  1094. if resp.StatusCode != http.StatusForbidden {
  1095. t.Logf("case %v", r)
  1096. t.Errorf("Expected http.Forbidden, but got %s", resp.Status)
  1097. }
  1098. }()
  1099. // Expect Alice's requests to succeed.
  1100. token = AliceToken
  1101. bodyBytes = bytes.NewReader([]byte(r.body))
  1102. req, err = http.NewRequest(r.verb, s.URL+r.URL, bodyBytes)
  1103. if err != nil {
  1104. t.Fatalf("unexpected error: %v", err)
  1105. }
  1106. req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
  1107. func() {
  1108. resp, err := transport.RoundTrip(req)
  1109. if err != nil {
  1110. t.Logf("case %v", r)
  1111. t.Fatalf("unexpected error: %v", err)
  1112. }
  1113. defer resp.Body.Close()
  1114. // Expect all of Alice's actions to at least get past authn/authz.
  1115. if resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden {
  1116. t.Logf("case %v", r)
  1117. t.Errorf("Expected something other than Unauthorized/Forbidden, but got %s", resp.Status)
  1118. }
  1119. }()
  1120. }
  1121. }
  1122. // newTestWebhookTokenAuthServer creates an http token authentication server
  1123. // that knows about both Alice and Bob.
  1124. func newTestWebhookTokenAuthServer() *httptest.Server {
  1125. serveHTTP := func(w http.ResponseWriter, r *http.Request) {
  1126. var review authenticationv1beta1.TokenReview
  1127. if err := json.NewDecoder(r.Body).Decode(&review); err != nil {
  1128. http.Error(w, fmt.Sprintf("failed to decode body: %v", err), http.StatusBadRequest)
  1129. return
  1130. }
  1131. type userInfo struct {
  1132. Username string `json:"username"`
  1133. UID string `json:"uid"`
  1134. Groups []string `json:"groups"`
  1135. }
  1136. type status struct {
  1137. Authenticated bool `json:"authenticated"`
  1138. User userInfo `json:"user"`
  1139. }
  1140. var username, uid string
  1141. authenticated := false
  1142. if review.Spec.Token == AliceToken {
  1143. authenticated, username, uid = true, "alice", "1"
  1144. } else if review.Spec.Token == BobToken {
  1145. authenticated, username, uid = true, "bob", "2"
  1146. }
  1147. resp := struct {
  1148. APIVersion string `json:"apiVersion"`
  1149. Status status `json:"status"`
  1150. }{
  1151. APIVersion: authenticationv1beta1.SchemeGroupVersion.String(),
  1152. Status: status{
  1153. authenticated,
  1154. userInfo{
  1155. Username: username,
  1156. UID: uid,
  1157. },
  1158. },
  1159. }
  1160. w.Header().Set("Content-Type", "application/json")
  1161. json.NewEncoder(w).Encode(resp)
  1162. }
  1163. server := httptest.NewUnstartedServer(http.HandlerFunc(serveHTTP))
  1164. server.Start()
  1165. return server
  1166. }