network_policy.go 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package network
  14. import (
  15. v1 "k8s.io/api/core/v1"
  16. networkingv1 "k8s.io/api/networking/v1"
  17. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  18. "k8s.io/apimachinery/pkg/util/intstr"
  19. "k8s.io/kubernetes/test/e2e/framework"
  20. e2elog "k8s.io/kubernetes/test/e2e/framework/log"
  21. imageutils "k8s.io/kubernetes/test/utils/image"
  22. "fmt"
  23. "github.com/onsi/ginkgo"
  24. )
  25. /*
  26. The following Network Policy tests verify that policy object definitions
  27. are correctly enforced by a networking plugin. It accomplishes this by launching
  28. a simple netcat server, and two clients with different
  29. attributes. Each test case creates a network policy which should only allow
  30. connections from one of the clients. The test then asserts that the clients
  31. failed or successfully connected as expected.
  32. */
  33. var _ = SIGDescribe("NetworkPolicy", func() {
  34. var service *v1.Service
  35. var podServer *v1.Pod
  36. f := framework.NewDefaultFramework("network-policy")
  37. ginkgo.Context("NetworkPolicy between server and client", func() {
  38. ginkgo.BeforeEach(func() {
  39. ginkgo.By("Creating a simple server that serves on port 80 and 81.")
  40. podServer, service = createServerPodAndService(f, f.Namespace, "server", []int{80, 81})
  41. ginkgo.By("Waiting for pod ready", func() {
  42. err := f.WaitForPodReady(podServer.Name)
  43. framework.ExpectNoError(err)
  44. })
  45. // Create pods, which should be able to communicate with the server on port 80 and 81.
  46. ginkgo.By("Testing pods can connect to both ports when no policy is present.")
  47. testCanConnect(f, f.Namespace, "client-can-connect-80", service, 80)
  48. testCanConnect(f, f.Namespace, "client-can-connect-81", service, 81)
  49. })
  50. ginkgo.AfterEach(func() {
  51. cleanupServerPodAndService(f, podServer, service)
  52. })
  53. ginkgo.It("should support a 'default-deny' policy [Feature:NetworkPolicy]", func() {
  54. policy := &networkingv1.NetworkPolicy{
  55. ObjectMeta: metav1.ObjectMeta{
  56. Name: "deny-all",
  57. },
  58. Spec: networkingv1.NetworkPolicySpec{
  59. PodSelector: metav1.LabelSelector{},
  60. Ingress: []networkingv1.NetworkPolicyIngressRule{},
  61. },
  62. }
  63. policy, err := f.ClientSet.NetworkingV1().NetworkPolicies(f.Namespace.Name).Create(policy)
  64. framework.ExpectNoError(err)
  65. defer cleanupNetworkPolicy(f, policy)
  66. // Create a pod with name 'client-cannot-connect', which will attempt to communicate with the server,
  67. // but should not be able to now that isolation is on.
  68. testCannotConnect(f, f.Namespace, "client-cannot-connect", service, 80)
  69. })
  70. ginkgo.It("should enforce policy based on PodSelector [Feature:NetworkPolicy]", func() {
  71. ginkgo.By("Creating a network policy for the server which allows traffic from the pod 'client-a'.")
  72. policy := &networkingv1.NetworkPolicy{
  73. ObjectMeta: metav1.ObjectMeta{
  74. Name: "allow-client-a-via-pod-selector",
  75. },
  76. Spec: networkingv1.NetworkPolicySpec{
  77. // Apply this policy to the Server
  78. PodSelector: metav1.LabelSelector{
  79. MatchLabels: map[string]string{
  80. "pod-name": podServer.Name,
  81. },
  82. },
  83. // Allow traffic only from client-a
  84. Ingress: []networkingv1.NetworkPolicyIngressRule{{
  85. From: []networkingv1.NetworkPolicyPeer{{
  86. PodSelector: &metav1.LabelSelector{
  87. MatchLabels: map[string]string{
  88. "pod-name": "client-a",
  89. },
  90. },
  91. }},
  92. }},
  93. },
  94. }
  95. policy, err := f.ClientSet.NetworkingV1().NetworkPolicies(f.Namespace.Name).Create(policy)
  96. framework.ExpectNoError(err)
  97. defer cleanupNetworkPolicy(f, policy)
  98. ginkgo.By("Creating client-a which should be able to contact the server.", func() {
  99. testCanConnect(f, f.Namespace, "client-a", service, 80)
  100. })
  101. ginkgo.By("Creating client-b which should not be able to contact the server.", func() {
  102. testCannotConnect(f, f.Namespace, "client-b", service, 80)
  103. })
  104. })
  105. ginkgo.It("should enforce policy based on NamespaceSelector [Feature:NetworkPolicy]", func() {
  106. nsA := f.Namespace
  107. nsBName := f.BaseName + "-b"
  108. // The CreateNamespace helper uses the input name as a Name Generator, so the namespace itself
  109. // will have a different name than what we are setting as the value of ns-name.
  110. // This is fine as long as we don't try to match the label as nsB.Name in our policy.
  111. nsB, err := f.CreateNamespace(nsBName, map[string]string{
  112. "ns-name": nsBName,
  113. })
  114. framework.ExpectNoError(err)
  115. // Create Server with Service in NS-B
  116. e2elog.Logf("Waiting for server to come up.")
  117. err = framework.WaitForPodRunningInNamespace(f.ClientSet, podServer)
  118. framework.ExpectNoError(err)
  119. // Create Policy for that service that allows traffic only via namespace B
  120. ginkgo.By("Creating a network policy for the server which allows traffic from namespace-b.")
  121. policy := &networkingv1.NetworkPolicy{
  122. ObjectMeta: metav1.ObjectMeta{
  123. Name: "allow-ns-b-via-namespace-selector",
  124. },
  125. Spec: networkingv1.NetworkPolicySpec{
  126. // Apply to server
  127. PodSelector: metav1.LabelSelector{
  128. MatchLabels: map[string]string{
  129. "pod-name": podServer.Name,
  130. },
  131. },
  132. // Allow traffic only from NS-B
  133. Ingress: []networkingv1.NetworkPolicyIngressRule{{
  134. From: []networkingv1.NetworkPolicyPeer{{
  135. NamespaceSelector: &metav1.LabelSelector{
  136. MatchLabels: map[string]string{
  137. "ns-name": nsBName,
  138. },
  139. },
  140. }},
  141. }},
  142. },
  143. }
  144. policy, err = f.ClientSet.NetworkingV1().NetworkPolicies(nsA.Name).Create(policy)
  145. framework.ExpectNoError(err)
  146. defer cleanupNetworkPolicy(f, policy)
  147. testCannotConnect(f, nsA, "client-a", service, 80)
  148. testCanConnect(f, nsB, "client-b", service, 80)
  149. })
  150. ginkgo.It("should enforce policy based on PodSelector with MatchExpressions[Feature:NetworkPolicy]", func() {
  151. ginkgo.By("Creating a network policy for the server which allows traffic from the pod 'client-a'.")
  152. policy := &networkingv1.NetworkPolicy{
  153. ObjectMeta: metav1.ObjectMeta{
  154. Name: "allow-client-a-via-pod-selector-with-match-expressions",
  155. },
  156. Spec: networkingv1.NetworkPolicySpec{
  157. PodSelector: metav1.LabelSelector{
  158. MatchLabels: map[string]string{
  159. "pod-name": podServer.Name,
  160. },
  161. },
  162. Ingress: []networkingv1.NetworkPolicyIngressRule{{
  163. From: []networkingv1.NetworkPolicyPeer{{
  164. PodSelector: &metav1.LabelSelector{
  165. MatchExpressions: []metav1.LabelSelectorRequirement{{
  166. Key: "pod-name",
  167. Operator: metav1.LabelSelectorOpIn,
  168. Values: []string{"client-a"},
  169. }},
  170. },
  171. }},
  172. }},
  173. },
  174. }
  175. policy, err := f.ClientSet.NetworkingV1().NetworkPolicies(f.Namespace.Name).Create(policy)
  176. framework.ExpectNoError(err, "Error creating Network Policy %v: %v", policy.ObjectMeta.Name, err)
  177. defer cleanupNetworkPolicy(f, policy)
  178. ginkgo.By("Creating client-a which should be able to contact the server.", func() {
  179. testCanConnect(f, f.Namespace, "client-a", service, 80)
  180. })
  181. ginkgo.By("Creating client-b which should not be able to contact the server.", func() {
  182. testCannotConnect(f, f.Namespace, "client-b", service, 80)
  183. })
  184. })
  185. ginkgo.It("should enforce policy based on NamespaceSelector with MatchExpressions[Feature:NetworkPolicy]", func() {
  186. nsA := f.Namespace
  187. nsBName := f.BaseName + "-b"
  188. nsB, err := f.CreateNamespace(nsBName, map[string]string{
  189. "ns-name": nsBName,
  190. })
  191. framework.ExpectNoError(err, "Error creating namespace %v: %v", nsBName, err)
  192. nsCName := f.BaseName + "-c"
  193. nsC, err := f.CreateNamespace(nsCName, map[string]string{
  194. "ns-name": nsCName,
  195. })
  196. framework.ExpectNoError(err, "Error creating namespace %v: %v", nsCName, err)
  197. // Create Policy for the server that allows traffic from namespace different than namespace-a
  198. ginkgo.By("Creating a network policy for the server which allows traffic from ns different than namespace-a.")
  199. policy := &networkingv1.NetworkPolicy{
  200. ObjectMeta: metav1.ObjectMeta{
  201. Name: "allow-any-ns-different-than-ns-a-via-ns-selector-with-match-expressions",
  202. },
  203. Spec: networkingv1.NetworkPolicySpec{
  204. PodSelector: metav1.LabelSelector{
  205. MatchLabels: map[string]string{
  206. "pod-name": podServer.Name,
  207. },
  208. },
  209. Ingress: []networkingv1.NetworkPolicyIngressRule{{
  210. From: []networkingv1.NetworkPolicyPeer{{
  211. NamespaceSelector: &metav1.LabelSelector{
  212. MatchExpressions: []metav1.LabelSelectorRequirement{{
  213. Key: "ns-name",
  214. Operator: metav1.LabelSelectorOpNotIn,
  215. Values: []string{nsCName},
  216. }},
  217. },
  218. }},
  219. }},
  220. },
  221. }
  222. policy, err = f.ClientSet.NetworkingV1().NetworkPolicies(nsA.Name).Create(policy)
  223. framework.ExpectNoError(err, "Error creating Network Policy %v: %v", policy.ObjectMeta.Name, err)
  224. defer cleanupNetworkPolicy(f, policy)
  225. testCannotConnect(f, nsC, "client-a", service, 80)
  226. testCanConnect(f, nsB, "client-a", service, 80)
  227. })
  228. ginkgo.It("should enforce policy based on PodSelector or NamespaceSelector [Feature:NetworkPolicy]", func() {
  229. nsA := f.Namespace
  230. nsBName := f.BaseName + "-b"
  231. nsB, err := f.CreateNamespace(nsBName, map[string]string{
  232. "ns-name": nsBName,
  233. })
  234. framework.ExpectNoError(err, "Error creating namespace %v: %v", nsBName, err)
  235. // Create Policy for the server that allows traffic only via client B or namespace B
  236. ginkgo.By("Creating a network policy for the server which allows traffic from client-b or namespace-b.")
  237. policy := &networkingv1.NetworkPolicy{
  238. ObjectMeta: metav1.ObjectMeta{
  239. Name: "allow-ns-b-via-namespace-selector-or-client-b-via-pod-selector",
  240. },
  241. Spec: networkingv1.NetworkPolicySpec{
  242. PodSelector: metav1.LabelSelector{
  243. MatchLabels: map[string]string{
  244. "pod-name": podServer.Name,
  245. },
  246. },
  247. Ingress: []networkingv1.NetworkPolicyIngressRule{{
  248. From: []networkingv1.NetworkPolicyPeer{{
  249. PodSelector: &metav1.LabelSelector{
  250. MatchLabels: map[string]string{
  251. "pod-name": "client-b",
  252. },
  253. },
  254. }, {
  255. NamespaceSelector: &metav1.LabelSelector{
  256. MatchLabels: map[string]string{
  257. "ns-name": nsBName,
  258. },
  259. },
  260. }},
  261. }},
  262. },
  263. }
  264. policy, err = f.ClientSet.NetworkingV1().NetworkPolicies(nsA.Name).Create(policy)
  265. framework.ExpectNoError(err, "Error creating Network Policy %v: %v", policy.ObjectMeta.Name, err)
  266. defer cleanupNetworkPolicy(f, policy)
  267. testCanConnect(f, nsB, "client-a", service, 80)
  268. testCanConnect(f, nsA, "client-b", service, 80)
  269. testCannotConnect(f, nsA, "client-c", service, 80)
  270. })
  271. ginkgo.It("should enforce policy based on PodSelector and NamespaceSelector [Feature:NetworkPolicy]", func() {
  272. nsA := f.Namespace
  273. nsBName := f.BaseName + "-b"
  274. nsB, err := f.CreateNamespace(nsBName, map[string]string{
  275. "ns-name": nsBName,
  276. })
  277. framework.ExpectNoError(err, "Error creating namespace %v: %v", nsBName, err)
  278. // Create Policy for the server that allows traffic only via client-b in namespace B
  279. ginkgo.By("Creating a network policy for the server which allows traffic from client-b in namespace-b.")
  280. policy := &networkingv1.NetworkPolicy{
  281. ObjectMeta: metav1.ObjectMeta{
  282. Name: "allow-client-b-in-ns-b-via-ns-selector-and-pod-selector",
  283. },
  284. Spec: networkingv1.NetworkPolicySpec{
  285. PodSelector: metav1.LabelSelector{
  286. MatchLabels: map[string]string{
  287. "pod-name": podServer.Name,
  288. },
  289. },
  290. Ingress: []networkingv1.NetworkPolicyIngressRule{{
  291. From: []networkingv1.NetworkPolicyPeer{{
  292. PodSelector: &metav1.LabelSelector{
  293. MatchLabels: map[string]string{
  294. "pod-name": "client-b",
  295. },
  296. },
  297. NamespaceSelector: &metav1.LabelSelector{
  298. MatchLabels: map[string]string{
  299. "ns-name": nsBName,
  300. },
  301. },
  302. }},
  303. }},
  304. },
  305. }
  306. policy, err = f.ClientSet.NetworkingV1().NetworkPolicies(nsA.Name).Create(policy)
  307. framework.ExpectNoError(err, "Error creating Network Policy %v: %v", policy.ObjectMeta.Name, err)
  308. defer cleanupNetworkPolicy(f, policy)
  309. testCannotConnect(f, nsB, "client-a", service, 80)
  310. testCannotConnect(f, nsA, "client-b", service, 80)
  311. testCanConnect(f, nsB, "client-b", service, 80)
  312. })
  313. ginkgo.It("should enforce policy based on Ports [Feature:NetworkPolicy]", func() {
  314. ginkgo.By("Creating a network policy for the Service which allows traffic only to one port.")
  315. policy := &networkingv1.NetworkPolicy{
  316. ObjectMeta: metav1.ObjectMeta{
  317. Name: "allow-ingress-on-port-81",
  318. },
  319. Spec: networkingv1.NetworkPolicySpec{
  320. // Apply to server
  321. PodSelector: metav1.LabelSelector{
  322. MatchLabels: map[string]string{
  323. "pod-name": podServer.Name,
  324. },
  325. },
  326. // Allow traffic only to one port.
  327. Ingress: []networkingv1.NetworkPolicyIngressRule{{
  328. Ports: []networkingv1.NetworkPolicyPort{{
  329. Port: &intstr.IntOrString{IntVal: 81},
  330. }},
  331. }},
  332. },
  333. }
  334. policy, err := f.ClientSet.NetworkingV1().NetworkPolicies(f.Namespace.Name).Create(policy)
  335. framework.ExpectNoError(err)
  336. defer cleanupNetworkPolicy(f, policy)
  337. ginkgo.By("Testing pods can connect only to the port allowed by the policy.")
  338. testCannotConnect(f, f.Namespace, "client-a", service, 80)
  339. testCanConnect(f, f.Namespace, "client-b", service, 81)
  340. })
  341. ginkgo.It("should enforce multiple, stacked policies with overlapping podSelectors [Feature:NetworkPolicy]", func() {
  342. ginkgo.By("Creating a network policy for the Service which allows traffic only to one port.")
  343. policy := &networkingv1.NetworkPolicy{
  344. ObjectMeta: metav1.ObjectMeta{
  345. Name: "allow-ingress-on-port-80",
  346. },
  347. Spec: networkingv1.NetworkPolicySpec{
  348. // Apply to server
  349. PodSelector: metav1.LabelSelector{
  350. MatchLabels: map[string]string{
  351. "pod-name": podServer.Name,
  352. },
  353. },
  354. // Allow traffic only to one port.
  355. Ingress: []networkingv1.NetworkPolicyIngressRule{{
  356. Ports: []networkingv1.NetworkPolicyPort{{
  357. Port: &intstr.IntOrString{IntVal: 80},
  358. }},
  359. }},
  360. },
  361. }
  362. policy, err := f.ClientSet.NetworkingV1().NetworkPolicies(f.Namespace.Name).Create(policy)
  363. framework.ExpectNoError(err)
  364. defer cleanupNetworkPolicy(f, policy)
  365. ginkgo.By("Creating a network policy for the Service which allows traffic only to another port.")
  366. policy2 := &networkingv1.NetworkPolicy{
  367. ObjectMeta: metav1.ObjectMeta{
  368. Name: "allow-ingress-on-port-81",
  369. },
  370. Spec: networkingv1.NetworkPolicySpec{
  371. // Apply to server
  372. PodSelector: metav1.LabelSelector{
  373. MatchLabels: map[string]string{
  374. "pod-name": podServer.Name,
  375. },
  376. },
  377. // Allow traffic only to one port.
  378. Ingress: []networkingv1.NetworkPolicyIngressRule{{
  379. Ports: []networkingv1.NetworkPolicyPort{{
  380. Port: &intstr.IntOrString{IntVal: 81},
  381. }},
  382. }},
  383. },
  384. }
  385. policy2, err = f.ClientSet.NetworkingV1().NetworkPolicies(f.Namespace.Name).Create(policy2)
  386. framework.ExpectNoError(err)
  387. defer cleanupNetworkPolicy(f, policy2)
  388. ginkgo.By("Testing pods can connect to both ports when both policies are present.")
  389. testCanConnect(f, f.Namespace, "client-a", service, 80)
  390. testCanConnect(f, f.Namespace, "client-b", service, 81)
  391. })
  392. ginkgo.It("should support allow-all policy [Feature:NetworkPolicy]", func() {
  393. ginkgo.By("Creating a network policy which allows all traffic.")
  394. policy := &networkingv1.NetworkPolicy{
  395. ObjectMeta: metav1.ObjectMeta{
  396. Name: "allow-all",
  397. },
  398. Spec: networkingv1.NetworkPolicySpec{
  399. // Allow all traffic
  400. PodSelector: metav1.LabelSelector{
  401. MatchLabels: map[string]string{},
  402. },
  403. Ingress: []networkingv1.NetworkPolicyIngressRule{{}},
  404. },
  405. }
  406. policy, err := f.ClientSet.NetworkingV1().NetworkPolicies(f.Namespace.Name).Create(policy)
  407. framework.ExpectNoError(err)
  408. defer cleanupNetworkPolicy(f, policy)
  409. ginkgo.By("Testing pods can connect to both ports when an 'allow-all' policy is present.")
  410. testCanConnect(f, f.Namespace, "client-a", service, 80)
  411. testCanConnect(f, f.Namespace, "client-b", service, 81)
  412. })
  413. ginkgo.It("should allow ingress access on one named port [Feature:NetworkPolicy]", func() {
  414. policy := &networkingv1.NetworkPolicy{
  415. ObjectMeta: metav1.ObjectMeta{
  416. Name: "allow-client-a-via-named-port-ingress-rule",
  417. },
  418. Spec: networkingv1.NetworkPolicySpec{
  419. // Apply this policy to the Server
  420. PodSelector: metav1.LabelSelector{
  421. MatchLabels: map[string]string{
  422. "pod-name": podServer.Name,
  423. },
  424. },
  425. // Allow traffic to only one named port: "serve-80".
  426. Ingress: []networkingv1.NetworkPolicyIngressRule{{
  427. Ports: []networkingv1.NetworkPolicyPort{{
  428. Port: &intstr.IntOrString{Type: intstr.String, StrVal: "serve-80"},
  429. }},
  430. }},
  431. },
  432. }
  433. policy, err := f.ClientSet.NetworkingV1().NetworkPolicies(f.Namespace.Name).Create(policy)
  434. framework.ExpectNoError(err)
  435. defer cleanupNetworkPolicy(f, policy)
  436. ginkgo.By("Creating client-a which should be able to contact the server.", func() {
  437. testCanConnect(f, f.Namespace, "client-a", service, 80)
  438. })
  439. ginkgo.By("Creating client-b which should not be able to contact the server on port 81.", func() {
  440. testCannotConnect(f, f.Namespace, "client-b", service, 81)
  441. })
  442. })
  443. ginkgo.It("should allow egress access on one named port [Feature:NetworkPolicy]", func() {
  444. clientPodName := "client-a"
  445. protocolUDP := v1.ProtocolUDP
  446. policy := &networkingv1.NetworkPolicy{
  447. ObjectMeta: metav1.ObjectMeta{
  448. Name: "allow-client-a-via-named-port-egress-rule",
  449. },
  450. Spec: networkingv1.NetworkPolicySpec{
  451. // Apply this policy to client-a
  452. PodSelector: metav1.LabelSelector{
  453. MatchLabels: map[string]string{
  454. "pod-name": clientPodName,
  455. },
  456. },
  457. // Allow traffic to only one named port: "serve-80".
  458. Egress: []networkingv1.NetworkPolicyEgressRule{{
  459. Ports: []networkingv1.NetworkPolicyPort{
  460. {
  461. Port: &intstr.IntOrString{Type: intstr.String, StrVal: "serve-80"},
  462. },
  463. // Allow DNS look-ups
  464. {
  465. Protocol: &protocolUDP,
  466. Port: &intstr.IntOrString{Type: intstr.Int, IntVal: 53},
  467. },
  468. },
  469. }},
  470. },
  471. }
  472. policy, err := f.ClientSet.NetworkingV1().NetworkPolicies(f.Namespace.Name).Create(policy)
  473. framework.ExpectNoError(err)
  474. defer cleanupNetworkPolicy(f, policy)
  475. ginkgo.By("Creating client-a which should be able to contact the server.", func() {
  476. testCanConnect(f, f.Namespace, clientPodName, service, 80)
  477. })
  478. ginkgo.By("Creating client-a which should not be able to contact the server on port 81.", func() {
  479. testCannotConnect(f, f.Namespace, clientPodName, service, 81)
  480. })
  481. })
  482. })
  483. })
  484. func testCanConnect(f *framework.Framework, ns *v1.Namespace, podName string, service *v1.Service, targetPort int) {
  485. ginkgo.By(fmt.Sprintf("Creating client pod %s that should successfully connect to %s.", podName, service.Name))
  486. podClient := createNetworkClientPod(f, ns, podName, service, targetPort)
  487. defer func() {
  488. ginkgo.By(fmt.Sprintf("Cleaning up the pod %s", podName))
  489. if err := f.ClientSet.CoreV1().Pods(ns.Name).Delete(podClient.Name, nil); err != nil {
  490. framework.Failf("unable to cleanup pod %v: %v", podClient.Name, err)
  491. }
  492. }()
  493. e2elog.Logf("Waiting for %s to complete.", podClient.Name)
  494. err := framework.WaitForPodNoLongerRunningInNamespace(f.ClientSet, podClient.Name, ns.Name)
  495. framework.ExpectNoError(err, "Pod did not finish as expected.")
  496. e2elog.Logf("Waiting for %s to complete.", podClient.Name)
  497. err = framework.WaitForPodSuccessInNamespace(f.ClientSet, podClient.Name, ns.Name)
  498. if err != nil {
  499. // Collect pod logs when we see a failure.
  500. logs, logErr := framework.GetPodLogs(f.ClientSet, f.Namespace.Name, podName, fmt.Sprintf("%s-container", podName))
  501. if logErr != nil {
  502. framework.Failf("Error getting container logs: %s", logErr)
  503. }
  504. // Collect current NetworkPolicies applied in the test namespace.
  505. policies, err := f.ClientSet.NetworkingV1().NetworkPolicies(f.Namespace.Name).List(metav1.ListOptions{})
  506. if err != nil {
  507. e2elog.Logf("error getting current NetworkPolicies for %s namespace: %s", f.Namespace.Name, err)
  508. }
  509. // Collect the list of pods running in the test namespace.
  510. podsInNS, err := framework.GetPodsInNamespace(f.ClientSet, f.Namespace.Name, map[string]string{})
  511. if err != nil {
  512. e2elog.Logf("error getting pods for %s namespace: %s", f.Namespace.Name, err)
  513. }
  514. pods := []string{}
  515. for _, p := range podsInNS {
  516. pods = append(pods, fmt.Sprintf("Pod: %s, Status: %s\n", p.Name, p.Status.String()))
  517. }
  518. framework.Failf("Pod %s should be able to connect to service %s, but was not able to connect.\nPod logs:\n%s\n\n Current NetworkPolicies:\n\t%v\n\n Pods:\n\t%v\n\n", podName, service.Name, logs, policies.Items, pods)
  519. // Dump debug information for the test namespace.
  520. framework.DumpDebugInfo(f.ClientSet, f.Namespace.Name)
  521. }
  522. }
  523. func testCannotConnect(f *framework.Framework, ns *v1.Namespace, podName string, service *v1.Service, targetPort int) {
  524. ginkgo.By(fmt.Sprintf("Creating client pod %s that should not be able to connect to %s.", podName, service.Name))
  525. podClient := createNetworkClientPod(f, ns, podName, service, targetPort)
  526. defer func() {
  527. ginkgo.By(fmt.Sprintf("Cleaning up the pod %s", podName))
  528. if err := f.ClientSet.CoreV1().Pods(ns.Name).Delete(podClient.Name, nil); err != nil {
  529. framework.Failf("unable to cleanup pod %v: %v", podClient.Name, err)
  530. }
  531. }()
  532. e2elog.Logf("Waiting for %s to complete.", podClient.Name)
  533. err := framework.WaitForPodSuccessInNamespace(f.ClientSet, podClient.Name, ns.Name)
  534. // We expect an error here since it's a cannot connect test.
  535. // Dump debug information if the error was nil.
  536. if err == nil {
  537. // Collect pod logs when we see a failure.
  538. logs, logErr := framework.GetPodLogs(f.ClientSet, f.Namespace.Name, podName, fmt.Sprintf("%s-container", podName))
  539. if logErr != nil {
  540. framework.Failf("Error getting container logs: %s", logErr)
  541. }
  542. // Collect current NetworkPolicies applied in the test namespace.
  543. policies, err := f.ClientSet.NetworkingV1().NetworkPolicies(f.Namespace.Name).List(metav1.ListOptions{})
  544. if err != nil {
  545. e2elog.Logf("error getting current NetworkPolicies for %s namespace: %s", f.Namespace.Name, err)
  546. }
  547. // Collect the list of pods running in the test namespace.
  548. podsInNS, err := framework.GetPodsInNamespace(f.ClientSet, f.Namespace.Name, map[string]string{})
  549. if err != nil {
  550. e2elog.Logf("error getting pods for %s namespace: %s", f.Namespace.Name, err)
  551. }
  552. pods := []string{}
  553. for _, p := range podsInNS {
  554. pods = append(pods, fmt.Sprintf("Pod: %s, Status: %s\n", p.Name, p.Status.String()))
  555. }
  556. framework.Failf("Pod %s should not be able to connect to service %s, but was able to connect.\nPod logs:\n%s\n\n Current NetworkPolicies:\n\t%v\n\n Pods:\n\t %v\n\n", podName, service.Name, logs, policies.Items, pods)
  557. // Dump debug information for the test namespace.
  558. framework.DumpDebugInfo(f.ClientSet, f.Namespace.Name)
  559. }
  560. }
  561. // Create a server pod with a listening container for each port in ports[].
  562. // Will also assign a pod label with key: "pod-name" and label set to the given podname for later use by the network
  563. // policy.
  564. func createServerPodAndService(f *framework.Framework, namespace *v1.Namespace, podName string, ports []int) (*v1.Pod, *v1.Service) {
  565. // Because we have a variable amount of ports, we'll first loop through and generate our Containers for our pod,
  566. // and ServicePorts.for our Service.
  567. containers := []v1.Container{}
  568. servicePorts := []v1.ServicePort{}
  569. for _, port := range ports {
  570. // Build the containers for the server pod.
  571. containers = append(containers, v1.Container{
  572. Name: fmt.Sprintf("%s-container-%d", podName, port),
  573. Image: imageutils.GetE2EImage(imageutils.Porter),
  574. Env: []v1.EnvVar{
  575. {
  576. Name: fmt.Sprintf("SERVE_PORT_%d", port),
  577. Value: "foo",
  578. },
  579. },
  580. Ports: []v1.ContainerPort{
  581. {
  582. ContainerPort: int32(port),
  583. Name: fmt.Sprintf("serve-%d", port),
  584. },
  585. },
  586. ReadinessProbe: &v1.Probe{
  587. Handler: v1.Handler{
  588. HTTPGet: &v1.HTTPGetAction{
  589. Path: "/",
  590. Port: intstr.IntOrString{
  591. IntVal: int32(port),
  592. },
  593. Scheme: v1.URISchemeHTTP,
  594. },
  595. },
  596. },
  597. })
  598. // Build the Service Ports for the service.
  599. servicePorts = append(servicePorts, v1.ServicePort{
  600. Name: fmt.Sprintf("%s-%d", podName, port),
  601. Port: int32(port),
  602. TargetPort: intstr.FromInt(port),
  603. })
  604. }
  605. ginkgo.By(fmt.Sprintf("Creating a server pod %s in namespace %s", podName, namespace.Name))
  606. pod, err := f.ClientSet.CoreV1().Pods(namespace.Name).Create(&v1.Pod{
  607. ObjectMeta: metav1.ObjectMeta{
  608. Name: podName,
  609. Labels: map[string]string{
  610. "pod-name": podName,
  611. },
  612. },
  613. Spec: v1.PodSpec{
  614. Containers: containers,
  615. RestartPolicy: v1.RestartPolicyNever,
  616. },
  617. })
  618. framework.ExpectNoError(err)
  619. e2elog.Logf("Created pod %v", pod.ObjectMeta.Name)
  620. svcName := fmt.Sprintf("svc-%s", podName)
  621. ginkgo.By(fmt.Sprintf("Creating a service %s for pod %s in namespace %s", svcName, podName, namespace.Name))
  622. svc, err := f.ClientSet.CoreV1().Services(namespace.Name).Create(&v1.Service{
  623. ObjectMeta: metav1.ObjectMeta{
  624. Name: svcName,
  625. },
  626. Spec: v1.ServiceSpec{
  627. Ports: servicePorts,
  628. Selector: map[string]string{
  629. "pod-name": podName,
  630. },
  631. },
  632. })
  633. framework.ExpectNoError(err)
  634. e2elog.Logf("Created service %s", svc.Name)
  635. return pod, svc
  636. }
  637. func cleanupServerPodAndService(f *framework.Framework, pod *v1.Pod, service *v1.Service) {
  638. ginkgo.By("Cleaning up the server.")
  639. if err := f.ClientSet.CoreV1().Pods(pod.Namespace).Delete(pod.Name, nil); err != nil {
  640. framework.Failf("unable to cleanup pod %v: %v", pod.Name, err)
  641. }
  642. ginkgo.By("Cleaning up the server's service.")
  643. if err := f.ClientSet.CoreV1().Services(service.Namespace).Delete(service.Name, nil); err != nil {
  644. framework.Failf("unable to cleanup svc %v: %v", service.Name, err)
  645. }
  646. }
  647. // Create a client pod which will attempt a netcat to the provided service, on the specified port.
  648. // This client will attempt a one-shot connection, then die, without restarting the pod.
  649. // Test can then be asserted based on whether the pod quit with an error or not.
  650. func createNetworkClientPod(f *framework.Framework, namespace *v1.Namespace, podName string, targetService *v1.Service, targetPort int) *v1.Pod {
  651. pod, err := f.ClientSet.CoreV1().Pods(namespace.Name).Create(&v1.Pod{
  652. ObjectMeta: metav1.ObjectMeta{
  653. Name: podName,
  654. Labels: map[string]string{
  655. "pod-name": podName,
  656. },
  657. },
  658. Spec: v1.PodSpec{
  659. RestartPolicy: v1.RestartPolicyNever,
  660. Containers: []v1.Container{
  661. {
  662. Name: fmt.Sprintf("%s-container", podName),
  663. Image: imageutils.GetE2EImage(imageutils.BusyBox),
  664. Args: []string{
  665. "/bin/sh",
  666. "-c",
  667. fmt.Sprintf("for i in $(seq 1 5); do nc -vz -w 8 %s.%s %d && exit 0 || sleep 1; done; exit 1",
  668. targetService.Name, targetService.Namespace, targetPort),
  669. },
  670. },
  671. },
  672. },
  673. })
  674. framework.ExpectNoError(err)
  675. return pod
  676. }
  677. func cleanupNetworkPolicy(f *framework.Framework, policy *networkingv1.NetworkPolicy) {
  678. ginkgo.By("Cleaning up the policy.")
  679. if err := f.ClientSet.NetworkingV1().NetworkPolicies(policy.Namespace).Delete(policy.Name, nil); err != nil {
  680. framework.Failf("unable to cleanup policy %v: %v", policy.Name, err)
  681. }
  682. }