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

scion-pki: enable kms support #4617

Merged
merged 2 commits into from
Sep 24, 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 doc/command/scion-pki/scion-pki.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ SEE ALSO
* :ref:`scion-pki certificate <scion-pki_certificate>` - Manage certificates for the SCION control plane PKI.
* :ref:`scion-pki completion <scion-pki_completion>` - Generate the autocompletion script for the specified shell
* :ref:`scion-pki key <scion-pki_key>` - Manage private and public keys
* :ref:`scion-pki kms <scion-pki_kms>` - Run the step-kms-plugin
* :ref:`scion-pki trc <scion-pki_trc>` - Manage TRCs for the SCION control plane PKI
* :ref:`scion-pki version <scion-pki_version>` - Show the scion-pki version information

2 changes: 2 additions & 0 deletions doc/command/scion-pki/scion-pki_certificate_create.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,14 @@ Options
--bundle Bundle the certificate with the issuer certificate as a certificate chain
--ca string The path to the issuer certificate
--ca-key string The path to the issuer private key used to sign the new certificate
--ca-kms string The uri to configure a Cloud KMS or an HSM used for signing the certificate.
--common-name string The common name that replaces the common name in the subject template
--csr Generate a certificate signign request instead of a certificate
--curve string The elliptic curve to use (P-256|P-384|P-521) (default "P-256")
--force Force overwritting existing files
-h, --help help for create
--key string The path to the existing private key to use instead of creating a new one
--kms string The uri to configure a Cloud KMS or an HSM.
--not-after time The NotAfter time of the certificate. Can either be a timestamp or an offset.

If the value is a timestamp, it is expected to either be an RFC 3339 formatted
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Options
::

-h, --help help for private
--kms string The uri to configure a Cloud KMS or an HSM.
--separator string The separator between file names (default "\n")

SEE ALSO
Expand Down
1 change: 1 addition & 0 deletions doc/command/scion-pki/scion-pki_certificate_sign.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ Options
--bundle Bundle the certificate with the issuer certificate as a certificate chain
--ca string The path to the issuer certificate
--ca-key string The path to the issuer private key used to sign the new certificate
--ca-kms string The uri to configure a Cloud KMS or an HSM used for signing the certificate.
-h, --help help for sign
--not-after time The NotAfter time of the certificate. Can either be a timestamp or an offset.

Expand Down
1 change: 1 addition & 0 deletions doc/command/scion-pki/scion-pki_key_match_certificate.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Options
::

-h, --help help for certificate
--kms string The uri to configure a Cloud KMS or an HSM.
--separator string The separator between file names (default "\n")

SEE ALSO
Expand Down
1 change: 1 addition & 0 deletions doc/command/scion-pki/scion-pki_key_public.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Options

--force Force overwritting existing public key
-h, --help help for public
--kms string The uri to configure a Cloud KMS or an HSM.
--out string Path to write public key

SEE ALSO
Expand Down
45 changes: 45 additions & 0 deletions doc/command/scion-pki/scion-pki_kms.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
:orphan:

.. _scion-pki_kms:

scion-pki kms
-------------

Run the step-kms-plugin

Synopsis
~~~~~~~~


This command leverages the step-kms-plugin to interact with cloud Key Management
Systems (KMS) and Hardware Security Modules (HSM).

The commands are passed directly to the step-kms-plugin. For more information on
the available commands and their usage, please refer to the step-kms-plugin
documentation at https://github.com/smallstep/step-kms-plugin. In order to enable
KMS support, the step-kms-plugin must be installed and available in the PATH.

Various commands of the scion-pki tool allow the use of KMS. In all cases, the
private key needs to already exist in the KMS. To instruct the scion-pki tool to
use the key in the KMS, the --kms flag must be set.

For more information about supported KMSs and uri pattern, please consult
https://smallstep.com/docs/step-ca/cryptographic-protection.


::

scion-pki kms [command] [flags]

Options
~~~~~~~

::

-h, --help help for kms

SEE ALSO
~~~~~~~~

* :ref:`scion-pki <scion-pki>` - SCION Control Plane PKI Management Tool

1 change: 1 addition & 0 deletions doc/command/scion-pki/scion-pki_trc_sign.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Options
::

-h, --help help for sign
--kms string The uri to configure a Cloud KMS or an HSM.
-o, --out string Output file path. If --out is set, --out-dir is ignored.
--out-dir string Output directory. If --out is set, --out-dir is ignored. (default ".")

Expand Down
12 changes: 12 additions & 0 deletions scion-pki/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
load("//tools/lint:go.bzl", "go_library")

