From 74c96119968c5801b73c667432cfabaef3f8a157 Mon Sep 17 00:00:00 2001 From: Andrew Martinez Date: Tue, 11 May 2021 11:16:21 -0400 Subject: [PATCH 1/2] add back reading the root identity --- controller/config/config.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/controller/config/config.go b/controller/config/config.go index e6930ab7f..56290c2ec 100644 --- a/controller/config/config.go +++ b/controller/config/config.go @@ -129,6 +129,13 @@ func (c *Config) CaPems() []byte { return c.caPemsBuf } +// AddCaPems adds a byte array of PEMs to the current buffered list of CAs. The PEMs should +// be certificate bodies separate by new lines. +func (c *Config) AddCaPems(caPems []byte) { + c.caPemsBuf = append(c.caPemsBuf, []byte("\n")...) + c.caPemsBuf = append(c.caPemsBuf, caPems...) +} + func (c *Config) loadRootIdentity(fabricConfigMap map[interface{}]interface{}) error { var fabricIdentitySubMap map[interface{}]interface{} if value, found := fabricConfigMap["identity"]; found { @@ -366,6 +373,10 @@ func LoadFromMap(configMap map[interface{}]interface{}) (*Config, error) { var err error + if err = edgeConfig.loadRootIdentity(configMap); err != nil { + return nil, err + } + if err = edgeConfig.loadApiSection(edgeConfigMap); err != nil { return nil, err } From 6ee4f6a8a52853567f9d480193c9746d1f09288e Mon Sep 17 00:00:00 2001 From: Andrew Martinez Date: Tue, 11 May 2021 14:08:38 -0400 Subject: [PATCH 2/2] rework CaPems for ca store calculations --- controller/config/config.go | 115 ++++++++++++++++++-------------- controller/server/client-api.go | 27 ++++++-- 2 files changed, 87 insertions(+), 55 deletions(-) diff --git a/controller/config/config.go b/controller/config/config.go index 56290c2ec..004c9fc53 100644 --- a/controller/config/config.go +++ b/controller/config/config.go @@ -69,9 +69,9 @@ type Config struct { Enabled bool Api Api Enrollment Enrollment - caPems [][]byte - caPemsBuf []byte - caPemsOnce sync.Once + + caPems *bytes.Buffer + caPemsOnce sync.Once } type HttpTimeouts struct { @@ -81,59 +81,35 @@ type HttpTimeouts struct { IdleTimeoutsDuration time.Duration } +func NewConfig() *Config { + return &Config{ + Enabled: false, + caPems: bytes.NewBuffer(nil), + } +} + func (c *Config) SessionTimeoutDuration() time.Duration { return c.Api.SessionTimeout } -func toHex(data []byte) string { - var buf bytes.Buffer - for i, b := range data { - if i > 0 { - _, _ = fmt.Fprintf(&buf, ":") - } - _, _ = fmt.Fprintf(&buf, "%02x", b) - } - return strings.ToUpper(buf.String()) -} - func (c *Config) CaPems() []byte { c.caPemsOnce.Do(func() { - buf := bytes.Buffer{} - //dedupe chains - pemMap := map[string][]byte{} - for _, caChain := range c.caPems { - rest := caChain - for len(rest) != 0 { - var block *pem.Block - block, rest = pem.Decode(rest) - - if block != nil { - hash := sha1.Sum(block.Bytes) - fingerprint := toHex(hash[:]) - pemMap[fingerprint] = pem.EncodeToMemory(block) - } - } - } - - i := 0 - for _, pemBytes := range pemMap { - if i != 0 { - buf.Write([]byte("\n")) - } - buf.Write(pemBytes) - i++ - } - c.caPemsBuf = buf.Bytes() + c.RefreshCaPems() }) - return c.caPemsBuf + return c.caPems.Bytes() } -// AddCaPems adds a byte array of PEMs to the current buffered list of CAs. The PEMs should -// be certificate bodies separate by new lines. +// AddCaPems adds a byte array of certificates to the current buffered list of CAs. The certificates +// should be in PEM format separated by new lines. RefreshCaPems should be called after all +// calls to AddCaPems are completed. func (c *Config) AddCaPems(caPems []byte) { - c.caPemsBuf = append(c.caPemsBuf, []byte("\n")...) - c.caPemsBuf = append(c.caPemsBuf, caPems...) + c.caPems.WriteString("\n") + c.caPems.Write(caPems) +} + +func (c *Config) RefreshCaPems() { + c.caPems = CalculateCaPems(c.caPems) } func (c *Config) loadRootIdentity(fabricConfigMap map[interface{}]interface{}) error { @@ -175,7 +151,8 @@ func (c *Config) loadRootIdentity(fabricConfigMap map[interface{}]interface{}) e return fmt.Errorf("could not read file CA file from [identity.ca]") } - c.caPems = append(c.caPems, c.RootIdentityCaPem) + _, _ = c.caPems.WriteString("\n") + _, _ = c.caPems.Write(c.RootIdentityCaPem) c.RootIdentity, err = identity.LoadIdentity(c.RootIdentityConfig) @@ -265,7 +242,8 @@ func (c *Config) loadEnrollmentSection(edgeConfigMap map[interface{}]interface{} pfxlog.Logger().WithError(err).Panic("unable to read [edge.enrollment.cert]") } //The signer is a valid trust anchor - c.caPems = append(c.caPems, certPem) + _, _ = c.caPems.WriteString("\n") + _, _ = c.caPems.Write(certPem) } else { return fmt.Errorf("required configuration value [edge.enrollment.cert] is missing") @@ -284,7 +262,8 @@ func (c *Config) loadEnrollmentSection(edgeConfigMap map[interface{}]interface{} return fmt.Errorf("could not read file CA file from [edge.enrollment.signingCert.ca]") } - c.caPems = append(c.caPems, c.Enrollment.SigningCertCaPem) + _, _ = c.caPems.WriteString("\n") + _, _ = c.caPems.Write(c.Enrollment.SigningCertCaPem) } //not an error if the signing certificate's CA is already represented in the root [identity.ca] if c.Enrollment.SigningCert, err = identity.LoadIdentity(c.Enrollment.SigningCertConfig); err != nil { @@ -351,9 +330,7 @@ func (c *Config) loadEnrollmentSection(edgeConfigMap map[interface{}]interface{} } func LoadFromMap(configMap map[interface{}]interface{}) (*Config, error) { - edgeConfig := &Config{ - Enabled: false, - } + edgeConfig := NewConfig() var edgeConfigMap map[interface{}]interface{} @@ -387,3 +364,39 @@ func LoadFromMap(configMap map[interface{}]interface{}) (*Config, error) { return edgeConfig, nil } + +// CalculateCaPems takes the supplied caPems buffer as a set of PEM Certificates separated by new lines. Duplicate +// certificates are removed and the result is returned as a bytes.Buffer of PEM Certificates separated by new lines. +func CalculateCaPems(caPems *bytes.Buffer) *bytes.Buffer { + caPemMap := map[string][]byte{} + + newCaPems := bytes.Buffer{} + blocksToProcess := caPems.Bytes() + for len(blocksToProcess) != 0 { + var block *pem.Block + block, blocksToProcess = pem.Decode(blocksToProcess) + + if block != nil { + hash := sha1.Sum(block.Bytes) + fingerprint := toHex(hash[:]) + newPem := pem.EncodeToMemory(block) + caPemMap[fingerprint] = newPem + _, _ = newCaPems.WriteString("\n") + _, _ = newCaPems.Write(newPem) + } + } + + return &newCaPems +} + +// toHex takes a byte array returns a hex formatted fingerprint +func toHex(data []byte) string { + var buf bytes.Buffer + for i, b := range data { + if i > 0 { + _, _ = fmt.Fprintf(&buf, ":") + } + _, _ = fmt.Fprintf(&buf, "%02x", b) + } + return strings.ToUpper(buf.String()) +} diff --git a/controller/server/client-api.go b/controller/server/client-api.go index 6b1ced8ed..bed9f812f 100644 --- a/controller/server/client-api.go +++ b/controller/server/client-api.go @@ -27,6 +27,7 @@ import ( "github.com/openziti/edge/rest_client_api_server" "github.com/openziti/fabric/controller/xweb" "github.com/pkg/errors" + "io/ioutil" "net/http" "strings" "time" @@ -40,21 +41,39 @@ type ClientApiFactory struct { } func (factory ClientApiFactory) Validate(config *xweb.Config) error { + clientApiFound := false for _, webListener := range config.WebListeners { for _, api := range webListener.APIs { - if api.Binding() == controller.ClientApiBinding { + + if webListener.IdentityConfig != nil && (api.Binding() == controller.ClientApiBinding || api.Binding() == controller.ManagementApiBinding){ + caBytes, err := ioutil.ReadFile(webListener.IdentityConfig.CA) + + if err != nil { + return errors.Errorf("could not read xweb web listener [%s]'s CA file [%s] to retrieve CA PEMs: %v", webListener.Name, webListener.IdentityConfig.CA, err) + } + + factory.appEnv.Config.AddCaPems(caBytes) + } + + if !clientApiFound && api.Binding() == controller.ClientApiBinding { for _, bindPoint := range webListener.BindPoints { if bindPoint.Address == factory.appEnv.Config.Api.Address { factory.appEnv.SetEnrollmentSigningCert(webListener.Identity.ServerCert()) - return nil + clientApiFound = true + break } } - break //done with this webListener, look for next if we are still running } } } - return errors.Errorf("could not find [edge.api.address] value [%s] as a bind point any instance of API [%s]", factory.appEnv.Config.Api.Address, controller.ClientApiBinding) + factory.appEnv.Config.RefreshCaPems() + + if !clientApiFound { + return errors.Errorf("could not find [edge.api.address] value [%s] as a bind point any instance of API [%s]", factory.appEnv.Config.Api.Address, controller.ClientApiBinding) + } + + return nil } func NewClientApiFactory(appEnv *env.AppEnv) *ClientApiFactory {