mirror of
https://github.com/azukaar/Cosmos-Server.git
synced 2026-05-24 14:28:41 -05:00
[skip ci] constellation refact part 1
This commit is contained in:
+2
-1
@@ -27,4 +27,5 @@ restic-arm
|
||||
rclone
|
||||
rclone-arm
|
||||
test-backup
|
||||
/backups
|
||||
/backups
|
||||
ca.pem
|
||||
@@ -114,6 +114,18 @@ function connect(file) {
|
||||
});
|
||||
}
|
||||
|
||||
function create(deviceName) {
|
||||
return wrap(fetch(`/cosmos/api/constellation/create`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
deviceName
|
||||
}),
|
||||
}))
|
||||
}
|
||||
|
||||
function block(nickname, devicename, block) {
|
||||
return wrap(fetch(`/cosmos/api/constellation/block`, {
|
||||
method: 'POST',
|
||||
@@ -137,5 +149,6 @@ export {
|
||||
connect,
|
||||
block,
|
||||
ping,
|
||||
create,
|
||||
pingDevice,
|
||||
};
|
||||
@@ -28,7 +28,7 @@ const AddDeviceModal = ({ users, config, refreshConfig, devices }) => {
|
||||
const {role, nickname} = useClientInfos();
|
||||
const isAdmin = role === "2";
|
||||
|
||||
let firstIP = "192.168.201.2/24";
|
||||
let firstIP = "192.168.201.2";
|
||||
if (devices && devices.length > 0) {
|
||||
const isIpFree = (ip) => {
|
||||
return devices.filter((d) => d.ip === ip).length === 0;
|
||||
@@ -41,7 +41,7 @@ const AddDeviceModal = ({ users, config, refreshConfig, devices }) => {
|
||||
i = 0;
|
||||
j++;
|
||||
}
|
||||
firstIP = "192.168." + j + "." + i + "/24";
|
||||
firstIP = "192.168." + j + "." + i;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,8 @@ const AddDeviceModal = ({ users, config, refreshConfig, devices }) => {
|
||||
}}
|
||||
|
||||
validationSchema={yup.object({
|
||||
deviceName: yup.string().required().min(3).max(32)
|
||||
.matches(/^[a-z0-9-]+$/, t('mgmt.constellation.setup.deviceName.validationError')),
|
||||
})}
|
||||
|
||||
onSubmit={(values, { setSubmitting, setStatus, setErrors }) => {
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Alert, Button, Chip, CircularProgress, IconButton, LinearProgress, Stac
|
||||
import { CosmosCheckbox, CosmosFormDivider, CosmosInputText } from "../config/users/formShortcuts";
|
||||
import MainCard from "../../components/MainCard";
|
||||
import { Formik } from "formik";
|
||||
import * as Yup from "yup";
|
||||
import { LoadingButton } from "@mui/lab";
|
||||
import ApiModal from "../../components/apiModal";
|
||||
import { isDomain } from "../../utils/indexs";
|
||||
@@ -190,18 +191,31 @@ export const ConstellationVPN = ({ freeVersion }) => {
|
||||
</>}
|
||||
{(isAdmin || constellationEnabled) && <Formik
|
||||
enableReinitialize
|
||||
validationSchema={Yup.object().shape({
|
||||
DeviceName: Yup.string().required().min(3).max(32)
|
||||
.matches(/^[a-z0-9-]+$/, t('mgmt.constellation.setup.deviceName.validationError')),
|
||||
})}
|
||||
initialValues={{
|
||||
DeviceName: config.ConstellationConfig.ThisDeviceName || '',
|
||||
Enabled: config.ConstellationConfig.Enabled,
|
||||
IsRelay: config.ConstellationConfig.NebulaConfig.Relay.AMRelay,
|
||||
IsRelay: config.ConstellationConfig.IsRelayNode,
|
||||
IsExitNode: config.ConstellationConfig.IsExitNode,
|
||||
SyncNodes: !config.ConstellationConfig.DoNotSyncNodes,
|
||||
ConstellationHostname: (config.ConstellationConfig.ConstellationHostname && config.ConstellationConfig.ConstellationHostname != "") ? config.ConstellationConfig.ConstellationHostname :
|
||||
getDefaultConstellationHostname(config)
|
||||
}}
|
||||
onSubmit={(values) => {
|
||||
onSubmit={async (values) => {
|
||||
const isCreating = !config.ConstellationConfig.ThisDeviceName;
|
||||
if (isCreating) {
|
||||
await API.constellation.create(values.DeviceName);
|
||||
setTimeout(() => {
|
||||
refreshConfig();
|
||||
}, 1500);
|
||||
return;
|
||||
}
|
||||
let newConfig = { ...config };
|
||||
newConfig.ConstellationConfig.Enabled = values.Enabled;
|
||||
newConfig.ConstellationConfig.NebulaConfig.Relay.AMRelay = values.IsRelay;
|
||||
newConfig.ConstellationConfig.IsRelayNode = values.IsRelay;
|
||||
newConfig.ConstellationConfig.IsExitNode = values.IsExitNode;
|
||||
newConfig.ConstellationConfig.ConstellationHostname = values.ConstellationHostname;
|
||||
newConfig.ConstellationConfig.DoNotSyncNodes = !values.SyncNodes;
|
||||
@@ -318,7 +332,16 @@ export const ConstellationVPN = ({ freeVersion }) => {
|
||||
</div>}
|
||||
|
||||
{!freeVersion && <>
|
||||
<CosmosCheckbox disabled={!isAdmin} formik={formik} name="Enabled" label={t('mgmt.constellation.setup.enabledCheckbox')} />
|
||||
<CosmosInputText
|
||||
disabled={!isAdmin || !!config.ConstellationConfig.ThisDeviceName}
|
||||
formik={formik}
|
||||
name="DeviceName"
|
||||
label={t('mgmt.constellation.setup.deviceName.label')}
|
||||
/>
|
||||
|
||||
{config.ConstellationConfig.ThisDeviceName && (
|
||||
<CosmosCheckbox disabled={!isAdmin} formik={formik} name="Enabled" label={t('mgmt.constellation.setup.enabledCheckbox')} />
|
||||
)}
|
||||
|
||||
{constellationEnabled && !config.ConstellationConfig.SlaveMode && <>
|
||||
{formik.values.Enabled && <>
|
||||
@@ -338,7 +361,9 @@ export const ConstellationVPN = ({ freeVersion }) => {
|
||||
variant="contained"
|
||||
color="primary"
|
||||
>
|
||||
{t('global.saveAction')}
|
||||
{config.ConstellationConfig.ThisDeviceName
|
||||
? t('global.saveAction')
|
||||
: t('mgmt.constellation.setup.createConstellation')}
|
||||
</LoadingButton>
|
||||
</>}
|
||||
</>}
|
||||
|
||||
@@ -251,6 +251,8 @@
|
||||
"mgmt.constellation.resetLabel": "Reset Network",
|
||||
"mgmt.constellation.resetText": "This will completely reset the network, and disconnect all the clients. You will need to reconnect them. This cannot be undone.",
|
||||
"mgmt.constellation.restartButton": "Restart VPN Service",
|
||||
"mgmt.constellation.setup.createConstellation": "Create Constellation",
|
||||
"mgmt.constellation.setup.deviceName.validationError": "Device must only contain lowercase letters, numbers and hyphens",
|
||||
"mgmt.constellation.setup.addDeviceSuccess": "Device added successfully! Download scan the QR Code from the Cosmos app or download the relevant files to your device along side the config and network certificate to connect:",
|
||||
"mgmt.constellation.setup.addDeviceText": "Add a Device to the constellation using either the Cosmos or Nebula client",
|
||||
"mgmt.constellation.setup.addDeviceTitle": "Add Device",
|
||||
|
||||
@@ -68,7 +68,6 @@ func handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
|
||||
for _, q := range r.Question {
|
||||
for hostname, _destination := range remoteHostnames {
|
||||
destination := CachedDeviceNames[_destination]
|
||||
destination = strings.ReplaceAll(destination, "/24", "")
|
||||
|
||||
if destination != "" {
|
||||
if strings.HasSuffix(q.Name, hostname + ".") && q.Qtype == dns.TypeA {
|
||||
@@ -103,7 +102,6 @@ func handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
|
||||
utils.Debug("DNS Question " + q.Name)
|
||||
for deviceName, ip := range CachedDeviceNames {
|
||||
procDeviceName := strings.ReplaceAll(deviceName, " ", "-")
|
||||
ip = strings.ReplaceAll(ip, "/24", "")
|
||||
|
||||
if strings.HasSuffix(q.Name, procDeviceName + ".") && q.Qtype == dns.TypeA {
|
||||
utils.Debug("DNS Overwrite " + procDeviceName + " with its IP")
|
||||
|
||||
+135
-31
@@ -7,7 +7,7 @@ import (
|
||||
"sync"
|
||||
"strings"
|
||||
"crypto/tls"
|
||||
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
|
||||
"github.com/nats-io/nats-server/v2/server"
|
||||
@@ -18,6 +18,16 @@ import (
|
||||
natsClient "github.com/nats-io/nats.go"
|
||||
)
|
||||
|
||||
type NodeHeartbeat struct {
|
||||
DeviceName string
|
||||
LastSeen time.Time
|
||||
IP string
|
||||
IsRelay bool
|
||||
IsLighthouse bool
|
||||
IsExitNode bool
|
||||
IsCosmosNode bool
|
||||
}
|
||||
|
||||
var ns *server.Server
|
||||
var MASTERUSER = "SERVERUSER"
|
||||
var MASTERPWD = utils.GenerateRandomString(24)
|
||||
@@ -37,7 +47,7 @@ func StartNATS() {
|
||||
return
|
||||
}
|
||||
|
||||
utils.Log("Starting NATS server...")
|
||||
utils.Log("[NATS] Starting NATS server on " + GetCurrentDeviceIP() + ":4222")
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
@@ -54,19 +64,19 @@ func StartNATS() {
|
||||
// Decode PEM encoded certificate
|
||||
certDERBlock, _ := pem.Decode(certPEMBlock)
|
||||
if certDERBlock == nil {
|
||||
utils.MajorError("Failed to start NATS: parse certificate PEM", nil)
|
||||
utils.MajorError("[NATS] Failed to start NATS: parse certificate PEM", nil)
|
||||
}
|
||||
|
||||
// Decode PEM encoded private key
|
||||
keyDERBlock, _ := pem.Decode(keyPEMBlock)
|
||||
if keyDERBlock == nil {
|
||||
utils.MajorError("Failed to start NATS: parse key PEM", nil)
|
||||
utils.MajorError("[NATS] Failed to start NATS: parse key PEM", nil)
|
||||
}
|
||||
|
||||
// Create tls.Certificate using the original PEM data
|
||||
cert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock)
|
||||
if err != nil {
|
||||
utils.MajorError("Failed to start NATS: create TLS certificate", err)
|
||||
utils.MajorError("[NATS] Failed to start NATS: create TLS certificate", err)
|
||||
}
|
||||
|
||||
// Configure the NATS server options
|
||||
@@ -80,6 +90,7 @@ func StartNATS() {
|
||||
},
|
||||
}
|
||||
|
||||
// if debug, add debug user
|
||||
for _, devices := range CachedDevices {
|
||||
username := sanitizeNATSUsername(devices.DeviceName)
|
||||
|
||||
@@ -97,16 +108,42 @@ func StartNATS() {
|
||||
})
|
||||
}
|
||||
|
||||
natsHost := GetCurrentDeviceIP()
|
||||
|
||||
if utils.LoggingLevelLabels[utils.GetMainConfig().LoggingLevel] == utils.DEBUG {
|
||||
users = append(users, &server.User{
|
||||
Username: "DEBUG",
|
||||
Password: "DEBUG",
|
||||
Permissions: nil,
|
||||
})
|
||||
|
||||
natsHost = "0.0.0.0"
|
||||
}
|
||||
|
||||
|
||||
opts := &server.Options{
|
||||
Host: "192.168.201.1",
|
||||
Host: natsHost,
|
||||
Port: 4222,
|
||||
|
||||
JetStream: true,
|
||||
StoreDir: utils.CONFIGFOLDER + "/jetstream",
|
||||
|
||||
TLSConfig: &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
ClientAuth: tls.NoClientCert,
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
|
||||
// Cluster: server.ClusterOpts{
|
||||
// Host: GetCurrentDeviceIP(),
|
||||
// Port: 6222,
|
||||
// TLSConfig: &tls.Config{
|
||||
// Certificates: []tls.Certificate{cert},
|
||||
// ClientAuth: tls.NoClientCert,
|
||||
// InsecureSkipVerify: true,
|
||||
// },
|
||||
// },
|
||||
|
||||
Users: users,
|
||||
}
|
||||
|
||||
@@ -121,7 +158,7 @@ func StartNATS() {
|
||||
continue
|
||||
}
|
||||
if NebulaFailedStarting {
|
||||
utils.Error("Nebula failed to start, aborting NATS server setup", nil)
|
||||
utils.Error("[NATS] Nebula failed to start, aborting NATS server setup", nil)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -130,18 +167,18 @@ func StartNATS() {
|
||||
// Wait for the server to be ready
|
||||
if !ns.ReadyForConnections(time.Duration(2 * (retries + 1)) * time.Second) {
|
||||
retries++
|
||||
utils.Debug("NATS server not ready...")
|
||||
utils.Debug("[NATS] NATS server not ready...")
|
||||
err = errors.New("NATS server not ready")
|
||||
continue
|
||||
}
|
||||
|
||||
utils.Debug("Retrying to start NATS server")
|
||||
utils.Debug("[NATS] Retrying to start NATS server")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
utils.MajorError("Error starting NATS server", err)
|
||||
utils.MajorError("[NATS] Error starting NATS server", err)
|
||||
} else {
|
||||
utils.Log("Started NATS server on host " + opts.Host + ":" + strconv.Itoa(opts.Port))
|
||||
utils.Log("[NATS] Started NATS server on host " + opts.Host + ":" + strconv.Itoa(opts.Port))
|
||||
if !utils.GetMainConfig().ConstellationConfig.SlaveMode {
|
||||
InitNATSClient()
|
||||
}
|
||||
@@ -149,7 +186,7 @@ func StartNATS() {
|
||||
}
|
||||
|
||||
func StopNATS() {
|
||||
utils.Log("Stopping NATS server...")
|
||||
utils.Log("[NATS] Stopping NATS server...")
|
||||
|
||||
if ns != nil {
|
||||
ns.Shutdown()
|
||||
@@ -162,6 +199,7 @@ func StopNATS() {
|
||||
var clientConfigLock = sync.Mutex{}
|
||||
var NATSClientTopic = ""
|
||||
var nc *nats.Conn
|
||||
var js nats.JetStreamContext
|
||||
func InitNATSClient() {
|
||||
if nc != nil {
|
||||
return
|
||||
@@ -174,11 +212,11 @@ func InitNATSClient() {
|
||||
retries := 0
|
||||
|
||||
if NebulaFailedStarting {
|
||||
utils.Error("Nebula failed to start, aborting NATS client connection", nil)
|
||||
utils.Error("[NATS] Nebula failed to start, aborting NATS client connection", nil)
|
||||
return
|
||||
}
|
||||
|
||||
utils.Log("Connecting to NATS server...")
|
||||
utils.Log("[NATS] Connecting to NATS server...")
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
@@ -186,7 +224,7 @@ func InitNATSClient() {
|
||||
user = sanitizeNATSUsername(user)
|
||||
|
||||
if err != nil {
|
||||
utils.MajorError("Error getting constellation credentials", err)
|
||||
utils.MajorError("[NATS] Error getting constellation credentials", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -205,7 +243,7 @@ func InitNATSClient() {
|
||||
|
||||
for err != nil {
|
||||
if retries == 10 {
|
||||
utils.MajorError("Error connecting to Constellation NATS server (timeout) - will continue trying", err)
|
||||
utils.MajorError("[NATS] Error connecting to Constellation NATS server (timeout) - will continue trying", err)
|
||||
}
|
||||
|
||||
if retries >= 11 {
|
||||
@@ -213,7 +251,7 @@ func InitNATSClient() {
|
||||
}
|
||||
|
||||
if NebulaFailedStarting {
|
||||
utils.Error("Nebula failed to start, aborting NATS client connection", nil)
|
||||
utils.Error("[NATS] Nebula failed to start, aborting NATS client connection", nil)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -232,19 +270,26 @@ func InitNATSClient() {
|
||||
|
||||
if err != nil {
|
||||
retries++
|
||||
utils.Debug("Retrying to start NATS Client: " + err.Error())
|
||||
utils.Debug("[NATS] Retrying to start NATS Client: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
utils.MajorError("Error connecting to Constellation NATS server", err)
|
||||
utils.MajorError("[NATS] Error connecting to Constellation NATS server", err)
|
||||
return
|
||||
} else {
|
||||
utils.Log("Connected to NATS server as " + user)
|
||||
utils.Log("[NATS] Connected to NATS server as " + user)
|
||||
NATSClientTopic = "cosmos." + user
|
||||
}
|
||||
|
||||
utils.Debug("NATS client connected")
|
||||
utils.Debug("[NATS] NATS client connected")
|
||||
|
||||
js, err = nc.JetStream()
|
||||
if err != nil {
|
||||
utils.MajorError("[NATS] Error getting JetStream context", err)
|
||||
}
|
||||
|
||||
utils.Debug("[NATS] JetStream context obtained")
|
||||
|
||||
if !utils.GetMainConfig().ConstellationConfig.SlaveMode {
|
||||
go MasterNATSClientRouter()
|
||||
@@ -255,6 +300,65 @@ func InitNATSClient() {
|
||||
RequestSyncPayload()
|
||||
clientConfigLock.Lock()
|
||||
}
|
||||
|
||||
ClientHeartbeatInit()
|
||||
|
||||
// POST CLIENT CONNECTION HOOK
|
||||
}
|
||||
|
||||
func ClientHeartbeatInit() {
|
||||
var kv nats.KeyValue
|
||||
var err error
|
||||
|
||||
kv, err = js.KeyValue("constellation-nodes")
|
||||
if err != nil {
|
||||
kv, err = js.CreateKeyValue(&nats.KeyValueConfig{
|
||||
Bucket: "constellation-nodes",
|
||||
TTL: 10 * time.Second,
|
||||
})
|
||||
if err != nil {
|
||||
utils.MajorError("[NATS] Error creating Key-Value store", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
utils.Debug("[NATS] Key-Value store 'constellation-nodes' ready")
|
||||
|
||||
go func() {
|
||||
device := GetCurrentDevice()
|
||||
key := sanitizeNATSUsername(device.DeviceName)
|
||||
|
||||
ticker := time.NewTicker(2 * time.Second)
|
||||
for range ticker.C {
|
||||
if !IsClientConnected() {
|
||||
utils.Warn("[NATS] NATS client not connected during heartbeat")
|
||||
InitNATSClient()
|
||||
}
|
||||
|
||||
utils.Debug("[NATS] Updating heartbeat for " + device.DeviceName)
|
||||
// insert JSON encoded heartbeat
|
||||
heartbeat := NodeHeartbeat{
|
||||
DeviceName: device.DeviceName,
|
||||
LastSeen: time.Now(),
|
||||
IP: device.IP,
|
||||
IsRelay: device.IsRelay,
|
||||
IsLighthouse: device.IsLighthouse,
|
||||
IsExitNode: device.IsExitNode,
|
||||
IsCosmosNode: device.IsCosmosNode,
|
||||
}
|
||||
|
||||
heartbeatData, err := json.Marshal(heartbeat)
|
||||
if err != nil {
|
||||
utils.Error("[NATS] Error marshalling heartbeat JSON", err)
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = kv.Put(key, heartbeatData)
|
||||
if err != nil {
|
||||
utils.Error("[NATS] Error updating heartbeat in Key-Value store", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func IsClientConnected() bool {
|
||||
@@ -312,7 +416,7 @@ func PublishNATSMessage(topic string, payload string) error {
|
||||
// Send a request and wait for a response
|
||||
err := nc.Publish(topic, []byte(payload))
|
||||
if err != nil {
|
||||
utils.Error("Error sending request", err)
|
||||
utils.Error("[NATS] Error sending request", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -320,7 +424,7 @@ func PublishNATSMessage(topic string, payload string) error {
|
||||
}
|
||||
|
||||
func MasterNATSClientRouter() {
|
||||
utils.Log("Starting NATS Master client router.")
|
||||
utils.Log("[NATS] Starting NATS Master client router.")
|
||||
|
||||
nc.Subscribe("cosmos."+MASTERUSER+".ping", func(m *nats.Msg) {
|
||||
utils.Debug("[MQ] Received: " + string(m.Data) + " from " + m.Subject)
|
||||
@@ -366,20 +470,20 @@ func MasterNATSClientRouter() {
|
||||
func SlaveNATSClientRouter() {
|
||||
utils.Log("Starting NATS Slave client router.")
|
||||
|
||||
username := sanitizeNATSUsername(DeviceName)
|
||||
username := sanitizeNATSUsername(GetCurrentDeviceName())
|
||||
|
||||
nc.Subscribe("cosmos."+username+".constellation.config.resync", func(m *nats.Msg) {
|
||||
utils.Log("Constellation config changed, resyncing...")
|
||||
utils.Log("[NATS] Constellation config changed, resyncing...")
|
||||
|
||||
config := m.Data
|
||||
|
||||
needRestart, err := SlaveConfigSync((string)(config))
|
||||
|
||||
if err != nil {
|
||||
utils.MajorError("Error re-syncing Constellation config, please manually sync", err)
|
||||
utils.MajorError("[NATS] Error re-syncing Constellation config, please manually sync", err)
|
||||
} else {
|
||||
if needRestart {
|
||||
utils.Warn("Slave config has changed, restarting Nebula...")
|
||||
utils.Warn("[NATS] Slave config has changed, restarting Nebula...")
|
||||
RestartNebula()
|
||||
utils.RestartHTTPServer()
|
||||
}
|
||||
@@ -387,7 +491,7 @@ func SlaveNATSClientRouter() {
|
||||
})
|
||||
|
||||
nc.Subscribe("cosmos."+username+".constellation.data.sync-receive", func(m *nats.Msg) {
|
||||
utils.Log("Constellation data sync received")
|
||||
utils.Log("[NATS] Constellation data sync received")
|
||||
|
||||
payload := m.Data
|
||||
|
||||
@@ -398,7 +502,7 @@ func SlaveNATSClientRouter() {
|
||||
func PingNATSClient() bool {
|
||||
user, _, err := GetNATSCredentials(!utils.GetMainConfig().ConstellationConfig.SlaveMode)
|
||||
if err != nil {
|
||||
utils.Error("Error getting constellation credentials", err)
|
||||
utils.Error("[NATS] Error getting constellation credentials", err)
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -406,12 +510,12 @@ func PingNATSClient() bool {
|
||||
|
||||
response, err := SendNATSMessage("cosmos."+user+".ping", "Ping")
|
||||
if err != nil {
|
||||
utils.Error("Error pinging NATS client", err)
|
||||
utils.Error("[NATS] Error pinging NATS client", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if response != "" {
|
||||
utils.Debug("NATS client response: " + response)
|
||||
utils.Debug("[NATS] NATS client response: " + response)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ func GetDeviceConfigSync(w http.ResponseWriter, req *http.Request) {
|
||||
utils.Log("DeviceConfigSync: Fetching devices for IP " + ip)
|
||||
|
||||
cursor, err := c.Find(nil, map[string]interface{}{
|
||||
"IP": ip + "/24",
|
||||
"IP": ip,
|
||||
"APIKey": auth,
|
||||
"Blocked": false,
|
||||
})
|
||||
@@ -354,29 +354,15 @@ func GetNATSCredentials(isMaster bool) (string, string, error) {
|
||||
if isMaster {
|
||||
return MASTERUSER, MASTERPWD, nil
|
||||
}
|
||||
|
||||
nebulaFile, err := ioutil.ReadFile(utils.CONFIGFOLDER + "nebula.yml")
|
||||
if err != nil {
|
||||
utils.Error("GetNATSCredentials: error while reading nebula.yml", err)
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
configMap := make(map[string]interface{})
|
||||
err = yaml.Unmarshal(nebulaFile, &configMap)
|
||||
if err != nil {
|
||||
utils.Error("GetNATSCredentials: Invalid slave config file for resync", err)
|
||||
return "", "", err
|
||||
}
|
||||
currentDevice := GetCurrentDevice()
|
||||
|
||||
if configMap["cstln_api_key"] == nil || configMap["cstln_device_name"] == nil {
|
||||
if currentDevice.APIKey == "" || currentDevice.DeviceName == "" {
|
||||
utils.Error("GetNATSCredentials: Invalid slave config file for resync", nil)
|
||||
return "", "", errors.New("Invalid slave config file for resync")
|
||||
}
|
||||
|
||||
apiKey := configMap["cstln_api_key"].(string)
|
||||
deviceName := configMap["cstln_device_name"].(string)
|
||||
|
||||
return deviceName, apiKey, nil
|
||||
return currentDevice.DeviceName, currentDevice.APIKey, nil
|
||||
}
|
||||
|
||||
func SlaveConfigSync(newConfig string) (bool, error) {
|
||||
@@ -410,15 +396,6 @@ func SlaveConfigSync(newConfig string) (bool, error) {
|
||||
endpoint := rawEndpoint.(string)
|
||||
endpoint += "cosmos/api/constellation/config-sync"
|
||||
|
||||
// utils.Log("SlaveConfigSync: Fetching config from " + endpoint.(string))
|
||||
|
||||
// fetch the config from the endpoint with Authorization header
|
||||
// req, err := http.NewRequest("GET", endpoint.(string), nil)
|
||||
// if err != nil {
|
||||
// utils.Error("SlaveConfigSync: Error creating request", err)
|
||||
// return false, err
|
||||
// }
|
||||
|
||||
body := newConfig
|
||||
|
||||
if body == "" {
|
||||
|
||||
@@ -3,6 +3,7 @@ package constellation
|
||||
import (
|
||||
"net/http"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
|
||||
"github.com/azukaar/cosmos-server/src/utils"
|
||||
@@ -25,9 +26,11 @@ type DeviceCreateRequestJSON struct {
|
||||
PublicHostname string `json:"PublicHostname",omitempty`
|
||||
Port string `json:"port",omitempty`
|
||||
|
||||
// internal
|
||||
APIKey string `json:"-"`
|
||||
}
|
||||
|
||||
func DeviceCreate(w http.ResponseWriter, req *http.Request) {
|
||||
func DeviceCreate_API(w http.ResponseWriter, req *http.Request) {
|
||||
if(req.Method == "POST") {
|
||||
var request DeviceCreateRequestJSON
|
||||
err1 := json.NewDecoder(req.Body).Decode(&request)
|
||||
@@ -45,6 +48,12 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
nickname := utils.Sanitize(request.Nickname)
|
||||
|
||||
if utils.AdminOrItselfOnly(w, req, nickname) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
errV := utils.Validate.Struct(request)
|
||||
if errV != nil {
|
||||
utils.Error("DeviceCreation: Invalid User Request", errV)
|
||||
@@ -52,147 +61,21 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) {
|
||||
http.StatusInternalServerError, "DC002")
|
||||
return
|
||||
}
|
||||
|
||||
nickname := utils.Sanitize(request.Nickname)
|
||||
deviceName := utils.Sanitize(request.DeviceName)
|
||||
APIKey := utils.GenerateRandomString(32)
|
||||
|
||||
// name cannot be "cosmos"
|
||||
if deviceName == "cosmos" {
|
||||
utils.Error("DeviceCreation: Device name cannot be 'cosmos'", nil)
|
||||
utils.HTTPError(w, "Device Creation Error: Device name cannot be 'cosmos'",
|
||||
http.StatusBadRequest, "DC008")
|
||||
return
|
||||
}
|
||||
cert, key, _, request, err := DeviceCreate(request)
|
||||
|
||||
APIKey := request.APIKey
|
||||
deviceName := request.DeviceName
|
||||
|
||||
if utils.AdminOrItselfOnly(w, req, nickname) != nil {
|
||||
capki, err := getCApki()
|
||||
if err != nil {
|
||||
utils.Error("DeviceCreation: Error while reading CA", err)
|
||||
utils.HTTPError(w, "Device Creation Error: " + err.Error(),
|
||||
http.StatusInternalServerError, "DC003")
|
||||
return
|
||||
}
|
||||
|
||||
utils.Log("ConstellationDeviceCreation: Creating Device " + deviceName)
|
||||
|
||||
c, closeDb, errCo := utils.GetEmbeddedCollection(utils.GetRootAppId(), "devices")
|
||||
defer closeDb()
|
||||
|
||||
if errCo != nil {
|
||||
utils.Error("Database Connect", errCo)
|
||||
utils.HTTPError(w, "Database", http.StatusInternalServerError, "DB001")
|
||||
return
|
||||
}
|
||||
|
||||
device := utils.Device{}
|
||||
|
||||
utils.Debug("ConstellationDeviceCreation: Creating Device " + deviceName)
|
||||
|
||||
err2 := c.FindOne(nil, map[string]interface{}{
|
||||
"DeviceName": deviceName,
|
||||
"Blocked": false,
|
||||
}).Decode(&device)
|
||||
|
||||
if err2 == mongo.ErrNoDocuments {
|
||||
|
||||
cert, key, fingerprint, err := generateNebulaCert(deviceName, request.IP, request.PublicKey, false)
|
||||
|
||||
if err != nil {
|
||||
utils.Error("DeviceCreation: Error while creating Device", err)
|
||||
utils.HTTPError(w, "Device Creation Error: " + err.Error(),
|
||||
http.StatusInternalServerError, "DC001")
|
||||
return
|
||||
}
|
||||
|
||||
// Cosmos nodes are also lighthouses
|
||||
if request.IsCosmosNode {
|
||||
request.IsLighthouse = true
|
||||
}
|
||||
|
||||
// Check cosmos node and devices limit
|
||||
if request.IsCosmosNode {
|
||||
totalClientLimit := 10 * int64(utils.GetNumberUsers())
|
||||
|
||||
count, errCount := c.CountDocuments(nil, map[string]interface{}{
|
||||
"IsCosmosNode": true,
|
||||
"Blocked": false,
|
||||
})
|
||||
if errCount != nil {
|
||||
utils.Error("DeviceCreation: Error while counting cosmos nodes", errCount)
|
||||
utils.HTTPError(w, "Device Creation Error", http.StatusInternalServerError, "DC009")
|
||||
return
|
||||
}
|
||||
|
||||
countDevices, errCountDevices := c.CountDocuments(nil, map[string]interface{}{
|
||||
"Blocked": false,
|
||||
})
|
||||
if errCountDevices != nil {
|
||||
utils.Error("DeviceCreation: Error while counting devices", errCountDevices)
|
||||
utils.HTTPError(w, "Device Creation Error", http.StatusInternalServerError, "DC011")
|
||||
return
|
||||
}
|
||||
|
||||
if countDevices >= totalClientLimit {
|
||||
utils.Error("DeviceCreation: Device limit reached", nil)
|
||||
utils.HTTPError(w, "Device limit reached", http.StatusConflict, "DC012")
|
||||
return
|
||||
}
|
||||
|
||||
if count >= int64(utils.GetNumberCosmosNode()) {
|
||||
utils.Error("DeviceCreation: Cosmos node limit reached", nil)
|
||||
utils.HTTPError(w, "Cosmos node limit reached", http.StatusConflict, "DC010")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if request.IsLighthouse && request.Nickname != "" {
|
||||
utils.Error("DeviceCreation: Lighthouse cannot belong to a user", nil)
|
||||
utils.HTTPError(w, "Device Creation Error: Lighthouse cannot have a nickname",
|
||||
http.StatusInternalServerError, "DC003")
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
utils.Error("DeviceCreation: Error while getting fingerprint", err)
|
||||
utils.HTTPError(w, "Device Creation Error: " + err.Error(),
|
||||
http.StatusInternalServerError, "DC007")
|
||||
return
|
||||
}
|
||||
|
||||
_, err3 := c.InsertOne(nil, map[string]interface{}{
|
||||
"Nickname": nickname,
|
||||
"DeviceName": deviceName,
|
||||
"PublicKey": key,
|
||||
"IP": request.IP,
|
||||
"IsLighthouse": request.IsLighthouse,
|
||||
"IsCosmosNode": request.IsCosmosNode,
|
||||
"IsRelay": request.IsLighthouse && request.IsRelay,
|
||||
"IsExitNode": request.IsLighthouse && request.IsExitNode,
|
||||
"PublicHostname": request.PublicHostname,
|
||||
"Port": request.Port,
|
||||
"Fingerprint": fingerprint,
|
||||
"APIKey": APIKey,
|
||||
"Blocked": false,
|
||||
"Invisible": request.Invisible && !request.IsLighthouse,
|
||||
})
|
||||
|
||||
if err3 != nil {
|
||||
utils.Error("DeviceCreation: Error while creating Device", err3)
|
||||
utils.HTTPError(w, "Device Creation Error: " + err3.Error(),
|
||||
http.StatusInternalServerError, "DC004")
|
||||
return
|
||||
}
|
||||
|
||||
capki, err := getCApki()
|
||||
if err != nil {
|
||||
utils.Error("DeviceCreation: Error while reading ca.crt", err)
|
||||
utils.HTTPError(w, "Device Creation Error: " + err.Error(),
|
||||
http.StatusInternalServerError, "DC006")
|
||||
return
|
||||
}
|
||||
|
||||
lightHousesList := []utils.ConstellationDevice{}
|
||||
if request.IsLighthouse {
|
||||
lightHousesList, err = GetAllLightHouses()
|
||||
}
|
||||
|
||||
|
||||
if err == nil {
|
||||
// read configYml from config/nebula.yml
|
||||
configYml, err := getYAMLClientConfig(deviceName, utils.CONFIGFOLDER + "nebula.yml", capki, cert, key, APIKey, utils.ConstellationDevice{
|
||||
Nickname: nickname,
|
||||
@@ -209,6 +92,11 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) {
|
||||
}, true, true)
|
||||
|
||||
|
||||
lightHousesList := []utils.ConstellationDevice{}
|
||||
if request.IsLighthouse {
|
||||
lightHousesList, err = GetAllLightHouses()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
utils.Error("DeviceCreation: Error while reading config", err)
|
||||
utils.HTTPError(w, "Device Creation Error: " + err.Error(),
|
||||
@@ -250,19 +138,122 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) {
|
||||
})
|
||||
|
||||
go RestartNebula()
|
||||
} else if err2 == nil {
|
||||
utils.Error("DeviceCreation: Device already exists", nil)
|
||||
utils.HTTPError(w, "Device name already exists", http.StatusConflict, "DC002")
|
||||
return
|
||||
} else {
|
||||
utils.Error("DeviceCreation: Error while finding device", err2)
|
||||
utils.HTTPError(w, "Device Creation Error: " + err2.Error(),
|
||||
http.StatusInternalServerError, "DC001")
|
||||
return
|
||||
utils.Error("DeviceCreation: Error creating device", err)
|
||||
utils.HTTPError(w, "Device Creation Error: " + err.Error(),
|
||||
http.StatusInternalServerError, "DC004")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
utils.Error("DeviceCreation: Method not allowed" + req.Method, nil)
|
||||
utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func DeviceCreate(request DeviceCreateRequestJSON) (string, string, string, DeviceCreateRequestJSON, error) {
|
||||
nickname := utils.Sanitize(request.Nickname)
|
||||
deviceName := utils.Sanitize(request.DeviceName)
|
||||
APIKey := utils.GenerateRandomString(32)
|
||||
|
||||
utils.Log("ConstellationDeviceCreation: Creating Device " + deviceName)
|
||||
|
||||
c, closeDb, errCo := utils.GetEmbeddedCollection(utils.GetRootAppId(), "devices")
|
||||
defer closeDb()
|
||||
|
||||
if errCo != nil {
|
||||
return "", "", "", DeviceCreateRequestJSON{}, errCo
|
||||
}
|
||||
|
||||
device := utils.Device{}
|
||||
|
||||
utils.Debug("ConstellationDeviceCreation: Creating Device " + deviceName)
|
||||
|
||||
err2 := c.FindOne(nil, map[string]interface{}{
|
||||
"DeviceName": deviceName,
|
||||
"Blocked": false,
|
||||
}).Decode(&device)
|
||||
|
||||
if err2 == mongo.ErrNoDocuments {
|
||||
cert, key, fingerprint, err := generateNebulaCert(deviceName, request.IP, request.PublicKey, false)
|
||||
|
||||
if err != nil {
|
||||
return "", "", "", DeviceCreateRequestJSON{}, err
|
||||
}
|
||||
|
||||
// Cosmos nodes are also lighthouses
|
||||
if request.IsCosmosNode {
|
||||
request.IsLighthouse = true
|
||||
}
|
||||
|
||||
// Check cosmos node and devices limit
|
||||
if request.IsCosmosNode {
|
||||
totalClientLimit := 10 * int64(utils.GetNumberUsers())
|
||||
|
||||
count, errCount := c.CountDocuments(nil, map[string]interface{}{
|
||||
"IsCosmosNode": true,
|
||||
"Blocked": false,
|
||||
})
|
||||
if errCount != nil {
|
||||
return "", "", "", DeviceCreateRequestJSON{}, errCount
|
||||
}
|
||||
|
||||
countDevices, errCountDevices := c.CountDocuments(nil, map[string]interface{}{
|
||||
"Blocked": false,
|
||||
})
|
||||
if errCountDevices != nil {
|
||||
return "", "", "", DeviceCreateRequestJSON{}, errCountDevices
|
||||
}
|
||||
|
||||
if countDevices >= totalClientLimit {
|
||||
return "", "", "", DeviceCreateRequestJSON{}, errors.New("DeviceCreation: Device limit reached")
|
||||
}
|
||||
|
||||
if count >= int64(utils.GetNumberCosmosNode()) {
|
||||
return "", "", "", DeviceCreateRequestJSON{}, errors.New("DeviceCreation: Cosmos Node limit reached")
|
||||
}
|
||||
}
|
||||
|
||||
if request.IsLighthouse && request.Nickname != "" {
|
||||
return "", "", "", DeviceCreateRequestJSON{}, errors.New("DeviceCreation: Lighthouse cannot belong to a user")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return "", "", "", DeviceCreateRequestJSON{}, err
|
||||
}
|
||||
|
||||
_, err3 := c.InsertOne(nil, map[string]interface{}{
|
||||
"Nickname": nickname,
|
||||
"DeviceName": deviceName,
|
||||
"PublicKey": key,
|
||||
"IP": request.IP,
|
||||
"IsLighthouse": request.IsLighthouse,
|
||||
"IsCosmosNode": request.IsCosmosNode,
|
||||
"IsRelay": request.IsLighthouse && request.IsRelay,
|
||||
"IsExitNode": request.IsLighthouse && request.IsExitNode,
|
||||
"PublicHostname": request.PublicHostname,
|
||||
"Port": request.Port,
|
||||
"Fingerprint": fingerprint,
|
||||
"APIKey": APIKey,
|
||||
"Blocked": false,
|
||||
"Invisible": request.Invisible && !request.IsLighthouse,
|
||||
})
|
||||
|
||||
if err3 != nil {
|
||||
return "", "", "", DeviceCreateRequestJSON{}, err3
|
||||
}
|
||||
|
||||
request.Nickname = nickname
|
||||
request.DeviceName = deviceName
|
||||
request.PublicKey = key
|
||||
request.IsRelay = request.IsLighthouse && request.IsRelay
|
||||
request.IsExitNode = request.IsLighthouse && request.IsExitNode
|
||||
request.Invisible = request.Invisible && !request.IsLighthouse
|
||||
|
||||
return cert, key, fingerprint, request, nil
|
||||
} else if err2 == nil {
|
||||
return "", "", "", DeviceCreateRequestJSON{}, errors.New("DeviceCreation: Device with this name already exists")
|
||||
} else {
|
||||
return "", "", "", DeviceCreateRequestJSON{}, err2
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ func ConstellationAPIDevices(w http.ResponseWriter, req *http.Request) {
|
||||
if (req.Method == "GET") {
|
||||
DeviceList(w, req)
|
||||
} else if (req.Method == "POST") {
|
||||
DeviceCreate(w, req)
|
||||
DeviceCreate_API(w, req)
|
||||
} else {
|
||||
utils.Error("UserRoute: Method not allowed" + req.Method, nil)
|
||||
utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001")
|
||||
|
||||
@@ -79,7 +79,7 @@ func DevicePublicList(w http.ResponseWriter, req *http.Request) {
|
||||
IP: "192.168.201.1",
|
||||
IsLighthouse: true,
|
||||
IsCosmosNode: true,
|
||||
IsRelay: config.ConstellationConfig.NebulaConfig.Relay.AMRelay,
|
||||
IsRelay: config.ConstellationConfig.IsRelayNode,
|
||||
IsExitNode: config.ConstellationConfig.IsExitNode,
|
||||
PublicHostname: config.ConstellationConfig.ConstellationHostname,
|
||||
Port: "4242",
|
||||
|
||||
@@ -37,7 +37,7 @@ func CheckConstellationToken(req *http.Request) error {
|
||||
utils.Log("DeviceConfigSync: Fetching devices for IP " + ip)
|
||||
|
||||
cursor, err := c.Find(nil, map[string]interface{}{
|
||||
"IP": ip + "/24",
|
||||
"IP": ip,
|
||||
"APIKey": auth,
|
||||
"Blocked": false,
|
||||
})
|
||||
|
||||
@@ -4,11 +4,111 @@ import (
|
||||
"net/http"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/azukaar/cosmos-server/src/utils"
|
||||
|
||||
"github.com/azukaar/cosmos-server/src/utils"
|
||||
)
|
||||
|
||||
func API_NewConstellation(w http.ResponseWriter, req *http.Request) {
|
||||
if utils.AdminOnly(w, req) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if req.Method == "POST" {
|
||||
utils.ConfigLock.Lock()
|
||||
defer utils.ConfigLock.Unlock()
|
||||
|
||||
utils.Log("API_NewConstellation: creating new Constellation")
|
||||
|
||||
var request struct {
|
||||
DeviceName string `json:"deviceName"`
|
||||
}
|
||||
|
||||
err := json.NewDecoder(req.Body).Decode(&request)
|
||||
if err != nil {
|
||||
utils.Error("API_NewConstellation: Invalid User Request", err)
|
||||
utils.HTTPError(w, "API_NewConstellation Error",
|
||||
http.StatusInternalServerError, "ANC001")
|
||||
return
|
||||
}
|
||||
|
||||
if request.DeviceName == "" {
|
||||
utils.Error("API_NewConstellation: Device name is required", nil)
|
||||
utils.HTTPError(w, "Device name is required",
|
||||
http.StatusBadRequest, "ANC002")
|
||||
return
|
||||
}
|
||||
|
||||
// check if ca.crt exists
|
||||
if _, err = os.Stat(utils.CONFIGFOLDER + "ca.crt"); os.IsNotExist(err) {
|
||||
utils.Log("Constellation: ca.crt not found, generating...")
|
||||
// generate ca.crt
|
||||
|
||||
errG := generateNebulaCACert("Cosmos - " + utils.GetMainConfig().ConstellationConfig.ConstellationHostname)
|
||||
if errG != nil {
|
||||
utils.Error("Constellation: error while generating ca.crt", errG)
|
||||
}
|
||||
}
|
||||
|
||||
utils.Log("Constellation: cosmos.crt generating...")
|
||||
// generate cosmos.crt
|
||||
_,_,_,errG := generateNebulaCert("cosmos", "192.168.201.1", "", true)
|
||||
if errG != nil {
|
||||
utils.Error("Constellation: error while generating cosmos.crt", errG)
|
||||
}
|
||||
|
||||
DeviceCreateRequest := DeviceCreateRequestJSON{
|
||||
DeviceName: request.DeviceName,
|
||||
IP: "192.168.201.1",
|
||||
IsLighthouse: true,
|
||||
IsCosmosNode: true,
|
||||
Nickname: "",
|
||||
}
|
||||
|
||||
_, _, _, response, err := DeviceCreate(DeviceCreateRequest)
|
||||
|
||||
if err != nil {
|
||||
utils.Error("API_NewConstellation: Error creating lighthouse device", err)
|
||||
utils.HTTPError(w, "API_NewConstellation Error: " + err.Error(),
|
||||
http.StatusInternalServerError, "ANC003")
|
||||
return
|
||||
}
|
||||
|
||||
deviceName := response.DeviceName
|
||||
|
||||
config := utils.ReadConfigFromFile()
|
||||
config.ConstellationConfig.Enabled = true
|
||||
config.ConstellationConfig.ThisDeviceName = deviceName
|
||||
|
||||
utils.SetBaseMainConfig(config)
|
||||
|
||||
utils.TriggerEvent(
|
||||
"cosmos.settings",
|
||||
"Settings updated",
|
||||
"success",
|
||||
"",
|
||||
map[string]interface{}{
|
||||
"from": "Constellation",
|
||||
})
|
||||
|
||||
utils.Log("API_NewConstellation: Constellation created with device name: " + deviceName)
|
||||
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"status": "OK",
|
||||
})
|
||||
|
||||
go func() {
|
||||
RestartNebula()
|
||||
utils.RestartHTTPServer()
|
||||
}()
|
||||
} else {
|
||||
utils.Error("API_NewConstellation: Method not allowed " + req.Method, nil)
|
||||
utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func API_ConnectToExisting(w http.ResponseWriter, req *http.Request) {
|
||||
if utils.AdminOnly(w, req) != nil {
|
||||
return
|
||||
@@ -55,6 +155,15 @@ func API_ConnectToExisting(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
// output utils.CONFIGFOLDER + "nebula.yml"
|
||||
err = ioutil.WriteFile(utils.CONFIGFOLDER + "nebula.yml", configMapString, 0644)
|
||||
|
||||
if deviceNameVal, ok := configMap["cstln_device_name"]; ok {
|
||||
config.ConstellationConfig.ThisDeviceName = deviceNameVal.(string)
|
||||
} else {
|
||||
utils.Error("API_ConnectToExisting: device name not found in config", nil)
|
||||
utils.HTTPError(w, "API_ConnectToExisting Error: device name not found in config",
|
||||
http.StatusInternalServerError, "ACE003")
|
||||
return
|
||||
}
|
||||
|
||||
// read values into main config
|
||||
if exitNodeVal, ok := configMap["cstln_is_exit_node"]; ok {
|
||||
|
||||
@@ -156,6 +156,20 @@ func UpdateFirewallBlockedClients() error {
|
||||
"proto": "any",
|
||||
"host": "any",
|
||||
})
|
||||
|
||||
// Always allow port 6222 (NATS Cluster) from any
|
||||
newInboundRules = append(newInboundRules, map[interface{}]interface{}{
|
||||
"port": 6222,
|
||||
"proto": "any",
|
||||
"host": "any",
|
||||
})
|
||||
|
||||
// Always allow port 7422 (NATS Leaf) from any
|
||||
newInboundRules = append(newInboundRules, map[interface{}]interface{}{
|
||||
"port": 7422,
|
||||
"proto": "any",
|
||||
"host": "any",
|
||||
})
|
||||
|
||||
// Always allow port 53 (DNS) from any
|
||||
newInboundRules = append(newInboundRules, map[interface{}]interface{}{
|
||||
@@ -512,4 +526,4 @@ func ValidateStaticHosts(logBuffer *lumberjack.Logger) error {
|
||||
|
||||
logger.Printf("Updated nebula-temp.yml after validation")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
+20
-69
@@ -2,18 +2,13 @@ package constellation
|
||||
|
||||
import (
|
||||
"github.com/azukaar/cosmos-server/src/utils"
|
||||
"os"
|
||||
"time"
|
||||
"strings"
|
||||
"io/ioutil"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var NebulaStarted = false
|
||||
var CachedDeviceNames = map[string]string{}
|
||||
var CachedDevices = map[string]utils.ConstellationDevice{}
|
||||
var DeviceName = ""
|
||||
var APIKey = ""
|
||||
|
||||
func resyncConstellationNodes() {
|
||||
if utils.GetMainConfig().ConstellationConfig.Enabled {
|
||||
@@ -24,6 +19,25 @@ func resyncConstellationNodes() {
|
||||
}
|
||||
|
||||
func Init() {
|
||||
InitConfig()
|
||||
|
||||
// if no hostname yet, set default one
|
||||
if utils.GetMainConfig().ConstellationConfig.ConstellationHostname == "" {
|
||||
utils.Log("Constellation: no hostname found, setting default one...")
|
||||
hostnames, _ := utils.ListIps(true)
|
||||
httpHostname := utils.GetMainConfig().HTTPConfig.Hostname
|
||||
if(utils.IsDomain(httpHostname)) {
|
||||
hostnames = append(hostnames, "vpn." + httpHostname)
|
||||
} else if httpHostname != "127.0.0.1" && httpHostname != "localhost" {
|
||||
hostnames = append(hostnames, httpHostname)
|
||||
}
|
||||
configFile := utils.ReadConfigFromFile()
|
||||
configFile.ConstellationConfig.ConstellationHostname = strings.Join(hostnames, ", ")
|
||||
utils.SetBaseMainConfig(configFile)
|
||||
} else {
|
||||
utils.Log("Constellation: hostname found: " + utils.GetMainConfig().ConstellationConfig.ConstellationHostname)
|
||||
}
|
||||
|
||||
utils.ResyncConstellationNodes = resyncConstellationNodes
|
||||
utils.ConstellationSlaveIPWarning = ""
|
||||
|
||||
@@ -50,48 +64,8 @@ func Init() {
|
||||
return
|
||||
}
|
||||
|
||||
InitConfig()
|
||||
|
||||
utils.Log("Initializing Constellation module...")
|
||||
|
||||
// if no hostname yet, set default one
|
||||
if utils.GetMainConfig().ConstellationConfig.ConstellationHostname == "" {
|
||||
utils.Log("Constellation: no hostname found, setting default one...")
|
||||
hostnames, _ := utils.ListIps(true)
|
||||
httpHostname := utils.GetMainConfig().HTTPConfig.Hostname
|
||||
if(utils.IsDomain(httpHostname)) {
|
||||
hostnames = append(hostnames, "vpn." + httpHostname)
|
||||
} else if httpHostname != "127.0.0.1" && httpHostname != "localhost" {
|
||||
hostnames = append(hostnames, httpHostname)
|
||||
}
|
||||
configFile := utils.ReadConfigFromFile()
|
||||
configFile.ConstellationConfig.ConstellationHostname = strings.Join(hostnames, ", ")
|
||||
utils.SetBaseMainConfig(configFile)
|
||||
} else {
|
||||
utils.Log("Constellation: hostname found: " + utils.GetMainConfig().ConstellationConfig.ConstellationHostname)
|
||||
}
|
||||
|
||||
// check if ca.crt exists
|
||||
if _, err = os.Stat(utils.CONFIGFOLDER + "ca.crt"); os.IsNotExist(err) {
|
||||
utils.Log("Constellation: ca.crt not found, generating...")
|
||||
// generate ca.crt
|
||||
|
||||
errG := generateNebulaCACert("Cosmos - " + utils.GetMainConfig().ConstellationConfig.ConstellationHostname)
|
||||
if errG != nil {
|
||||
utils.Error("Constellation: error while generating ca.crt", errG)
|
||||
}
|
||||
}
|
||||
|
||||
// check if cosmos.crt exists
|
||||
if _, err := os.Stat(utils.CONFIGFOLDER + "cosmos.crt"); os.IsNotExist(err) {
|
||||
utils.Log("Constellation: cosmos.crt not found, generating...")
|
||||
// generate cosmos.crt
|
||||
_,_,_,errG := generateNebulaCert("cosmos", "192.168.201.1/24", "", true)
|
||||
if errG != nil {
|
||||
utils.Error("Constellation: error while generating cosmos.crt", errG)
|
||||
}
|
||||
}
|
||||
|
||||
// export nebula.yml
|
||||
utils.Log("Constellation: exporting nebula.yml...")
|
||||
err := ExportConfigToYAML(utils.GetMainConfig().ConstellationConfig, utils.CONFIGFOLDER + "nebula.yml")
|
||||
@@ -140,29 +114,6 @@ func Init() {
|
||||
}
|
||||
}
|
||||
|
||||
// cache device name and api key
|
||||
nebulaFile, err := ioutil.ReadFile(utils.CONFIGFOLDER + "nebula.yml")
|
||||
if err == nil {
|
||||
configMap := make(map[string]interface{})
|
||||
err = yaml.Unmarshal(nebulaFile, &configMap)
|
||||
if err != nil {
|
||||
utils.Error("Constellation: error while unmarshalling nebula.yml", err)
|
||||
} else {
|
||||
if configMap["cstln_device_name"] == nil || configMap["cstln_api_key"] == nil {
|
||||
utils.Warn("Constellation: device name or api key not found in nebula.yml")
|
||||
DeviceName = ""
|
||||
APIKey = ""
|
||||
} else {
|
||||
DeviceName = configMap["cstln_device_name"].(string)
|
||||
APIKey = configMap["cstln_api_key"].(string)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
utils.Error("Constellation: error while reading nebula.yml", err)
|
||||
DeviceName = ""
|
||||
APIKey = ""
|
||||
}
|
||||
|
||||
// Does not work because of Digital Ocean's floating IP's gateway system
|
||||
// if utils.GetMainConfig().ConstellationConfig.SlaveMode {
|
||||
// // check if the IP
|
||||
@@ -188,7 +139,7 @@ func Init() {
|
||||
}
|
||||
|
||||
if utils.GetMainConfig().ConstellationConfig.SlaveMode {
|
||||
go (func() {
|
||||
go (func() {
|
||||
InitNATSClient()
|
||||
|
||||
var err error
|
||||
|
||||
@@ -71,6 +71,9 @@ func startNebulaInBackground() error {
|
||||
|
||||
UpdateFirewallBlockedClients()
|
||||
AdjustDNS(logBuffer)
|
||||
// removed because cannot use multiple hostnames
|
||||
// AdjustConfigHostname()
|
||||
// removed because no retry logic if a node is disconnected: let Nebula handle it
|
||||
//ValidateStaticHosts(logBuffer)
|
||||
|
||||
NebulaFailedStarting = false
|
||||
@@ -273,6 +276,7 @@ func ResetNebula() error {
|
||||
config.ConstellationConfig.SlaveMode = false
|
||||
config.ConstellationConfig.DNSDisabled = false
|
||||
config.ConstellationConfig.FirewallBlockedClients = []string{}
|
||||
config.ConstellationConfig.ThisDeviceName = ""
|
||||
|
||||
utils.SetBaseMainConfig(config)
|
||||
|
||||
@@ -389,7 +393,7 @@ func ExportConfigToYAML(overwriteConfig utils.ConstellationConfig, outputPath st
|
||||
// if no lighthouses, be one
|
||||
finalConfig.Lighthouse.AMLighthouse = len(finalConfig.Lighthouse.Hosts) == 0
|
||||
|
||||
finalConfig.Relay.AMRelay = overwriteConfig.NebulaConfig.Relay.AMRelay
|
||||
finalConfig.Relay.AMRelay = overwriteConfig.IsRelayNode
|
||||
|
||||
finalConfig.Relay.Relays = []string{}
|
||||
for _, l := range lh {
|
||||
@@ -504,7 +508,7 @@ func getYAMLClientConfig(name, configPath, capki, cert, key, APIKey string, devi
|
||||
relayMap["am_relay"] = device.IsRelay && device.IsLighthouse
|
||||
relayMap["use_relays"] = !(device.IsRelay && device.IsLighthouse)
|
||||
relayMap["relays"] = []string{}
|
||||
if utils.GetMainConfig().ConstellationConfig.NebulaConfig.Relay.AMRelay {
|
||||
if utils.GetMainConfig().ConstellationConfig.IsRelayNode {
|
||||
relayMap["relays"] = append(relayMap["relays"].([]string), "192.168.201.1")
|
||||
}
|
||||
|
||||
@@ -771,6 +775,8 @@ func GetConfigAttribute(configPath string, attr string) (string, error) {
|
||||
func generateNebulaCert(name, ip, PK string, saveToFile bool) (string, string, string, error) {
|
||||
// Run the nebula-cert command
|
||||
var cmd *exec.Cmd
|
||||
|
||||
ip = ip + "/24"
|
||||
|
||||
// Read the generated certificate and key files
|
||||
certPath := fmt.Sprintf("./%s.crt", name)
|
||||
@@ -998,7 +1004,7 @@ func generateNebulaCACert(name string) error {
|
||||
}
|
||||
|
||||
func GetDeviceIp(device string) string {
|
||||
return strings.ReplaceAll(CachedDeviceNames[device], "/24", "")
|
||||
return CachedDeviceNames[device]
|
||||
}
|
||||
|
||||
func populateIPTableMasquerade() {
|
||||
@@ -1136,4 +1142,28 @@ func pingLighthouse(lh utils.ConstellationDevice, retries int) {
|
||||
} else {
|
||||
utils.Debug("Constellation: Lighthouse " + lh.IP + " (" + cleanIp(lh.IP) + ") is reachable")
|
||||
}
|
||||
}
|
||||
|
||||
func GetCurrentDevice() utils.ConstellationDevice {
|
||||
config := utils.GetMainConfig()
|
||||
name := config.ConstellationConfig.ThisDeviceName
|
||||
return CachedDevices[name]
|
||||
}
|
||||
|
||||
func GetCurrentDeviceName() string {
|
||||
config := utils.GetMainConfig()
|
||||
name := config.ConstellationConfig.ThisDeviceName
|
||||
return name
|
||||
}
|
||||
|
||||
func GetCurrentDeviceAPIKey() string {
|
||||
config := utils.GetMainConfig()
|
||||
name := config.ConstellationConfig.ThisDeviceName
|
||||
return CachedDevices[name].APIKey
|
||||
}
|
||||
|
||||
func GetCurrentDeviceIP() string {
|
||||
config := utils.GetMainConfig()
|
||||
name := config.ConstellationConfig.ThisDeviceName
|
||||
return CachedDevices[name].IP
|
||||
}
|
||||
@@ -548,6 +548,7 @@ func InitServer() *mux.Router {
|
||||
srapiAdmin.HandleFunc("/api/constellation/restart", constellation.API_Restart)
|
||||
srapiAdmin.HandleFunc("/api/constellation/reset", constellation.API_Reset)
|
||||
srapiAdmin.HandleFunc("/api/constellation/connect", constellation.API_ConnectToExisting)
|
||||
srapiAdmin.HandleFunc("/api/constellation/create", constellation.API_NewConstellation)
|
||||
srapiAdmin.HandleFunc("/api/constellation/config", constellation.API_GetConfig)
|
||||
srapiAdmin.HandleFunc("/api/constellation/logs", constellation.API_GetLogs)
|
||||
srapiAdmin.HandleFunc("/api/constellation/block", constellation.DeviceBlock)
|
||||
|
||||
@@ -81,9 +81,9 @@ func AddConstellationToken(route utils.ProxyRouteConfig) func(next http.Handler)
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// If the request is from a Constellation tunnel, add the token
|
||||
if route.TunnelVia == constellation.DeviceName {
|
||||
if route.TunnelVia == constellation.GetCurrentDeviceName() {
|
||||
// Add the token
|
||||
r.Header.Set("x-cstln-auth", constellation.APIKey)
|
||||
r.Header.Set("x-cstln-auth", constellation.GetCurrentDeviceAPIKey())
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
|
||||
+6
-3
@@ -292,15 +292,18 @@ type ConstellationConfig struct {
|
||||
DNSDisabled bool
|
||||
DNSPort string
|
||||
DNSFallback string
|
||||
IsExitNode bool
|
||||
DNSBlockBlacklist bool
|
||||
DNSAdditionalBlocklists []string
|
||||
CustomDNSEntries []ConstellationDNSEntry
|
||||
NebulaConfig NebulaConfig
|
||||
ConstellationHostname string
|
||||
Tunnels []ProxyRouteConfig
|
||||
FirewallBlockedClients []string `json:"FirewallBlockedClients" bson:"FirewallBlockedClients"`
|
||||
OverrideNebulaExitNodeInterface string
|
||||
ThisDeviceName string
|
||||
|
||||
// TODO REMOVE
|
||||
ConstellationHostname string
|
||||
IsExitNode bool
|
||||
IsRelayNode bool
|
||||
}
|
||||
|
||||
type ConstellationDNSEntry struct {
|
||||
|
||||
Reference in New Issue
Block a user