Delete old secretstore, cmd/node and config modules
This removes even more code that is no longer necessary or interferes with new concepts. It also refactors the storage stuff into a StorageManager which deals with all the paths and async initialization.
This does intentionally break a few things which will be fixed when the CA code lands.
Test Plan: Manually tested using make launch, CI is in a separate ticket
X-Origin-Diff: phab/D182
GitOrigin-RevId: 282a4bd84b47010d859e03da53b2c2de8183b13b
diff --git a/internal/storage/data.go b/internal/storage/data.go
index 30c6686..2ffd180 100644
--- a/internal/storage/data.go
+++ b/internal/storage/data.go
@@ -16,3 +16,150 @@
package storage
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "smalltown/internal/common"
+ "smalltown/pkg/tpm"
+ "sync"
+
+ "go.uber.org/zap"
+ "golang.org/x/sys/unix"
+)
+
+const (
+ dataMountPath = "/data"
+ espMountPath = "/esp"
+ espDataPath = espMountPath + "/EFI/smalltown"
+ etcdSealedKeyLocation = espDataPath + "/data-key.bin"
+)
+
+type Manager struct {
+ logger *zap.Logger
+ dataReady bool
+ initializationError error
+ mutex sync.RWMutex
+}
+
+func Initialize(logger *zap.Logger) (*Manager, error) {
+ if err := FindPartitions(); err != nil {
+ return nil, err
+ }
+
+ if err := os.Mkdir("/esp", 0755); err != nil {
+ return nil, err
+ }
+
+ // We're mounting ESP sync for reliability, this lowers our chances of getting half-written files
+ if err := unix.Mount(ESPDevicePath, espMountPath, "vfat", unix.MS_NOEXEC|unix.MS_NODEV|unix.MS_SYNC, ""); err != nil {
+ return nil, err
+ }
+
+ manager := &Manager{
+ logger: logger,
+ dataReady: false,
+ }
+
+ manager.mutex.Lock()
+ defer manager.mutex.Unlock()
+
+ sealedKeyFile, err := os.Open(etcdSealedKeyLocation)
+ if os.IsNotExist(err) {
+ logger.Info("Initializing encrypted storage, this might take a while...")
+ go manager.initializeData()
+ } else if err != nil {
+ return nil, err
+ } else {
+ sealedKey, err := ioutil.ReadAll(sealedKeyFile)
+ sealedKeyFile.Close()
+ if err != nil {
+ return nil, err
+ }
+ key, err := tpm.Unseal(sealedKey)
+ if err != nil {
+ return nil, err
+ }
+ if err := MapEncryptedBlockDevice("data", SmalltownDataCryptPath, key); err != nil {
+ return nil, err
+ }
+ if err := manager.mountData(); err != nil {
+ return nil, err
+ }
+ logger.Info("Mounted encrypted storage")
+ }
+ return manager, nil
+}
+
+func (s *Manager) initializeData() {
+ key, err := tpm.GenerateSafeKey(256 / 8)
+ if err != nil {
+ s.logger.Error("Failed to generate master key", zap.Error(err))
+ s.initializationError = fmt.Errorf("Failed to generate master key: %w", err)
+ return
+ }
+ sealedKey, err := tpm.Seal(key, tpm.FullSystemPCRs)
+ if err != nil {
+ s.logger.Error("Failed to seal master key", zap.Error(err))
+ s.initializationError = fmt.Errorf("Failed to seal master key: %w", err)
+ return
+ }
+ if err := InitializeEncryptedBlockDevice("data", SmalltownDataCryptPath, key); err != nil {
+ s.logger.Error("Failed to initialize encrypted block device", zap.Error(err))
+ s.initializationError = fmt.Errorf("Failed to initialize encrypted block device: %w", err)
+ return
+ }
+ mkfsCmd := exec.Command("/bin/mkfs.xfs", "-qf", "/dev/data")
+ if _, err := mkfsCmd.Output(); err != nil {
+ s.logger.Error("Failed to format encrypted block device", zap.Error(err))
+ s.initializationError = fmt.Errorf("Failed to format encrypted block device: %w", err)
+ return
+ }
+ // This file is the marker if the partition has
+ if err := ioutil.WriteFile(etcdSealedKeyLocation, sealedKey, 0600); err != nil {
+ panic(err)
+ }
+
+ if err := s.mountData(); err != nil {
+ s.initializationError = err
+ return
+ }
+
+ s.mutex.Lock()
+ s.dataReady = true
+ s.mutex.Unlock()
+
+ s.logger.Info("Initialized encrypted storage")
+}
+
+func (s *Manager) mountData() error {
+ if err := os.Mkdir("/data", 0755); err != nil {
+ return err
+ }
+
+ if err := unix.Mount("/dev/data", "/data", "xfs", unix.MS_NOEXEC|unix.MS_NODEV, ""); err != nil {
+ return err
+ }
+ return nil
+}
+
+// GetPathInPlace returns a path in the given place
+// It may return ErrNotInitialized if the place you're trying to access
+// is not initialized or ErrUnknownPlace if the place is not known
+func (s *Manager) GetPathInPlace(place common.DataPlace, path string) (string, error) {
+ s.mutex.RLock()
+ defer s.mutex.RUnlock()
+ switch place {
+ case common.PlaceESP:
+ return filepath.Join(espDataPath, path), nil
+ case common.PlaceData:
+ if s.dataReady {
+ return filepath.Join(dataMountPath, path), nil
+ }
+ return "", common.ErrNotInitialized
+ default:
+ return "", common.ErrUnknownPlace
+ }
+}
diff --git a/internal/storage/xfs.go b/internal/storage/xfs.go
new file mode 100644
index 0000000..30c6686
--- /dev/null
+++ b/internal/storage/xfs.go
@@ -0,0 +1,18 @@
+// 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 storage
+