go_library(
name = "go_default_library",
srcs = [
"flags.go",
"plugin.go",
],
importpath = "github.com/scionproto/scion/scion-pki",
visibility = ["//visibility:public"],
deps = ["@com_github_spf13_pflag//:go_default_library"],
)
1 change: 1 addition & 0 deletions scion-pki/certs/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ go_library(
"//private/svc:go_default_library",
"//private/tracing:go_default_library",
"//private/trust:go_default_library",
"//scion-pki:go_default_library",
"//scion-pki/encoding:go_default_library",
"//scion-pki/file:go_default_library",
"//scion-pki/key:go_default_library",
Expand Down
30 changes: 24 additions & 6 deletions scion-pki/certs/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
"github.com/scionproto/scion/pkg/scrypto/cppki"
"github.com/scionproto/scion/private/app/command"
"github.com/scionproto/scion/private/app/flag"
scionpki "github.com/scionproto/scion/scion-pki"
"github.com/scionproto/scion/scion-pki/file"
"github.com/scionproto/scion/scion-pki/key"
)
Expand Down Expand Up @@ -125,7 +126,9 @@ func newCreateCmd(pather command.Pather) *cobra.Command {
notAfter flag.Time
ca string
caKey string
caKms string
existingKey string
kms string
curve string
bundle bool
force bool
Expand Down Expand Up @@ -193,7 +196,7 @@ A valid example for a JSON formatted template::
Args: cobra.RangeArgs(2, 3),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 2 && flags.existingKey == "" {
return serrors.New("positional key file is required")
return serrors.New("the positional key file is required")
}
ct, err := parseCertType(flags.profile)
if err != nil {
Expand All @@ -203,6 +206,9 @@ A valid example for a JSON formatted template::
if err != nil {
return serrors.Wrap("creating subject", err)
}
if flags.existingKey == "" && flags.kms != "" {
return serrors.New("the kms flag is only allowed with an existing key")
}

// Only check that the flags are set appropriately here.
// Do the actual parsing after the usage help message is silenced.
Expand All @@ -220,10 +226,10 @@ A valid example for a JSON formatted template::

cmd.SilenceUsage = true

var privKey key.PrivateKey
var privKey crypto.Signer
var encodedKey []byte
if flags.existingKey != "" {
if privKey, err = key.LoadPrivateKey(flags.existingKey); err != nil {
if privKey, err = key.LoadPrivateKey(flags.kms, flags.existingKey); err != nil {
return serrors.Wrap("loading existing private key", err)
}
} else {
Expand All @@ -234,18 +240,23 @@ A valid example for a JSON formatted template::
return serrors.Wrap("encoding fresh private key", err)
}
}
if !key.IsX509Signer(privKey) {
return serrors.New("the private key cannot be used in X.509 certificates",
"type", fmt.Sprintf("%T", privKey),
)
}

var caCertRaw []byte
var caCert *x509.Certificate
var caKey key.PrivateKey
var caKey crypto.Signer
if loadCA {
if caCertRaw, err = os.ReadFile(flags.ca); err != nil {
return serrors.Wrap("read CA certificate", err)
}
if caCert, err = parseCertificate(caCertRaw); err != nil {
return serrors.Wrap("parsing CA certificate", err)
}
if caKey, err = key.LoadPrivateKey(flags.caKey); err != nil {
if caKey, err = key.LoadPrivateKey(flags.caKms, flags.caKey); err != nil {
return serrors.Wrap("loading CA private key", err)
}
}
Expand All @@ -272,6 +283,12 @@ A valid example for a JSON formatted template::
}
fmt.Printf("CSR successfully written to %q\n", csrFile)
} else {
if !key.IsX509Signer(caKey) {
return serrors.New("the CA key cannot be used to create X.509 certificates",
"type", fmt.Sprintf("%T", caKey),
)
}

cert, err := CreateCertificate(CertParams{
Type: ct,
Subject: subject,
Expand Down Expand Up @@ -359,7 +376,8 @@ offset from the current time.`,
cmd.Flags().BoolVar(&flags.force, "force", false,
"Force overwritting existing files",
)

scionpki.BindFlagKmsCA(cmd.Flags(), &flags.caKms)
scionpki.BindFlagKms(cmd.Flags(), &flags.kms)
return cmd
}

Expand Down
2 changes: 1 addition & 1 deletion scion-pki/certs/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@ func TestNewCreateCmdCSR(t *testing.T) {
Validate: func(t *testing.T, csr *x509.CertificateRequest) {
require.NoError(t, csr.CheckSignature())
require.Equal(t, "1-ff00:0:111 Certificate", csr.Subject.CommonName)
priv, err := key.LoadPrivateKey("testdata/create/private.key")
priv, err := key.LoadPrivateKey("", "testdata/create/private.key")
require.NoError(t, err)
require.Equal(t, priv.Public(), csr.PublicKey)
},
Expand Down
9 changes: 6 additions & 3 deletions scion-pki/certs/match.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/scionproto/scion/pkg/private/serrors"
"github.com/scionproto/scion/pkg/scrypto/cppki"
"github.com/scionproto/scion/private/app/command"
scionpki "github.com/scionproto/scion/scion-pki"
"github.com/scionproto/scion/scion-pki/key"
)

Expand All @@ -45,6 +46,7 @@ func newMatchCmd(pather command.Pather) *cobra.Command {
func newMatchPrivateKey(pather command.Pather) *cobra.Command {
var flags struct {
separator string
kms string
}
cmd := &cobra.Command{
Use: "private <certificate> <private-key> [<private-key> ...]",
Expand Down Expand Up @@ -74,7 +76,7 @@ The output contains all the private keys that are authenticated by the certifica

var keys []string
for _, file := range args[1:] {
key, err := loadPackedPublicFromPrivate(file)
key, err := loadPackedPublicFromPrivate(flags.kms, file)
if err != nil {
fmt.Fprintf(os.Stderr, "WARN: ignoring %q: %s\n", file, err)
continue
Expand All @@ -92,11 +94,12 @@ The output contains all the private keys that are authenticated by the certifica
},
}
cmd.Flags().StringVar(&flags.separator, "separator", "\n", "The separator between file names")
scionpki.BindFlagKms(cmd.Flags(), &flags.kms)
return cmd
}

func loadPackedPublicFromPrivate(file string) ([]byte, error) {
key, err := key.LoadPrivateKey(file)
func loadPackedPublicFromPrivate(kms, name string) ([]byte, error) {
key, err := key.LoadPrivateKey(kms, name)
if err != nil {
return nil, err
}
Expand Down
6 changes: 5 additions & 1 deletion scion-pki/certs/renew.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,11 @@ The template is expressed in JSON. A valid example::
span.SetTag("remote-options", remotes)

// Load private key.
privPrev, err := key.LoadPrivateKey(keyFile)
// XXX(roosd): The renewal process does currently not support KMS.
// This is a bit more involved, and requires some refactoring of the
// flags and the key loading/creation process. For now, KMS is also
// not a direct use-case for AS certificates.
privPrev, err := key.LoadPrivateKey("", keyFile)
if err != nil {
return serrors.Wrap("reading private key", err)
}
Expand Down
2 changes: 1 addition & 1 deletion scion-pki/certs/renew_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func TestExtractChain(t *testing.T) {
chain := xtest.LoadChain(t, "testdata/renew/ISD1-ASff00_0_111.pem")

caChain := xtest.LoadChain(t, "testdata/renew/ISD1-ASff00_0_110.pem")
key, err := key.LoadPrivateKey("testdata/renew/cp-as-110.key")
key, err := key.LoadPrivateKey("", "testdata/renew/cp-as-110.key")
require.NoError(t, err)
caSigner := trust.Signer{
PrivateKey: key,
Expand Down
10 changes: 9 additions & 1 deletion scion-pki/certs/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/scionproto/scion/pkg/scrypto/cppki"
"github.com/scionproto/scion/private/app/command"
"github.com/scionproto/scion/private/app/flag"
scionpki "github.com/scionproto/scion/scion-pki"
"github.com/scionproto/scion/scion-pki/key"
)

Expand All @@ -39,6 +40,7 @@ func newSignCmd(pather command.Pather) *cobra.Command {
notAfter flag.Time
ca string
caKey string
caKms string
bundle bool
}
flags.notBefore = flag.Time{
Expand Down Expand Up @@ -115,10 +117,15 @@ and not to \--not-before.
if err != nil {
return serrors.Wrap("parsing CA certificate", err)
}
caKey, err := key.LoadPrivateKey(flags.caKey)
caKey, err := key.LoadPrivateKey(flags.caKms, flags.caKey)
if err != nil {
return serrors.Wrap("loading CA private key", err)
}
if !key.IsX509Signer(caKey) {
return serrors.New("the CA key cannot be used to create X.509 certificates",
"type", fmt.Sprintf("%T", caKey),
)
}

subject := csr.Subject
subject.ExtraNames = csr.Subject.Names
Expand Down Expand Up @@ -190,6 +197,7 @@ offset from the current time.`,
cmd.Flags().BoolVar(&flags.bundle, "bundle", false,
"Bundle the certificate with the issuer certificate as a certificate chain",
)
scionpki.BindFlagKmsCA(cmd.Flags(), &flags.caKms)
cmd.MarkFlagRequired("ca")
cmd.MarkFlagRequired("ca-key")

Expand Down
2 changes: 2 additions & 0 deletions scion-pki/cmd/scion-pki/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ go_library(
name = "go_default_library",
srcs = [
"gendocs.go",
"kms.go",
"main.go",
"version.go",
],
Expand All @@ -20,6 +21,7 @@ go_library(
"//pkg/private/serrors:go_default_library",
"//private/app:go_default_library",
"//private/env:go_default_library",
"//scion-pki:go_default_library",
"//scion-pki/certs:go_default_library",
"//scion-pki/key:go_default_library",
"//scion-pki/testcrypto:go_default_library",
Expand Down
Loading
Loading