diff --git a/tests/e2e/go.mod b/tests/e2e/go.mod index 0a704fa306..4ce8406ffd 100644 --- a/tests/e2e/go.mod +++ b/tests/e2e/go.mod @@ -7,6 +7,7 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/octago/sflags v0.2.0 github.com/spf13/pflag v1.0.5 + golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 k8s.io/api v0.23.5 k8s.io/apimachinery v0.23.5 k8s.io/client-go v9.0.0+incompatible @@ -122,7 +123,6 @@ require ( github.com/xanzy/ssh-agent v0.3.1 // indirect go.opencensus.io v0.23.0 // indirect go.uber.org/atomic v1.9.0 // indirect - golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect diff --git a/tests/e2e/kubetest2-kops/deployer/common.go b/tests/e2e/kubetest2-kops/deployer/common.go index 29b69943bc..fc03d86e0f 100644 --- a/tests/e2e/kubetest2-kops/deployer/common.go +++ b/tests/e2e/kubetest2-kops/deployer/common.go @@ -29,6 +29,7 @@ import ( "k8s.io/kops/tests/e2e/kubetest2-kops/gce" "k8s.io/kops/tests/e2e/pkg/kops" "k8s.io/kops/tests/e2e/pkg/target" + "k8s.io/kops/tests/e2e/pkg/util" "sigs.k8s.io/kubetest2/pkg/boskos" ) @@ -66,13 +67,13 @@ func (d *deployer) initialize() error { switch d.CloudProvider { case "aws": - // These environment variables are defined by the "preset-aws-ssh" prow preset - // https://github.com/kubernetes/test-infra/blob/3d3b325c98b739b526ba5d93ce21c90a05e1f46d/config/prow/config.yaml#L653-L670 - if d.SSHPrivateKeyPath == "" { - d.SSHPrivateKeyPath = os.Getenv("AWS_SSH_PRIVATE_KEY_FILE") - } - if d.SSHPublicKeyPath == "" { - d.SSHPublicKeyPath = os.Getenv("AWS_SSH_PUBLIC_KEY_FILE") + if d.SSHPrivateKeyPath == "" || d.SSHPublicKeyPath == "" { + publicKeyPath, privateKeyPath, err := util.CreateSSHKeyPair(d.ClusterName) + if err != nil { + return err + } + d.SSHPublicKeyPath = publicKeyPath + d.SSHPrivateKeyPath = privateKeyPath } case "digitalocean": if d.SSHPrivateKeyPath == "" { @@ -185,6 +186,9 @@ func (d *deployer) env() []string { vars = append(vars, k+"="+v) } } + // Recognized by the e2e framework + // https://github.com/kubernetes/kubernetes/blob/a750d8054a6cb3167f495829ce3e77ab0ccca48e/test/e2e/framework/ssh/ssh.go#L59-L62 + vars = append(vars, fmt.Sprintf("KUBE_SSH_KEY_PATH=%v", d.SSHPrivateKeyPath)) } else if d.CloudProvider == "digitalocean" { // Pass through some env vars if set for _, k := range []string{"DIGITALOCEAN_ACCESS_TOKEN", "S3_ACCESS_KEY_ID", "S3_SECRET_ACCESS_KEY"} { diff --git a/tests/e2e/pkg/util/pki.go b/tests/e2e/pkg/util/pki.go new file mode 100644 index 0000000000..48deb7e848 --- /dev/null +++ b/tests/e2e/pkg/util/pki.go @@ -0,0 +1,91 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "crypto/ed25519" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + "os" + "path/filepath" + + "golang.org/x/crypto/ssh" +) + +// CreateSSHKeyPair creates a key pair in a temp directory +// and returns the paths to the private and public keys respectively. +// The file paths are deterministic from the clusterName. +func CreateSSHKeyPair(clusterName string) (string, string, error) { + _, privateKey, err := ed25519.GenerateKey(nil) + if err != nil { + return "", "", err + } + publicKey, err := ssh.NewPublicKey(privateKey.Public()) + if err != nil { + return "", "", err + } + + publicKeyContents := ssh.MarshalAuthorizedKey(publicKey) + + user := os.Getenv("USER") + if user == "" { + user = "user" + } + comment := fmt.Sprintf(" %v\n", user) + + // AWS requires a comment on the SSH public key but MarshalAuthorizedKey doesn't create one + publicKeyContents = publicKeyContents[:len(publicKeyContents)-1] + publicKeyContents = append(publicKeyContents, []byte(comment)...) + + privateKeyContents, err := x509.MarshalPKCS8PrivateKey(privateKey) + if err != nil { + return "", "", err + } + + tmp := os.TempDir() + dir := filepath.Join(tmp, "kops", clusterName) + err = os.MkdirAll(dir, 0700) + if err != nil { + return "", "", err + } + + publicKeyPath := filepath.Join(dir, "id_ed25519.pub") + privateKeyPath := filepath.Join(dir, "id_ed25519") + + if _, err := os.Stat(privateKeyPath); errors.Is(err, os.ErrNotExist) { + if err := os.WriteFile(publicKeyPath, publicKeyContents, 0644); err != nil { + return "", "", err + } + f, err := os.OpenFile(privateKeyPath, os.O_WRONLY|os.O_CREATE, 0600) + if err != nil { + return "", "", err + } + defer f.Close() + + err = pem.Encode(f, &pem.Block{ + Type: "PRIVATE KEY", + Bytes: privateKeyContents, + }) + if err != nil { + return "", "", err + } + } + + return publicKeyPath, privateKeyPath, nil +}