blob: 329b357291017d6a4f67b985963e81be28b7f85a [file] [log] [blame]
// Copyright 2020 The Monogon Project Authors.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package localstorage
import (
"errors"
"fmt"
"os"
"google.golang.org/protobuf/proto"
"source.monogon.dev/metropolis/node/core/localstorage/declarative"
"source.monogon.dev/metropolis/pkg/tpm"
apb "source.monogon.dev/metropolis/proto/api"
ppb "source.monogon.dev/metropolis/proto/private"
)
// ESPDirectory is the EFI System Partition. It is a cleartext partition
// available to the system at early boot, and must contain all data required
// for the system to bootstrap, register into, or join a cluster.
type ESPDirectory struct {
declarative.Directory
Metropolis ESPMetropolisDirectory `dir:"metropolis"`
}
// ESPMetropolisDirectory is the directory inside the EFI System Partition where
// Metropolis-related data is stored that's not read by EFI itself like
// bootstrap-related data.
type ESPMetropolisDirectory struct {
declarative.Directory
SealedConfiguration ESPSealedConfiguration `file:"sealed_configuration.pb"`
NodeParameters ESPNodeParameters `file:"parameters.pb"`
}
// ESPSealedConfiguration is a TPM sealed serialized
// private.SealedConfiguration protobuf. It contains all data required for a
// node to be able to join a cluster after startup.
type ESPSealedConfiguration struct {
declarative.File
}
// ESPNodeParameters is the configuration for this node when first
// bootstrapping a cluster or registering into an existing one. It's a
// api.NodeParameters protobuf message.
type ESPNodeParameters struct {
declarative.File
}
var (
ErrNoSealed = errors.New("no sealed configuration exists")
ErrSealedUnavailable = errors.New("sealed configuration temporary unavailable")
ErrSealedCorrupted = errors.New("sealed configuration corrupted")
ErrNoParameters = errors.New("no parameters found")
ErrParametersCorrupted = errors.New("parameters corrupted")
)
func (e *ESPNodeParameters) Unmarshal() (*apb.NodeParameters, error) {
bytes, err := e.Read()
if err != nil {
if os.IsNotExist(err) {
return nil, ErrNoParameters
}
return nil, fmt.Errorf("%w: when reading sealed data: %v", ErrNoParameters, err)
}
config := apb.NodeParameters{}
err = proto.Unmarshal(bytes, &config)
if err != nil {
return nil, fmt.Errorf("%w: when unmarshaling: %v", ErrParametersCorrupted, err)
}
return &config, nil
}
func (e *ESPSealedConfiguration) Unseal() (*ppb.SealedConfiguration, error) {
bytes, err := e.Read()
if err != nil {
if os.IsNotExist(err) {
return nil, ErrNoSealed
}
return nil, fmt.Errorf("%w: when reading sealed data: %v", ErrSealedUnavailable, err)
}
bytes, err = tpm.Unseal(bytes)
if err != nil {
return nil, fmt.Errorf("%w: when unsealing: %v", ErrSealedCorrupted, err)
}
config := ppb.SealedConfiguration{}
err = proto.Unmarshal(bytes, &config)
if err != nil {
return nil, fmt.Errorf("%w: when unmarshaling: %v", ErrSealedCorrupted, err)
}
return &config, nil
}