rootless.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. package validate
  2. import (
  3. "errors"
  4. "fmt"
  5. "strings"
  6. "github.com/opencontainers/runc/libcontainer/configs"
  7. )
  8. // rootlessEUID makes sure that the config can be applied when runc
  9. // is being executed as a non-root user (euid != 0) in the current user namespace.
  10. func (v *ConfigValidator) rootlessEUID(config *configs.Config) error {
  11. if !config.RootlessEUID {
  12. return nil
  13. }
  14. if err := rootlessEUIDMappings(config); err != nil {
  15. return err
  16. }
  17. if err := rootlessEUIDMount(config); err != nil {
  18. return err
  19. }
  20. // XXX: We currently can't verify the user config at all, because
  21. // configs.Config doesn't store the user-related configs. So this
  22. // has to be verified by setupUser() in init_linux.go.
  23. return nil
  24. }
  25. func hasIDMapping(id int, mappings []configs.IDMap) bool {
  26. for _, m := range mappings {
  27. if id >= m.ContainerID && id < m.ContainerID+m.Size {
  28. return true
  29. }
  30. }
  31. return false
  32. }
  33. func rootlessEUIDMappings(config *configs.Config) error {
  34. if !config.Namespaces.Contains(configs.NEWUSER) {
  35. return errors.New("rootless container requires user namespaces")
  36. }
  37. if len(config.UidMappings) == 0 {
  38. return errors.New("rootless containers requires at least one UID mapping")
  39. }
  40. if len(config.GidMappings) == 0 {
  41. return errors.New("rootless containers requires at least one GID mapping")
  42. }
  43. return nil
  44. }
  45. // mount verifies that the user isn't trying to set up any mounts they don't have
  46. // the rights to do. In addition, it makes sure that no mount has a `uid=` or
  47. // `gid=` option that doesn't resolve to root.
  48. func rootlessEUIDMount(config *configs.Config) error {
  49. // XXX: We could whitelist allowed devices at this point, but I'm not
  50. // convinced that's a good idea. The kernel is the best arbiter of
  51. // access control.
  52. for _, mount := range config.Mounts {
  53. // Check that the options list doesn't contain any uid= or gid= entries
  54. // that don't resolve to root.
  55. for _, opt := range strings.Split(mount.Data, ",") {
  56. if strings.HasPrefix(opt, "uid=") {
  57. var uid int
  58. n, err := fmt.Sscanf(opt, "uid=%d", &uid)
  59. if n != 1 || err != nil {
  60. // Ignore unknown mount options.
  61. continue
  62. }
  63. if !hasIDMapping(uid, config.UidMappings) {
  64. return errors.New("cannot specify uid= mount options for unmapped uid in rootless containers")
  65. }
  66. }
  67. if strings.HasPrefix(opt, "gid=") {
  68. var gid int
  69. n, err := fmt.Sscanf(opt, "gid=%d", &gid)
  70. if n != 1 || err != nil {
  71. // Ignore unknown mount options.
  72. continue
  73. }
  74. if !hasIDMapping(gid, config.GidMappings) {
  75. return errors.New("cannot specify gid= mount options for unmapped gid in rootless containers")
  76. }
  77. }
  78. }
  79. }
  80. return nil
  81. }