-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
286 lines (234 loc) · 8.89 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
package main
import (
"archive/zip"
"flag"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"time"
)
// Loggers for different log levels
var (
WarnLogger *log.Logger
InfoLogger *log.Logger
ErrLogger *log.Logger
)
// Configuration struct holds parameters for the application
type Configuration struct {
OutputDir string // Directory for the generated zip archive
OutputFile string // Name of the generated zip archive file
LogFile string // Log file path
SftpAddr string // SFTP server address
SftpUser string // SFTP server username
SftpPass string // SFTP server password
SftpDir string // Target directory on the SFTP server
SftpFile string // Target filename on the SFTP server
DagobertAddr string // Dagobert server URL
DagobertCase string // Dagobert case id
DagobertKey string // Dagobert API key
DagobertFile string // Target filename on Dagobert
CollectionRoots []string // Search root paths
QuackTargets string // Quack collection paths file
KapeTargets string // Kape target name
KapeFiles string // Directory with Kape target and module files
RawAccess bool // Use raw NTFS access (Windows only)
SkipTraversal bool
SkipCollection bool
SkipUpload bool
SkipCleanup bool
}
func main() {
// Initialize loggers
InfoLogger = log.New(os.Stdout, "INFO: ", log.Ldate|log.Ltime)
WarnLogger = log.New(os.Stdout, "WARN: ", log.Ldate|log.Ltime)
ErrLogger = log.New(os.Stdout, "ERRR: ", log.Ldate|log.Ltime)
// Record the start time
begin := time.Now()
defer func() {
// Log the total execution time
InfoLogger.Printf("donald finished in %v.", time.Since(begin))
}()
// Parse command line parameters
cfg, err := ParseConfig()
if err != nil {
ErrLogger.Fatalf("Coud not parse command line parameters: %q", err)
}
paths, err := step1TraverseFS(cfg)
if err != nil {
ErrLogger.Fatalf("Stage 1: Unrecoverable error: %v", err)
}
err = step2CollectFiles(cfg, paths)
if err != nil {
ErrLogger.Fatalf("Stage 2: Unrecoverable error: %v", err)
}
err = step3UploadSFTP(cfg)
if err != nil {
ErrLogger.Fatalf("Stage 3 (SFTP): Unrecoverable error: %v", err)
}
err = step3UploadDagobert(cfg)
if err != nil {
ErrLogger.Fatalf("Stage 3 (Dagobert): Unrecoverable error: %v", err)
}
err = step4CleanUp(cfg)
if err != nil {
ErrLogger.Fatalf("Stage 4: Unrecoverable error: %v", err)
}
}
// step1TraverseFS traverses the file system based on the provided Configuration,
// collects file paths, and logs the progress.
func step1TraverseFS(cfg Configuration) ([]string, error) {
if cfg.SkipTraversal {
InfoLogger.Println("Stage 1: Traversing file tree skipped.")
return []string{}, nil
}
// Log the start of Stage 1
InfoLogger.Println("Stage 1: Traversing file tree ...")
start := time.Now()
paths, err := GetPaths(cfg)
if err != nil {
return nil, err
}
// Log the completion of Stage 1 along with the elapsed time
InfoLogger.Printf("Stage 1 finished in %v", time.Since(start))
return paths, nil
}
// step2CollectFiles collects files based on the provided file paths, creates a zip archive,
// and logs the progress.
func step2CollectFiles(cfg Configuration, paths []string) error {
if cfg.SkipCollection {
InfoLogger.Println("Stage 2: Collecting files skipped.")
return nil
}
// Log the start of Stage 2
InfoLogger.Println("Stage 2: Collecting files ...")
start := time.Now()
// Create the output file for the zip archive
fh, err := os.Create(filepath.Join(cfg.OutputDir, cfg.OutputFile))
if err != nil {
return err
}
// Create a zip archive
archive := zip.NewWriter(fh)
// Iterate over file paths and collect files into the zip archive
for _, path := range paths {
if cfg.RawAccess {
err = CollectFileRaw(cfg, archive, path)
} else {
err = CollectFile(cfg, archive, path)
}
if err != nil {
// Log a warning if file collection fails for a specific path
WarnLogger.Printf("Stage 2: Failed to collect file: %v", err)
}
}
// Close the zip archive
err = archive.Close()
if err != nil {
return err
}
// Log the completion of Stage 2 along with the elapsed time
InfoLogger.Printf("Stage 2 finished in %v", time.Since(start))
return nil
}
// step3UploadSFTP uploads the zip archive to an SFTP server (if configured)
// and logs the progress.
func step3UploadSFTP(cfg Configuration) error {
// Check if SFTP address is empty, if so, skip the upload
if cfg.SkipUpload || cfg.SftpAddr == "" {
InfoLogger.Println("Stage 3: Uploading archive to SFTP skipped.")
return nil
}
// Log the start of Stage 3
InfoLogger.Println("Stage 3: Uploading archive to SFTP ...")
start := time.Now()
// Upload the zip archive to the SFTP server
err := UploadSFTP(cfg)
if err != nil {
return err
}
// Log the completion of Stage 3 along with the elapsed time
InfoLogger.Printf("Stage 3 (SFTP) finished in %v", time.Since(start))
return nil
}
// step3UploadDagobert uploads the zip archive to an Dagobert server (if configured)
// and logs the progress.
func step3UploadDagobert(cfg Configuration) error {
// Check if Dagobert address is empty, if so, skip the upload
if cfg.SkipUpload || cfg.DagobertAddr == "" {
InfoLogger.Println("Stage 3: Uploading archive to Dagobert skipped.")
return nil
}
// Log the start of Stage 3
InfoLogger.Println("Stage 3: Uploading archive to Dagobert ...")
start := time.Now()
// Upload the zip archive to the SFTP server
err := UploadDagobert(cfg)
if err != nil {
return err
}
// Log the completion of Stage 3 along with the elapsed time
InfoLogger.Printf("Stage 3 (Dagobert) finished in %v", time.Since(start))
return nil
}
// step4CleanUp removes temporary files created during the process
// and logs the progress.
func step4CleanUp(cfg Configuration) error {
if cfg.SkipCleanup {
InfoLogger.Println("Stage 4: Cleanup of temporary files skipped.")
return nil
}
// Log the start of Stage 4
InfoLogger.Println("Stage 4: Cleanup of temporary files ...")
start := time.Now()
// Remove the zip archive file
err := os.Remove(filepath.Join(cfg.OutputDir, cfg.OutputFile))
if err != nil {
return err
}
// Log the completion of Stage 4 along with the elapsed time
InfoLogger.Printf("Stage 4 finished in %v", time.Since(start))
return nil
}
// ParseConfig parses command line parameters and returns a Configuration struct
func ParseConfig() (Configuration, error) {
// Initialize Configuration struct
cfg := Configuration{}
now := time.Now().Format("20060102150405")
hostname, err := os.Hostname()
if err != nil {
return cfg, err
}
// Set command line flags and default values
flag.StringVar(&cfg.OutputDir, "od", ".", "Defines the directory that the zip archive will be created in.")
flag.StringVar(&cfg.OutputFile, "of", hostname+"-"+now+".zip", "Defines the name of the zip archive created.")
flag.StringVar(&cfg.SftpAddr, "sftp-addr", "", "SFTP server address")
flag.StringVar(&cfg.SftpUser, "sftp-user", "", "SFTP username")
flag.StringVar(&cfg.SftpPass, "sftp-pass", "", "SFTP password")
flag.StringVar(&cfg.SftpDir, "sftp-dir", ".", "Defines the output directory on the SFTP server, as it may be a different location than the archive generated on disk.")
flag.StringVar(&cfg.SftpFile, "sftp-file", hostname+"-"+now+".zip", "Defines the name of the zip archive created on the SFTP server.")
flag.StringVar(&cfg.DagobertAddr, "dagobert-addr", "", "Dagobert URL")
flag.StringVar(&cfg.DagobertCase, "dagobert-case", "", "Dagobert case id")
flag.StringVar(&cfg.DagobertKey, "dagobert-key", "", "Dagobert API Key")
flag.StringVar(&cfg.DagobertFile, "dagobert-file", hostname+"-"+now+".zip", "Defines the name of the zip archive created on the SFTP server.")
defaultRoots := strings.Join(DefaulRootPaths(), ", ")
usageRoots := fmt.Sprintf("Defines the search root path(s). If multiple root paths are given, they are traversed in order. (default %q)", defaultRoots)
flag.Func("root", usageRoots, func(s string) error {
cfg.CollectionRoots = append(cfg.CollectionRoots, s)
return nil
})
flag.StringVar(&cfg.QuackTargets, "c", "", "Add custom collection paths (one entry per line). NOTE: Please see example.quack for the syntax.")
flag.StringVar(&cfg.KapeTargets, "kt", "", "The KAPE target configuration to collect, without the extension. ")
flag.StringVar(&cfg.KapeFiles, "kf", "KapeFiles", "Directory containing targets intended for use with KAPE.")
flag.BoolVar(&cfg.RawAccess, "raw", true, "Use raw NTFS access. Only supported on Windows.")
flag.BoolVar(&cfg.SkipTraversal, "skip-traversal", false, "Skip step #1: traversal / enumaration.")
flag.BoolVar(&cfg.SkipCollection, "skip-collection", false, "Skip step #2: collection.")
flag.BoolVar(&cfg.SkipUpload, "skip-upload", false, "Skip step #3: upload.")
flag.BoolVar(&cfg.SkipCleanup, "skip-cleanup", false, "Skip step #4: cleanup.")
flag.Parse()
// Sanity checks & modifications
cfg.SkipTraversal = cfg.SkipTraversal || cfg.SkipCollection
cfg.SkipCleanup = cfg.SkipCleanup || cfg.SkipUpload || (cfg.SftpAddr == "" && cfg.DagobertAddr == "")
return cfg, nil
}