|
1 | 1 | package loggers |
2 | 2 |
|
3 | 3 | import ( |
4 | | - "fmt" |
5 | | - "net" |
6 | | - "os" |
7 | | - "sync" |
8 | | - "sync/atomic" |
9 | | - "time" |
10 | | - |
11 | 4 | "github.com/evilsocket/opensnitch/daemon/log" |
12 | | - "github.com/evilsocket/opensnitch/daemon/log/formats" |
13 | 5 | ) |
14 | 6 |
|
15 | 7 | const ( |
16 | 8 | LOGGER_REMOTE_SYSLOG = "remote_syslog" |
17 | | - // restart syslog connection after these amount of errors |
18 | | - maxAllowedErrors = 10 |
19 | | -) |
20 | | - |
21 | | -var ( |
22 | | - // default write / connect timeouts |
23 | | - writeTimeout, _ = time.ParseDuration("1s") |
24 | | - connTimeout, _ = time.ParseDuration("5s") |
25 | | -) |
26 | | - |
27 | | -// connection status |
28 | | -const ( |
29 | | - DISCONNECTED = iota |
30 | | - CONNECTED |
31 | | - CONNECTING |
32 | 9 | ) |
33 | 10 |
|
34 | | -// RemoteSyslog defines the logger that writes traces to the syslog. |
35 | | -// It can write to the local or a remote daemon. |
36 | 11 | type RemoteSyslog struct { |
37 | | - Syslog |
38 | | - mu *sync.RWMutex |
39 | | - netConn net.Conn |
40 | | - Hostname string |
41 | | - Timeout time.Duration |
42 | | - ConnectTimeout time.Duration |
43 | | - errors uint32 |
44 | | - status uint32 |
| 12 | + Remote |
45 | 13 | } |
46 | 14 |
|
47 | 15 | // NewRemoteSyslog returns a new object that manipulates and prints outbound connections |
48 | 16 | // to a remote syslog server, with the given format (RFC5424 by default) |
49 | 17 | func NewRemoteSyslog(cfg LoggerConfig) (*RemoteSyslog, error) { |
50 | | - var err error |
51 | | - log.Info("NewSyslog logger: %v", cfg) |
52 | | - |
53 | | - sys := &RemoteSyslog{ |
54 | | - mu: &sync.RWMutex{}, |
55 | | - } |
56 | | - sys.Name = LOGGER_REMOTE_SYSLOG |
57 | | - sys.cfg = cfg |
58 | | - |
59 | | - // list of allowed formats for this logger |
60 | | - sys.logFormat = formats.NewRfc5424() |
61 | | - if cfg.Format == formats.RFC3164 { |
62 | | - sys.logFormat = formats.NewRfc3164() |
63 | | - } else if cfg.Format == formats.CSV { |
64 | | - sys.logFormat = formats.NewCSV() |
65 | | - } |
66 | | - |
67 | | - sys.Tag = logTag |
68 | | - if cfg.Tag != "" { |
69 | | - sys.Tag = cfg.Tag |
70 | | - } |
71 | | - sys.Hostname, err = os.Hostname() |
72 | | - if err != nil { |
73 | | - sys.Hostname = "localhost" |
74 | | - } |
75 | | - sys.Timeout, err = time.ParseDuration(cfg.WriteTimeout) |
76 | | - if err != nil || cfg.WriteTimeout == "" { |
77 | | - sys.Timeout = writeTimeout |
78 | | - } |
79 | | - |
80 | | - sys.ConnectTimeout, err = time.ParseDuration(cfg.ConnectTimeout) |
81 | | - if err != nil || cfg.ConnectTimeout == "" { |
82 | | - sys.ConnectTimeout = connTimeout |
83 | | - } |
84 | | - |
85 | | - if err = sys.Open(); err != nil { |
86 | | - log.Error("Error loading logger [%s]: %s", sys.Name, err) |
87 | | - return nil, err |
88 | | - } |
89 | | - log.Info("[%s] initialized: %v", sys.Name, cfg) |
90 | | - |
91 | | - return sys, err |
92 | | -} |
93 | | - |
94 | | -// Open opens a new connection with a server or with the daemon. |
95 | | -func (s *RemoteSyslog) Open() (err error) { |
96 | | - atomic.StoreUint32(&s.errors, 0) |
97 | | - if s.cfg.Server == "" { |
98 | | - return fmt.Errorf("[%s] Server address must not be empty", s.Name) |
99 | | - } |
100 | | - s.mu.Lock() |
101 | | - s.netConn, err = s.Dial(s.cfg.Protocol, s.cfg.Server, s.ConnectTimeout) |
102 | | - s.mu.Unlock() |
| 18 | + log.Info("NewRemoteSyslog logger: %v", cfg) |
103 | 19 |
|
104 | | - if err == nil { |
105 | | - atomic.StoreUint32(&s.status, CONNECTED) |
106 | | - } |
107 | | - return err |
108 | | -} |
109 | | - |
110 | | -// Dial opens a new connection with a syslog server. |
111 | | -func (s *RemoteSyslog) Dial(proto, addr string, connTimeout time.Duration) (netConn net.Conn, err error) { |
112 | | - switch proto { |
113 | | - case "udp", "tcp": |
114 | | - netConn, err = net.DialTimeout(proto, addr, connTimeout) |
115 | | - if err != nil { |
116 | | - return nil, err |
117 | | - } |
118 | | - default: |
119 | | - return nil, fmt.Errorf("[%s] Network protocol %s not supported", s.Name, proto) |
120 | | - } |
121 | | - |
122 | | - return netConn, nil |
123 | | -} |
124 | | - |
125 | | -// Close closes the writer object |
126 | | -func (s *RemoteSyslog) Close() (err error) { |
127 | | - //s.mu.RLock() |
128 | | - if s.netConn != nil { |
129 | | - err = s.netConn.Close() |
130 | | - s.netConn = nil |
131 | | - } |
132 | | - //s.mu.RUnlock() |
133 | | - atomic.StoreUint32(&s.status, DISCONNECTED) |
134 | | - return |
135 | | -} |
136 | | - |
137 | | -// ReOpen tries to reestablish the connection with the writer |
138 | | -func (s *RemoteSyslog) ReOpen() { |
139 | | - if atomic.LoadUint32(&s.status) == CONNECTING { |
140 | | - return |
141 | | - } |
142 | | - atomic.StoreUint32(&s.status, CONNECTING) |
143 | | - if err := s.Close(); err != nil { |
144 | | - log.Debug("[%s] error closing Close(): %s", s.Name, err) |
| 20 | + r, err := NewRemote(cfg) |
| 21 | + r.Name = LOGGER_REMOTE_SYSLOG |
| 22 | + rs := &RemoteSyslog{ |
| 23 | + Remote: *r, |
145 | 24 | } |
146 | 25 |
|
147 | | - if err := s.Open(); err != nil { |
148 | | - log.Debug("[%s] ReOpen() error: %s", s.Name, err) |
149 | | - return |
150 | | - } |
151 | | -} |
152 | | - |
153 | | -// Transform transforms data for proper ingestion. |
154 | | -func (s *RemoteSyslog) Transform(args ...interface{}) (out string) { |
155 | | - if s.logFormat != nil { |
156 | | - args = append(args, s.Hostname) |
157 | | - args = append(args, s.Tag) |
158 | | - out = s.logFormat.Transform(args...) |
159 | | - } |
160 | | - return |
161 | | -} |
162 | | - |
163 | | -func (s *RemoteSyslog) Write(msg string) { |
164 | | - deadline := time.Now().Add(s.Timeout) |
165 | | - |
166 | | - // BUG: it's fairly common to have write timeouts via udp/tcp. |
167 | | - // Reopening the connection with the server helps to resume sending events to syslog, |
168 | | - // and have a continuous stream of events. Otherwise it'd stop working. |
169 | | - // I haven't figured out yet why these write errors ocurr. |
170 | | - s.mu.RLock() |
171 | | - defer s.mu.RUnlock() |
172 | | - if s.netConn == nil { |
173 | | - s.ReOpen() |
174 | | - return |
175 | | - } |
176 | | - s.netConn.SetWriteDeadline(deadline) |
177 | | - _, err := s.netConn.Write([]byte(msg)) |
178 | | - if err == nil { |
179 | | - return |
180 | | - } |
181 | | - |
182 | | - log.Debug("[%s] %s write error: %v", s.Name, s.cfg.Protocol, err.(net.Error)) |
183 | | - atomic.AddUint32(&s.errors, 1) |
184 | | - if atomic.LoadUint32(&s.errors) > maxAllowedErrors { |
185 | | - s.ReOpen() |
186 | | - return |
187 | | - } |
| 26 | + return rs, err |
188 | 27 | } |
189 | 28 |
|
190 | 29 | // https://cs.opensource.google/go/go/+/refs/tags/go1.18.2:src/log/syslog/syslog.go;l=286;drc=0a1a092c4b56a1d4033372fbd07924dad8cbb50b |
191 | | -func (s *RemoteSyslog) formatLine(msg string) string { |
| 30 | +func (rs *RemoteSyslog) formatLine(msg string) string { |
192 | 31 | return msg |
193 | 32 | } |
0 commit comments