-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
137 lines (123 loc) · 4.78 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
// (c) Siemens AG 2023
//
// SPDX-License-Identifier: MIT
// Packetflix is a capture-as-a-service implementation: it captures network
// packets from the network interfaces of a container, pod, process-less virtual
// IP stack, et cetera. The captured network packets are then streamed live via
// a websocket connection to clients connecting to our service.
//
// If available, then Packetflix will use GhostWire to update stale container
// references with the most up-to-date information as to capture correctly from
// pods and containers which were reloaded, et cetera. For this, run GhostWire
// side-by-side with Packetflix.
package main
import (
"encoding/json"
"fmt"
"net/http"
"runtime"
"strings"
"time"
"github.com/integrii/flaggy"
log "github.com/sirupsen/logrus"
)
// versionPage creates a /version JSON response giving versioning
// information about this micro service.
func versionHandler(w http.ResponseWriter, req *http.Request) {
v := map[string]interface{}{
"name": "packetflix",
"version": SemVersion,
}
j, err := json.Marshal(v)
if err != nil {
log.Errorf("cannot create version JSON: %s", err.Error())
}
_, err = w.Write(j)
if err != nil {
log.Errorf("cannot create /version: %s", err.Error())
}
}
// A HTTP request logging handler that sits in front of the (de)mux so it sees
// all HTTP/WS requests and can log them before handing them to the (de)muxer.
// It only puts itself in front of the specified (de)mux HTTP handler if logging
// of requests (or headers) is enabled, otherwise it directly returns the
// (de)muxer instead.
func requestLogger(h http.Handler) http.Handler {
if !LogRequests && !LogRequestHeaders {
return h
}
// Prepend the (de)mux handler with the real logging HTTP handler...
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
addr := req.RemoteAddr
if i := strings.LastIndex(addr, ":"); i != -1 {
addr = addr[:i]
}
log.Infof("%s - - [%s] %q %d %d %q %q",
addr,
time.Now().Format("02/Jan/2006:15:04:05 -0700"),
fmt.Sprintf("%s %s %s", req.Method, req.URL.Path, req.Proto),
-1,
-1,
req.Referer(),
req.UserAgent())
// Hand over to the (de)muxer handler...
h.ServeHTTP(w, req)
})
}
// The drama unfolds...
func main() {
// Not strictly necessary, but makes life easier for lxkns/Ghostwire:
// without it, we might end up with the situation where the OS-level thread
// from which we started the dumpcap binary and which we thus switched into
// the target's network namespace ends up being the main thread and thus the
// process itself. Now that then makes lxkns see packetflix' process being
// the ealdorman in a network namespace different from its container's
// network namespace.
runtime.LockOSThread()
log.SetFormatter(&log.TextFormatter{
ForceColors: true,
FullTimestamp: true,
})
// Get the show going...
log.Infof("Packetflix Capture-as-a-Service version %s", SemVersion)
// Set up CLI arg parsing.
flaggy.SetName("Packetflix")
flaggy.SetDescription("live network packet capture streaming from untouchable containers")
flaggy.SetVersion(SemVersion)
flaggy.Bool(&Debug, "", "debug", "log debugging messages")
flaggy.Bool(&LogRequests, "", "log-requests", "log frontend HTTP/WS requests")
flaggy.Bool(&LogRequestHeaders, "", "log-headers", "log frontend HTTP/WS request headers")
flaggy.UInt16(&Port, "p", "port",
"port to expose capture service on")
flaggy.String(&DiscoveryService, "", "discovery-service",
"name/address of discovery service")
flaggy.UInt16(&DiscoveryPort, "", "gw-port",
"port of local GhostWire discovery service")
flaggy.Bool(&ProxyDiscoveryService, "", "proxy-discovery",
fmt.Sprintf("enable/disable proxy discovery service SPA and API (default: %t)", ProxyDiscoveryService))
flaggy.Parse()
if Debug {
log.SetLevel(log.DebugLevel)
log.Debug("debugging messages enabled")
}
// For unknown reasons the "thing" reponsible for demultiplexing incomming
// HTTP requests onto HTTP handlers is termaed a "multiplexer". Yet, this is
// a demultiplexer. Confusing.
//
// See: https://en.wikipedia.org/wiki/Multiplexer
demux := http.NewServeMux()
demux.HandleFunc("/capture", captureHandler)
demux.HandleFunc("/version", versionHandler)
if ProxyDiscoveryService {
// Enable reverse proxying to the associated GhostWire discovery service
// instance at (fixed) path "/" ... everything not handled otherwise
// will thus end up in the Ghostwire service and its SPA.
log.Debug("forwarding to discovery service enabled")
demux.Handle("/", gwHandler)
}
log.Infof("starting capture service websocket server on port %d...", Port)
log.Error(
http.ListenAndServe(
fmt.Sprintf("[::]:%d", Port),
requestLogger(demux)))
}