feat: establish secure update foundation
Container-Image veröffentlichen / container (push) v1.4.0 wurde manuell veröffentlicht und verifiziert
Container-Image veröffentlichen / container (push) v1.4.0 wurde manuell veröffentlicht und verifiziert
This commit is contained in:
1 parent
f02ec9977c
commit
bb60c06c0e
28 files changed
+1403
-280
No files matched your search
@@ -37,6 +37,16 @@ type Cipher struct {
|
||||
}
|
||||
|
||||
func LoadCipher(path string) (*Cipher, error) {
|
||||
info, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("inspect master key: %w", err)
|
||||
}
|
||||
if !info.Mode().IsRegular() || info.Mode()&os.ModeSymlink != 0 {
|
||||
return nil, errors.New("master key must be a regular file")
|
||||
}
|
||||
if err := os.Chmod(path, 0o600); err != nil {
|
||||
return nil, fmt.Errorf("secure master key: %w", err)
|
||||
}
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read master key: %w", err)
|
||||
@@ -47,6 +57,17 @@ func LoadCipher(path string) (*Cipher, error) {
|
||||
// LoadOrCreateCipher loads an existing master key or creates one on first start.
|
||||
// The key must live on a persistent volume separate from the application data.
|
||||
func LoadOrCreateCipher(path string) (*Cipher, error) {
|
||||
directory := filepath.Dir(path)
|
||||
if err := os.MkdirAll(directory, 0o700); err != nil {
|
||||
return nil, fmt.Errorf("create master key directory: %w", err)
|
||||
}
|
||||
info, err := os.Lstat(directory)
|
||||
if err != nil || !info.IsDir() || info.Mode()&os.ModeSymlink != 0 {
|
||||
return nil, errors.New("master key directory must be a regular directory")
|
||||
}
|
||||
if err := os.Chmod(directory, 0o700); err != nil {
|
||||
return nil, fmt.Errorf("secure master key directory: %w", err)
|
||||
}
|
||||
cipher, err := LoadCipher(path)
|
||||
if err == nil {
|
||||
return cipher, nil
|
||||
@@ -55,9 +76,6 @@ func LoadOrCreateCipher(path string) (*Cipher, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
|
||||
return nil, fmt.Errorf("create master key directory: %w", err)
|
||||
}
|
||||
key := make([]byte, chacha20poly1305.KeySize)
|
||||
if _, err := rand.Read(key); err != nil {
|
||||
return nil, fmt.Errorf("create master key: %w", err)
|
||||
|
||||
@@ -56,6 +56,13 @@ func TestLoadOrCreateCipherPersistsSecureKey(t *testing.T) {
|
||||
if permissions := info.Mode().Perm(); permissions != 0o600 {
|
||||
t.Fatalf("master key permissions = %o, want 600", permissions)
|
||||
}
|
||||
directoryInfo, err := os.Stat(filepath.Dir(path))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if permissions := directoryInfo.Mode().Perm(); permissions != 0o700 {
|
||||
t.Fatalf("master key directory permissions = %o, want 700", permissions)
|
||||
}
|
||||
ciphertext, nonce, err := first.Encrypt("persistent token")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -70,6 +77,23 @@ func TestLoadOrCreateCipherPersistsSecureKey(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadCipherRejectsSymlinkedMasterKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
directory := t.TempDir()
|
||||
target := filepath.Join(directory, "target")
|
||||
key := make([]byte, 32)
|
||||
if err := os.WriteFile(target, []byte(base64.RawStdEncoding.EncodeToString(key)), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
link := filepath.Join(directory, "master_key")
|
||||
if err := os.Symlink(target, link); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := LoadCipher(link); err == nil {
|
||||
t.Fatal("symlinked master key was accepted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPasswordHash(t *testing.T) {
|
||||
t.Parallel()
|
||||
hash, err := HashPassword("a secure password")
|
||||
|
||||
Reference in new issue
Block a user