package saas import ( "bytes" "encoding/json" "fmt" "io/ioutil" "net/http" "net/url" "os" "path" "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/sts" c "github.com/future-architect/vuls/config" "github.com/future-architect/vuls/models" "github.com/future-architect/vuls/util" "golang.org/x/xerrors" ) // Writer writes results to SaaS type Writer struct{} // TempCredential : TempCredential type TempCredential struct { Credential *sts.Credentials `json:"Credential"` S3Bucket string `json:"S3Bucket"` S3ResultsDir string `json:"S3ResultsDir"` } type payload struct { GroupID int64 `json:"GroupID"` Token string `json:"Token"` ScannedBy string `json:"ScannedBy"` ScannedIPv4s string `json:"ScannedIPv4s"` ScannedIPv6s string `json:"ScannedIPv6s"` } // UploadSaas : UploadSaas func (w Writer) Write(rs ...models.ScanResult) (err error) { // dir string, configPath string, config *c.Config if len(rs) == 0 { return nil } ipv4s, ipv6s, err := util.IP() if err != nil { util.Log.Errorf("Failed to fetch scannedIPs. err: %+v", err) } hostname, _ := os.Hostname() payload := payload{ GroupID: c.Conf.Saas.GroupID, Token: c.Conf.Saas.Token, ScannedBy: hostname, ScannedIPv4s: strings.Join(ipv4s, ", "), ScannedIPv6s: strings.Join(ipv6s, ", "), } var body []byte if body, err = json.Marshal(payload); err != nil { return xerrors.Errorf("Failed to Marshal to JSON: %w", err) } var req *http.Request if req, err = http.NewRequest("POST", c.Conf.Saas.URL, bytes.NewBuffer(body)); err != nil { return err } req.Header.Set("Content-Type", "application/json") req.Header.Set("Accept", "application/json") proxy := c.Conf.HTTPProxy var client http.Client if proxy != "" { proxyURL, _ := url.Parse(proxy) client = http.Client{ Transport: &http.Transport{ Proxy: http.ProxyURL(proxyURL), }, } } else { client = http.Client{} } var resp *http.Response if resp, err = client.Do(req); err != nil { return err } defer resp.Body.Close() if resp.StatusCode != 200 { return xerrors.Errorf("Failed to get Credential. Request JSON : %s,", string(body)) } var t []byte if t, err = ioutil.ReadAll(resp.Body); err != nil { return err } var tempCredential TempCredential if err = json.Unmarshal(t, &tempCredential); err != nil { return xerrors.Errorf("Failed to unmarshal saas credential file. err : %s", err) } credential := credentials.NewStaticCredentialsFromCreds(credentials.Value{ AccessKeyID: *tempCredential.Credential.AccessKeyId, SecretAccessKey: *tempCredential.Credential.SecretAccessKey, SessionToken: *tempCredential.Credential.SessionToken, }) var sess *session.Session if sess, err = session.NewSession(&aws.Config{ Credentials: credential, Region: aws.String("ap-northeast-1"), }); err != nil { return xerrors.Errorf("Failed to new aws session. err: %w", err) } svc := s3.New(sess) for _, r := range rs { s3Key := renameKeyName(r.ServerUUID, r.Container) var b []byte if b, err = json.Marshal(r); err != nil { return xerrors.Errorf("Failed to Marshal to JSON: %w", err) } util.Log.Infof("Uploading...: ServerName: %s, ", r.ServerName) putObjectInput := &s3.PutObjectInput{ Bucket: aws.String(tempCredential.S3Bucket), Key: aws.String(path.Join(tempCredential.S3ResultsDir, s3Key)), Body: bytes.NewReader(b), } if _, err := svc.PutObject(putObjectInput); err != nil { return xerrors.Errorf("Failed to upload data to %s/%s, err: %w", tempCredential.S3Bucket, s3Key, err) } } util.Log.Infof("done") return nil } func renameKeyName(uuid string, container models.Container) string { if len(container.ContainerID) == 0 { return fmt.Sprintf("%s.json", uuid) } return fmt.Sprintf("%s@%s.json", container.UUID, uuid) }