224 lines
8.5 KiB
Go
224 lines
8.5 KiB
Go
package main
|
|
|
|
import (
|
|
"concord.hectabit.org/Hectabit/burgerbackup/lib/common"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"errors"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/spf13/viper"
|
|
"golang.org/x/crypto/argon2"
|
|
"io"
|
|
"log"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func main() {
|
|
var configPath = "./config.ini"
|
|
if len(os.Args) > 1 {
|
|
if os.Args[1] == "-h" || os.Args[1] == "--help" {
|
|
log.Println("[INFO] Usage: burgerbackup-client </path/to/config/file>")
|
|
os.Exit(0)
|
|
} else {
|
|
configPath = os.Args[1]
|
|
}
|
|
}
|
|
|
|
if _, err := os.Stat(configPath); err == nil {
|
|
log.Println("[INFO] Config loaded at", time.Now().Unix())
|
|
} else if os.IsNotExist(err) {
|
|
originalConfigPath := configPath
|
|
configPath = "/etc/burgerbackup/server.ini"
|
|
if _, err := os.Stat(configPath); err == nil {
|
|
log.Println("[INFO] Config loaded at", time.Now().Unix())
|
|
log.Println("[WARN] The config file was not found at", originalConfigPath, "so the default config path of", configPath, "was used.")
|
|
} else if os.IsNotExist(err) {
|
|
log.Fatalln("[FATAL]", originalConfigPath, "does not exist")
|
|
} else {
|
|
log.Fatalln("[FATAL] File is in quantum uncertainty:", err)
|
|
}
|
|
} else {
|
|
log.Fatalln("[FATAL] File is in quantum uncertainty:", err)
|
|
}
|
|
|
|
viper.SetConfigType("ini")
|
|
configPathSlice := strings.Split(configPath, "/")
|
|
configPathNoIni := strings.Split(configPathSlice[len(configPathSlice)-1], ".")[0]
|
|
viper.SetConfigName(configPathNoIni)
|
|
configPathNoFile := strings.Join(configPathSlice[:len(configPathSlice)-1], "/") + "/"
|
|
viper.AddConfigPath(configPathNoFile)
|
|
viper.AutomaticEnv()
|
|
|
|
err := viper.ReadInConfig()
|
|
if err != nil {
|
|
log.Fatalln("[FATAL] Error in config file at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
}
|
|
|
|
backupKey := viper.GetString("config.BACKUP_KEY")
|
|
cryptoKey := viper.GetString("config.CRYPTO_KEY")
|
|
port := viper.GetInt("config.PORT")
|
|
host := viper.GetString("config.HOST")
|
|
backupFolder := viper.GetString("config.BACKUP_FOLDER")
|
|
|
|
if host == "" {
|
|
log.Fatalln("[FATAL] HOST is not set")
|
|
}
|
|
|
|
if port == 0 {
|
|
log.Fatalln("[FATAL] PORT is not set")
|
|
}
|
|
|
|
if backupKey == "" {
|
|
log.Fatalln("[FATAL] BACKUP_KEY is not set")
|
|
} else if backupKey == "supersecretkey" {
|
|
log.Println("[WARN] SECRET_KEY is set to a default value. Please set it to another value.")
|
|
}
|
|
|
|
if cryptoKey == "" {
|
|
log.Fatalln("[FATAL] CRYPTO_KEY is not set")
|
|
} else if cryptoKey == "supersecretkey" {
|
|
log.Println("[WARN] SECRET_KEY is set to a default value. If it is also set to this on the client, please set both to another value.")
|
|
}
|
|
|
|
if _, err := os.Stat(backupFolder); os.IsNotExist(err) {
|
|
log.Println("[INFO] Backup folder does not exist, creating")
|
|
if err := os.Mkdir(backupFolder, 0755); err != nil {
|
|
log.Fatalln("[FATAL] Could not create backup folder:", err)
|
|
}
|
|
}
|
|
|
|
gin.SetMode(gin.ReleaseMode)
|
|
router := gin.Default()
|
|
|
|
router.Use(func(c *gin.Context) {
|
|
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
|
c.Writer.Header().Set("Access-Control-Allow-Headers", "*, Authorization")
|
|
c.Writer.Header().Set("Access-Control-Allow-Methods", "*")
|
|
|
|
if c.Request.Method == "OPTIONS" {
|
|
c.AbortWithStatus(200)
|
|
return
|
|
}
|
|
|
|
c.Next()
|
|
})
|
|
|
|
router.POST("/api/backup", func(c *gin.Context) {
|
|
clientBackupKey := c.GetHeader("Authorization")
|
|
if clientBackupKey == "" {
|
|
c.JSON(400, gin.H{"error": "BackupKey not found", "goErr": "backupKey not found"})
|
|
return
|
|
}
|
|
|
|
var authenticated bool
|
|
authenticated, err = common.VerifyHash(clientBackupKey, backupKey)
|
|
if err != nil {
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerbackup and refer to the documentation for more info. Your error code is: UNKNOWN-API-BACKUP-VERIFYERR", "goErr": err.Error()})
|
|
log.Println("[ERROR] Error in /api/backup hash verify:", err)
|
|
return
|
|
}
|
|
|
|
if authenticated {
|
|
file, err := c.FormFile("file")
|
|
if err != nil {
|
|
if errors.Is(err, http.ErrMissingFile) {
|
|
c.JSON(400, gin.H{"error": "No file uploaded", "goErr": err.Error()})
|
|
} else {
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerbackup and refer to the documentation for more info. Your error code is: UNKNOWN-API-BACKUP-FILEERR", "goErr": err.Error()})
|
|
log.Println("[ERROR] Error in /api/backup file upload:", err)
|
|
}
|
|
return
|
|
}
|
|
encryptedFile, err := file.Open()
|
|
if err != nil {
|
|
c.JSON(422, gin.H{"error": "Could not open file", "goErr": err.Error()})
|
|
}
|
|
defer func(encryptedFile multipart.File) {
|
|
err := encryptedFile.Close()
|
|
if err != nil {
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerbackup and refer to the documentation for more info. Your error code is: UNKNOWN-API-BACKUP-FILEDEFERERR", "goErr": err.Error()})
|
|
log.Println("[ERROR] Error in /api/backup file defer:", err)
|
|
return
|
|
}
|
|
}(encryptedFile)
|
|
|
|
encryptedData, err := io.ReadAll(encryptedFile)
|
|
if err != nil {
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerbackup and refer to the documentation for more info. Your error code is: UNKNOWN-API-BACKUP-FILECONTENTERR", "goErr": err.Error()})
|
|
log.Println("[ERROR] Error in /api/backup file read:", err)
|
|
return
|
|
}
|
|
|
|
cryptoKeyHashed := argon2.IDKey([]byte(cryptoKey), []byte("burgerbackup"), 1, 64*1024, 4, 32)
|
|
decryptedData, err := common.DecryptAES(cryptoKeyHashed, encryptedData)
|
|
if err != nil {
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerbackup and refer to the documentation for more info. Your error code is: UNKNOWN-API-BACKUP-DECRYPTERR", "goErr": err.Error()})
|
|
log.Println("[ERROR] Error in /api/backup file decrypt:", err)
|
|
return
|
|
}
|
|
|
|
decryptedDataMap := make(map[string]interface{})
|
|
err = json.Unmarshal(decryptedData, &decryptedDataMap)
|
|
if err != nil {
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerbackup and refer to the documentation for more info. Your error code is: UNKNOWN-API-BACKUP-JSONERR", "goErr": err.Error()})
|
|
log.Println("[ERROR] Error in /api/backup file json:", err)
|
|
return
|
|
}
|
|
|
|
originalFileName, ok := decryptedDataMap["filename"].(string)
|
|
if !ok {
|
|
c.JSON(422, gin.H{"error": "Could not get filename", "goErr": "could not get filename"})
|
|
return
|
|
}
|
|
|
|
contentBase64, ok := decryptedDataMap["content"].(string)
|
|
if !ok {
|
|
c.JSON(422, gin.H{"error": "Could not get content", "goErr": "could not get content"})
|
|
return
|
|
}
|
|
|
|
content, err := base64.StdEncoding.DecodeString(contentBase64)
|
|
if err != nil {
|
|
c.JSON(422, gin.H{"error": "Could not decode base64 content", "goErr": err.Error()})
|
|
return
|
|
}
|
|
|
|
log.Println("[INFO] Received backup from", c.ClientIP(), "with filename", originalFileName)
|
|
fileNameSlice := strings.Split(originalFileName, ".")
|
|
|
|
var fileName string
|
|
if len(fileNameSlice) >= 2 {
|
|
fileName = fileNameSlice[0] + "_" + strconv.FormatInt(time.Now().Unix(), 10) + "." + fileNameSlice[1]
|
|
} else if len(fileNameSlice) == 1 {
|
|
fileName = fileNameSlice[0] + "_" + strconv.FormatInt(time.Now().Unix(), 10)
|
|
} else {
|
|
fileName = "unnamed-backup_" + strconv.FormatInt(time.Now().Unix(), 10)
|
|
}
|
|
|
|
filePath := backupFolder + "/" + fileName
|
|
if err := os.WriteFile(filePath, content, 0644); err != nil {
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerbackup and refer to the documentation for more info. Your error code is: UNKNOWN-API-BACKUP-WRITEERR", "goErr": err.Error()})
|
|
log.Println("[ERROR] Error in /api/backup file write:", err)
|
|
return
|
|
}
|
|
|
|
log.Println("[INFO] Successfully saved backup from", c.ClientIP())
|
|
c.JSON(200, gin.H{"success": "true", "timeTaken": time.Now().Unix(), "goErr": ""})
|
|
} else {
|
|
c.JSON(401, gin.H{"error": "Incorrect backup key", "goErr": "incorrect backup key"})
|
|
}
|
|
})
|
|
|
|
log.Println("[INFO] Server started at", time.Now().Unix())
|
|
log.Println("[INFO] Welcome to Burgerbackup! Today we are running on IP " + host + " on port " + strconv.Itoa(port) + ".")
|
|
err = router.Run(host + ":" + strconv.Itoa(port))
|
|
if err != nil {
|
|
log.Fatalln("[FATAL] Server failed to begin operations at", time.Now().Unix(), err)
|
|
}
|
|
}
|