Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): kasr cached keys to deprecate local #318

Merged
merged 5 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,4 @@ jobs:
uses: bats-core/bats-action@2.0.0
- run: tests/encrypt-decrypt.bats
- run: tests/kas-grants.bats
- run: tests/kas-registry.bats
110 changes: 65 additions & 45 deletions cmd/kas-registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/opentdf/otdfctl/pkg/man"
"github.com/opentdf/platform/protocol/go/policy"
"github.com/spf13/cobra"
"google.golang.org/protobuf/encoding/protojson"
)

var policy_kasRegistryCmd *cobra.Command
Expand All @@ -21,23 +22,24 @@ func policy_getKeyAccessRegistry(cmd *cobra.Command, args []string) {

kas, err := h.GetKasRegistryEntry(id)
if err != nil {
errMsg := fmt.Sprintf("Failed to get KAS registry entry (%s)", id)
errMsg := fmt.Sprintf("Failed to get Registered KAS entry (%s)", id)
cli.ExitWithError(errMsg, err)
}

keyType := "Local"
key := kas.PublicKey.GetLocal()
keyType := "Cached"
key := &policy.PublicKey{}
key.PublicKey = &policy.PublicKey_Cached{Cached: kas.GetPublicKey().GetCached()}
if kas.PublicKey.GetRemote() != "" {
keyType = "Remote"
key = kas.PublicKey.GetRemote()
key.PublicKey = &policy.PublicKey_Remote{Remote: kas.GetPublicKey().GetRemote()}
}

t := cli.NewTabular(
[]string{"Id", kas.Id},
// TODO: render labels [https://github.com/opentdf/otdfctl/issues/73]
[]string{"URI", kas.Uri},
[]string{"PublicKey Type", keyType},
[]string{"PublicKey", key},
[]string{"PublicKey", kas.GetPublicKey().String()},
)
HandleSuccess(cmd, kas.Id, t, kas)
}
Expand All @@ -48,7 +50,7 @@ func policy_listKeyAccessRegistries(cmd *cobra.Command, args []string) {

list, err := h.ListKasRegistryEntries()
if err != nil {
cli.ExitWithError("Failed to list KAS registry entries", err)
cli.ExitWithError("Failed to list Registered KAS entries", err)
}

t := cli.NewTable(
Expand All @@ -59,18 +61,19 @@ func policy_listKeyAccessRegistries(cmd *cobra.Command, args []string) {
)
rows := []table.Row{}
for _, kas := range list {
keyType := "Local"
key := kas.PublicKey.GetLocal()
keyType := "Cached"
key := policy.PublicKey{}
key.PublicKey = &policy.PublicKey_Cached{Cached: kas.GetPublicKey().GetCached()}
if kas.PublicKey.GetRemote() != "" {
keyType = "Remote"
key = kas.PublicKey.GetRemote()
key.PublicKey = &policy.PublicKey_Remote{Remote: kas.GetPublicKey().GetRemote()}
}

rows = append(rows, table.NewRow(table.RowData{
"id": kas.Id,
"uri": kas.Uri,
"id": kas.GetId(),
"uri": kas.GetUri(),
"pk_loc": keyType,
"pk": key,
"pk": kas.GetPublicKey().String(),
}))
}
t = t.WithRows(rows)
Expand All @@ -83,23 +86,28 @@ func policy_createKeyAccessRegistry(cmd *cobra.Command, args []string) {

flagHelper := cli.NewFlagHelper(cmd)
uri := flagHelper.GetRequiredString("uri")
local := flagHelper.GetOptionalString("public-key-local")
cachedJSON := flagHelper.GetOptionalString("public-keys")
remote := flagHelper.GetOptionalString("public-key-remote")
metadataLabels := flagHelper.GetStringSlice("label", metadataLabels, cli.FlagHelperStringSliceOptions{Min: 0})

if local == "" && remote == "" {
e := fmt.Errorf("A public key is required. Please pass either a local or remote public key")
cli.ExitWithError("Issue with create flags 'public-key-local' and 'public-key-remote': ", e)
if cachedJSON == "" && remote == "" {
e := fmt.Errorf("a public key is required. Please pass either a cached or remote public key")
cli.ExitWithError("Issue with create flags 'public-keys' and 'public-key-remote'", e)
}

key := &policy.PublicKey{}
keyType := "Local"
if local != "" {
keyType := "Cached"
if cachedJSON != "" {
if remote != "" {
e := fmt.Errorf("Only one public key is allowed. Please pass either a local or remote public key but not both")
cli.ExitWithError("Issue with create flags 'public-key-local' and 'public-key-remote': ", e)
e := fmt.Errorf("only one public key is allowed. Please pass either a cached or remote public key but not both")
cli.ExitWithError("Issue with create flags 'public-keys' and 'public-key-remote'", e)
}
cached := new(policy.PublicKey)
err := protojson.Unmarshal([]byte(cachedJSON), cached)
if err != nil {
cli.ExitWithError("Failed to unmarshal cached public key JSON", err)
}
key.PublicKey = &policy.PublicKey_Local{Local: local}
key = cached
} else {
keyType = "Remote"
key.PublicKey = &policy.PublicKey_Remote{Remote: remote}
Expand All @@ -111,14 +119,14 @@ func policy_createKeyAccessRegistry(cmd *cobra.Command, args []string) {
getMetadataMutable(metadataLabels),
)
if err != nil {
cli.ExitWithError("Failed to create KAS registry entry", err)
cli.ExitWithError("Failed to create Registered KAS entry", err)
}

t := cli.NewTabular(
[]string{"Id", created.Id},
[]string{"URI", created.Uri},
[]string{"PublicKey Type", keyType},
[]string{"PublicKey", local},
[]string{"PublicKey", cachedJSON},
// TODO: render labels [https://github.com/opentdf/otdfctl/issues/73]
)

Expand All @@ -133,21 +141,25 @@ func policy_updateKeyAccessRegistry(cmd *cobra.Command, args []string) {

id := flagHelper.GetRequiredString("id")
uri := flagHelper.GetOptionalString("uri")
local := flagHelper.GetOptionalString("public-key-local")
cachedJSON := flagHelper.GetOptionalString("public-keys")
remote := flagHelper.GetOptionalString("public-key-remote")
labels := flagHelper.GetStringSlice("label", metadataLabels, cli.FlagHelperStringSliceOptions{Min: 0})

if local == "" && remote == "" && len(labels) == 0 && uri == "" {
cli.ExitWithError("No values were passed to update. Please pass at least one value to update (E.G. 'uri', 'public-key-local', 'public-key-remote', 'label')", nil)
if cachedJSON == "" && remote == "" && len(labels) == 0 && uri == "" {
cli.ExitWithError("No values were passed to update. Please pass at least one value to update (E.G. 'uri', 'public-keys', 'public-key-remote', 'label')", nil)
}

// TODO: should update of a type of key be a dangerous mutation or cause a need for confirmation in the CLI?
var pubKey *policy.PublicKey
if local != "" && remote != "" {
e := fmt.Errorf("Only one public key is allowed. Please pass either a local or remote public key but not both")
cli.ExitWithError("Issue with update flags 'public-key-local' and 'public-key-remote': ", e)
} else if local != "" {
pubKey = &policy.PublicKey{PublicKey: &policy.PublicKey_Local{Local: local}}
if cachedJSON != "" && remote != "" {
e := fmt.Errorf("only one public key is allowed. Please pass either a cached or remote public key but not both")
cli.ExitWithError("Issue with update flags 'public-keys' and 'public-key-remote': ", e)
} else if cachedJSON != "" {
cached := new(policy.PublicKey)
err := protojson.Unmarshal([]byte(cachedJSON), cached)
if err != nil {
cli.ExitWithError("Failed to unmarshal cached public key JSON", err)
}
pubKey = cached
} else if remote != "" {
pubKey = &policy.PublicKey{PublicKey: &policy.PublicKey_Remote{Remote: remote}}
}
Expand All @@ -160,11 +172,11 @@ func policy_updateKeyAccessRegistry(cmd *cobra.Command, args []string) {
getMetadataUpdateBehavior(),
)
if err != nil {
cli.ExitWithError(fmt.Sprintf("Failed to update KAS registry entry (%s)", id), err)
cli.ExitWithError(fmt.Sprintf("Failed to update Registered KAS entry (%s)", id), err)
}
t := cli.NewTabular(
[]string{"Id", id},
[]string{"URI", uri},
[]string{"URI", updated.GetUri()},
// TODO: render labels [https://github.com/opentdf/otdfctl/issues/73]
)
HandleSuccess(cmd, id, t, updated)
Expand All @@ -176,17 +188,20 @@ func policy_deleteKeyAccessRegistry(cmd *cobra.Command, args []string) {

flagHelper := cli.NewFlagHelper(cmd)
id := flagHelper.GetRequiredString("id")
force := flagHelper.GetOptionalBool("force")

kas, err := h.GetKasRegistryEntry(id)
if err != nil {
errMsg := fmt.Sprintf("Failed to get KAS registry entry (%s)", id)
errMsg := fmt.Sprintf("Failed to get Registered KAS entry (%s)", id)
cli.ExitWithError(errMsg, err)
}

cli.ConfirmAction(cli.ActionDelete, "KAS Registry Entry: ", id, false)
if !force {
cli.ConfirmAction(cli.ActionDelete, "Registered KAS", id, false)
}

if _, err := h.DeleteKasRegistryEntry(id); err != nil {
errMsg := fmt.Sprintf("Failed to delete KAS registry entry (%s)", id)
errMsg := fmt.Sprintf("Failed to delete Registered KAS entry (%s)", id)
cli.ExitWithError(errMsg, err)
}

Expand Down Expand Up @@ -224,10 +239,10 @@ func init() {
createDoc.GetDocFlag("uri").Description,
)
createDoc.Flags().StringP(
createDoc.GetDocFlag("public-key-local").Name,
createDoc.GetDocFlag("public-key-local").Shorthand,
createDoc.GetDocFlag("public-key-local").Default,
createDoc.GetDocFlag("public-key-local").Description,
createDoc.GetDocFlag("public-keys").Name,
createDoc.GetDocFlag("public-keys").Shorthand,
createDoc.GetDocFlag("public-keys").Default,
createDoc.GetDocFlag("public-keys").Description,
)
createDoc.Flags().StringP(
createDoc.GetDocFlag("public-key-remote").Name,
Expand All @@ -253,10 +268,10 @@ func init() {
updateDoc.GetDocFlag("uri").Description,
)
updateDoc.Flags().StringP(
updateDoc.GetDocFlag("public-key-local").Name,
updateDoc.GetDocFlag("public-key-local").Shorthand,
updateDoc.GetDocFlag("public-key-local").Default,
updateDoc.GetDocFlag("public-key-local").Description,
updateDoc.GetDocFlag("public-keys").Name,
updateDoc.GetDocFlag("public-keys").Shorthand,
updateDoc.GetDocFlag("public-keys").Default,
updateDoc.GetDocFlag("public-keys").Description,
)
updateDoc.Flags().StringP(
updateDoc.GetDocFlag("public-key-remote").Name,
Expand All @@ -275,6 +290,11 @@ func init() {
deleteDoc.GetDocFlag("id").Default,
deleteDoc.GetDocFlag("id").Description,
)
deleteDoc.Flags().Bool(
deleteDoc.GetDocFlag("force").Name,
false,
deleteDoc.GetDocFlag("force").Description,
)

doc := man.Docs.GetCommand("policy/kas-registry",
man.WithSubcommands(createDoc, getDoc, listDoc, updateDoc, deleteDoc),
Expand Down
2 changes: 1 addition & 1 deletion docs/man/policy/kas-registry/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ command:

The Key Access Server (KAS) registry is a record of KASs safeguarding access and maintaining public keys.
The registry contains critical information like each server's uri, its public key (which can be
either local or at a remote uri), and any metadata about the server. Key Access Servers grant keys
either cached or at a remote uri), and any metadata about the server. Key Access Servers grant keys
for specified Namespaces, Attributes, and their Values via Attribute Key Access Grants and Attribute Value Key
Access Grants.

Expand Down
50 changes: 30 additions & 20 deletions docs/man/policy/kas-registry/create.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ command:
shorthand: u
description: URI of the Key Access Server
required: true
- name: public-key-local
shorthand: p
description: Public key of the Key Access Server
- name: public-keys
shorthand: c
description: One or more public keys saved for the KAS
- name: public-key-remote
shorthand: r
description: URI of the public key of the Key Access Server
description: Remote URI where the public key can be retrieved for the KAS
- name: label
description: "Optional metadata 'labels' in the format: key=value"
shorthand: l
Expand All @@ -25,27 +25,37 @@ command:

For more information about registration of Key Access Servers, see the manual for `kas-registry`.

> Warning: storage of the public key as `remote` or `local` may soon be deprecated in
> favor of reaching out to the KAS directly for the public key.

Public keys can be stored as either `remote` or `local` under the following JSON structure.
Public keys can be stored as either `remote` or `cached` under the following JSON structure.

### Remote

```json
{ "remote": "https://mykas.com/public_key" }
The value passed to the `--public-key-remote` flag puts the hosted location where the public key
can be retrieved for the registered KAS under the `remote` key, such as `https://kas.io/public_key`

### Cached

```json5
{
"cached": {
// One or more known public keys for the KAS
"keys":[
{
// x509 ASN.1 content in PEM envelope, usually
"pem": "base64encodedCert",
// key identifier
"kid": "<your key id>",
// algorithm (either: 1 for rsa:2048, 2 for ec:secp256r1)
"alg": 1
}
]
}
}
```

The JSON value passed to the `--public-key-remote` flag puts the location where the public key
can be accessed for a the registered KAS under the `remote` key.
The JSON value passed to the `--public-keys` flag stores the set of public keys for the KAS.

### Local
The PEM base64 encoding should contain everything `-----BEGIN CERTIFICATE-----\nMIIB...5Q=\n-----END CERTIFICATE-----\n`.

```json
{ "local": "myBase64EncodedCert" }
```

The JSON value passed to the `--public-key-local` flag puts a base64-encoded key value under
the `local` key.
### Local

The base64 encoding should contain everything `-----BEGIN CERTIFICATE-----\nMIIB...5Q=\n-----END CERTIFICATE-----\n`.
Deprecated.
2 changes: 2 additions & 0 deletions docs/man/policy/kas-registry/delete.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ command:
shorthand: i
description: ID of the Key Access Server registration
required: true
- name: force
description: Force deletion without interactive confirmation (dangerous)
---
6 changes: 3 additions & 3 deletions docs/man/policy/kas-registry/update.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@ command:
- name: uri
shorthand: u
description: URI of the Key Access Server
- name: public-key-local
- name: public-keys
shorthand: p
description: Public key of the Key Access Server
description: One or more public keys saved for the KAS
- name: public-key-remote
shorthand: r
description: URI of the public key of the Key Access Server
- name: label
description: "Optional metadata 'labels' in the format: key=value"
shorthand: l
default: ""
default: ''
- name: force-replace-labels
description: Destructively replace entire set of existing metadata 'labels' with any provided to this command
default: false
Expand Down
Loading
Loading