rootless.go 2.6 KB

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