diff --git a/authority/authority.go b/authority/authority.go index 0f171fa7..48b7a566 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -442,10 +442,17 @@ func (a *Authority) init() error { // Initialize step-ca Admin Database if it's not already initialized using // WithAdminDB. if a.adminDB == nil { - // Check if AuthConfig already exists - a.adminDB, err = adminDBNosql.New(a.db.(nosql.DB), admin.DefaultAuthorityID) - if err != nil { - return err + if a.config.AuthorityConfig.AuthorityID == "" { + // Check if AuthConfig already exists + a.adminDB, err = adminDBNosql.New(a.db.(nosql.DB), admin.DefaultAuthorityID) + if err != nil { + return err + } + } else { + a.adminDB, err = createLinkedCAClient(a.config.AuthorityConfig.AuthorityID, "localhost:6040") + if err != nil { + return err + } } } diff --git a/authority/linkedca.go b/authority/linkedca.go new file mode 100644 index 00000000..0d9a748f --- /dev/null +++ b/authority/linkedca.go @@ -0,0 +1,171 @@ +package authority + +import ( + "context" + "crypto/tls" + "crypto/x509" + "io/ioutil" + "path/filepath" + + "github.com/pkg/errors" + "github.com/smallstep/certificates/errs" + "go.step.sm/cli-utils/config" + "go.step.sm/linkedca" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" +) + +type linkedCaClient struct { + client linkedca.MajordomoClient + authorityID string +} + +func createLinkedCAClient(authorityID, endpoint string) (*linkedCaClient, error) { + base := filepath.Join(config.StepPath(), "linkedca") + rootFile := filepath.Join(base, "root_ca.crt") + certFile := filepath.Join(base, "linkedca.crt") + keyFile := filepath.Join(base, "linkedca.key") + + b, err := ioutil.ReadFile(rootFile) + if err != nil { + return nil, errors.Wrap(err, "error reading linkedca root") + } + pool := x509.NewCertPool() + if !pool.AppendCertsFromPEM(b) { + return nil, errors.Errorf("error reading %s: no certificates were found", rootFile) + } + + conn, err := grpc.Dial(endpoint, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + RootCAs: pool, + GetClientCertificate: func(*tls.CertificateRequestInfo) (*tls.Certificate, error) { + cert, err := tls.LoadX509KeyPair(certFile, keyFile) + if err != nil { + return nil, errors.Wrap(err, "error reading linkedca certificate") + } + return &cert, nil + }, + }))) + if err != nil { + return nil, errors.Wrapf(err, "error connecting %s", endpoint) + } + + return &linkedCaClient{ + client: linkedca.NewMajordomoClient(conn), + authorityID: authorityID, + }, nil +} + +func (c *linkedCaClient) CreateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error { + resp, err := c.client.CreateProvisioner(ctx, &linkedca.CreateProvisionerRequest{ + Type: prov.Type, + Name: prov.Name, + Details: prov.Details, + Claims: prov.Claims, + X509Template: prov.X509Template, + SshTemplate: prov.SshTemplate, + }) + if err != nil { + return errors.Wrap(err, "error creating provisioner") + } + prov.Id = resp.Id + prov.AuthorityId = resp.AuthorityId + return nil +} + +func (c *linkedCaClient) GetProvisioner(ctx context.Context, id string) (*linkedca.Provisioner, error) { + resp, err := c.client.GetConfiguration(ctx, &linkedca.ConfigurationRequest{ + AuthorityId: c.authorityID, + }) + if err != nil { + return nil, errors.Wrap(err, "error getting provisioners") + } + for _, p := range resp.Provisioners { + if p.Id == id { + return p, nil + } + } + return nil, errs.NotFound("provisioner not found") +} + +func (c *linkedCaClient) GetProvisioners(ctx context.Context) ([]*linkedca.Provisioner, error) { + resp, err := c.client.GetConfiguration(ctx, &linkedca.ConfigurationRequest{ + AuthorityId: c.authorityID, + }) + if err != nil { + return nil, errors.Wrap(err, "error getting provisioners") + } + return resp.Provisioners, nil +} + +func (c *linkedCaClient) UpdateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error { + _, err := c.client.UpdateProvisioner(ctx, &linkedca.UpdateProvisionerRequest{ + Id: prov.Id, + Name: prov.Name, + Details: prov.Details, + Claims: prov.Claims, + X509Template: prov.X509Template, + SshTemplate: prov.SshTemplate, + }) + return errors.Wrap(err, "error updating provisioner") +} + +func (c *linkedCaClient) DeleteProvisioner(ctx context.Context, id string) error { + _, err := c.client.DeleteProvisioner(ctx, &linkedca.DeleteProvisionerRequest{ + Id: id, + }) + return errors.Wrap(err, "error deleting provisioner") +} + +func (c *linkedCaClient) CreateAdmin(ctx context.Context, adm *linkedca.Admin) error { + resp, err := c.client.CreateAdmin(ctx, &linkedca.CreateAdminRequest{ + Subject: adm.Subject, + ProvisionerId: adm.ProvisionerId, + Type: adm.Type, + }) + if err != nil { + return errors.Wrap(err, "error creating admin") + } + adm.Id = resp.Id + adm.AuthorityId = resp.AuthorityId + return nil +} + +func (c *linkedCaClient) GetAdmin(ctx context.Context, id string) (*linkedca.Admin, error) { + resp, err := c.client.GetConfiguration(ctx, &linkedca.ConfigurationRequest{ + AuthorityId: c.authorityID, + }) + if err != nil { + return nil, errors.Wrap(err, "error getting admins") + } + for _, a := range resp.Admins { + if a.Id == id { + return a, nil + } + } + return nil, errs.NotFound("admin not found") +} + +func (c *linkedCaClient) GetAdmins(ctx context.Context) ([]*linkedca.Admin, error) { + resp, err := c.client.GetConfiguration(ctx, &linkedca.ConfigurationRequest{ + AuthorityId: c.authorityID, + }) + if err != nil { + return nil, errors.Wrap(err, "error getting admins") + } + return resp.Admins, nil +} + +func (c *linkedCaClient) UpdateAdmin(ctx context.Context, adm *linkedca.Admin) error { + _, err := c.client.UpdateAdmin(ctx, &linkedca.UpdateAdminRequest{ + Id: adm.Id, + Type: adm.Type, + }) + return errors.Wrap(err, "error updating admin") +} + +func (c *linkedCaClient) DeleteAdmin(ctx context.Context, id string) error { + _, err := c.client.DeleteAdmin(ctx, &linkedca.DeleteAdminRequest{ + Id: id, + }) + return errors.Wrap(err, "error deleting admin") +}