Skip to content

Commit 085fa47

Browse files
author
Julien Pivotto
authored
Merge pull request #472 from grafana/inline-ca-strings
Allow TLS settings to be specified inline
2 parents f505d86 + bcb00f1 commit 085fa47

File tree

2 files changed

+295
-53
lines changed

2 files changed

+295
-53
lines changed

config/http_config.go

Lines changed: 141 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -579,8 +579,7 @@ func NewRoundTripperFromConfig(cfg HTTPClientConfig, name string, optFuncs ...HT
579579
// No need for a RoundTripper that reloads the CA file automatically.
580580
return newRT(tlsConfig)
581581
}
582-
583-
return NewTLSRoundTripper(tlsConfig, cfg.TLSConfig.CAFile, cfg.TLSConfig.CertFile, cfg.TLSConfig.KeyFile, newRT)
582+
return NewTLSRoundTripper(tlsConfig, cfg.TLSConfig.roundTripperSettings(), newRT)
584583
}
585584

586585
type authorizationCredentialsRoundTripper struct {
@@ -750,7 +749,7 @@ func (rt *oauth2RoundTripper) RoundTrip(req *http.Request) (*http.Response, erro
750749
if len(rt.config.TLSConfig.CAFile) == 0 {
751750
t, _ = tlsTransport(tlsConfig)
752751
} else {
753-
t, err = NewTLSRoundTripper(tlsConfig, rt.config.TLSConfig.CAFile, rt.config.TLSConfig.CertFile, rt.config.TLSConfig.KeyFile, tlsTransport)
752+
t, err = NewTLSRoundTripper(tlsConfig, rt.config.TLSConfig.roundTripperSettings(), tlsTransport)
754753
if err != nil {
755754
return nil, err
756755
}
@@ -817,6 +816,10 @@ func cloneRequest(r *http.Request) *http.Request {
817816

818817
// NewTLSConfig creates a new tls.Config from the given TLSConfig.
819818
func NewTLSConfig(cfg *TLSConfig) (*tls.Config, error) {
819+
if err := cfg.Validate(); err != nil {
820+
return nil, err
821+
}
822+
820823
tlsConfig := &tls.Config{
821824
InsecureSkipVerify: cfg.InsecureSkipVerify,
822825
MinVersion: uint16(cfg.MinVersion),
@@ -831,7 +834,11 @@ func NewTLSConfig(cfg *TLSConfig) (*tls.Config, error) {
831834

832835
// If a CA cert is provided then let's read it in so we can validate the
833836
// scrape target's certificate properly.
834-
if len(cfg.CAFile) > 0 {
837+
if len(cfg.CA) > 0 {
838+
if !updateRootCA(tlsConfig, []byte(cfg.CA)) {
839+
return nil, fmt.Errorf("unable to use inline CA cert")
840+
}
841+
} else if len(cfg.CAFile) > 0 {
835842
b, err := readCAFile(cfg.CAFile)
836843
if err != nil {
837844
return nil, err
@@ -844,12 +851,9 @@ func NewTLSConfig(cfg *TLSConfig) (*tls.Config, error) {
844851
if len(cfg.ServerName) > 0 {
845852
tlsConfig.ServerName = cfg.ServerName
846853
}
854+
847855
// If a client cert & key is provided then configure TLS config accordingly.
848-
if len(cfg.CertFile) > 0 && len(cfg.KeyFile) == 0 {
849-
return nil, fmt.Errorf("client cert file %q specified without client key file", cfg.CertFile)
850-
} else if len(cfg.KeyFile) > 0 && len(cfg.CertFile) == 0 {
851-
return nil, fmt.Errorf("client key file %q specified without client cert file", cfg.KeyFile)
852-
} else if len(cfg.CertFile) > 0 && len(cfg.KeyFile) > 0 {
856+
if cfg.usingClientCert() && cfg.usingClientKey() {
853857
// Verify that client cert and key are valid.
854858
if _, err := cfg.getClientCertificate(nil); err != nil {
855859
return nil, err
@@ -862,6 +866,12 @@ func NewTLSConfig(cfg *TLSConfig) (*tls.Config, error) {
862866

863867
// TLSConfig configures the options for TLS connections.
864868
type TLSConfig struct {
869+
// Text of the CA cert to use for the targets.
870+
CA string `yaml:"ca,omitempty" json:"ca,omitempty"`
871+
// Text of the client cert file for the targets.
872+
Cert string `yaml:"cert,omitempty" json:"cert,omitempty"`
873+
// Text of the client key file for the targets.
874+
Key Secret `yaml:"key,omitempty" json:"key,omitempty"`
865875
// The CA cert to use for the targets.
866876
CAFile string `yaml:"ca_file,omitempty" json:"ca_file,omitempty"`
867877
// The client cert file for the targets.
@@ -891,29 +901,77 @@ func (c *TLSConfig) SetDirectory(dir string) {
891901
// UnmarshalYAML implements the yaml.Unmarshaler interface.
892902
func (c *TLSConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
893903
type plain TLSConfig
894-
return unmarshal((*plain)(c))
904+
if err := unmarshal((*plain)(c)); err != nil {
905+
return err
906+
}
907+
return c.Validate()
895908
}
896909

897-
// readCertAndKey reads the cert and key files from the disk.
898-
func readCertAndKey(certFile, keyFile string) ([]byte, []byte, error) {
899-
certData, err := os.ReadFile(certFile)
900-
if err != nil {
901-
return nil, nil, err
910+
// Validate validates the TLSConfig to check that only one of the inlined or
911+
// file-based fields for the TLS CA, client certificate, and client key are
912+
// used.
913+
func (c *TLSConfig) Validate() error {
914+
if len(c.CA) > 0 && len(c.CAFile) > 0 {
915+
return fmt.Errorf("at most one of ca and ca_file must be configured")
916+
}
917+
if len(c.Cert) > 0 && len(c.CertFile) > 0 {
918+
return fmt.Errorf("at most one of cert and cert_file must be configured")
919+
}
920+
if len(c.Key) > 0 && len(c.KeyFile) > 0 {
921+
return fmt.Errorf("at most one of key and key_file must be configured")
902922
}
903923

904-
keyData, err := os.ReadFile(keyFile)
905-
if err != nil {
906-
return nil, nil, err
924+
if c.usingClientCert() && !c.usingClientKey() {
925+
return fmt.Errorf("exactly one of key or key_file must be configured when a client certificate is configured")
926+
} else if c.usingClientKey() && !c.usingClientCert() {
927+
return fmt.Errorf("exactly one of cert or cert_file must be configured when a client key is configured")
907928
}
908929

909-
return certData, keyData, nil
930+
return nil
931+
}
932+
933+
func (c *TLSConfig) usingClientCert() bool {
934+
return len(c.Cert) > 0 || len(c.CertFile) > 0
935+
}
936+
937+
func (c *TLSConfig) usingClientKey() bool {
938+
return len(c.Key) > 0 || len(c.KeyFile) > 0
939+
}
940+
941+
func (c *TLSConfig) roundTripperSettings() TLSRoundTripperSettings {
942+
return TLSRoundTripperSettings{
943+
CA: c.CA,
944+
CAFile: c.CAFile,
945+
Cert: c.Cert,
946+
CertFile: c.CertFile,
947+
Key: string(c.Key),
948+
KeyFile: c.KeyFile,
949+
}
910950
}
911951

912952
// getClientCertificate reads the pair of client cert and key from disk and returns a tls.Certificate.
913953
func (c *TLSConfig) getClientCertificate(_ *tls.CertificateRequestInfo) (*tls.Certificate, error) {
914-
certData, keyData, err := readCertAndKey(c.CertFile, c.KeyFile)
915-
if err != nil {
916-
return nil, fmt.Errorf("unable to read specified client cert (%s) & key (%s): %s", c.CertFile, c.KeyFile, err)
954+
var (
955+
certData, keyData []byte
956+
err error
957+
)
958+
959+
if c.CertFile != "" {
960+
certData, err = os.ReadFile(c.CertFile)
961+
if err != nil {
962+
return nil, fmt.Errorf("unable to read specified client cert (%s): %s", c.CertFile, err)
963+
}
964+
} else {
965+
certData = []byte(c.Cert)
966+
}
967+
968+
if c.KeyFile != "" {
969+
keyData, err = os.ReadFile(c.KeyFile)
970+
if err != nil {
971+
return nil, fmt.Errorf("unable to read specified client key (%s): %s", c.KeyFile, err)
972+
}
973+
} else {
974+
keyData = []byte(c.Key)
917975
}
918976

919977
cert, err := tls.X509KeyPair(certData, keyData)
@@ -946,30 +1004,32 @@ func updateRootCA(cfg *tls.Config, b []byte) bool {
9461004
// tlsRoundTripper is a RoundTripper that updates automatically its TLS
9471005
// configuration whenever the content of the CA file changes.
9481006
type tlsRoundTripper struct {
949-
caFile string
950-
certFile string
951-
keyFile string
1007+
settings TLSRoundTripperSettings
9521008

9531009
// newRT returns a new RoundTripper.
9541010
newRT func(*tls.Config) (http.RoundTripper, error)
9551011

9561012
mtx sync.RWMutex
9571013
rt http.RoundTripper
958-
hashCAFile []byte
959-
hashCertFile []byte
960-
hashKeyFile []byte
1014+
hashCAData []byte
1015+
hashCertData []byte
1016+
hashKeyData []byte
9611017
tlsConfig *tls.Config
9621018
}
9631019

1020+
type TLSRoundTripperSettings struct {
1021+
CA, CAFile string
1022+
Cert, CertFile string
1023+
Key, KeyFile string
1024+
}
1025+
9641026
func NewTLSRoundTripper(
9651027
cfg *tls.Config,
966-
caFile, certFile, keyFile string,
1028+
settings TLSRoundTripperSettings,
9671029
newRT func(*tls.Config) (http.RoundTripper, error),
9681030
) (http.RoundTripper, error) {
9691031
t := &tlsRoundTripper{
970-
caFile: caFile,
971-
certFile: certFile,
972-
keyFile: keyFile,
1032+
settings: settings,
9731033
newRT: newRT,
9741034
tlsConfig: cfg,
9751035
}
@@ -979,44 +1039,74 @@ func NewTLSRoundTripper(
9791039
return nil, err
9801040
}
9811041
t.rt = rt
982-
_, t.hashCAFile, t.hashCertFile, t.hashKeyFile, err = t.getTLSFilesWithHash()
1042+
_, t.hashCAData, t.hashCertData, t.hashKeyData, err = t.getTLSDataWithHash()
9831043
if err != nil {
9841044
return nil, err
9851045
}
9861046

9871047
return t, nil
9881048
}
9891049

990-
func (t *tlsRoundTripper) getTLSFilesWithHash() ([]byte, []byte, []byte, []byte, error) {
991-
b1, err := readCAFile(t.caFile)
992-
if err != nil {
993-
return nil, nil, nil, nil, err
1050+
func (t *tlsRoundTripper) getTLSDataWithHash() ([]byte, []byte, []byte, []byte, error) {
1051+
var (
1052+
caBytes, certBytes, keyBytes []byte
1053+
1054+
err error
1055+
)
1056+
1057+
if t.settings.CAFile != "" {
1058+
caBytes, err = os.ReadFile(t.settings.CAFile)
1059+
if err != nil {
1060+
return nil, nil, nil, nil, err
1061+
}
1062+
} else if t.settings.CA != "" {
1063+
caBytes = []byte(t.settings.CA)
1064+
}
1065+
1066+
if t.settings.CertFile != "" {
1067+
certBytes, err = os.ReadFile(t.settings.CertFile)
1068+
if err != nil {
1069+
return nil, nil, nil, nil, err
1070+
}
1071+
} else if t.settings.Cert != "" {
1072+
certBytes = []byte(t.settings.Cert)
9941073
}
995-
h1 := sha256.Sum256(b1)
9961074

997-
var h2, h3 [32]byte
998-
if t.certFile != "" {
999-
b2, b3, err := readCertAndKey(t.certFile, t.keyFile)
1075+
if t.settings.KeyFile != "" {
1076+
keyBytes, err = os.ReadFile(t.settings.KeyFile)
10001077
if err != nil {
10011078
return nil, nil, nil, nil, err
10021079
}
1003-
h2, h3 = sha256.Sum256(b2), sha256.Sum256(b3)
1080+
} else if t.settings.Key != "" {
1081+
keyBytes = []byte(t.settings.Key)
1082+
}
1083+
1084+
var caHash, certHash, keyHash [32]byte
1085+
1086+
if len(caBytes) > 0 {
1087+
caHash = sha256.Sum256(caBytes)
1088+
}
1089+
if len(certBytes) > 0 {
1090+
certHash = sha256.Sum256(certBytes)
1091+
}
1092+
if len(keyBytes) > 0 {
1093+
keyHash = sha256.Sum256(keyBytes)
10041094
}
10051095

1006-
return b1, h1[:], h2[:], h3[:], nil
1096+
return caBytes, caHash[:], certHash[:], keyHash[:], nil
10071097
}
10081098

10091099
// RoundTrip implements the http.RoundTrip interface.
10101100
func (t *tlsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
1011-
caData, caHash, certHash, keyHash, err := t.getTLSFilesWithHash()
1101+
caData, caHash, certHash, keyHash, err := t.getTLSDataWithHash()
10121102
if err != nil {
10131103
return nil, err
10141104
}
10151105

10161106
t.mtx.RLock()
1017-
equal := bytes.Equal(caHash[:], t.hashCAFile) &&
1018-
bytes.Equal(certHash[:], t.hashCertFile) &&
1019-
bytes.Equal(keyHash[:], t.hashKeyFile)
1107+
equal := bytes.Equal(caHash[:], t.hashCAData) &&
1108+
bytes.Equal(certHash[:], t.hashCertData) &&
1109+
bytes.Equal(keyHash[:], t.hashKeyData)
10201110
rt := t.rt
10211111
t.mtx.RUnlock()
10221112
if equal {
@@ -1029,7 +1119,7 @@ func (t *tlsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
10291119
// using GetClientCertificate.
10301120
tlsConfig := t.tlsConfig.Clone()
10311121
if !updateRootCA(tlsConfig, caData) {
1032-
return nil, fmt.Errorf("unable to use specified CA cert %s", t.caFile)
1122+
return nil, fmt.Errorf("unable to use specified CA cert %s", t.settings.CAFile)
10331123
}
10341124
rt, err = t.newRT(tlsConfig)
10351125
if err != nil {
@@ -1039,9 +1129,9 @@ func (t *tlsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
10391129

10401130
t.mtx.Lock()
10411131
t.rt = rt
1042-
t.hashCAFile = caHash[:]
1043-
t.hashCertFile = certHash[:]
1044-
t.hashKeyFile = keyHash[:]
1132+
t.hashCAData = caHash[:]
1133+
t.hashCertData = certHash[:]
1134+
t.hashKeyData = keyHash[:]
10451135
t.mtx.Unlock()
10461136

10471137
return rt.RoundTrip(req)

0 commit comments

Comments
 (0)