Add aTLS endpoint to KMS (#236)

* Move file watcher and validator to internal

* Add aTLS endpoint to KMS for Kubernetes external requests

* Update Go version in Dockerfiles

* Move most KMS packages to internal

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
Daniel Weiße 2022-06-29 16:13:01 +02:00 committed by GitHub
parent 042f668d20
commit f9a581f329
66 changed files with 550 additions and 355 deletions

View File

@ -30,5 +30,5 @@ jobs:
with:
name: kmsserver
projectVersion: '0.0.0'
dockerfile: Dockerfile.kms
dockerfile: kms/server/Dockerfile
githubToken: ${{ secrets.GITHUB_TOKEN }}

View File

@ -43,7 +43,7 @@ jobs:
"activation-service" )
echo "microServiceDockerfile=activation/Dockerfile" >> $GITHUB_ENV ;;
"kmsserver" )
echo "microServiceDockerfile=Dockerfile.kms" >> $GITHUB_ENV ;;
echo "microServiceDockerfile=kms/server/Dockerfile" >> $GITHUB_ENV ;;
"verification-service" )
echo "microServiceDockerfile=verify/Dockerfile" >> $GITHUB_ENV ;;
esac

View File

@ -5,7 +5,7 @@ RUN dnf -y update && \
dnf clean all
# Install Go
ARG GO_VER=1.18
ARG GO_VER=1.18.3
RUN wget https://go.dev/dl/go${GO_VER}.linux-amd64.tar.gz && \
tar -C /usr/local -xzf go${GO_VER}.linux-amd64.tar.gz && \
rm go${GO_VER}.linux-amd64.tar.gz

View File

@ -5,7 +5,7 @@ RUN dnf -y update && \
dnf clean all
# Install Go
ARG GO_VER=1.18
ARG GO_VER=1.18.3
RUN wget https://go.dev/dl/go${GO_VER}.linux-amd64.tar.gz && \
tar -C /usr/local -xzf go${GO_VER}.linux-amd64.tar.gz && \
rm go${GO_VER}.linux-amd64.tar.gz
@ -25,7 +25,7 @@ WORKDIR /constellation/activation
ARG PROJECT_VERSION=0.0.0
RUN CGO_ENABLED=0 go build -o activation-service -trimpath -buildvcs=false -ldflags "-s -w -buildid='' -X github.com/edgelesssys/constellation/internal/constants.VersionInfo=${PROJECT_VERSION}" ./cmd/
# We would like to use a scratch image here, but we require CA certificates to be installed for some operations.
# We would like to use a scratch image here, but we require CA certificates to be installed for aTLS operations on GCP.
FROM fedora@sha256:36af84ba69e21c9ef86a0424a090674c433b2b80c2462e57503886f1d823abe8 as release
COPY --from=build /constellation/activation/activation-service /activation
ENTRYPOINT [ "/activation" ]

View File

