-
Notifications
You must be signed in to change notification settings - Fork 26
/
ignition.go
178 lines (159 loc) · 6.36 KB
/
ignition.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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
package templates
import (
"bytes"
"encoding/json"
"fmt"
"strings"
"text/template"
"github.com/Masterminds/sprig"
"github.com/aokoli/goutils"
"github.com/coreos/container-linux-config-transpiler/config"
"github.com/coreos/container-linux-config-transpiler/config/platform"
"github.com/coreos/ignition/config/validate/report"
"github.com/go-kit/kit/log"
"github.com/tredoe/osutil/user/crypt/sha512_crypt"
"k8s.io/api/core/v1"
kubernikusv1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1"
"github.com/sapcc/kubernikus/pkg/version"
)
type ignition struct {
requiredNodeSecrets []string
}
var Ignition = &ignition{
requiredNodeSecrets: []string{
"tls-ca.pem",
"kubelet-clients-ca.pem",
"apiserver-clients-system-kube-proxy.pem",
"apiserver-clients-system-kube-proxy-key.pem",
"bootstrapToken",
"openstack-auth-url",
"openstack-username",
"openstack-password",
"openstack-domain-name",
"openstack-region",
},
}
var passwordHashRounds = 1000000
func (i *ignition) getIgnitionTemplate(kluster *kubernikusv1.Kluster) string {
switch {
case strings.HasPrefix(kluster.Spec.Version, "1.9"):
return Node_1_9
case strings.HasPrefix(kluster.Spec.Version, "1.8"):
return Node_1_8
default:
return Node_1_7
}
}
func (i *ignition) GenerateNode(kluster *kubernikusv1.Kluster, secret *v1.Secret, logger log.Logger) ([]byte, error) {
for _, field := range i.requiredNodeSecrets {
if _, ok := secret.Data[field]; !ok {
return nil, fmt.Errorf("Field %s missing in secret", field)
}
}
ignition := i.getIgnitionTemplate(kluster)
tmpl, err := template.New("node").Funcs(sprig.TxtFuncMap()).Parse(ignition)
if err != nil {
return nil, err
}
//this is the old default for backwards comptibility with clusters that don't have a passwort generated
//TODO: Remove once all klusters are upgraded
passwordHash := "$6$rounds=1000000$aldshc,xbneroyw$I756LN/FtceE1deC2H.tGeSdeeelaaZWRwzmbEuO1SANf7ssyPjnbQjlW/FcMvWGUGrhF64tX9fK0abE/4oQ80"
if nodePassword, ok := secret.Data["node-password"]; ok {
passwordCrypter := sha512_crypt.New()
//generate 16 byte random salt
salt, err := goutils.Random(sha512_crypt.SaltLenMax, 32, 127, true, true)
if err != nil {
return nil, fmt.Errorf("Unable to generate random salt: %s", err)
}
//We crank up the heat to 1 million rounds of hashing for this password (default 5000)
//Reason for this is we expose the resulting hash in the metadata service which is not very secure.
//It takes about 500ms on my workstation to compute this hash. So this means login to a node is also
// delayed for about a second which should be ok as this password is only meant as a last resort.
passwordHash, err = passwordCrypter.Generate(nodePassword, append([]byte(fmt.Sprintf("%srounds=%d$", sha512_crypt.MagicPrefix, passwordHashRounds)), salt...))
if err != nil {
return nil, fmt.Errorf("Faied to generate salted password: %s", err)
}
}
data := struct {
TLSCA string
KubeletClientsCA string
ApiserverClientsSystemKubeProxy string
ApiserverClientsSystemKubeProxyKey string
ClusterDomain string
ClusterDNSAddress string
ClusterCIDR string
ApiserverURL string
ApiserverIP string
BootstrapToken string
OpenstackAuthURL string
OpenstackUsername string
OpenstackPassword string
OpenstackDomain string
OpenstackRegion string
OpenstackLBSubnetID string
OpenstackLBFloatingNetworkID string
OpenstackRouterID string
KubernikusImage string
KubernikusImageTag string
LoginPassword string
LoginPublicKey string
}{
TLSCA: string(secret.Data["tls-ca.pem"]),
KubeletClientsCA: string(secret.Data["kubelet-clients-ca.pem"]),
ApiserverClientsSystemKubeProxy: string(secret.Data["apiserver-clients-system-kube-proxy.pem"]),
ApiserverClientsSystemKubeProxyKey: string(secret.Data["apiserver-clients-system-kube-proxy-key.pem"]),
BootstrapToken: string(secret.Data["bootstrapToken"]),
ClusterCIDR: kluster.Spec.ClusterCIDR,
ClusterDNSAddress: kluster.Spec.DNSAddress,
ClusterDomain: kluster.Spec.DNSDomain,
ApiserverURL: kluster.Status.Apiserver,
ApiserverIP: kluster.Spec.AdvertiseAddress,
OpenstackAuthURL: string(secret.Data["openstack-auth-url"]),
OpenstackUsername: string(secret.Data["openstack-username"]),
OpenstackPassword: string(secret.Data["openstack-password"]),
OpenstackDomain: string(secret.Data["openstack-domain-name"]),
OpenstackRegion: string(secret.Data["openstack-region"]),
OpenstackLBSubnetID: kluster.Spec.Openstack.LBSubnetID,
OpenstackLBFloatingNetworkID: kluster.Spec.Openstack.LBFloatingNetworkID,
OpenstackRouterID: kluster.Spec.Openstack.RouterID,
KubernikusImage: "sapcc/kubernikus",
KubernikusImageTag: version.GitCommit,
LoginPassword: passwordHash,
LoginPublicKey: kluster.Spec.SSHPublicKey,
}
var dataOut []byte
var buffer bytes.Buffer
var report report.Report
defer func() {
logger.Log(
"msg", "ignition debug",
"data", data,
"yaml", string(buffer.Bytes()),
"json", string(dataOut),
"report", report.String(),
"v", 6,
"err", err)
}()
err = tmpl.Execute(&buffer, data)
if err != nil {
return nil, err
}
ignitionConfig, ast, report := config.Parse(buffer.Bytes())
if len(report.Entries) > 0 {
if report.IsFatal() {
return nil, fmt.Errorf("Couldn't transpile ignition file: %v", report.String())
}
}
ignitionConfig2_0, report := config.ConvertAs2_0(ignitionConfig, platform.OpenStackMetadata, ast)
if len(report.Entries) > 0 {
if report.IsFatal() {
return nil, fmt.Errorf("Couldn't convert ignition config: %v", report.String())
}
}
dataOut, err = json.MarshalIndent(&ignitionConfig2_0, "", " ")
dataOut = append(dataOut, '\n')
if err != nil {
return nil, err
}
return dataOut, nil
}