-
Notifications
You must be signed in to change notification settings - Fork 0
/
api.go
288 lines (262 loc) · 8.18 KB
/
api.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
287
288
package opnborg
import (
"errors"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
)
// global const
const _version = "v0.0.23"
// global var
var sleep, prometheusWebUI, grafanaWebUI, grafanaFreeBSD, grafanaHAProxy string
// OPNCall
type OPNCall struct {
Targets string // list of OPNSense Appliances, csv comma seperated
Key string // OPNSense Backup User API Key (required)
Secret string // OPNSense Backup User API Secret (required)
Path string // OPNSense Backup Files Target Path, default:'.'
TLSKeyPin string // TLS Connection Server Certificate KeyPIN
AppName string // Display and SysLog Application Name
Email string // Git Commiter eMail Address (default: git@opnborg)
Sleep int64 // number of seconds to sleep between polls
Daemon bool // daemonize (run in background), default: true
Debug bool // verbose debug logs, defaults to false
Git bool // create and commit all xml files & changes to local .git repo, default: true
extGIT bool // when available, use external git for verification
dirty atomic.Bool // git global (atomic) worktree state
Httpd struct {
Enable bool // enable internal web server
Server string // internal httpd server listen ip & port (string, default: 127.0.0.1:6464)
CAcert string // httpd server certificate (path to pem encoded x509 file - full certificate chain)
CAkey string // httpd server key (path to pem encoded tls server key file)
CAClient string // httpd client CA (path to pem endcoded x509 file - if set, it will enforce mTLS-only mode)
}
Prometheus struct {
Enable bool
WebUI string
}
Grafana struct {
Enable bool
WebUI string
FreeBSD string
HAProxy string
}
RSysLog struct {
Enable bool // enable RFC5424 compliant remote syslog store server (default: false)
Server string // internal syslog listen ip and port [ example: 192.168.0.100:5140 ] (required)
}
Sync struct {
Enable bool // enable Master Server
validConf bool // internal state (skip if master conf is invalid/unreachable)
Master string // Master Server Name
PKG struct {
Enable bool // enable packages sync
Packages []string // list of Packages to sync
}
}
}
// Setup reads OPNBorgs configuration via env, sanitizes, sets sane defaults
func Setup() (*OPNCall, error) {
// check if setup requirements are meet
if err := checkRequired(); err != nil {
return nil, err
}
// setup from env
config := &OPNCall{
Targets: os.Getenv("OPN_TARGETS"),
Key: os.Getenv("OPN_APIKEY"),
Secret: os.Getenv("OPN_APISECRET"),
TLSKeyPin: os.Getenv("OPN_TLSKEYPIN"),
Path: os.Getenv("OPN_PATH"),
Email: os.Getenv("OPN_EMAIL"),
}
// setup app
if config.AppName == "" {
config.AppName = "[OPNBORG-API]"
}
// sanitize input
if config.Path == "" {
config.Path = filepath.Dir("./")
}
// validate bools, set defaults
config.Debug = false
if _, ok := os.LookupEnv("OPN_DEBUG"); ok {
config.Debug = true
}
config.Git = true
if _, ok := os.LookupEnv("OPN_NOGIT"); ok {
config.Git = false
}
config.Daemon = true
if _, ok := os.LookupEnv("OPN_NODAEMON"); ok {
config.Daemon = false
}
// configure remote syslog server
config.RSysLog.Enable = false
if config.Daemon {
if _, ok := os.LookupEnv("OPN_RSYSLOG_ENABLE"); ok {
if _, ok := os.LookupEnv("OPN_RSYSLOG_SERVER"); ok {
config.RSysLog.Enable = true
config.RSysLog.Server = os.Getenv("OPN_RSYSLOG_SERVER")
if len(strings.Split(config.RSysLog.Server, ":")) < 1 {
return nil, errors.New(fmt.Sprintf("env var 'OPN_RSYSLOG_SRV' format error, example \"192.168.0.100:5140\""))
}
}
}
}
// configure httpd
config.Httpd.Enable = false
if config.Daemon {
if _, ok := os.LookupEnv("OPN_HTTPD_ENABLE"); ok {
if _, ok := os.LookupEnv("OPN_HTTPD_SERVER"); ok {
config.Httpd.Enable = true
config.Httpd.Server = os.Getenv("OPN_HTTPD_SERVER")
if config.Httpd.Server == "" {
config.Httpd.Server = "127.0.0.1:6464"
}
if len(strings.Split(config.Httpd.Server, ":")) < 1 {
return nil, errors.New(fmt.Sprintf("env var 'OPN_HTTPD_SRV' format error, example \"127.0.0.1:6464\""))
}
config.Httpd.CAcert = os.Getenv("OPN_HTTPD_CACERT")
config.Httpd.CAkey = os.Getenv("OPN_HTTPD_CAKEY")
config.Httpd.CAClient = os.Getenv("OPN_HTTPD_CACLIENT")
}
}
}
// config Master
config.Sync.Enable = false
config.Sync.validConf = false
config.Sync.PKG.Enable = false
if _, ok := os.LookupEnv("OPN_MASTER"); ok {
config.Sync.Enable = true
config.Sync.Master = os.Getenv("OPN_MASTER")
if _, ok := os.LookupEnv("OPN_SYNC_PKG"); ok {
config.Sync.PKG.Enable = true
}
}
// prometheus
if _, ok := os.LookupEnv("OPN_PROMETHEUS_WEBUI"); ok {
config.Prometheus.Enable = true
config.Prometheus.WebUI = os.Getenv("OPN_PROMETHEUS_WEBUI")
config.Prometheus.WebUI = config.Prometheus.WebUI
prometheusWebUI = config.Prometheus.WebUI
}
// grafana
if _, ok := os.LookupEnv("OPN_GRAFANA_WEBUI"); ok {
config.Grafana.Enable = true
config.Grafana.WebUI = os.Getenv("OPN_GRAFANA_WEBUI")
config.Grafana.WebUI = config.Grafana.WebUI
grafanaWebUI = config.Grafana.WebUI
if _, ok := os.LookupEnv("OPN_GRAFANA_DASHBOARD_FREEBSD"); ok {
config.Grafana.FreeBSD = os.Getenv("OPN_GRAFANA_DASHBOARD_FREEBSD")
grafanaFreeBSD = config.Grafana.FreeBSD
}
if _, ok := os.LookupEnv("OPN_GRAFANA_DASHBOARD_HAPROXY"); ok {
config.Grafana.HAProxy = os.Getenv("OPN_GRAFANA_DASHBOARD_HAPROXY")
grafanaHAProxy = config.Grafana.HAProxy
}
}
// configure eMail default
if config.Email == "" {
config.Email = "git@opnborg"
}
// configure sleep for daemon mode
sleep = "0"
if config.Daemon {
config.Sleep = 3600
if sleep, ok := os.LookupEnv("OPN_SLEEP"); ok {
var err error
config.Sleep, err = strconv.ParseInt(sleep, 10, 64)
if err != nil {
return nil, errors.New(fmt.Sprintf("env var 'OPN_SLEEP' must contain a number in seconds without prefix or suffix"))
}
}
if config.Sleep < 10 {
config.Sleep = 10
}
sleep = strconv.FormatInt(config.Sleep, 10)
}
config.extGIT = true
return config, nil
}
// global
var hive []string
var hiveMutex sync.Mutex
// Start Application
func Start(config *OPNCall) error {
// spin up Log/Display Engine
display.Add(1)
go startLog(config)
// spin up internal webserver
go startWeb(config)
// spin up internal rsyslog server
go startRSysLog(config)
// setup hive
servers := strings.Split(config.Targets, ",")
for _, server := range servers {
status := _na + " <b>Member: </b> " + server + " <b>Version: </b>n/a <b>Last Seen: </b>n/a<br>"
hive = append(hive, status)
}
// startup app version & state, sleep panic gate
suffix := "[CLI-ONE-TIME-PASS-MODE]"
if config.Daemon {
suffix = "[DAEMON-MODE][SLEEP:" + sleep + " SECONDS]"
}
displayChan <- []byte("[STARTING][" + _app + "][" + _version + "]" + suffix)
// loop
for {
// init
var err error
// fetch target configuration from master server
if config.Sync.Enable {
config.Sync.validConf = true
config, err = readMasterConf(config)
if err != nil {
config.Sync.validConf = false
displayChan <- []byte("[ERROR][UNABLE-TO-READ-MASTER-CONFIG]" + err.Error())
}
}
// reset global (atomic) git worktree state tracker
if config.Git {
config.dirty.Store(false)
}
// spinup individual worker for every server
if config.Debug {
displayChan <- []byte("[STARTING][BACKUP]")
}
for id, server := range servers {
wg.Add(1)
go actionSrv(server, config, id, &wg)
}
// wait till all worker done
wg.Wait()
// check files into local git repo
if config.dirty.Load() {
if config.Git {
if err := gitCheckIn(config); err != nil {
displayChan <- []byte("[GIT][REPO][CHECKIN][FAIL]")
return err
}
displayChan <- []byte("[CHANGES-DETECTED][GIT][REPO][CHECKIN][FINISH]")
}
displayChan <- []byte("[CHANGES-DETECTED][UPDATES-DONE][FINISH]")
}
// finish
if config.Debug {
displayChan <- []byte("[FINISH][BACKUP][ALL]")
}
// exit if not in daemon mode
if !config.Daemon {
close(displayChan)
display.Wait()
return nil
}
// wait loop
time.Sleep(time.Duration(config.Sleep) * time.Second)
}
}