Skip to content

Commit

Permalink
Only write TLS files for provided certificates
Browse files Browse the repository at this point in the history
--c2c-creds flag is optional for backwards compatibility. If flag is not
provided do not try to parse the default certificate for c2c.
  • Loading branch information
mariash committed Dec 9, 2021
1 parent 7d74821 commit fc02500
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ var _ = Describe("Acceptance", func() {
session *gexec.Session
)

BeforeEach(func() {
JustBeforeEach(func() {
nginxBin, err := gexec.Build("code.cloudfoundry.org/envoy-nginx/fixtures/nginx")
Expect(err).ToNot(HaveOccurred())

Expand Down Expand Up @@ -133,7 +133,7 @@ var _ = Describe("Acceptance", func() {
Expect(string(currentKey)).To(Equal(expectedKey))
})

It("rewrites the id cert and key file and reloads nginx", func() {
It("rewrites the c2c cert and key file and reloads nginx", func() {
err := RotateCert("../fixtures/cf_assets_envoy_config/sds-c2c-cert-and-key-rotated.yaml", sdsC2CCredsFile)
Expect(err).ToNot(HaveOccurred())

Expand Down Expand Up @@ -164,6 +164,20 @@ var _ = Describe("Acceptance", func() {
Expect(string(currentKey)).To(Equal(expectedKey))
})
})

Context("when c2c creds file is not provided", func() {
BeforeEach(func() {
cmd = exec.Command(envoyNginxBin, "-c", EnvoyFixture, "--id-creds", sdsIdCredsFile, "--id-validation", SdsIdValidationFixture)
})

It("does not reload the nginx", func() {
err := RotateCert("../fixtures/cf_assets_envoy_config/sds-c2c-cert-and-key-rotated.yaml", sdsC2CCredsFile)
Expect(err).ToNot(HaveOccurred())

Eventually(session.Out).ShouldNot(gbytes.Say("detected change in sdsfile"))
Eventually(session.Out).ShouldNot(gbytes.Say(fmt.Sprintf("-p,%s,-s,reload", strings.Replace(nginxDir, `\`, `\\`, -1))))
})
})
})
})

Expand Down
23 changes: 15 additions & 8 deletions src/code.cloudfoundry.org/envoy-nginx/app/envoy.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,16 @@ func (a App) Run(nginxConfDir, nginxBinPath, sdsIdCreds, sdsC2CCreds, sdsIdValid
}

envoyConfParser := parser.NewEnvoyConfParser()
sdsIdCredParser := parser.NewSdsCredParser(sdsIdCreds)
sdsC2CCredParser := parser.NewSdsCredParser(sdsC2CCreds)

sdsIdCredParser := parser.NewSdsIdCredParser(sdsIdCreds)
sdsCredParsers := []parser.SdsCredParser{sdsIdCredParser}
if sdsC2CCreds != "" {
sdsC2CCredParser := parser.NewSdsC2CCredParser(sdsC2CCreds)
sdsCredParsers = append(sdsCredParsers, sdsC2CCredParser)
}
sdsIdValidationParser := parser.NewSdsIdValidationParser(sdsIdValidation)

nginxConfParser := parser.NewNginxConfig(envoyConfParser, sdsIdCredParser, sdsC2CCredParser, sdsIdValidationParser, nginxConfDir)
nginxConfParser := parser.NewNginxConfig(envoyConfParser, sdsCredParsers, sdsIdValidationParser, nginxConfDir)

errorChan := make(chan error)
readyChan := make(chan bool)
Expand All @@ -96,11 +101,13 @@ func (a App) Run(nginxConfDir, nginxBinPath, sdsIdCreds, sdsC2CCreds, sdsIdValid
})
}()

go func() {
errorChan <- WatchFile(sdsC2CCreds, readyChan, func() error {
return a.sdsFileUpdated(sdsC2CCreds, nginxConfParser)
})
}()
if sdsC2CCreds != "" {
go func() {
errorChan <- WatchFile(sdsC2CCreds, readyChan, func() error {
return a.sdsFileUpdated(sdsC2CCreds, nginxConfParser)
})
}()
}

go func() {
<-readyChan
Expand Down
6 changes: 2 additions & 4 deletions src/code.cloudfoundry.org/envoy-nginx/app/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import "strings"

const (
DefaultEnvoyConfigPath = "C:\\etc\\cf-assets\\envoy_config\\envoy.yaml"
DefaultSdsIdCertAndKeysPath = "C:\\etc\\cf-assets\\envoy_config\\sds-id-cert-and-key.yaml"
DefaultSdsC2CCertAndKeysPath = "C:\\etc\\cf-assets\\envoy_config\\sds-c2c-cert-and-key.yaml"
DefaultSdsIdValidationContextPath = "C:\\etc\\cf-assets\\envoy_config\\sds-id-validation-context.yaml"
DefaultSdsIdCertAndKeysPath = "C:\\etc\\cf-assets\\envoy_config\\sds-server-cert-and-key.yaml"
DefaultSdsIdValidationContextPath = "C:\\etc\\cf-assets\\envoy_config\\sds-server-validation-context.yaml"
)

type Options struct {
Expand All @@ -25,7 +24,6 @@ func NewFlags() Flags {
options: Options{
EnvoyConfig: DefaultEnvoyConfigPath,
SdsIdCreds: DefaultSdsIdCertAndKeysPath,
SdsC2CCreds: DefaultSdsC2CCertAndKeysPath,
SdsIdValidation: DefaultSdsIdValidationContextPath,
},
}
Expand Down
2 changes: 1 addition & 1 deletion src/code.cloudfoundry.org/envoy-nginx/app/flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ var _ = Describe("Flags", func() {
opts := flags.Parse([]string{})
Expect(opts.EnvoyConfig).To(Equal(app.DefaultEnvoyConfigPath))
Expect(opts.SdsIdCreds).To(Equal(app.DefaultSdsIdCertAndKeysPath))
Expect(opts.SdsC2CCreds).To(Equal(app.DefaultSdsC2CCertAndKeysPath))
Expect(opts.SdsC2CCreds).To(BeEmpty())
Expect(opts.SdsIdValidation).To(Equal(app.DefaultSdsIdValidationContextPath))
})

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package fakes

import "code.cloudfoundry.org/envoy-nginx/parser"

type SdsCredParser struct {
GetCertAndKeyCall struct {
CallCount int
Expand All @@ -9,10 +11,23 @@ type SdsCredParser struct {
Error error
}
}

ConfigTypeCall struct {
CallCount int
Returns struct {
ConfigType parser.SdsConfigType
}
}
}

func (e SdsCredParser) GetCertAndKey() (string, string, error) {
e.GetCertAndKeyCall.CallCount++

return e.GetCertAndKeyCall.Returns.Cert, e.GetCertAndKeyCall.Returns.Key, e.GetCertAndKeyCall.Returns.Error
}

func (e SdsCredParser) ConfigType() parser.SdsConfigType {
e.ConfigTypeCall.CallCount++

return e.ConfigTypeCall.Returns.ConfigType
}
61 changes: 28 additions & 33 deletions src/code.cloudfoundry.org/envoy-nginx/parser/nginx_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,19 @@ type envoyConfParser interface {
GetClusters(conf EnvoyConf) ([]Cluster, map[string][]ListenerInfo)
}

type sdsCredParser interface {
type SdsCredParser interface {
GetCertAndKey() (string, string, error)
ConfigType() SdsConfigType
}

type sdsValidationParser interface {
type SdsValidationParser interface {
GetCACert() (string, error)
}

type NginxConfig struct {
envoyConfParser envoyConfParser
sdsIdCredParser sdsCredParser
sdsC2CCredParser sdsCredParser
sdsValidationParser sdsValidationParser
sdsCredParsers []SdsCredParser
sdsValidationParser SdsValidationParser
nginxDir string
confFile string
idCertFile string
Expand All @@ -55,11 +55,10 @@ type NginxConfig struct {
pidFile string
}

func NewNginxConfig(envoyConfParser envoyConfParser, sdsIdCredParser sdsCredParser, sdsC2CCredParser sdsCredParser, sdsValidationParser sdsValidationParser, nginxDir string) NginxConfig {
func NewNginxConfig(envoyConfParser envoyConfParser, sdsCredParsers []SdsCredParser, sdsValidationParser SdsValidationParser, nginxDir string) NginxConfig {
return NginxConfig{
envoyConfParser: envoyConfParser,
sdsIdCredParser: sdsIdCredParser,
sdsC2CCredParser: sdsC2CCredParser,
sdsCredParsers: sdsCredParsers,
sdsValidationParser: sdsValidationParser,
nginxDir: nginxDir,
confFile: filepath.Join(nginxDir, "conf", "nginx.conf"),
Expand Down Expand Up @@ -188,34 +187,30 @@ stream {
}

func (n NginxConfig) WriteTLSFiles() error {
cert, key, err := n.sdsIdCredParser.GetCertAndKey()
if err != nil {
return fmt.Errorf("get cert and key from sds id cred parser: %s", err)
}

err = ioutil.WriteFile(n.idCertFile, []byte(cert), FilePerm)
if err != nil {
return fmt.Errorf("write cert: %s", err)
}

err = ioutil.WriteFile(n.idKeyFile, []byte(key), FilePerm)
if err != nil {
return fmt.Errorf("write key: %s", err)
}
for _, sdsCredParser := range n.sdsCredParsers {
cert, key, err := sdsCredParser.GetCertAndKey()
if err != nil {
return fmt.Errorf("get cert and key from sds cred parser: %s", err)
}

cert, key, err = n.sdsC2CCredParser.GetCertAndKey()
if err != nil {
return fmt.Errorf("get cert and key from sds c2c cred parser: %s", err)
}
var certFile, keyFile string
if sdsCredParser.ConfigType() == SdsIdConfigType {
certFile = n.idCertFile
keyFile = n.idKeyFile
} else {
certFile = n.c2cCertFile
keyFile = n.c2cKeyFile
}

err = ioutil.WriteFile(n.c2cCertFile, []byte(cert), FilePerm)
if err != nil {
return fmt.Errorf("write cert: %s", err)
}
err = ioutil.WriteFile(certFile, []byte(cert), FilePerm)
if err != nil {
return fmt.Errorf("write cert: %s", err)
}

err = ioutil.WriteFile(n.c2cKeyFile, []byte(key), FilePerm)
if err != nil {
return fmt.Errorf("write key: %s", err)
err = ioutil.WriteFile(keyFile, []byte(key), FilePerm)
if err != nil {
return fmt.Errorf("write key: %s", err)
}
}

caCert, err := n.sdsValidationParser.GetCACert()
Expand Down
46 changes: 42 additions & 4 deletions src/code.cloudfoundry.org/envoy-nginx/parser/nginx_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ var _ = Describe("Nginx Config", func() {
err = os.Mkdir(filepath.Join(tmpdir, "conf"), os.ModePerm)
Expect(err).ShouldNot(HaveOccurred())

nginxConfig = parser.NewNginxConfig(envoyConfParser, sdsIdCredParser, sdsC2CCredParser, sdsValidationParser, tmpdir)
nginxConfig = parser.NewNginxConfig(envoyConfParser, []parser.SdsCredParser{sdsIdCredParser, sdsC2CCredParser}, sdsValidationParser, tmpdir)
})

AfterEach(func() {
Expand All @@ -51,8 +51,10 @@ var _ = Describe("Nginx Config", func() {
BeforeEach(func() {
sdsIdCredParser.GetCertAndKeyCall.Returns.Cert = "some-id-cert"
sdsIdCredParser.GetCertAndKeyCall.Returns.Key = "some-id-key"
sdsIdCredParser.ConfigTypeCall.Returns.ConfigType = parser.SdsIdConfigType
sdsC2CCredParser.GetCertAndKeyCall.Returns.Cert = "some-c2c-cert"
sdsC2CCredParser.GetCertAndKeyCall.Returns.Key = "some-c2c-key"
sdsC2CCredParser.ConfigTypeCall.Returns.ConfigType = parser.SdsC2CConfigType
sdsValidationParser.GetCACertCall.Returns.CA = "some-ca-cert"
})

Expand Down Expand Up @@ -88,14 +90,50 @@ var _ = Describe("Nginx Config", func() {
Expect(string(ca)).To(Equal("some-ca-cert"))
})

Context("when c2c sds cred parser is not provided", func() {
BeforeEach(func() {
nginxConfig = parser.NewNginxConfig(envoyConfParser, []parser.SdsCredParser{sdsIdCredParser}, sdsValidationParser, tmpdir)
})

It("only writes cert and key for provided parsers", func() {
err := nginxConfig.WriteTLSFiles()
Expect(err).ShouldNot(HaveOccurred())

certPath := filepath.Join(tmpdir, "id-cert.pem")
keyPath := filepath.Join(tmpdir, "id-key.pem")

cert, err := ioutil.ReadFile(string(certPath))
Expect(err).ShouldNot(HaveOccurred())
Expect(string(cert)).To(Equal("some-id-cert"))

key, err := ioutil.ReadFile(string(keyPath))
Expect(err).ShouldNot(HaveOccurred())
Expect(string(key)).To(Equal("some-id-key"))

certPath = filepath.Join(tmpdir, "c2c-cert.pem")
keyPath = filepath.Join(tmpdir, "c2c-key.pem")

_, err = os.Stat(string(certPath))
Expect(err).Should(HaveOccurred())

_, err = os.Stat(string(keyPath))
Expect(err).Should(HaveOccurred())

caPath := filepath.Join(tmpdir, "id-ca.pem")
ca, err := ioutil.ReadFile(string(caPath))
Expect(err).ShouldNot(HaveOccurred())
Expect(string(ca)).To(Equal("some-ca-cert"))
})
})

Context("when id sds cred parser fails to get cert and key", func() {
BeforeEach(func() {
sdsIdCredParser.GetCertAndKeyCall.Returns.Error = errors.New("banana")
})

It("returns a helpful error message", func() {
err := nginxConfig.WriteTLSFiles()
Expect(err).To(MatchError("get cert and key from sds id cred parser: banana"))
Expect(err).To(MatchError("get cert and key from sds cred parser: banana"))
})
})

Expand All @@ -106,7 +144,7 @@ var _ = Describe("Nginx Config", func() {

It("returns a helpful error message", func() {
err := nginxConfig.WriteTLSFiles()
Expect(err).To(MatchError("get cert and key from sds c2c cred parser: banana"))
Expect(err).To(MatchError("get cert and key from sds cred parser: banana"))
})
})

Expand Down Expand Up @@ -249,7 +287,7 @@ var _ = Describe("Nginx Config", func() {

Context("when ioutil fails to write the nginx.conf", func() {
BeforeEach(func() {
nginxConfig = parser.NewNginxConfig(envoyConfParser, sdsIdCredParser, sdsC2CCredParser, sdsValidationParser, "not-a-real-dir")
nginxConfig = parser.NewNginxConfig(envoyConfParser, []parser.SdsCredParser{sdsIdCredParser, sdsC2CCredParser}, sdsValidationParser, "not-a-real-dir")
})
// We do not test that ioutil.WriteFile fails for cert/key because
// our trick to cause that function to fail only works once!
Expand Down
27 changes: 20 additions & 7 deletions src/code.cloudfoundry.org/envoy-nginx/parser/sds_creds_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,27 @@ type PrivateKey struct {
InlineString string `yaml:"inline_string,omitempty"`
}

type SdsCredParser struct {
file string
type sdsCredParser struct {
file string
configType SdsConfigType
}

func NewSdsCredParser(file string) SdsCredParser {
return SdsCredParser{
file: file,
func NewSdsIdCredParser(file string) SdsCredParser {
return sdsCredParser{
file: file,
configType: SdsIdConfigType,
}
}

func NewSdsC2CCredParser(file string) SdsCredParser {
return sdsCredParser{
file: file,
configType: SdsC2CConfigType,
}
}

/* Parses the Envoy SDS file and extracts the cert and key */
func (p SdsCredParser) GetCertAndKey() (string, string, error) {
func (p sdsCredParser) GetCertAndKey() (string, string, error) {
contents, err := ioutil.ReadFile(p.file)
if err != nil {
return "", "", fmt.Errorf("Failed to read sds creds: %s", err)
Expand All @@ -54,11 +63,15 @@ func (p SdsCredParser) GetCertAndKey() (string, string, error) {
}

if len(auth.Resources) < 1 {
return "", "", errors.New("resources section not found in sds-server-cert-and-key.yaml")
return "", "", errors.New("resources section not found in sds cred file")
}

cert := auth.Resources[0].TLSCertificate.CertChain.InlineString
key := auth.Resources[0].TLSCertificate.PrivateKey.InlineString

return cert, key, nil
}

func (p sdsCredParser) ConfigType() SdsConfigType {
return p.configType
}
Loading

0 comments on commit fc02500

Please sign in to comment.