Skip to content

Commit b788382

Browse files
authored
persists node seed on each disk to be safe if disk changed (#74)
Signed-off-by: Ashraf Fouda <ashraf.m.fouda@gmail.com>
1 parent a73be7c commit b788382

1 file changed

Lines changed: 81 additions & 12 deletions

File tree

pkg/identity/store/file.go

Lines changed: 81 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"crypto/ed25519"
55
"encoding/json"
66
"os"
7+
"path/filepath"
78

89
"github.com/pkg/errors"
910
"github.com/threefoldtech/zosbase/pkg/versioned"
@@ -39,19 +40,78 @@ func (f *FileStore) Kind() string {
3940

4041
func (f *FileStore) Set(key ed25519.PrivateKey) error {
4142
seed := key.Seed()
42-
return versioned.WriteFile(f.path, SeedVersion1, seed, 0400)
43+
// write to primary location first
44+
if err := versioned.WriteFile(f.path, SeedVersion1, seed, 0400); err != nil {
45+
return err
46+
}
47+
48+
// also mirror the seed to all mounted disks under /mnt/*/seed.txt
49+
mirrors, _ := f.mirrorPaths()
50+
for _, p := range mirrors {
51+
_ = versioned.WriteFile(p, SeedVersion1, seed, 0400)
52+
}
53+
return nil
4354
}
4455

4556
func (f *FileStore) Annihilate() error {
4657
return os.Remove(f.path)
4758
}
4859

4960
func (f *FileStore) Get() (ed25519.PrivateKey, error) {
50-
version, data, err := versioned.ReadFile(f.path)
61+
key, err := f.readKeyFrom(f.path)
62+
if errors.Is(err, ErrKeyDoesNotExist) {
63+
// try to recover from any mirrored copies on mounted disks
64+
mirrors, _ := f.mirrorPaths()
65+
for _, p := range mirrors {
66+
if k, merr := f.readKeyFrom(p); merr == nil {
67+
// write back to primary location for future boots
68+
_ = os.MkdirAll(filepath.Dir(f.path), 0o700)
69+
_ = versioned.WriteFile(f.path, SeedVersion1, k.Seed(), 0400)
70+
// ensure the seed is present across all mirrors
71+
_ = f.ensureMirrors(k.Seed())
72+
return k, nil
73+
}
74+
}
75+
}
76+
if err == nil {
77+
// ensure the seed is present across all mirrors on every successful read
78+
_ = f.ensureMirrors(key.Seed())
79+
}
80+
return key, err
81+
}
82+
83+
func (f *FileStore) Exists() (bool, error) {
84+
if _, err := os.Stat(f.path); os.IsNotExist(err) {
85+
return false, nil
86+
} else if err != nil {
87+
return false, errors.Wrap(err, "failed to check seed file")
88+
}
89+
90+
return true, nil
91+
}
92+
93+
// ensureMirrors guarantees that the seed file exists on all mirror locations under /mnt/*
94+
// It is best-effort; failures for individual mirrors are ignored.
95+
func (f *FileStore) ensureMirrors(seed []byte) error {
96+
mirrors, err := f.mirrorPaths()
97+
if err != nil {
98+
return err
99+
}
100+
for _, p := range mirrors {
101+
if _, statErr := os.Stat(p); os.IsNotExist(statErr) {
102+
_ = versioned.WriteFile(p, SeedVersion1, seed, 0400)
103+
}
104+
}
105+
return nil
106+
}
107+
108+
// readKeyFrom reads and decodes an identity seed from a given path
109+
// supporting both 1.0.0 (raw seed) and 1.1.0 (mnemonic json) formats.
110+
func (f *FileStore) readKeyFrom(path string) (ed25519.PrivateKey, error) {
111+
version, data, err := versioned.ReadFile(path)
51112
if versioned.IsNotVersioned(err) {
52-
// this is a compatibility code for seed files
53-
// in case it does not have any version information
54-
if err := versioned.WriteFile(f.path, SeedVersionLatest, data, 0400); err != nil {
113+
// compatibility for old non-versioned seed files
114+
if err := versioned.WriteFile(path, SeedVersionLatest, data, 0400); err != nil {
55115
return nil, err
56116
}
57117
version = SeedVersion1
@@ -68,6 +128,7 @@ func (f *FileStore) Get() (ed25519.PrivateKey, error) {
68128
if version.EQ(SeedVersion1) {
69129
return keyFromSeed(data)
70130
}
131+
71132
// it means we read json data instead of the secret
72133
type Seed110Struct struct {
73134
Mnemonics string `json:"mnemonic"`
@@ -85,12 +146,20 @@ func (f *FileStore) Get() (ed25519.PrivateKey, error) {
85146
return keyFromSeed(seed)
86147
}
87148

88-
func (f *FileStore) Exists() (bool, error) {
89-
if _, err := os.Stat(f.path); os.IsNotExist(err) {
90-
return false, nil
91-
} else if err != nil {
92-
return false, errors.Wrap(err, "failed to check seed file")
149+
// mirrorPaths lists candidate seed paths under all mounted disks in /mnt/*
150+
// The path is mirrored at: /mnt/<disk>/seed.txt (flat, no original directories)
151+
func (f *FileStore) mirrorPaths() ([]string, error) {
152+
entries, err := os.ReadDir("/mnt")
153+
if err != nil {
154+
return nil, err
93155
}
94-
95-
return true, nil
156+
var paths []string
157+
for _, e := range entries {
158+
if !e.IsDir() {
159+
continue
160+
}
161+
name := filepath.Base(f.path) // usually "seed.txt"
162+
paths = append(paths, filepath.Join("/mnt", e.Name(), name))
163+
}
164+
return paths, nil
96165
}

0 commit comments

Comments
 (0)