auth_test.go 42 KB

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