@ -9,13 +9,12 @@ import (
"github.com/edgelesssys/constellation/activation/kubeadm"
"github.com/edgelesssys/constellation/activation/kubernetesca"
"github.com/edgelesssys/constellation/activation/server"
"github.com/edgelesssys/constellation/activation/validator"
"github.com/edgelesssys/constellation/activation/watcher"
"github.com/edgelesssys/constellation/internal/atls"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/edgelesssys/constellation/internal/grpc/atlscredentials"
"github.com/edgelesssys/constellation/internal/logger"
"github.com/edgelesssys/constellation/internal/watcher"
"github.com/spf13/afero"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
@ -34,7 +33,7 @@ func main() {
handler := file.NewHandler(afero.NewOsFs())
validator, err := validator.New(log.Named("validator"), *provider, handler)
validator, err := watcher.NewValidator(log.Named("validator"), *provider, handler)
if err != nil {
flag.Usage()
log.With(zap.Error(err)).Fatalf("Failed to create validator")
@ -63,8 +62,8 @@ func main() {
defer watcher.Close()
go func() {
log.Infof("starting file watcher for measurements file %s", filepath.Join(constants.ActivationBasePath, constants.ActivationMeasurementsFilename))
if err := watcher.Watch(filepath.Join(constants.ActivationBasePath, constants.ActivationMeasurementsFilename)); err != nil {
log.Infof("starting file watcher for measurements file %s", filepath.Join(constants.ServiceBasePath, constants.MeasurementsFilename))
if err := watcher.Watch(filepath.Join(constants.ServiceBasePath, constants.MeasurementsFilename)); err != nil {
log.With(zap.Error(err)).Fatalf("Failed to watch measurements file")
}
}()

View File

@ -5,7 +5,7 @@ import (
"fmt"
"github.com/edgelesssys/constellation/internal/logger"
"github.com/edgelesssys/constellation/kms/server/kmsapi/kmsproto"
"github.com/edgelesssys/constellation/kms/kmsproto"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"

View File

@ -6,7 +6,7 @@ import (
"testing"
"github.com/edgelesssys/constellation/internal/logger"
"github.com/edgelesssys/constellation/kms/server/kmsapi/kmsproto"
"github.com/edgelesssys/constellation/kms/kmsproto"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc"
"google.golang.org/grpc/test/bufconn"

View File

@ -124,7 +124,7 @@ func (s *Server) activateNode(ctx context.Context, diskUUID, nodeName string) (n
log := s.log.With(zap.String("peerAddress", grpclog.PeerAddrFromContext(ctx)))
log.Infof("Loading IDs")
var id attestationtypes.ID
if err := s.file.ReadJSON(filepath.Join(constants.ActivationBasePath, constants.ActivationIDFilename), &id); err != nil {
if err := s.file.ReadJSON(filepath.Join(constants.ServiceBasePath, constants.IDFilename), &id); err != nil {
log.With(zap.Error(err)).Errorf("Unable to load IDs")
return nodeParameters{}, status.Errorf(codes.Internal, "unable to load IDs: %s", err)
}

View File

@ -130,7 +130,7 @@ func TestActivateNode(t *testing.T) {
file := file.NewHandler(afero.NewMemMapFs())
if len(tc.id) > 0 {
require.NoError(file.Write(filepath.Join(constants.ActivationBasePath, constants.ActivationIDFilename), tc.id, 0o644))
require.NoError(file.Write(filepath.Join(constants.ServiceBasePath, constants.IDFilename), tc.id, 0o644))
}
api := New(
logger.NewTest(t),
@ -217,7 +217,7 @@ func TestActivateWorkerNode(t *testing.T) {
require := require.New(t)
file := file.NewHandler(afero.NewMemMapFs())
require.NoError(file.Write(filepath.Join(constants.ActivationBasePath, constants.ActivationIDFilename), tc.id, 0o644))
require.NoError(file.Write(filepath.Join(constants.ServiceBasePath, constants.IDFilename), tc.id, 0o644))
api := New(
logger.NewTest(t),
@ -322,7 +322,7 @@ func TestActivateControlPlaneNode(t *testing.T) {
require := require.New(t)
file := file.NewHandler(afero.NewMemMapFs())
require.NoError(file.Write(filepath.Join(constants.ActivationBasePath, constants.ActivationIDFilename), tc.id, 0o644))
require.NoError(file.Write(filepath.Join(constants.ServiceBasePath, constants.IDFilename), tc.id, 0o644))
api := New(
logger.NewTest(t),

View File

@ -9,7 +9,7 @@ import (
"github.com/edgelesssys/constellation/coordinator/state"
"github.com/edgelesssys/constellation/internal/atls"
"github.com/edgelesssys/constellation/internal/grpc/atlscredentials"
kms "github.com/edgelesssys/constellation/kms/server/setup"
kms "github.com/edgelesssys/constellation/kms/setup"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"google.golang.org/grpc"
)

View File

@ -25,7 +25,7 @@ import (
"github.com/edgelesssys/constellation/internal/grpc/dialer"
"github.com/edgelesssys/constellation/internal/grpc/testdialer"
"github.com/edgelesssys/constellation/internal/oid"
kms "github.com/edgelesssys/constellation/kms/server/setup"
kms "github.com/edgelesssys/constellation/kms/setup"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@ -19,7 +19,7 @@ import (
"github.com/edgelesssys/constellation/internal/deploy/user"
"github.com/edgelesssys/constellation/internal/file"
"github.com/edgelesssys/constellation/kms/kms"
kmsSetup "github.com/edgelesssys/constellation/kms/server/setup"
kmsSetup "github.com/edgelesssys/constellation/kms/setup"
"go.uber.org/zap"
"google.golang.org/grpc"
)

View File

@ -19,7 +19,7 @@ import (
"github.com/edgelesssys/constellation/internal/grpc/dialer"
"github.com/edgelesssys/constellation/internal/grpc/testdialer"
"github.com/edgelesssys/constellation/internal/oid"
kms "github.com/edgelesssys/constellation/kms/server/setup"
kms "github.com/edgelesssys/constellation/kms/setup"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@ -22,7 +22,7 @@ import (
"github.com/edgelesssys/constellation/internal/grpc/atlscredentials"
"github.com/edgelesssys/constellation/internal/grpc/dialer"
"github.com/edgelesssys/constellation/internal/oid"
kms "github.com/edgelesssys/constellation/kms/server/setup"
kms "github.com/edgelesssys/constellation/kms/setup"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@ -16,7 +16,7 @@ import (
"github.com/edgelesssys/constellation/internal/grpc/dialer"
"github.com/edgelesssys/constellation/internal/grpc/testdialer"
"github.com/edgelesssys/constellation/internal/oid"
kms "github.com/edgelesssys/constellation/kms/server/setup"
kms "github.com/edgelesssys/constellation/kms/setup"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@ -138,7 +138,7 @@ func NewActivationDaemonset(csp, measurementsJSON, idJSON string) *activationDae
Image: activationImage,
Ports: []k8s.ContainerPort{
{
ContainerPort: 9090,
ContainerPort: constants.ActivationServicePort,
Name: "tcp",
},
},
@ -148,13 +148,12 @@ func NewActivationDaemonset(csp, measurementsJSON, idJSON string) *activationDae
Args: []string{
fmt.Sprintf("--cloud-provider=%s", csp),
fmt.Sprintf("--kms-endpoint=kms.kube-system:%d", constants.KMSPort),
"--v=5",
},
VolumeMounts: []k8s.VolumeMount{
{
Name: "config",
ReadOnly: true,
MountPath: constants.ActivationBasePath,
MountPath: constants.ServiceBasePath,
},
{
Name: "kubeadm",

View File

@ -14,7 +14,8 @@ import (
type kmsDeployment struct {
ServiceAccount k8s.ServiceAccount
Service k8s.Service
ServiceInternal k8s.Service
ServiceExternal k8s.Service
ClusterRole rbac.ClusterRole
ClusterRoleBinding rbac.ClusterRoleBinding
Deployment apps.Deployment
@ -23,7 +24,7 @@ type kmsDeployment struct {
}
// NewKMSDeployment creates a new *kmsDeployment to use as the key management system inside Constellation.
func NewKMSDeployment(masterSecret []byte) *kmsDeployment {
func NewKMSDeployment(csp string, masterSecret []byte) *kmsDeployment {
return &kmsDeployment{
ServiceAccount: k8s.ServiceAccount{
TypeMeta: meta.TypeMeta{
@ -35,7 +36,7 @@ func NewKMSDeployment(masterSecret []byte) *kmsDeployment {
Namespace: "kube-system",
},
},
Service: k8s.Service{
ServiceInternal: k8s.Service{
TypeMeta: meta.TypeMeta{
APIVersion: "v1",
Kind: "Service",
@ -59,6 +60,31 @@ func NewKMSDeployment(masterSecret []byte) *kmsDeployment {
},
},
},
ServiceExternal: k8s.Service{
TypeMeta: meta.TypeMeta{
APIVersion: "v1",
Kind: "Service",
},
ObjectMeta: meta.ObjectMeta{
Name: "kms-external",
Namespace: "kube-system",
},
Spec: k8s.ServiceSpec{
Type: k8s.ServiceTypeNodePort,
Ports: []k8s.ServicePort{
{
Name: "atls",
Protocol: k8s.ProtocolTCP,
Port: constants.KMSATLSPort,
TargetPort: intstr.FromInt(constants.KMSATLSPort),
NodePort: constants.KMSNodePort,
},
},
Selector: map[string]string{
"k8s-app": "kms",
},
},
},
ClusterRole: rbac.ClusterRole{
TypeMeta: meta.TypeMeta{
APIVersion: "rbac.authorization.k8s.io/v1",
@ -161,14 +187,35 @@ func NewKMSDeployment(masterSecret []byte) *kmsDeployment {
},
Volumes: []k8s.Volume{
{
Name: "mastersecret",
Name: "config",
VolumeSource: k8s.VolumeSource{
Secret: &k8s.SecretVolumeSource{
SecretName: constants.ConstellationMasterSecretStoreName,
Items: []k8s.KeyToPath{
Projected: &k8s.ProjectedVolumeSource{
Sources: []k8s.VolumeProjection{
{
Key: constants.ConstellationMasterSecretKey,
Path: "constellation-mastersecret.base64",
ConfigMap: &k8s.ConfigMapProjection{
LocalObjectReference: k8s.LocalObjectReference{
Name: "activation-config",
},
Items: []k8s.KeyToPath{
{
Key: constants.MeasurementsFilename,
Path: constants.MeasurementsFilename,
},
},
},
},
{
Secret: &k8s.SecretProjection{
LocalObjectReference: k8s.LocalObjectReference{
Name: constants.ConstellationMasterSecretStoreName,
},
Items: []k8s.KeyToPath{
{
Key: constants.ConstellationMasterSecretKey,
Path: constants.MasterSecretFilename,
},
},
},
},
},
},
@ -181,14 +228,15 @@ func NewKMSDeployment(masterSecret []byte) *kmsDeployment {
Name: "kms",
Image: kmsImage,
Args: []string{
fmt.Sprintf("--atls-port=%d", constants.KMSATLSPort),
fmt.Sprintf("--port=%d", constants.KMSPort),
"--v=5",
fmt.Sprintf("--cloud-provider=%s", csp),
},
VolumeMounts: []k8s.VolumeMount{
{
Name: "mastersecret",
Name: "config",
ReadOnly: true,
MountPath: "/constellation/",
MountPath: constants.ServiceBasePath,
},
},
},

View File

@ -12,7 +12,7 @@ func TestKMSMarshalUnmarshal(t *testing.T) {
assert := assert.New(t)
testMS := []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}
kmsDepl := NewKMSDeployment(testMS)
kmsDepl := NewKMSDeployment("test", testMS)
data, err := kmsDepl.Marshal()
require.NoError(err)

View File

@ -142,7 +142,7 @@ func (k *KubeWrapper) InitCluster(
return fmt.Errorf("setting up pod network: %w", err)
}
kms := resources.NewKMSDeployment(masterSecret)
kms := resources.NewKMSDeployment(k.cloudProvider, masterSecret)
if err = k.clusterUtil.SetupKMS(k.client, kms); err != nil {
return fmt.Errorf("setting up kms: %w", err)
}

View File

@ -23,7 +23,7 @@ import (
"github.com/edgelesssys/constellation/internal/grpc/dialer"
"github.com/edgelesssys/constellation/internal/grpc/testdialer"
"github.com/edgelesssys/constellation/internal/oid"
kms "github.com/edgelesssys/constellation/kms/server/setup"
kms "github.com/edgelesssys/constellation/kms/setup"
"github.com/edgelesssys/constellation/state/keyservice/keyproto"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"

View File

@ -9,7 +9,7 @@ import (
"github.com/edgelesssys/constellation/coordinator/state"
attestationtypes "github.com/edgelesssys/constellation/internal/attestation/types"
"github.com/edgelesssys/constellation/internal/deploy/ssh"
kms "github.com/edgelesssys/constellation/kms/server/setup"
kms "github.com/edgelesssys/constellation/kms/setup"
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
)

View File

@ -13,7 +13,7 @@ import (
"github.com/edgelesssys/constellation/internal/deploy/ssh"
"github.com/edgelesssys/constellation/internal/deploy/user"
"github.com/edgelesssys/constellation/internal/logger"
kms "github.com/edgelesssys/constellation/kms/server/setup"
kms "github.com/edgelesssys/constellation/kms/setup"
"go.uber.org/zap/zapcore"
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
)

View File

@ -11,7 +11,7 @@ import (
"github.com/edgelesssys/constellation/coordinator/peer"
"github.com/edgelesssys/constellation/coordinator/state"
"github.com/edgelesssys/constellation/coordinator/store"
kms "github.com/edgelesssys/constellation/kms/server/setup"
kms "github.com/edgelesssys/constellation/kms/setup"
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
)

View File

@ -28,12 +28,17 @@ const (
VerifyServicePortGRPC = 9090
VerifyServiceNodePortHTTP = 30080
VerifyServiceNodePortGRPC = 30081
KMSPort = 9000
CoordinatorPort = 9000
EnclaveSSHPort = 2222
SSHPort = 22
WireguardPort = 51820
NVMEOverTCPPort = 8009
// KMSPort is the port the KMS server listens on.
KMSPort = 9000
// KMSATLSPort is the port the KMS aTLS server listens on.
KMSATLSPort = 9001
// KMSNodePort is the aTLS port exposed as a NodePort.
KMSNodePort = 30091
CoordinatorPort = 9000
EnclaveSSHPort = 2222
SSHPort = 22
WireguardPort = 51820
NVMEOverTCPPort = 8009
// Default NodePort Range
// https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport
NodePortFrom = 30000
@ -53,10 +58,16 @@ const (
CoreOSAdminConfFilename = "/etc/kubernetes/admin.conf"
KubeadmCertificateDir = "/etc/kubernetes/pki"
// Filenames for the Activation service.
ActivationBasePath = "/var/config"
ActivationMeasurementsFilename = "measurements"
ActivationIDFilename = "id"
//
// Filenames for Constellation's micro services.
//
// ServiceBasePath is the base path for the mounted micro services files.
ServiceBasePath = "/var/config"
// MeasurementsFilename is the filename of CC measurements.
MeasurementsFilename = "measurements"
// IDFilename is the filename of Constellation's IDs.
IDFilename = "id"
//
// Cryptographic constants.

View File

@ -1,4 +1,4 @@
package validator
package watcher
import (
"encoding/asn1"
@ -25,8 +25,8 @@ type Updatable struct {
atls.Validator
}
// New initializes a new updatable validator.
func New(log *logger.Logger, csp string, fileHandler file.Handler) (*Updatable, error) {
// NewValidator initializes a new updatable validator.
func NewValidator(log *logger.Logger, csp string, fileHandler file.Handler) (*Updatable, error) {
var newValidator newValidatorFunc
switch cloudprovider.FromString(csp) {
case cloudprovider.Azure:
@ -71,7 +71,7 @@ func (u *Updatable) Update() error {
u.log.Infof("Updating expected measurements")
var measurements map[uint32][]byte
if err := u.fileHandler.ReadJSON(filepath.Join(constants.ActivationBasePath, constants.ActivationMeasurementsFilename), &measurements); err != nil {
if err := u.fileHandler.ReadJSON(filepath.Join(constants.ServiceBasePath, constants.MeasurementsFilename), &measurements); err != nil {
return err
}
u.log.Debugf("New measurements: %v", measurements)

View File

@ -1,4 +1,4 @@
package validator
package watcher
import (
"bytes"
@ -60,7 +60,7 @@ func TestNewUpdateableValidator(t *testing.T) {
handler := file.NewHandler(afero.NewMemMapFs())
if tc.writeFile {
require.NoError(handler.WriteJSON(
filepath.Join(constants.ActivationBasePath, constants.ActivationMeasurementsFilename),
filepath.Join(constants.ServiceBasePath, constants.MeasurementsFilename),
map[uint32][]byte{
11: {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
},
@ -68,7 +68,7 @@ func TestNewUpdateableValidator(t *testing.T) {
))
}
_, err := New(
_, err := NewValidator(
logger.NewTest(t),
tc.provider,
handler,
@ -104,7 +104,7 @@ func TestUpdate(t *testing.T) {
// write measurement config
require.NoError(handler.WriteJSON(
filepath.Join(constants.ActivationBasePath, constants.ActivationMeasurementsFilename),
filepath.Join(constants.ServiceBasePath, constants.MeasurementsFilename),
map[uint32][]byte{
11: {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
},
@ -155,7 +155,7 @@ func TestUpdateConcurrency(t *testing.T) {
},
}
require.NoError(handler.WriteJSON(
filepath.Join(constants.ActivationBasePath, constants.ActivationMeasurementsFilename),
filepath.Join(constants.ServiceBasePath, constants.MeasurementsFilename),
map[uint32][]byte{
11: {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
},

View File

@ -59,7 +59,7 @@ func (f *FileWatcher) Watch(file string) error {
// file changes may be indicated by either a WRITE, CHMOD, CREATE or RENAME event
if event.Op&(fsnotify.Write|fsnotify.Chmod|fsnotify.Create|fsnotify.Rename) != 0 {
if err := f.updater.Update(); err != nil {
log.With(zap.Error(err)).Errorf("Failed to update activation validator")
log.With(zap.Error(err)).Errorf("Update failed")
}
}

View File

@ -1,12 +1,14 @@
FROM ubuntu@sha256:7cc0576c7c0ec2384de5cbf245f41567e922aab1b075f3e8ad565f508032df17 as build
FROM fedora@sha256:36af84ba69e21c9ef86a0424a090674c433b2b80c2462e57503886f1d823abe8 as build
ENV DEBIAN_FRONTEND="noninteractive"
RUN apt-get update && apt-get install wget git -y
RUN dnf -y update && \
dnf install -y wget git
# Install Go
ARG GO_VER=1.18
RUN wget https://go.dev/dl/go${GO_VER}.linux-amd64.tar.gz
RUN tar -C /usr/local -xzf go${GO_VER}.linux-amd64.tar.gz && rm go${GO_VER}.linux-amd64.tar.gz
ARG GO_VER=1.18.3
RUN wget https://go.dev/dl/go${GO_VER}.linux-amd64.tar.gz && \
tar -C /usr/local -xzf go${GO_VER}.linux-amd64.tar.gz && \
rm go${GO_VER}.linux-amd64.tar.gz
ENV PATH ${PATH}:/usr/local/go/bin
# Download go dependencies
@ -25,6 +27,7 @@ WORKDIR /constellation/kms/server/cmd
ARG PROJECT_VERSION=0.0.0
RUN CGO_ENABLED=0 go build -o /constellation/build/kmsserver -trimpath -buildvcs=false -ldflags "-s -w -buildid='' -X github.com/edgelesssys/constellation/internal/constants.VersionInfo=${PROJECT_VERSION}"
FROM scratch as release
# We would like to use a scratch image here, but we require CA certificates to be installed for aTLS operations on GCP.
FROM fedora@sha256:36af84ba69e21c9ef86a0424a090674c433b2b80c2462e57503886f1d823abe8 as release
COPY --from=build /constellation/build/kmsserver /kmsserver
ENTRYPOINT ["/kmsserver"]

108
kms/cmd/main.go Normal file
View File

@ -0,0 +1,108 @@
package main
import (
"context"
"errors"
"flag"
"fmt"
"net"
"path/filepath"
"strconv"
"time"
"github.com/edgelesssys/constellation/internal/atls"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/edgelesssys/constellation/internal/grpc/atlscredentials"
"github.com/edgelesssys/constellation/internal/logger"
"github.com/edgelesssys/constellation/internal/watcher"
"github.com/edgelesssys/constellation/kms/internal/server"
"github.com/edgelesssys/constellation/kms/setup"
"github.com/spf13/afero"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func main() {
port := flag.String("port", strconv.Itoa(constants.KMSPort), "Port gRPC server listens on")
portATLS := flag.String("atls-port", strconv.Itoa(constants.KMSNodePort), "Port aTLS server listens on")
provider := flag.String("cloud-provider", "", "cloud service provider this binary is running on")
masterSecretPath := flag.String("master-secret", filepath.Join(constants.ServiceBasePath, constants.MasterSecretFilename), "Path to the Constellation master secret")
flag.Parse()
log := logger.New(logger.JSONLog, zapcore.InfoLevel)
log.With(zap.String("version", constants.VersionInfo), zap.String("cloudProvider", *provider)).
Infof("Constellation Key Management Service")
validator, err := watcher.NewValidator(log.Named("validator"), *provider, file.NewHandler(afero.NewOsFs()))
if err != nil {
flag.Usage()
log.With(zap.Error(err)).Fatalf("Failed to create validator")
}
creds := atlscredentials.New(nil, []atls.Validator{validator})
// set up Key Management Service
masterKey, err := readMainSecret(*masterSecretPath)
if err != nil {
log.With(zap.Error(err)).Fatalf("Failed to read master secret")
}
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
defer cancel()
conKMS, err := setup.SetUpKMS(ctx, setup.NoStoreURI, setup.ClusterKMSURI)
if err != nil {
log.With(zap.Error(err)).Fatalf("Failed to setup KMS")
}
if err := conKMS.CreateKEK(ctx, "Constellation", masterKey); err != nil {
log.With(zap.Error(err)).Fatalf("Failed to create KMS KEK from MasterKey")
}
// set up listeners
atlsListener, err := net.Listen("tcp", net.JoinHostPort("", *portATLS))
if err != nil {
log.With(zap.Error(err)).Fatalf("Failed to listen on port %s", *portATLS)
}
plainListener, err := net.Listen("tcp", net.JoinHostPort("", *port))
if err != nil {
log.With(zap.Error(err)).Fatalf("Failed to listen on port %s", *port)
}
// start the measurements file watcher
watcher, err := watcher.New(log.Named("fileWatcher"), validator)
if err != nil {
log.With(zap.Error(err)).Fatalf("Failed to create watcher for measurements updates")
}
defer watcher.Close()
go func() {
log.Infof("starting file watcher for measurements file %s", filepath.Join(constants.ServiceBasePath, constants.MeasurementsFilename))
if err := watcher.Watch(filepath.Join(constants.ServiceBasePath, constants.MeasurementsFilename)); err != nil {
log.With(zap.Error(err)).Fatalf("Failed to watch measurements file")
}
}()
// start the server
if err := server.New(log.Named("server"), conKMS).Run(atlsListener, plainListener, creds); err != nil {
log.With(zap.Error(err)).Fatalf("Failed to run KMS server")
}
}
// readMainSecret reads the base64 encoded main secret file from specified path and returns the secret as bytes.
func readMainSecret(fileName string) ([]byte, error) {
if fileName == "" {
return nil, errors.New("no filename to master secret provided")
}
fileHandler := file.NewHandler(afero.NewOsFs())
secretBytes, err := fileHandler.Read(fileName)
if err != nil {
return nil, err
}
if len(secretBytes) < constants.MasterSecretLengthMin {
return nil, fmt.Errorf("provided master secret is smaller than the required minimum of %d bytes", constants.MasterSecretLengthMin)
}
return secretBytes, nil
}

View File

@ -0,0 +1,108 @@
// Package server implements an API to manage encryption keys.
package server
import (
"context"
"net"
"sync"
"github.com/edgelesssys/constellation/internal/grpc/atlscredentials"
"github.com/edgelesssys/constellation/internal/grpc/grpclog"
"github.com/edgelesssys/constellation/internal/logger"
"github.com/edgelesssys/constellation/kms/kms"
"github.com/edgelesssys/constellation/kms/kmsproto"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// Server implements an encryption key management server.
// The server serves aTLS for cluster external requests
// and plain gRPC for cluster internal requests.
type Server struct {
log *logger.Logger
conKMS kms.CloudKMS
kmsproto.UnimplementedAPIServer
}
// New creates a new Server.
func New(log *logger.Logger, conKMS kms.CloudKMS) *Server {
return &Server{
log: log,
conKMS: conKMS,
}
}
// Run starts both the plain gRPC server and the aTLS gRPC server.
// If one of the servers fails, the other server will be closed and the error will be returned.
func (s *Server) Run(atlsListener, plainListener net.Listener, credentials *atlscredentials.Credentials) error {
var err error
var once sync.Once
var wg sync.WaitGroup
atlsServer := grpc.NewServer(
grpc.Creds(credentials),
s.log.Named("gRPC.aTLS").GetServerUnaryInterceptor(),
)
kmsproto.RegisterAPIServer(atlsServer, s)
plainServer := grpc.NewServer(s.log.Named("gRPC.cluster").GetServerUnaryInterceptor())
kmsproto.RegisterAPIServer(plainServer, s)
s.log.Named("gRPC").WithIncreasedLevel(zapcore.WarnLevel).ReplaceGRPCLogger()
// start the plain gRPC server
wg.Add(1)
go func() {
defer wg.Done()
defer atlsServer.GracefulStop()
s.log.Infof("Starting Constellation key management service on %s", plainListener.Addr().String())
plainErr := plainServer.Serve(plainListener)
if plainErr != nil {
once.Do(func() { err = plainErr })
}
}()
// start the aTLS server
wg.Add(1)
go func() {
defer wg.Done()
defer plainServer.GracefulStop()
s.log.Infof("Starting Constellation aTLS key management service on %s", atlsListener.Addr().String())
atlsErr := atlsServer.Serve(atlsListener)
if atlsErr != nil {
once.Do(func() { err = atlsErr })
}
}()
wg.Wait()
return err
}
// GetDataKey returns a data key.
func (s *Server) GetDataKey(ctx context.Context, in *kmsproto.GetDataKeyRequest) (*kmsproto.GetDataKeyResponse, error) {
log := s.log.With("peerAddress", grpclog.PeerAddrFromContext(ctx))
// Error on 0 key length
if in.Length == 0 {
log.Errorf("Requested key length is zero")
return nil, status.Error(codes.InvalidArgument, "can't derive key with length zero")
}
// Error on empty DataKeyId
if in.DataKeyId == "" {
log.Errorf("No data key ID specified")
return nil, status.Error(codes.InvalidArgument, "no data key ID specified")
}
key, err := s.conKMS.GetDEK(ctx, "Constellation", "key-"+in.DataKeyId, int(in.Length))
if err != nil {
log.With(zap.Error(err)).Errorf("Failed to get data key")
return nil, status.Errorf(codes.Internal, "%v", err)
}
return &kmsproto.GetDataKeyResponse{DataKey: key}, nil
}

View File

@ -0,0 +1,117 @@
package server
import (
"context"
"errors"
"net"
"sync"
"testing"
"github.com/edgelesssys/constellation/internal/grpc/atlscredentials"
"github.com/edgelesssys/constellation/internal/grpc/testdialer"
"github.com/edgelesssys/constellation/internal/logger"
"github.com/edgelesssys/constellation/kms/kmsproto"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/goleak"
)
func TestMain(m *testing.M) {
goleak.VerifyTestMain(m)
}
func TestRun(t *testing.T) {
assert := assert.New(t)
closeErr := errors.New("closed")
var err error
var wg sync.WaitGroup
server := New(logger.NewTest(t), &stubKMS{})
creds := atlscredentials.New(nil, nil)
atlsListener, plainListener := setUpTestListeners()
wg.Add(1)
go func() {
defer wg.Done()
err = server.Run(atlsListener, plainListener, creds)
}()
assert.NoError(plainListener.Close())
wg.Wait()
assert.Equal(closeErr, err)
atlsListener, plainListener = setUpTestListeners()
wg.Add(1)
go func() {
defer wg.Done()
err = server.Run(atlsListener, plainListener, creds)
}()
assert.NoError(atlsListener.Close())
wg.Wait()
assert.Equal(closeErr, err)
atlsListener, plainListener = setUpTestListeners()
wg.Add(1)
go func() {
defer wg.Done()
err = server.Run(atlsListener, plainListener, creds)
}()
go assert.NoError(atlsListener.Close())
go assert.NoError(plainListener.Close())
wg.Wait()
assert.Equal(closeErr, err)
}
func TestGetDataKey(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
log := logger.NewTest(t)
kms := &stubKMS{derivedKey: []byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5}}
api := New(log, kms)
res, err := api.GetDataKey(context.Background(), &kmsproto.GetDataKeyRequest{DataKeyId: "1", Length: 32})
require.NoError(err)
assert.Equal(kms.derivedKey, res.DataKey)
// Test no data key id
res, err = api.GetDataKey(context.Background(), &kmsproto.GetDataKeyRequest{Length: 32})
require.Error(err)
assert.Nil(res)
// Test no / zero key length
res, err = api.GetDataKey(context.Background(), &kmsproto.GetDataKeyRequest{DataKeyId: "1"})
require.Error(err)
assert.Nil(res)
// Test derive key error
api = New(log, &stubKMS{deriveKeyErr: errors.New("error")})
res, err = api.GetDataKey(context.Background(), &kmsproto.GetDataKeyRequest{DataKeyId: "1", Length: 32})
assert.Error(err)
assert.Nil(res)
}
func setUpTestListeners() (net.Listener, net.Listener) {
atlsListener := testdialer.NewBufconnDialer().GetListener(net.JoinHostPort("192.0.2.1", "9001"))
plainListener := testdialer.NewBufconnDialer().GetListener(net.JoinHostPort("192.0.2.1", "9000"))
return atlsListener, plainListener
}
type stubKMS struct {
masterKey []byte
derivedKey []byte
deriveKeyErr error
}
func (c *stubKMS) CreateKEK(ctx context.Context, keyID string, kek []byte) error {
c.masterKey = kek
return nil
}
func (c *stubKMS) GetDEK(ctx context.Context, kekID string, dekID string, dekSize int) ([]byte, error) {
if c.deriveKeyErr != nil {
return nil, c.deriveKeyErr
}
return c.derivedKey, nil
}

View File

@ -10,7 +10,7 @@ import (
awsconfig "github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/aws/aws-sdk-go-v2/service/s3/types"
"github.com/edgelesssys/constellation/kms/config"
"github.com/edgelesssys/constellation/kms/internal/config"
)
type awsS3ClientAPI interface {

View File

@ -9,7 +9,7 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
"github.com/aws/aws-sdk-go-v2/feature/s3/manager"
"github.com/edgelesssys/constellation/kms/config"
"github.com/edgelesssys/constellation/kms/internal/config"
)
type azureContainerAPI interface {

View File

@ -1,6 +1,6 @@
//go:build integration
package integration
package storage
import (
"context"
@ -12,7 +12,6 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
"github.com/edgelesssys/constellation/kms/storage"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/api/option"
@ -43,7 +42,7 @@ func TestGoogleCloudStorage(t *testing.T) {
t.Log("Running test...")
ctx, cancel := context.WithTimeout(context.Background(), time.Second*50)
defer cancel()
store, err := storage.NewGoogleCloudStorage(ctx, projectName, bucketName, nil, option.WithoutAuthentication())
store, err := NewGoogleCloudStorage(ctx, projectName, bucketName, nil, option.WithoutAuthentication())
require.NoError(err)
testDEK1 := []byte("test DEK")
@ -67,7 +66,7 @@ func TestGoogleCloudStorage(t *testing.T) {
_, err = store.Get(ctx, "invalid:key")
assert.Error(err)
assert.ErrorIs(err, storage.ErrDEKUnset)
assert.ErrorIs(err, ErrDEKUnset)
}
func setupEmulator(ctx context.Context, cli *client.Client, imageName string) (container.ContainerCreateCreatedBody, error) {

View File

@ -14,9 +14,9 @@ import (
"github.com/aws/aws-sdk-go-v2/service/kms"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/aws/aws-sdk-go-v2/service/s3/types"
kmsconfig "github.com/edgelesssys/constellation/kms/config"
kmsconfig "github.com/edgelesssys/constellation/kms/internal/config"
"github.com/edgelesssys/constellation/kms/internal/storage"
awsInterface "github.com/edgelesssys/constellation/kms/kms/aws"
"github.com/edgelesssys/constellation/kms/storage"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

View File

@ -8,9 +8,9 @@ import (
"testing"
"time"
"github.com/edgelesssys/constellation/kms/config"
"github.com/edgelesssys/constellation/kms/internal/config"
"github.com/edgelesssys/constellation/kms/internal/storage"
"github.com/edgelesssys/constellation/kms/kms/azure"
"github.com/edgelesssys/constellation/kms/storage"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

View File

@ -8,9 +8,9 @@ import (
"testing"
"time"
"github.com/edgelesssys/constellation/kms/config"
"github.com/edgelesssys/constellation/kms/internal/config"
"github.com/edgelesssys/constellation/kms/internal/storage"
"github.com/edgelesssys/constellation/kms/kms/gcp"
"github.com/edgelesssys/constellation/kms/storage"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1"

View File

@ -11,10 +11,10 @@ import (
awsconfig "github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/kms"
"github.com/aws/aws-sdk-go-v2/service/kms/types"
"github.com/edgelesssys/constellation/kms/config"
"github.com/edgelesssys/constellation/kms/internal/config"
"github.com/edgelesssys/constellation/kms/internal/storage"
kmsInterface "github.com/edgelesssys/constellation/kms/kms"
"github.com/edgelesssys/constellation/kms/kms/util"
"github.com/edgelesssys/constellation/kms/storage"
)
const (

View File

@ -16,9 +16,9 @@ import (
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/kms"
"github.com/aws/aws-sdk-go-v2/service/kms/types"
"github.com/edgelesssys/constellation/kms/config"
"github.com/edgelesssys/constellation/kms/internal/config"
"github.com/edgelesssys/constellation/kms/internal/storage"
kmsInterface "github.com/edgelesssys/constellation/kms/kms"
"github.com/edgelesssys/constellation/kms/storage"
"github.com/stretchr/testify/assert"
)

View File

@ -10,10 +10,10 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets"
"github.com/edgelesssys/constellation/kms/config"
"github.com/edgelesssys/constellation/kms/internal/config"
"github.com/edgelesssys/constellation/kms/internal/storage"
"github.com/edgelesssys/constellation/kms/kms"
"github.com/edgelesssys/constellation/kms/kms/util"
"github.com/edgelesssys/constellation/kms/storage"
)
const (

View File

@ -8,8 +8,8 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets"
"github.com/edgelesssys/constellation/kms/internal/storage"
"github.com/edgelesssys/constellation/kms/kms"
"github.com/edgelesssys/constellation/kms/storage"
"github.com/stretchr/testify/assert"
)

View File

@ -12,10 +12,10 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys"
"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys/crypto"
"github.com/edgelesssys/constellation/kms/config"
"github.com/edgelesssys/constellation/kms/internal/config"
"github.com/edgelesssys/constellation/kms/internal/storage"
"github.com/edgelesssys/constellation/kms/kms"
"github.com/edgelesssys/constellation/kms/kms/util"
"github.com/edgelesssys/constellation/kms/storage"
)
type hsmClientAPI interface {

View File

@ -10,8 +10,8 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys"
"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys/crypto"
"github.com/Azure/go-autorest/autorest/to"
"github.com/edgelesssys/constellation/kms/internal/storage"
"github.com/edgelesssys/constellation/kms/kms"
"github.com/edgelesssys/constellation/kms/storage"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

View File

@ -12,10 +12,10 @@ import (
"time"
kms "cloud.google.com/go/kms/apiv1"
"github.com/edgelesssys/constellation/kms/config"
"github.com/edgelesssys/constellation/kms/internal/config"
"github.com/edgelesssys/constellation/kms/internal/storage"
kmsInterface "github.com/edgelesssys/constellation/kms/kms"
"github.com/edgelesssys/constellation/kms/kms/util"
"github.com/edgelesssys/constellation/kms/storage"
"github.com/googleapis/gax-go/v2"
"google.golang.org/api/option"
kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1"

View File

@ -5,9 +5,9 @@ import (
"errors"
"testing"
"github.com/edgelesssys/constellation/kms/internal/storage"
kmsInterface "github.com/edgelesssys/constellation/kms/kms"
"github.com/edgelesssys/constellation/kms/kms/util"
"github.com/edgelesssys/constellation/kms/storage"
"github.com/googleapis/gax-go/v2"
"github.com/stretchr/testify/assert"
"google.golang.org/api/option"

View File

@ -2,7 +2,7 @@
// versions:
// protoc-gen-go v1.28.0
// protoc v3.20.1
// source: kmsapi.proto
// source: kms.proto
package kmsproto
@ -32,7 +32,7 @@ type GetDataKeyRequest struct {
func (x *GetDataKeyRequest) Reset() {
*x = GetDataKeyRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_kmsapi_proto_msgTypes[0]
mi := &file_kms_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -45,7 +45,7 @@ func (x *GetDataKeyRequest) String() string {
func (*GetDataKeyRequest) ProtoMessage() {}
func (x *GetDataKeyRequest) ProtoReflect() protoreflect.Message {
mi := &file_kmsapi_proto_msgTypes[0]
mi := &file_kms_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -58,7 +58,7 @@ func (x *GetDataKeyRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetDataKeyRequest.ProtoReflect.Descriptor instead.
func (*GetDataKeyRequest) Descriptor() ([]byte, []int) {
return file_kmsapi_proto_rawDescGZIP(), []int{0}
return file_kms_proto_rawDescGZIP(), []int{0}
}
func (x *GetDataKeyRequest) GetDataKeyId() string {
@ -86,7 +86,7 @@ type GetDataKeyResponse struct {
func (x *GetDataKeyResponse) Reset() {
*x = GetDataKeyResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_kmsapi_proto_msgTypes[1]
mi := &file_kms_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -99,7 +99,7 @@ func (x *GetDataKeyResponse) String() string {
func (*GetDataKeyResponse) ProtoMessage() {}
func (x *GetDataKeyResponse) ProtoReflect() protoreflect.Message {
mi := &file_kmsapi_proto_msgTypes[1]
mi := &file_kms_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -112,7 +112,7 @@ func (x *GetDataKeyResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetDataKeyResponse.ProtoReflect.Descriptor instead.
func (*GetDataKeyResponse) Descriptor() ([]byte, []int) {
return file_kmsapi_proto_rawDescGZIP(), []int{1}
return file_kms_proto_rawDescGZIP(), []int{1}
}
func (x *GetDataKeyResponse) GetDataKey() []byte {
@ -122,50 +122,49 @@ func (x *GetDataKeyResponse) GetDataKey() []byte {
return nil
}
var File_kmsapi_proto protoreflect.FileDescriptor
var File_kms_proto protoreflect.FileDescriptor
var file_kmsapi_proto_rawDesc = []byte{
0x0a, 0x0c, 0x6b, 0x6d, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06,
0x6b, 0x6d, 0x73, 0x61, 0x70, 0x69, 0x22, 0x4b, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74,
0x61, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x64,
0x61, 0x74, 0x61, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x09, 0x64, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6c,
0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6c, 0x65, 0x6e,
0x67, 0x74, 0x68, 0x22, 0x2f, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65,
0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x64, 0x61, 0x74,
0x61, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x64, 0x61, 0x74,
0x61, 0x4b, 0x65, 0x79, 0x32, 0x4a, 0x0a, 0x03, 0x41, 0x50, 0x49, 0x12, 0x43, 0x0a, 0x0a, 0x47,
0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x2e, 0x6b, 0x6d, 0x73, 0x61,
0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6b, 0x6d, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65,
0x74, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x42, 0x41, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65,
0x64, 0x67, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x73, 0x79, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x74,
0x65, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6b, 0x6d, 0x73, 0x2f, 0x73, 0x65, 0x72,
0x76, 0x65, 0x72, 0x2f, 0x6b, 0x6d, 0x73, 0x61, 0x70, 0x69, 0x2f, 0x6b, 0x6d, 0x73, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
var file_kms_proto_rawDesc = []byte{
0x0a, 0x09, 0x6b, 0x6d, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x6b, 0x6d, 0x73,
0x22, 0x4b, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6b, 0x65,
0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x61, 0x74, 0x61,
0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x2f, 0x0a,
0x12, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6b, 0x65, 0x79, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x64, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, 0x32, 0x44,
0x0a, 0x03, 0x41, 0x50, 0x49, 0x12, 0x3d, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61,
0x4b, 0x65, 0x79, 0x12, 0x16, 0x2e, 0x6b, 0x6d, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74,
0x61, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6b, 0x6d,
0x73, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x65, 0x64, 0x67, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x73, 0x79, 0x73, 0x2f, 0x63,
0x6f, 0x6e, 0x73, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6b, 0x6d, 0x73,
0x2f, 0x6b, 0x6d, 0x73, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
}
var (
file_kmsapi_proto_rawDescOnce sync.Once
file_kmsapi_proto_rawDescData = file_kmsapi_proto_rawDesc
file_kms_proto_rawDescOnce sync.Once
file_kms_proto_rawDescData = file_kms_proto_rawDesc
)
func file_kmsapi_proto_rawDescGZIP() []byte {
file_kmsapi_proto_rawDescOnce.Do(func() {
file_kmsapi_proto_rawDescData = protoimpl.X.CompressGZIP(file_kmsapi_proto_rawDescData)
func file_kms_proto_rawDescGZIP() []byte {
file_kms_proto_rawDescOnce.Do(func() {
file_kms_proto_rawDescData = protoimpl.X.CompressGZIP(file_kms_proto_rawDescData)
})
return file_kmsapi_proto_rawDescData
return file_kms_proto_rawDescData
}
var file_kmsapi_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_kmsapi_proto_goTypes = []interface{}{
(*GetDataKeyRequest)(nil), // 0: kmsapi.GetDataKeyRequest
(*GetDataKeyResponse)(nil), // 1: kmsapi.GetDataKeyResponse
var file_kms_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_kms_proto_goTypes = []interface{}{
(*GetDataKeyRequest)(nil), // 0: kms.GetDataKeyRequest
(*GetDataKeyResponse)(nil), // 1: kms.GetDataKeyResponse
}
var file_kmsapi_proto_depIdxs = []int32{
0, // 0: kmsapi.API.GetDataKey:input_type -> kmsapi.GetDataKeyRequest
1, // 1: kmsapi.API.GetDataKey:output_type -> kmsapi.GetDataKeyResponse
var file_kms_proto_depIdxs = []int32{
0, // 0: kms.API.GetDataKey:input_type -> kms.GetDataKeyRequest
1, // 1: kms.API.GetDataKey:output_type -> kms.GetDataKeyResponse
1, // [1:2] is the sub-list for method output_type
0, // [0:1] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
@ -173,13 +172,13 @@ var file_kmsapi_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for field type_name
}
func init() { file_kmsapi_proto_init() }
func file_kmsapi_proto_init() {
if File_kmsapi_proto != nil {
func init() { file_kms_proto_init() }
func file_kms_proto_init() {
if File_kms_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_kmsapi_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
file_kms_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetDataKeyRequest); i {
case 0:
return &v.state
@ -191,7 +190,7 @@ func file_kmsapi_proto_init() {
return nil
}
}
file_kmsapi_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
file_kms_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetDataKeyResponse); i {
case 0:
return &v.state
@ -208,18 +207,18 @@ func file_kmsapi_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_kmsapi_proto_rawDesc,
RawDescriptor: file_kms_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_kmsapi_proto_goTypes,
DependencyIndexes: file_kmsapi_proto_depIdxs,
MessageInfos: file_kmsapi_proto_msgTypes,
GoTypes: file_kms_proto_goTypes,
DependencyIndexes: file_kms_proto_depIdxs,
MessageInfos: file_kms_proto_msgTypes,
}.Build()
File_kmsapi_proto = out.File
file_kmsapi_proto_rawDesc = nil
file_kmsapi_proto_goTypes = nil
file_kmsapi_proto_depIdxs = nil
File_kms_proto = out.File
file_kms_proto_rawDesc = nil
file_kms_proto_goTypes = nil
file_kms_proto_depIdxs = nil
}

View File

@ -1,8 +1,8 @@
syntax = "proto3";
package kmsapi;
package kms;
option go_package = "github.com/edgelesssys/constellation/kms/server/kmsapi/kmsproto";
option go_package = "github.com/edgelesssys/constellation/kms/kmsproto";
service API {
rpc GetDataKey(GetDataKeyRequest) returns (GetDataKeyResponse);

View File

@ -2,7 +2,7 @@
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.20.1
// source: kmsapi.proto
// source: kms.proto
package kmsproto
@ -35,7 +35,7 @@ func NewAPIClient(cc grpc.ClientConnInterface) APIClient {
func (c *aPIClient) GetDataKey(ctx context.Context, in *GetDataKeyRequest, opts ...grpc.CallOption) (*GetDataKeyResponse, error) {
out := new(GetDataKeyResponse)
err := c.cc.Invoke(ctx, "/kmsapi.API/GetDataKey", in, out, opts...)
err := c.cc.Invoke(ctx, "/kms.API/GetDataKey", in, out, opts...)
if err != nil {
return nil, err
}
@ -80,7 +80,7 @@ func _API_GetDataKey_Handler(srv interface{}, ctx context.Context, dec func(inte
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/kmsapi.API/GetDataKey",
FullMethod: "/kms.API/GetDataKey",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(APIServer).GetDataKey(ctx, req.(*GetDataKeyRequest))
@ -92,7 +92,7 @@ func _API_GetDataKey_Handler(srv interface{}, ctx context.Context, dec func(inte
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var API_ServiceDesc = grpc.ServiceDesc{
ServiceName: "kmsapi.API",
ServiceName: "kms.API",
HandlerType: (*APIServer)(nil),
Methods: []grpc.MethodDesc{
{
@ -101,5 +101,5 @@ var API_ServiceDesc = grpc.ServiceDesc{
},
},
Streams: []grpc.StreamDesc{},
Metadata: "kmsapi.proto",
Metadata: "kms.proto",
}

View File

@ -1,83 +0,0 @@
package main
import (
"context"
"errors"
"flag"
"fmt"
"net"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/edgelesssys/constellation/internal/logger"
"github.com/edgelesssys/constellation/kms/server/kmsapi"
"github.com/edgelesssys/constellation/kms/server/kmsapi/kmsproto"
"github.com/edgelesssys/constellation/kms/server/setup"
"github.com/spf13/afero"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"google.golang.org/grpc"
)
func main() {
port := flag.String("port", "9000", "Port gRPC server listens on")
masterSecretPath := flag.String("master-secret", "/constellation/constellation-mastersecret.base64", "Path to the Constellation master secret")
flag.Parse()
log := logger.New(logger.JSONLog, zapcore.InfoLevel)
log.With(zap.String("version", constants.VersionInfo)).Infof("Constellation Key Management Service")
masterKey, err := readMainSecret(*masterSecretPath)
if err != nil {
log.With(zap.Error(err)).Fatalf("Failed to read master secret")
}
conKMS, err := setup.SetUpKMS(context.Background(), setup.NoStoreURI, setup.ClusterKMSURI)
if err != nil {
log.With(zap.Error(err)).Fatalf("Failed to setup KMS")
}
if err := conKMS.CreateKEK(context.Background(), "Constellation", masterKey); err != nil {
log.With(zap.Error(err)).Fatalf("Failed to create KMS KEK from MasterKey")
}
lis, err := net.Listen("tcp", net.JoinHostPort("", *port))
if err != nil {
log.With(zap.Error(err)).Fatalf("Failed to listen")
}
srv := kmsapi.New(log.Named("server"), conKMS)
log.Named("gRPC").WithIncreasedLevel(zapcore.WarnLevel).ReplaceGRPCLogger()
// TODO: Launch server with aTLS to allow attestation for clients.
grpcServer := grpc.NewServer(log.Named("gRPC").GetServerUnaryInterceptor())
kmsproto.RegisterAPIServer(grpcServer, srv)
log.Infof("Starting key management service on %s", lis.Addr().String())
if err := grpcServer.Serve(lis); err != nil {
log.With(zap.Error(err)).Fatalf("Failed to serve")
}
}
// readMainSecret reads the base64 encoded main secret file from specified path and returns the secret as bytes.
func readMainSecret(fileName string) ([]byte, error) {
if fileName == "" {
return nil, errors.New("no filename to master secret provided")
}
fileHandler := file.NewHandler(afero.NewOsFs())
secretBytes, err := fileHandler.Read(fileName)
if err != nil {
return nil, err
}
if len(secretBytes) < constants.MasterSecretLengthMin {
return nil, fmt.Errorf("provided master secret is smaller than the required minimum of %d bytes", constants.MasterSecretLengthMin)
}
return secretBytes, nil
}

View File

@ -1,53 +0,0 @@
// Package kmsapi implements an API to manage encryption keys.
package kmsapi
import (
"context"
"github.com/edgelesssys/constellation/internal/grpc/grpclog"
"github.com/edgelesssys/constellation/internal/logger"
"github.com/edgelesssys/constellation/kms/kms"
"github.com/edgelesssys/constellation/kms/server/kmsapi/kmsproto"
"go.uber.org/zap"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// API resembles an encryption key management api server through logger, CloudKMS and proto-unimplemented server.
type API struct {
log *logger.Logger
conKMS kms.CloudKMS
kmsproto.UnimplementedAPIServer
}
// New creates a new API.
func New(log *logger.Logger, conKMS kms.CloudKMS) *API {
return &API{
log: log,
conKMS: conKMS,
}
}
// GetDataKey returns a data key.
func (a *API) GetDataKey(ctx context.Context, in *kmsproto.GetDataKeyRequest) (*kmsproto.GetDataKeyResponse, error) {
log := a.log.With("peerAddress", grpclog.PeerAddrFromContext(ctx))
// Error on 0 key length
if in.Length == 0 {
log.Errorf("Requested key length is zero")
return nil, status.Error(codes.InvalidArgument, "can't derive key with length zero")
}
// Error on empty DataKeyId
if in.DataKeyId == "" {
log.Errorf("No data key ID specified")
return nil, status.Error(codes.InvalidArgument, "no data key ID specified")
}
key, err := a.conKMS.GetDEK(ctx, "Constellation", "key-"+in.DataKeyId, int(in.Length))
if err != nil {
log.With(zap.Error(err)).Errorf("Failed to get data key")
return nil, status.Errorf(codes.Internal, "%v", err)
}
return &kmsproto.GetDataKeyResponse{DataKey: key}, nil
}

View File

@ -1,60 +0,0 @@
package kmsapi
import (
"context"
"errors"
"testing"
"github.com/edgelesssys/constellation/internal/logger"
"github.com/edgelesssys/constellation/kms/server/kmsapi/kmsproto"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGetDataKey(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
log := logger.NewTest(t)
kms := &stubKMS{derivedKey: []byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5}}
api := New(log, kms)
res, err := api.GetDataKey(context.Background(), &kmsproto.GetDataKeyRequest{DataKeyId: "1", Length: 32})
require.NoError(err)
assert.Equal(kms.derivedKey, res.DataKey)
// Test no data key id
res, err = api.GetDataKey(context.Background(), &kmsproto.GetDataKeyRequest{Length: 32})
require.Error(err)
assert.Nil(res)
// Test no / zero key length
res, err = api.GetDataKey(context.Background(), &kmsproto.GetDataKeyRequest{DataKeyId: "1"})
require.Error(err)
assert.Nil(res)
// Test derive key error
api = New(log, &stubKMS{deriveKeyErr: errors.New("error")})
res, err = api.GetDataKey(context.Background(), &kmsproto.GetDataKeyRequest{DataKeyId: "1", Length: 32})
assert.Error(err)
assert.Nil(res)
}
type stubKMS struct {
masterKey []byte
derivedKey []byte
deriveKeyErr error
}
func (c *stubKMS) CreateKEK(ctx context.Context, keyID string, kek []byte) error {
c.masterKey = kek
return nil
}
func (c *stubKMS) GetDEK(ctx context.Context, kekID string, dekID string, dekSize int) ([]byte, error) {
if c.deriveKeyErr != nil {
return nil, c.deriveKeyErr
}
return c.derivedKey, nil
}

View File

@ -6,12 +6,12 @@ import (
"net/url"
"strconv"
"github.com/edgelesssys/constellation/kms/internal/storage"
"github.com/edgelesssys/constellation/kms/kms"
"github.com/edgelesssys/constellation/kms/kms/aws"
"github.com/edgelesssys/constellation/kms/kms/azure"
"github.com/edgelesssys/constellation/kms/kms/cluster"
"github.com/edgelesssys/constellation/kms/kms/gcp"
"github.com/edgelesssys/constellation/kms/storage"
kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1"
)

View File

@ -46,7 +46,7 @@ RUN protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_o
## key management
WORKDIR /kms
COPY kms/server/kmsapi/kmsproto/*.proto /kms
COPY kms/kmsproto/*.proto /kms
RUN protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative *.proto
## activation
@ -64,6 +64,6 @@ COPY --from=build /pubapi/*.go coordinator/pubapi/pubproto/
COPY --from=build /vpnapi/*.go coordinator/vpnapi/vpnproto/
COPY --from=build /disk-mapper/*.go state/keyservice/keyproto/
COPY --from=build /service/*.go debugd/service/
COPY --from=build /kms/*.go kms/server/kmsapi/kmsproto/
COPY --from=build /kms/*.go kms/kmsproto/
COPY --from=build /activation/*.go activation/activationproto/
COPY --from=build /verify/*.go verify/verifyproto/

View File

@ -27,7 +27,7 @@ import (
"github.com/edgelesssys/constellation/internal/atls"
"github.com/edgelesssys/constellation/internal/grpc/atlscredentials"
"github.com/edgelesssys/constellation/internal/oid"
kms "github.com/edgelesssys/constellation/kms/server/setup"
kms "github.com/edgelesssys/constellation/kms/setup"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/goleak"