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..8c8be26 --- /dev/null +++ b/sztp-agent/pkg/secureagent/configuration_test.go @@ -0,0 +1,377 @@ +package secureagent + +import ( + "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, + } + 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, + } + 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 ef775f9..cc0fa44 100644 --- a/sztp-agent/pkg/secureagent/daemon.go +++ b/sztp-agent/pkg/secureagent/daemon.go @@ -10,23 +10,13 @@ package secureagent import ( "bytes" - "crypto/tls" - "crypto/x509" "encoding/asn1" "encoding/base64" "encoding/json" - "errors" "fmt" - "io" "log" - "net/http" "net/url" - "os" - "os/exec" - "path/filepath" "reflect" - "strconv" - "strings" "time" "github.com/github/smimesign/ietf-cms/protocol" @@ -180,176 +170,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 - } - - caCert, _ := os.ReadFile(a.GetBootstrapTrustAnchorCert()) - caCertPool := x509.NewCertPool() - caCertPool.AppendCertsFromPEM(caCert) - cert, _ := tls.LoadX509KeyPair(a.GetDeviceEndEntityCert(), a.GetDevicePrivateKey()) - - check := 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}, - }, - }, - } - - response, err := check.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 eb7d968..17578f3 100644 --- a/sztp-agent/pkg/secureagent/daemon_test.go +++ b/sztp-agent/pkg/secureagent/daemon_test.go @@ -348,686 +348,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 { - 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, - } - 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, - } - 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, - } - 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..daff80d --- /dev/null +++ b/sztp-agent/pkg/secureagent/image.go @@ -0,0 +1,110 @@ +/* +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 ( + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "io" + "log" + "net/http" + "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 + } + + caCert, _ := os.ReadFile(a.GetBootstrapTrustAnchorCert()) + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + cert, _ := tls.LoadX509KeyPair(a.GetDeviceEndEntityCert(), a.GetDevicePrivateKey()) + + check := 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}, + }, + }, + } + + response, err := check.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..24cd285 --- /dev/null +++ b/sztp-agent/pkg/secureagent/image_test.go @@ -0,0 +1,318 @@ +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" { + 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, + } + if err := a.downloadAndValidateImage(); (err != nil) != tt.wantErr { + t.Errorf("downloadAndValidateImage() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +}