Class: OsCtl::Image::Operations::Image::FixFileCapabilities

Inherits:
Base
  • Object
show all
Includes:
Lib::Utils::Log
Defined in:
lib/osctl/image/operations/image/fix_file_capabilities.rb

Overview

Fix file capabilities in a built image

Since the image is built in a user namespace, its file capabilities include user id (it’s a v3 file capability in kernel’s terms). This makes the capabilities to work only in the same or compatible user namespace, which is almost never the case in practice. By resetting the capabilities from the host, the kernel will store them without user id (v2 in kernel’s terms) and that will make them work in all user namespaces.

Defined Under Namespace

Classes: FileCapability

Instance Method Summary collapse

Methods inherited from Base

run

Constructor Details

#initialize(image, install_dir) ⇒ FixFileCapabilities

Returns a new instance of FixFileCapabilities.



26
27
28
29
30
# File 'lib/osctl/image/operations/image/fix_file_capabilities.rb', line 26

def initialize(image, install_dir)
  super()
  @image = image
  @install_dir = install_dir
end

Instance Method Details

#executeObject



32
33
34
# File 'lib/osctl/image/operations/image/fix_file_capabilities.rb', line 32

def execute
  fix_capabilities(read_capabilities)
end

#fix_capabilities(file_caps) ⇒ Object (protected)



95
96
97
98
99
100
101
# File 'lib/osctl/image/operations/image/fix_file_capabilities.rb', line 95

def fix_capabilities(file_caps)
  file_caps.each do |file_cap|
    unless Kernel.system('setcap', file_cap.setcap, file_cap.file)
      raise OperationError, "failed to set file capability #{file_cap}"
    end
  end
end

#log_typeObject



36
37
38
# File 'lib/osctl/image/operations/image/fix_file_capabilities.rb', line 36

def log_type
  "filecaps #{@image.name}"
end

#read_capabilitiesObject (protected)



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/osctl/image/operations/image/fix_file_capabilities.rb', line 42

def read_capabilities
  file_caps = []

  IO.popen("getcap -r #{@install_dir}", 'r') do |io|
    io.each_line do |line|
      # Example lines:
      # /file cap_setuid,cap_net_raw=ep
      # /file with spaces cap_setuid=ep
      # /allcaps =ep
      parts = line.strip.split

      # We explicitly do not handle files with spaces in their name, as those
      # will almost never be found in a base image and it is harder to parse.
      if parts.length != 2
        log(:warn, "Unhandled file capability #{line.inspect}")
        next
      end

      file, caps_str = parts
      caps = []
      cap_flags = nil

      caps_str.split(',').each do |v|
        if v.index('=')
          cap, flags = v.split('=')
        else
          cap = v
          flags = nil
        end

        if flags && cap_flags.nil?
          cap_flags = flags
        elsif flags && cap_flags && flags != cap_flags
          raise OperationError,
                "unexpected capability #{cap}=#{flags} on #{file.inspect}, expected flags #{cap_flags}"
        end

        caps << cap
      end

      file_cap = FileCapability.new(file, caps, cap_flags)
      log(:info, "Found file capability #{file_cap}")
      file_caps << file_cap
    end
  end

  if $?.exitstatus != 0
    raise OperationError, "getcap failed with exit status #{$?.exitstatus}"
  end

  file_caps
end