crypt_reboot 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 449c9b12be881f213dffc9090686e7d73bb3f2523806c5edf41dc2e4d7d17c4b
4
- data.tar.gz: ba391871d8d5f438ba2474a8a21d8738be020636ac8c4bfcd0fd8100d7e07aec
3
+ metadata.gz: 90456cf783f95d79186882a7fce00b153b4ff1928a68efd4609fb7e8859d3b04
4
+ data.tar.gz: d5fd15cb6705793a7e357666158eaa4f95f5c14d41b22b1d82d087a921fa512c
5
5
  SHA512:
6
- metadata.gz: 466befee2a5027cae8be7cb036c87e8c779e64fb4725775111b112e53ff5bf2691bc543ac86e7c229e8e75642c0c3eb0168fed1bca14a2b0598b94bf3c5245c0
7
- data.tar.gz: 68bef507bea7444722f3281b94d8ea5af0bda0d031871f82fc9ff0398614e7b83564f2b541a42d745d2eefecde9f0a409817dcd29332b01797cd13b434bd7db0
6
+ metadata.gz: 5c99c3e114727a1236ef3dd9f536a56b1a89639c4447f6387ec5bcd1bd25234f2655a75f306ace56dd78c36a0aadf77da4321ed6626c36c63e38c9852fefefea
7
+ data.tar.gz: 37acfc427190670ad362cfb60a8a18ed29bcab338d24ac728ac23f4e3934c6669175db7872836ac46211d09dd16cba861259ec8e99c829050c1a8a3b875b9724
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## [0.1.2] - 2023-07-22
2
+
3
+ - Lock memory to prevent secrets leaking to swap
4
+ - Use `ramfs` instead of `tmpfs` for the same reason
5
+
6
+ ## [0.1.1] - 2023-07-13
7
+
8
+ - Standardize passphrase prompt
9
+
1
10
  ## [0.1.0] - 2023-07-09
2
11
 
