mirror of
https://github.com/kerberos-io/agent.git
synced 2026-03-14 00:38:54 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
552f5dbea6 | ||
|
|
2844a5a419 | ||
|
|
c4b9610f58 | ||
|
|
6a44498730 | ||
|
|
a2cebaf90b | ||
|
|
3f58f26dfd | ||
|
|
a8d5f56f1e | ||
|
|
1eb62d80c7 | ||
|
|
e474a62dbc |
19
README.md
19
README.md
@@ -109,8 +109,10 @@ This repository contains everything you'll need to know about our core product,
|
||||
- Single camera per instance (e.g. one container per camera).
|
||||
- Primary and secondary stream setup (record full-res, stream low-res).
|
||||
- Low resolution streaming through MQTT and full resolution streaming through WebRTC.
|
||||
- End-to-end encryption through MQTT using RSA and AES.
|
||||
- Ability to specifiy conditions: offline mode, motion region, time table, continuous recording, etc.
|
||||
- Post- and pre-recording on motion detection.
|
||||
- Encryption at rest using AES-256-CBC.
|
||||
- Ability to create fragmented recordings, and streaming though HLS fMP4.
|
||||
- [Deploy where you want](#how-to-run-and-deploy-a-kerberos-agent) with the tools you use: `docker`, `docker compose`, `ansible`, `terraform`, `kubernetes`, etc.
|
||||
- Cloud storage/persistance: Kerberos Hub, Kerberos Vault and Dropbox. [(WIP: Minio, Storj, Google Drive, FTP etc.)](https://github.com/kerberos-io/agent/issues/95)
|
||||
@@ -147,6 +149,20 @@ The default username and password for the Kerberos Agent is:
|
||||
|
||||
**_Please note that you change the username and password for a final installation, see [Configure with environment variables](#configure-with-environment-variables) below._**
|
||||
|
||||
## Encryption
|
||||
|
||||
You can encrypt your recordings and outgoing MQTT messages with your own AES and RSA keys by enabling the encryption settings. Once enabled all your recordings will be encrypted using AES-256-CBC and your symmetric key. You can either use the default `openssl` toolchain to decrypt the recordings with your AES key, as following:
|
||||
|
||||
openssl aes-256-cbc -d -md md5 -in encrypted.mp4 -out decrypted.mp4 -k your-key-96ab185xxxxxxxcxxxxxxxx6a59c62e8
|
||||
|
||||
, and additionally you can decrypt a folder of recordings, using the Kerberos Agent binary as following:
|
||||
|
||||
go run main.go -action decrypt ./data/recordings your-key-96ab185xxxxxxxcxxxxxxxx6a59c62e8
|
||||
|
||||
or for a single file:
|
||||
|
||||
go run main.go -action decrypt ./data/recordings/video.mp4 your-key-96ab185xxxxxxxcxxxxxxxx6a59c62e8
|
||||
|
||||
## Configure and persist with volume mounts
|
||||
|
||||
An example of how to mount a host directory is shown below using `docker`, but is applicable for [all the deployment models and tools described above](#running-and-automating-a-kerberos-agent).
|
||||
@@ -227,7 +243,8 @@ Next to attaching the configuration file, it is also possible to override the co
|
||||
| `AGENT_KERBEROSVAULT_DIRECTORY` | The directory, in the provider, where the recordings will be stored in. | "" |
|
||||
| `AGENT_DROPBOX_ACCESS_TOKEN` | The Access Token from your Dropbox app, that is used to leverage the Dropbox SDK. | "" |
|
||||
| `AGENT_DROPBOX_DIRECTORY` | The directory, in the provider, where the recordings will be stored in. | "" |
|
||||
| `AGENT_ENCRYPTION` | Enable 'true' or disable 'false' end-to-end encryption through MQTT (recordings will follow). | "false" |
|
||||
| `AGENT_ENCRYPTION` | Enable 'true' or disable 'false' end-to-end encryption for MQTT messages. | "false" |
|
||||
| `AGENT_ENCRYPTION_RECORDINGS` | Enable 'true' or disable 'false' end-to-end encryption for recordings. | "false" |
|
||||
| `AGENT_ENCRYPTION_FINGERPRINT` | The fingerprint of the keypair (public/private keys), so you know which one to use. | "" |
|
||||
| `AGENT_ENCRYPTION_PRIVATE_KEY` | The private key (assymetric/RSA) to decryptand sign requests send over MQTT. | "" |
|
||||
| `AGENT_ENCRYPTION_SYMMETRIC_KEY` | The symmetric key (AES) to encrypt and decrypt request send over MQTT. | "" |
|
||||
|
||||
@@ -78,6 +78,21 @@ func main() {
|
||||
case "discover":
|
||||
log.Log.Info(timeout)
|
||||
|
||||
case "decrypt":
|
||||
log.Log.Info("Decrypting: " + flag.Arg(0) + " with key: " + flag.Arg(1))
|
||||
symmetricKey := []byte(flag.Arg(1))
|
||||
|
||||
if symmetricKey == nil || len(symmetricKey) == 0 {
|
||||
log.Log.Fatal("Main: symmetric key should not be empty")
|
||||
return
|
||||
}
|
||||
if len(symmetricKey) != 32 {
|
||||
log.Log.Fatal("Main: symmetric key should be 32 bytes")
|
||||
return
|
||||
}
|
||||
|
||||
utils.Decrypt(flag.Arg(0), symmetricKey)
|
||||
|
||||
case "run":
|
||||
{
|
||||
// Print Kerberos.io ASCII art
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/kerberos-io/agent/machinery/src/encryption"
|
||||
"github.com/kerberos-io/agent/machinery/src/log"
|
||||
"github.com/kerberos-io/agent/machinery/src/models"
|
||||
"github.com/kerberos-io/agent/machinery/src/utils"
|
||||
@@ -405,6 +406,27 @@ func HandleRecordStream(queue *pubsub.Queue, configDirectory string, configurati
|
||||
utils.CreateFragmentedMP4(fullName, config.Capture.FragmentedDuration)
|
||||
}
|
||||
|
||||
// Check if we need to encrypt the recording.
|
||||
if config.Encryption != nil && config.Encryption.Enabled == "true" && config.Encryption.Recordings == "true" && config.Encryption.SymmetricKey != "" {
|
||||
// reopen file into memory 'fullName'
|
||||
contents, err := os.ReadFile(fullName)
|
||||
if err == nil {
|
||||
// encrypt
|
||||
encryptedContents, err := encryption.AesEncrypt(contents, config.Encryption.SymmetricKey)
|
||||
if err == nil {
|
||||
// write back to file
|
||||
err := os.WriteFile(fullName, []byte(encryptedContents), 0644)
|
||||
if err != nil {
|
||||
log.Log.Error("HandleRecordStream: error writing file: " + err.Error())
|
||||
}
|
||||
} else {
|
||||
log.Log.Error("HandleRecordStream: error encrypting file: " + err.Error())
|
||||
}
|
||||
} else {
|
||||
log.Log.Error("HandleRecordStream: error reading file: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Create a symbol linc.
|
||||
fc, _ := os.Create(configDirectory + "/data/cloud/" + name)
|
||||
fc.Close()
|
||||
|
||||
@@ -554,10 +554,12 @@ func HandleLiveStreamHD(livestreamCursor *pubsub.QueueCursor, configuration *mod
|
||||
for handshake := range communication.HandleLiveHDHandshake {
|
||||
log.Log.Info("HandleLiveStreamHD: setting up a peer connection.")
|
||||
key := config.Key + "/" + handshake.SessionID
|
||||
webrtc.CandidatesMutex.Lock()
|
||||
_, ok := webrtc.CandidateArrays[key]
|
||||
if !ok {
|
||||
webrtc.CandidateArrays[key] = make(chan string)
|
||||
}
|
||||
webrtc.CandidatesMutex.Unlock()
|
||||
webrtc.InitializeWebRTCConnection(configuration, communication, mqttClient, videoTrack, audioTrack, handshake, webrtc.CandidateArrays[key])
|
||||
|
||||
}
|
||||
|
||||
@@ -464,11 +464,10 @@ func OverrideWithEnvironmentVariables(configuration *models.Configuration) {
|
||||
|
||||
/* When encryption is enabled */
|
||||
case "AGENT_ENCRYPTION":
|
||||
if value == "true" {
|
||||
configuration.Config.Encryption.Enabled = true
|
||||
} else {
|
||||
configuration.Config.Encryption.Enabled = false
|
||||
}
|
||||
configuration.Config.Encryption.Enabled = value
|
||||
break
|
||||
case "AGENT_ENCRYPTION_RECORDINGS":
|
||||
configuration.Config.Encryption.Recordings = value
|
||||
break
|
||||
case "AGENT_ENCRYPTION_FINGERPRINT":
|
||||
configuration.Config.Encryption.Fingerprint = value
|
||||
@@ -510,6 +509,15 @@ func SaveConfig(configDirectory string, config models.Config, configuration *mod
|
||||
}
|
||||
|
||||
func StoreConfig(configDirectory string, config models.Config) error {
|
||||
|
||||
// Encryption key can be set wrong.
|
||||
if config.Encryption != nil {
|
||||
encryptionPrivateKey := config.Encryption.PrivateKey
|
||||
// Replace \\n by \n
|
||||
encryptionPrivateKey = strings.ReplaceAll(encryptionPrivateKey, "\\n", "\n")
|
||||
config.Encryption.PrivateKey = encryptionPrivateKey
|
||||
}
|
||||
|
||||
// Save into database
|
||||
if os.Getenv("DEPLOYMENT") == "factory" || os.Getenv("MACHINERY_ENVIRONMENT") == "kubernetes" {
|
||||
// Write to mongodb
|
||||
|
||||
@@ -38,58 +38,58 @@ func SignWithPrivateKey(data []byte, privateKey *rsa.PrivateKey) ([]byte, error)
|
||||
return signature, err
|
||||
}
|
||||
|
||||
func AesEncrypt(content string, password string) (string, error) {
|
||||
func AesEncrypt(content []byte, password string) ([]byte, error) {
|
||||
salt := make([]byte, 8)
|
||||
_, err := rand.Read(salt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
key, iv, err := DefaultEvpKDF([]byte(password), salt)
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
cipherBytes := PKCS5Padding([]byte(content), aes.BlockSize)
|
||||
cipherBytes := PKCS5Padding(content, aes.BlockSize)
|
||||
mode.CryptBlocks(cipherBytes, cipherBytes)
|
||||
|
||||
data := make([]byte, 16+len(cipherBytes))
|
||||
copy(data[:8], []byte("Salted__"))
|
||||
copy(data[8:16], salt)
|
||||
copy(data[16:], cipherBytes)
|
||||
cipherText := make([]byte, 16+len(cipherBytes))
|
||||
copy(cipherText[:8], []byte("Salted__"))
|
||||
copy(cipherText[8:16], salt)
|
||||
copy(cipherText[16:], cipherBytes)
|
||||
|
||||
cipherText := base64.StdEncoding.EncodeToString(data)
|
||||
//cipherText := base64.StdEncoding.EncodeToString(data)
|
||||
return cipherText, nil
|
||||
}
|
||||
|
||||
func AesDecrypt(cipherText string, password string) (string, error) {
|
||||
data, err := base64.StdEncoding.DecodeString(cipherText)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if string(data[:8]) != "Salted__" {
|
||||
return "", errors.New("invalid crypto js aes encryption")
|
||||
func AesDecrypt(cipherText []byte, password string) ([]byte, error) {
|
||||
//data, err := base64.StdEncoding.DecodeString(cipherText)
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
if string(cipherText[:8]) != "Salted__" {
|
||||
return nil, errors.New("invalid crypto js aes encryption")
|
||||
}
|
||||
|
||||
salt := data[8:16]
|
||||
cipherBytes := data[16:]
|
||||
salt := cipherText[8:16]
|
||||
cipherBytes := cipherText[16:]
|
||||
key, iv, err := DefaultEvpKDF([]byte(password), salt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(cipherBytes, cipherBytes)
|
||||
|
||||
result := PKCS5UnPadding(cipherBytes)
|
||||
return string(result), nil
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/27677236/encryption-in-javascript-and-decryption-with-php/27678978#27678978
|
||||
|
||||
@@ -42,7 +42,7 @@ type Config struct {
|
||||
HubPrivateKey string `json:"hub_private_key" bson:"hub_private_key"`
|
||||
HubSite string `json:"hub_site" bson:"hub_site"`
|
||||
ConditionURI string `json:"condition_uri" bson:"condition_uri"`
|
||||
Encryption *Encryption `json:"encryption" bson:"encryption"`
|
||||
Encryption *Encryption `json:"encryption,omitempty" bson:"encryption,omitempty"`
|
||||
}
|
||||
|
||||
// Capture defines which camera type (Id) you are using (IP, USB or Raspberry Pi camera),
|
||||
@@ -161,7 +161,8 @@ type Dropbox struct {
|
||||
|
||||
// Encryption
|
||||
type Encryption struct {
|
||||
Enabled bool `json:"enabled" bson:"enabled"`
|
||||
Enabled string `json:"enabled" bson:"enabled"`
|
||||
Recordings string `json:"recordings" bson:"recordings"`
|
||||
Fingerprint string `json:"fingerprint" bson:"fingerprint"`
|
||||
PrivateKey string `json:"private_key" bson:"private_key"`
|
||||
SymmetricKey string `json:"symmetric_key" bson:"symmetric_key"`
|
||||
|
||||
@@ -30,7 +30,7 @@ func PackageMQTTMessage(configuration *Configuration, msg Message) ([]byte, erro
|
||||
// At the moment we don't do the encryption part, but we'll implement it
|
||||
// once the legacy methods (subscriptions are moved).
|
||||
msg.Encrypted = false
|
||||
if configuration.Config.Encryption != nil && configuration.Config.Encryption.Enabled {
|
||||
if configuration.Config.Encryption != nil && configuration.Config.Encryption.Enabled == "true" {
|
||||
msg.Encrypted = true
|
||||
}
|
||||
msg.PublicKey = ""
|
||||
@@ -65,15 +65,19 @@ func PackageMQTTMessage(configuration *Configuration, msg Message) ([]byte, erro
|
||||
|
||||
// Create a 16bit key random
|
||||
k := configuration.Config.Encryption.SymmetricKey
|
||||
encryptedValue, err := encryption.AesEncrypt(string(data), k)
|
||||
encryptedValue, err := encryption.AesEncrypt(data, k)
|
||||
if err == nil {
|
||||
|
||||
// Sign the encrypted value
|
||||
signature, err := encryption.SignWithPrivateKey([]byte(encryptedValue), rsaKey)
|
||||
base64Signature := base64.StdEncoding.EncodeToString(signature)
|
||||
|
||||
msg.Payload.EncryptedValue = encryptedValue
|
||||
msg.Payload.Signature = base64Signature
|
||||
msg.Payload.Value = make(map[string]interface{})
|
||||
data := base64.StdEncoding.EncodeToString(encryptedValue)
|
||||
// Sign the encrypted value
|
||||
signature, err := encryption.SignWithPrivateKey([]byte(data), rsaKey)
|
||||
if err == nil {
|
||||
base64Signature := base64.StdEncoding.EncodeToString(signature)
|
||||
msg.Payload.EncryptedValue = data
|
||||
msg.Payload.Signature = base64Signature
|
||||
msg.Payload.Value = make(map[string]interface{})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
jwt "github.com/appleboy/gin-jwt/v2"
|
||||
"github.com/gin-contrib/pprof"
|
||||
@@ -12,6 +14,7 @@ import (
|
||||
"log"
|
||||
|
||||
_ "github.com/kerberos-io/agent/machinery/docs"
|
||||
"github.com/kerberos-io/agent/machinery/src/encryption"
|
||||
"github.com/kerberos-io/agent/machinery/src/models"
|
||||
swaggerFiles "github.com/swaggo/files"
|
||||
ginSwagger "github.com/swaggo/gin-swagger"
|
||||
@@ -77,7 +80,7 @@ func StartServer(configDirectory string, configuration *models.Configuration, co
|
||||
r.Use(static.Serve("/settings", static.LocalFile(configDirectory+"/www", true)))
|
||||
r.Use(static.Serve("/login", static.LocalFile(configDirectory+"/www", true)))
|
||||
r.Handle("GET", "/file/*filepath", func(c *gin.Context) {
|
||||
Files(c, configDirectory)
|
||||
Files(c, configDirectory, configuration)
|
||||
})
|
||||
|
||||
// Run the api on port
|
||||
@@ -87,8 +90,50 @@ func StartServer(configDirectory string, configuration *models.Configuration, co
|
||||
}
|
||||
}
|
||||
|
||||
func Files(c *gin.Context, configDirectory string) {
|
||||
c.Header("Access-Control-Allow-Origin", "*")
|
||||
c.Header("Content-Type", "video/mp4")
|
||||
c.File(configDirectory + "/data/recordings" + c.Param("filepath"))
|
||||
func Files(c *gin.Context, configDirectory string, configuration *models.Configuration) {
|
||||
|
||||
// Get File
|
||||
filePath := configDirectory + "/data/recordings" + c.Param("filepath")
|
||||
_, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
c.JSON(404, gin.H{"error": "File not found"})
|
||||
return
|
||||
}
|
||||
|
||||
contents, err := os.ReadFile(filePath)
|
||||
if err == nil {
|
||||
|
||||
// Get symmetric key
|
||||
symmetricKey := configuration.Config.Encryption.SymmetricKey
|
||||
// Decrypt file
|
||||
if symmetricKey != "" {
|
||||
|
||||
// Read file
|
||||
if err != nil {
|
||||
c.JSON(404, gin.H{"error": "File not found"})
|
||||
return
|
||||
}
|
||||
|
||||
// Decrypt file
|
||||
contents, err = encryption.AesDecrypt(contents, symmetricKey)
|
||||
if err != nil {
|
||||
c.JSON(404, gin.H{"error": "File not found"})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Get fileSize from contents
|
||||
fileSize := len(contents)
|
||||
|
||||
// Send file to gin
|
||||
c.Header("Access-Control-Allow-Origin", "*")
|
||||
c.Header("Content-Disposition", "attachment; filename="+filePath)
|
||||
c.Header("Content-Type", "video/mp4")
|
||||
c.Header("Content-Length", strconv.Itoa(fileSize))
|
||||
// Send contents to gin
|
||||
io.WriteString(c.Writer, string(contents))
|
||||
} else {
|
||||
c.JSON(404, gin.H{"error": "File not found"})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package mqtt
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
@@ -168,7 +169,7 @@ func MQTTListenerHandler(mqttClient mqtt.Client, hubKey string, configDirectory
|
||||
// Messages might be encrypted, if so we'll
|
||||
// need to decrypt them.
|
||||
var payload models.Payload
|
||||
if message.Encrypted && configuration.Config.Encryption != nil && configuration.Config.Encryption.Enabled {
|
||||
if message.Encrypted && configuration.Config.Encryption != nil && configuration.Config.Encryption.Enabled == "true" {
|
||||
encryptedValue := message.Payload.EncryptedValue
|
||||
if len(encryptedValue) > 0 {
|
||||
symmetricKey := configuration.Config.Encryption.SymmetricKey
|
||||
@@ -198,12 +199,16 @@ func MQTTListenerHandler(mqttClient mqtt.Client, hubKey string, configDirectory
|
||||
if decryptedKey != nil {
|
||||
if string(decryptedKey) == symmetricKey {
|
||||
// Decrypt value with decryptedKey
|
||||
decryptedValue, err := encryption.AesDecrypt(encryptedValue, string(decryptedKey))
|
||||
data, err := base64.StdEncoding.DecodeString(encryptedValue)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
decryptedValue, err := encryption.AesDecrypt(data, string(decryptedKey))
|
||||
if err != nil {
|
||||
log.Log.Error("MQTTListenerHandler: error decrypting message: " + err.Error())
|
||||
return
|
||||
}
|
||||
json.Unmarshal([]byte(decryptedValue), &payload)
|
||||
json.Unmarshal(decryptedValue, &payload)
|
||||
} else {
|
||||
log.Log.Error("MQTTListenerHandler: error decrypting message, assymetric keys do not match.")
|
||||
return
|
||||
@@ -333,10 +338,16 @@ func HandleRequestConfig(mqttClient mqtt.Client, hubKey string, payload models.P
|
||||
|
||||
if key != "" && name != "" {
|
||||
|
||||
// Copy the config, as we don't want to share the encryption part.
|
||||
deepCopy := configuration.Config
|
||||
|
||||
var configMap map[string]interface{}
|
||||
inrec, _ := json.Marshal(configuration.Config)
|
||||
inrec, _ := json.Marshal(deepCopy)
|
||||
json.Unmarshal(inrec, &configMap)
|
||||
|
||||
// Unset encryption part.
|
||||
delete(configMap, "encryption")
|
||||
|
||||
message := models.Message{
|
||||
Payload: models.Payload{
|
||||
Action: "receive-config",
|
||||
@@ -370,6 +381,10 @@ func HandleUpdateConfig(mqttClient mqtt.Client, hubKey string, payload models.Pa
|
||||
if configPayload.Timestamp != 0 {
|
||||
|
||||
config := configPayload.Config
|
||||
|
||||
// Make sure to remove Encryption part, as we don't want to save it.
|
||||
config.Encryption = configuration.Config.Encryption
|
||||
|
||||
err := configService.SaveConfig(configDirectory, config, configuration, communication)
|
||||
if err == nil {
|
||||
log.Log.Info("HandleUpdateConfig: Config updated")
|
||||
@@ -442,7 +457,12 @@ func HandleReceiveHDCandidates(mqttClient mqtt.Client, hubKey string, payload mo
|
||||
|
||||
if receiveHDCandidatesPayload.Timestamp != 0 {
|
||||
if communication.CameraConnected {
|
||||
channel := webrtc.CandidateArrays[receiveHDCandidatesPayload.SessionID]
|
||||
key := configuration.Config.Key + "/" + receiveHDCandidatesPayload.SessionID
|
||||
channel := webrtc.CandidateArrays[key]
|
||||
if channel == nil {
|
||||
channel = make(chan string)
|
||||
webrtc.CandidateArrays[key] = channel
|
||||
}
|
||||
log.Log.Info("HandleReceiveHDCandidates: " + receiveHDCandidatesPayload.Candidate)
|
||||
channel <- receiveHDCandidatesPayload.Candidate
|
||||
} else {
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kerberos-io/agent/machinery/src/encryption"
|
||||
"github.com/kerberos-io/agent/machinery/src/log"
|
||||
"github.com/kerberos-io/agent/machinery/src/models"
|
||||
)
|
||||
@@ -330,3 +331,67 @@ func PrintConfiguration(configuration *models.Configuration) {
|
||||
}
|
||||
log.Log.Info("Printing our configuration (config.json): " + configurationVariables)
|
||||
}
|
||||
|
||||
func Decrypt(directoryOrFile string, symmetricKey []byte) {
|
||||
// Check if file or directory
|
||||
fileInfo, err := os.Stat(directoryOrFile)
|
||||
if err != nil {
|
||||
log.Log.Fatal(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var files []string
|
||||
if fileInfo.IsDir() {
|
||||
// Create decrypted directory
|
||||
err = os.MkdirAll(directoryOrFile+"/decrypted", 0755)
|
||||
if err != nil {
|
||||
log.Log.Fatal(err.Error())
|
||||
return
|
||||
}
|
||||
dir, err := os.ReadDir(directoryOrFile)
|
||||
if err != nil {
|
||||
log.Log.Fatal(err.Error())
|
||||
return
|
||||
}
|
||||
for _, file := range dir {
|
||||
// Check if file is not a directory
|
||||
if !file.IsDir() {
|
||||
// Check if an mp4 file
|
||||
if strings.HasSuffix(file.Name(), ".mp4") {
|
||||
files = append(files, directoryOrFile+"/"+file.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
files = append(files, directoryOrFile)
|
||||
}
|
||||
|
||||
// We'll loop over all files and decrypt them one by one.
|
||||
for _, file := range files {
|
||||
|
||||
// Read file
|
||||
content, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
log.Log.Fatal(err.Error())
|
||||
return
|
||||
}
|
||||
// Decrypt using AES key
|
||||
decrypted, err := encryption.AesDecrypt(content, string(symmetricKey))
|
||||
if err != nil {
|
||||
log.Log.Fatal("Something went wrong while decrypting: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Write decrypted content to file with appended .decrypted
|
||||
// Get filename split by / and get last element.
|
||||
fileParts := strings.Split(file, "/")
|
||||
fileName := fileParts[len(fileParts)-1]
|
||||
pathToFile := strings.Join(fileParts[:len(fileParts)-1], "/")
|
||||
|
||||
err = os.WriteFile(pathToFile+"/decrypted/"+fileName, []byte(decrypted), 0644)
|
||||
if err != nil {
|
||||
log.Log.Fatal(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +125,7 @@ func InitializeWebRTCConnection(configuration *models.Configuration, communicati
|
||||
Credential: w.TurnServersCredential,
|
||||
},
|
||||
},
|
||||
//ICETransportPolicy: pionWebRTC.ICETransportPolicyRelay,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -179,6 +180,9 @@ func InitializeWebRTCConnection(configuration *models.Configuration, communicati
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// When an ICE candidate is available send to the other Pion instance
|
||||
// the other Pion instance will add this candidate by calling AddICECandidate
|
||||
var candidatesMux sync.Mutex
|
||||
// When an ICE candidate is available send to the other peer using the signaling server (MQTT).
|
||||
// The other peer will add this candidate by calling AddICECandidate
|
||||
peerConnection.OnICECandidate(func(candidate *pionWebRTC.ICECandidate) {
|
||||
@@ -186,6 +190,9 @@ func InitializeWebRTCConnection(configuration *models.Configuration, communicati
|
||||
return
|
||||
}
|
||||
|
||||
candidatesMux.Lock()
|
||||
defer candidatesMux.Unlock()
|
||||
|
||||
// Create a config map
|
||||
valueMap := make(map[string]interface{})
|
||||
candateJSON := candidate.ToJSON()
|
||||
@@ -208,7 +215,9 @@ func InitializeWebRTCConnection(configuration *models.Configuration, communicati
|
||||
}
|
||||
payload, err := models.PackageMQTTMessage(configuration, message)
|
||||
if err == nil {
|
||||
mqttClient.Publish("kerberos/hub/"+hubKey, 0, false, payload)
|
||||
log.Log.Info("InitializeWebRTCConnection:" + string(candateBinary))
|
||||
token := mqttClient.Publish("kerberos/hub/"+hubKey, 2, false, payload)
|
||||
token.Wait()
|
||||
} else {
|
||||
log.Log.Info("HandleRequestConfig: something went wrong while sending acknowledge config to hub: " + string(payload))
|
||||
}
|
||||
@@ -233,7 +242,8 @@ func InitializeWebRTCConnection(configuration *models.Configuration, communicati
|
||||
}
|
||||
payload, err := models.PackageMQTTMessage(configuration, message)
|
||||
if err == nil {
|
||||
mqttClient.Publish("kerberos/hub/"+hubKey, 0, false, payload)
|
||||
token := mqttClient.Publish("kerberos/hub/"+hubKey, 2, false, payload)
|
||||
token.Wait()
|
||||
} else {
|
||||
log.Log.Info("HandleRequestConfig: something went wrong while sending acknowledge config to hub: " + string(payload))
|
||||
}
|
||||
|
||||
@@ -85,7 +85,16 @@
|
||||
"advanced_configuration": "Erweiterte Konfiguration",
|
||||
"description_advanced_configuration": "Erweiterte Einstellungen um Funktionen des Kerberos Agent zu aktivieren oder deaktivieren",
|
||||
"offline_mode": "Offline Modus",
|
||||
"description_offline_mode": "Ausgehende Verbindungen deaktivieren"
|
||||
"description_offline_mode": "Ausgehende Verbindungen deaktivieren",
|
||||
"encryption": "Encryption",
|
||||
"description_encryption": "Enable encryption for all outgoing traffic. MQTT messages and/or recordings will be encrypted using AES-256. A private key is used for signing.",
|
||||
"encryption_enabled": "Enable MQTT encryption",
|
||||
"description_encryption_enabled": "Enable encryption for all MQTT messages.",
|
||||
"encryption_recordings_enabled": "Enable recording encryption",
|
||||
"description_encryption_recordings_enabled": "Enable encryption for all recordings.",
|
||||
"encryption_fingerprint": "Fingerprint",
|
||||
"encryption_privatekey": "Private key",
|
||||
"encryption_symmetrickey": "Symmetric key"
|
||||
},
|
||||
"camera": {
|
||||
"camera": "Kamera",
|
||||
|
||||
@@ -85,7 +85,16 @@
|
||||
"advanced_configuration": "Advanced configuration",
|
||||
"description_advanced_configuration": "Detailed configuration options to enable or disable specific parts of the Kerberos Agent",
|
||||
"offline_mode": "Offline mode",
|
||||
"description_offline_mode": "Disable all outgoing traffic"
|
||||
"description_offline_mode": "Disable all outgoing traffic",
|
||||
"encryption": "Encryption",
|
||||
"description_encryption": "Enable encryption for all outgoing traffic. MQTT messages and/or recordings will be encrypted using AES-256. A private key is used for signing.",
|
||||
"encryption_enabled": "Enable MQTT encryption",
|
||||
"description_encryption_enabled": "Enable encryption for all MQTT messages.",
|
||||
"encryption_recordings_enabled": "Enable recording encryption",
|
||||
"description_encryption_recordings_enabled": "Enable encryption for all recordings.",
|
||||
"encryption_fingerprint": "Fingerprint",
|
||||
"encryption_privatekey": "Private key",
|
||||
"encryption_symmetrickey": "Symmetric key"
|
||||
},
|
||||
"camera": {
|
||||
"camera": "Camera",
|
||||
|
||||
@@ -85,7 +85,16 @@
|
||||
"advanced_configuration": "Advanced configuration",
|
||||
"description_advanced_configuration": "Detailed configuration options to enable or disable specific parts of the Kerberos Agent",
|
||||
"offline_mode": "Offline mode",
|
||||
"description_offline_mode": "Disable all outgoing traffic"
|
||||
"description_offline_mode": "Disable all outgoing traffic",
|
||||
"encryption": "Encryption",
|
||||
"description_encryption": "Enable encryption for all outgoing traffic. MQTT messages and/or recordings will be encrypted using AES-256. A private key is used for signing.",
|
||||
"encryption_enabled": "Enable MQTT encryption",
|
||||
"description_encryption_enabled": "Enable encryption for all MQTT messages.",
|
||||
"encryption_recordings_enabled": "Enable recording encryption",
|
||||
"description_encryption_recordings_enabled": "Enable encryption for all recordings.",
|
||||
"encryption_fingerprint": "Fingerprint",
|
||||
"encryption_privatekey": "Private key",
|
||||
"encryption_symmetrickey": "Symmetric key"
|
||||
},
|
||||
"camera": {
|
||||
"camera": "Camera",
|
||||
|
||||
@@ -84,7 +84,16 @@
|
||||
"advanced_configuration": "Configuration avancée",
|
||||
"description_advanced_configuration": "Les options de configuration détaillées pour activer ou désactiver des composants spécifiques de l'Agent Kerberos",
|
||||
"offline_mode": "Mode hors-ligne",
|
||||
"description_offline_mode": "Désactiver tout le trafic sortant"
|
||||
"description_offline_mode": "Désactiver tout le trafic sortant",
|
||||
"encryption": "Encryption",
|
||||
"description_encryption": "Enable encryption for all outgoing traffic. MQTT messages and/or recordings will be encrypted using AES-256. A private key is used for signing.",
|
||||
"encryption_enabled": "Enable MQTT encryption",
|
||||
"description_encryption_enabled": "Enable encryption for all MQTT messages.",
|
||||
"encryption_recordings_enabled": "Enable recording encryption",
|
||||
"description_encryption_recordings_enabled": "Enable encryption for all recordings.",
|
||||
"encryption_fingerprint": "Fingerprint",
|
||||
"encryption_privatekey": "Private key",
|
||||
"encryption_symmetrickey": "Symmetric key"
|
||||
},
|
||||
"camera": {
|
||||
"camera": "Caméra",
|
||||
|
||||
@@ -85,7 +85,16 @@
|
||||
"advanced_configuration": "एडवांस कॉन्फ़िगरेशन",
|
||||
"description_advanced_configuration": "Kerberos एजेंट के विशिष्ट भागों को सक्षम या अक्षम करने के लिए विस्तृत कॉन्फ़िगरेशन विकल्प",
|
||||
"offline_mode": "ऑफ़लाइन मोड",
|
||||
"description_offline_mode": "सभी आउटगोइंग ट्रैफ़िक अक्षम करें"
|
||||
"description_offline_mode": "सभी आउटगोइंग ट्रैफ़िक अक्षम करें",
|
||||
"encryption": "Encryption",
|
||||
"description_encryption": "Enable encryption for all outgoing traffic. MQTT messages and/or recordings will be encrypted using AES-256. A private key is used for signing.",
|
||||
"encryption_enabled": "Enable MQTT encryption",
|
||||
"description_encryption_enabled": "Enable encryption for all MQTT messages.",
|
||||
"encryption_recordings_enabled": "Enable recording encryption",
|
||||
"description_encryption_recordings_enabled": "Enable encryption for all recordings.",
|
||||
"encryption_fingerprint": "Fingerprint",
|
||||
"encryption_privatekey": "Private key",
|
||||
"encryption_symmetrickey": "Symmetric key"
|
||||
},
|
||||
"camera": {
|
||||
"camera": "कैमरा",
|
||||
|
||||
@@ -85,7 +85,16 @@
|
||||
"advanced_configuration": "Configurazione avanzata",
|
||||
"description_advanced_configuration": "Opzioni di configurazione dettagliate per abilitare o disabilitare parti specifiche del Kerberos Agent",
|
||||
"offline_mode": "Modalità offline",
|
||||
"description_offline_mode": "Disabilita traffico in uscita"
|
||||
"description_offline_mode": "Disabilita traffico in uscita",
|
||||
"encryption": "Encryption",
|
||||
"description_encryption": "Enable encryption for all outgoing traffic. MQTT messages and/or recordings will be encrypted using AES-256. A private key is used for signing.",
|
||||
"encryption_enabled": "Enable MQTT encryption",
|
||||
"description_encryption_enabled": "Enable encryption for all MQTT messages.",
|
||||
"encryption_recordings_enabled": "Enable recording encryption",
|
||||
"description_encryption_recordings_enabled": "Enable encryption for all recordings.",
|
||||
"encryption_fingerprint": "Fingerprint",
|
||||
"encryption_privatekey": "Private key",
|
||||
"encryption_symmetrickey": "Symmetric key"
|
||||
},
|
||||
"camera": {
|
||||
"camera": "Videocamera",
|
||||
|
||||
@@ -85,7 +85,16 @@
|
||||
"advanced_configuration": "詳細設定",
|
||||
"description_advanced_configuration": "Kerberos エージェントの特定の部分を有効または無効にするための詳細な構成オプション",
|
||||
"offline_mode": "オフラインモード",
|
||||
"description_offline_mode": "すべての送信トラフィックを無効にする"
|
||||
"description_offline_mode": "すべての送信トラフィックを無効にする",
|
||||
"encryption": "Encryption",
|
||||
"description_encryption": "Enable encryption for all outgoing traffic. MQTT messages and/or recordings will be encrypted using AES-256. A private key is used for signing.",
|
||||
"encryption_enabled": "Enable MQTT encryption",
|
||||
"description_encryption_enabled": "Enable encryption for all MQTT messages.",
|
||||
"encryption_recordings_enabled": "Enable recording encryption",
|
||||
"description_encryption_recordings_enabled": "Enable encryption for all recordings.",
|
||||
"encryption_fingerprint": "Fingerprint",
|
||||
"encryption_privatekey": "Private key",
|
||||
"encryption_symmetrickey": "Symmetric key"
|
||||
},
|
||||
"camera": {
|
||||
"camera": "カメラ",
|
||||
|
||||
@@ -85,7 +85,16 @@
|
||||
"advanced_configuration": "Geavanceerde instellingen",
|
||||
"description_advanced_configuration": "Detail instellingen om bepaalde functionaliteiten van je Kerberos Agent aan en uit te zetten",
|
||||
"offline_mode": "Offline modus",
|
||||
"description_offline_mode": "Uitzetten van uitgaande connectiviteit"
|
||||
"description_offline_mode": "Uitzetten van uitgaande connectiviteit",
|
||||
"encryption": "Encrypteer",
|
||||
"description_encryption": "Activeer encryptie voor alle uitgaande verkeer. MQTT berichten en/of opnames worden geencrypteerd met AES-256. Een private sleutel wordt gebruikt voor het ondertekenen.",
|
||||
"encryption_enabled": "Activeer MQTT encryptie",
|
||||
"description_encryption_enabled": "Activeer encryptie voor alle MQTT berichten.",
|
||||
"encryption_recordings_enabled": "Activeer opname encryptie",
|
||||
"description_encryption_recordings_enabled": "Activeer encryptie voor alle opnames.",
|
||||
"encryption_fingerprint": "Vingerafdruk",
|
||||
"encryption_privatekey": "Private sleutel",
|
||||
"encryption_symmetrickey": "Symmetrische sleutel"
|
||||
},
|
||||
"camera": {
|
||||
"camera": "Camera",
|
||||
|
||||
@@ -85,7 +85,16 @@
|
||||
"advanced_configuration": "Advanced configuration",
|
||||
"description_advanced_configuration": "Detailed configuration options to enable or disable specific parts of the Kerberos Agent",
|
||||
"offline_mode": "Offline mode",
|
||||
"description_offline_mode": "Disable all outgoing traffic"
|
||||
"description_offline_mode": "Disable all outgoing traffic",
|
||||
"encryption": "Encryption",
|
||||
"description_encryption": "Enable encryption for all outgoing traffic. MQTT messages and/or recordings will be encrypted using AES-256. A private key is used for signing.",
|
||||
"encryption_enabled": "Enable MQTT encryption",
|
||||
"description_encryption_enabled": "Enable encryption for all MQTT messages.",
|
||||
"encryption_recordings_enabled": "Enable recording encryption",
|
||||
"description_encryption_recordings_enabled": "Enable encryption for all recordings.",
|
||||
"encryption_fingerprint": "Fingerprint",
|
||||
"encryption_privatekey": "Private key",
|
||||
"encryption_symmetrickey": "Symmetric key"
|
||||
},
|
||||
"camera": {
|
||||
"camera": "Camera",
|
||||
|
||||
@@ -85,7 +85,16 @@
|
||||
"advanced_configuration": "Configurações avançadas",
|
||||
"description_advanced_configuration": "Opções de configuração detalhadas para habilitar ou desabilitar partes específicas do Kerberos Agent",
|
||||
"offline_mode": "Modo Offline",
|
||||
"description_offline_mode": "Desative todo o tráfego de saída"
|
||||
"description_offline_mode": "Desative todo o tráfego de saída",
|
||||
"encryption": "Encryption",
|
||||
"description_encryption": "Enable encryption for all outgoing traffic. MQTT messages and/or recordings will be encrypted using AES-256. A private key is used for signing.",
|
||||
"encryption_enabled": "Enable MQTT encryption",
|
||||
"description_encryption_enabled": "Enable encryption for all MQTT messages.",
|
||||
"encryption_recordings_enabled": "Enable recording encryption",
|
||||
"description_encryption_recordings_enabled": "Enable encryption for all recordings.",
|
||||
"encryption_fingerprint": "Fingerprint",
|
||||
"encryption_privatekey": "Private key",
|
||||
"encryption_symmetrickey": "Symmetric key"
|
||||
},
|
||||
"camera": {
|
||||
"camera": "Câmera",
|
||||
|
||||
@@ -85,7 +85,16 @@
|
||||
"advanced_configuration": "高级配置",
|
||||
"description_advanced_configuration": "启用或禁用 Kerberos Agent 特定部分详细配置选项",
|
||||
"offline_mode": "离线模式",
|
||||
"description_offline_mode": "禁用所有传出流量"
|
||||
"description_offline_mode": "禁用所有传出流量",
|
||||
"encryption": "Encryption",
|
||||
"description_encryption": "Enable encryption for all outgoing traffic. MQTT messages and/or recordings will be encrypted using AES-256. A private key is used for signing.",
|
||||
"encryption_enabled": "Enable MQTT encryption",
|
||||
"description_encryption_enabled": "Enable encryption for all MQTT messages.",
|
||||
"encryption_recordings_enabled": "Enable recording encryption",
|
||||
"description_encryption_recordings_enabled": "Enable encryption for all recordings.",
|
||||
"encryption_fingerprint": "Fingerprint",
|
||||
"encryption_privatekey": "Private key",
|
||||
"encryption_symmetrickey": "Symmetric key"
|
||||
},
|
||||
"camera": {
|
||||
"camera": "相机",
|
||||
|
||||
@@ -24,6 +24,7 @@ const LanguageSelect = () => {
|
||||
pt: { label: 'Português', dir: 'ltr', active: false },
|
||||
es: { label: 'Español', dir: 'ltr', active: false },
|
||||
ja: { label: '日本', dir: 'rlt', active: false },
|
||||
hi: { label: 'हिंदी', dir: 'ltr', active: false },
|
||||
};
|
||||
|
||||
if (!languageMap[selected]) {
|
||||
|
||||
@@ -810,6 +810,24 @@ class Settings extends React.Component {
|
||||
this.onUpdateDropdown('', 'timezone', value[0], config)
|
||||
}
|
||||
/>
|
||||
<br />
|
||||
<hr />
|
||||
<p>
|
||||
{t('settings.overview.description_advanced_configuration')}
|
||||
</p>
|
||||
<div className="toggle-wrapper">
|
||||
<Toggle
|
||||
on={config.offline === 'true'}
|
||||
disabled={false}
|
||||
onClick={(event) =>
|
||||
this.onUpdateToggle('', 'offline', event, config)
|
||||
}
|
||||
/>
|
||||
<div>
|
||||
<span>{t('settings.overview.offline_mode')}</span>
|
||||
<p>{t('settings.overview.description_offline_mode')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</BlockBody>
|
||||
<BlockFooter>
|
||||
<Button
|
||||
@@ -1239,25 +1257,95 @@ class Settings extends React.Component {
|
||||
{showOverviewSection && (
|
||||
<Block>
|
||||
<BlockHeader>
|
||||
<h4>{t('settings.overview.advanced_configuration')}</h4>
|
||||
<h4>{t('settings.overview.encryption')}</h4>
|
||||
</BlockHeader>
|
||||
<BlockBody>
|
||||
<p>
|
||||
{t('settings.overview.description_advanced_configuration')}
|
||||
</p>
|
||||
<p>{t('settings.overview.description_encryption')}</p>
|
||||
<div className="toggle-wrapper">
|
||||
<Toggle
|
||||
on={config.offline === 'true'}
|
||||
on={config.encryption.enabled === 'true'}
|
||||
disabled={false}
|
||||
onClick={(event) =>
|
||||
this.onUpdateToggle('', 'offline', event, config)
|
||||
this.onUpdateToggle(
|
||||
'encryption',
|
||||
'enabled',
|
||||
event,
|
||||
config.encryption
|
||||
)
|
||||
}
|
||||
/>
|
||||
<div>
|
||||
<span>{t('settings.overview.offline_mode')}</span>
|
||||
<p>{t('settings.overview.description_offline_mode')}</p>
|
||||
<span>{t('settings.overview.encryption_enabled')}</span>
|
||||
<p>
|
||||
{t('settings.overview.description_encryption_enabled')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="toggle-wrapper">
|
||||
<Toggle
|
||||
on={config.encryption.recordings === 'true'}
|
||||
disabled={false}
|
||||
onClick={(event) =>
|
||||
this.onUpdateToggle(
|
||||
'encryption',
|
||||
'recordings',
|
||||
event,
|
||||
config.encryption
|
||||
)
|
||||
}
|
||||
/>
|
||||
<div>
|
||||
<span>
|
||||
{t('settings.overview.encryption_recordings_enabled')}
|
||||
</span>
|
||||
<p>
|
||||
{t(
|
||||
'settings.overview.description_encryption_recordings_enabled'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Input
|
||||
noPadding
|
||||
label={t('settings.overview.encryption_fingerprint')}
|
||||
value={config.encryption.fingerprint}
|
||||
onChange={(value) =>
|
||||
this.onUpdateField(
|
||||
'encryption',
|
||||
'fingerprint',
|
||||
value,
|
||||
config.encryption
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
noPadding
|
||||
label={t('settings.overview.encryption_privatekey')}
|
||||
value={config.encryption.private_key}
|
||||
onChange={(value) =>
|
||||
this.onUpdateField(
|
||||
'encryption',
|
||||
'private_key',
|
||||
value,
|
||||
config.encryption
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
noPadding
|
||||
label={t('settings.overview.encryption_symmetrickey')}
|
||||
value={config.encryption.symmetric_key}
|
||||
onChange={(value) =>
|
||||
this.onUpdateField(
|
||||
'encryption',
|
||||
'symmetric_key',
|
||||
value,
|
||||
config.encryption
|
||||
)
|
||||
}
|
||||
/>
|
||||
</BlockBody>
|
||||
<BlockFooter>
|
||||
<Button
|
||||
|
||||
Reference in New Issue
Block a user