1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package instance
import (
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"io"
"os"
)
type Token string
const Redacted = "[redacted]"
func (Token) String() string { return Redacted }
func (Token) GoString() string { return Redacted }
const TokenBytes = 32
func New(r io.Reader) (Token, error) {
if r == nil {
r = rand.Reader
}
buf := make([]byte, TokenBytes)
if _, err := r.Read(buf); err != nil {
return "", fmt.Errorf("instance: mint token: %w", err)
}
return Token("k10" + hex.EncodeToString(buf)), nil
}
var ErrEmptyToken = errors.New("instance: empty token")
type Plan struct {
Token Token
DeleteBootstrapRow bool
ReSeedBundle bool
Reason string
}
func PlanFor(token Token, datastoreHasRow bool) (Plan, error) {
if token == "" {
return Plan{}, ErrEmptyToken
}
if !datastoreHasRow {
return Plan{
Token: token,
DeleteBootstrapRow: false,
ReSeedBundle: false,
Reason: "no row to reconcile against; k3s authors the bundle itself on a fresh " +
"datastore, and there is nothing for it to disagree with",
}, nil
}
return Plan{
Token: token,
DeleteBootstrapRow: true,
ReSeedBundle: true,
Reason: "the row is encrypted with the PREVIOUS INSTANCE's token, so k3s would exit " +
"with 'bootstrap data already found and encrypted with different token' -- and " +
"genUsers would then force cred/passwd to the new token AFTER the reconcile, " +
"making the NEXT start fatal as well. Deleting the row is what makes the " +
"rotation possible, and having no row to reconcile against is what makes " +
"pre-seeding the 13 entries safe",
}, nil
}
func WriteFile(path string, t Token) error {
if t == "" {
return ErrEmptyToken
}
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
if err != nil {
return fmt.Errorf("instance: opening %s: %w", path, err)
}
if _, err := f.WriteString(string(t)); err != nil {
f.Close()
return fmt.Errorf("instance: writing %s: %w", path, err)
}
return f.Close()
}