From c84ca03eeb5258b2fb378af8045639cf6304558f Mon Sep 17 00:00:00 2001 From: Simon Murray Date: Fri, 30 Aug 2024 11:06:47 +0100 Subject: [PATCH] Add in Ephemeral SSH Support (#53) This gives us an emergency backdoor for support, because you can't turn back time and suddenely magic SSH into existence when stuff does go wrong. --- Makefile | 2 +- .../region.unikorn-cloud.org_identities.yaml | 2 +- ...unikorn-cloud.org_openstackidentities.yaml | 10 +- ...n-cloud.org_openstackphysicalnetworks.yaml | 2 +- ...on.unikorn-cloud.org_physicalnetworks.yaml | 2 +- .../region.unikorn-cloud.org_regions.yaml | 2 +- ...ion.unikorn-cloud.org_vlanallocations.yaml | 2 +- go.mod | 2 +- pkg/apis/unikorn/v1alpha1/types.go | 4 + .../unikorn/v1alpha1/zz_generated.deepcopy.go | 10 + pkg/handler/handler.go | 1 + pkg/openapi/schema.go | 195 +++++++++--------- pkg/openapi/server.spec.yaml | 5 + pkg/openapi/types.go | 3 + pkg/providers/helpers.go | 31 +++ pkg/providers/openstack/compute.go | 28 +++ pkg/providers/openstack/provider.go | 44 +++- 17 files changed, 233 insertions(+), 112 deletions(-) diff --git a/Makefile b/Makefile index 310b585..87cb06f 100644 --- a/Makefile +++ b/Makefile @@ -64,7 +64,7 @@ FLAGS=-trimpath -ldflags '-X $(MODULE)/pkg/constants.Version=$(VERSION) -X $(MOD LINT_VERSION=v1.59.1 # Defines the version of the CRD generation tools to use. -CONTROLLER_TOOLS_VERSION=v0.14.0 +CONTROLLER_TOOLS_VERSION=v0.16.1 # Defines the version of code generator tools to use. # This should be kept in sync with the Kubenetes library versions defined in go.mod. diff --git a/charts/region/crds/region.unikorn-cloud.org_identities.yaml b/charts/region/crds/region.unikorn-cloud.org_identities.yaml index 1fcae08..c6c84a9 100644 --- a/charts/region/crds/region.unikorn-cloud.org_identities.yaml +++ b/charts/region/crds/region.unikorn-cloud.org_identities.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.1 name: identities.region.unikorn-cloud.org spec: group: region.unikorn-cloud.org diff --git a/charts/region/crds/region.unikorn-cloud.org_openstackidentities.yaml b/charts/region/crds/region.unikorn-cloud.org_openstackidentities.yaml index 8f232cd..974f14a 100644 --- a/charts/region/crds/region.unikorn-cloud.org_openstackidentities.yaml +++ b/charts/region/crds/region.unikorn-cloud.org_openstackidentities.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.1 name: openstackidentities.region.unikorn-cloud.org spec: group: region.unikorn-cloud.org @@ -73,6 +73,14 @@ spec: description: ServerGroupID is the ID of the server group created for the identity. type: string + sshKeyName: + description: SSHKeyName is the ssh key that may be injected into clusters + by consuming services. + type: string + sshPrivateKey: + description: SSHPrivateKey is a PEM encoded private key. + format: byte + type: string userID: description: UserID is the ID of the user created for the identity. type: string diff --git a/charts/region/crds/region.unikorn-cloud.org_openstackphysicalnetworks.yaml b/charts/region/crds/region.unikorn-cloud.org_openstackphysicalnetworks.yaml index c85bff9..555a3a8 100644 --- a/charts/region/crds/region.unikorn-cloud.org_openstackphysicalnetworks.yaml +++ b/charts/region/crds/region.unikorn-cloud.org_openstackphysicalnetworks.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.1 name: openstackphysicalnetworks.region.unikorn-cloud.org spec: group: region.unikorn-cloud.org diff --git a/charts/region/crds/region.unikorn-cloud.org_physicalnetworks.yaml b/charts/region/crds/region.unikorn-cloud.org_physicalnetworks.yaml index 2e8d396..1e67a61 100644 --- a/charts/region/crds/region.unikorn-cloud.org_physicalnetworks.yaml +++ b/charts/region/crds/region.unikorn-cloud.org_physicalnetworks.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.1 name: physicalnetworks.region.unikorn-cloud.org spec: group: region.unikorn-cloud.org diff --git a/charts/region/crds/region.unikorn-cloud.org_regions.yaml b/charts/region/crds/region.unikorn-cloud.org_regions.yaml index a951219..5605d18 100644 --- a/charts/region/crds/region.unikorn-cloud.org_regions.yaml +++ b/charts/region/crds/region.unikorn-cloud.org_regions.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.1 name: regions.region.unikorn-cloud.org spec: group: region.unikorn-cloud.org diff --git a/charts/region/crds/region.unikorn-cloud.org_vlanallocations.yaml b/charts/region/crds/region.unikorn-cloud.org_vlanallocations.yaml index 88b29b8..f951837 100644 --- a/charts/region/crds/region.unikorn-cloud.org_vlanallocations.yaml +++ b/charts/region/crds/region.unikorn-cloud.org_vlanallocations.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.16.1 name: vlanallocations.region.unikorn-cloud.org spec: group: region.unikorn-cloud.org diff --git a/go.mod b/go.mod index 3a6134a..6830bf8 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( go.opentelemetry.io/otel v1.29.0 go.opentelemetry.io/otel/sdk v1.29.0 go.opentelemetry.io/otel/trace v1.29.0 + golang.org/x/crypto v0.26.0 k8s.io/api v0.31.0 k8s.io/apimachinery v0.31.0 sigs.k8s.io/controller-runtime v0.19.0 @@ -73,7 +74,6 @@ require ( go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.26.0 // indirect golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 // indirect golang.org/x/net v0.28.0 // indirect golang.org/x/oauth2 v0.22.0 // indirect diff --git a/pkg/apis/unikorn/v1alpha1/types.go b/pkg/apis/unikorn/v1alpha1/types.go index d4001b5..d132b57 100644 --- a/pkg/apis/unikorn/v1alpha1/types.go +++ b/pkg/apis/unikorn/v1alpha1/types.go @@ -344,6 +344,10 @@ type OpenstackIdentitySpec struct { ApplicationCredentialSecret *string `json:"applicationCredentialSecret,omitempty"` // ServerGroupID is the ID of the server group created for the identity. ServerGroupID *string `json:"serverGroupID,omitempty"` + // SSHKeyName is the ssh key that may be injected into clusters by consuming services. + SSHKeyName *string `json:"sshKeyName,omitempty"` + // SSHPrivateKey is a PEM encoded private key. + SSHPrivateKey []byte `json:"sshPrivateKey,omitempty"` } type OpenstackIdentityStatus struct{} diff --git a/pkg/apis/unikorn/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/unikorn/v1alpha1/zz_generated.deepcopy.go index c09e137..58bc20f 100644 --- a/pkg/apis/unikorn/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/unikorn/v1alpha1/zz_generated.deepcopy.go @@ -451,6 +451,16 @@ func (in *OpenstackIdentitySpec) DeepCopyInto(out *OpenstackIdentitySpec) { *out = new(string) **out = **in } + if in.SSHKeyName != nil { + in, out := &in.SSHKeyName, &out.SSHKeyName + *out = new(string) + **out = **in + } + if in.SSHPrivateKey != nil { + in, out := &in.SSHPrivateKey, &out.SSHPrivateKey + *out = make([]byte, len(*in)) + copy(*out, *in) + } return } diff --git a/pkg/handler/handler.go b/pkg/handler/handler.go index aca0f36..9ed3ca1 100644 --- a/pkg/handler/handler.go +++ b/pkg/handler/handler.go @@ -344,6 +344,7 @@ func (h *Handler) convertIdentity(ctx context.Context, in *unikornv1.Identity) * UserId: openstackIdentity.Spec.UserID, ProjectId: openstackIdentity.Spec.ProjectID, ServerGroupId: openstackIdentity.Spec.ServerGroupID, + SshKeyName: openstackIdentity.Spec.SSHKeyName, } if openstackIdentity.Spec.CloudConfig != nil { diff --git a/pkg/openapi/schema.go b/pkg/openapi/schema.go index 4e8997d..14af854 100644 --- a/pkg/openapi/schema.go +++ b/pkg/openapi/schema.go @@ -19,103 +19,104 @@ import ( // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", + "/fWfJ9L9fUj37u5/AwAA//+PuvDR0nsAAA==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/pkg/openapi/server.spec.yaml b/pkg/openapi/server.spec.yaml index 4bb123e..4a90a42 100644 --- a/pkg/openapi/server.spec.yaml +++ b/pkg/openapi/server.spec.yaml @@ -545,6 +545,9 @@ components: serverGroupId: description: Server group identifier allocated for the intrastructure. type: string + sshKeyName: + description: Ephemeral SSH key generated for the identity. + type: string identitySpec: description: |- A provider specific identity, while the client can list regions to infer the @@ -802,6 +805,7 @@ components: cloudConfig: dGhpcyBpcyBhIHRlc3QK projectId: eb9c92d937464d14bf87e50fa726380d userId: a19678a28126497dba24b54c96a064fa + sshKeyNmae: foo identitiesResponse: description: A list of identites. content: @@ -825,6 +829,7 @@ components: cloudConfig: dGhpcyBpcyBhIHRlc3QK projectId: eb9c92d937464d14bf87e50fa726380d userId: a19678a28126497dba24b54c96a064fa + sshKeyNmae: foo physicalNetworkResponse: description: A physical network. content: diff --git a/pkg/openapi/types.go b/pkg/openapi/types.go index ed6630d..ec139b0 100644 --- a/pkg/openapi/types.go +++ b/pkg/openapi/types.go @@ -144,6 +144,9 @@ type IdentitySpecOpenStack struct { // ServerGroupId Server group identifier allocated for the intrastructure. ServerGroupId *string `json:"serverGroupId,omitempty"` + // SshKeyName Ephemeral SSH key generated for the identity. + SshKeyName *string `json:"sshKeyName,omitempty"` + // UserId User identitifer allocated for the infrastructure. UserId *string `json:"userId,omitempty"` } diff --git a/pkg/providers/helpers.go b/pkg/providers/helpers.go index bbf5f28..cec1354 100644 --- a/pkg/providers/helpers.go +++ b/pkg/providers/helpers.go @@ -16,6 +16,14 @@ limitations under the License. package providers +import ( + "crypto/ed25519" + "crypto/rand" + "encoding/pem" + + "golang.org/x/crypto/ssh" +) + func (f Flavor) GPUCount() int { if f.GPU != nil { return f.GPU.Count @@ -23,3 +31,26 @@ func (f Flavor) GPUCount() int { return 0 } + +// GenerateSSHKeyPair creates an ephemeral SSH keypair, returning the +// public and private keys in SSH fingerprint and PEM formats respectively. +func GenerateSSHKeyPair() ([]byte, []byte, error) { + pub, priv, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + return nil, nil, err + } + + block, err := ssh.MarshalPrivateKey(priv, "unikorn ephemeral ed25519 key") + if err != nil { + return nil, nil, err + } + + privateKey := pem.EncodeToMemory(block) + + publicKey, err := ssh.NewPublicKey(pub) + if err != nil { + return nil, nil, err + } + + return ssh.MarshalAuthorizedKey(publicKey), privateKey, nil +} diff --git a/pkg/providers/openstack/compute.go b/pkg/providers/openstack/compute.go index db88992..e508bb0 100644 --- a/pkg/providers/openstack/compute.go +++ b/pkg/providers/openstack/compute.go @@ -19,6 +19,7 @@ package openstack import ( "context" + "fmt" "slices" "time" @@ -69,6 +70,33 @@ func NewComputeClient(ctx context.Context, provider CredentialProvider, options return c, nil } +// CreateKeypair creates a new keypair. +// NOTE: while OpenStack can generate one for us, we have far more control doing it ourselves +// thus allowing us to impose stricter security, and it's more provider agnostic that way. +func (c *ComputeClient) CreateKeypair(ctx context.Context, name, publicKey string) error { + tracer := otel.GetTracerProvider().Tracer(constants.Application) + + _, span := tracer.Start(ctx, "POST /compute/v2/os-keypairs", trace.WithSpanKind(trace.SpanKindClient)) + defer span.End() + + opts := &keypairs.CreateOpts{ + Name: name, + Type: "ssh", + PublicKey: publicKey, + } + + return keypairs.Create(ctx, c.client, opts).Err +} + +func (c *ComputeClient) DeleteKeypair(ctx context.Context, name string) error { + tracer := otel.GetTracerProvider().Tracer(constants.Application) + + _, span := tracer.Start(ctx, fmt.Sprintf("DELETE /compute/v2/os-keypairs/%s", name), trace.WithSpanKind(trace.SpanKindClient)) + defer span.End() + + return keypairs.Delete(ctx, c.client, name, nil).Err +} + // KeyPairs returns a list of key pairs. func (c *ComputeClient) KeyPairs(ctx context.Context) ([]keypairs.KeyPair, error) { tracer := otel.GetTracerProvider().Tracer(constants.Application) diff --git a/pkg/providers/openstack/provider.go b/pkg/providers/openstack/provider.go index 4eee47c..62da9e3 100644 --- a/pkg/providers/openstack/provider.go +++ b/pkg/providers/openstack/provider.go @@ -572,7 +572,10 @@ func (p *Provider) createClientConfig(identity *unikornv1.OpenstackIdentity) err return nil } -func (p *Provider) createIdentityServerGroup(ctx context.Context, identity *unikornv1.OpenstackIdentity) error { +// keyPairName is a fixed name for our per-identity keypair. +const keyPairName = "unikorn-openstack-provider" + +func (p *Provider) createIdentityComputeResources(ctx context.Context, identity *unikornv1.OpenstackIdentity) error { if identity.Spec.ServerGroupID != nil { return nil } @@ -587,6 +590,7 @@ func (p *Provider) createIdentityServerGroup(ctx context.Context, identity *unik name := identityResourceName(identity) + // Create a server group, that can be used by clients for soft anti-affinity. result, err := computeService.CreateServerGroup(ctx, name) if err != nil { return err @@ -594,6 +598,23 @@ func (p *Provider) createIdentityServerGroup(ctx context.Context, identity *unik identity.Spec.ServerGroupID = &result.ID + // Create an SSH key pair that can be used to gain access to servers. + // This is primarily a debugging aid, and you need to opt in at the client service + // to actually inject it into anything. Besides, you have the uesrname and password + // available anyway, so you can do a server recovery and steal all the data that way. + publicKey, privateKey, err := providers.GenerateSSHKeyPair() + if err != nil { + return err + } + + if err := computeService.CreateKeypair(ctx, keyPairName, string(publicKey)); err != nil { + return err + } + + t := keyPairName + identity.Spec.SSHKeyName = &t + identity.Spec.SSHPrivateKey = privateKey + return nil } @@ -706,7 +727,7 @@ func (p *Provider) CreateIdentity(ctx context.Context, identity *unikornv1.Ident } // Add in any optional configuration. - if err := p.createIdentityServerGroup(ctx, openstackIdentity); err != nil { + if err := p.createIdentityComputeResources(ctx, openstackIdentity); err != nil { return err } @@ -741,15 +762,24 @@ func (p *Provider) DeleteIdentity(ctx context.Context, identity *unikornv1.Ident defer record() - if openstackIdentity.Spec.ServerGroupID != nil { - // Rescope to the user/project... - providerClient := NewPasswordProvider(p.region.Spec.Openstack.Endpoint, *openstackIdentity.Spec.UserID, *openstackIdentity.Spec.Password, *openstackIdentity.Spec.ProjectID) + // Rescope to the user/project... + providerClient := NewPasswordProvider(p.region.Spec.Openstack.Endpoint, *openstackIdentity.Spec.UserID, *openstackIdentity.Spec.Password, *openstackIdentity.Spec.ProjectID) - computeService, err := NewComputeClient(ctx, providerClient, p.region.Spec.Openstack.Compute) - if err != nil { + computeService, err := NewComputeClient(ctx, providerClient, p.region.Spec.Openstack.Compute) + if err != nil { + return err + } + + if openstackIdentity.Spec.SSHKeyName != nil { + if err := computeService.DeleteKeypair(ctx, keyPairName); err != nil { return err } + openstackIdentity.Spec.SSHKeyName = nil + openstackIdentity.Spec.SSHPrivateKey = nil + } + + if openstackIdentity.Spec.ServerGroupID != nil { if err := computeService.DeleteServerGroup(ctx, *openstackIdentity.Spec.ServerGroupID); err != nil { return err }