Serge Bazanski | 9f0e1e8 | 2023-03-31 22:36:54 +0000 | [diff] [blame] | 1 | package certs |
| 2 | |
| 3 | import ( |
| 4 | "net" |
| 5 | "time" |
| 6 | ) |
| 7 | |
| 8 | // Certificates is the set of certificates required to run our Kubernetes |
| 9 | // production. |
| 10 | type Certificates struct { |
| 11 | CAs CAs |
| 12 | |
| 13 | ProdviderIntermediateCA *Certificate |
| 14 | |
| 15 | Global Global |
| 16 | PerNode map[string]PerNode |
| 17 | } |
| 18 | |
| 19 | type ensurer interface { |
| 20 | Ensure() error |
| 21 | } |
| 22 | |
| 23 | // Ensure checks that all the Kubernetes production certificates and keys are |
| 24 | // present on disk, generating them as necessary. |
| 25 | // |
| 26 | // If the user has not decrypted cluster/secrets, an error will be returned. |
| 27 | // However, deeper sync checks are not currently performed. |
| 28 | func (c *Certificates) Ensure() error { |
| 29 | sub := []ensurer{ |
| 30 | &c.CAs, |
| 31 | c.ProdviderIntermediateCA, |
| 32 | &c.Global, |
| 33 | } |
| 34 | for _, pn := range c.PerNode { |
| 35 | sub = append(sub, &pn) |
| 36 | } |
| 37 | for _, s := range sub { |
| 38 | if err := s.Ensure(); err != nil { |
| 39 | return err |
| 40 | } |
| 41 | } |
| 42 | return nil |
| 43 | } |
| 44 | |
| 45 | // CAs are the root certificate authorities we use. |
| 46 | type CAs struct { |
| 47 | // EtcdPeer is used by etcd member nodes to authenticate peers. |
| 48 | EtcdPeer *Certificate |
| 49 | // Etcd is used by etcd membrer nodes to authenticate clients (ie. |
| 50 | // kube-apiservers). |
| 51 | Etcd *Certificate |
| 52 | // Kube is the main Kubernetes 'identity' CA, used to identify components |
| 53 | // and users. |
| 54 | Kube *Certificate |
| 55 | // KubeFront is the proxy/aggregation CA used by external apiservices to |
| 56 | // authenticate incoming apiserver connections. |
| 57 | KubeFront *Certificate |
| 58 | // Admitomatic is the CA used by the admitomatic webhook to authenticate |
| 59 | // incoming apiserver connections. |
| 60 | Admitomatic *Certificate |
| 61 | } |
| 62 | |
| 63 | func (c *CAs) Ensure() error { |
| 64 | sub := []ensurer{ |
| 65 | c.EtcdPeer, |
| 66 | c.Etcd, |
| 67 | c.Kube, |
| 68 | c.KubeFront, |
| 69 | c.Admitomatic, |
| 70 | } |
| 71 | for _, s := range sub { |
| 72 | if err := s.Ensure(); err != nil { |
| 73 | return err |
| 74 | } |
| 75 | } |
| 76 | return nil |
| 77 | } |
| 78 | |
| 79 | // Global are all the non-per-node certificates we use. |
| 80 | type Global struct { |
| 81 | // EtcdKube is used by kubernetes apiservers to authenticate to etcd |
| 82 | // members. |
| 83 | EtcdKube *Certificate |
| 84 | |
| 85 | // KubeApiserver is used by kubernetes apiservers to authenticate to other |
| 86 | // kubernetes components/users. |
| 87 | KubeApiserver *Certificate |
| 88 | // KubeControllerManager is used by kubernetes controller managers to |
| 89 | // authenticate to the kubernetes apiservers. |
| 90 | KubeControllerManager *Certificate |
| 91 | // KubeScheduler is used by the kubernetes schedulers to authenticate to |
| 92 | // the kubernetes apiservers. |
| 93 | KubeScheduler *Certificate |
| 94 | |
| 95 | // KubefrontApiserver is used by the kubernetes apiserver to authenticate |
| 96 | // to external apiservices. |
| 97 | KubefrontApiserver *Certificate |
| 98 | |
| 99 | // AdmitomaticWebhook is used by the admitomatic webhook to authenticate to |
| 100 | // the Kubernetes apiservers. |
| 101 | AdmitomaticWebhook *Certificate |
| 102 | } |
| 103 | |
| 104 | func (g *Global) Ensure() error { |
| 105 | sub := []ensurer{ |
| 106 | g.EtcdKube, |
| 107 | g.KubeApiserver, |
| 108 | g.KubeControllerManager, |
| 109 | g.KubeScheduler, |
| 110 | g.KubefrontApiserver, |
| 111 | g.AdmitomaticWebhook, |
| 112 | } |
| 113 | for _, s := range sub { |
| 114 | if err := s.Ensure(); err != nil { |
| 115 | return err |
| 116 | } |
| 117 | } |
| 118 | return nil |
| 119 | } |
| 120 | |
| 121 | func (c *Certificates) MakeKubeEmergencyCreds(root, breadcrumb string) *Certificate { |
| 122 | return &Certificate{ |
| 123 | name: "emergency", |
| 124 | duration: 7 * 24 * time.Hour, |
| 125 | root: root, |
| 126 | kind: kindClient, |
| 127 | cn: "admin", |
| 128 | san: []string{"admin", breadcrumb}, |
| 129 | o: "system:masters", |
| 130 | issuer: c.CAs.Kube, |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | // Per node are all the per-node certificates we use. |
| 135 | type PerNode struct { |
| 136 | // EtcdPeer is used by etcd members to authenticate to other etcd members. |
| 137 | EtcdPeer *Certificate |
| 138 | // EtcdClient is used by etcd members to authenticate to their clients. |
| 139 | EtcdClient *Certificate |
| 140 | |
| 141 | // Kubelet is used by kubelets to authenticate to other kubernetes |
| 142 | // components. |
| 143 | Kubelet *Certificate |
| 144 | } |
| 145 | |
| 146 | func (p *PerNode) Ensure() error { |
| 147 | sub := []ensurer{ |
| 148 | p.EtcdPeer, |
| 149 | p.EtcdClient, |
| 150 | p.Kubelet, |
| 151 | } |
| 152 | for _, s := range sub { |
| 153 | if err := s.Ensure(); err != nil { |
| 154 | return err |
| 155 | } |
| 156 | } |
| 157 | return nil |
| 158 | } |
| 159 | |
| 160 | func mkCA(root, name, cn string) *Certificate { |
| 161 | return &Certificate{ |
| 162 | name: name, |
| 163 | root: root, |
| 164 | kind: kindCA, |
| 165 | cn: cn, |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // Prepare builds our Certificates structure at a given location on the |
| 170 | // filesystem, for the given nodes. |
| 171 | // |
| 172 | // Calling Ensure() on the returned Certificates will actually engage |
| 173 | // generation logic. Before that, no disk accesses are performed. |
| 174 | func Prepare(root string, fqdns []string) Certificates { |
| 175 | certs := Certificates{ |
| 176 | CAs: CAs{ |
| 177 | EtcdPeer: mkCA(root, "ca-etcdpeer", "etcd peer ca"), |
| 178 | Etcd: mkCA(root, "ca-etcd", "etcd ca"), |
| 179 | Kube: mkCA(root, "ca-kube", "kubernetes main CA"), |
| 180 | KubeFront: mkCA(root, "ca-kubefront", "kubernetes frontend CA"), |
| 181 | Admitomatic: mkCA(root, "ca-admitomatic", "admitomatic webhook CA"), |
| 182 | }, |
| 183 | PerNode: make(map[string]PerNode), |
| 184 | } |
| 185 | |
| 186 | certs.ProdviderIntermediateCA = &Certificate{ |
| 187 | name: "ca-kube-prodvider", |
| 188 | root: root, |
| 189 | kind: kindProdvider, |
| 190 | cn: "kubernetes prodvider intermediate", |
| 191 | issuer: certs.CAs.Kube, |
| 192 | } |
| 193 | certs.Global = Global{ |
| 194 | EtcdKube: &Certificate{ |
| 195 | name: "etcd-kube", |
| 196 | root: root, |
| 197 | kind: kindClient, |
| 198 | cn: "kube etcd client certificate", |
| 199 | san: []string{"kube"}, |
| 200 | issuer: certs.CAs.Etcd, |
| 201 | }, |
| 202 | KubeApiserver: &Certificate{ |
| 203 | name: "kube-apiserver", |
| 204 | root: root, |
| 205 | kind: kindClientServer, |
| 206 | cn: "k0.hswaw.net", |
| 207 | san: []string{ |
| 208 | "k0.hswaw.net", |
| 209 | "kubernetes.default.svc.k0.hswaw.net", |
| 210 | }, |
| 211 | ips: []net.IP{ |
| 212 | {10, 10, 12, 1}, |
| 213 | }, |
| 214 | issuer: certs.CAs.Kube, |
| 215 | }, |
| 216 | KubeControllerManager: &Certificate{ |
| 217 | name: "kube-controllermanager", |
| 218 | root: root, |
| 219 | kind: kindClientServer, |
| 220 | cn: "system:kube-controller-manager", |
| 221 | san: []string{"system:kube-controller-manager"}, |
| 222 | o: "system:kube-controller-manager", |
| 223 | issuer: certs.CAs.Kube, |
| 224 | }, |
| 225 | KubeScheduler: &Certificate{ |
| 226 | name: "kube-scheduler", |
| 227 | root: root, |
| 228 | kind: kindClientServer, |
| 229 | cn: "system:kube-scheduler", |
| 230 | san: []string{"system:kube-scheduler"}, |
| 231 | o: "system:kube-scheduler", |
| 232 | issuer: certs.CAs.Kube, |
| 233 | }, |
| 234 | KubefrontApiserver: &Certificate{ |
| 235 | name: "kubefront-apiserver", |
| 236 | root: root, |
| 237 | kind: kindClientServer, |
| 238 | cn: "Kubernetes Frontend", |
| 239 | san: []string{"apiserver"}, |
| 240 | issuer: certs.CAs.KubeFront, |
| 241 | }, |
| 242 | AdmitomaticWebhook: &Certificate{ |
| 243 | name: "admitomatic-webhook", |
| 244 | root: root, |
| 245 | kind: kindServer, |
| 246 | cn: "Admitomatic Webhook", |
| 247 | san: []string{"admitomatic.admitomatic.svc"}, |
| 248 | issuer: certs.CAs.Admitomatic, |
| 249 | }, |
| 250 | } |
| 251 | for _, fqdn := range fqdns { |
| 252 | certs.PerNode[fqdn] = PerNode{ |
| 253 | EtcdPeer: &Certificate{ |
| 254 | name: "etcdpeer-" + fqdn, |
| 255 | root: root, |
| 256 | kind: kindClientServer, |
| 257 | cn: "node etcd peer certificate", |
| 258 | san: []string{fqdn}, |
| 259 | issuer: certs.CAs.EtcdPeer, |
| 260 | }, |
| 261 | EtcdClient: &Certificate{ |
| 262 | name: "etcd-" + fqdn, |
| 263 | root: root, |
| 264 | // etcd seems to need client too, as it's connecting to itself |
| 265 | // for... some reason? |
| 266 | // https://github.com/etcd-io/etcd/issues/9785 |
| 267 | kind: kindClientServer, |
| 268 | cn: "node etcd server certificate", |
| 269 | san: []string{fqdn}, |
| 270 | issuer: certs.CAs.Etcd, |
| 271 | }, |
| 272 | Kubelet: &Certificate{ |
| 273 | name: "kube-kubelet-" + fqdn, |
| 274 | root: root, |
| 275 | kind: kindClientServer, |
| 276 | cn: "system:node:" + fqdn, |
| 277 | o: "system:nodes", |
| 278 | san: []string{ |
| 279 | "system:node:" + fqdn, |
| 280 | fqdn, |
| 281 | }, |
| 282 | issuer: certs.CAs.Kube, |
| 283 | }, |
| 284 | } |
| 285 | } |
| 286 | |
| 287 | return certs |
| 288 | } |