Skip to content

Commit

Permalink
Merge pull request #539 from azdagron/keymanager-refactor
Browse files Browse the repository at this point in the history
Server CA refactor to use KeyManager instead of ServerCA plugin
  • Loading branch information
azdagron committed Jul 20, 2018
2 parents 375954d + 27924b3 commit d7f02b3
Show file tree
Hide file tree
Showing 80 changed files with 4,442 additions and 3,204 deletions.
64 changes: 52 additions & 12 deletions cmd/spire-server/cli/run/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package run

import (
"context"
"crypto/x509/pkix"
"errors"
"flag"
"fmt"
Expand All @@ -11,6 +12,7 @@ import (
"os"
"path/filepath"
"strconv"
"time"

"github.com/hashicorp/hcl"
"github.com/spiffe/spire/pkg/common/catalog"
Expand All @@ -35,18 +37,28 @@ type serverConfig struct {
BindAddress string `hcl:"bind_address"`
BindPort int `hcl:"bind_port"`
BindHTTPPort int `hcl:"bind_http_port"`
DataDir string `hcl:"data_dir"`
TrustDomain string `hcl:"trust_domain"`
LogFile string `hcl:"log_file"`
LogLevel string `hcl:"log_level"`
BaseSVIDTtl int `hcl:"base_svid_ttl"`
ServerSVIDTtl int `hcl:"server_svid_ttl"`
ConfigPath string
Umask string `hcl:"umask"`
UpstreamBundle bool `hcl:"upstream_bundle"`
ProfilingEnabled bool `hcl:"profiling_enabled"`
ProfilingPort int `hcl:"profiling_port"`
ProfilingFreq int `hcl:"profiling_freq"`
ProfilingNames []string `hcl:"profiling_names"`
Umask string `hcl:"umask"`
UpstreamBundle bool `hcl:"upstream_bundle"`
ProfilingEnabled bool `hcl:"profiling_enabled"`
ProfilingPort int `hcl:"profiling_port"`
ProfilingFreq int `hcl:"profiling_freq"`
ProfilingNames []string `hcl:"profiling_names"`
SVIDTTL string `hcl:"svid_ttl"`
CATTL string `hcl:"ca_ttl"`
CASubject *caSubjectConfig `hcl:"ca_subject"`
}

type caSubjectConfig struct {
Country []string `hcl:"country"`
Organization []string `hcl:"organization"`
CommonName string `hcl:"common_name"`
}

// Run CLI struct
Expand Down Expand Up @@ -127,12 +139,7 @@ func parseFile(filePath string) (*runConfig, error) {
if err != nil {
return nil, err
}
hclTree, err := hcl.Parse(string(data))
if err != nil {
return nil, err
}

if err := hcl.DecodeObject(&c, hclTree); err != nil {
if err := hcl.Decode(&c, string(data)); err != nil {
return nil, err
}

Expand All @@ -149,6 +156,7 @@ func parseFlags(args []string) (*runConfig, error) {
flags.StringVar(&c.Server.TrustDomain, "trustDomain", "", "The trust domain that this server belongs to")
flags.StringVar(&c.Server.LogFile, "logFile", "", "File to write logs to")
flags.StringVar(&c.Server.LogLevel, "logLevel", "", "DEBUG, INFO, WARN or ERROR")
flags.StringVar(&c.Server.DataDir, "dataDir", "", "Directory to store runtime data to")
flags.StringVar(&c.Server.ConfigPath, "config", defaultConfigPath, "Path to a SPIRE config file")
flags.StringVar(&c.Server.Umask, "umask", "", "Umask value to use for new files")
flags.BoolVar(&c.Server.UpstreamBundle, "upstreamBundle", false, "Include upstream CA certificates in the bundle")
Expand Down Expand Up @@ -190,6 +198,10 @@ func mergeConfig(orig *server.Config, cmd *runConfig) error {
orig.BindHTTPAddress.Port = cmd.Server.BindHTTPPort
}

if cmd.Server.DataDir != "" {
orig.DataDir = cmd.Server.DataDir
}

if cmd.Server.TrustDomain != "" {
trustDomain := url.URL{
Scheme: "spiffe",
Expand Down Expand Up @@ -245,6 +257,30 @@ func mergeConfig(orig *server.Config, cmd *runConfig) error {
}
}

if cmd.Server.SVIDTTL != "" {
ttl, err := time.ParseDuration(cmd.Server.SVIDTTL)
if err != nil {
return fmt.Errorf("unable to parse default ttl %q: %v", cmd.Server.SVIDTTL, err)
}
orig.SVIDTTL = ttl
}

if cmd.Server.CATTL != "" {
ttl, err := time.ParseDuration(cmd.Server.CATTL)
if err != nil {
return fmt.Errorf("unable to parse default ttl %q: %v", cmd.Server.CATTL, err)
}
orig.CATTL = ttl
}

if subject := cmd.Server.CASubject; subject != nil {
orig.CASubject = pkix.Name{
Organization: subject.Organization,
Country: subject.Country,
CommonName: subject.CommonName,
}
}

return nil
}

Expand All @@ -261,6 +297,10 @@ func validateConfig(c *server.Config) error {
return errors.New("TrustDomain is required")
}

if c.DataDir == "" {
return errors.New("DataDir is required")
}

return nil
}

Expand Down
28 changes: 13 additions & 15 deletions conf/server/ha-postgres.conf
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,21 @@ server {
bind_port = "8081"
bind_http_port = "8080"
trust_domain = "example.org"
data_dir = "./.data"
plugin_dir = "./conf/server/plugin"
log_level = "DEBUG"
umask = ""
upstream_bundle = true
svid_ttl = "5m"
ca_ttl = "24h"
ca_subject = {
Country = ["US"],
Organization = ["SPIFFE"],
CommonName = "",
}
}

plugins {
ServerCA "memory" {
enabled = true
plugin_data {
trust_domain = "example.org",
key_size = 2048,
backdate_seconds = 1,
default_ttl = 300,
cert_subject = {
Country = ["US"],
Organization = ["SPIFFE"],
CommonName = "",
}
}
}

DataStore "sql" {
enabled = true
plugin_data {
Expand All @@ -45,6 +38,11 @@ plugins {
plugin_data {}
}

KeyManager "memory" {
enabled = true
plugin_data {}
}

UpstreamCA "disk" {
enabled = true
plugin_data {
Expand Down
27 changes: 12 additions & 15 deletions conf/server/server.conf
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,20 @@ server {
bind_port = "8081"
bind_http_port = "8080"
trust_domain = "example.org"
data_dir = "./.data"
plugin_dir = "./conf/server/plugin"
log_level = "DEBUG"
umask = ""
upstream_bundle = true
svid_ttl = "1h"
ca_subject = {
Country = ["US"],
Organization = ["SPIFFE"],
CommonName = "",
}
}

plugins {
ServerCA "memory" {
enabled = true
plugin_data {
trust_domain = "example.org",
key_size = 2048,
backdate_seconds = 1,
default_ttl = 3600,
cert_subject = {
Country = ["US"],
Organization = ["SPIFFE"],
CommonName = "",
}
}
}

DataStore "sql" {
enabled = true
plugin_data {
Expand All @@ -45,6 +37,11 @@ plugins {
plugin_data {}
}

KeyManager "memory" {
enabled = true
plugin_data = {}
}

UpstreamCA "disk" {
enabled = true
plugin_data {
Expand Down
21 changes: 0 additions & 21 deletions doc/plugin_server_ca_memory.md

This file was deleted.

10 changes: 10 additions & 0 deletions doc/plugin_server_keymanager_disk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Server plugin: KeyManager "disk"

The `disk` key manager maintains a set of private keys that are persisted to
disk.

The plugin accepts the following configuration options:

| Configuration | Description |
| -------------- | ------------------------------------- |
| keys_path | Path to the keys file on disk |
6 changes: 6 additions & 0 deletions doc/plugin_server_keymanager_memory.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Server plugin: KeyManager "memory"

The `memory` key manager creates and maintains a set of private keys held
only in memory.

It has no configuration.
42 changes: 42 additions & 0 deletions pkg/common/cryptoutil/keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package cryptoutil

import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"errors"
"fmt"

"github.com/spiffe/spire/proto/server/keymanager"
)

func RSAPublicKeyEqual(a, b *rsa.PublicKey) bool {
return a.E == b.E && a.N.Cmp(b.N) == 0
}

func ECDSAPublicKeyEqual(a, b *ecdsa.PublicKey) bool {
return a.Curve == b.Curve && a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0
}

func ECDSAKeyMatches(privateKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey) bool {
return ECDSAPublicKeyEqual(&privateKey.PublicKey, publicKey)
}

func GetPublicKey(ctx context.Context, km keymanager.KeyManager, keyId string) (crypto.PublicKey, error) {
resp, err := km.GetPublicKey(ctx, &keymanager.GetPublicKeyRequest{
KeyId: keyId,
})
if err != nil {
return nil, err
}
if resp.PublicKey == nil {
return nil, errors.New("response missing public key")
}
publicKey, err := x509.ParsePKIXPublicKey(resp.PublicKey.PkixData)
if err != nil {
return nil, fmt.Errorf("unable to parse public key pkix data: %v", err)
}
return publicKey, nil
}
72 changes: 72 additions & 0 deletions pkg/common/cryptoutil/signer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package cryptoutil

import (
"context"
"crypto"
"crypto/x509"
"errors"
"fmt"
"io"

"github.com/spiffe/spire/proto/server/keymanager"
)

type KeyManagerSigner struct {
km keymanager.KeyManager
keyId string
publicKey crypto.PublicKey
}

var _ crypto.Signer = (*KeyManagerSigner)(nil)

func NewKeyManagerSigner(km keymanager.KeyManager, keyId string, publicKey crypto.PublicKey) *KeyManagerSigner {
return &KeyManagerSigner{
km: km,
keyId: keyId,
publicKey: publicKey,
}
}

func (s *KeyManagerSigner) Public() crypto.PublicKey {
return s.publicKey
}

func (s *KeyManagerSigner) SignContext(ctx context.Context, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
resp, err := s.km.SignData(ctx, &keymanager.SignDataRequest{
KeyId: s.keyId,
Data: digest,
HashAlgorithm: keymanager.HashAlgorithm(opts.HashFunc()),
})
if err != nil {
return nil, err
}
if len(resp.Signature) == 0 {
return nil, fmt.Errorf("response missing signature data")
}
return resp.Signature, nil
}

func (s *KeyManagerSigner) Sign(_ io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
// rand is purposefully ignored since it can't be communicated between
// the plugin boundary. The crypto.Signer interface implies this is ok
// when it says "possibly using entropy from rand".
return s.SignContext(context.Background(), digest, opts)
}

func GenerateKeyAndSigner(ctx context.Context, km keymanager.KeyManager, keyId string, algorithm keymanager.KeyAlgorithm) (*KeyManagerSigner, error) {
resp, err := km.GenerateKey(ctx, &keymanager.GenerateKeyRequest{
KeyId: keyId,
KeyAlgorithm: algorithm,
})
if err != nil {
return nil, err
}
if resp.PublicKey == nil {
return nil, errors.New("response missing public key")
}
publicKey, err := x509.ParsePKIXPublicKey(resp.PublicKey.PkixData)
if err != nil {
return nil, fmt.Errorf("unable to parse public key pkix data: %v", err)
}
return NewKeyManagerSigner(km, keyId, publicKey), nil
}
18 changes: 18 additions & 0 deletions pkg/common/diskutil/file.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package diskutil

import (
"io/ioutil"
"os"
)

func AtomicWriteFile(path string, data []byte, mode os.FileMode) error {
if err := ioutil.WriteFile(path+".tmp", data, mode); err != nil {
return err
}

if err := os.Rename(path+".tmp", path); err != nil {
return err
}

return nil
}

0 comments on commit d7f02b3

Please sign in to comment.