3
12
  - Initial release
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Cryptreboot
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/crypt_reboot.svg)](https://badge.fury.io/rb/crypt_reboot)
4
+
3
5
  Convenient reboot for Linux systems with encrypted root partition.
4
6
 
5
7
  > Just type `cryptreboot` instead of `reboot`.
@@ -26,13 +28,16 @@ Debian, Ubuntu, Linux Mint, Pop!_OS, etc.
26
28
  On the other hand, do not expect it to work on other distributions now.
27
29
  But support for them may come in upcoming versions.
28
30
 
29
- Following distributions were tested by the author:
31
+ Following distributions were tested by the author on the AMD64 machine:
32
+ - DappNode 0.2.75 is based on Debian 12, see below
33
+ - Debian 12 needs [symlinks for kernel and initramfs](#no-symlinks-to-most-recent-kernel-and-initramfs)
34
+ - Pop!_OS 22.04 LTS
35
+ - Ubuntu 23.04
30
36
  - Ubuntu 22.04 LTS
31
37
  - Ubuntu 20.04 LTS needs tiny adjustments to system settings,
32
38
  specifically [changing compression](#lz4-initramfs-compression) and
33
39
  [fixing systemd kexec support](#staged-kernel-not-being-executed-by-systemd)
34
40
  - ~~Ubuntu 18.04 LTS~~ is not supported (initramfs uses *pre-crypttab* format)
35
- - Pop!_OS 22.04 LTS
36
41
 
37
42
  If you have successfully run cryptreboot on another distribution,
38
43
  please contact me and I will update the list.
@@ -48,16 +53,23 @@ You need to ensure those are installed:
48
53
  If you use recent, mainstream Linux distribution, other requirements are
49
54
  probably already met:
50
55
  - `kexec` support in the kernel
51
- - `tmpfs` filesystem support in kernel
56
+ - `ramfs` filesystem support in kernel
52
57
  - `cryptsetup` (if you use disk encryption, it should be installed)
53
58
  - `systemd` or another way to guarantee staged kernel is executed on reboot
54
59
  - `strace` (not required if `--skip-lz4-check` flag is specified)
55
60
 
61
+ If you use Debian-based distribution, use this command to install required packages:
62
+
63
+ $ sudo apt install --no-install-recommends cryptsetup-initramfs kexec-tools ruby strace systemd
64
+
65
+ When asked if kexec should handle reboots, answer `yes` (however the answer probably
66
+ doesn't matter for cryptreboot to work).
67
+
56
68
  ## Installation
57
69
 
58
- Make sure the required software is installed, then install the gem by executing:
70
+ Make sure the required software is installed, then install the gem system-wide by executing:
59
71
 
60
- $ gem install crypt_reboot
72
+ $ sudo gem install crypt_reboot
61
73
 
62
74
  ## Usage
63
75
 
@@ -128,6 +140,26 @@ To cancel the change, remove the file:
128
140
 
129
141
  $ sudo rm /etc/systemd/system/systemd-kexec.service.d/override.conf
130
142
 
143
+ ### No symlinks to most recent kernel and initramfs
144
+
145
+ By default cryptreboot looks for kernel in `/boot/vmlinuz` and for initramfs
146
+ in `/boot/initrd.img`. If those files are missing in your Linux distribution,
147
+ cryptreboot will fail, unless you use `--kernel` and `--initramfs` command line
148
+ options.
149
+
150
+ $ sudo cryptreboot --kernel /boot/vmlinuz-`uname -r` --initramfs /boot/initrd.img-`uname -r`
151
+
152
+ If you don't want to specify options every time you reboot, add symlinks to
153
+ the currently running kernel and initramfs:
154
+
155
+ $ cd /boot
156
+ $ sudo ln -sf vmlinuz-`uname -r` vmlinuz
157
+ $ sudo ln -sf initrd.img-`uname -r` initrd.img
158
+
159
+ Unfortunately, you need to rerun it after each kernel upgrade, otherwise,
160
+ cryptreboot is going to boot the old kernel.
161
+ Upcoming versions of cryptreboot will offer better solutions.
162
+
131
163
  ## Development
132
164
 
133
165
  After checking out the repo, run `bundle install` to install
data/lib/basic_loader.rb CHANGED
@@ -14,6 +14,7 @@ require 'crypt_reboot/gziper'
14
14
  require 'crypt_reboot/initramfs_patch_squeezer'
15
15
  require 'crypt_reboot/kexec_patching_loader'
16
16
  require 'crypt_reboot/lazy_config'
17
+ require 'crypt_reboot/memory_locker'
17
18
  require 'crypt_reboot/passphrase_asker'
18
19
  require 'crypt_reboot/patched_initramfs_generator'
19
20
  require 'crypt_reboot/rebooter'
@@ -5,9 +5,10 @@ module CryptReboot
5
5
  # Interprets parameters, executes everything and returns callable object
6
6
  class ParamsParsingExecutor
7
7
  def call(raw_params)
8
+ locker.call
8
9
  params = parser.call(raw_params)
9
10
  handle_action_params!(params) or configure_and_exec(params)
10
- rescue StandardError => e
11
+ rescue StandardError, Interrupt => e
11
12
  raise if debug?
12
13
 
13
14
  sad_exiter_class.new(error_message(e))
@@ -45,7 +46,7 @@ module CryptReboot
45
46
 
46
47
  attr_reader :parser, :config_updater, :loader, :help_generator,
47
48
  :version_string, :debug_checker, :rebooter,
48
- :happy_exiter_class, :sad_exiter_class
49
+ :happy_exiter_class, :sad_exiter_class, :locker
49
50
 
50
51
  # rubocop:disable Metrics/ParameterLists
51
52
  def initialize(parser: Params::Parser.new,
@@ -56,7 +57,8 @@ module CryptReboot
56
57
  debug_checker: LazyConfig.debug,
57
58
  rebooter: Rebooter.new,
58
59
  happy_exiter_class: HappyExiter,
59
- sad_exiter_class: SadExiter)
60
+ sad_exiter_class: SadExiter,
61
+ locker: MemoryLocker.new)
60
62
  @parser = parser
61
63
  @config_updater = config_updater
62
64
  @loader = loader
@@ -66,6 +68,7 @@ module CryptReboot
66
68
  @rebooter = rebooter
67
69
  @happy_exiter_class = happy_exiter_class
68
70
  @sad_exiter_class = sad_exiter_class
71
+ @locker = locker
69
72
  end
70
73
  # rubocop:enable Metrics/ParameterLists
71
74
  end
@@ -6,10 +6,9 @@ module CryptReboot
6
6
  def call(entries, base_dir)
7
7
  files = {}
8
8
  modified_entries = entries.map do |entry|
9
- headevice = entry.headevice(header_prefix: base_dir)
10
- next entry unless luks?(headevice)
9
+ next entry unless luks?(entry, base_dir)
11
10
 
12
- data = luks_data_fetcher.call(headevice)
11
+ data = fetch_data(entry, base_dir)
13
12
  keyfile = keyfile_locator.call(entry.target)
14
13
  files[keyfile] = data.key
15
14
  entry_converter.call(entry, data, keyfile)
@@ -22,10 +21,16 @@ module CryptReboot
22
21
  CRYPTAB_PATH = '/cryptroot/crypttab'
23
22
  private_constant :CRYPTAB_PATH
24
23
 
25
- def luks?(headevice)
24
+ def luks?(entry, base_dir)
25
+ headevice = entry.headevice(header_prefix: base_dir)
26
26
  luks_checker.call(headevice)
27
27
  end
28
28
 
29
+ def fetch_data(entry, base_dir)
30
+ headevice = entry.headevice(header_prefix: base_dir)
31
+ luks_data_fetcher.call(headevice, entry.target)
32
+ end
33
+
29
34
  attr_reader :keyfile_locator, :entry_converter, :serializer,
30
35
  :luks_data_fetcher, :luks_checker
31
36
 
@@ -4,10 +4,10 @@ module CryptReboot
4
4
  module Luks
5
5
  # Fetch LUKS data including key (user will be asked for passphrase)
6
6
  class DataFetcher
7
- def call(headevice)
7
+ def call(headevice, target)
8
8
  version = detector.call(headevice)
9
9
  data = dumper.call(headevice, version)
10
- pass = asker.call("Enter passphrase to unlock #{headevice}: ")
10
+ pass = asker.call("Please unlock disk #{target}: ")
11
11
  key = key_fetcher.call(headevice, pass)
12
12
  data.with_key(key)
13
13
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ffi'
4
+
5
+ module CryptReboot
6
+ # Lock process memory, so it won't be swapped by the kernel.
7
+ # It is implemented as a one-way operation: there is no unlock.
8
+ # That's because it's hard to properly clean memory in Ruby.
9
+ class MemoryLocker
10
+ Error = Class.new StandardError
11
+
12
+ def call
13
+ return if Libc.mlockall(Libc::MCL_CURRENT | Libc::MCL_FUTURE).zero?
14
+
15
+ raise Error, "Failed to lock memory: #{FFI.errno}"
16
+ end
17
+
18
+ # Low level interface to libc
19
+ module Libc
20
+ extend FFI::Library
21
+ ffi_lib 'libc.so.6'
22
+
23
+ # define mlockall constants
24
+ MCL_CURRENT = 1
25
+ MCL_FUTURE = 2
26
+ MCL_ONFAULT = 4
27
+
28
+ # declare mlockall function
29
+ attach_function :mlockall, [:int], :int
30
+ end
31
+ private_constant :Libc
32
+ end
33
+ end
@@ -4,7 +4,7 @@ require 'tmpdir'
4
4
 
5
5
  module CryptReboot
6
6
  module SafeTemp
7
- # Create temporary directory, mounts tmpfs and yields tmp dir location.
7
+ # Create temporary directory, mounts ramfs and yields tmp dir location.
8
8
  # Make sure to cleanup afterwards.
9
9
  class Directory
10
10
  def call
@@ -2,7 +2,9 @@
2
2
 
3
3
  module CryptReboot
4
4
  module SafeTemp
5
- # Mount tmpfs at the given mount point, yield and unmount
5
+ # Mount ramfs at the given mount point, yield and unmount.
6
+ # We don't want the contents of directory to be swapped,
7
+ # therefore ramfs is used instead of tmpfs.
6
8
  class Mounter
7
9
  def call(dir, &block)
8
10
  mounter.call(dir)
@@ -21,7 +23,7 @@ module CryptReboot
21
23
 
22
24
  def initialize(runner: Runner::NoResult.new,
23
25
  mounter: lambda { |dir|
24
- runner.call(Config.mount_path, '-t', 'tmpfs', '-o', 'mode=700', 'none', dir)
26
+ runner.call(Config.mount_path, '-t', 'ramfs', '-o', 'mode=700', 'none', dir)
25
27
  },
26
28
  umounter: ->(dir) { runner.call(Config.umount_path, dir) })
27
29
  @runner = runner
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CryptReboot
4
- VERSION = '0.1.0'
4
+ VERSION = '0.1.2'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: crypt_reboot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paweł Pokrywka
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-07-09 00:00:00.000000000 Z
11
+ date: 2023-07-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: tty-command
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: ffi
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 1.0.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 1.0.0
41
55
  description:
42
56
  email:
43
57
  - pepawel@users.noreply.github.com
@@ -93,6 +107,7 @@ files:
93
107
  - lib/crypt_reboot/luks/dumper/luks_v2_parser.rb
94
108
  - lib/crypt_reboot/luks/key_fetcher.rb
95
109
  - lib/crypt_reboot/luks/version_detector.rb
110
+ - lib/crypt_reboot/memory_locker.rb
96
111
  - lib/crypt_reboot/passphrase_asker.rb
97
112
  - lib/crypt_reboot/patched_initramfs_generator.rb
98
113
  - lib/crypt_reboot/rebooter.rb