-
Notifications
You must be signed in to change notification settings - Fork 574
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
overlord/fdestate: keep FDE state up to date #14516
base: fde-manager-features
Are you sure you want to change the base?
overlord/fdestate: keep FDE state up to date #14516
Conversation
728ff63
to
14d22b2
Compare
} | ||
} | ||
|
||
type KeyDigest struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might be a type that should be handled in secboot. And we just use json.RawMessage for it.
overlord/fdestate/fdemgr.go
Outdated
if !locked { | ||
m.state.Lock() | ||
defer m.state.Unlock() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not elegant. But I am not sure how to handle it correctly. We do resealing sometimes locked, sometimes not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we know where is it called with the state really already unlocked?
overlord/fdestate/fdemgr.go
Outdated
m.state.Lock() | ||
defer m.state.Unlock() | ||
return ensureState(m.state) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's a bit unclear if this will really run before any reseal op? maybe it would be better to use StartUp for this?
425117e
to
5d69be1
Compare
overlord/fdestate/fdemgr.go
Outdated
func (m *FDEManager) resealKeyForBootChains(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { | ||
doUpdate := func(role string, containerRole string, bootModes []string, models []secboot.ModelForSealing, tpmPCRProfile []byte) error { | ||
if unlocker != nil { | ||
m.state.Lock() | ||
defer m.state.Unlock() | ||
} | ||
return updateParameters(m.state, role, containerRole, bootModes, models, tpmPCRProfile) | ||
} | ||
if unlocker != nil { | ||
locker := unlocker() | ||
defer locker() | ||
} | ||
return backend.ResealKeyForBootChains(doUpdate, method, rootdir, params, expectReseal) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is not covered by tests in this package, but still covered by tests from overlord/managers_test.go
. I wonder if we should add tests in the current package. Or move them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ideally it should be tested in the package directly too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. It was easier than expected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
drive-by comment
type ResealKeysParams struct { | ||
// The snap model parameters | ||
ModelParams []*SealKeyModelParams | ||
PCRProfile SerializedPCRProfile |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could this be []byte ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you want to convert a json into base64 to put it in another json?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have added marshalling/unmarshalling methods here.
secboot/secboot_sb.go
Outdated
@@ -291,3 +295,32 @@ func DeleteKeys(node string, matches map[string]bool) error { | |||
|
|||
return nil | |||
} | |||
|
|||
func GetPrimaryKeyHash(devicePath string, alg crypto.Hash) ([]byte, []byte, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
func GetPrimaryKeyHash(devicePath string, alg crypto.Hash) ([]byte, []byte, error) { | |
func GetPrimaryKeyHash(devicePath string, alg crypto.Hash) (salt []byte, digest []byte, err error) { |
secboot/secboot_sb.go
Outdated
return nil, nil, err | ||
} | ||
|
||
h := hmac.New(func() hash.Hash { return alg.New() }, salt[:]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should work:
h := hmac.New(func() hash.Hash { return alg.New() }, salt[:]) | |
h := hmac.New(alg.New, salt[:]) |
secboot/secboot_sb.go
Outdated
return false, err | ||
} | ||
|
||
h := hmac.New(func() hash.Hash { return alg.New() }, salt[:]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
h := hmac.New(func() hash.Hash { return alg.New() }, salt[:]) | |
h := hmac.New(alg.New, salt[:]) |
osutil/disks/disks.go
Outdated
|
||
// ErrMountPointNotFound is return by DMCryptUUIDFromMountPoint a path | ||
// is not a mount point | ||
type ErrMountPointNotFound struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to keep uses or errors.Is() simple, you could have:
var ErrMountPointNotFound = errors.New("cannot find mountpoint")
type errMountPointNotFound struct{
Path string
}
func (e *errMountPointNotFound) Error() string {
return fmt.Sprintf("cannot find mountpoint %q", e.Path)
}
func (e *errMountPointNotFound) Unwrap() { return ErrMountPintNotFound }
var models []secboot.ModelForSealing | ||
for _, m := range modelParams { | ||
models = append(models, m.Model) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var models []secboot.ModelForSealing | |
for _, m := range modelParams { | |
models = append(models, m.Model) | |
} | |
func modelsFromModelParams() []secboot.ModelForSealing { | |
var models []secboot.ModelForSealing | |
for _, m := range modelParams { | |
models = append(models, m.Model) | |
} | |
return models | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I understand. Github is probably not showing me the right thing. Do you want me to factor the similar code from resealFallbackObjectKeys
and resealRunObjectKeys
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, not a big thing though, but both functions are close to each other and maybe it would be a bit nicer
} | ||
|
||
if errors.Is(dataErr, disks.ErrNoDmUUID) && errors.Is(saveErr, disks.ErrNoDmUUID) { | ||
return nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this could use a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
overlord/fdestate/fdestate.go
Outdated
return err | ||
} | ||
if !sameDigest { | ||
return fmt.Errorf("Primary key for data and save partition are not the same") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return fmt.Errorf("Primary key for data and save partition are not the same") | |
return fmt.Errorf("primary key for data and save partition are not the same") |
|
||
// Note that Parameters will be updated on first update | ||
s.KeyslotRoles = map[string]KeyslotRoleInfo{ | ||
"run+recover": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
feels like this should be a const somewhere to rule out use of say recover+run
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem here is that we need that value both in overlord/fdestate
and overlord/fdestate/backend
. But overlord/fdestate/backend
can not import overlord/fdestate
. So it has to be in the backend. Unless we create new package. @pedronis what do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's leave TODOs for now, I wonder which other places will need these string constants
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
KeyFiles: keyFiles, | ||
TPMPolicyAuthKeyFile: authKeyFile, | ||
} | ||
if err := secbootResealKeys(resealKeyParams); err != nil { | ||
return fmt.Errorf("cannot reseal the encryption key: %v", err) | ||
} | ||
|
||
if err := updateState("run+recover", "all", []string{"run", "recover"}, models, pcrProfile); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps same thing about bare "all"
, what if we simply have type for it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's leave a TODO about maybe. We'll try to figure it out later when we have a better feeling of what's needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
overlord/fdestate/fdestate.go
Outdated
// BootModes are the optional list of approved modes (run, recover, ...) | ||
BootModes []string `json:"boot-modes,omitempty"` | ||
// Tpm2PCRProfile is an optional serialized PCR profile | ||
Tpm2PCRProfile secboot.SerializedPCRProfile `json:"tpm2-pcr-profile,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tpm2PCRProfile secboot.SerializedPCRProfile `json:"tpm2-pcr-profile,omitempty"` | |
TPM2PCRProfile secboot.SerializedPCRProfile `json:"tpm2-pcr-profile,omitempty"` |
overlord/fdestate/fdestate.go
Outdated
// Tpm2PCRPolicyRevocationCounter is the handle for the TPM | ||
// policy revocation counter. A value of 0 means it is not | ||
// set. | ||
Tpm2PCRPolicyRevocationCounter uint32 `json:"tpm2-pcr-policy-revocation-counter,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tpm2PCRPolicyRevocationCounter uint32 `json:"tpm2-pcr-policy-revocation-counter,omitempty"` | |
TPM2PCRPolicyRevocationCounter uint32 `json:"tpm2-pcr-policy-revocation-counter,omitempty"` |
overlord/fdestate/fdestate.go
Outdated
type KeyslotRoleInfo struct { | ||
// PrimaryKeyId is the ID for the primary key found in | ||
// PrimaryKeys field of FdeState | ||
PrimaryKeyId int `json:"primary-key-id"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PrimaryKeyId int `json:"primary-key-id"` | |
PrimaryKeyID int `json:"primary-key-id"` |
&mockModel{}, | ||
} | ||
|
||
fdestate.UpdateParameters(st, "run+recover", "container-role", []string{"run"}, models, secboot.SerializedPCRProfile(`"serialized-profile"`)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fdestate.UpdateParameters(st, "run+recover", "container-role", []string{"run"}, models, secboot.SerializedPCRProfile(`"serialized-profile"`)) | |
fdestate.UpdateParameters(st, "run+recover", "container-role", []string{"run"}, models, secboot.SerializedPCRProfile(`{"something":"serialized-profile"}`)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason why? It was valid json.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose just to make a bit more realistic in terms of the kind of object we expect here, "aaa" is valid JSON but is also valid many other things
fdestate.UpdateParameters(st, "run+recover", "container-role", []string{"run"}, models, secboot.SerializedPCRProfile(`"serialized-profile"`)) | ||
|
||
var fdeSt fdestate.FdeState | ||
err := st.Get("fde", &fdeSt) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and to make sure serialization for SerialziedPCRProfile is working correctly I think I'd add something like:
var raw map[string]any
c.Assert(t.Get("fde", &raw), Nil)
c.Check(raw, DeepEquals, map[string]any{
...
"tpm2-prc-profile": map[string]any {
"something": "serialized-profile",
}
})
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not think that would work. The TPM2PCRProfile
field is kept serialized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I get it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have put a test in secboot
because this is where we define that type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks
284c647
to
00abb2d
Compare
StartUp() initializes the empty profiles, and reseal updates them.
00abb2d
to
46eb39e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank you, bunch of small comments
osutil/disks/disks.go
Outdated
@@ -237,3 +238,23 @@ func RegisterDeviceMapperBackResolver(name string, f func(dmUUID, dmName []byte) | |||
func unregisterDeviceMapperBackResolver(name string) { | |||
delete(deviceMapperBackResolvers, name) | |||
} | |||
|
|||
// ErrNoDmUUID is return by DMCryptUUIDFromMountPoint when the device |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: returned
osutil/disks/disks.go
Outdated
@@ -237,3 +238,23 @@ func RegisterDeviceMapperBackResolver(name string, f func(dmUUID, dmName []byte) | |||
func unregisterDeviceMapperBackResolver(name string) { | |||
delete(deviceMapperBackResolvers, name) | |||
} | |||
|
|||
// ErrNoDmUUID is return by DMCryptUUIDFromMountPoint when the device | |||
// at the mount point is not a device mapper device |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing final .
osutil/disks/disks.go
Outdated
var ErrNoDmUUID = errors.New("device has no DM_UUID") | ||
|
||
// ErrMountPointNotFound is return by DMCryptUUIDFromMountPoint a path | ||
// is not a mount point |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same issues
secboot/secboot_tpm.go
Outdated
@@ -628,6 +625,16 @@ func buildPCRProtectionProfile(modelParams []*SealKeyModelParams) (*sb_tpm2.PCRP | |||
return pcrProfile, nil | |||
} | |||
|
|||
// BuildPCRProtectionProfile builds and serializes a PCR profile from | |||
// a list of SealKeyModelParams |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing final .
overlord/fdestate/fdestate.go
Outdated
|
||
roleInfo, hasRole := s.KeyslotRoles[role] | ||
if !hasRole { | ||
return fmt.Errorf("Cannot find role %s", role) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cannot find key role ?
overlord/fdestate/fdestate.go
Outdated
case "sha256": | ||
*h = hashAlg(crypto.SHA256) | ||
default: | ||
return fmt.Errorf("Unknown algorithm %s", s) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lowercase: unknown ...
overlord/fdestate/fdestate.go
Outdated
case crypto.SHA256: | ||
return json.Marshal("sha256") | ||
default: | ||
return nil, fmt.Errorf("Unknown algorithm %v", h) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lowercase: unknown ...
overlord/fdestate/fdemgr.go
Outdated
func (m *FDEManager) resealKeyForBootChains(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { | ||
doUpdate := func(role string, containerRole string, bootModes []string, models []secboot.ModelForSealing, tpmPCRProfile []byte) error { | ||
if unlocker != nil { | ||
m.state.Lock() | ||
defer m.state.Unlock() | ||
} | ||
return updateParameters(m.state, role, containerRole, bootModes, models, tpmPCRProfile) | ||
} | ||
if unlocker != nil { | ||
locker := unlocker() | ||
defer locker() | ||
} | ||
return backend.ResealKeyForBootChains(doUpdate, method, rootdir, params, expectReseal) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ideally it should be tested in the package directly too
Ensure() initializes the empty profiles, and reseal updates them.
This is on top of #14400