-
Notifications
You must be signed in to change notification settings - Fork 2
/
maintenance.go
143 lines (121 loc) · 4.22 KB
/
maintenance.go
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package secrets
import (
"context"
stdlibx509 "crypto/x509"
"fmt"
"time"
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/cosi-project/runtime/pkg/state"
"github.com/siderolabs/crypto/x509"
"github.com/siderolabs/gen/optional"
"go.uber.org/zap"
"github.com/siderolabs/talos/pkg/machinery/resources/secrets"
timeresource "github.com/siderolabs/talos/pkg/machinery/resources/time"
"github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1"
)
// MaintenanceController manages secrets.MaintenanceServiceCerts.
type MaintenanceController struct{}
// Name implements controller.Controller interface.
func (ctrl *MaintenanceController) Name() string {
return "secrets.MaintenanceController"
}
// Inputs implements controller.Controller interface.
func (ctrl *MaintenanceController) Inputs() []controller.Input {
return []controller.Input{
{
Namespace: secrets.NamespaceName,
Type: secrets.MaintenanceRootType,
ID: optional.Some(secrets.MaintenanceRootID),
Kind: controller.InputWeak,
},
{
Namespace: secrets.NamespaceName,
Type: secrets.CertSANType,
ID: optional.Some(secrets.CertSANMaintenanceID),
Kind: controller.InputWeak,
},
// time status isn't fetched, but the fact that it is in dependencies means
// that certs will be regenerated on time sync/jump (as reconcile will be triggered)
{
Namespace: v1alpha1.NamespaceName,
Type: timeresource.StatusType,
ID: optional.Some(timeresource.StatusID),
Kind: controller.InputWeak,
},
}
}
// Outputs implements controller.Controller interface.
func (ctrl *MaintenanceController) Outputs() []controller.Output {
return []controller.Output{
{
Type: secrets.MaintenanceServiceCertsType,
Kind: controller.OutputExclusive,
},
}
}
// Run implements controller.Controller interface.
//
//nolint:gocyclo
func (ctrl *MaintenanceController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
refreshTicker := time.NewTicker(x509.DefaultCertificateValidityDuration / 2)
defer refreshTicker.Stop()
for {
select {
case <-ctx.Done():
return nil
case <-r.EventCh():
case <-refreshTicker.C:
}
rootSecrets, err := safe.ReaderGetByID[*secrets.MaintenanceRoot](ctx, r, secrets.MaintenanceRootID)
if err != nil {
if state.IsNotFoundError(err) {
continue
}
return fmt.Errorf("error getting maintenance root secrets: %w", err)
}
certSANs, err := safe.ReaderGetByID[*secrets.CertSAN](ctx, r, secrets.CertSANMaintenanceID)
if err != nil {
if state.IsNotFoundError(err) {
continue
}
return fmt.Errorf("error getting certSANs: %w", err)
}
ca, err := x509.NewCertificateAuthorityFromCertificateAndKey(rootSecrets.TypedSpec().CA)
if err != nil {
return fmt.Errorf("failed to parse CA certificate: %w", err)
}
serverCert, err := x509.NewKeyPair(ca,
x509.IPAddresses(certSANs.TypedSpec().StdIPs()),
x509.DNSNames(certSANs.TypedSpec().DNSNames),
x509.CommonName(certSANs.TypedSpec().FQDN),
x509.NotAfter(time.Now().Add(x509.DefaultCertificateValidityDuration)),
x509.KeyUsage(stdlibx509.KeyUsageDigitalSignature),
x509.ExtKeyUsage([]stdlibx509.ExtKeyUsage{
stdlibx509.ExtKeyUsageServerAuth,
}),
)
if err != nil {
return fmt.Errorf("failed to generate maintenance server cert: %w", err)
}
if err = safe.WriterModify(ctx, r, secrets.NewMaintenanceServiceCerts(),
func(maintenanceSecrets *secrets.MaintenanceServiceCerts) error {
spec := maintenanceSecrets.TypedSpec()
spec.CA = &x509.PEMEncodedCertificateAndKey{
Crt: rootSecrets.TypedSpec().CA.Crt,
}
spec.Server = x509.NewCertificateAndKeyFromKeyPair(serverCert)
return nil
}); err != nil {
return fmt.Errorf("error modifying resource: %w", err)
}
serverFingerprint, _ := x509.SPKIFingerprintFromDER(serverCert.Certificate.Certificate[0]) //nolint:errcheck
logger.Debug("generated new certificates",
zap.Stringer("server", serverFingerprint),
)
r.ResetRestartBackoff()
}
}