security_context_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  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 dockershim
  14. import (
  15. "fmt"
  16. "strconv"
  17. "testing"
  18. "github.com/blang/semver"
  19. dockercontainer "github.com/docker/docker/api/types/container"
  20. "github.com/stretchr/testify/assert"
  21. runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
  22. )
  23. func TestModifyContainerConfig(t *testing.T) {
  24. var uid int64 = 123
  25. var username = "testuser"
  26. var gid int64 = 423
  27. cases := []struct {
  28. name string
  29. sc *runtimeapi.LinuxContainerSecurityContext
  30. expected *dockercontainer.Config
  31. isErr bool
  32. }{
  33. {
  34. name: "container.SecurityContext.RunAsUser set",
  35. sc: &runtimeapi.LinuxContainerSecurityContext{
  36. RunAsUser: &runtimeapi.Int64Value{Value: uid},
  37. },
  38. expected: &dockercontainer.Config{
  39. User: strconv.FormatInt(uid, 10),
  40. },
  41. isErr: false,
  42. },
  43. {
  44. name: "container.SecurityContext.RunAsUsername set",
  45. sc: &runtimeapi.LinuxContainerSecurityContext{
  46. RunAsUsername: username,
  47. },
  48. expected: &dockercontainer.Config{
  49. User: username,
  50. },
  51. isErr: false,
  52. },
  53. {
  54. name: "container.SecurityContext.RunAsUsername and container.SecurityContext.RunAsUser set",
  55. sc: &runtimeapi.LinuxContainerSecurityContext{
  56. RunAsUsername: username,
  57. RunAsUser: &runtimeapi.Int64Value{Value: uid},
  58. },
  59. expected: &dockercontainer.Config{
  60. User: username,
  61. },
  62. isErr: false,
  63. },
  64. {
  65. name: "no RunAsUser value set",
  66. sc: &runtimeapi.LinuxContainerSecurityContext{},
  67. expected: &dockercontainer.Config{},
  68. isErr: false,
  69. },
  70. {
  71. name: "RunAsUser value set, RunAsGroup set",
  72. sc: &runtimeapi.LinuxContainerSecurityContext{
  73. RunAsUser: &runtimeapi.Int64Value{Value: uid},
  74. RunAsGroup: &runtimeapi.Int64Value{Value: gid},
  75. },
  76. expected: &dockercontainer.Config{
  77. User: "123:423",
  78. },
  79. isErr: false,
  80. },
  81. {
  82. name: "RunAsUsername value set, RunAsGroup set",
  83. sc: &runtimeapi.LinuxContainerSecurityContext{
  84. RunAsUsername: username,
  85. RunAsGroup: &runtimeapi.Int64Value{Value: gid},
  86. },
  87. expected: &dockercontainer.Config{
  88. User: "testuser:423",
  89. },
  90. isErr: false,
  91. },
  92. {
  93. name: "RunAsUser/RunAsUsername not set, RunAsGroup set",
  94. sc: &runtimeapi.LinuxContainerSecurityContext{
  95. RunAsGroup: &runtimeapi.Int64Value{Value: gid},
  96. },
  97. isErr: true,
  98. },
  99. {
  100. name: "RunAsUser/RunAsUsername both set, RunAsGroup set",
  101. sc: &runtimeapi.LinuxContainerSecurityContext{
  102. RunAsUser: &runtimeapi.Int64Value{Value: uid},
  103. RunAsUsername: username,
  104. RunAsGroup: &runtimeapi.Int64Value{Value: gid},
  105. },
  106. expected: &dockercontainer.Config{
  107. User: "testuser:423",
  108. },
  109. isErr: false,
  110. },
  111. }
  112. for _, tc := range cases {
  113. dockerCfg := &dockercontainer.Config{}
  114. err := modifyContainerConfig(tc.sc, dockerCfg)
  115. if tc.isErr {
  116. assert.NotNil(t, err)
  117. } else {
  118. assert.Nil(t, err)
  119. assert.Equal(t, tc.expected, dockerCfg, "[Test case %q]", tc.name)
  120. }
  121. }
  122. }
  123. func TestModifyHostConfig(t *testing.T) {
  124. setNetworkHC := &dockercontainer.HostConfig{}
  125. // When we have Privileged pods, we do not need to use the
  126. // Masked / Readonly paths.
  127. setPrivSC := &runtimeapi.LinuxContainerSecurityContext{}
  128. setPrivSC.Privileged = true
  129. setPrivSC.MaskedPaths = []string{"/hello/world/masked"}
  130. setPrivSC.ReadonlyPaths = []string{"/hello/world/readonly"}
  131. setPrivHC := &dockercontainer.HostConfig{
  132. Privileged: true,
  133. }
  134. unsetPrivSC := &runtimeapi.LinuxContainerSecurityContext{}
  135. unsetPrivSC.Privileged = false
  136. unsetPrivSC.MaskedPaths = []string{"/hello/world/masked"}
  137. unsetPrivSC.ReadonlyPaths = []string{"/hello/world/readonly"}
  138. unsetPrivHC := &dockercontainer.HostConfig{
  139. Privileged: false,
  140. MaskedPaths: []string{"/hello/world/masked"},
  141. ReadonlyPaths: []string{"/hello/world/readonly"},
  142. }
  143. setCapsHC := &dockercontainer.HostConfig{
  144. CapAdd: []string{"addCapA", "addCapB"},
  145. CapDrop: []string{"dropCapA", "dropCapB"},
  146. }
  147. setSELinuxHC := &dockercontainer.HostConfig{
  148. SecurityOpt: []string{
  149. fmt.Sprintf("%s:%s", selinuxLabelUser('='), "user"),
  150. fmt.Sprintf("%s:%s", selinuxLabelRole('='), "role"),
  151. fmt.Sprintf("%s:%s", selinuxLabelType('='), "type"),
  152. fmt.Sprintf("%s:%s", selinuxLabelLevel('='), "level"),
  153. },
  154. }
  155. cases := []struct {
  156. name string
  157. sc *runtimeapi.LinuxContainerSecurityContext
  158. expected *dockercontainer.HostConfig
  159. }{
  160. {
  161. name: "fully set container.SecurityContext",
  162. sc: fullValidSecurityContext(),
  163. expected: fullValidHostConfig(),
  164. },
  165. {
  166. name: "empty container.SecurityContext",
  167. sc: &runtimeapi.LinuxContainerSecurityContext{},
  168. expected: setNetworkHC,
  169. },
  170. {
  171. name: "container.SecurityContext.Privileged",
  172. sc: setPrivSC,
  173. expected: setPrivHC,
  174. },
  175. {
  176. name: "container.SecurityContext.NoPrivileges",
  177. sc: unsetPrivSC,
  178. expected: unsetPrivHC,
  179. },
  180. {
  181. name: "container.SecurityContext.Capabilities",
  182. sc: &runtimeapi.LinuxContainerSecurityContext{
  183. Capabilities: inputCapabilities(),
  184. },
  185. expected: setCapsHC,
  186. },
  187. {
  188. name: "container.SecurityContext.SELinuxOptions",
  189. sc: &runtimeapi.LinuxContainerSecurityContext{
  190. SelinuxOptions: inputSELinuxOptions(),
  191. },
  192. expected: setSELinuxHC,
  193. },
  194. }
  195. for _, tc := range cases {
  196. dockerCfg := &dockercontainer.HostConfig{}
  197. modifyHostConfig(tc.sc, dockerCfg, '=')
  198. assert.Equal(t, tc.expected, dockerCfg, "[Test case %q]", tc.name)
  199. }
  200. }
  201. func TestModifyHostConfigWithGroups(t *testing.T) {
  202. supplementalGroupsSC := &runtimeapi.LinuxContainerSecurityContext{}
  203. supplementalGroupsSC.SupplementalGroups = []int64{2222}
  204. supplementalGroupHC := &dockercontainer.HostConfig{}
  205. supplementalGroupHC.GroupAdd = []string{"2222"}
  206. testCases := []struct {
  207. name string
  208. securityContext *runtimeapi.LinuxContainerSecurityContext
  209. expected *dockercontainer.HostConfig
  210. }{
  211. {
  212. name: "nil",
  213. securityContext: nil,
  214. expected: &dockercontainer.HostConfig{},
  215. },
  216. {
  217. name: "SupplementalGroup",
  218. securityContext: supplementalGroupsSC,
  219. expected: supplementalGroupHC,
  220. },
  221. }
  222. for _, tc := range testCases {
  223. dockerCfg := &dockercontainer.HostConfig{}
  224. modifyHostConfig(tc.securityContext, dockerCfg, '=')
  225. assert.Equal(t, tc.expected, dockerCfg, "[Test case %q]", tc.name)
  226. }
  227. }
  228. func TestModifyHostConfigAndNamespaceOptionsForContainer(t *testing.T) {
  229. priv := true
  230. sandboxID := "sandbox"
  231. sandboxNSMode := fmt.Sprintf("container:%v", sandboxID)
  232. setPrivSC := &runtimeapi.LinuxContainerSecurityContext{}
  233. setPrivSC.Privileged = priv
  234. setPrivHC := &dockercontainer.HostConfig{
  235. Privileged: true,
  236. IpcMode: dockercontainer.IpcMode(sandboxNSMode),
  237. NetworkMode: dockercontainer.NetworkMode(sandboxNSMode),
  238. PidMode: dockercontainer.PidMode(sandboxNSMode),
  239. }
  240. setCapsHC := &dockercontainer.HostConfig{
  241. CapAdd: []string{"addCapA", "addCapB"},
  242. CapDrop: []string{"dropCapA", "dropCapB"},
  243. IpcMode: dockercontainer.IpcMode(sandboxNSMode),
  244. NetworkMode: dockercontainer.NetworkMode(sandboxNSMode),
  245. PidMode: dockercontainer.PidMode(sandboxNSMode),
  246. }
  247. setSELinuxHC := &dockercontainer.HostConfig{
  248. SecurityOpt: []string{
  249. fmt.Sprintf("%s:%s", selinuxLabelUser('='), "user"),
  250. fmt.Sprintf("%s:%s", selinuxLabelRole('='), "role"),
  251. fmt.Sprintf("%s:%s", selinuxLabelType('='), "type"),
  252. fmt.Sprintf("%s:%s", selinuxLabelLevel('='), "level"),
  253. },
  254. IpcMode: dockercontainer.IpcMode(sandboxNSMode),
  255. NetworkMode: dockercontainer.NetworkMode(sandboxNSMode),
  256. PidMode: dockercontainer.PidMode(sandboxNSMode),
  257. }
  258. cases := []struct {
  259. name string
  260. sc *runtimeapi.LinuxContainerSecurityContext
  261. expected *dockercontainer.HostConfig
  262. }{
  263. {
  264. name: "container.SecurityContext.Privileged",
  265. sc: setPrivSC,
  266. expected: setPrivHC,
  267. },
  268. {
  269. name: "container.SecurityContext.Capabilities",
  270. sc: &runtimeapi.LinuxContainerSecurityContext{
  271. Capabilities: inputCapabilities(),
  272. },
  273. expected: setCapsHC,
  274. },
  275. {
  276. name: "container.SecurityContext.SELinuxOptions",
  277. sc: &runtimeapi.LinuxContainerSecurityContext{
  278. SelinuxOptions: inputSELinuxOptions(),
  279. },
  280. expected: setSELinuxHC,
  281. },
  282. }
  283. for _, tc := range cases {
  284. dockerCfg := &dockercontainer.HostConfig{}
  285. modifyHostConfig(tc.sc, dockerCfg, '=')
  286. modifyContainerNamespaceOptions(tc.sc.GetNamespaceOptions(), sandboxID, dockerCfg)
  287. assert.Equal(t, tc.expected, dockerCfg, "[Test case %q]", tc.name)
  288. }
  289. }
  290. func TestModifySandboxNamespaceOptions(t *testing.T) {
  291. cases := []struct {
  292. name string
  293. nsOpt *runtimeapi.NamespaceOption
  294. expected *dockercontainer.HostConfig
  295. }{
  296. {
  297. name: "Host Network NamespaceOption",
  298. nsOpt: &runtimeapi.NamespaceOption{
  299. Network: runtimeapi.NamespaceMode_NODE,
  300. },
  301. expected: &dockercontainer.HostConfig{
  302. NetworkMode: namespaceModeHost,
  303. },
  304. },
  305. {
  306. name: "Host IPC NamespaceOption",
  307. nsOpt: &runtimeapi.NamespaceOption{
  308. Ipc: runtimeapi.NamespaceMode_NODE,
  309. },
  310. expected: &dockercontainer.HostConfig{
  311. IpcMode: namespaceModeHost,
  312. NetworkMode: "default",
  313. },
  314. },
  315. {
  316. name: "Host PID NamespaceOption",
  317. nsOpt: &runtimeapi.NamespaceOption{
  318. Pid: runtimeapi.NamespaceMode_NODE,
  319. },
  320. expected: &dockercontainer.HostConfig{
  321. PidMode: namespaceModeHost,
  322. NetworkMode: "default",
  323. },
  324. },
  325. }
  326. for _, tc := range cases {
  327. dockerCfg := &dockercontainer.HostConfig{}
  328. modifySandboxNamespaceOptions(tc.nsOpt, dockerCfg, nil)
  329. assert.Equal(t, tc.expected, dockerCfg, "[Test case %q]", tc.name)
  330. }
  331. }
  332. func TestModifyContainerNamespaceOptions(t *testing.T) {
  333. sandboxID := "sandbox"
  334. sandboxNSMode := fmt.Sprintf("container:%v", sandboxID)
  335. cases := []struct {
  336. name string
  337. nsOpt *runtimeapi.NamespaceOption
  338. expected *dockercontainer.HostConfig
  339. }{
  340. {
  341. name: "Host Network NamespaceOption",
  342. nsOpt: &runtimeapi.NamespaceOption{
  343. Network: runtimeapi.NamespaceMode_NODE,
  344. },
  345. expected: &dockercontainer.HostConfig{
  346. NetworkMode: dockercontainer.NetworkMode(sandboxNSMode),
  347. IpcMode: dockercontainer.IpcMode(sandboxNSMode),
  348. UTSMode: namespaceModeHost,
  349. PidMode: dockercontainer.PidMode(sandboxNSMode),
  350. },
  351. },
  352. {
  353. name: "Host IPC NamespaceOption",
  354. nsOpt: &runtimeapi.NamespaceOption{
  355. Ipc: runtimeapi.NamespaceMode_NODE,
  356. },
  357. expected: &dockercontainer.HostConfig{
  358. NetworkMode: dockercontainer.NetworkMode(sandboxNSMode),
  359. IpcMode: dockercontainer.IpcMode(sandboxNSMode),
  360. PidMode: dockercontainer.PidMode(sandboxNSMode),
  361. },
  362. },
  363. {
  364. name: "Host PID NamespaceOption",
  365. nsOpt: &runtimeapi.NamespaceOption{
  366. Pid: runtimeapi.NamespaceMode_NODE,
  367. },
  368. expected: &dockercontainer.HostConfig{
  369. NetworkMode: dockercontainer.NetworkMode(sandboxNSMode),
  370. IpcMode: dockercontainer.IpcMode(sandboxNSMode),
  371. PidMode: namespaceModeHost,
  372. },
  373. },
  374. }
  375. for _, tc := range cases {
  376. dockerCfg := &dockercontainer.HostConfig{}
  377. modifyContainerNamespaceOptions(tc.nsOpt, sandboxID, dockerCfg)
  378. assert.Equal(t, tc.expected, dockerCfg, "[Test case %q]", tc.name)
  379. }
  380. }
  381. func TestModifyContainerNamespacePIDOverride(t *testing.T) {
  382. cases := []struct {
  383. name string
  384. version *semver.Version
  385. input, expected dockercontainer.PidMode
  386. }{
  387. {
  388. name: "mode:CONTAINER docker:NEW",
  389. version: &semver.Version{Major: 1, Minor: 26},
  390. input: "",
  391. expected: "",
  392. },
  393. {
  394. name: "mode:CONTAINER docker:OLD",
  395. version: &semver.Version{Major: 1, Minor: 25},
  396. input: "",
  397. expected: "",
  398. },
  399. {
  400. name: "mode:HOST docker:NEW",
  401. version: &semver.Version{Major: 1, Minor: 26},
  402. input: "host",
  403. expected: "host",
  404. },
  405. {
  406. name: "mode:HOST docker:OLD",
  407. version: &semver.Version{Major: 1, Minor: 25},
  408. input: "host",
  409. expected: "host",
  410. },
  411. {
  412. name: "mode:POD docker:NEW",
  413. version: &semver.Version{Major: 1, Minor: 26},
  414. input: "container:sandbox",
  415. expected: "container:sandbox",
  416. },
  417. {
  418. name: "mode:POD docker:OLD",
  419. version: &semver.Version{Major: 1, Minor: 25},
  420. input: "container:sandbox",
  421. expected: "",
  422. },
  423. }
  424. for _, tc := range cases {
  425. dockerCfg := &dockercontainer.HostConfig{PidMode: tc.input}
  426. modifyContainerPIDNamespaceOverrides(tc.version, dockerCfg, "sandbox")
  427. assert.Equal(t, tc.expected, dockerCfg.PidMode, "[Test case %q]", tc.name)
  428. }
  429. }
  430. func fullValidSecurityContext() *runtimeapi.LinuxContainerSecurityContext {
  431. return &runtimeapi.LinuxContainerSecurityContext{
  432. Privileged: true,
  433. Capabilities: inputCapabilities(),
  434. SelinuxOptions: inputSELinuxOptions(),
  435. }
  436. }
  437. func inputCapabilities() *runtimeapi.Capability {
  438. return &runtimeapi.Capability{
  439. AddCapabilities: []string{"addCapA", "addCapB"},
  440. DropCapabilities: []string{"dropCapA", "dropCapB"},
  441. }
  442. }
  443. func inputSELinuxOptions() *runtimeapi.SELinuxOption {
  444. user := "user"
  445. role := "role"
  446. stype := "type"
  447. level := "level"
  448. return &runtimeapi.SELinuxOption{
  449. User: user,
  450. Role: role,
  451. Type: stype,
  452. Level: level,
  453. }
  454. }
  455. func fullValidHostConfig() *dockercontainer.HostConfig {
  456. return &dockercontainer.HostConfig{
  457. Privileged: true,
  458. CapAdd: []string{"addCapA", "addCapB"},
  459. CapDrop: []string{"dropCapA", "dropCapB"},
  460. SecurityOpt: []string{
  461. fmt.Sprintf("%s:%s", selinuxLabelUser('='), "user"),
  462. fmt.Sprintf("%s:%s", selinuxLabelRole('='), "role"),
  463. fmt.Sprintf("%s:%s", selinuxLabelType('='), "type"),
  464. fmt.Sprintf("%s:%s", selinuxLabelLevel('='), "level"),
  465. },
  466. }
  467. }