diff --git a/sztp-agent/cmd/daemon.go b/sztp-agent/cmd/daemon.go index 276a22f..92d49ef 100644 --- a/sztp-agent/cmd/daemon.go +++ b/sztp-agent/cmd/daemon.go @@ -59,7 +59,7 @@ func Daemon() *cobra.Command { return fmt.Errorf("must not be folder: %q", filePath) } } - client := secureagent.NewHttpClient(bootstrapTrustAnchorCert, deviceEndEntityCert, devicePrivateKey) + client := secureagent.NewHTTPClient(bootstrapTrustAnchorCert, deviceEndEntityCert, devicePrivateKey) a := secureagent.NewAgent(bootstrapURL, serialNumber, dhcpLeaseFile, devicePassword, devicePrivateKey, deviceEndEntityCert, bootstrapTrustAnchorCert, &client) return a.RunCommandDaemon() }, diff --git a/sztp-agent/cmd/disable.go b/sztp-agent/cmd/disable.go index fd4e285..157776d 100644 --- a/sztp-agent/cmd/disable.go +++ b/sztp-agent/cmd/disable.go @@ -34,7 +34,7 @@ func Disable() *cobra.Command { Use: "disable", Short: "Run the disable command", RunE: func(_ *cobra.Command, _ []string) error { - client := secureagent.NewHttpClient(bootstrapTrustAnchorCert, deviceEndEntityCert, devicePrivateKey) + client := secureagent.NewHTTPClient(bootstrapTrustAnchorCert, deviceEndEntityCert, devicePrivateKey) a := secureagent.NewAgent(bootstrapURL, serialNumber, dhcpLeaseFile, devicePassword, devicePrivateKey, deviceEndEntityCert, bootstrapTrustAnchorCert, &client) return a.RunCommandDisable() }, diff --git a/sztp-agent/cmd/enable.go b/sztp-agent/cmd/enable.go index 7bf1299..49f53b4 100644 --- a/sztp-agent/cmd/enable.go +++ b/sztp-agent/cmd/enable.go @@ -34,7 +34,7 @@ func Enable() *cobra.Command { Use: "enable", Short: "Run the enable command", RunE: func(_ *cobra.Command, _ []string) error { - client := secureagent.NewHttpClient(bootstrapTrustAnchorCert, deviceEndEntityCert, devicePrivateKey) + client := secureagent.NewHTTPClient(bootstrapTrustAnchorCert, deviceEndEntityCert, devicePrivateKey) a := secureagent.NewAgent(bootstrapURL, serialNumber, dhcpLeaseFile, devicePassword, devicePrivateKey, deviceEndEntityCert, bootstrapTrustAnchorCert, &client) return a.RunCommandEnable() }, diff --git a/sztp-agent/cmd/run.go b/sztp-agent/cmd/run.go index df5407f..99a9b1f 100644 --- a/sztp-agent/cmd/run.go +++ b/sztp-agent/cmd/run.go @@ -59,7 +59,7 @@ func Run() *cobra.Command { return fmt.Errorf("must not be folder: %q", filePath) } } - client := secureagent.NewHttpClient(bootstrapTrustAnchorCert, deviceEndEntityCert, devicePrivateKey) + client := secureagent.NewHTTPClient(bootstrapTrustAnchorCert, deviceEndEntityCert, devicePrivateKey) a := secureagent.NewAgent(bootstrapURL, serialNumber, dhcpLeaseFile, devicePassword, devicePrivateKey, deviceEndEntityCert, bootstrapTrustAnchorCert, &client) return a.RunCommand() }, diff --git a/sztp-agent/cmd/status.go b/sztp-agent/cmd/status.go index bff8842..dbc80df 100644 --- a/sztp-agent/cmd/status.go +++ b/sztp-agent/cmd/status.go @@ -34,7 +34,7 @@ func Status() *cobra.Command { Use: "status", Short: "Run the status command", RunE: func(_ *cobra.Command, _ []string) error { - client := secureagent.NewHttpClient(bootstrapTrustAnchorCert, deviceEndEntityCert, devicePrivateKey) + client := secureagent.NewHTTPClient(bootstrapTrustAnchorCert, deviceEndEntityCert, devicePrivateKey) a := secureagent.NewAgent(bootstrapURL, serialNumber, dhcpLeaseFile, devicePassword, devicePrivateKey, deviceEndEntityCert, bootstrapTrustAnchorCert, &client) return a.RunCommandStatus() }, diff --git a/sztp-agent/pkg/secureagent/agent.go b/sztp-agent/pkg/secureagent/agent.go index 5230c76..6797046 100644 --- a/sztp-agent/pkg/secureagent/agent.go +++ b/sztp-agent/pkg/secureagent/agent.go @@ -10,10 +10,7 @@ Copyright (C) 2022 Red Hat. package secureagent import ( - "crypto/tls" - "crypto/x509" "net/http" - "os" ) const ( @@ -184,25 +181,3 @@ func (a *Agent) SetContentTypeReq(ct string) { func (a *Agent) SetProgressJSON(p ProgressJSON) { a.ProgressJSON = p } - -func NewHttpClient(bootstrapTrustAnchorCert string, deviceEndEntityCert string, devicePrivateKey string) http.Client { - caCert, _ := os.ReadFile(bootstrapTrustAnchorCert) - caCertPool := x509.NewCertPool() - caCertPool.AppendCertsFromPEM(caCert) - cert, _ := tls.LoadX509KeyPair(deviceEndEntityCert, devicePrivateKey) - client := http.Client{ - CheckRedirect: func(r *http.Request, _ []*http.Request) error { - r.URL.Opaque = r.URL.Path - return nil - }, - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - //nolint:gosec - InsecureSkipVerify: true, // TODO: remove skip verify - RootCAs: caCertPool, - Certificates: []tls.Certificate{cert}, - }, - }, - } - return client -} diff --git a/sztp-agent/pkg/secureagent/configuration.go b/sztp-agent/pkg/secureagent/configuration.go new file mode 100644 index 0000000..1a6f013 --- /dev/null +++ b/sztp-agent/pkg/secureagent/configuration.go @@ -0,0 +1,94 @@ +package secureagent + +import ( + "encoding/base64" + "log" + "os" + "os/exec" +) + +func (a *Agent) copyConfigurationFile() error { + log.Println("[INFO] Starting the Copy Configuration.") + _ = a.doReportProgress(ProgressTypeConfigInitiated, "Configuration Initiated") + // Copy the configuration file to the device + file, err := os.Create(ARTIFACTS_PATH + a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.InfoTimestampReference + "-config") + if err != nil { + log.Println("[ERROR] creating the configuration file", err.Error()) + return err + } + defer func() { + if err := file.Close(); err != nil { + log.Println("[ERROR] Error when closing:", err) + } + }() + + plainTest, _ := base64.StdEncoding.DecodeString(a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.Configuration) + _, err = file.WriteString(string(plainTest)) + if err != nil { + log.Println("[ERROR] writing the configuration file", err.Error()) + return err + } + // nolint:gosec + err = os.Chmod(ARTIFACTS_PATH+a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.InfoTimestampReference+"-config", 0744) + if err != nil { + log.Println("[ERROR] changing the configuration file permission", err.Error()) + return err + } + log.Println("[INFO] Configuration file copied successfully") + _ = a.doReportProgress(ProgressTypeConfigComplete, "Configuration Complete") + return nil +} + +func (a *Agent) launchScriptsConfiguration(typeOf string) error { + var script, scriptName string + var reportStart, reportEnd ProgressType + switch typeOf { + case "post": + script = a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.PostConfigurationScript + scriptName = "post" + reportStart = ProgressTypePostScriptInitiated + reportEnd = ProgressTypePostScriptComplete + default: // pre or default + script = a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.PreConfigurationScript + scriptName = "pre" + reportStart = ProgressTypePreScriptInitiated + reportEnd = ProgressTypePreScriptComplete + } + log.Println("[INFO] Starting the " + scriptName + "-configuration.") + _ = a.doReportProgress(reportStart, "Report starting") + // nolint:gosec + file, err := os.Create(ARTIFACTS_PATH + a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.InfoTimestampReference + scriptName + "configuration.sh") + if err != nil { + log.Println("[ERROR] creating the "+scriptName+"-configuration script", err.Error()) + return err + } + defer func() { + if err := file.Close(); err != nil { + log.Println("[ERROR] Error when closing:", err) + } + }() + + plainTest, _ := base64.StdEncoding.DecodeString(script) + _, err = file.WriteString(string(plainTest)) + if err != nil { + log.Println("[ERROR] writing the "+scriptName+"-configuration script", err.Error()) + return err + } + // nolint:gosec + err = os.Chmod(ARTIFACTS_PATH+a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.InfoTimestampReference+scriptName+"configuration.sh", 0755) + if err != nil { + log.Println("[ERROR] changing the "+scriptName+"-configuration script permission", err.Error()) + return err + } + log.Println("[INFO] " + scriptName + "-configuration script created successfully") + cmd := exec.Command("/bin/sh", ARTIFACTS_PATH+a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.InfoTimestampReference+scriptName+"configuration.sh") //nolint:gosec + out, err := cmd.Output() + if err != nil { + log.Println("[ERROR] running the "+scriptName+"-configuration script", err.Error()) + return err + } + log.Println(string(out)) // remove it + _ = a.doReportProgress(reportEnd, "Report end") + log.Println("[INFO] " + scriptName + "-Configuration script executed successfully") + return nil +} diff --git a/sztp-agent/pkg/secureagent/configuration_test.go b/sztp-agent/pkg/secureagent/configuration_test.go new file mode 100644 index 0000000..3aa2581 --- /dev/null +++ b/sztp-agent/pkg/secureagent/configuration_test.go @@ -0,0 +1,380 @@ +package secureagent + +import ( + "net/http" + "testing" +) + +// nolint:funlen +func TestAgent_copyConfigurationFile(t *testing.T) { + type fields struct { + BootstrapURL string + SerialNumber string + DevicePassword string + DevicePrivateKey string + DeviceEndEntityCert string + BootstrapTrustAnchorCert string + ContentTypeReq string + InputJSONContent string + DhcpLeaseFile string + ProgressJSON ProgressJSON + BootstrapServerOnboardingInfo BootstrapServerOnboardingInfo + BootstrapServerRedirectInfo BootstrapServerRedirectInfo + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "Error Writing file", + fields: fields{ + BootstrapURL: "", + SerialNumber: "", + DevicePassword: "", + DevicePrivateKey: "", + DeviceEndEntityCert: "", + BootstrapTrustAnchorCert: "", + ContentTypeReq: "", + InputJSONContent: "", + DhcpLeaseFile: "", + ProgressJSON: ProgressJSON{}, + BootstrapServerOnboardingInfo: BootstrapServerOnboardingInfo{ + IetfSztpConveyedInfoOnboardingInformation: struct { + InfoTimestampReference string + BootImage struct { + DownloadURI []string `json:"download-uri"` + ImageVerification []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + } `json:"image-verification"` + } `json:"boot-image"` + PreConfigurationScript string `json:"pre-configuration-script"` + ConfigurationHandling string `json:"configuration-handling"` + Configuration string `json:"configuration"` + PostConfigurationScript string `json:"post-configuration-script"` + }{ + InfoTimestampReference: " ../ ", + BootImage: struct { + DownloadURI []string `json:"download-uri"` + ImageVerification []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + } `json:"image-verification"` + }{ + DownloadURI: []string{}, + ImageVerification: []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + }{{ + HashAlgorithm: "md5", + HashValue: "d41d8cd98f00b204e9800998ecf8427e", + }}, + }, + PreConfigurationScript: "", + ConfigurationHandling: "", + Configuration: "", + PostConfigurationScript: "", + }, + }, + BootstrapServerRedirectInfo: BootstrapServerRedirectInfo{}, + }, + wantErr: true, + }, + { + name: "OK Case", + fields: fields{ + BootstrapURL: "", + SerialNumber: "", + DevicePassword: "", + DevicePrivateKey: "", + DeviceEndEntityCert: "", + BootstrapTrustAnchorCert: "", + ContentTypeReq: "", + InputJSONContent: "", + DhcpLeaseFile: "", + ProgressJSON: ProgressJSON{}, + BootstrapServerOnboardingInfo: BootstrapServerOnboardingInfo{ + IetfSztpConveyedInfoOnboardingInformation: struct { + InfoTimestampReference string + BootImage struct { + DownloadURI []string `json:"download-uri"` + ImageVerification []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + } `json:"image-verification"` + } `json:"boot-image"` + PreConfigurationScript string `json:"pre-configuration-script"` + ConfigurationHandling string `json:"configuration-handling"` + Configuration string `json:"configuration"` + PostConfigurationScript string `json:"post-configuration-script"` + }{ + InfoTimestampReference: "PATHOK", + BootImage: struct { + DownloadURI []string `json:"download-uri"` + ImageVerification []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + } `json:"image-verification"` + }{ + DownloadURI: []string{}, + ImageVerification: []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + }{{ + HashAlgorithm: "md5", + HashValue: "d41d8cd98f00b204e9800998ecf8427e", + }}, + }, + PreConfigurationScript: "", + ConfigurationHandling: "", + Configuration: "", + PostConfigurationScript: "", + }, + }, + BootstrapServerRedirectInfo: BootstrapServerRedirectInfo{}, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &Agent{ + BootstrapURL: tt.fields.BootstrapURL, + SerialNumber: tt.fields.SerialNumber, + DevicePassword: tt.fields.DevicePassword, + DevicePrivateKey: tt.fields.DevicePrivateKey, + DeviceEndEntityCert: tt.fields.DeviceEndEntityCert, + BootstrapTrustAnchorCert: tt.fields.BootstrapTrustAnchorCert, + ContentTypeReq: tt.fields.ContentTypeReq, + InputJSONContent: tt.fields.InputJSONContent, + DhcpLeaseFile: tt.fields.DhcpLeaseFile, + ProgressJSON: tt.fields.ProgressJSON, + BootstrapServerOnboardingInfo: tt.fields.BootstrapServerOnboardingInfo, + BootstrapServerRedirectInfo: tt.fields.BootstrapServerRedirectInfo, + HttpClient: &http.Client{}, + } + if err := a.copyConfigurationFile(); (err != nil) != tt.wantErr { + t.Errorf("copyConfigurationFile() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +// nolint:funlen +func TestAgent_launchScriptsConfiguration(t *testing.T) { + type fields struct { + BootstrapURL string + SerialNumber string + DevicePassword string + DevicePrivateKey string + DeviceEndEntityCert string + BootstrapTrustAnchorCert string + ContentTypeReq string + InputJSONContent string + DhcpLeaseFile string + ProgressJSON ProgressJSON + BootstrapServerOnboardingInfo BootstrapServerOnboardingInfo + BootstrapServerRedirectInfo BootstrapServerRedirectInfo + } + type args struct { + typeOf string + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + { + args: args{typeOf: "default or pre"}, + name: "OK Case with PRE", + fields: fields{ + BootstrapURL: "", + SerialNumber: "", + DevicePassword: "", + DevicePrivateKey: "", + DeviceEndEntityCert: "", + BootstrapTrustAnchorCert: "", + ContentTypeReq: "", + InputJSONContent: "", + DhcpLeaseFile: "", + ProgressJSON: ProgressJSON{}, + BootstrapServerOnboardingInfo: BootstrapServerOnboardingInfo{ + IetfSztpConveyedInfoOnboardingInformation: struct { + InfoTimestampReference string + BootImage struct { + DownloadURI []string `json:"download-uri"` + ImageVerification []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + } `json:"image-verification"` + } `json:"boot-image"` + PreConfigurationScript string `json:"pre-configuration-script"` + ConfigurationHandling string `json:"configuration-handling"` + Configuration string `json:"configuration"` + PostConfigurationScript string `json:"post-configuration-script"` + }{ + InfoTimestampReference: "PATHOK", + BootImage: struct { + DownloadURI []string `json:"download-uri"` + ImageVerification []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + } `json:"image-verification"` + }{ + DownloadURI: []string{}, + ImageVerification: []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + }{{ + HashAlgorithm: "md5", + HashValue: "d41d8cd98f00b204e9800998ecf8427e", + }}, + }, + PreConfigurationScript: "", + ConfigurationHandling: "", + Configuration: "", + PostConfigurationScript: "", + }, + }, + BootstrapServerRedirectInfo: BootstrapServerRedirectInfo{}, + }, + wantErr: false, + }, + { + args: args{typeOf: "post"}, + name: "OK Case with POST", + fields: fields{ + BootstrapURL: "", + SerialNumber: "", + DevicePassword: "", + DevicePrivateKey: "", + DeviceEndEntityCert: "", + BootstrapTrustAnchorCert: "", + ContentTypeReq: "", + InputJSONContent: "", + DhcpLeaseFile: "", + ProgressJSON: ProgressJSON{}, + BootstrapServerOnboardingInfo: BootstrapServerOnboardingInfo{ + IetfSztpConveyedInfoOnboardingInformation: struct { + InfoTimestampReference string + BootImage struct { + DownloadURI []string `json:"download-uri"` + ImageVerification []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + } `json:"image-verification"` + } `json:"boot-image"` + PreConfigurationScript string `json:"pre-configuration-script"` + ConfigurationHandling string `json:"configuration-handling"` + Configuration string `json:"configuration"` + PostConfigurationScript string `json:"post-configuration-script"` + }{ + InfoTimestampReference: "PATHOK", + BootImage: struct { + DownloadURI []string `json:"download-uri"` + ImageVerification []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + } `json:"image-verification"` + }{ + DownloadURI: []string{}, + ImageVerification: []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + }{{ + HashAlgorithm: "md5", + HashValue: "d41d8cd98f00b204e9800998ecf8427e", + }}, + }, + PreConfigurationScript: "", + ConfigurationHandling: "", + Configuration: "", + PostConfigurationScript: "", + }, + }, + BootstrapServerRedirectInfo: BootstrapServerRedirectInfo{}, + }, + wantErr: false, + }, + { + args: args{typeOf: "post"}, + name: "OK Case with POST", + fields: fields{ + BootstrapURL: "", + SerialNumber: "", + DevicePassword: "", + DevicePrivateKey: "", + DeviceEndEntityCert: "", + BootstrapTrustAnchorCert: "", + ContentTypeReq: "", + InputJSONContent: "", + DhcpLeaseFile: "", + ProgressJSON: ProgressJSON{}, + BootstrapServerOnboardingInfo: BootstrapServerOnboardingInfo{ + IetfSztpConveyedInfoOnboardingInformation: struct { + InfoTimestampReference string + BootImage struct { + DownloadURI []string `json:"download-uri"` + ImageVerification []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + } `json:"image-verification"` + } `json:"boot-image"` + PreConfigurationScript string `json:"pre-configuration-script"` + ConfigurationHandling string `json:"configuration-handling"` + Configuration string `json:"configuration"` + PostConfigurationScript string `json:"post-configuration-script"` + }{ + InfoTimestampReference: " ../", + BootImage: struct { + DownloadURI []string `json:"download-uri"` + ImageVerification []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + } `json:"image-verification"` + }{ + DownloadURI: []string{}, + ImageVerification: []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + }{{ + HashAlgorithm: "md5", + HashValue: "d41d8cd98f00b204e9800998ecf8427e", + }}, + }, + PreConfigurationScript: "", + ConfigurationHandling: "", + Configuration: "", + PostConfigurationScript: "", + }, + }, + BootstrapServerRedirectInfo: BootstrapServerRedirectInfo{}, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &Agent{ + BootstrapURL: tt.fields.BootstrapURL, + SerialNumber: tt.fields.SerialNumber, + DevicePassword: tt.fields.DevicePassword, + DevicePrivateKey: tt.fields.DevicePrivateKey, + DeviceEndEntityCert: tt.fields.DeviceEndEntityCert, + BootstrapTrustAnchorCert: tt.fields.BootstrapTrustAnchorCert, + ContentTypeReq: tt.fields.ContentTypeReq, + InputJSONContent: tt.fields.InputJSONContent, + DhcpLeaseFile: tt.fields.DhcpLeaseFile, + ProgressJSON: tt.fields.ProgressJSON, + BootstrapServerOnboardingInfo: tt.fields.BootstrapServerOnboardingInfo, + BootstrapServerRedirectInfo: tt.fields.BootstrapServerRedirectInfo, + HttpClient: &http.Client{}, + } + if err := a.launchScriptsConfiguration(tt.args.typeOf); (err != nil) != tt.wantErr { + t.Errorf("launchScriptsConfiguration() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/sztp-agent/pkg/secureagent/daemon.go b/sztp-agent/pkg/secureagent/daemon.go index a7778ea..2064d90 100644 --- a/sztp-agent/pkg/secureagent/daemon.go +++ b/sztp-agent/pkg/secureagent/daemon.go @@ -15,15 +15,9 @@ import ( "encoding/json" "errors" "fmt" - "io" "log" "net/url" - "os" - "os/exec" - "path/filepath" "reflect" - "strconv" - "strings" "time" "github.com/github/smimesign/ietf-cms/protocol" @@ -121,6 +115,12 @@ func (a *Agent) doHandleBootstrapRedirect() error { addr := a.BootstrapServerRedirectInfo.IetfSztpConveyedInfoRedirectInformation.BootstrapServer[0].Address port := a.BootstrapServerRedirectInfo.IetfSztpConveyedInfoRedirectInformation.BootstrapServer[0].Port + if addr == "" { + return errors.New("invalid redirect address") + } + if port <= 0 { + return errors.New("invalid port") + } // Change URL to point to new redirect IP and PORT u, err := url.Parse(a.GetBootstrapURL()) if err != nil { @@ -177,156 +177,3 @@ func (a *Agent) doRequestBootstrapServerOnboardingInfo() error { } return errri } - -//nolint:funlen -func (a *Agent) downloadAndValidateImage() error { - log.Printf("[INFO] Starting the Download Image: %v", a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.BootImage.DownloadURI) - _ = a.doReportProgress(ProgressTypeBootImageInitiated, "BootImage Initiated") - // Download the image from DownloadURI and save it to a file - a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.InfoTimestampReference = fmt.Sprintf("%8d", time.Now().Unix()) - for i, item := range a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.BootImage.DownloadURI { - // TODO: maybe need to file download to a function in util.go - log.Printf("[INFO] Downloading Image %v", item) - // Create a empty file - file, err := os.Create(ARTIFACTS_PATH + a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.InfoTimestampReference + filepath.Base(item)) - if err != nil { - return err - } - - response, err := a.HttpClient.Get(item) - if err != nil { - return err - } - - sizeorigin, _ := strconv.Atoi(response.Header.Get("Content-Length")) - downloadSize := int64(sizeorigin) - log.Printf("[INFO] Downloading the image with size: %v", downloadSize) - - if response.StatusCode != 200 { - return errors.New("received non 200 response code") - } - size, err := io.Copy(file, response.Body) - if err != nil { - return err - } - defer func() { - if err := file.Close(); err != nil { - log.Println("[ERROR] Error when closing:", err) - } - }() - defer func() { - if err := response.Body.Close(); err != nil { - log.Println("[ERROR] Error when closing:", err) - } - }() - - log.Printf("[INFO] Downloaded file: %s with size: %d", ARTIFACTS_PATH+a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.InfoTimestampReference+filepath.Base(item), size) - log.Println("[INFO] Verify the file checksum: ", ARTIFACTS_PATH+a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.InfoTimestampReference+filepath.Base(item)) - switch a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.BootImage.ImageVerification[i].HashAlgorithm { - case "ietf-sztp-conveyed-info:sha-256": - filePath := ARTIFACTS_PATH + a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.InfoTimestampReference + filepath.Base(item) - checksum, err := calculateSHA256File(filePath) - original := strings.ReplaceAll(a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.BootImage.ImageVerification[i].HashValue, ":", "") - if err != nil { - log.Println("[ERROR] Could not calculate checksum", err) - } - log.Println("calculated: " + checksum) - log.Println("expected : " + original) - if checksum != original { - return errors.New("checksum mismatch") - } - log.Println("[INFO] Checksum verified successfully") - _ = a.doReportProgress(ProgressTypeBootImageComplete, "BootImage Complete") - return nil - default: - return errors.New("unsupported hash algorithm") - } - } - return nil -} - -func (a *Agent) copyConfigurationFile() error { - log.Println("[INFO] Starting the Copy Configuration.") - _ = a.doReportProgress(ProgressTypeConfigInitiated, "Configuration Initiated") - // Copy the configuration file to the device - file, err := os.Create(ARTIFACTS_PATH + a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.InfoTimestampReference + "-config") - if err != nil { - log.Println("[ERROR] creating the configuration file", err.Error()) - return err - } - defer func() { - if err := file.Close(); err != nil { - log.Println("[ERROR] Error when closing:", err) - } - }() - - plainTest, _ := base64.StdEncoding.DecodeString(a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.Configuration) - _, err = file.WriteString(string(plainTest)) - if err != nil { - log.Println("[ERROR] writing the configuration file", err.Error()) - return err - } - // nolint:gosec - err = os.Chmod(ARTIFACTS_PATH+a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.InfoTimestampReference+"-config", 0744) - if err != nil { - log.Println("[ERROR] changing the configuration file permission", err.Error()) - return err - } - log.Println("[INFO] Configuration file copied successfully") - _ = a.doReportProgress(ProgressTypeConfigComplete, "Configuration Complete") - return nil -} - -func (a *Agent) launchScriptsConfiguration(typeOf string) error { - var script, scriptName string - var reportStart, reportEnd ProgressType - switch typeOf { - case "post": - script = a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.PostConfigurationScript - scriptName = "post" - reportStart = ProgressTypePostScriptInitiated - reportEnd = ProgressTypePostScriptComplete - default: // pre or default - script = a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.PreConfigurationScript - scriptName = "pre" - reportStart = ProgressTypePreScriptInitiated - reportEnd = ProgressTypePreScriptComplete - } - log.Println("[INFO] Starting the " + scriptName + "-configuration.") - _ = a.doReportProgress(reportStart, "Report starting") - // nolint:gosec - file, err := os.Create(ARTIFACTS_PATH + a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.InfoTimestampReference + scriptName + "configuration.sh") - if err != nil { - log.Println("[ERROR] creating the "+scriptName+"-configuration script", err.Error()) - return err - } - defer func() { - if err := file.Close(); err != nil { - log.Println("[ERROR] Error when closing:", err) - } - }() - - plainTest, _ := base64.StdEncoding.DecodeString(script) - _, err = file.WriteString(string(plainTest)) - if err != nil { - log.Println("[ERROR] writing the "+scriptName+"-configuration script", err.Error()) - return err - } - // nolint:gosec - err = os.Chmod(ARTIFACTS_PATH+a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.InfoTimestampReference+scriptName+"configuration.sh", 0755) - if err != nil { - log.Println("[ERROR] changing the "+scriptName+"-configuration script permission", err.Error()) - return err - } - log.Println("[INFO] " + scriptName + "-configuration script created successfully") - cmd := exec.Command("/bin/sh", ARTIFACTS_PATH+a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.InfoTimestampReference+scriptName+"configuration.sh") //nolint:gosec - out, err := cmd.Output() - if err != nil { - log.Println("[ERROR] running the "+scriptName+"-configuration script", err.Error()) - return err - } - log.Println(string(out)) // remove it - _ = a.doReportProgress(reportEnd, "Report end") - log.Println("[INFO] " + scriptName + "-Configuration script executed successfully") - return nil -} diff --git a/sztp-agent/pkg/secureagent/daemon_test.go b/sztp-agent/pkg/secureagent/daemon_test.go index ea784aa..c9341c0 100644 --- a/sztp-agent/pkg/secureagent/daemon_test.go +++ b/sztp-agent/pkg/secureagent/daemon_test.go @@ -164,6 +164,83 @@ func deleteTempTestFile(file string) { } } +func TestAgent_doHandleBootstrapRedirect(t *testing.T) { + type fields struct { + InputBootstrapURL string + BootstrapServerRedirectInfo BootstrapServerRedirectInfo + } + tests := []struct { + name string + fields fields + wantErr bool + expectedBootstrapURL string + }{ + { + name: "Fail test with invalid address", + fields: fields{ + InputBootstrapURL: "", + BootstrapServerRedirectInfo: BootstrapServerRedirectInfo{ + IetfSztpConveyedInfoRedirectInformation: struct { + BootstrapServer []struct { + Address string `json:"address"` + Port int `json:"port"` + TrustAnchor string `json:"trust-anchor"` + } `json:"bootstrap-server"` + }{ + BootstrapServer: []struct { + Address string `json:"address"` + Port int `json:"port"` + TrustAnchor string `json:"trust-anchor"` + }{{ + Address: "", + Port: 0, + }}, + }, + }, + }, + wantErr: true, + expectedBootstrapURL: "", + }, + { + name: "Fail test with invalid port", + fields: fields{ + InputBootstrapURL: "", + BootstrapServerRedirectInfo: BootstrapServerRedirectInfo{ + IetfSztpConveyedInfoRedirectInformation: struct { + BootstrapServer []struct { + Address string `json:"address"` + Port int `json:"port"` + TrustAnchor string `json:"trust-anchor"` + } `json:"bootstrap-server"` + }{ + BootstrapServer: []struct { + Address string `json:"address"` + Port int `json:"port"` + TrustAnchor string `json:"trust-anchor"` + }{{ + Address: "8.8.8.8", + Port: -1000, + }}, + }, + }, + }, + wantErr: true, + expectedBootstrapURL: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &Agent{ + BootstrapURL: tt.fields.InputBootstrapURL, + BootstrapServerRedirectInfo: tt.fields.BootstrapServerRedirectInfo, + } + if err := a.doHandleBootstrapRedirect(); (err != nil) != tt.wantErr { + t.Errorf("doHandleBootstrapRedirect() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + //nolint:funlen func TestAgent_doReqBootstrap(t *testing.T) { var output []byte @@ -349,689 +426,3 @@ func TestAgent_doReqBootstrap(t *testing.T) { }) } } - -//nolint:funlen -func TestAgent_downloadAndValidateImage(t *testing.T) { - svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == "/imageOK" { - w.WriteHeader(200) - } else { - w.WriteHeader(400) - } - })) - defer svr.Close() - type fields struct { - BootstrapURL string - SerialNumber string - DevicePassword string - DevicePrivateKey string - DeviceEndEntityCert string - BootstrapTrustAnchorCert string - ContentTypeReq string - InputJSONContent string - DhcpLeaseFile string - ProgressJSON ProgressJSON - BootstrapServerOnboardingInfo BootstrapServerOnboardingInfo - BootstrapServerRedirectInfo BootstrapServerRedirectInfo - } - tests := []struct { - name string - fields fields - wantErr bool - }{ - { - name: "error writing file", - fields: fields{ - BootstrapURL: "", - SerialNumber: "", - DevicePassword: "", - DevicePrivateKey: "", - DeviceEndEntityCert: "", - BootstrapTrustAnchorCert: "", - ContentTypeReq: "", - InputJSONContent: "", - DhcpLeaseFile: "", - ProgressJSON: ProgressJSON{}, - BootstrapServerOnboardingInfo: BootstrapServerOnboardingInfo{ - IetfSztpConveyedInfoOnboardingInformation: struct { - InfoTimestampReference string - BootImage struct { - DownloadURI []string `json:"download-uri"` - ImageVerification []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - } `json:"image-verification"` - } `json:"boot-image"` - PreConfigurationScript string `json:"pre-configuration-script"` - ConfigurationHandling string `json:"configuration-handling"` - Configuration string `json:"configuration"` - PostConfigurationScript string `json:"post-configuration-script"` - }{ - InfoTimestampReference: "", - BootImage: struct { - DownloadURI []string `json:"download-uri"` - ImageVerification []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - } `json:"image-verification"` - }{ - DownloadURI: []string{"WrongURL"}, - ImageVerification: nil, - }, - PreConfigurationScript: "", - ConfigurationHandling: "", - Configuration: "", - PostConfigurationScript: "", - }, - }, - BootstrapServerRedirectInfo: BootstrapServerRedirectInfo{}, - }, - wantErr: true, - }, - { - name: "Image wrong", - fields: fields{ - BootstrapURL: "", - SerialNumber: "", - DevicePassword: "", - DevicePrivateKey: "", - DeviceEndEntityCert: "", - BootstrapTrustAnchorCert: "", - ContentTypeReq: "", - InputJSONContent: "", - DhcpLeaseFile: "", - ProgressJSON: ProgressJSON{}, - BootstrapServerOnboardingInfo: BootstrapServerOnboardingInfo{ - IetfSztpConveyedInfoOnboardingInformation: struct { - InfoTimestampReference string - BootImage struct { - DownloadURI []string `json:"download-uri"` - ImageVerification []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - } `json:"image-verification"` - } `json:"boot-image"` - PreConfigurationScript string `json:"pre-configuration-script"` - ConfigurationHandling string `json:"configuration-handling"` - Configuration string `json:"configuration"` - PostConfigurationScript string `json:"post-configuration-script"` - }{ - InfoTimestampReference: "TIMESTAMP", - BootImage: struct { - DownloadURI []string `json:"download-uri"` - ImageVerification []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - } `json:"image-verification"` - }{ - DownloadURI: []string{svr.URL + "/imageWRONG"}, - ImageVerification: nil, - }, - PreConfigurationScript: "", - ConfigurationHandling: "", - Configuration: "", - PostConfigurationScript: "", - }, - }, - BootstrapServerRedirectInfo: BootstrapServerRedirectInfo{}, - }, - wantErr: true, - }, - { - name: "Image wrong", - fields: fields{ - BootstrapURL: "", - SerialNumber: "", - DevicePassword: "", - DevicePrivateKey: "", - DeviceEndEntityCert: "", - BootstrapTrustAnchorCert: "", - ContentTypeReq: "", - InputJSONContent: "", - DhcpLeaseFile: "", - ProgressJSON: ProgressJSON{}, - BootstrapServerOnboardingInfo: BootstrapServerOnboardingInfo{ - IetfSztpConveyedInfoOnboardingInformation: struct { - InfoTimestampReference string - BootImage struct { - DownloadURI []string `json:"download-uri"` - ImageVerification []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - } `json:"image-verification"` - } `json:"boot-image"` - PreConfigurationScript string `json:"pre-configuration-script"` - ConfigurationHandling string `json:"configuration-handling"` - Configuration string `json:"configuration"` - PostConfigurationScript string `json:"post-configuration-script"` - }{ - InfoTimestampReference: "TIMESTAMP", - BootImage: struct { - DownloadURI []string `json:"download-uri"` - ImageVerification []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - } `json:"image-verification"` - }{ - DownloadURI: []string{}, - ImageVerification: nil, - }, - PreConfigurationScript: "", - ConfigurationHandling: "", - Configuration: "", - PostConfigurationScript: "", - }, - }, - BootstrapServerRedirectInfo: BootstrapServerRedirectInfo{}, - }, - wantErr: false, - }, - { - name: "OK Case but with error due to hash checksum", - fields: fields{ - BootstrapURL: "", - SerialNumber: "", - DevicePassword: "", - DevicePrivateKey: "", - DeviceEndEntityCert: "", - BootstrapTrustAnchorCert: "", - ContentTypeReq: "", - InputJSONContent: "", - DhcpLeaseFile: "", - ProgressJSON: ProgressJSON{}, - BootstrapServerOnboardingInfo: BootstrapServerOnboardingInfo{ - IetfSztpConveyedInfoOnboardingInformation: struct { - InfoTimestampReference string - BootImage struct { - DownloadURI []string `json:"download-uri"` - ImageVerification []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - } `json:"image-verification"` - } `json:"boot-image"` - PreConfigurationScript string `json:"pre-configuration-script"` - ConfigurationHandling string `json:"configuration-handling"` - Configuration string `json:"configuration"` - PostConfigurationScript string `json:"post-configuration-script"` - }{ - InfoTimestampReference: "TIMESTAMP", - BootImage: struct { - DownloadURI []string `json:"download-uri"` - ImageVerification []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - } `json:"image-verification"` - }{ - DownloadURI: []string{svr.URL + "/imageOK"}, - ImageVerification: []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - }{{ - HashAlgorithm: "ietf-sztp-conveyed-info:sha-256", - HashValue: "d41d8cd98f00b204e9800998ecf8427e", - }}, - }, - PreConfigurationScript: "", - ConfigurationHandling: "", - Configuration: "", - PostConfigurationScript: "", - }, - }, - BootstrapServerRedirectInfo: BootstrapServerRedirectInfo{}, - }, - wantErr: true, - }, - { - name: "OK Case but with error due to hash checksum", - fields: fields{ - BootstrapURL: "", - SerialNumber: "", - DevicePassword: "", - DevicePrivateKey: "", - DeviceEndEntityCert: "", - BootstrapTrustAnchorCert: "", - ContentTypeReq: "", - InputJSONContent: "", - DhcpLeaseFile: "", - ProgressJSON: ProgressJSON{}, - BootstrapServerOnboardingInfo: BootstrapServerOnboardingInfo{ - IetfSztpConveyedInfoOnboardingInformation: struct { - InfoTimestampReference string - BootImage struct { - DownloadURI []string `json:"download-uri"` - ImageVerification []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - } `json:"image-verification"` - } `json:"boot-image"` - PreConfigurationScript string `json:"pre-configuration-script"` - ConfigurationHandling string `json:"configuration-handling"` - Configuration string `json:"configuration"` - PostConfigurationScript string `json:"post-configuration-script"` - }{ - InfoTimestampReference: "TIMESTAMP", - BootImage: struct { - DownloadURI []string `json:"download-uri"` - ImageVerification []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - } `json:"image-verification"` - }{ - DownloadURI: []string{svr.URL + "/imageOK"}, - ImageVerification: []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - }{{ - HashAlgorithm: "WRONG HASH ALGORITHM", - HashValue: "d41d8cd98f00b204e9800998ecf8427e", - }}, - }, - PreConfigurationScript: "", - ConfigurationHandling: "", - Configuration: "", - PostConfigurationScript: "", - }, - }, - BootstrapServerRedirectInfo: BootstrapServerRedirectInfo{}, - }, - wantErr: true, - }, - } - for _, tt := range tests { - deleteTempTestFile(ARTIFACTS_PATH + "/imageOK") - t.Run(tt.name, func(t *testing.T) { - a := &Agent{ - BootstrapURL: tt.fields.BootstrapURL, - SerialNumber: tt.fields.SerialNumber, - DevicePassword: tt.fields.DevicePassword, - DevicePrivateKey: tt.fields.DevicePrivateKey, - DeviceEndEntityCert: tt.fields.DeviceEndEntityCert, - BootstrapTrustAnchorCert: tt.fields.BootstrapTrustAnchorCert, - ContentTypeReq: tt.fields.ContentTypeReq, - InputJSONContent: tt.fields.InputJSONContent, - DhcpLeaseFile: tt.fields.DhcpLeaseFile, - ProgressJSON: tt.fields.ProgressJSON, - BootstrapServerOnboardingInfo: tt.fields.BootstrapServerOnboardingInfo, - BootstrapServerRedirectInfo: tt.fields.BootstrapServerRedirectInfo, - HttpClient: svr.Client(), - } - if err := a.downloadAndValidateImage(); (err != nil) != tt.wantErr { - t.Errorf("downloadAndValidateImage() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} - -// nolint:funlen -func TestAgent_copyConfigurationFile(t *testing.T) { - type fields struct { - BootstrapURL string - SerialNumber string - DevicePassword string - DevicePrivateKey string - DeviceEndEntityCert string - BootstrapTrustAnchorCert string - ContentTypeReq string - InputJSONContent string - DhcpLeaseFile string - ProgressJSON ProgressJSON - BootstrapServerOnboardingInfo BootstrapServerOnboardingInfo - BootstrapServerRedirectInfo BootstrapServerRedirectInfo - } - tests := []struct { - name string - fields fields - wantErr bool - }{ - { - name: "Error Writing file", - fields: fields{ - BootstrapURL: "", - SerialNumber: "", - DevicePassword: "", - DevicePrivateKey: "", - DeviceEndEntityCert: "", - BootstrapTrustAnchorCert: "", - ContentTypeReq: "", - InputJSONContent: "", - DhcpLeaseFile: "", - ProgressJSON: ProgressJSON{}, - BootstrapServerOnboardingInfo: BootstrapServerOnboardingInfo{ - IetfSztpConveyedInfoOnboardingInformation: struct { - InfoTimestampReference string - BootImage struct { - DownloadURI []string `json:"download-uri"` - ImageVerification []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - } `json:"image-verification"` - } `json:"boot-image"` - PreConfigurationScript string `json:"pre-configuration-script"` - ConfigurationHandling string `json:"configuration-handling"` - Configuration string `json:"configuration"` - PostConfigurationScript string `json:"post-configuration-script"` - }{ - InfoTimestampReference: " ../ ", - BootImage: struct { - DownloadURI []string `json:"download-uri"` - ImageVerification []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - } `json:"image-verification"` - }{ - DownloadURI: []string{}, - ImageVerification: []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - }{{ - HashAlgorithm: "md5", - HashValue: "d41d8cd98f00b204e9800998ecf8427e", - }}, - }, - PreConfigurationScript: "", - ConfigurationHandling: "", - Configuration: "", - PostConfigurationScript: "", - }, - }, - BootstrapServerRedirectInfo: BootstrapServerRedirectInfo{}, - }, - wantErr: true, - }, - { - name: "OK Case", - fields: fields{ - BootstrapURL: "", - SerialNumber: "", - DevicePassword: "", - DevicePrivateKey: "", - DeviceEndEntityCert: "", - BootstrapTrustAnchorCert: "", - ContentTypeReq: "", - InputJSONContent: "", - DhcpLeaseFile: "", - ProgressJSON: ProgressJSON{}, - BootstrapServerOnboardingInfo: BootstrapServerOnboardingInfo{ - IetfSztpConveyedInfoOnboardingInformation: struct { - InfoTimestampReference string - BootImage struct { - DownloadURI []string `json:"download-uri"` - ImageVerification []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - } `json:"image-verification"` - } `json:"boot-image"` - PreConfigurationScript string `json:"pre-configuration-script"` - ConfigurationHandling string `json:"configuration-handling"` - Configuration string `json:"configuration"` - PostConfigurationScript string `json:"post-configuration-script"` - }{ - InfoTimestampReference: "PATHOK", - BootImage: struct { - DownloadURI []string `json:"download-uri"` - ImageVerification []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - } `json:"image-verification"` - }{ - DownloadURI: []string{}, - ImageVerification: []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - }{{ - HashAlgorithm: "md5", - HashValue: "d41d8cd98f00b204e9800998ecf8427e", - }}, - }, - PreConfigurationScript: "", - ConfigurationHandling: "", - Configuration: "", - PostConfigurationScript: "", - }, - }, - BootstrapServerRedirectInfo: BootstrapServerRedirectInfo{}, - }, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - a := &Agent{ - BootstrapURL: tt.fields.BootstrapURL, - SerialNumber: tt.fields.SerialNumber, - DevicePassword: tt.fields.DevicePassword, - DevicePrivateKey: tt.fields.DevicePrivateKey, - DeviceEndEntityCert: tt.fields.DeviceEndEntityCert, - BootstrapTrustAnchorCert: tt.fields.BootstrapTrustAnchorCert, - ContentTypeReq: tt.fields.ContentTypeReq, - InputJSONContent: tt.fields.InputJSONContent, - DhcpLeaseFile: tt.fields.DhcpLeaseFile, - ProgressJSON: tt.fields.ProgressJSON, - BootstrapServerOnboardingInfo: tt.fields.BootstrapServerOnboardingInfo, - BootstrapServerRedirectInfo: tt.fields.BootstrapServerRedirectInfo, - HttpClient: &http.Client{}, - } - if err := a.copyConfigurationFile(); (err != nil) != tt.wantErr { - t.Errorf("copyConfigurationFile() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} - -// nolint:funlen -func TestAgent_launchScriptsConfiguration(t *testing.T) { - type fields struct { - BootstrapURL string - SerialNumber string - DevicePassword string - DevicePrivateKey string - DeviceEndEntityCert string - BootstrapTrustAnchorCert string - ContentTypeReq string - InputJSONContent string - DhcpLeaseFile string - ProgressJSON ProgressJSON - BootstrapServerOnboardingInfo BootstrapServerOnboardingInfo - BootstrapServerRedirectInfo BootstrapServerRedirectInfo - } - type args struct { - typeOf string - } - tests := []struct { - name string - fields fields - args args - wantErr bool - }{ - { - args: args{typeOf: "default or pre"}, - name: "OK Case with PRE", - fields: fields{ - BootstrapURL: "", - SerialNumber: "", - DevicePassword: "", - DevicePrivateKey: "", - DeviceEndEntityCert: "", - BootstrapTrustAnchorCert: "", - ContentTypeReq: "", - InputJSONContent: "", - DhcpLeaseFile: "", - ProgressJSON: ProgressJSON{}, - BootstrapServerOnboardingInfo: BootstrapServerOnboardingInfo{ - IetfSztpConveyedInfoOnboardingInformation: struct { - InfoTimestampReference string - BootImage struct { - DownloadURI []string `json:"download-uri"` - ImageVerification []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - } `json:"image-verification"` - } `json:"boot-image"` - PreConfigurationScript string `json:"pre-configuration-script"` - ConfigurationHandling string `json:"configuration-handling"` - Configuration string `json:"configuration"` - PostConfigurationScript string `json:"post-configuration-script"` - }{ - InfoTimestampReference: "PATHOK", - BootImage: struct { - DownloadURI []string `json:"download-uri"` - ImageVerification []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - } `json:"image-verification"` - }{ - DownloadURI: []string{}, - ImageVerification: []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - }{{ - HashAlgorithm: "md5", - HashValue: "d41d8cd98f00b204e9800998ecf8427e", - }}, - }, - PreConfigurationScript: "", - ConfigurationHandling: "", - Configuration: "", - PostConfigurationScript: "", - }, - }, - BootstrapServerRedirectInfo: BootstrapServerRedirectInfo{}, - }, - wantErr: false, - }, - { - args: args{typeOf: "post"}, - name: "OK Case with POST", - fields: fields{ - BootstrapURL: "", - SerialNumber: "", - DevicePassword: "", - DevicePrivateKey: "", - DeviceEndEntityCert: "", - BootstrapTrustAnchorCert: "", - ContentTypeReq: "", - InputJSONContent: "", - DhcpLeaseFile: "", - ProgressJSON: ProgressJSON{}, - BootstrapServerOnboardingInfo: BootstrapServerOnboardingInfo{ - IetfSztpConveyedInfoOnboardingInformation: struct { - InfoTimestampReference string - BootImage struct { - DownloadURI []string `json:"download-uri"` - ImageVerification []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - } `json:"image-verification"` - } `json:"boot-image"` - PreConfigurationScript string `json:"pre-configuration-script"` - ConfigurationHandling string `json:"configuration-handling"` - Configuration string `json:"configuration"` - PostConfigurationScript string `json:"post-configuration-script"` - }{ - InfoTimestampReference: "PATHOK", - BootImage: struct { - DownloadURI []string `json:"download-uri"` - ImageVerification []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - } `json:"image-verification"` - }{ - DownloadURI: []string{}, - ImageVerification: []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - }{{ - HashAlgorithm: "md5", - HashValue: "d41d8cd98f00b204e9800998ecf8427e", - }}, - }, - PreConfigurationScript: "", - ConfigurationHandling: "", - Configuration: "", - PostConfigurationScript: "", - }, - }, - BootstrapServerRedirectInfo: BootstrapServerRedirectInfo{}, - }, - wantErr: false, - }, - { - args: args{typeOf: "post"}, - name: "OK Case with POST", - fields: fields{ - BootstrapURL: "", - SerialNumber: "", - DevicePassword: "", - DevicePrivateKey: "", - DeviceEndEntityCert: "", - BootstrapTrustAnchorCert: "", - ContentTypeReq: "", - InputJSONContent: "", - DhcpLeaseFile: "", - ProgressJSON: ProgressJSON{}, - BootstrapServerOnboardingInfo: BootstrapServerOnboardingInfo{ - IetfSztpConveyedInfoOnboardingInformation: struct { - InfoTimestampReference string - BootImage struct { - DownloadURI []string `json:"download-uri"` - ImageVerification []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - } `json:"image-verification"` - } `json:"boot-image"` - PreConfigurationScript string `json:"pre-configuration-script"` - ConfigurationHandling string `json:"configuration-handling"` - Configuration string `json:"configuration"` - PostConfigurationScript string `json:"post-configuration-script"` - }{ - InfoTimestampReference: " ../", - BootImage: struct { - DownloadURI []string `json:"download-uri"` - ImageVerification []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - } `json:"image-verification"` - }{ - DownloadURI: []string{}, - ImageVerification: []struct { - HashAlgorithm string `json:"hash-algorithm"` - HashValue string `json:"hash-value"` - }{{ - HashAlgorithm: "md5", - HashValue: "d41d8cd98f00b204e9800998ecf8427e", - }}, - }, - PreConfigurationScript: "", - ConfigurationHandling: "", - Configuration: "", - PostConfigurationScript: "", - }, - }, - BootstrapServerRedirectInfo: BootstrapServerRedirectInfo{}, - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - a := &Agent{ - BootstrapURL: tt.fields.BootstrapURL, - SerialNumber: tt.fields.SerialNumber, - DevicePassword: tt.fields.DevicePassword, - DevicePrivateKey: tt.fields.DevicePrivateKey, - DeviceEndEntityCert: tt.fields.DeviceEndEntityCert, - BootstrapTrustAnchorCert: tt.fields.BootstrapTrustAnchorCert, - ContentTypeReq: tt.fields.ContentTypeReq, - InputJSONContent: tt.fields.InputJSONContent, - DhcpLeaseFile: tt.fields.DhcpLeaseFile, - ProgressJSON: tt.fields.ProgressJSON, - BootstrapServerOnboardingInfo: tt.fields.BootstrapServerOnboardingInfo, - BootstrapServerRedirectInfo: tt.fields.BootstrapServerRedirectInfo, - HttpClient: &http.Client{}, - } - if err := a.launchScriptsConfiguration(tt.args.typeOf); (err != nil) != tt.wantErr { - t.Errorf("launchScriptsConfiguration() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} diff --git a/sztp-agent/pkg/secureagent/image.go b/sztp-agent/pkg/secureagent/image.go new file mode 100644 index 0000000..9af486c --- /dev/null +++ b/sztp-agent/pkg/secureagent/image.go @@ -0,0 +1,87 @@ +/* +SPDX-License-Identifier: Apache-2.0 +Copyright (C) 2022-2023 Intel Corporation +Copyright (c) 2022 Dell Inc, or its subsidiaries. +Copyright (C) 2022 Red Hat. +*/ + +package secureagent + +import ( + "errors" + "fmt" + "io" + "log" + "os" + "path/filepath" + "strconv" + "strings" + "time" +) + +//nolint:funlen +func (a *Agent) downloadAndValidateImage() error { + log.Printf("[INFO] Starting the Download Image: %v", a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.BootImage.DownloadURI) + _ = a.doReportProgress(ProgressTypeBootImageInitiated, "BootImage Initiated") + // Download the image from DownloadURI and save it to a file + a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.InfoTimestampReference = fmt.Sprintf("%8d", time.Now().Unix()) + for i, item := range a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.BootImage.DownloadURI { + // TODO: maybe need to file download to a function in util.go + log.Printf("[INFO] Downloading Image %v", item) + // Create a empty file + file, err := os.Create(ARTIFACTS_PATH + a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.InfoTimestampReference + filepath.Base(item)) + if err != nil { + return err + } + + response, err := a.HttpClient.Get(item) + if err != nil { + return err + } + + sizeorigin, _ := strconv.Atoi(response.Header.Get("Content-Length")) + downloadSize := int64(sizeorigin) + log.Printf("[INFO] Downloading the image with size: %v", downloadSize) + + if response.StatusCode != 200 { + return errors.New("received non 200 response code") + } + size, err := io.Copy(file, response.Body) + if err != nil { + return err + } + defer func() { + if err := file.Close(); err != nil { + log.Println("[ERROR] Error when closing:", err) + } + }() + defer func() { + if err := response.Body.Close(); err != nil { + log.Println("[ERROR] Error when closing:", err) + } + }() + + log.Printf("[INFO] Downloaded file: %s with size: %d", ARTIFACTS_PATH+a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.InfoTimestampReference+filepath.Base(item), size) + log.Println("[INFO] Verify the file checksum: ", ARTIFACTS_PATH+a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.InfoTimestampReference+filepath.Base(item)) + switch a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.BootImage.ImageVerification[i].HashAlgorithm { + case "ietf-sztp-conveyed-info:sha-256": + filePath := ARTIFACTS_PATH + a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.InfoTimestampReference + filepath.Base(item) + checksum, err := calculateSHA256File(filePath) + original := strings.ReplaceAll(a.BootstrapServerOnboardingInfo.IetfSztpConveyedInfoOnboardingInformation.BootImage.ImageVerification[i].HashValue, ":", "") + if err != nil { + log.Println("[ERROR] Could not calculate checksum", err) + } + log.Println("calculated: " + checksum) + log.Println("expected : " + original) + if checksum != original { + return errors.New("checksum mismatch") + } + log.Println("[INFO] Checksum verified successfully") + _ = a.doReportProgress(ProgressTypeBootImageComplete, "BootImage Complete") + return nil + default: + return errors.New("unsupported hash algorithm") + } + } + return nil +} diff --git a/sztp-agent/pkg/secureagent/image_test.go b/sztp-agent/pkg/secureagent/image_test.go new file mode 100644 index 0000000..bc73f5f --- /dev/null +++ b/sztp-agent/pkg/secureagent/image_test.go @@ -0,0 +1,319 @@ +package secureagent + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +//nolint:funlen +func TestAgent_downloadAndValidateImage(t *testing.T) { + svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/imageOK" || r.URL.Path == "/report-progress" { + w.WriteHeader(200) + } else { + w.WriteHeader(400) + } + })) + defer svr.Close() + + type fields struct { + BootstrapURL string + SerialNumber string + DevicePassword string + DevicePrivateKey string + DeviceEndEntityCert string + BootstrapTrustAnchorCert string + ContentTypeReq string + InputJSONContent string + DhcpLeaseFile string + ProgressJSON ProgressJSON + BootstrapServerOnboardingInfo BootstrapServerOnboardingInfo + BootstrapServerRedirectInfo BootstrapServerRedirectInfo + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "error writing file", + fields: fields{ + BootstrapURL: "", + SerialNumber: "", + DevicePassword: "", + DevicePrivateKey: "", + DeviceEndEntityCert: "", + BootstrapTrustAnchorCert: "", + ContentTypeReq: "", + InputJSONContent: "", + DhcpLeaseFile: "", + ProgressJSON: ProgressJSON{}, + BootstrapServerOnboardingInfo: BootstrapServerOnboardingInfo{ + IetfSztpConveyedInfoOnboardingInformation: struct { + InfoTimestampReference string + BootImage struct { + DownloadURI []string `json:"download-uri"` + ImageVerification []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + } `json:"image-verification"` + } `json:"boot-image"` + PreConfigurationScript string `json:"pre-configuration-script"` + ConfigurationHandling string `json:"configuration-handling"` + Configuration string `json:"configuration"` + PostConfigurationScript string `json:"post-configuration-script"` + }{ + InfoTimestampReference: "", + BootImage: struct { + DownloadURI []string `json:"download-uri"` + ImageVerification []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + } `json:"image-verification"` + }{ + DownloadURI: []string{"WrongURL"}, + ImageVerification: nil, + }, + PreConfigurationScript: "", + ConfigurationHandling: "", + Configuration: "", + PostConfigurationScript: "", + }, + }, + BootstrapServerRedirectInfo: BootstrapServerRedirectInfo{}, + }, + wantErr: true, + }, + { + name: "Image wrong", + fields: fields{ + BootstrapURL: "", + SerialNumber: "", + DevicePassword: "", + DevicePrivateKey: "", + DeviceEndEntityCert: "", + BootstrapTrustAnchorCert: "", + ContentTypeReq: "", + InputJSONContent: "", + DhcpLeaseFile: "", + ProgressJSON: ProgressJSON{}, + BootstrapServerOnboardingInfo: BootstrapServerOnboardingInfo{ + IetfSztpConveyedInfoOnboardingInformation: struct { + InfoTimestampReference string + BootImage struct { + DownloadURI []string `json:"download-uri"` + ImageVerification []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + } `json:"image-verification"` + } `json:"boot-image"` + PreConfigurationScript string `json:"pre-configuration-script"` + ConfigurationHandling string `json:"configuration-handling"` + Configuration string `json:"configuration"` + PostConfigurationScript string `json:"post-configuration-script"` + }{ + InfoTimestampReference: "TIMESTAMP", + BootImage: struct { + DownloadURI []string `json:"download-uri"` + ImageVerification []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + } `json:"image-verification"` + }{ + DownloadURI: []string{svr.URL + "/imageWRONG"}, + ImageVerification: nil, + }, + PreConfigurationScript: "", + ConfigurationHandling: "", + Configuration: "", + PostConfigurationScript: "", + }, + }, + BootstrapServerRedirectInfo: BootstrapServerRedirectInfo{}, + }, + wantErr: true, + }, + { + name: "Image wrong", + fields: fields{ + BootstrapURL: "", + SerialNumber: "", + DevicePassword: "", + DevicePrivateKey: "", + DeviceEndEntityCert: "", + BootstrapTrustAnchorCert: "", + ContentTypeReq: "", + InputJSONContent: "", + DhcpLeaseFile: "", + ProgressJSON: ProgressJSON{}, + BootstrapServerOnboardingInfo: BootstrapServerOnboardingInfo{ + IetfSztpConveyedInfoOnboardingInformation: struct { + InfoTimestampReference string + BootImage struct { + DownloadURI []string `json:"download-uri"` + ImageVerification []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + } `json:"image-verification"` + } `json:"boot-image"` + PreConfigurationScript string `json:"pre-configuration-script"` + ConfigurationHandling string `json:"configuration-handling"` + Configuration string `json:"configuration"` + PostConfigurationScript string `json:"post-configuration-script"` + }{ + InfoTimestampReference: "TIMESTAMP", + BootImage: struct { + DownloadURI []string `json:"download-uri"` + ImageVerification []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + } `json:"image-verification"` + }{ + DownloadURI: []string{}, + ImageVerification: nil, + }, + PreConfigurationScript: "", + ConfigurationHandling: "", + Configuration: "", + PostConfigurationScript: "", + }, + }, + BootstrapServerRedirectInfo: BootstrapServerRedirectInfo{}, + }, + wantErr: false, + }, + { + name: "OK Case but with error due to hash checksum", + fields: fields{ + BootstrapURL: "", + SerialNumber: "", + DevicePassword: "", + DevicePrivateKey: "", + DeviceEndEntityCert: "", + BootstrapTrustAnchorCert: "", + ContentTypeReq: "", + InputJSONContent: "", + DhcpLeaseFile: "", + ProgressJSON: ProgressJSON{}, + BootstrapServerOnboardingInfo: BootstrapServerOnboardingInfo{ + IetfSztpConveyedInfoOnboardingInformation: struct { + InfoTimestampReference string + BootImage struct { + DownloadURI []string `json:"download-uri"` + ImageVerification []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + } `json:"image-verification"` + } `json:"boot-image"` + PreConfigurationScript string `json:"pre-configuration-script"` + ConfigurationHandling string `json:"configuration-handling"` + Configuration string `json:"configuration"` + PostConfigurationScript string `json:"post-configuration-script"` + }{ + InfoTimestampReference: "TIMESTAMP", + BootImage: struct { + DownloadURI []string `json:"download-uri"` + ImageVerification []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + } `json:"image-verification"` + }{ + DownloadURI: []string{svr.URL + "/imageOK"}, + ImageVerification: []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + }{{ + HashAlgorithm: "ietf-sztp-conveyed-info:sha-256", + HashValue: "d41d8cd98f00b204e9800998ecf8427e", + }}, + }, + PreConfigurationScript: "", + ConfigurationHandling: "", + Configuration: "", + PostConfigurationScript: "", + }, + }, + BootstrapServerRedirectInfo: BootstrapServerRedirectInfo{}, + }, + wantErr: true, + }, + { + name: "OK Case but with error due to hash checksum", + fields: fields{ + BootstrapURL: "", + SerialNumber: "", + DevicePassword: "", + DevicePrivateKey: "", + DeviceEndEntityCert: "", + BootstrapTrustAnchorCert: "", + ContentTypeReq: "", + InputJSONContent: "", + DhcpLeaseFile: "", + ProgressJSON: ProgressJSON{}, + BootstrapServerOnboardingInfo: BootstrapServerOnboardingInfo{ + IetfSztpConveyedInfoOnboardingInformation: struct { + InfoTimestampReference string + BootImage struct { + DownloadURI []string `json:"download-uri"` + ImageVerification []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + } `json:"image-verification"` + } `json:"boot-image"` + PreConfigurationScript string `json:"pre-configuration-script"` + ConfigurationHandling string `json:"configuration-handling"` + Configuration string `json:"configuration"` + PostConfigurationScript string `json:"post-configuration-script"` + }{ + InfoTimestampReference: "TIMESTAMP", + BootImage: struct { + DownloadURI []string `json:"download-uri"` + ImageVerification []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + } `json:"image-verification"` + }{ + DownloadURI: []string{svr.URL + "/imageOK"}, + ImageVerification: []struct { + HashAlgorithm string `json:"hash-algorithm"` + HashValue string `json:"hash-value"` + }{{ + HashAlgorithm: "WRONG HASH ALGORITHM", + HashValue: "d41d8cd98f00b204e9800998ecf8427e", + }}, + }, + PreConfigurationScript: "", + ConfigurationHandling: "", + Configuration: "", + PostConfigurationScript: "", + }, + }, + BootstrapServerRedirectInfo: BootstrapServerRedirectInfo{}, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &Agent{ + BootstrapURL: tt.fields.BootstrapURL, + SerialNumber: tt.fields.SerialNumber, + DevicePassword: tt.fields.DevicePassword, + DevicePrivateKey: tt.fields.DevicePrivateKey, + DeviceEndEntityCert: tt.fields.DeviceEndEntityCert, + BootstrapTrustAnchorCert: tt.fields.BootstrapTrustAnchorCert, + ContentTypeReq: tt.fields.ContentTypeReq, + InputJSONContent: tt.fields.InputJSONContent, + DhcpLeaseFile: tt.fields.DhcpLeaseFile, + ProgressJSON: tt.fields.ProgressJSON, + BootstrapServerOnboardingInfo: tt.fields.BootstrapServerOnboardingInfo, + BootstrapServerRedirectInfo: tt.fields.BootstrapServerRedirectInfo, + HttpClient: &http.Client{}, + } + if err := a.downloadAndValidateImage(); (err != nil) != tt.wantErr { + t.Errorf("downloadAndValidateImage() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/sztp-agent/pkg/secureagent/ssh_test.go b/sztp-agent/pkg/secureagent/ssh_test.go index 48a8b5b..35e7036 100644 --- a/sztp-agent/pkg/secureagent/ssh_test.go +++ b/sztp-agent/pkg/secureagent/ssh_test.go @@ -12,6 +12,7 @@ func Test_readSSHHostKeyPublicFiles(t *testing.T) { file string content string Algorithm string + KeyOnly bool } tests := []struct { name string @@ -24,6 +25,7 @@ func Test_readSSHHostKeyPublicFiles(t *testing.T) { file: "/tmp/test.pub", content: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID0mjQXlOvkM2HO5vTrSOdHOl3BGOqDiHrx8yYdbP8xR", Algorithm: "ssh-ed25519", + KeyOnly: false, }, want: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID0mjQXlOvkM2HO5vTrSOdHOl3BGOqDiHrx8yYdbP8xR", }, @@ -33,6 +35,7 @@ func Test_readSSHHostKeyPublicFiles(t *testing.T) { file: "/tmp/test.pub", content: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID0mjQXlOvkM2HO5vTrSOdHOl3BGOqDiHrx8yYdbP8xR comment", Algorithm: "ssh-ed25519", + KeyOnly: false, }, want: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID0mjQXlOvkM2HO5vTrSOdHOl3BGOqDiHrx8yYdbP8xR", }, @@ -42,6 +45,7 @@ func Test_readSSHHostKeyPublicFiles(t *testing.T) { file: "/tmp/test.pub", content: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID0mjQXlOvkM2HO5vTrSOdHOl3BGOqDiHrx8yYdbP8xR comment error", Algorithm: "ssh-ed25519", + KeyOnly: false, }, want: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID0mjQXlOvkM2HO5vTrSOdHOl3BGOqDiHrx8yYdbP8xR", }, @@ -50,6 +54,7 @@ func Test_readSSHHostKeyPublicFiles(t *testing.T) { args: args{ file: "/tmp/test.pub", content: "ssh-ed25519", + KeyOnly: false, }, want: "ssh-ed25519", }, @@ -58,9 +63,20 @@ func Test_readSSHHostKeyPublicFiles(t *testing.T) { args: args{ file: "/tmp/test.pub", content: "", + KeyOnly: false, }, want: "", }, + { + name: "Test file only key", + args: args{ + file: "/tmp/test.pub", + content: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID0mjQXlOvkM2HO5vTrSOdHOl3BGOqDiHrx8yYdbP8xR", + Algorithm: "ssh-ed25519", + KeyOnly: true, + }, + want: "AAAAC3NzaC1lZDI1NTE5AAAAID0mjQXlOvkM2HO5vTrSOdHOl3BGOqDiHrx8yYdbP8xR", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -68,7 +84,7 @@ func Test_readSSHHostKeyPublicFiles(t *testing.T) { createTempTestFile(tt.args.file, tt.args.content, true) } for _, key := range readSSHHostKeyPublicFiles(tt.args.file) { - if got := getSSHHostKeyString(key, true); !reflect.DeepEqual(got, tt.want) { + if got := getSSHHostKeyString(key, !tt.args.KeyOnly); !reflect.DeepEqual(got, tt.want) { t.Errorf("readSSHHostKeyPublicFiles() - got: %v, want %v", got, tt.want) } } diff --git a/sztp-agent/pkg/secureagent/tls.go b/sztp-agent/pkg/secureagent/tls.go index b33d763..62a7ac7 100644 --- a/sztp-agent/pkg/secureagent/tls.go +++ b/sztp-agent/pkg/secureagent/tls.go @@ -10,15 +10,43 @@ package secureagent import ( "bytes" + "crypto/tls" + "crypto/x509" "encoding/json" "errors" "io" "log" "net/http" + "os" + "path/filepath" "strconv" "strings" ) +// Instantiate a new HTTP Client +func NewHTTPClient(bootstrapTrustAnchorCert string, deviceEndEntityCert string, devicePrivateKey string) http.Client { + certPath := filepath.Clean(bootstrapTrustAnchorCert) + caCert, _ := os.ReadFile(certPath) + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + cert, _ := tls.LoadX509KeyPair(deviceEndEntityCert, devicePrivateKey) + client := http.Client{ + CheckRedirect: func(r *http.Request, _ []*http.Request) error { + r.URL.Opaque = r.URL.Path + return nil + }, + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + //nolint:gosec + InsecureSkipVerify: true, // TODO: remove skip verify + RootCAs: caCertPool, + Certificates: []tls.Certificate{cert}, + }, + }, + } + return client +} + func (a *Agent) doTLSRequest(input string, url string, empty bool) (*BootstrapServerPostOutput, error) { var postResponse BootstrapServerPostOutput var errorResponse BootstrapServerErrorOutput