Skip to content

Commit

Permalink
Support legacy cryptsetup versions on ubuntu 20.04 (#24426)
Browse files Browse the repository at this point in the history
  • Loading branch information
mostlikelee authored Dec 5, 2024
1 parent 4f2daf2 commit 7985261
Show file tree
Hide file tree
Showing 4 changed files with 284 additions and 1 deletion.
1 change: 1 addition & 0 deletions orbit/24215-ubuntu-2004
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* added support for linux key escrow on ubuntu 20.04
13 changes: 13 additions & 0 deletions orbit/pkg/luks/luks.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package luks

import (
"errors"
"regexp"

"github.com/fleetdm/fleet/v4/orbit/pkg/dialog"
)

Expand Down Expand Up @@ -34,3 +37,13 @@ func New(escrower KeyEscrower) *LuksRunner {
escrower: escrower,
}
}

func extractJSON(input []byte) ([]byte, error) {
// Regular expression to extract JSON
re := regexp.MustCompile(`(?s)\{.*\}`)
match := re.FindString(string(input))
if match == "" {
return nil, errors.New("no JSON found")
}
return []byte(match), nil
}
56 changes: 55 additions & 1 deletion orbit/pkg/luks/luks_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import (
"math/big"
"os/exec"
"regexp"
"strings"
"time"

"github.com/Masterminds/semver"
"github.com/fleetdm/fleet/v4/orbit/pkg/dialog"
"github.com/fleetdm/fleet/v4/orbit/pkg/kdialog"
"github.com/fleetdm/fleet/v4/orbit/pkg/lvm"
Expand Down Expand Up @@ -334,12 +336,34 @@ type KDF struct {
}

func getSaltforKeySlot(ctx context.Context, devicePath string, keySlot uint) (string, error) {
cmd := exec.CommandContext(ctx, "cryptsetup", "luksDump", "--dump-json-metadata", devicePath)
var jsonFlag string
var jsonNeedsExtraction bool

lessThan2_4, err := isCryptsetupVersionLessThan2_4()
if err != nil {
return "", fmt.Errorf("Failed to check cryptsetup version: %w", err)
}

if lessThan2_4 {
jsonFlag = "--debug-json"
jsonNeedsExtraction = true
} else {
jsonFlag = "--dump-json-metadata"
}

cmd := exec.CommandContext(ctx, "cryptsetup", "luksDump", jsonFlag, devicePath)
output, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("Failed to run cryptsetup luksDump: %w", err)
}

if jsonNeedsExtraction {
output, err = extractJSON(output)
if err != nil {
return "", fmt.Errorf("Failed to extract JSON from cryptsetup luksDump output: %w", err)
}
}

var dump LuksDump
if err := json.Unmarshal(output, &dump); err != nil {
return "", fmt.Errorf("Failed to unmarshal luksDump output: %w", err)
Expand All @@ -361,3 +385,33 @@ func removeKeySlot(ctx context.Context, devicePath string, keySlot uint) error {

return nil
}

// isCryptsetupVersionLessThan2_4 checks if the installed cryptsetup version is less than 2.4.0
func isCryptsetupVersionLessThan2_4() (bool, error) {
cmd := exec.Command("cryptsetup", "--version")
output, err := cmd.Output()
if err != nil {
return false, fmt.Errorf("failed to run cryptsetup: %w", err)
}

// Parse the output
// Examples of output:
// "cryptsetup 2.7.0 flags: UDEV BLKID KEYRING FIPS KERNEL_CAPI HW_OPAL"
// "cryptsetup 2.2.2"
outputStr := strings.TrimSpace(string(output))
parts := strings.Fields(outputStr)

// The second field should always contain the version number
if len(parts) < 2 {
return false, fmt.Errorf("unexpected output format: %s", outputStr)
}

installedVersion, err := semver.NewVersion(parts[1])
if err != nil {
return false, fmt.Errorf("failed to parse version: %w", err)
}

// Compare against version 2.4.0
targetVersion := semver.MustParse("2.4.0")
return installedVersion.LessThan(targetVersion), nil
}
215 changes: 215 additions & 0 deletions orbit/pkg/luks/luks_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
package luks

import (
"testing"

"github.com/tj/assert"
)

// output from cryptsetup luksDump /dev/sda3 --debug-json command
// on cryptsetup 2.2.2
var output = `# cryptsetup 2.2.2 processing "cryptsetup luksDump /dev/sda3 --debug-json"
# Running command luksDump.
# Locking memory.
# Installing SIGINT/SIGTERM handler.
# Unblocking interruption on signal.
# Allocating context for crypt device /dev/sda3.
# Trying to open and read device /dev/sda3 with direct-io.
# Initialising device-mapper backend library.
# Trying to load any crypt type from device /dev/sda3.
# Crypto backend (OpenSSL 1.1.1f 31 Mar 2020) initialized in cryptsetup library version 2.2.2.
# Detected kernel Linux 5.4.0-200-generic aarch64.
# Loading LUKS2 header (repair disabled).
# Acquiring read lock for device /dev/sda3.
# Opening lock resource file /run/cryptsetup/L_8:3
# Verifying lock handle for /dev/sda3.
# Device /dev/sda3 READ lock taken.
# Trying to read primary LUKS2 header at offset 0x0.
# Opening locked device /dev/sda3
# Veryfing locked device handle (bdev)
# LUKS2 header version 2 of size 16384 bytes, checksum sha256.
# Checksum:f16cfd36bbc588eb178d9f40e7da030edc6ed04cfb012ee14be14e3af459439b (on-disk)
# Checksum:f16cfd36bbc588eb178d9f40e7da030edc6ed04cfb012ee14be14e3af459439b (in-memory)
# Trying to read secondary LUKS2 header at offset 0x4000.
# Reusing open ro fd on device /dev/sda3
# LUKS2 header version 2 of size 16384 bytes, checksum sha256.
# Checksum:0ce47e7c0460addb7a5ee2546e8404521ca0caf2e02830b1d4ef7172bf617d84 (on-disk)
# Checksum:0ce47e7c0460addb7a5ee2546e8404521ca0caf2e02830b1d4ef7172bf617d84 (in-memory)
# Device size 65442676736, offset 16777216.
# Device /dev/sda3 READ lock released.
# Only 2 active CPUs detected, PBKDF threads decreased from 4 to 2.
# Not enough physical memory detected, PBKDF max memory decreased from 1048576kB to 1010800kB.
# PBKDF argon2i, time_ms 2000 (iterations 0), max_memory_kb 1010800, parallel_threads 2.
# {
"keyslots":{
"0":{
"type":"luks2",
"key_size":64,
"af":{
"type":"luks1",
"stripes":4000,
"hash":"sha256"
},
"area":{
"type":"raw",
"offset":"32768",
"size":"258048",
"encryption":"aes-xts-plain64",
"key_size":64
},
"kdf":{
"type":"argon2i",
"time":5,
"memory":1011024,
"cpus":2,
"salt":"b8+t4hTY/IFecqsKR20UZSUPFDZqyAtJ9lxYg5ye2Hg="
}
}
},
"tokens":{
},
"segments":{
"0":{
"type":"crypt",
"offset":"16777216",
"size":"dynamic",
"iv_tweak":"0",
"encryption":"aes-xts-plain64",
"sector_size":512
}
},
"digests":{
"0":{
"type":"pbkdf2",
"keyslots":[
"0"
],
"segments":[
"0"
],
"hash":"sha256",
"iterations":457493,
"salt":"BbZbHfAY5e90aoKjTYSJoj8ZLiRueUofVJWWG/4trWw=",
"digest":"f2sSMaJlh5qbdVUYT+RhabOmEit96KBIq0ltzAnfARc="
}
},
"config":{
"json_size":"12288",
"keyslots_size":"16744448"
}
}LUKS header information
Version: 2
Epoch: 5
Metadata area: 16384 [bytes]
Keyslots area: 16744448 [bytes]
UUID: 3cf8a080-045d-4bc2-889c-994c9b4a18aa
Label: (no label)
Subsystem: (no subsystem)
Flags: (no flags)
Data segments:
0: crypt
offset: 16777216 [bytes]
length: (whole device)
cipher: aes-xts-plain64
sector: 512 [bytes]
Keyslots:
0: luks2
Key: 512 bits
Priority: normal
Cipher: aes-xts-plain64
Cipher key: 512 bits
PBKDF: argon2i
Time cost: 5
Memory: 1011024
Threads: 2
Salt: 6f cf ad e2 14 d8 fc 81 5e 72 ab 0a 47 6d 14 65
25 0f 14 36 6a c8 0b 49 f6 5c 58 83 9c 9e d8 78
AF stripes: 4000
AF hash: sha256
Area offset:32768 [bytes]
Area length:258048 [bytes]
Digest ID: 0
Tokens:
Digests:
0: pbkdf2
Hash: sha256
Iterations: 457493
Salt: 05 b6 5b 1d f0 18 e5 ef 74 6a 82 a3 4d 84 89 a2
3f 19 2e 24 6e 79 4a 1f 54 95 96 1b fe 2d ad 6c
Digest: 7f 6b 12 31 a2 65 87 9a 9b 75 55 18 4f e4 61 69
b3 a6 12 2b 7d e8 a0 48 ab 49 6d cc 09 df 01 17
# Releasing crypt device /dev/sda3 context.
# Releasing device-mapper backend.
# Closing read only fd for /dev/sda3.
# Unlocking memory.
Command successful.`

var extractedJSON = `{
"keyslots":{
"0":{
"type":"luks2",
"key_size":64,
"af":{
"type":"luks1",
"stripes":4000,
"hash":"sha256"
},
"area":{
"type":"raw",
"offset":"32768",
"size":"258048",
"encryption":"aes-xts-plain64",
"key_size":64
},
"kdf":{
"type":"argon2i",
"time":5,
"memory":1011024,
"cpus":2,
"salt":"b8+t4hTY/IFecqsKR20UZSUPFDZqyAtJ9lxYg5ye2Hg="
}
}
},
"tokens":{
},
"segments":{
"0":{
"type":"crypt",
"offset":"16777216",
"size":"dynamic",
"iv_tweak":"0",
"encryption":"aes-xts-plain64",
"sector_size":512
}
},
"digests":{
"0":{
"type":"pbkdf2",
"keyslots":[
"0"
],
"segments":[
"0"
],
"hash":"sha256",
"iterations":457493,
"salt":"BbZbHfAY5e90aoKjTYSJoj8ZLiRueUofVJWWG/4trWw=",
"digest":"f2sSMaJlh5qbdVUYT+RhabOmEit96KBIq0ltzAnfARc="
}
},
"config":{
"json_size":"12288",
"keyslots_size":"16744448"
}
}`

func TestExtractJson(t *testing.T) {
json, err := extractJSON([]byte(output))
assert.NoError(t, err)
assert.Equal(t, extractedJSON, string(json))

_, err = extractJSON([]byte("no json"))
assert.Error(t, err)
}

0 comments on commit 7985261

Please sign in to comment.