subpath_nsenter_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. // +build linux
  2. /*
  3. Copyright 2017 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 subpath
  15. import (
  16. "io/ioutil"
  17. "os"
  18. "path/filepath"
  19. "strings"
  20. "testing"
  21. "golang.org/x/sys/unix"
  22. "k8s.io/utils/nsenter"
  23. nsutil "k8s.io/kubernetes/pkg/volume/util/nsenter"
  24. )
  25. func TestCheckDeviceInode(t *testing.T) {
  26. testDir, err := ioutil.TempDir("", "nsenter-mounter-device-")
  27. if err != nil {
  28. t.Fatalf("Cannot create temporary directory: %s", err)
  29. }
  30. defer os.RemoveAll(testDir)
  31. tests := []struct {
  32. name string
  33. srcPath string
  34. dstPath string
  35. expectError string
  36. }{
  37. {
  38. name: "the same file",
  39. srcPath: filepath.Join(testDir, "1"),
  40. dstPath: filepath.Join(testDir, "1"),
  41. expectError: "",
  42. },
  43. {
  44. name: "different file on the same FS",
  45. srcPath: filepath.Join(testDir, "2.1"),
  46. dstPath: filepath.Join(testDir, "2.2"),
  47. expectError: "different inode",
  48. },
  49. {
  50. name: "different file on different device",
  51. srcPath: filepath.Join(testDir, "3"),
  52. // /proc is always on a different "device" than /tmp (or $TEMP)
  53. dstPath: "/proc/self/status",
  54. expectError: "different device",
  55. },
  56. }
  57. for _, test := range tests {
  58. if err := ioutil.WriteFile(test.srcPath, []byte{}, 0644); err != nil {
  59. t.Errorf("Test %q: cannot create srcPath %s: %s", test.name, test.srcPath, err)
  60. continue
  61. }
  62. // Don't create dst if it exists
  63. if _, err := os.Stat(test.dstPath); os.IsNotExist(err) {
  64. if err := ioutil.WriteFile(test.dstPath, []byte{}, 0644); err != nil {
  65. t.Errorf("Test %q: cannot create dstPath %s: %s", test.name, test.dstPath, err)
  66. continue
  67. }
  68. } else if err != nil {
  69. t.Errorf("Test %q: cannot check existence of dstPath %s: %s", test.name, test.dstPath, err)
  70. continue
  71. }
  72. fd, err := unix.Open(test.srcPath, unix.O_CREAT, 0644)
  73. if err != nil {
  74. t.Errorf("Test %q: cannot open srcPath %s: %s", test.name, test.srcPath, err)
  75. continue
  76. }
  77. err = checkDeviceInode(fd, test.dstPath)
  78. if test.expectError == "" && err != nil {
  79. t.Errorf("Test %q: expected no error, got %s", test.name, err)
  80. }
  81. if test.expectError != "" {
  82. if err == nil {
  83. t.Errorf("Test %q: expected error, got none", test.name)
  84. } else {
  85. if !strings.Contains(err.Error(), test.expectError) {
  86. t.Errorf("Test %q: expected error %q, got %q", test.name, test.expectError, err)
  87. }
  88. }
  89. }
  90. }
  91. }
  92. func newFakeNsenterMounter(tmpdir string, t *testing.T) (*nsutil.Mounter, string, string, *nsenter.Nsenter, error) {
  93. rootfsPath := filepath.Join(tmpdir, "rootfs")
  94. if err := os.Mkdir(rootfsPath, 0755); err != nil {
  95. return nil, "", "", nil, err
  96. }
  97. ne, err := nsenter.NewFakeNsenter(rootfsPath)
  98. if err != nil {
  99. return nil, "", "", nil, err
  100. }
  101. varlibPath := filepath.Join(tmpdir, "/var/lib/kubelet")
  102. if err := os.MkdirAll(varlibPath, 0755); err != nil {
  103. return nil, "", "", nil, err
  104. }
  105. return nsutil.NewMounter(varlibPath, ne), rootfsPath, varlibPath, ne, nil
  106. }
  107. func TestNsenterSafeMakeDir(t *testing.T) {
  108. tests := []struct {
  109. name string
  110. prepare func(base, rootfs, varlib string) (expectedDir string, err error)
  111. subdir string
  112. expectError bool
  113. // If true, "base" directory for SafeMakeDir will be /var/lib/kubelet
  114. baseIsVarLib bool
  115. }{
  116. {
  117. name: "simple directory",
  118. // evaluated in base
  119. subdir: "some/subdirectory/structure",
  120. prepare: func(base, rootfs, varlib string) (expectedDir string, err error) {
  121. // expected to be created in /roots/
  122. expectedDir = filepath.Join(rootfs, base, "some/subdirectory/structure")
  123. return expectedDir, nil
  124. },
  125. },
  126. {
  127. name: "simple existing directory",
  128. // evaluated in base
  129. subdir: "some/subdirectory/structure",
  130. prepare: func(base, rootfs, varlib string) (expectedDir string, err error) {
  131. // On the host: directory exists
  132. hostPath := filepath.Join(base, "some/subdirectory/structure")
  133. if err := os.MkdirAll(hostPath, 0755); err != nil {
  134. return "", err
  135. }
  136. // In rootfs: directory exists
  137. kubeletPath := filepath.Join(rootfs, hostPath)
  138. if err := os.MkdirAll(kubeletPath, 0755); err != nil {
  139. return "", err
  140. }
  141. // expected to be created in /roots/
  142. expectedDir = kubeletPath
  143. return expectedDir, nil
  144. },
  145. },
  146. {
  147. name: "absolute symlink into safe place",
  148. // evaluated in base
  149. subdir: "some/subdirectory/structure",
  150. prepare: func(base, rootfs, varlib string) (expectedDir string, err error) {
  151. // On the host: /base/other/subdirectory exists, /base/some is link to /base/other
  152. hostPath := filepath.Join(base, "other/subdirectory")
  153. if err := os.MkdirAll(hostPath, 0755); err != nil {
  154. return "", err
  155. }
  156. somePath := filepath.Join(base, "some")
  157. otherPath := filepath.Join(base, "other")
  158. if err := os.Symlink(otherPath, somePath); err != nil {
  159. return "", err
  160. }
  161. // In rootfs: /base/other/subdirectory exists
  162. kubeletPath := filepath.Join(rootfs, hostPath)
  163. if err := os.MkdirAll(kubeletPath, 0755); err != nil {
  164. return "", err
  165. }
  166. // expected 'structure' to be created
  167. expectedDir = filepath.Join(rootfs, hostPath, "structure")
  168. return expectedDir, nil
  169. },
  170. },
  171. {
  172. name: "relative symlink into safe place",
  173. // evaluated in base
  174. subdir: "some/subdirectory/structure",
  175. prepare: func(base, rootfs, varlib string) (expectedDir string, err error) {
  176. // On the host: /base/other/subdirectory exists, /base/some is link to other
  177. hostPath := filepath.Join(base, "other/subdirectory")
  178. if err := os.MkdirAll(hostPath, 0755); err != nil {
  179. return "", err
  180. }
  181. somePath := filepath.Join(base, "some")
  182. if err := os.Symlink("other", somePath); err != nil {
  183. return "", err
  184. }
  185. // In rootfs: /base/other/subdirectory exists
  186. kubeletPath := filepath.Join(rootfs, hostPath)
  187. if err := os.MkdirAll(kubeletPath, 0755); err != nil {
  188. return "", err
  189. }
  190. // expected 'structure' to be created
  191. expectedDir = filepath.Join(rootfs, hostPath, "structure")
  192. return expectedDir, nil
  193. },
  194. },
  195. {
  196. name: "symlink into unsafe place",
  197. // evaluated in base
  198. subdir: "some/subdirectory/structure",
  199. prepare: func(base, rootfs, varlib string) (expectedDir string, err error) {
  200. // On the host: /base/some is link to /bin/other
  201. somePath := filepath.Join(base, "some")
  202. if err := os.Symlink("/bin", somePath); err != nil {
  203. return "", err
  204. }
  205. return "", nil
  206. },
  207. expectError: true,
  208. },
  209. {
  210. name: "simple directory in /var/lib/kubelet",
  211. // evaluated in varlib
  212. subdir: "some/subdirectory/structure",
  213. baseIsVarLib: true,
  214. prepare: func(base, rootfs, varlib string) (expectedDir string, err error) {
  215. // expected to be created in /base/var/lib/kubelet, not in /rootfs!
  216. expectedDir = filepath.Join(varlib, "some/subdirectory/structure")
  217. return expectedDir, nil
  218. },
  219. },
  220. {
  221. name: "safe symlink in /var/lib/kubelet",
  222. // evaluated in varlib
  223. subdir: "some/subdirectory/structure",
  224. baseIsVarLib: true,
  225. prepare: func(base, rootfs, varlib string) (expectedDir string, err error) {
  226. // On the host: /varlib/kubelet/other/subdirectory exists, /varlib/some is link to other
  227. hostPath := filepath.Join(varlib, "other/subdirectory")
  228. if err := os.MkdirAll(hostPath, 0755); err != nil {
  229. return "", err
  230. }
  231. somePath := filepath.Join(varlib, "some")
  232. if err := os.Symlink("other", somePath); err != nil {
  233. return "", err
  234. }
  235. // expected to be created in /base/var/lib/kubelet, not in /rootfs!
  236. expectedDir = filepath.Join(varlib, "other/subdirectory/structure")
  237. return expectedDir, nil
  238. },
  239. },
  240. {
  241. name: "unsafe symlink in /var/lib/kubelet",
  242. // evaluated in varlib
  243. subdir: "some/subdirectory/structure",
  244. baseIsVarLib: true,
  245. prepare: func(base, rootfs, varlib string) (expectedDir string, err error) {
  246. // On the host: /varlib/some is link to /bin
  247. somePath := filepath.Join(varlib, "some")
  248. if err := os.Symlink("/bin", somePath); err != nil {
  249. return "", err
  250. }
  251. return "", nil
  252. },
  253. expectError: true,
  254. },
  255. }
  256. for _, test := range tests {
  257. tmpdir, err := ioutil.TempDir("", "nsenter-get-safedir-")
  258. if err != nil {
  259. t.Error(err)
  260. continue
  261. }
  262. defer os.RemoveAll(tmpdir)
  263. mounter, rootfs, varlib, ne, err := newFakeNsenterMounter(tmpdir, t)
  264. if err != nil {
  265. t.Error(err)
  266. continue
  267. }
  268. fsp := NewNSEnter(mounter, ne, varlib)
  269. // Prepare base directory for the test
  270. testBase := filepath.Join(tmpdir, "base")
  271. if err := os.Mkdir(testBase, 0755); err != nil {
  272. t.Error(err)
  273. continue
  274. }
  275. // Prepare base directory also in /rootfs
  276. rootfsBase := filepath.Join(rootfs, testBase)
  277. if err := os.MkdirAll(rootfsBase, 0755); err != nil {
  278. t.Error(err)
  279. continue
  280. }
  281. expectedDir := ""
  282. if test.prepare != nil {
  283. expectedDir, err = test.prepare(testBase, rootfs, varlib)
  284. if err != nil {
  285. t.Error(err)
  286. continue
  287. }
  288. }
  289. if test.baseIsVarLib {
  290. // use /var/lib/kubelet as the test base so we can test creating
  291. // subdirs there directly in /var/lib/kubenet and not in
  292. // /rootfs/var/lib/kubelet
  293. testBase = varlib
  294. }
  295. err = fsp.SafeMakeDir(test.subdir, testBase, 0755)
  296. if err != nil && !test.expectError {
  297. t.Errorf("Test %q: unexpected error: %s", test.name, err)
  298. }
  299. if test.expectError {
  300. if err == nil {
  301. t.Errorf("Test %q: expected error, got none", test.name)
  302. } else {
  303. if !strings.Contains(err.Error(), "is outside of allowed base") {
  304. t.Errorf("Test %q: expected error to contain \"is outside of allowed base\", got this one instead: %s", test.name, err)
  305. }
  306. }
  307. }
  308. if expectedDir != "" {
  309. _, err := os.Stat(expectedDir)
  310. if err != nil {
  311. t.Errorf("Test %q: expected %q to exist, got error: %s", test.name, expectedDir, err)
  312. }
  313. }
  314. }
  315. }