Skip to content

Commit

Permalink
Added upload_gcs() function to directly upload to GCS. (Velocidex#87)
Browse files Browse the repository at this point in the history
This is useful when collecting large artifacts locally in stand alone mode.
  • Loading branch information
scudette authored Sep 29, 2019
1 parent ee0ce3d commit ee80bfd
Show file tree
Hide file tree
Showing 9 changed files with 195 additions and 6 deletions.
4 changes: 2 additions & 2 deletions artifacts/assets/ab0x.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ package constants
import "regexp"

const (
VERSION = "0.3.3"
VERSION = "0.3.4"
ENROLLMENT_WELL_KNOWN_FLOW = "aff4:/flows/E:Enrol"
MONITORING_WELL_KNOWN_FLOW = FLOW_PREFIX + "Monitoring"

Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module www.velocidex.com/golang/velociraptor

require (
cloud.google.com/go v0.41.0 // indirect
cloud.google.com/go v0.41.0
github.com/Depado/bfchroma v1.1.2
github.com/Masterminds/goutils v1.1.0 // indirect
github.com/Masterminds/semver v1.4.2 // indirect
Expand Down Expand Up @@ -77,10 +77,12 @@ require (
github.com/tink-ab/tempfile v0.0.0-20180226111222-33beb0518f1a
github.com/xor-gate/ar v0.0.0-20170530204233-5c72ae81e2b7 // indirect
github.com/xor-gate/debpkg v0.0.0-20181217150151-a0c70a3d4213
golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4 // indirect
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7
golang.org/x/net v0.0.0-20190921015927-1a5e07d1ff72
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
golang.org/x/sys v0.0.0-20190920190810-ef0ce1748380
google.golang.org/api v0.7.0
google.golang.org/genproto v0.0.0-20190701230453-710ae3a149df
google.golang.org/grpc v1.22.0
gopkg.in/alecthomas/kingpin.v2 v2.2.6
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIE
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY=
github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
Expand All @@ -142,6 +143,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.6/go.mod h1:vNeuVxBJEsws4ogUvrchl83t
github.com/hanwen/go-fuse v1.0.0 h1:GxS9Zrn6c35/BnfiVsZVWmsG803xwE7eVRDvcf/BEVc=
github.com/hanwen/go-fuse v1.0.0/go.mod h1:unqXarDXqzAk0rt98O2tVndEPIpUgLD9+rwFisZH3Ok=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ=
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
Expand Down Expand Up @@ -284,7 +286,10 @@ github.com/xor-gate/ar v0.0.0-20170530204233-5c72ae81e2b7/go.mod h1:TCWCUPhQU1j7
github.com/xor-gate/debpkg v0.0.0-20181217150151-a0c70a3d4213 h1:vAXuIRI3I7kcyIXOk4HmjtFK9yNpTcUZjECxHi7bMLE=
github.com/xor-gate/debpkg v0.0.0-20181217150151-a0c70a3d4213/go.mod h1:SoEebpbmls5W+VITEo305GbSi4uCEcRkFgvpTNyQQVM=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4 h1:QlVATYS7JBoZMVaf+cNjb90WD/beKVHnIxFKT4QaHVI=
golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
Expand Down Expand Up @@ -363,6 +368,7 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190624190245-7f2218787638 h1:uIfBkD8gLczr4XDgYpt/qJYds2YJwZRNw4zs7wSnNhk=
golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0 h1:9sdfJOzWlkqPltHAuzT2Cp+yrBeY1KRVYgms8soxMwM=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
Expand Down Expand Up @@ -405,6 +411,7 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
www.velocidex.com/golang/evtx v0.0.0-20190210013513-b45fe1505163 h1:V7V2q7jlyUaJMdYMbugnIJobhCmOCMyU8zxhogVU51U=
www.velocidex.com/golang/evtx v0.0.0-20190210013513-b45fe1505163/go.mod h1:6EBI36C3s9YR484Vfw7ZNQywHpDv/XHSaNbFF24xejs=
www.velocidex.com/golang/go-ntfs v0.0.0-20190926065814-c6e6548a412c h1:HYUYb+oEP04/IWzVHw4/XzFNXpcWjouAPwSHiLafKU8=
Expand Down
4 changes: 3 additions & 1 deletion reporting/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,9 @@ func (self *Container) Upload(
}
components = append(components, sanitize(component))
}
sanitized_name := "/" + path.Join(components...)

// Zip members must not have absolute paths.
sanitized_name := path.Join(components...)
writer, err := self.zip.Create(sanitized_name)
if err != nil {
return nil, err
Expand Down
143 changes: 143 additions & 0 deletions vql/tools/gcs_upload.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package tools

import (
"crypto/md5"
"crypto/sha256"
"encoding/hex"
"fmt"
"io"

"cloud.google.com/go/storage"
"golang.org/x/net/context"
"google.golang.org/api/option"
"www.velocidex.com/golang/velociraptor/glob"
"www.velocidex.com/golang/velociraptor/utils"
vql_subsystem "www.velocidex.com/golang/velociraptor/vql"
"www.velocidex.com/golang/velociraptor/vql/networking"
"www.velocidex.com/golang/vfilter"
)

type GCSUploadArgs struct {
File string `vfilter:"required,field=file,doc=The file to upload"`
Name string `vfilter:"optional,field=name,doc=The name of the file that should be stored on the server"`
Accessor string `vfilter:"optional,field=accessor,doc=The accessor to use"`
Bucket string `vfilter:"required,field=bucket,doc=The bucket to upload to"`
Project string `vfilter:"required,field=project,doc=The project to upload to"`
Credentials string `vfilter:"required,field=credentials,doc=The credentials to use"`
}

type GCSUploadFunction struct{}

func (self *GCSUploadFunction) Call(ctx context.Context,
scope *vfilter.Scope,
args *vfilter.Dict) vfilter.Any {

arg := &GCSUploadArgs{}
err := vfilter.ExtractArgs(scope, args, arg)
if err != nil {
scope.Log("upload_gcs: %s", err.Error())
return vfilter.Null{}
}

accessor, err := glob.GetAccessor(arg.Accessor, ctx)
if err != nil {
scope.Log("upload_gcs: %v", err)
return vfilter.Null{}
}

file, err := accessor.Open(arg.File)
if err != nil {
scope.Log("upload_gcs: Unable to open %s: %s",
arg.File, err.Error())
return &vfilter.Null{}
}
defer file.Close()

if arg.Name == "" {
arg.Name = arg.File
}

stat, err := file.Stat()
if err != nil {
scope.Log("upload_gcs: Unable to stat %s: %v",
arg.File, err)
} else if !stat.IsDir() {
upload_response, err := upload_gcs(
ctx, scope, file, arg.Project,
arg.Bucket,
arg.Name, arg.Credentials)
if err != nil {
scope.Log("upload_gcs: %v", err)
return vfilter.Null{}
}
return upload_response
}

return vfilter.Null{}
}

func objectURL(objAttrs *storage.ObjectAttrs) string {
return fmt.Sprintf("https://storage.googleapis.com/%s/%s",
objAttrs.Bucket, objAttrs.Name)
}

func upload_gcs(ctx context.Context, scope *vfilter.Scope,
reader io.Reader,
projectID, bucket, name string,
credentials string) (
*networking.UploadResponse, error) {

// Cache the bucket handle between invocations.
var bucket_handle *storage.BucketHandle
bucket_handle_cache := vql_subsystem.CacheGet(scope, bucket)
if bucket_handle_cache == nil {
client, err := storage.NewClient(ctx, option.WithCredentialsJSON(
[]byte(credentials)))
if err != nil {
return nil, err
}

bucket_handle = client.Bucket(bucket)
vql_subsystem.CacheSet(scope, bucket, bucket_handle)

} else {
bucket_handle = bucket_handle_cache.(*storage.BucketHandle)
}

obj := bucket_handle.Object(name)
writer := obj.NewWriter(ctx)

sha_sum := sha256.New()
md5_sum := md5.New()
log_writer := &vql_subsystem.LogWriter{
Scope: scope,
Message: "upload_gcs " + name}

n, err := utils.Copy(ctx, utils.NewTee(
writer, sha_sum, md5_sum, log_writer), reader)
if err != nil {
return &networking.UploadResponse{
Error: err.Error(),
}, err
}

return &networking.UploadResponse{
Path: name,
Size: uint64(n),
Sha256: hex.EncodeToString(sha_sum.Sum(nil)),
Md5: hex.EncodeToString(md5_sum.Sum(nil)),
}, nil
}

func (self GCSUploadFunction) Info(
scope *vfilter.Scope, type_map *vfilter.TypeMap) *vfilter.FunctionInfo {
return &vfilter.FunctionInfo{
Name: "upload_gcs",
Doc: "Upload files to GCS.",
ArgType: type_map.AddType(scope, &GCSUploadFunction{}),
}
}

func init() {
vql_subsystem.RegisterFunction(&GCSUploadFunction{})
}
26 changes: 26 additions & 0 deletions vql/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package vql

import (
"encoding/json"
"time"

"github.com/pkg/errors"
actions_proto "www.velocidex.com/golang/velociraptor/actions/proto"
Expand Down Expand Up @@ -61,3 +62,28 @@ func RowToDict(scope *vfilter.Scope, row vfilter.Row) *vfilter.Dict {

return result
}

// A writer which periodically reports how much has been
// written. Useful for tee with another writer.
type LogWriter struct {
Scope *vfilter.Scope
Message string
Period time.Duration

next_log time.Time
total_size int
}

func (self *LogWriter) Write(buff []byte) (int, error) {
if self.Period == 0 {
self.Period = 5 * time.Second
}

self.total_size += len(buff)
if time.Now().After(self.next_log) {
self.next_log = time.Now().Add(self.Period)
self.Scope.Log("%s: Uploaded %v bytes",
self.Message, self.total_size)
}
return len(buff), nil
}
8 changes: 8 additions & 0 deletions vql/windows/filesystems/ntfs_lazy_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,21 @@ func ExtractI30List(accessor_ctx *AccessorContext,
} else {
lru_map = make(map[string]*cacheMFT)
for _, record := range mft_entry.Dir(ntfs_ctx) {
if !record.IsValid() {
continue
}

filename := record.File()
name_type := filename.NameType().Name
if name_type == "DOS" {
continue
}

component := filename.Name()
if component == "." || component == ".." {
continue
}

mft_id := int64(record.MftReference())
lru_map[strings.ToLower(component)] = &cacheMFT{
mft_id: mft_id,
Expand Down
3 changes: 2 additions & 1 deletion vql/windows/filesystems/ntfs_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ func (self *NTFSFileSystemAccessor) getNTFSContext(device string) (
reader: reader,
fd: raw_fd,
ntfs_ctx: ntfs_ctx,
path_listing: cache.NewLRUCache(2000),
path_listing: cache.NewLRUCache(200),
}
fd_cache[device] = ctx
timestamp = time.Now()
Expand Down Expand Up @@ -588,6 +588,7 @@ func Open(self *ntfs.MFT_ENTRY, accessor_ctx *AccessorContext, device, filename
return nil, errors.New("Not found")
}

// NOTE: This refreshes each parent directory in the LRU.
directory := self
path := ""
for _, component := range components {
Expand Down

0 comments on commit ee80bfd

Please sign in to comment.