-
Notifications
You must be signed in to change notification settings - Fork 912
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
api: add vapi/crypto package with support for native KMS
- Loading branch information
Showing
6 changed files
with
353 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/* | ||
Copyright (c) 2024-2024 VMware, Inc. All Rights Reserved. | ||
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 crypto provides access to CryptoManagerKmip methods used to manage cryptographic key providers. | ||
For creating and delete native providers, see package vapi/crypto. | ||
*/ | ||
package crypto |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
/* | ||
Copyright (c) 2024-2024 VMware, Inc. All Rights Reserved. | ||
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 crypto | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/http" | ||
|
||
"github.com/vmware/govmomi/vapi/crypto/internal" | ||
"github.com/vmware/govmomi/vapi/rest" | ||
) | ||
|
||
// Manager extends rest.Client, adding crypto related methods. | ||
// Currently providing create, delete and export only. | ||
// See crypto.ManagerKmip for getting provider details. | ||
type Manager struct { | ||
*rest.Client | ||
} | ||
|
||
// NewManager creates a new Manager instance with the given client. | ||
func NewManager(client *rest.Client) *Manager { | ||
return &Manager{ | ||
Client: client, | ||
} | ||
} | ||
|
||
type KmsProviderConstraints struct { | ||
TpmRequired bool `json:"tpm_required"` | ||
} | ||
|
||
type KmsProviderCreateSpec struct { | ||
Provider string `json:"provider"` | ||
Constraints KmsProviderConstraints `json:"constraints"` | ||
} | ||
|
||
type KmsProviderExportSpec struct { | ||
Provider string `json:"provider"` | ||
Password string `json:"password,omitempty"` | ||
} | ||
|
||
type KmsProviderDownloadToken struct { | ||
Token string `json:"token"` | ||
Expiry string `json:"expiry"` | ||
} | ||
|
||
type KmsProviderExportLocation struct { | ||
URL string `json:"url"` | ||
DownloadToken KmsProviderDownloadToken `json:"download_token"` | ||
} | ||
|
||
type KmsProviderExport struct { | ||
Type string `json:"type"` | ||
Location *KmsProviderExportLocation `json:"location,omitempty"` | ||
} | ||
|
||
func (c *Manager) KmsProviderCreate(ctx context.Context, spec KmsProviderCreateSpec) error { | ||
resource := c.Resource(internal.KmsProvidersPath) | ||
request := resource.Request(http.MethodPost, spec) | ||
return c.Do(ctx, request, nil) | ||
} | ||
|
||
func (c *Manager) KmsProviderDelete(ctx context.Context, provider string) error { | ||
resource := c.Resource(internal.KmsProvidersPath).WithSubpath(provider) | ||
request := resource.Request(http.MethodDelete) | ||
return c.Do(ctx, request, nil) | ||
} | ||
|
||
func (c *Manager) KmsProviderExport(ctx context.Context, spec KmsProviderExportSpec) (*KmsProviderExport, error) { | ||
resource := c.Resource(internal.KmsProvidersPath).WithParam("action", "export") | ||
request := resource.Request(http.MethodPost, spec) | ||
|
||
var res KmsProviderExport | ||
if err := c.Do(ctx, request, &res); err != nil { | ||
return nil, err | ||
} | ||
|
||
return &res, nil | ||
} | ||
|
||
func (c *Manager) KmsProviderExportRequest(ctx context.Context, export *KmsProviderExportLocation) (*http.Request, error) { | ||
req, err := http.NewRequest(http.MethodGet, export.URL, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", export.DownloadToken.Token)) | ||
|
||
return req, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/* | ||
Copyright (c) 2024-2024 VMware, Inc. All Rights Reserved. | ||
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 vapi/crypto provides access to the Crypto Manager REST APIs that are not available in the SOAP API. | ||
Currently for creating and deleting native providers only. | ||
See the top-level package crypto for getting provider details via crypto.ManagerKmip. | ||
See also: https://blogs.vmware.com/code/2023/07/30/automate-vsphere-native-key-providers/ | ||
*/ | ||
package crypto |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/* | ||
Copyright (c) 2024-2024 VMware, Inc. All Rights Reserved. | ||
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 internal | ||
|
||
const ( | ||
KmsProvidersPath = "/api/vcenter/crypto-manager/kms/providers" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
/* | ||
Copyright (c) 2024-2024 VMware, Inc. All Rights Reserved. | ||
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 simulator | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"net/url" | ||
"path" | ||
"time" | ||
|
||
"github.com/google/uuid" | ||
|
||
"github.com/vmware/govmomi/simulator" | ||
"github.com/vmware/govmomi/vapi/crypto" | ||
"github.com/vmware/govmomi/vapi/crypto/internal" | ||
vapi "github.com/vmware/govmomi/vapi/simulator" | ||
"github.com/vmware/govmomi/vim25/types" | ||
) | ||
|
||
const ( | ||
typeNativeProvider = string(types.KmipClusterInfoKmsManagementTypeNativeProvider) | ||
backupPath = "/cryptomanager/kms/" | ||
) | ||
|
||
func init() { | ||
simulator.RegisterEndpoint(func(s *simulator.Service, r *simulator.Registry) { | ||
New(r, s.Listen).Register(s, r) | ||
}) | ||
} | ||
|
||
// Handler implements the Cluster Modules API simulator | ||
type Handler struct { | ||
URL *url.URL | ||
Map *simulator.Registry | ||
} | ||
|
||
// New creates a Handler instance | ||
func New(r *simulator.Registry, u *url.URL) *Handler { | ||
return &Handler{ | ||
Map: r, | ||
URL: u, | ||
} | ||
} | ||
|
||
// Register Namespace Management API paths with the vapi simulator's http.ServeMux | ||
func (h *Handler) Register(s *simulator.Service, r *simulator.Registry) { | ||
if r.IsVPX() { | ||
s.HandleFunc(internal.KmsProvidersPath, h.providers) | ||
s.HandleFunc(internal.KmsProvidersPath+"/", h.providersID) | ||
s.HandleFunc(backupPath, h.backup) | ||
} | ||
} | ||
|
||
// We need to use the simulator objects directly when updating fields (e.g. HasBackup) | ||
// Skipping the trouble of locking for now, as existing use-cases would not race. | ||
func (h *Handler) find(id string) *types.KmipClusterInfo { | ||
m := h.Map.CryptoManager() | ||
for i := range m.KmipServers { | ||
p := &m.KmipServers[i] | ||
if p.ClusterId.Id == id { | ||
return p | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (h *Handler) backup(w http.ResponseWriter, r *http.Request) { | ||
id := path.Base(r.RequestURI) | ||
p := h.find(id) | ||
if p == nil { | ||
vapi.ApiErrorNotFound(w) | ||
return | ||
} | ||
|
||
// Content of the simulated backup does not matter for the use-case we're covering: | ||
// Export sets HasBackup=true, which sets CryptoManagerKmipClusterStatus.OverallStatus=green | ||
p.HasBackup = types.NewBool(true) | ||
|
||
name := fmt.Sprintf("%s%s.p12", id, time.Now().Format(time.RFC3339)) | ||
|
||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", name)) | ||
_ = json.NewEncoder(w).Encode(p) | ||
} | ||
|
||
func (h *Handler) providers(w http.ResponseWriter, r *http.Request) { | ||
switch r.Method { | ||
case http.MethodPost: | ||
switch r.URL.Query().Get("action") { | ||
case "": | ||
var spec crypto.KmsProviderCreateSpec | ||
|
||
if vapi.Decode(r, w, &spec) { | ||
if h.find(spec.Provider) != nil { | ||
vapi.ApiErrorAlreadyExists(w) | ||
return | ||
} | ||
} | ||
|
||
m := h.Map.CryptoManager() | ||
m.KmipServers = append(m.KmipServers, types.KmipClusterInfo{ | ||
ClusterId: types.KeyProviderId{Id: spec.Provider}, | ||
ManagementType: typeNativeProvider, | ||
TpmRequired: &spec.Constraints.TpmRequired, | ||
HasBackup: types.NewBool(false), | ||
}) | ||
case "export": | ||
var spec crypto.KmsProviderExportSpec | ||
var p *types.KmipClusterInfo | ||
|
||
if vapi.Decode(r, w, &spec) { | ||
if p = h.find(spec.Provider); p == nil { | ||
vapi.ApiErrorNotFound(w) | ||
return | ||
} | ||
if p.ManagementType != typeNativeProvider { | ||
vapi.ApiErrorUnsupported(w) | ||
return | ||
} | ||
|
||
u := url.URL{ | ||
Scheme: h.URL.Scheme, | ||
Host: h.URL.Host, | ||
Path: backupPath + spec.Provider, | ||
} | ||
|
||
res := crypto.KmsProviderExport{ | ||
Type: "LOCATION", | ||
Location: &crypto.KmsProviderExportLocation{ | ||
URL: u.String(), | ||
DownloadToken: crypto.KmsProviderDownloadToken{ | ||
Token: uuid.NewString(), | ||
Expiry: time.Now().Add(time.Minute).Format(time.RFC3339), | ||
}, | ||
}, | ||
} | ||
|
||
vapi.StatusOK(w, res) | ||
} | ||
} | ||
default: | ||
w.WriteHeader(http.StatusMethodNotAllowed) | ||
} | ||
} | ||
|
||
func (h *Handler) providersID(w http.ResponseWriter, r *http.Request) { | ||
switch r.Method { | ||
case http.MethodDelete: | ||
id := path.Base(r.RequestURI) | ||
p := h.find(id) | ||
if p == nil { | ||
vapi.ApiErrorNotFound(w) | ||
return | ||
} | ||
if p.ManagementType != typeNativeProvider { | ||
vapi.ApiErrorUnsupported(w) | ||
return | ||
} | ||
m := h.Map.CryptoManager() | ||
_ = m.UnregisterKmsCluster(simulator.SpoofContext(), &types.UnregisterKmsCluster{ | ||
This: m.Self, | ||
ClusterId: types.KeyProviderId{Id: id}, | ||
}) | ||
vapi.StatusOK(w) | ||
default: | ||
w.WriteHeader(http.StatusMethodNotAllowed) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters