Skip to content

Commit

Permalink
Update ejbca to use the new Validate / pluginconf setup.
Browse files Browse the repository at this point in the history
Complete with updated unit tests.

Signed-off-by: Edwin Buck <edwbuck@gmail.com>
  • Loading branch information
edwbuck committed Sep 9, 2024
1 parent f7d5b30 commit d1bc067
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 54 deletions.
69 changes: 65 additions & 4 deletions pkg/server/plugin/upstreamauthority/ejbca/ejbca.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ import (

ejbcaclient "github.com/Keyfactor/ejbca-go-client-sdk/api/ejbca"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/hcl"
"github.com/spiffe/spire-plugin-sdk/pluginsdk"
upstreamauthorityv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/plugin/server/upstreamauthority/v1"
configv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/service/common/config/v1"
"github.com/spiffe/spire/pkg/common/catalog"
"github.com/spiffe/spire/pkg/common/coretypes/x509certificate"
"github.com/spiffe/spire/pkg/common/pluginconf"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
Expand Down Expand Up @@ -84,6 +86,56 @@ type Config struct {
AccountBindingID string `hcl:"account_binding_id" json:"account_binding_id"`
}

func (p *Plugin) buildConfig(coreConfig catalog.CoreConfig, hclText string, status *pluginconf.Status) *Config {
logger := p.logger.Named("parseConfig")
logger.Debug("Decoding EJBCA configuration")

newConfig := &Config{}
if err := hcl.Decode(&newConfig, hclText); err != nil {
status.ReportErrorf("failed to decode configuration: %v", err)
return nil
}

if newConfig.Hostname == "" {
status.ReportError("hostname is required")
}
if newConfig.CAName == "" {
status.ReportError("ca_name is required")
}
if newConfig.EndEntityProfileName == "" {
status.ReportError("end_entity_profile_name is required")
}
if newConfig.CertificateProfileName == "" {
status.ReportError("certificate_profile_name is required")
}

// If ClientCertPath or ClientCertKeyPath were not found in the main server conf file,
// load them from the environment.
if newConfig.ClientCertPath == "" {
newConfig.ClientCertPath = p.hooks.getEnv("EJBCA_CLIENT_CERT_PATH")
}
if newConfig.ClientCertKeyPath == "" {
newConfig.ClientCertKeyPath = p.hooks.getEnv("EJBCA_CLIENT_CERT_KEY_PATH")
}

// If ClientCertPath or ClientCertKeyPath were not present in either the conf file or
// the environment, return an error.
if newConfig.ClientCertPath == "" {
logger.Error("Client certificate is required for mTLS authentication")
status.ReportError("client_cert or EJBCA_CLIENT_CERT_PATH is required for mTLS authentication")
}
if newConfig.ClientCertKeyPath == "" {
logger.Error("Client key is required for mTLS authentication")
status.ReportError("client_key or EJBCA_CLIENT_KEY_PATH is required for mTLS authentication")
}

if newConfig.CaCertPath == "" {
newConfig.CaCertPath = p.hooks.getEnv("EJBCA_CA_CERT_PATH")
}

return newConfig
}

// New returns an instantiated EJBCA UpstreamAuthority plugin
func New() *Plugin {
p := &Plugin{}
Expand All @@ -96,26 +148,35 @@ func New() *Plugin {
// Configure configures the EJBCA UpstreamAuthority plugin. This is invoked by SPIRE when the plugin is
// first loaded. After the first invocation, it may be used to reconfigure the plugin.
func (p *Plugin) Configure(_ context.Context, req *configv1.ConfigureRequest) (*configv1.ConfigureResponse, error) {
config, err := p.parseConfig(req)
newConfig, _, err := pluginconf.Build(req, p.buildConfig)
if err != nil {
return nil, err
}

authenticator, err := p.hooks.newAuthenticator(config)
authenticator, err := p.hooks.newAuthenticator(newConfig)
if err != nil {
return nil, err
}

client, err := p.newEjbcaClient(config, authenticator)
client, err := p.newEjbcaClient(newConfig, authenticator)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "failed to create EJBCA client: %v", err)
}

p.setConfig(config)
p.setConfig(newConfig)
p.setClient(client)
return &configv1.ConfigureResponse{}, nil
}

func (p *Plugin) Validate(_ context.Context, req *configv1.ValidateRequest) (*configv1.ValidateResponse, error) {
_, notes, err := pluginconf.Build(req, p.buildConfig)

return &configv1.ValidateResponse{
Valid: err == nil,
Notes: notes,
}, nil
}

// SetLogger is called by the framework when the plugin is loaded and provides
// the plugin with a logger wired up to SPIRE's logging facilities.
func (p *Plugin) SetLogger(logger hclog.Logger) {
Expand Down
50 changes: 0 additions & 50 deletions pkg/server/plugin/upstreamauthority/ejbca/ejbca_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import (

ejbcaclient "github.com/Keyfactor/ejbca-go-client-sdk/api/ejbca"
"github.com/gogo/status"
"github.com/hashicorp/hcl"
configv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/service/common/config/v1"
"github.com/spiffe/spire/pkg/common/pemutil"
"google.golang.org/grpc/codes"
)
Expand All @@ -18,54 +16,6 @@ type ejbcaClient interface {
EnrollPkcs10Certificate(ctx context.Context) ejbcaclient.ApiEnrollPkcs10CertificateRequest
}

func (p *Plugin) parseConfig(req *configv1.ConfigureRequest) (*Config, error) {
logger := p.logger.Named("parseConfig")
config := new(Config)
logger.Debug("Decoding EJBCA configuration")
if err := hcl.Decode(&config, req.HclConfiguration); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "failed to decode configuration: %v", err)
}

if config.Hostname == "" {
return nil, status.Error(codes.InvalidArgument, "hostname is required")
}
if config.CAName == "" {
return nil, status.Error(codes.InvalidArgument, "ca_name is required")
}
if config.EndEntityProfileName == "" {
return nil, status.Error(codes.InvalidArgument, "end_entity_profile_name is required")
}
if config.CertificateProfileName == "" {
return nil, status.Error(codes.InvalidArgument, "certificate_profile_name is required")
}

// If ClientCertPath or ClientCertKeyPath were not found in the main server conf file,
// load them from the environment.
if config.ClientCertPath == "" {
config.ClientCertPath = p.hooks.getEnv("EJBCA_CLIENT_CERT_PATH")
}
if config.ClientCertKeyPath == "" {
config.ClientCertKeyPath = p.hooks.getEnv("EJBCA_CLIENT_CERT_KEY_PATH")
}

// If ClientCertPath or ClientCertKeyPath were not present in either the conf file or
// the environment, return an error.
if config.ClientCertPath == "" {
logger.Error("Client certificate is required for mTLS authentication")
return nil, status.Error(codes.InvalidArgument, "client_cert or EJBCA_CLIENT_CERT_PATH is required for mTLS authentication")
}
if config.ClientCertKeyPath == "" {
logger.Error("Client key is required for mTLS authentication")
return nil, status.Error(codes.InvalidArgument, "client_key or EJBCA_CLIENT_KEY_PATH is required for mTLS authentication")
}

if config.CaCertPath == "" {
config.CaCertPath = p.hooks.getEnv("EJBCA_CA_CERT_PATH")
}

return config, nil
}

