Skip to content
This repository was archived by the owner on Aug 12, 2025. It is now read-only.

Commit d8effdd

Browse files
committed
Switch from in-memory CA key/pair to in-cluster spec
1 parent 5a9a13b commit d8effdd

File tree

13 files changed

+161
-133
lines changed

13 files changed

+161
-133
lines changed

Gopkg.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ To deploy a cluster:
4040
* `provider-components.yaml` - note that this file _will_ contain your secrets, specifically `PACKET_API_KEY`, to be loaded into the cluster
4141
* `addons.yaml` - note that this file _will_ contain your secrets, specifically `PACKET_API_KEY`, to be loaded into the cluster
4242
1. If desired, edit the following files:
43-
* `cluster.yaml` - to change parameters or settings, including network CIDRs
43+
* `cluster.yaml` - to change parameters or settings, including network CIDRs, and, if desired, your own CA certificate and key
4444
* `machines.yaml` - to change parameters or settings, including machine types and quantity
4545
1. Run `clusterctl` with the appropriate command.
4646

@@ -68,6 +68,16 @@ Run `clusterctl create cluster --help` for more options, for example to use an e
6868
1. Deploy add-on components, e.g. the [packet cloud-controller-manager](https://github.com/packethost/packet-ccm) and the [packet cloud storage interface provider](https://github.com/packethost/csi-packet)
6969
1. If a new bootstrap cluster was created, terminate it
7070

71+
#### Defaults
72+
73+
If you do not change the generated `yaml` files, it will use defaults. You can look in the `*.yaml.template` files in [cmd/clusterctl/examples/packet/](./cmd/clusterctl/examples/packet/) for details.
74+
75+
* CA key/certificate: leave blank, which will cause the `manager` to create one.
76+
* service CIDR: `172.25.0.0/16`
77+
* pod CIDR: `172.26.0.0/16`
78+
* service domain: `cluster.local`
79+
* cluster name: `test1-<random>`, where random is a random 5-character string containing the characters `a-z0-9`
80+
7181
#### About Those Secrets
7282

7383
Notice that the API key is load into _two_ separate files, `provider-components.yaml` and `addons.yaml`. This is unfortunately necessary.
@@ -132,19 +142,17 @@ The Packet cluster-api provider follows the standard design for cluster-api. It
132142
The actual machines are deployed using `kubeadm`. The deployment process uses the following process.
133143

134144
1. When a new `Cluster` is created:
135-
* create a new CA certificate and key, encrypt and save them in a bootstrap controller Kubernetes secret
136-
* create a new kubeadm token, encrypt and save in a bootstrap controller Kubernetes secret. This
137-
* create an admin user certificate using the CA certificate and key, encrypt and save them in a bootstrap controller Kubernetes secret
145+
* if the `ClusterSpec` does not include a CA key/certificate pair, create one and save it on the `Cluster` object
138146
2. When a new master `Machine` is created:
139-
* retrieve the CA certificate and key from the bootstrap controller Kubernetes secret
140-
* retrieve the kubeadm token from the bootstrap controller Kubernetes secret
147+
* retrieve the CA certificate and key from the `Cluster` object
141148
* launch a new server instance on Packet
142-
* set the `cloud-init` on the instance to run `kubeadm init`, passing it the CA certificate and key and kubeadm token
149+
* set the `cloud-init` on the instance to run `kubeadm init`, passing it the CA certificate and key
143150
3. When a new worker `Machine` is created:
144-
* retrieve the kubeadm token from the Kubernetes secret
151+
* check if the cluster is ready, i.e. has a valid endpoint; if not, retry every 15 seconds
152+
* generate a new kubeadm bootstrap token and save it to the workload cluster
145153
* launch a new server instance on Packet
146-
* set the `cloud-init` on the instance to run `kubeadm join`, passing it the kubeadm token
147-
4. When a user requests the kubeconfig via `clusterctl`, it retrieves it from the Kubernetes secret and passes it to the user
154+
* set the `cloud-init` on the instance to run `kubeadm join`, passing it the newly generated kubeadm token
155+
4. When a user requests the kubeconfig via `clusterctl`, generate a new one using the CA key/certificate pair
148156

149157

150158
## Building
@@ -218,7 +226,7 @@ You can run the `manager` locally on your own laptop in order to ease and speed
218226
1. Set your `KUBECONFIG` to point to that cluster, e.g. `export KUBECONFIG=...`
219227
1. Create a local OS/arch `manager` binary, essentially `make manager`. This will save it as `bin/manager-<os>-<arch>`, e.g. `bin/manager-linux-arm64` or `bin/manager-darwin-amd64`
220228
1. Generate your yaml `./generate-yaml.sh`
221-
1. Run the manager against the cluster with the local configs, `bin/manager-darwin-amd64 -config ./config/default/machine_configs.yaml -ca-cache ./out/cache.json`
229+
1. Run the manager against the cluster with the local configs, `bin/manager-darwin-amd64 -config ./config/default/machine_configs.yaml`
222230

223231
In the above example:
224232

cmd/clusterctl/examples/packet/cluster.yaml.template

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,6 @@ spec:
1414
apiVersion: "packetprovider/v1alpha1"
1515
kind: "PacketClusterProviderSpec"
1616
projectID: "$PROJECT_ID"
17+
caKeyPair:
18+
cert: ""
19+
key: ""

cmd/manager/main.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ func main() {
5353
metricsAddr := flag.String("metrics-addr", ":8080", "The address the metric endpoint binds to.")
5454

5555
machineSetupConfig := flag.String("config", "/etc/machineconfig/machine_configs.yaml", "path to the machine setup config")
56-
caCache := flag.String("ca-cache", "", "path to file to save cluster credentials; should be used for development purposes only, as it saves actual CA credentials")
5756
flag.Parse()
5857

5958
log := logf.Log.WithName("packet-controller-manager")
@@ -79,9 +78,8 @@ func main() {
7978
}
8079
// get a deployer, which is needed at various stages
8180
deployer, err := deployer.New(deployer.Params{
82-
Client: client,
83-
Port: controlPort,
84-
CACache: *caCache,
81+
Client: client,
82+
Port: controlPort,
8583
})
8684
if err != nil {
8785
klog.Fatalf(err.Error())

config/crds/packetprovider_v1alpha1_packetclusterproviderspec.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,20 @@ spec:
1919
of an object. Servers should convert recognized schemas to the latest
2020
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources'
2121
type: string
22+
caKeyPair:
23+
description: CAKeyPair is the key pair for ca certs.
24+
properties:
25+
cert:
26+
description: base64 encoded cert and key
27+
format: byte
28+
type: string
29+
key:
30+
format: byte
31+
type: string
32+
required:
33+
- cert
34+
- key
35+
type: object
2236
kind:
2337
description: 'Kind is a string value representing the REST resource this
2438
object represents. Servers may infer this from the endpoint the client

pkg/apis/packetprovider/v1alpha1/packetclusterproviderspec_types.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@ type PacketClusterProviderSpec struct {
3333
metav1.ObjectMeta `json:"metadata,omitempty"`
3434

3535
ProjectID string `json:"projectID"`
36+
37+
// CAKeyPair is the key pair for ca certs.
38+
CAKeyPair KeyPair `json:"caKeyPair,omitempty"`
39+
}
40+
41+
// KeyPair is how operators can supply custom keypairs for kubeadm to use.
42+
type KeyPair struct {
43+
// base64 encoded cert and key
44+
Cert []byte `json:"cert"`
45+
Key []byte `json:"key"`
3646
}
3747

3848
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

pkg/apis/packetprovider/v1alpha1/packetclusterproviderspec_types_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ func TestStoragePacketClusterProviderSpec(t *testing.T) {
3434
ObjectMeta: metav1.ObjectMeta{
3535
Name: "foo",
3636
Namespace: "default",
37-
}}
37+
},
38+
CAKeyPair: KeyPair{
39+
Key: []byte{10, 20, 30},
40+
Cert: []byte{10, 20, 30},
41+
},
42+
}
3843
g := gomega.NewGomegaWithT(t)
3944

4045
// Test Create

pkg/apis/packetprovider/v1alpha1/zz_generated.deepcopy.go

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/cloud/packet/actuators/cluster/actuator.go

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424

2525
"github.com/packethost/cluster-api-provider-packet/pkg/cloud/packet/ca"
2626
"github.com/packethost/cluster-api-provider-packet/pkg/cloud/packet/deployer"
27+
"github.com/packethost/cluster-api-provider-packet/pkg/cloud/packet/util"
2728
clusterv1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1"
2829
client "sigs.k8s.io/cluster-api/pkg/client/clientset_generated/clientset/typed/cluster/v1alpha1"
2930
controllerError "sigs.k8s.io/cluster-api/pkg/controller/error"
@@ -60,16 +61,39 @@ func (a *Actuator) Reconcile(cluster *clusterv1.Cluster) error {
6061
log.Printf("Reconciling cluster %v.", cluster.Name)
6162
// save the original status
6263
clusterCopy := cluster.DeepCopy()
64+
// get a client we can use
65+
var (
66+
clusterClient client.ClusterInterface
67+
updatedCluster *clusterv1.Cluster
68+
)
69+
if a.clustersGetter != nil {
70+
clusterClient = a.clustersGetter.Clusters(cluster.Namespace)
71+
}
6372
// ensure that we have a CA cert/key and save it
64-
if cert, _ := a.deployer.GetCA(cluster.Name); cert == nil {
73+
c, err := util.ClusterProviderFromProviderConfig(cluster.Spec.ProviderSpec)
74+
if err != nil {
75+
return fmt.Errorf("unable to unpack cluster provider for cluster %s: %v", cluster.Name, err)
76+
}
77+
if len(c.CAKeyPair.Cert) == 0 || len(c.CAKeyPair.Cert) == 0 {
6578
caCertAndKey, err := ca.GenerateCACertAndKey(cluster.Name, "")
6679
if err != nil {
67-
return fmt.Errorf("unable to generate CA cert and key: %v", err)
80+
return fmt.Errorf("unable to generate CA cert and key for cluster %s: %v", cluster.Name, err)
6881
}
69-
err = a.deployer.PutCA(cluster.Name, caCertAndKey)
82+
c.CAKeyPair.Cert = caCertAndKey.Certificate
83+
c.CAKeyPair.Key = caCertAndKey.PrivateKey
84+
// update cluster spec
85+
spec, err := util.ClusterProviderConfigFromProvider(c)
7086
if err != nil {
71-
return fmt.Errorf("unable to save CA cert and key: %v", err)
87+
return fmt.Errorf("unable to convert newly generated provider spec with CA key/certificate to provider config for %s: %v", cluster.Name, err)
88+
}
89+
cluster.Spec.ProviderSpec = spec
90+
log.Printf("saving updated cluster spec %s", cluster.Name)
91+
if updatedCluster, err = clusterClient.Update(cluster); err != nil {
92+
msg := fmt.Sprintf("failed to save updated cluster %s: %v", cluster.Name, err)
93+
log.Printf(msg)
94+
return fmt.Errorf(msg)
7295
}
96+
cluster = updatedCluster
7397
}
7498
// ensure that we save the correct IP address for the cluster
7599
address, err := a.deployer.GetIP(cluster, nil)
@@ -91,10 +115,6 @@ func (a *Actuator) Reconcile(cluster *clusterv1.Cluster) error {
91115
}
92116
}
93117

94-
var clusterClient client.ClusterInterface
95-
if a.clustersGetter != nil {
96-
clusterClient = a.clustersGetter.Clusters(cluster.Namespace)
97-
}
98118
if !reflect.DeepEqual(cluster.Status, clusterCopy.Status) {
99119
log.Printf("saving updated cluster status %s", cluster.Name)
100120
if _, err := clusterClient.UpdateStatus(cluster); err != nil {
@@ -110,7 +130,5 @@ func (a *Actuator) Reconcile(cluster *clusterv1.Cluster) error {
110130
// Delete deletes a cluster and is invoked by the Cluster Controller
111131
func (a *Actuator) Delete(cluster *clusterv1.Cluster) error {
112132
log.Printf("Deleting cluster %v.", cluster.Name)
113-
// remove the CA cert key
114-
a.deployer.DeleteCA(cluster.Name)
115133
return nil
116134
}

pkg/cloud/packet/actuators/machine/actuator.go

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import (
2828
"github.com/packethost/cluster-api-provider-packet/pkg/cloud/packet/util"
2929
"github.com/packethost/packngo"
3030
clusterv1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1"
31-
"sigs.k8s.io/cluster-api/pkg/cert"
3231
)
3332

3433
const (
@@ -78,6 +77,10 @@ func (a *Actuator) Create(ctx context.Context, cluster *clusterv1.Cluster, machi
7877
if err != nil {
7978
return fmt.Errorf("Unable to read providerSpec from machine config: %v", err)
8079
}
80+
clusterConfig, err := util.ClusterProviderFromProviderConfig(cluster.Spec.ProviderSpec)
81+
if err != nil {
82+
return fmt.Errorf("unable to unpack cluster provider: %v", err)
83+
}
8184

8285
tags := []string{
8386
util.GenerateMachineTag(string(machine.UID)),
@@ -97,16 +100,8 @@ func (a *Actuator) Create(ctx context.Context, cluster *clusterv1.Cluster, machi
97100
)
98101
if machine.Spec.Versions.ControlPlane != "" {
99102
role = "master"
100-
// generate a cluster CA cert and key
101-
var (
102-
caCertAndKey *cert.CertificateAuthority
103-
ok bool
104-
)
105-
if caCertAndKey, ok = a.deployer.Certs[cluster.Name]; !ok {
106-
return fmt.Errorf("Unable to read CA cert/key for uninitialized cluster %s", cluster.Name)
107-
}
108-
caCert = caCertAndKey.Certificate
109-
caKey = caCertAndKey.PrivateKey
103+
caCert = clusterConfig.CAKeyPair.Cert
104+
caKey = clusterConfig.CAKeyPair.Key
110105
tags = append(tags, util.MasterTag)
111106
} else {
112107
token, err = a.deployer.NewBootstrapToken(cluster)

0 commit comments

Comments
 (0)