crypt_reboot 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +3 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +169 -0
  5. data/exe/cryptreboot +9 -0
  6. data/lib/basic_loader.rb +60 -0
  7. data/lib/crypt_reboot/boot_config.rb +24 -0
  8. data/lib/crypt_reboot/cli/exiter.rb +25 -0
  9. data/lib/crypt_reboot/cli/happy_exiter.rb +14 -0
  10. data/lib/crypt_reboot/cli/params/definition.rb +125 -0
  11. data/lib/crypt_reboot/cli/params/flattener.rb +24 -0
  12. data/lib/crypt_reboot/cli/params/help_generator.rb +22 -0
  13. data/lib/crypt_reboot/cli/params/parser.rb +29 -0
  14. data/lib/crypt_reboot/cli/params_parsing_executor.rb +73 -0
  15. data/lib/crypt_reboot/cli/sad_exiter.rb +14 -0
  16. data/lib/crypt_reboot/cli.rb +8 -0
  17. data/lib/crypt_reboot/concatenator.rb +24 -0
  18. data/lib/crypt_reboot/config.rb +20 -0
  19. data/lib/crypt_reboot/crypt_tab/deserializer.rb +28 -0
  20. data/lib/crypt_reboot/crypt_tab/entry.rb +39 -0
  21. data/lib/crypt_reboot/crypt_tab/entry_deserializer.rb +51 -0
  22. data/lib/crypt_reboot/crypt_tab/entry_serializer.rb +21 -0
  23. data/lib/crypt_reboot/crypt_tab/keyfile_locator.rb +21 -0
  24. data/lib/crypt_reboot/crypt_tab/luks_to_plain_converter.rb +39 -0
  25. data/lib/crypt_reboot/crypt_tab/serializer.rb +29 -0
  26. data/lib/crypt_reboot/files_generator.rb +44 -0
  27. data/lib/crypt_reboot/files_writer.rb +17 -0
  28. data/lib/crypt_reboot/gziper.rb +22 -0
  29. data/lib/crypt_reboot/initramfs/archiver.rb +35 -0
  30. data/lib/crypt_reboot/initramfs/decompressor/intolerant_decompressor.rb +65 -0
  31. data/lib/crypt_reboot/initramfs/decompressor/tolerant_decompressor.rb +28 -0
  32. data/lib/crypt_reboot/initramfs/decompressor.rb +12 -0
  33. data/lib/crypt_reboot/initramfs/extractor.rb +36 -0
  34. data/lib/crypt_reboot/initramfs/patcher.rb +47 -0
  35. data/lib/crypt_reboot/initramfs_patch_squeezer.rb +28 -0
  36. data/lib/crypt_reboot/instantiable_config.rb +52 -0
  37. data/lib/crypt_reboot/kexec/loader.rb +30 -0
  38. data/lib/crypt_reboot/kexec_patching_loader.rb +28 -0
  39. data/lib/crypt_reboot/lazy_config.rb +22 -0
  40. data/lib/crypt_reboot/luks/checker.rb +27 -0
  41. data/lib/crypt_reboot/luks/data.rb +37 -0
  42. data/lib/crypt_reboot/luks/data_fetcher.rb +30 -0
  43. data/lib/crypt_reboot/luks/dumper/luks_v1_parser.rb +64 -0
  44. data/lib/crypt_reboot/luks/dumper/luks_v2_parser.rb +62 -0
  45. data/lib/crypt_reboot/luks/dumper.rb +30 -0
  46. data/lib/crypt_reboot/luks/key_fetcher.rb +50 -0
  47. data/lib/crypt_reboot/luks/version_detector.rb +32 -0
  48. data/lib/crypt_reboot/passphrase_asker.rb +15 -0
  49. data/lib/crypt_reboot/patched_initramfs_generator.rb +21 -0
  50. data/lib/crypt_reboot/rebooter.rb +20 -0
  51. data/lib/crypt_reboot/runner/binary.rb +12 -0
  52. data/lib/crypt_reboot/runner/boolean.rb +18 -0
  53. data/lib/crypt_reboot/runner/generic.rb +46 -0
  54. data/lib/crypt_reboot/runner/lines.rb +13 -0
  55. data/lib/crypt_reboot/runner/no_result.rb +13 -0
  56. data/lib/crypt_reboot/runner/text.rb +12 -0
  57. data/lib/crypt_reboot/runner.rb +8 -0
  58. data/lib/crypt_reboot/safe_temp/directory.rb +28 -0
  59. data/lib/crypt_reboot/safe_temp/file_name.rb +23 -0
  60. data/lib/crypt_reboot/safe_temp/mounter.rb +33 -0
  61. data/lib/crypt_reboot/single_assign_restricted_map.rb +26 -0
  62. data/lib/crypt_reboot/version.rb +5 -0
  63. data/lib/crypt_reboot.rb +17 -0
  64. metadata +138 -0
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+
5
+ module CryptReboot
6
+ # Global configuration singleton
7
+ class Config < InstantiableConfig
8
+ include Singleton
9
+
10
+ class << self
11
+ def method_missing(method_name, *args, **kwargs, &block)
12
+ instance.respond_to?(method_name) ? instance.send(method_name, *args, **kwargs, &block) : super
13
+ end
14
+
15
+ def respond_to_missing?(name, *_, **_)
16
+ instance.respond_to?(name)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CryptReboot
4
+ module CryptTab
5
+ # Load crypttab file and return array with deserialized entries
6
+ class Deserializer
7
+ def call(filename = nil, content: File.read(filename))
8
+ split_to_important_lines(content).map do |line|
9
+ entry_deserializer.call line
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def split_to_important_lines(content)
16
+ content.split(/\n+|\r+/)
17
+ .reject(&:empty?)
18
+ .reject { |line| line.start_with? '#' }
19
+ end
20
+
21
+ attr_reader :entry_deserializer
22
+
23
+ def initialize(entry_deserializer: EntryDeserializer.new)
24
+ @entry_deserializer = entry_deserializer
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CryptReboot
4
+ module CryptTab
5
+ # Value-object describing entry in crypttab file
6
+ class Entry
7
+ attr_reader :target, :source, :key_file, :options, :flags
8
+
9
+ def ==(other)
10
+ target == other.target && source == other.source && key_file == other.key_file &&
11
+ options == other.options && flags.sort == other.flags.sort
12
+ end
13
+
14
+ def headevice(header_prefix: nil)
15
+ if header_prefix && header_path
16
+ File.join(header_prefix, header_path)
17
+ elsif header_path
18
+ header_path
19
+ else
20
+ source
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def header_path
27
+ options[:header]
28
+ end
29
+
30
+ def initialize(target:, source:, key_file:, options:, flags:)
31
+ @target = target
32
+ @source = source
33
+ @key_file = key_file
34
+ @options = options
35
+ @flags = flags
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CryptReboot
4
+ module CryptTab
5
+ # Deserialize crypttab line into value object
6
+ class EntryDeserializer
7
+ InvalidFormat = Class.new StandardError
8
+
9
+ def call(line)
10
+ target, source, key_file, raw_floptions = columns = line.split
11
+ raise InvalidFormat if columns.size < 3
12
+
13
+ floptions = raw_floptions.to_s.split(',')
14
+ flags = extract_flags(floptions)
15
+ options = extract_options(floptions)
16
+ entry_class.new(
17
+ target: target, source: source, key_file: key_file,
18
+ options: options, flags: flags
19
+ )
20
+ end
21
+
22
+ private
23
+
24
+ def extract_flags(floptions)
25
+ floptions.reject do |floption|
26
+ floption.include?('=')
27
+ end.map(&:to_sym)
28
+ end
29
+
30
+ def extract_options(floptions)
31
+ options = floptions.select do |floption|
32
+ floption.include?('=')
33
+ end
34
+ options.to_h do |option|
35
+ parse_option(option)
36
+ end
37
+ end
38
+
39
+ def parse_option(option)
40
+ name, value = option.split('=')
41
+ [name.to_sym, value.to_i.to_s == value ? value.to_i : value]
42
+ end
43
+
44
+ attr_reader :entry_class
45
+
46
+ def initialize(entry_class: Entry)
47
+ @entry_class = entry_class
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CryptReboot
4
+ module CryptTab
5
+ # Serialize crypttab entry into one line of text
6
+ class EntrySerializer
7
+ def call(entry)
8
+ floptions = (entry.flags + serialize_options(entry.options)).join(',')
9
+ [entry.target, entry.source, entry.key_file, floptions].join(' ')
10
+ end
11
+
12
+ private
13
+
14
+ def serialize_options(options)
15
+ options.map do |option, value|
16
+ [option, value].join('=')
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CryptReboot
4
+ module CryptTab
5
+ # Return path of keyfile for given target
6
+ class KeyfileLocator
7
+ def call(target)
8
+ File.join(base_dir, target + extension)
9
+ end
10
+
11
+ private
12
+
13
+ attr_reader :base_dir, :extension
14
+
15
+ def initialize(base_dir: '/cryptreboot', extension: '.key')
16
+ @base_dir = base_dir
17
+ @extension = extension
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CryptReboot
4
+ module CryptTab
5
+ # Convert given crypttab entry from LUKS to plain mode
6
+ class LuksToPlainConverter
7
+ def call(entry, data, keyfile)
8
+ entry_class.new(
9
+ target: entry.target,
10
+ source: entry.source,
11
+ key_file: keyfile,
12
+ options: convert_options(entry.options, data),
13
+ flags: entry.flags - [:luks] + [:plain]
14
+ )
15
+ end
16
+
17
+ private
18
+
19
+ # According to cryptsetup manual, offset is specified in 512-byte sectors.
20
+ # Therefore sector size of the actual device shouldn't be used.
21
+ OFFSET_SECTOR_SIZE = 512
22
+ private_constant :OFFSET_SECTOR_SIZE
23
+
24
+ def convert_options(options, data)
25
+ options = options.reject do |option, _|
26
+ %i[keyfile-size keyslot key-slot header keyscript].include? option
27
+ end
28
+ options[:'sector-size'] ||= data.sector_size # allow user to set it explicitly
29
+ options.merge({ cipher: data.cipher, size: data.key_bits, offset: data.offset / OFFSET_SECTOR_SIZE })
30
+ end
31
+
32
+ attr_reader :entry_class
33
+
34
+ def initialize(entry_class: Entry)
35
+ @entry_class = entry_class
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CryptReboot
4
+ module CryptTab
5
+ # Serialize entries and return crypttab file content as a string
6
+ class Serializer
7
+ def call(entries)
8
+ body = serialize(entries).join("\n")
9
+ "#{header}\n#{body}\n"
10
+ end
11
+
12
+ private
13
+
14
+ def serialize(entries)
15
+ entries.map do |entry|
16
+ entry_serializer.call(entry)
17
+ end
18
+ end
19
+
20
+ attr_reader :entry_serializer, :header
21
+
22
+ def initialize(entry_serializer: EntrySerializer.new,
23
+ header: '# This file has been patched by cryptreboot')
24
+ @entry_serializer = entry_serializer
25
+ @header = header
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CryptReboot
4
+ # Generate a hash with file names as keys and file contents as values
5
+ class FilesGenerator
6
+ def call(entries, base_dir)
7
+ files = {}
8
+ modified_entries = entries.map do |entry|
9
+ headevice = entry.headevice(header_prefix: base_dir)
10
+ next entry unless luks?(headevice)
11
+
12
+ data = luks_data_fetcher.call(headevice)
13
+ keyfile = keyfile_locator.call(entry.target)
14
+ files[keyfile] = data.key
15
+ entry_converter.call(entry, data, keyfile)
16
+ end
17
+ files.merge(CRYPTAB_PATH => serializer.call(modified_entries))
18
+ end
19
+
20
+ private
21
+
22
+ CRYPTAB_PATH = '/cryptroot/crypttab'
23
+ private_constant :CRYPTAB_PATH
24
+
25
+ def luks?(headevice)
26
+ luks_checker.call(headevice)
27
+ end
28
+
29
+ attr_reader :keyfile_locator, :entry_converter, :serializer,
30
+ :luks_data_fetcher, :luks_checker
31
+
32
+ def initialize(keyfile_locator: CryptTab::KeyfileLocator.new,
33
+ entry_converter: CryptTab::LuksToPlainConverter.new,
34
+ serializer: CryptTab::Serializer.new,
35
+ luks_data_fetcher: Luks::DataFetcher.new,
36
+ luks_checker: Luks::Checker.new)
37
+ @keyfile_locator = keyfile_locator
38
+ @entry_converter = entry_converter
39
+ @serializer = serializer
40
+ @luks_data_fetcher = luks_data_fetcher
41
+ @luks_checker = luks_checker
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+
5
+ module CryptReboot
6
+ # Writes files from hash to specified directory
7
+ class FilesWriter
8
+ def call(files, target_dir)
9
+ files.each do |relative_path, content|
10
+ path = File.join(target_dir, relative_path)
11
+ dir = File.dirname(path)
12
+ FileUtils.mkdir_p(dir) unless File.directory?(dir)
13
+ File.binwrite(path, content)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'zlib'
4
+
5
+ module CryptReboot
6
+ # Gzip data and save it to file
7
+ class Gziper
8
+ def call(archive_path, data)
9
+ writer.call(archive_path) do |gz|
10
+ gz.write data
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ attr_reader :writer
17
+
18
+ def initialize(writer: Zlib::GzipWriter.method(:open))
19
+ @writer = writer
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'find'
4
+
5
+ module CryptReboot
6
+ module Initramfs
7
+ # Create compressed CPIO archive from files in a given directory
8
+ class Archiver
9
+ def call(dir, archive)
10
+ Dir.chdir(dir) do
11
+ uncompressed = runner.call(cpio, '-oH', 'newc', '--reproducible', input: finder.call)
12
+ gziper.call(archive, uncompressed)
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def cpio
19
+ lazy_cpio.call
20
+ end
21
+
22
+ attr_reader :runner, :finder, :lazy_cpio, :gziper
23
+
24
+ def initialize(runner: Runner::Binary.new,
25
+ finder: -> { Find.find('.').to_a.join("\n") },
26
+ lazy_cpio: LazyConfig.cpio_path,
27
+ gziper: Gziper.new)
28
+ @runner = runner
29
+ @finder = finder
30
+ @lazy_cpio = lazy_cpio
31
+ @gziper = gziper
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shellwords'
4
+
5
+ module CryptReboot
6
+ module Initramfs
7
+ # Extract initramfs in strace to check if compression is supported
8
+ class Decompressor
9
+ class IntolerantDecompressor
10
+ Lz4NotAllowed = Class.new StandardError
11
+
12
+ def call(filename, dir)
13
+ command_line = prepare_command_line(filename, dir)
14
+ lines = runner.call(command_line)
15
+ raise_exception if intolerable_tool_used?(lines)
16
+ end
17
+
18
+ private
19
+
20
+ def prepare_command_line(filename, dir)
21
+ options = '-f --trace=execve -z -qq --signal=\!all'
22
+ args = "#{filename.shellescape} #{dir.shellescape}"
23
+ strace_command_line = "#{strace.shellescape} #{options} #{unmkinitramfs.shellescape} #{args}"
24
+ grep_command_line = "#{grep.shellescape} --line-buffered lz4"
25
+ # guarantee at least 1 line of grep output, otherwise grep will return non-zero status
26
+ grep_fixer = 'echo lz4 \"-t\"'
27
+ "(#{grep_fixer}; #{strace_command_line}) 2>&1 | #{grep_command_line}"
28
+ end
29
+
30
+ def intolerable_tool_used?(lines)
31
+ !!lines.find { |line| line !~ /"-t"/ && line !~ /"--test"/ }
32
+ end
33
+
34
+ def raise_exception
35
+ raise Lz4NotAllowed, 'LZ4 compression is not allowed, change the compression ' \
36
+ 'algorithm in initramfs.conf and regenerate the initramfs image'
37
+ end
38
+
39
+ def unmkinitramfs
40
+ lazy_unmkinitramfs.call
41
+ end
42
+
43
+ def strace
44
+ lazy_strace.call
45
+ end
46
+
47
+ def grep
48
+ lazy_grep.call
49
+ end
50
+
51
+ attr_reader :lazy_unmkinitramfs, :lazy_strace, :lazy_grep, :runner
52
+
53
+ def initialize(lazy_unmkinitramfs: LazyConfig.unmkinitramfs_path,
54
+ lazy_strace: LazyConfig.strace_path,
55
+ lazy_grep: LazyConfig.grep_path,
56
+ runner: Runner::Lines.new)
57
+ @lazy_unmkinitramfs = lazy_unmkinitramfs
58
+ @lazy_strace = lazy_strace
59
+ @lazy_grep = lazy_grep
60
+ @runner = runner
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CryptReboot
4
+ module Initramfs
5
+ class Decompressor
6
+ # Simply extract initramfs
7
+ class TolerantDecompressor
8
+ def call(filename, dir)
9
+ runner.call(unmkinitramfs, filename, dir)
10
+ end
11
+
12
+ private
13
+
14
+ attr_reader :lazy_unmkinitramfs, :runner
15
+
16
+ def unmkinitramfs
17
+ lazy_unmkinitramfs.call
18
+ end
19
+
20
+ def initialize(lazy_unmkinitramfs: LazyConfig.unmkinitramfs_path,
21
+ runner: Runner::NoResult.new)
22
+ @lazy_unmkinitramfs = lazy_unmkinitramfs
23
+ @runner = runner
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CryptReboot
4
+ module Initramfs
5
+ # Instantiates appropriate decompressor
6
+ class Decompressor
7
+ def call(skip_lz4_check: Config.skip_lz4_check)
8
+ skip_lz4_check ? TolerantDecompressor.new : IntolerantDecompressor.new
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tmpdir'
4
+
5
+ module CryptReboot
6
+ module Initramfs
7
+ # Create temporary directory, extract initramfs there and yield, cleaning afterwards
8
+ class Extractor
9
+ def call(filename)
10
+ tmp_maker.call do |dir|
11
+ logger.call message
12
+ decompressor.call(filename, dir)
13
+ yield dir
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def decompressor
20
+ decompressor_factory.call
21
+ end
22
+
23
+ attr_reader :tmp_maker, :decompressor_factory, :message, :logger
24
+
25
+ def initialize(tmp_maker: Dir.method(:mktmpdir),
26
+ decompressor_factory: Decompressor.new,
27
+ message: 'Extracting initramfs... To speed things up, future versions will employ cache.',
28
+ logger: ->(msg) { warn msg })
29
+ @tmp_maker = tmp_maker
30
+ @decompressor_factory = decompressor_factory
31
+ @message = message
32
+ @logger = logger
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+
5
+ module CryptReboot
6
+ module Initramfs
7
+ # Yield path to initramfs patched with files_spec.
8
+ # Patched initramfs will be removed afterwards if user doesn't want to save it
9
+ class Patcher
10
+ def call(initramfs_path, files_spec)
11
+ temp_provider.call do |base_dir|
12
+ files_dir, patch_path, patched_path = prefix('files', 'patch', 'result', with: base_dir)
13
+ writer.call(files_spec, files_dir)
14
+ archiver.call(files_dir, patch_path)
15
+ saver.call(patch_path)
16
+ concatenator.call(initramfs_path, patch_path, to: patched_path)
17
+ yield patched_path
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def prefix(*file_names, with:)
24
+ file_names.map do |file_name|
25
+ File.join(with, file_name)
26
+ end
27
+ end
28
+
29
+ attr_reader :temp_provider, :writer, :archiver, :concatenator, :saver
30
+
31
+ def initialize(temp_provider: SafeTemp::Directory.new,
32
+ writer: FilesWriter.new,
33
+ archiver: Archiver.new,
34
+ concatenator: Concatenator.new,
35
+ saver: lambda { |file|
36
+ dir = Config.patch_save_path
37
+ FileUtils.cp(file, dir) if dir
38
+ })
39
+ @temp_provider = temp_provider
40
+ @writer = writer
41
+ @archiver = archiver
42
+ @concatenator = concatenator
43
+ @saver = saver
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CryptReboot
4
+ # Transform initramfs image into a patch (files hash)
5
+ class InitramfsPatchSqueezer
6
+ def call(initramfs_path)
7
+ extractor.call(initramfs_path) do |tmp_dir|
8
+ crypttab_path = File.join(tmp_dir, crypttab_relative_path)
9
+ crypttab_entries = deserializer.call(crypttab_path)
10
+ files_generator.call(crypttab_entries, tmp_dir)
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ attr_reader :crypttab_relative_path, :extractor, :deserializer, :files_generator
17
+
18
+ def initialize(crypttab_relative_path = 'main/cryptroot/crypttab',
19
+ extractor: Initramfs::Extractor.new,
20
+ deserializer: CryptTab::Deserializer.new,
21
+ files_generator: FilesGenerator.new)
22
+ @crypttab_relative_path = crypttab_relative_path
23
+ @extractor = extractor
24
+ @deserializer = deserializer
25
+ @files_generator = files_generator
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CryptReboot
4
+ # Configuration object
5
+ class InstantiableConfig
6
+ UnrecognizedSetting = Class.new StandardError
7
+
8
+ attr_reader :initramfs, :cmdline, :kernel, :patch_save_path, :cat_path, :cpio_path,
9
+ :unmkinitramfs_path, :kexec_path, :cryptsetup_path, :reboot_path,
10
+ :mount_path, :umount_path, :strace_path, :grep_path,
11
+ :debug, :prepare_only, :skip_lz4_check
12
+
13
+ def update!(**settings)
14
+ settings.each do |name, value|
15
+ set!(name, value)
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def set!(name, value)
22
+ raise UnrecognizedSetting, "Unrecognized setting `#{name}`" unless instance_variable_defined?(:"@#{name}")
23
+
24
+ instance_variable_set(:"@#{name}", value)
25
+ end
26
+
27
+ # rubocop:disable Metrics/MethodLength
28
+ def initialize
29
+ # Options
30
+ @initramfs = '/boot/initrd.img'
31
+ @cmdline = nil
32
+ @kernel = '/boot/vmlinuz'
33
+ @patch_save_path = nil
34
+ @cat_path = 'cat'
35
+ @cpio_path = 'cpio'
36
+ @unmkinitramfs_path = 'unmkinitramfs'
37
+ @kexec_path = 'kexec'
38
+ @cryptsetup_path = 'cryptsetup'
39
+ @reboot_path = 'reboot'
40
+ @mount_path = 'mount'
41
+ @umount_path = 'umount'
42
+ @strace_path = 'strace'
43
+ @grep_path = 'grep'
44
+
45
+ # Flags
46
+ @debug = false
47
+ @prepare_only = false
48
+ @skip_lz4_check = false
49
+ end
50
+ # rubocop:enable Metrics/MethodLength
51
+ end
52
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CryptReboot
4
+ module Kexec
5
+ # Load new kernel and initramfs into memory, making then ready for later execution
6
+ class Loader
7
+ def call(boot_config)
8
+ args = [tool, '-al', boot_config.kernel]
9
+ args += ['--initrd', boot_config.initramfs] if boot_config.initramfs
10
+ args += boot_config.cmdline ? ['--append', boot_config.cmdline] : ['--reuse-cmdline']
11
+
12
+ runner.call(*args)
13
+ end
14
+
15
+ private
16
+
17
+ def tool
18
+ lazy_tool.call
19
+ end
20
+
21
+ attr_reader :lazy_tool, :runner
22
+
23
+ def initialize(lazy_tool: LazyConfig.kexec_path,
24
+ runner: Runner::NoResult.new)
25
+ @lazy_tool = lazy_tool
26
+ @runner = runner
27
+ end
28
+ end
29
+ end
30
+ end