Skip to content

Commit 0d48ee9

Browse files
authored
Bugfix: increase max size of embedded config to 100Mb (#4690)
Also rework error handling for better error reporting when the file is corrupt.
1 parent 91fa1b7 commit 0d48ee9

File tree

9 files changed

+121
-24
lines changed

9 files changed

+121
-24
lines changed

bin/main.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ import (
2626
"runtime/trace"
2727
"time"
2828

29+
"errors"
30+
2931
kingpin "github.com/alecthomas/kingpin/v2"
30-
errors "github.com/go-errors/errors"
3132
"www.velocidex.com/golang/velociraptor/config"
3233
config_proto "www.velocidex.com/golang/velociraptor/config/proto"
3334
"www.velocidex.com/golang/velociraptor/constants"
@@ -152,6 +153,10 @@ func main() {
152153
args = append(args, post...)
153154
Prelog("Autoexec with parameters: %v", args)
154155
}
156+
157+
if errors.Is(err, utils.EmbeddedConfigError) {
158+
kingpin.FatalIfError(err, "EmbeddedConfigError")
159+
}
155160
}
156161

157162
// Log the actual argv that will be run.

config/embedded.go

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package config
33
import (
44
"bytes"
55
"compress/zlib"
6+
"errors"
7+
"fmt"
68
"io"
79
"os"
810

@@ -16,30 +18,34 @@ func ExtractEmbeddedConfig(
1618

1719
fd, err := os.Open(embedded_file)
1820
if err != nil {
19-
return nil, err
21+
return nil, fmt.Errorf("%w: %v", utils.EmbeddedConfigError, err)
2022
}
2123

2224
// Read a lot of the file into memory so we can extract the
23-
// configuration. This solution only loads the first 10mb into
25+
// configuration. This solution only loads the first 100mb into
2426
// memory which should be sufficient for most practical config
2527
// files. If there are embedded binaries they will not be read and
2628
// will be ignored at this stage (thay can be extracted with the
2729
// 'me' accessor).
28-
buf, err := utils.ReadAllWithLimit(fd, 10*1024*1024)
29-
if err != nil {
30-
return nil, err
30+
buf, err := utils.ReadAllWithLimit(fd, 100*1024*1024)
31+
32+
// It is ok to have a short read here.
33+
if err != nil && !errors.Is(err, utils.MemoryBufferExceeded) {
34+
return nil, fmt.Errorf("%w: %v", utils.EmbeddedConfigError, err)
3135
}
3236

3337
// Find the embedded marker in the buffer.
3438
match := embedded_re.FindIndex(buf)
3539
if match == nil {
36-
return nil, noEmbeddedConfig
40+
return nil, fmt.Errorf("%w: Unable to find signature", utils.EmbeddedConfigError)
3741
}
3842

3943
embedded_string := buf[match[0]:]
4044
return decode_embedded_config(embedded_string)
4145
}
4246

47+
// Read the embedded config from this binary - just uses the static
48+
// allocated string variable.
4349
func read_embedded_config() (*config_proto.Config, error) {
4450
return decode_embedded_config(FileConfigDefaultYaml)
4551
}
@@ -49,14 +55,15 @@ func decode_embedded_config(encoded_string []byte) (*config_proto.Config, error)
4955
idx := bytes.IndexByte(encoded_string, '\n')
5056

5157
if len(encoded_string) < idx+10 {
52-
return nil, noEmbeddedConfig
58+
return nil, fmt.Errorf("%w: embedded file too short", utils.EmbeddedConfigError)
5359
}
5460

5561
// If the following line still starts with # then the file is not
5662
// repacked - the repacker will replace all further data with the
5763
// compressed string.
5864
if encoded_string[idx+1] == '#' {
59-
return nil, noEmbeddedConfig
65+
return nil, fmt.Errorf("%w: embedded file is not packed yet",
66+
utils.EmbeddedConfigError)
6067
}
6168

6269
// Decompress the rest of the data - note that zlib will ignore
@@ -65,20 +72,20 @@ func decode_embedded_config(encoded_string []byte) (*config_proto.Config, error)
6572
// whole string here.
6673
r, err := zlib.NewReader(bytes.NewReader(encoded_string[idx+1:]))
6774
if err != nil {
68-
return nil, err
75+
return nil, fmt.Errorf("%w: %v", utils.EmbeddedConfigError, err)
6976
}
7077

7178
b := &bytes.Buffer{}
7279
_, err = io.Copy(b, r)
7380
if err != nil {
74-
return nil, err
81+
return nil, fmt.Errorf("%w: %v", utils.EmbeddedConfigError, err)
7582
}
7683
r.Close()
7784

7885
result := &config_proto.Config{}
7986
err = yaml.Unmarshal(b.Bytes(), result)
8087
if err != nil {
81-
return nil, err
88+
return nil, fmt.Errorf("%w: %v", utils.EmbeddedConfigError, err)
8289
}
8390
return result, nil
8491
}

config/loader.go

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ import (
1818
)
1919

2020
var (
21-
noEmbeddedConfig = errors.New(
22-
"No embedded config - you can pack one with the `config repack` command")
21+
silentError = errors.New("Silent")
2322

2423
embedded_re = regexp.MustCompile(`#{3}<Begin Embedded Config>\r?\n`)
2524

@@ -312,17 +311,23 @@ func (self *Loader) WithEmbedded(embedded_file string) *Loader {
312311
self.loaders = append(self.loaders, loaderFunction{
313312
name: "WithEmbedded",
314313
loader_func: func(self *Loader) (*config_proto.Config, error) {
314+
// Try to get the embedded config inside the binary.
315315
if embedded_file == "" {
316316
result, err := read_embedded_config()
317317
if err != nil {
318-
return nil, err
318+
// not a critical error - the binary does not have
319+
// to have an embedded config
320+
return nil, silentError
319321
}
320322

321-
self.Log("Loaded embedded config")
323+
self.Log("Loaded embedded config from binary")
322324

325+
// Set the EmbeddedFile for the "me" accessor- we will
326+
// get binaries from this file.
323327
EmbeddedFile, err = os.Executable()
324328
return result, err
325329
}
330+
326331
// Ensure the "me" accessor uses this file for embedded zip.
327332
full_path, err := filepath.Abs(embedded_file)
328333
if err != nil {
@@ -334,9 +339,11 @@ func (self *Loader) WithEmbedded(embedded_file string) *Loader {
334339
result, err := ExtractEmbeddedConfig(full_path)
335340
if err == nil {
336341
self.Log("Loaded embedded config from %v", full_path)
342+
return result, nil
337343
}
338-
return result, err
339344

345+
self.Log("Unable to parse embedded config from %v: %v", full_path, err.Error())
346+
return result, HardError{err}
340347
}})
341348
return self
342349
}
@@ -492,11 +499,13 @@ func (self *Loader) LoadAndValidate() (*config_proto.Config, error) {
492499
}
493500

494501
// Stop on hard errors.
495-
_, ok := err.(HardError)
502+
he, ok := err.(HardError)
496503
if ok {
497-
return nil, err
504+
return nil, he.Err
505+
}
506+
if err != silentError {
507+
self.Log("%v", err)
498508
}
499-
self.Log("%v", err)
500509
}
501510
return nil, errors.New("Unable to load config from any source.")
502511
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package server
2+
3+
import (
4+
"archive/zip"
5+
"encoding/json"
6+
"net/http"
7+
8+
"github.com/Velocidex/ordereddict"
9+
"www.velocidex.com/golang/velociraptor/logging"
10+
"www.velocidex.com/golang/velociraptor/services"
11+
"www.velocidex.com/golang/velociraptor/services/debug"
12+
"www.velocidex.com/golang/velociraptor/vql/acl_managers"
13+
"www.velocidex.com/golang/vfilter"
14+
)
15+
16+
func (self *debugMux) handleEverything(w http.ResponseWriter, r *http.Request) {
17+
w.Header().Set("Content-Type", "application/zip")
18+
w.Header().Set("Content-Disposition", "attachment; profiles.zip")
19+
w.WriteHeader(200)
20+
21+
zip_writer := zip.NewWriter(w)
22+
defer zip_writer.Close()
23+
24+
builder := services.ScopeBuilder{
25+
Config: self.config_obj,
26+
ACLManager: acl_managers.NullACLManager{},
27+
Env: ordereddict.NewDict(),
28+
}
29+
30+
manager, err := services.GetRepositoryManager(self.config_obj)
31+
if err != nil {
32+
return
33+
}
34+
35+
scope := manager.BuildScope(builder)
36+
defer scope.Close()
37+
38+
seen := make(map[string]bool)
39+
40+
for _, i := range debug.GetProfileWriters() {
41+
_, pres := seen[i.Name]
42+
if pres {
43+
logger := logging.GetLogger(self.config_obj, &logging.FrontendComponent)
44+
logger.Error("Non unique profile name %v", i.Name)
45+
}
46+
seen[i.Name] = true
47+
48+
fd, err := zip_writer.CreateHeader(&zip.FileHeader{
49+
Name: i.Name,
50+
Method: zip.Deflate,
51+
})
52+
if err != nil {
53+
continue
54+
}
55+
56+
output_chan := make(chan vfilter.Row)
57+
go func() {
58+
defer close(output_chan)
59+
60+
i.ProfileWriter(r.Context(), scope, output_chan)
61+
}()
62+
63+
for row := range output_chan {
64+
serialized, _ := json.Marshal(row)
65+
serialized = append(serialized, '\n')
66+
_, _ = fd.Write(serialized)
67+
}
68+
}
69+
}

services/debug/server/handlers.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ func (self *debugMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
136136
case "metrics":
137137
promhttp.Handler().ServeHTTP(w, r)
138138

139+
case "everything":
140+
self.handleEverything(w, r)
141+
139142
default:
140143
self.HandleIndex(w, r)
141144
}

services/debug/server/template.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ $(document).ready(function() {
135135
</head>
136136
<body>
137137
<h1>Velociraptor Debug Server</h1>
138+
<a href="%s/debug/everything">Everything</a>
138139
<div class="category">Internal</div>
139140
<ul class="categories">
140141
<li><a href="%s/debug/metrics">Metrics</a></li>
@@ -202,7 +203,7 @@ func (self *debugMux) renderCategory(node *debug.CategoryTreeNode) string {
202203
func (self *debugMux) HandleIndex(w http.ResponseWriter, r *http.Request) {
203204
w.Header().Set("Content-Type", "text/html; charset=utf-8")
204205

205-
_, _ = w.Write([]byte(fmt.Sprintf(indexHeader, self.base, self.base)))
206+
_, _ = w.Write([]byte(fmt.Sprintf(indexHeader, self.base, self.base, self.base)))
206207
categories := debug.GetProfileTree()
207208
_, _ = w.Write([]byte(self.renderCategory(categories)))
208209
_, _ = w.Write([]byte(`</body></html>`))

services/spec.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ func ClientServicesSpec() *config_proto.ServerServicesConfig {
3636
Launcher: true,
3737
HttpCommunicator: true,
3838
ClientEventTable: true,
39+
ClientInfo: true,
3940
}
4041
}
4142

utils/copy.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,16 @@ var (
1717
return &buffer
1818
},
1919
}
20+
21+
MemoryBufferExceeded = Wrap(IOError, "Memory buffer exceeded")
2022
)
2123

22-
func ReadAllWithLimit(
23-
fd io.Reader, limit int) ([]byte, error) {
24+
func ReadAllWithLimit(fd io.Reader, limit int) ([]byte, error) {
2425

2526
// If we reach the limit signal this as an error!
2627
res, err := ioutil.ReadAll(io.LimitReader(fd, int64(limit)))
2728
if len(res) >= limit {
28-
return nil, Wrap(IOError, "Memory buffer exceeded")
29+
return res, MemoryBufferExceeded
2930
}
3031

3132
return res, err

utils/errors.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ var (
2323
CancelledError = errors.New("Cancelled")
2424
SecretsEnforced = errors.New("Secrets are enforced - you must specify a secret name")
2525
PermissionDenied = errors.New("PermissionDenied")
26+
EmbeddedConfigError = errors.New("EmbeddedConfigError")
2627
)
2728

2829
// This is a custom error type that wraps an inner error but does not

0 commit comments

Comments
 (0)