Class: OsCtld::AppArmor

Inherits:
Object
  • Object
show all
Extended by:
OsCtl::Lib::Utils::System
Includes:
OsCtl::Lib::Utils::Log, OsCtl::Lib::Utils::System
Defined in:
lib/osctld/apparmor.rb

Constant Summary collapse

PATHS =

Paths where `apparmor_parser` searches for configuration files

[RunState::APPARMOR_DIR]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ct) ⇒ AppArmor

Returns a new instance of AppArmor

Parameters:



93
94
95
# File 'lib/osctld/apparmor.rb', line 93

def initialize(ct)
  @ct = ct
end

Instance Attribute Details

#ctObject (readonly, protected)

Returns the value of attribute ct



176
177
178
# File 'lib/osctld/apparmor.rb', line 176

def ct
  @ct
end

Class Method Details

.apparmor_parser(pool, cmd, profiles, opts = {}) ⇒ Object

Call apparmor_parser

Parameters:

  • pool (Pool)
  • cmd ("a", "r", "R")
  • profiles (Array<String>)

    absolute paths to profiles

  • opts (Hash) (defaults to: {})

    options for `syscmd`



84
85
86
87
88
89
90
# File 'lib/osctld/apparmor.rb', line 84

def self.apparmor_parser(pool, cmd, profiles, opts = {})
  syscmd(
    "apparmor_parser -#{cmd} -W -v #{PATHS.map { |v| "-I #{v}" }.join(' ')} "+
    "-L #{cache_dir(pool)} #{profiles.join(' ')}",
    opts
  )
end

.assets(add, pool) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/osctld/apparmor.rb', line 62

def self.assets(add, pool)
  add.directory(
    profile_dir(pool),
    desc: 'Per-container AppArmor profiles',
    user: 0,
    group: 0,
    mode: 0700
  )
  add.directory(
    profile_dir(pool),
    desc: 'Cache for apparmor_parser',
    user: 0,
    group: 0,
    mode: 0700
  )
end

.cache_dir(pool) ⇒ Object

Per-pool runstate directory with profile cache



58
59
60
# File 'lib/osctld/apparmor.rb', line 58

def self.cache_dir(pool)
  File.join(pool.apparmor_dir, 'cache')
end

.profile_dir(pool) ⇒ Object

Per-pool runstate directory with profiles



53
54
55
# File 'lib/osctld/apparmor.rb', line 53

def self.profile_dir(pool)
  File.join(pool.apparmor_dir, 'profiles')
end

.setupObject

Prepare shared files in `/run/osctl`



18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/osctld/apparmor.rb', line 18

def self.setup
  base = File.join(RunState::APPARMOR_DIR, 'osctl')
  features = File.join(base, 'features')

  [base, features].each do |dir|
    Dir.mkdir(dir, 0755) unless Dir.exist?(dir)
  end

  ErbTemplate.render_to(
    'apparmor/features/nesting',
    {},
    File.join(features, 'nesting')
  )
end

.setup_pool(pool) ⇒ Object

Load profiles of running containers from `pool`

Parameters:



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/osctld/apparmor.rb', line 35

def self.setup_pool(pool)
  [profile_dir(pool), cache_dir(pool)].each do |dir|
    Dir.mkdir(dir, 0700) unless Dir.exist?(dir)
  end

  cts = DB::Containers.get.select do |ct|
    next(false) if ct.pool != pool || !ct.running?

    ct.apparmor.generate_profile
    true
  end

  if cts.any?
    apparmor_parser(pool, 'r', cts.map { |ct| ct.apparmor.profile_path })
  end
end

Instance Method Details

#apparmor_parser(cmd, opts = {}) ⇒ Object (protected)



186
187
188
# File 'lib/osctld/apparmor.rb', line 186

def apparmor_parser(cmd, opts = {})
  self.class.apparmor_parser(ct.pool, cmd, [profile_path], opts)
end

#cache_dirObject (protected)



182
183
184
# File 'lib/osctld/apparmor.rb', line 182

def cache_dir
  self.class.cache_dir(ct.pool)
end

#create_namespaceObject

Create an AppArmor namespace for the container



140
141
142
143
# File 'lib/osctld/apparmor.rb', line 140

def create_namespace
  path = namespace_path
  Dir.mkdir(path) unless Dir.exist?(path)
end

#destroy_namespaceObject

Destroy the container's AppArmor namespace



146
147
148
149
# File 'lib/osctld/apparmor.rb', line 146

def destroy_namespace
  path = namespace_path
  Dir.rmdir(path) if Dir.exist?(path)
end

#destroy_profileObject

Remove the container's profile from the kernel and remove it from cache



132
133
134
135
136
137
# File 'lib/osctld/apparmor.rb', line 132

def destroy_profile
  unload_profile

  cached = File.join(cache_dir, profile_name)
  File.unlink(cached) if File.exist?(cached)
end

#dup(new_ct) ⇒ Object



169
170
171
172
173
# File 'lib/osctld/apparmor.rb', line 169

def dup(new_ct)
  ret = super()
  ret.instance_variable_set('@ct', new_ct)
  ret
end

#generate_profileObject

Generate AppArmor profile for the container

The profile is generated only if it has been changed to let `apparmor_parser` use cached profiles for faster container startup times.



108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/osctld/apparmor.rb', line 108

def generate_profile
  ErbTemplate.render_to_if_changed('apparmor/profile', {
    name: profile_name,
    namespace: namespace,
    ct: ct,
    all_combinations_of: ->(arr) do
      ret = []
      arr.count.times { |i| ret.concat(arr.combination(i+1).to_a) }
      ret
    end,
  }, profile_path)
end

#load_profileObject

Load the container's profile into the kernel



122
123
124
# File 'lib/osctld/apparmor.rb', line 122

def load_profile
  apparmor_parser('r')
end

#namespaceObject



159
160
161
162
163
# File 'lib/osctld/apparmor.rb', line 159

def namespace
  # Ubuntu's AppArmor service initializes profiles only when in a namespace
  # beginning with `lxd-` or `lxc-`, so we have to use the prefix as well.
  "lxc-#{profile_name}"
end

#namespace_pathObject (protected)



178
179
180
# File 'lib/osctld/apparmor.rb', line 178

def namespace_path
  File.join('/sys/kernel/security/apparmor/policy/namespaces', namespace)
end

#namespace_profile_nameObject



165
166
167
# File 'lib/osctld/apparmor.rb', line 165

def namespace_profile_name
  "#{profile_name}//&:#{namespace}:"
end

#profile_nameObject



151
152
153
# File 'lib/osctld/apparmor.rb', line 151

def profile_name
  "ct-#{ct.pool.name}-#{ct.id}"
end

#profile_pathObject



155
156
157
# File 'lib/osctld/apparmor.rb', line 155

def profile_path
  File.join(self.class.profile_dir(ct.pool), profile_name)
end

#setupObject

Generate container profile, load it and create a namespace



98
99
100
101
102
# File 'lib/osctld/apparmor.rb', line 98

def setup
  generate_profile
  load_profile
  create_namespace
end

#unload_profileObject

Remove the container's profile from the kernel



127
128
129
# File 'lib/osctld/apparmor.rb', line 127

def unload_profile
  apparmor_parser('R', valid_rcs: [254])
end