@@ -3,9 +3,12 @@ package tacquito
33import (
44 "crypto/tls"
55 "crypto/x509"
6+ "encoding/json"
67 "errors"
8+ "fmt"
79 "net"
810 "os"
11+ "path/filepath"
912 "time"
1013)
1114
@@ -45,21 +48,19 @@ func (l *TLSDeadlineListener) SetDeadline(t time.Time) error {
4548func GenTLSConfig (certFile , keyFile , CAFile string , requireMutualAuth bool ) (* tls.Config , error ) {
4649 config := & tls.Config {
4750 MinVersion : tls .VersionTLS13 , // Require TLS 1.3 as per the IETF draft
48- ClientAuth : tls .VerifyClientCertIfGiven , // Mutual authentication is optional
51+ ClientAuth : tls .VerifyClientCertIfGiven , // Mutual authentication is optional to start with, but is highly recommended
4952 }
5053
5154 if certFile == "" || keyFile == "" {
5255 return nil , errors .New ("TLS is enabled but certificate or key file is not provided" )
5356 }
5457
55- // Load server certificate and key
5658 cert , err := tls .LoadX509KeyPair (certFile , keyFile )
5759 if err != nil {
5860 return nil , err
5961 }
6062 config .Certificates = []tls.Certificate {cert }
6163
62- // Configure client certificate verification if CA file is provided
6364 if CAFile != "" {
6465 data , err := os .ReadFile (CAFile )
6566 if err != nil {
@@ -80,25 +81,24 @@ func GenTLSConfig(certFile, keyFile, CAFile string, requireMutualAuth bool) (*tl
8081}
8182
8283// createTLSConfig creates a TLS configuration based on the provided command-line flags
83- func GenClientTLSConfig (serverName , certFile , keyFile , CAFile string , skipVerification bool ) (* tls.Config , error ) {
84+ func GenClientTLSConfig (p * ParsedTLSConfig ) (* tls.Config , error ) {
8485 config := & tls.Config {
8586 MinVersion : tls .VersionTLS13 , // Require TLS 1.3 as per the IETF draft
86- ServerName : serverName ,
87- InsecureSkipVerify : skipVerification ,
87+ ServerName : p . ServerName ,
88+ InsecureSkipVerify : p . InsecureSkipVerify , // false by default
8889 }
8990
9091 // Client certificates are optional - only load them for mutual TLS when both are provided
91- if certFile != "" && keyFile != "" {
92- cert , err := tls .LoadX509KeyPair (certFile , keyFile )
92+ if p . CertFile != "" && p . KeyFile != "" {
93+ cert , err := tls .LoadX509KeyPair (p . CertFile , p . KeyFile )
9394 if err != nil {
9495 return nil , err
9596 }
9697 config .Certificates = []tls.Certificate {cert }
9798 }
9899
99- // Set RootCAs for server certificate verification if CA file is provided
100- if CAFile != "" {
101- data , err := os .ReadFile (CAFile )
100+ if p .CAFile != "" {
101+ data , err := os .ReadFile (p .CAFile )
102102 if err != nil {
103103 return nil , err
104104 }
@@ -111,3 +111,96 @@ func GenClientTLSConfig(serverName, certFile, keyFile, CAFile string, skipVerifi
111111
112112 return config , nil
113113}
114+
115+ // TLSConfig represents the TLS configuration that can be loaded from a JSON file
116+ // If a TLS config file is specified, TLS is automatically enabled
117+ type ParsedTLSConfig struct {
118+ // Certificate files
119+ CertFile string `json:"cert_file"`
120+ KeyFile string `json:"key_file"`
121+ CAFile string `json:"ca_file"`
122+
123+ // Server name for certificate validation
124+ ServerName string `json:"server_name"`
125+
126+ // Skip certificate verification (not recommended for production)
127+ InsecureSkipVerify bool `json:"insecure_skip_verify"`
128+ }
129+
130+ // LoadTLSConfig loads TLS configuration from a JSON file
131+ func LoadTLSConfig (filename string ) (* ParsedTLSConfig , error ) {
132+ if filename == "" {
133+ return nil , fmt .Errorf ("TLS config file path is empty" )
134+ }
135+
136+ if _ , err := os .Stat (filename ); os .IsNotExist (err ) {
137+ return nil , fmt .Errorf ("TLS config file does not exist: %s" , filename )
138+ }
139+
140+ data , err := os .ReadFile (filename )
141+ if err != nil {
142+ return nil , fmt .Errorf ("failed to read TLS config file: %w" , err )
143+ }
144+
145+ var config ParsedTLSConfig
146+ err = json .Unmarshal (data , & config )
147+ if err != nil {
148+ return nil , fmt .Errorf ("failed to parse JSON TLS config: %w" , err )
149+ }
150+
151+ if err := config .Validate (); err != nil {
152+ return nil , fmt .Errorf ("invalid TLS config: %w" , err )
153+ }
154+
155+ return & config , nil
156+ }
157+
158+ // Validate checks if the TLS configuration is valid
159+ func (c * ParsedTLSConfig ) Validate () error {
160+ var err error
161+
162+ if c .CertFile , err = resolvePath (c .CertFile , "TLS certificate" ); err != nil {
163+ return err
164+ }
165+
166+ if c .KeyFile , err = resolvePath (c .KeyFile , "TLS key" ); err != nil {
167+ return err
168+ }
169+
170+ if c .CAFile , err = resolvePath (c .CAFile , "TLS CA" ); err != nil {
171+ return err
172+ }
173+
174+ // If client cert is specified, key must also be specified and vice versa
175+ if c .CertFile != "" && c .KeyFile == "" {
176+ return fmt .Errorf ("TLS key file must be specified when certificate file is provided" )
177+ }
178+ if c .KeyFile != "" && c .CertFile == "" {
179+ return fmt .Errorf ("TLS certificate file must be specified when key file is provided" )
180+ }
181+
182+ return nil
183+ }
184+
185+ // resolvePath converts relative paths to absolute paths and checks if the file exists
186+ // Returns the absolute path and an error if the file doesn't exist or path conversion fails
187+ func resolvePath (path , fileType string ) (string , error ) {
188+ if path == "" {
189+ return "" , nil
190+ }
191+
192+ // Convert to absolute path (handles relative paths like ./file or ../file)
193+ absPath , err := filepath .Abs (path )
194+ if err != nil {
195+ return "" , fmt .Errorf ("failed to convert %s file path '%s' to absolute path: %w" , fileType , path , err )
196+ }
197+
198+ if _ , err := os .Stat (absPath ); err != nil {
199+ if os .IsNotExist (err ) {
200+ return "" , fmt .Errorf ("%s file does not exist: %s" , fileType , absPath )
201+ }
202+ return "" , err
203+ }
204+
205+ return absPath , nil
206+ }
0 commit comments