Skip to content

Commit

Permalink
add: unifi-backup-backend
Browse files Browse the repository at this point in the history
  • Loading branch information
paepckehh committed Dec 3, 2024
1 parent 5b5075a commit 6fd454c
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 138 deletions.
115 changes: 0 additions & 115 deletions actionUnifi.go

This file was deleted.

7 changes: 4 additions & 3 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import (
)

// global exported consts
const SemVer = "v0.1.42"
const SemVer = "v0.1.44"

// global var
var (
tg []OPNGroup
unifiEnable, unifiBackupNow atomic.Bool
sleep, borg, pkgmaster string
wazuhWebUI, unifiWebUI, prometheusWebUI *url.URL
grafanaWebUI, grafanaFreeBSD, grafanaUnifi, grafanaHAProxy *url.URL
Expand Down Expand Up @@ -57,7 +58,6 @@ type OPNCall struct {
Version string
Backup struct {
Enable bool
Hour int
User string
Secret string
}
Expand Down Expand Up @@ -92,7 +92,8 @@ type OPNCall struct {
// global
var hive []string
var hiveMutex sync.Mutex
var update = make(chan bool, 1)
var updateOPN = make(chan bool, 1)
var updateUnifi = make(chan bool, 1)

// Start Application Server
func Start(config *OPNCall) error {
Expand Down
1 change: 0 additions & 1 deletion example-env-config-unifi.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,5 @@ export OPN_WAZUH_WEBUI='http://localhost:9292'
export OPN_PROMETHEUS_WEBUI='http://localhost:9191'
export OPN_UNIFI_WEBUI='https://localhost:8443'
export OPN_UNIFI_VERSION='8.5.6'
export OPN_UNIFI_HOUR='22'
export OPN_UNIFI_BACKUP_USER='admin'
export OPN_UNIFI_BACKUP_SECRET='start'
6 changes: 5 additions & 1 deletion httpd-handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ const (
// getForceHandler
func getForceHandler() http.Handler {
h := func(r http.ResponseWriter, q *http.Request) {
update <- true
updateOPN <- true
if unifiEnable.Load() {
unifiBackupNow.Store(true)
updateUnifi <- true
}
r = headHTML(r)
_, _ = r.Write([]byte(_forceRedirect))
}
Expand Down
12 changes: 2 additions & 10 deletions setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ func Setup() (*OPNCall, error) {
if config.Unifi.WebUI, err = checkURL("OPN_UNIFI_WEBUI"); err != nil {
return config, err
}
unifiEnable.Store(false)
if config.Unifi.WebUI != nil {
unifiWebUI = config.Unifi.WebUI
config.Unifi.Backup.Enable = false
Expand All @@ -128,21 +129,12 @@ func Setup() (*OPNCall, error) {
config.Unifi.Backup.Secret = os.Getenv("OPN_UNIFI_BACKUP_SECRET")
}
if config.Unifi.Backup.User != "" && config.Unifi.Backup.Secret != "" {
unifiEnable.Store(true)
config.Unifi.Backup.Enable = true
if _, ok := os.LookupEnv("OPN_UNIFI_VERSION"); !ok {
return config, errors.New("OPN_UNIFI_VERSION must contain the unifi controller version number (eg.: '5.6.9') when backup is enabled")
}
config.Unifi.Version = os.Getenv("OPN_UNIFI_VERSION")
config.Unifi.Backup.Hour = 20
if _, ok := os.LookupEnv("OPN_UNIFI_BACKUP_HOUR"); ok {
hour, err := strconv.Atoi(os.Getenv("OPN_UNIFI_HOUR"))
if err != nil {
return config, err
}
if hour < 0 || hour > 23 {
return config, errors.New("OPN_UNIFI_BACKUP_HOUR must be an integer between 0-23")
}
}
}
}

Expand Down
13 changes: 5 additions & 8 deletions srv.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ func srv(config *OPNCall) error {
// spin up timer
go func() {
time.Sleep(time.Duration(config.Sleep) * time.Second)
update <- true
updateOPN <- true
if unifiEnable.Load() {
updateUnifi <- true
}
}()

// main loop
Expand Down Expand Up @@ -113,12 +116,6 @@ func srv(config *OPNCall) error {
display.Wait()
return nil
}

// set loop wait
// select {
// case <-update:
// break
// }
<-update
<-updateOPN
}
}
136 changes: 136 additions & 0 deletions srvUnifi.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package opnborg

import (
"bytes"
"crypto/sha256"
"crypto/tls"
"encoding/json"
"io"
"io/ioutil"
"net/http"
"net/http/cookiejar"
"time"
)

// perform unifi backup
func unifiBackupServer(config *OPNCall) {

// info
displayChan <- []byte("[UNIFI][BACKUP][START][CONTROLLER] " + config.Unifi.WebUI.Hostname())

// setup session
jar, err := cookiejar.New(nil)
if err != nil {
displayChan <- []byte("[UNIFI][BACKUP][ERROR][UNABLE-TO-SETUP-COOKIE-JAR]" + err.Error())
return // unrecoverable
}

// setup tls secure transport
transport := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
client := http.Client{
Jar: jar,
Transport: transport,
}

// prep login
login := map[string]string{"username": config.Unifi.Backup.User, "password": config.Unifi.Backup.Secret}
postLogin, err := json.Marshal(login)
if err != nil {
displayChan <- []byte("[UNIFI][BACKUP][ERROR][CREDENTIALS-JSON-ENCODING-FAIL]" + err.Error())
return // unrecoverable
}

// prep system test
system := map[string]interface{}{"cmd": "async-backup", "days": 0}
postSystem, err := json.Marshal(system)
if err != nil {
displayChan <- []byte("[UNIFI][BACKUP][ERROR][CONFIG-SYSTEM-TEST-JSON-ENCODING-FAIL]" + err.Error())
return // unrecoverable
}

// init
ts := time.Now()
isReachable := true

// enfore init backup
unifiBackupNow.Store(true)

// loop
for {
// reset
isReachable = true

// perform actual login
res, err := client.Post(config.Unifi.WebUI.String()+"/api/login", "application/json", bytes.NewBuffer(postLogin))
if err != nil {
isReachable = false
displayChan <- []byte("[UNIFI][BACKUP][ERROR][UNABLE-TO-AUTENTHICATE]" + err.Error())
}
if res.StatusCode != 200 {
isReachable = false
body, _ := ioutil.ReadAll(res.Body)
displayChan <- []byte("[UNIFI][BACKUP][ERROR][UNABLE-TO-AUTENTHICATE][BODY] ")
displayChan <- body
}

// perform actual system reachable test
res, err = client.Post(config.Unifi.WebUI.String()+"/api/s/default/cmd/system", "application/json", bytes.NewBuffer(postSystem))
if err != nil {
isReachable = false
displayChan <- []byte("[UNIFI][BACKUP][ERROR][CONFIG-DOWNLOAD-FAIL] " + err.Error())
}
if res.StatusCode != 200 {
isReachable = false
body, _ := ioutil.ReadAll(res.Body)
displayChan <- []byte("[UNIFI][BACKUP][ERROR][CONFIG-DOWNLOAD-FAIL][BODY] ")
displayChan <- body
}

// isReachable && last backup > 6 hours
if isReachable && time.Now().Sub(ts) < time.Duration(6*time.Hour) {
unifiBackupNow.Store(true)
}

// perform backup
if unifiBackupNow.Load() {

// reset unifiBackupNow
unifiBackupNow.Store(false)

// update timestamp
ts = time.Now()

// download backup file
res, err = client.Get(config.Unifi.WebUI.String() + "/dl/backup/" + config.Unifi.Version + ".unf")
if err != nil {
displayChan <- []byte("[UNIFI][BACKUP][ERROR][BACKUP-DOWNLOAD-FILE-HEAD-FAIL] " + err.Error())
}
defer res.Body.Close()

// write file
if err == nil {

// read body
unf, err := io.ReadAll(res.Body)
if err != nil {
displayChan <- []byte("[UNIFI][BACKUP][ERROR][BACKUP-DOWNLOAD-FILE-BODY-FAIL] " + err.Error())
}

// check into store
if err == nil {
checkIntoStore(config, "unifi-"+config.Unifi.WebUI.Hostname(), "unf", unf, ts, sha256.Sum256(unf))
displayChan <- []byte("[UNIFI][BACKUP][SUCCESSFUL]")

// flag git store as dirty
config.dirty.Store(true)
}
}
displayChan <- []byte("[UNIFI][BACKUP][FINISH]")
}
<-updateUnifi
}
}

0 comments on commit 6fd454c

Please sign in to comment.