validator.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. package validate
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "strings"
  7. "github.com/opencontainers/runc/libcontainer/configs"
  8. )
  9. type Validator interface {
  10. Validate(*configs.Config) error
  11. }
  12. func New() Validator {
  13. return &ConfigValidator{}
  14. }
  15. type ConfigValidator struct {
  16. }
  17. func (v *ConfigValidator) Validate(config *configs.Config) error {
  18. if err := v.rootfs(config); err != nil {
  19. return err
  20. }
  21. if err := v.network(config); err != nil {
  22. return err
  23. }
  24. if err := v.hostname(config); err != nil {
  25. return err
  26. }
  27. if err := v.security(config); err != nil {
  28. return err
  29. }
  30. if err := v.usernamespace(config); err != nil {
  31. return err
  32. }
  33. if err := v.sysctl(config); err != nil {
  34. return err
  35. }
  36. return nil
  37. }
  38. // rootfs validates if the rootfs is an absolute path and is not a symlink
  39. // to the container's root filesystem.
  40. func (v *ConfigValidator) rootfs(config *configs.Config) error {
  41. cleaned, err := filepath.Abs(config.Rootfs)
  42. if err != nil {
  43. return err
  44. }
  45. if cleaned, err = filepath.EvalSymlinks(cleaned); err != nil {
  46. return err
  47. }
  48. if filepath.Clean(config.Rootfs) != cleaned {
  49. return fmt.Errorf("%s is not an absolute path or is a symlink", config.Rootfs)
  50. }
  51. return nil
  52. }
  53. func (v *ConfigValidator) network(config *configs.Config) error {
  54. if !config.Namespaces.Contains(configs.NEWNET) {
  55. if len(config.Networks) > 0 || len(config.Routes) > 0 {
  56. return fmt.Errorf("unable to apply network settings without a private NET namespace")
  57. }
  58. }
  59. return nil
  60. }
  61. func (v *ConfigValidator) hostname(config *configs.Config) error {
  62. if config.Hostname != "" && !config.Namespaces.Contains(configs.NEWUTS) {
  63. return fmt.Errorf("unable to set hostname without a private UTS namespace")
  64. }
  65. return nil
  66. }
  67. func (v *ConfigValidator) security(config *configs.Config) error {
  68. // restrict sys without mount namespace
  69. if (len(config.MaskPaths) > 0 || len(config.ReadonlyPaths) > 0) &&
  70. !config.Namespaces.Contains(configs.NEWNS) {
  71. return fmt.Errorf("unable to restrict sys entries without a private MNT namespace")
  72. }
  73. return nil
  74. }
  75. func (v *ConfigValidator) usernamespace(config *configs.Config) error {
  76. if config.Namespaces.Contains(configs.NEWUSER) {
  77. if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
  78. return fmt.Errorf("USER namespaces aren't enabled in the kernel")
  79. }
  80. } else {
  81. if config.UidMappings != nil || config.GidMappings != nil {
  82. return fmt.Errorf("User namespace mappings specified, but USER namespace isn't enabled in the config")
  83. }
  84. }
  85. return nil
  86. }
  87. // sysctl validates that the specified sysctl keys are valid or not.
  88. // /proc/sys isn't completely namespaced and depending on which namespaces
  89. // are specified, a subset of sysctls are permitted.
  90. func (v *ConfigValidator) sysctl(config *configs.Config) error {
  91. validSysctlMap := map[string]bool{
  92. "kernel.msgmax": true,
  93. "kernel.msgmnb": true,
  94. "kernel.msgmni": true,
  95. "kernel.sem": true,
  96. "kernel.shmall": true,
  97. "kernel.shmmax": true,
  98. "kernel.shmmni": true,
  99. "kernel.shm_rmid_forced": true,
  100. }
  101. for s := range config.Sysctl {
  102. if validSysctlMap[s] || strings.HasPrefix(s, "fs.mqueue.") {
  103. if config.Namespaces.Contains(configs.NEWIPC) {
  104. continue
  105. } else {
  106. return fmt.Errorf("sysctl %q is not allowed in the hosts ipc namespace", s)
  107. }
  108. }
  109. if strings.HasPrefix(s, "net.") {
  110. if config.Namespaces.Contains(configs.NEWNET) {
  111. continue
  112. } else {
  113. return fmt.Errorf("sysctl %q is not allowed in the hosts network namespace", s)
  114. }
  115. }
  116. return fmt.Errorf("sysctl %q is not in a separate kernel namespace", s)
  117. }
  118. return nil
  119. }