diff --git a/internal/config/config.go b/internal/config/config.go index d620d0e..273e5c5 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -31,6 +31,8 @@ import ( "gopkg.in/yaml.v3" + "git.cacert.org/cacert-gosigner/pkg/messages" + "git.cacert.org/cacert-gosigner/internal/x509/openssl" "git.cacert.org/cacert-gosigner/internal/x509/revoking" "git.cacert.org/cacert-gosigner/internal/x509/signing" @@ -540,6 +542,7 @@ type CaCertificateEntry struct { KeyPair crypto.Signer Parent string Storage string + Profiles []messages.CAProfile } func (c *CaCertificateEntry) UnmarshalYAML(value *yaml.Node) error { @@ -551,6 +554,10 @@ func (c *CaCertificateEntry) UnmarshalYAML(value *yaml.Node) error { ExtKeyUsage []string `yaml:"ext-key-usages,omitempty"` Parent string `yaml:"parent"` Storage string `yaml:"storage"` + Profiles []struct { + Name string `yaml:"name"` + UseFor string `yaml:"use-for"` + } `yaml:"profiles"` } err := value.Decode(&m) @@ -590,6 +597,22 @@ func (c *CaCertificateEntry) UnmarshalYAML(value *yaml.Node) error { c.Storage = "default" } + if m.Profiles != nil { + c.Profiles = make([]messages.CAProfile, len(m.Profiles)) + + for i, prof := range m.Profiles { + usage, err := messages.ParseUsage(prof.UseFor) + if err != nil { + return fmt.Errorf("config error: %w", err) + } + + c.Profiles[i] = messages.CAProfile{ + Name: prof.Name, + UseFor: usage, + } + } + } + return nil } diff --git a/internal/hsm/hsm.go b/internal/hsm/hsm.go index 315fcc3..17de7dc 100644 --- a/internal/hsm/hsm.go +++ b/internal/hsm/hsm.go @@ -37,6 +37,8 @@ import ( "github.com/ThalesIgnite/crypto11" "github.com/sirupsen/logrus" + "git.cacert.org/cacert-gosigner/pkg/messages" + "git.cacert.org/cacert-gosigner/internal/config" "git.cacert.org/cacert-gosigner/internal/health" ) @@ -68,7 +70,7 @@ func (a *Access) Healthy() (*health.Info, error) { moreInfo := make(map[string]string) - const checkFailed = "failed" + var checkFailed = messages.CertificateInfo{Status: "failed", Signing: false}.String() for _, ca := range a.signerConfig.RootCAs() { infoKey := fmt.Sprintf("root-%s", ca) @@ -82,7 +84,12 @@ func (a *Access) Healthy() (*health.Info, error) { continue } - moreInfo[infoKey] = fmt.Sprintf("ok, valid until %s", cert.NotAfter.UTC().Format(time.RFC3339)) + moreInfo[infoKey] = messages.CertificateInfo{ + Status: "ok", + Signing: false, + Profiles: []messages.CAProfile{}, + ValidUntil: cert.NotAfter.UTC(), + }.String() } for _, ca := range a.signerConfig.SubordinateCAs() { @@ -115,7 +122,12 @@ func (a *Access) Healthy() (*health.Info, error) { continue } - moreInfo[infoKey] = fmt.Sprintf("ok, valid until %s", cert.NotAfter.UTC().Format(time.RFC3339)) + moreInfo[infoKey] = messages.CertificateInfo{ + Status: "ok", + Signing: true, + Profiles: def.Profiles, + ValidUntil: cert.NotAfter.UTC(), + }.String() } return &health.Info{ diff --git a/pkg/messages/messages.go b/pkg/messages/messages.go index 744e2d1..7b1c759 100644 --- a/pkg/messages/messages.go +++ b/pkg/messages/messages.go @@ -22,6 +22,7 @@ package messages import ( "crypto/x509" + "encoding/json" "encoding/pem" "fmt" "math/big" @@ -144,6 +145,66 @@ func (i *HealthInfo) String() string { return builder.String() } +type ProfileUsage string + +const ( + UsageOcsp ProfileUsage = "ocsp" + UsagePerson ProfileUsage = "person" + UsageClient ProfileUsage = "client" + UsageCode ProfileUsage = "code" + UsageServer ProfileUsage = "server" +) + +var validProfileUsages = map[string]ProfileUsage{ + "ocsp": UsageOcsp, + "person": UsagePerson, + "client": UsageClient, + "code": UsageCode, + "server": UsageServer, +} + +func ParseUsage(u string) (ProfileUsage, error) { + usage, ok := validProfileUsages[u] + if !ok { + return "", fmt.Errorf("unsupported profile usage: %s", u) + } + + return usage, nil +} + +type CAProfile struct { + Name string `json:"name"` + UseFor ProfileUsage `json:"use-for"` +} + +func (p CAProfile) String() string { + return fmt.Sprintf("profile['%s': '%s']", p.Name, p.UseFor) +} + +type CertificateInfo struct { + Status string `json:"status"` + Signing bool `json:"signing"` + Profiles []CAProfile `json:"profiles"` + ValidUntil time.Time `json:"valid-until"` +} + +func (i CertificateInfo) String() string { + marshal, _ := json.Marshal(i) + + return string(marshal) +} + +func (i *HealthInfo) ParseCertificateInfo(info string) (*CertificateInfo, error) { + certInfo := CertificateInfo{} + + err := json.Unmarshal([]byte(info), &certInfo) + if err != nil { + return nil, fmt.Errorf("could not parse certificate information: %w", err) + } + + return &certInfo, nil +} + type HealthResponse struct { Version string `msgpack:"version"` Healthy bool `msgpack:"healthy"`