@@ -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
586585type 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.
819818func 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.
864868type 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.
892902func (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.
913953func (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.
9481006type 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+
9641026func 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.
10101100func (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