func (p *Plugin) getAuthenticator(config *Config) (ejbcaclient.Authenticator, error) {
var err error
logger := p.logger.Named("getAuthenticator")
Expand Down
7 changes: 7 additions & 0 deletions pkg/server/plugin/upstreamauthority/ejbca/ejbca_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
ejbcaclient "github.com/Keyfactor/ejbca-go-client-sdk/api/ejbca"
"github.com/hashicorp/go-hclog"
"github.com/spiffe/go-spiffe/v2/spiffeid"
"github.com/spiffe/spire/pkg/common/catalog"
commonutil "github.com/spiffe/spire/pkg/common/util"
"github.com/spiffe/spire/pkg/server/plugin/upstreamauthority"
"github.com/spiffe/spire/test/plugintest"
Expand Down Expand Up @@ -374,6 +375,9 @@ func TestConfigure(t *testing.T) {

options := []plugintest.Option{
plugintest.CaptureConfigureError(&err),
plugintest.CoreConfig(catalog.CoreConfig{
TrustDomain: trustDomain,
}),
plugintest.Configure(tt.config),
}

Expand Down Expand Up @@ -536,6 +540,9 @@ func TestMintX509CAAndSubscribe(t *testing.T) {

options := []plugintest.Option{
plugintest.CaptureConfigureError(&err),
plugintest.CoreConfig(catalog.CoreConfig{
TrustDomain: trustDomain,
}),
plugintest.ConfigureJSON(config),
}

Expand Down

0 comments on commit d1bc067

Please sign in to comment.