crypt_reboot 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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