Skip to content

Commit bd9b8e3

Browse files
committed
add salt and keyslot
1 parent 0d02e81 commit bd9b8e3

File tree

4 files changed

+86
-30
lines changed

4 files changed

+86
-30
lines changed

orbit/pkg/luks/luks.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package luks_runner
1+
package luks
22

33
import (
44
"github.com/fleetdm/fleet/v4/orbit/pkg/dialog"
@@ -14,8 +14,19 @@ type LuksRunner struct {
1414
}
1515

1616
type LuksResponse struct {
17-
Key string `json:"key"`
18-
Err string `json:"err"`
17+
// Passphrase is a newly created passphrase generated by fleetd for securing the LUKS volume.
18+
// This passphrase will be securely escrowed to the server.
19+
Passphrase string
20+
21+
// KeySlot specifies the LUKS key slot where this new passphrase was created.
22+
// It is currently not used, but may be useful in the future for passphrase rotation.
23+
KeySlot *uint
24+
25+
// Salt is the salt used to generate the LUKS key.
26+
Salt string
27+
28+
// Err is the error message that occurred during the escrow process.
29+
Err string
1930
}
2031

2132
func New(escrower KeyEscrower, notifier dialog.Dialog) *LuksRunner {

orbit/pkg/luks/luks_linux.go

Lines changed: 65 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
//go:build linux
22

3-
package luks_runner
3+
package luks
44

55
import (
66
"context"
77
"crypto/rand"
8+
"encoding/json"
89
"errors"
910
"fmt"
1011
"math/big"
12+
"os/exec"
1113
"regexp"
1214
"time"
1315

@@ -16,7 +18,7 @@ import (
1618
"github.com/fleetdm/fleet/v4/server/fleet"
1719
"github.com/rs/zerolog/log"
1820
"github.com/siderolabs/go-blockdevice/v2/encryption"
19-
"github.com/siderolabs/go-blockdevice/v2/encryption/luks"
21+
luksdevice "github.com/siderolabs/go-blockdevice/v2/encryption/luks"
2022
)
2123

2224
const (
@@ -40,13 +42,27 @@ func (lr *LuksRunner) Run(oc *fleet.OrbitConfig) error {
4042
return nil
4143
}
4244

45+
devicePath, err := lvm.FindRootDisk()
46+
if err != nil {
47+
return fmt.Errorf("Failed to find LUKS Root Partition: %w", err)
48+
}
49+
4350
var response LuksResponse
44-
key, err := lr.getEscrowKey(ctx)
51+
key, keyslot, err := lr.getEscrowKey(ctx, devicePath)
4552
if err != nil {
4653
response.Err = err.Error()
4754
}
4855

49-
response.Key = string(key)
56+
response.Passphrase = string(key)
57+
response.KeySlot = keyslot
58+
59+
if keyslot != nil {
60+
salt, err := getSaltforKeySlot(ctx, devicePath, *keyslot)
61+
if err != nil {
62+
return fmt.Errorf("Failed to get salt for key slot: %w", err)
63+
}
64+
response.Salt = salt
65+
}
5066

5167
if err := lr.escrower.SendLinuxKeyEscrowResponse(response); err != nil {
5268
if err := lr.infoPrompt(ctx, infoFailedTitle, infoFailedText); err != nil {
@@ -70,31 +86,26 @@ func (lr *LuksRunner) Run(oc *fleet.OrbitConfig) error {
7086
return nil
7187
}
7288

73-
func (lr *LuksRunner) getEscrowKey(ctx context.Context) ([]byte, error) {
74-
devicePath, err := lvm.FindRootDisk()
75-
if err != nil {
76-
return nil, fmt.Errorf("Failed to find LUKS Root Partition: %w", err)
77-
}
78-
79-
device := luks.New(luks.AESXTSPlain64Cipher)
89+
func (lr *LuksRunner) getEscrowKey(ctx context.Context, devicePath string) ([]byte, *uint, error) {
90+
device := luksdevice.New(luksdevice.AESXTSPlain64Cipher)
8091

8192
// Prompt user for existing LUKS passphrase
8293
passphrase, err := lr.entryPrompt(ctx, entryDialogTitle, entryDialogText)
8394
if err != nil {
84-
return nil, fmt.Errorf("Failed to show passphrase entry prompt: %w", err)
95+
return nil, nil, fmt.Errorf("Failed to show passphrase entry prompt: %w", err)
8596
}
8697

8798
// Validate the passphrase
8899
for {
89100
valid, err := lr.passphraseIsValid(ctx, device, devicePath, passphrase)
90101
if err != nil {
91-
return nil, fmt.Errorf("Failed validating passphrase: %w", err)
102+
return nil, nil, fmt.Errorf("Failed validating passphrase: %w", err)
92103
}
93104

94105
if !valid {
95106
passphrase, err = lr.entryPrompt(ctx, entryDialogTitle, retryEntryDialogText)
96107
if err != nil {
97-
return nil, fmt.Errorf("Failed re-prompting for passphrase: %w", err)
108+
return nil, nil, fmt.Errorf("Failed re-prompting for passphrase: %w", err)
98109
}
99110
continue
100111
}
@@ -104,41 +115,41 @@ func (lr *LuksRunner) getEscrowKey(ctx context.Context) ([]byte, error) {
104115

105116
if len(passphrase) == 0 {
106117
log.Debug().Msg("Passphrase is empty, no password supplied, dialog was canceled, or timed out")
107-
return nil, nil
118+
return nil, nil, nil
108119
}
109120

110121
escrowPassphrase, err := generateRandomPassphrase()
111122
if err != nil {
112-
return nil, fmt.Errorf("Failed to generate random passphrase: %w", err)
123+
return nil, nil, fmt.Errorf("Failed to generate random passphrase: %w", err)
113124
}
114125

115126
// Create a new key slot, error if all key slots are full
116127
// keySlot 0 is assumed to be the user's passphrase
117128
// so we start at 1
118-
keySlot := 1
129+
var keySlot uint = 1
119130
for {
120131
if keySlot == maxKeySlots {
121-
return nil, errors.New("all LUKS key slots are full")
132+
return nil, nil, errors.New("all LUKS key slots are full")
122133
}
123134

124135
userKey := encryption.NewKey(0, passphrase)
125-
escrowKey := encryption.NewKey(keySlot, escrowPassphrase)
136+
escrowKey := encryption.NewKey(int(keySlot), escrowPassphrase)
126137

127138
if err := device.AddKey(ctx, devicePath, userKey, escrowKey); err != nil {
128139
if ErrKeySlotFull.MatchString(err.Error()) {
129140
keySlot++
130141
continue
131142
}
132-
return nil, fmt.Errorf("Failed to add key: %w", err)
143+
return nil, nil, fmt.Errorf("Failed to add key: %w", err)
133144
}
134145

135146
break
136147
}
137148

138-
return escrowPassphrase, nil
149+
return escrowPassphrase, &keySlot, nil
139150
}
140151

141-
func (lr *LuksRunner) passphraseIsValid(ctx context.Context, device *luks.LUKS, devicePath string, passphrase []byte) (bool, error) {
152+
func (lr *LuksRunner) passphraseIsValid(ctx context.Context, device *luksdevice.LUKS, devicePath string, passphrase []byte) (bool, error) {
142153
if len(passphrase) == 0 {
143154
return false, nil
144155
}
@@ -219,3 +230,35 @@ func (lr *LuksRunner) infoPrompt(ctx context.Context, title, text string) error
219230

220231
return nil
221232
}
233+
234+
type LuksDump struct {
235+
Keyslots map[string]Keyslot `json:"keyslots"`
236+
}
237+
238+
type Keyslot struct {
239+
KDF KDF `json:"kdf"`
240+
}
241+
242+
type KDF struct {
243+
Salt string `json:"salt"`
244+
}
245+
246+
func getSaltforKeySlot(ctx context.Context, devicePath string, keySlot uint) (string, error) {
247+
cmd := exec.CommandContext(ctx, "cryptsetup", "luksDump", "--dump-json-metadata", devicePath)
248+
output, err := cmd.Output()
249+
if err != nil {
250+
return "", fmt.Errorf("Failed to run cryptsetup luksDump: %w", err)
251+
}
252+
253+
var dump LuksDump
254+
if err := json.Unmarshal(output, &dump); err != nil {
255+
return "", fmt.Errorf("Failed to unmarshal luksDump output: %w", err)
256+
}
257+
258+
slot, ok := dump.Keyslots[fmt.Sprintf("%d", keySlot)]
259+
if !ok {
260+
return "", errors.New("key slot not found")
261+
}
262+
263+
return slot.KDF.Salt, nil
264+
}

orbit/pkg/luks/luks_stub.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//go:build !linux
22
// +build !linux
33

4-
package luks_runner
4+
package luks
55

66
import (
77
"github.com/fleetdm/fleet/v4/server/fleet"

server/service/orbit_client.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import (
2222

2323
"github.com/fleetdm/fleet/v4/orbit/pkg/constant"
2424
"github.com/fleetdm/fleet/v4/orbit/pkg/logging"
25-
luks_runner "github.com/fleetdm/fleet/v4/orbit/pkg/luks"
25+
"github.com/fleetdm/fleet/v4/orbit/pkg/luks"
2626
"github.com/fleetdm/fleet/v4/orbit/pkg/platform"
2727
"github.com/fleetdm/fleet/v4/pkg/retry"
2828
"github.com/fleetdm/fleet/v4/server/fleet"
@@ -670,15 +670,17 @@ func (oc *OrbitClient) GetSetupExperienceStatus() (*fleet.SetupExperienceStatusP
670670
return resp.Results, nil
671671
}
672672

673-
func (oc *OrbitClient) SendLinuxKeyEscrowResponse(lr luks_runner.LuksResponse) error {
673+
func (oc *OrbitClient) SendLinuxKeyEscrowResponse(lr luks.LuksResponse) error {
674674
verb, path := "POST", "/api/fleet/orbit/luks_data"
675675
var resp orbitPostLUKSResponse
676676
if err := oc.authenticatedRequest(verb, path, &orbitPostLUKSRequest{
677-
Passphrase: lr.Key,
678-
SlotKey: "1",
677+
Passphrase: lr.Passphrase,
678+
KeySlot: lr.KeySlot,
679+
Salt: lr.Salt,
679680
ClientError: lr.Err,
680681
}, &resp); err != nil {
681682
return err
682683
}
684+
683685
return nil
684686
}

0 commit comments

Comments
 (0)