diff --git a/changelog.md b/changelog.md
index d3d2ed4..1f51c44 100644
--- a/changelog.md
+++ b/changelog.md
@@ -6,7 +6,11 @@
## Version 0.10.0
- Added Constellation
- DNS Challenge is now used for all certificates when enabled
->>>>>>> b8a9e71 ([release] v0.10.0-unstable)
+ - Rework headers for better compatibility
+
+## Version 0.9.20 - 0.9.21
+ - Add option to disable CORS hardening (with empty value)
+
## Version 0.9.19
- Add country whitelist option to geoblocker
- No countries blocked by default anymore
diff --git a/client/src/api/constellation.tsx b/client/src/api/constellation.tsx
index 61b0923..375aabe 100644
--- a/client/src/api/constellation.tsx
+++ b/client/src/api/constellation.tsx
@@ -28,6 +28,16 @@ function restart() {
}))
}
+
+function reset() {
+ return wrap(fetch('/cosmos/api/constellation/reset', {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ }))
+}
+
function getConfig() {
return wrap(fetch('/cosmos/api/constellation/config', {
method: 'GET',
@@ -46,10 +56,22 @@ function getLogs() {
}))
}
+function connect(file) {
+ return wrap(fetch('/cosmos/api/constellation/connect', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(file),
+ }))
+}
+
export {
list,
addDevice,
restart,
getConfig,
getLogs,
+ reset,
+ connect,
};
\ No newline at end of file
diff --git a/client/src/components/confirmModal.jsx b/client/src/components/confirmModal.jsx
new file mode 100644
index 0000000..81bf673
--- /dev/null
+++ b/client/src/components/confirmModal.jsx
@@ -0,0 +1,48 @@
+// material-ui
+import { LoadingButton } from '@mui/lab';
+import { Button } from '@mui/material';
+import Dialog from '@mui/material/Dialog';
+import DialogActions from '@mui/material/DialogActions';
+import DialogContent from '@mui/material/DialogContent';
+import DialogContentText from '@mui/material/DialogContentText';
+import DialogTitle from '@mui/material/DialogTitle';
+import * as React from 'react';
+import { useEffect, useState } from 'react';
+
+const ConfirmModal = ({ callback, label, content }) => {
+ const [openModal, setOpenModal] = useState(false);
+
+ return <>
+ setOpenModal(false)}>
+ Are you sure?
+
+
+ {content}
+
+
+
+ {
+ setOpenModal(false);
+ }}>Cancel
+ {
+ callback();
+ setOpenModal(false);
+ }}>Confirm
+
+
+
+ {
+ setOpenModal(true);
+ }}
+ >
+ {label}
+
+ >
+};
+
+export default ConfirmModal;
diff --git a/client/src/components/fileUpload.jsx b/client/src/components/fileUpload.jsx
index a7ba9fc..a541984 100644
--- a/client/src/components/fileUpload.jsx
+++ b/client/src/components/fileUpload.jsx
@@ -2,7 +2,7 @@ import React from 'react';
import { Button } from '@mui/material';
import { UploadOutlined } from '@ant-design/icons';
-export default function UploadButtons({OnChange, accept, label}) {
+export default function UploadButtons({OnChange, accept, label, variant, fullWidth, size}) {
return (
- }>
+ }>
{label || 'Upload'}
diff --git a/client/src/pages/constellation/addDevice.jsx b/client/src/pages/constellation/addDevice.jsx
index 7d401ec..ea2aea8 100644
--- a/client/src/pages/constellation/addDevice.jsx
+++ b/client/src/pages/constellation/addDevice.jsx
@@ -12,10 +12,67 @@ import { PlusCircleFilled } from '@ant-design/icons';
import { Formik } from 'formik';
import * as yup from 'yup';
import * as API from '../../api';
-import { CosmosFormDivider, CosmosInputText, CosmosSelect } from '../config/users/formShortcuts';
+import { CosmosCheckbox, CosmosFormDivider, CosmosInputText, CosmosSelect } from '../config/users/formShortcuts';
import { DownloadFile } from '../../api/downloadButton';
import QRCode from 'qrcode';
+const getDocker = (data, isCompose) => {
+ let lighthouses = '';
+
+ for (let i = 0; i < data.LighthousesList.length; i++) {
+ const l = data.LighthousesList[i];
+ lighthouses += l.publicHostname + ";" + l.ip + ":" + l.port + ";" + l.isRelay + ",";
+ }
+
+ let containerName = "cosmos-constellation-lighthouse";
+ let imageName = "cosmos-constellation-lighthouse:latest";
+
+ let volPath = "/var/lib/cosmos-constellation";
+
+ if (isCompose) {
+ return `
+version: "3.8"
+services:
+ ${containerName}:
+ image: ${imageName}
+ container_name: ${containerName}
+ restart: unless-stopped
+ network_mode: bridge
+ ports:
+ - "${data.Port}:4242"
+ volumes:
+ - ${volPath}:/config
+ environment:
+ - CA=${JSON.stringify(data.CA)}
+ - CERT=${JSON.stringify(data.PrivateKey)}
+ - KEY=${JSON.stringify(data.PublicKey)}
+ - LIGHTHOUSES=${lighthouses}
+ - PUBLIC_HOSTNAME=${data.PublicHostname}
+ - IS_RELAY=${data.IsRelay}
+ - IP=${data.IP}
+`;
+ } else {
+ return `
+docker run -d \\
+ --name ${containerName} \\
+ --restart unless-stopped \\
+ --network bridge \\
+ -v ${volPath}:/config \\
+ -e CA=${JSON.stringify(data.CA)} \\
+ -e CERT=${JSON.stringify(data.PrivateKey)} \\
+ -e KEY=${JSON.stringify(data.PublicKey)} \\
+ -e LIGHTHOUSES=${lighthouses} \\
+ -e PUBLIC_HOSTNAME=${data.PublicHostname} \\
+ -e IS_RELAY=${data.IsRelay} \\
+ -e IP=${data.IP} \\
+ -p ${data.Port}:4242 \\
+ ${imageName}
+`;
+ }
+
+}
+
+
const AddDeviceModal = ({ users, config, isAdmin, refreshConfig, devices }) => {
const [openModal, setOpenModal] = useState(false);
const [isDone, setIsDone] = useState(null);
@@ -63,12 +120,18 @@ const AddDeviceModal = ({ users, config, isAdmin, refreshConfig, devices }) => {
deviceName: '',
ip: firstIP,
publicKey: '',
+ Port: "4242",
+ PublicHostname: '',
+ IsRelay: true,
+ isLighthouse: false,
}}
validationSchema={yup.object({
})}
onSubmit={(values, { setSubmitting, setStatus, setErrors }) => {
+ if(values.isLighthouse) values.nickname = null;
+
return API.constellation.addDevice(values).then(({data}) => {
setIsDone(data);
refreshConfig();
@@ -85,52 +148,55 @@ const AddDeviceModal = ({ users, config, isAdmin, refreshConfig, devices }) => {
{isDone ?
- Device added successfully!
+ 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:
-
-
-
-
- {/*
- */}
+ {/* {isDone.isLighthouse ? <>
+
+
+
+
+ > : <> */}
+
+
+
+
+ {/* >} */}
+
-
- {/*
-
- */}
:
- Add a device to the constellation using either the Cosmos or Nebula client
+ Add a Device to the constellation using either the Cosmos or Nebula client
+
+ {!formik.values.isLighthouse &&
{
return [u.nickname, u.nickname]
})
}
- />
+ />}
{
formik={formik}
/>
+ {/* */}
+
+ {formik.values.isLighthouse && <>
+
+
+
+
+
+ >}
{formik.errors && formik.errors.length > 0 &&
{formik.errors.map((err) => {
@@ -189,7 +276,9 @@ const AddDeviceModal = ({ users, config, isAdmin, refreshConfig, devices }) => {
setIsDone(null);
setOpenModal(true);
}}
- variant="contained"
+ variant={
+ "contained"
+ }
startIcon={ }
>
Add Device
diff --git a/client/src/pages/constellation/index.jsx b/client/src/pages/constellation/index.jsx
index f11d2d3..d4d0518 100644
--- a/client/src/pages/constellation/index.jsx
+++ b/client/src/pages/constellation/index.jsx
@@ -4,14 +4,26 @@ import * as API from "../../api";
import AddDeviceModal from "./addDevice";
import PrettyTableView from "../../components/tableView/prettyTableView";
import { DeleteButton } from "../../components/delete";
-import { CloudOutlined, DesktopOutlined, LaptopOutlined, MobileOutlined, TabletOutlined } from "@ant-design/icons";
+import { CloudOutlined, CloudServerOutlined, CompassOutlined, DesktopOutlined, LaptopOutlined, MobileOutlined, TabletOutlined } from "@ant-design/icons";
import IsLoggedIn from "../../isLoggedIn";
-import { Button, CircularProgress, Stack } from "@mui/material";
-import { CosmosCheckbox, CosmosFormDivider } from "../config/users/formShortcuts";
+import { Alert, Button, CircularProgress, Stack } from "@mui/material";
+import { CosmosCheckbox, CosmosFormDivider, CosmosInputText } from "../config/users/formShortcuts";
import MainCard from "../../components/MainCard";
import { Formik } from "formik";
import { LoadingButton } from "@mui/lab";
import ApiModal from "../../components/apiModal";
+import { isDomain } from "../../utils/indexs";
+import ConfirmModal from "../../components/confirmModal";
+import UploadButtons from "../../components/fileUpload";
+
+const getDefaultConstellationHostname = (config) => {
+ // if domain is set, use it
+ if(isDomain(config.HTTPConfig.Hostname)) {
+ return "vpn." + config.HTTPConfig.Hostname;
+ } else {
+ return config.HTTPConfig.Hostname;
+ }
+}
export const ConstellationIndex = () => {
const [isAdmin, setIsAdmin] = useState(false);
@@ -41,6 +53,8 @@ export const ConstellationIndex = () => {
return
} else if (r.deviceName.toLowerCase().includes("tablet")) {
return
+ } else if (r.deviceName.toLowerCase().includes("lighthouse") || r.deviceName.toLowerCase().includes("server")) {
+ return
} else {
return
}
@@ -53,22 +67,30 @@ export const ConstellationIndex = () => {
+ {config.ConstellationConfig.Enabled && config.ConstellationConfig.SlaveMode && <>
+
+ You are currently connected to an external constellation network. Use your main Cosmos server to manage your constellation network and devices.
+
+ >}
{
let newConfig = { ...config };
newConfig.ConstellationConfig.Enabled = values.Enabled;
newConfig.ConstellationConfig.NebulaConfig.Relay.AMRelay = values.IsRelay;
+ newConfig.ConstellationConfig.ConstellationHostname = values.ConstellationHostname;
return API.config.set(newConfig);
}}
>
{(formik) => (
+ {config.ConstellationConfig.Enabled && !config.ConstellationConfig.SlaveMode && <>
r.deviceName}
buttons={[
-
+ ,
]}
columns={[
{
@@ -121,6 +170,10 @@ export const ConstellationIndex = () => {
title: 'Owner',
field: (r) => {r.nickname} ,
},
+ {
+ title: 'Type',
+ field: (r) => {r.isLighthouse ? "Lighthouse" : "Client"} ,
+ },
{
title: 'Constellation IP',
screenMin: 'md',
@@ -137,6 +190,7 @@ export const ConstellationIndex = () => {
}
]}
/>
+ >}
> :
diff --git a/package.json b/package.json
index fd46e32..f5bc0e2 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "cosmos-server",
- "version": "0.10.0-unstable14",
+ "version": "0.10.0-unstable15",
"description": "",
"main": "test-server.js",
"bugs": {
diff --git a/src/constellation/api_devices_create.go b/src/constellation/api_devices_create.go
index f37e54a..f7ea61d 100644
--- a/src/constellation/api_devices_create.go
+++ b/src/constellation/api_devices_create.go
@@ -9,10 +9,18 @@ import (
)
type DeviceCreateRequestJSON struct {
- Nickname string `json:"nickname",validate:"required,min=3,max=32,alphanum"`
DeviceName string `json:"deviceName",validate:"required,min=3,max=32,alphanum"`
IP string `json:"ip",validate:"required,ipv4"`
PublicKey string `json:"publicKey",omitempty`
+
+ // for devices only
+ Nickname string `json:"nickname",validate:"max=32,alphanum",omitempty`
+
+ // for lighthouse only
+ IsLighthouse bool `json:"isLighthouse",omitempty`
+ IsRelay bool `json:"isRelay",omitempty`
+ PublicHostname string `json:"PublicHostname",omitempty`
+ Port string `json:"port",omitempty`
}
func DeviceCreate(w http.ResponseWriter, req *http.Request) {
@@ -67,11 +75,22 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) {
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
+ }
+
_, err3 := c.InsertOne(nil, map[string]interface{}{
"Nickname": nickname,
"DeviceName": deviceName,
"PublicKey": key,
"IP": request.IP,
+ "IsLighthouse": request.IsLighthouse,
+ "IsRelay": request.IsRelay,
+ "PublicHostname": request.PublicHostname,
+ "Port": request.Port,
})
if err3 != nil {
@@ -88,9 +107,24 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) {
http.StatusInternalServerError, "DC006")
return
}
+
+ lightHousesList := []utils.ConstellationDevice{}
+ if request.IsLighthouse {
+ lightHousesList, err = GetAllLightHouses()
+ }
// read configYml from config/nebula.yml
- configYml, err := getYAMLClientConfig(deviceName, utils.CONFIGFOLDER + "nebula.yml", capki, cert, key)
+ configYml, err := getYAMLClientConfig(deviceName, utils.CONFIGFOLDER + "nebula.yml", capki, cert, key, utils.ConstellationDevice{
+ Nickname: nickname,
+ DeviceName: deviceName,
+ PublicKey: key,
+ IP: request.IP,
+ IsLighthouse: request.IsLighthouse,
+ IsRelay: request.IsRelay,
+ PublicHostname: request.PublicHostname,
+ Port: request.Port,
+ })
+
if err != nil {
utils.Error("DeviceCreation: Error while reading config", err)
utils.HTTPError(w, "Device Creation Error: " + err.Error(),
@@ -108,6 +142,11 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) {
"IP": request.IP,
"Config": configYml,
"CA": capki,
+ "IsLighthouse": request.IsLighthouse,
+ "IsRelay": request.IsRelay,
+ "PublicHostname": request.PublicHostname,
+ "Port": request.Port,
+ "LighthousesList": lightHousesList,
},
})
} else if err2 == nil {
diff --git a/src/constellation/api_devices_list.go b/src/constellation/api_devices_list.go
index c2efc0f..df8b151 100644
--- a/src/constellation/api_devices_list.go
+++ b/src/constellation/api_devices_list.go
@@ -29,7 +29,7 @@ func DeviceList(w http.ResponseWriter, req *http.Request) {
return
}
- var devices []utils.Device
+ var devices []utils.ConstellationDevice
// Check if user is an admin
if isAdmin {
@@ -47,11 +47,6 @@ func DeviceList(w http.ResponseWriter, req *http.Request) {
utils.HTTPError(w, "Error decoding devices", http.StatusInternalServerError, "DL002")
return
}
-
- // Remove the private key from the response
- for i := range devices {
- devices[i].PrivateKey = ""
- }
} else {
// If not admin, get user's devices based on their nickname
nickname := req.Header.Get("x-cosmos-user")
@@ -68,11 +63,6 @@ func DeviceList(w http.ResponseWriter, req *http.Request) {
utils.HTTPError(w, "Error decoding devices", http.StatusInternalServerError, "DL004")
return
}
-
- // Remove the private key from the response
- for i := range devices {
- devices[i].PrivateKey = ""
- }
}
// Respond with the list of devices
diff --git a/src/constellation/api_nebula.go b/src/constellation/api_nebula.go
index 38be4a5..1d67c2f 100644
--- a/src/constellation/api_nebula.go
+++ b/src/constellation/api_nebula.go
@@ -55,6 +55,26 @@ func API_Restart(w http.ResponseWriter, req *http.Request) {
}
}
+func API_Reset(w http.ResponseWriter, req *http.Request) {
+ if utils.AdminOnly(w, req) != nil {
+ return
+ }
+
+ if(req.Method == "GET") {
+ ResetNebula()
+
+ utils.Log("Constellation: nebula reset")
+
+ json.NewEncoder(w).Encode(map[string]interface{}{
+ "status": "OK",
+ })
+ } else {
+ utils.Error("SettingGet: Method not allowed" + req.Method, nil)
+ utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001")
+ return
+ }
+}
+
func API_GetLogs(w http.ResponseWriter, req *http.Request) {
if utils.AdminOnly(w, req) != nil {
return
diff --git a/src/constellation/api_nebula_connect.go b/src/constellation/api_nebula_connect.go
new file mode 100644
index 0000000..8972c74
--- /dev/null
+++ b/src/constellation/api_nebula_connect.go
@@ -0,0 +1,47 @@
+package constellation
+
+import (
+ "net/http"
+ "encoding/json"
+ "io/ioutil"
+
+
+ "github.com/azukaar/cosmos-server/src/utils"
+)
+
+func API_ConnectToExisting(w http.ResponseWriter, req *http.Request) {
+ if utils.AdminOnly(w, req) != nil {
+ return
+ }
+
+ if(req.Method == "POST") {
+ body, err := ioutil.ReadAll(req.Body)
+ if err != nil {
+ utils.Error("API_Restart: Invalid User Request", err)
+ utils.HTTPError(w, "API_Restart Error",
+ http.StatusInternalServerError, "AR001")
+ return
+ }
+
+ config := utils.ReadConfigFromFile()
+ config.ConstellationConfig.Enabled = true
+ config.ConstellationConfig.SlaveMode = true
+ config.ConstellationConfig.DNS = false
+ // ConstellationHostname =
+
+ // output utils.CONFIGFOLDER + "nebula.yml"
+ err = ioutil.WriteFile(utils.CONFIGFOLDER + "nebula.yml", body, 0644)
+
+ utils.SetBaseMainConfig(config)
+
+ RestartNebula()
+
+ json.NewEncoder(w).Encode(map[string]interface{}{
+ "status": "OK",
+ })
+ } else {
+ utils.Error("SettingGet: Method not allowed" + req.Method, nil)
+ utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001")
+ return
+ }
+}
diff --git a/src/constellation/index.go b/src/constellation/index.go
index 0697166..6f6bbba 100644
--- a/src/constellation/index.go
+++ b/src/constellation/index.go
@@ -6,32 +6,36 @@ import (
)
func Init() {
+ var err error
+
// if Constellation is enabled
if utils.GetMainConfig().ConstellationConfig.Enabled {
- InitConfig()
-
- utils.Log("Initializing Constellation module...")
+ if !utils.GetMainConfig().ConstellationConfig.SlaveMode {
+ InitConfig()
+
+ utils.Log("Initializing Constellation module...")
- // 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
- generateNebulaCACert("Cosmos - " + utils.GetMainConfig().HTTPConfig.Hostname)
- }
+ // 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
+ generateNebulaCACert("Cosmos - " + utils.GetMainConfig().ConstellationConfig.ConstellationHostname)
+ }
- // 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
- generateNebulaCert("cosmos", "192.168.201.1/24", "", true)
- }
+ // 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
+ generateNebulaCert("cosmos", "192.168.201.1/24", "", true)
+ }
- // export nebula.yml
- utils.Log("Constellation: exporting nebula.yml...")
- err := ExportConfigToYAML(utils.GetMainConfig().ConstellationConfig, utils.CONFIGFOLDER + "nebula.yml")
+ // export nebula.yml
+ utils.Log("Constellation: exporting nebula.yml...")
+ err := ExportConfigToYAML(utils.GetMainConfig().ConstellationConfig, utils.CONFIGFOLDER + "nebula.yml")
- if err != nil {
- utils.Error("Constellation: error while exporting nebula.yml", err)
+ if err != nil {
+ utils.Error("Constellation: error while exporting nebula.yml", err)
+ }
}
// start nebula
diff --git a/src/constellation/nebula.go b/src/constellation/nebula.go
index da8d614..7645e57 100644
--- a/src/constellation/nebula.go
+++ b/src/constellation/nebula.go
@@ -80,18 +80,92 @@ func RestartNebula() {
Init()
}
+func ResetNebula() error {
+ stop()
+ utils.Log("Resetting nebula...")
+ os.RemoveAll(utils.CONFIGFOLDER + "nebula.yml")
+ os.RemoveAll(utils.CONFIGFOLDER + "ca.crt")
+ os.RemoveAll(utils.CONFIGFOLDER + "ca.key")
+ os.RemoveAll(utils.CONFIGFOLDER + "cosmos.crt")
+ os.RemoveAll(utils.CONFIGFOLDER + "cosmos.key")
+ // remove everything in db
+
+ c, err := utils.GetCollection(utils.GetRootAppId(), "devices")
+ if err != nil {
+ return err
+ }
+
+ _, err = c.DeleteMany(nil, map[string]interface{}{})
+ if err != nil {
+ return err
+ }
+
+ Init()
+
+ return nil
+}
+
+func GetAllLightHouses() ([]utils.ConstellationDevice, error) {
+ c, err := utils.GetCollection(utils.GetRootAppId(), "devices")
+ if err != nil {
+ return []utils.ConstellationDevice{}, err
+ }
+
+ var devices []utils.ConstellationDevice
+
+ cursor, err := c.Find(nil, map[string]interface{}{
+ "IsLighthouse": true,
+ })
+ cursor.All(nil, &devices)
+
+ if err != nil {
+ return []utils.ConstellationDevice{}, err
+ }
+
+ return devices, nil
+}
+
+func cleanIp(ip string) string {
+ return strings.Split(ip, "/")[0]
+}
+
func ExportConfigToYAML(overwriteConfig utils.ConstellationConfig, outputPath string) error {
// Combine defaultConfig and overwriteConfig
finalConfig := NebulaDefaultConfig
finalConfig.StaticHostMap = map[string][]string{
"192.168.201.1": []string{
- utils.GetMainConfig().HTTPConfig.Hostname + ":4242",
+ utils.GetMainConfig().ConstellationConfig.ConstellationHostname + ":4242",
},
}
+ // for each lighthouse
+ lh, err := GetAllLightHouses()
+ if err != nil {
+ return err
+ }
+
+ for _, l := range lh {
+ finalConfig.StaticHostMap[cleanIp(l.IP)] = []string{
+ l.PublicHostname + ":" + l.Port,
+ }
+ }
+
+ // add other lighthouses
+ finalConfig.Lighthouse.Hosts = []string{}
+ for _, l := range lh {
+ finalConfig.Lighthouse.Hosts = append(finalConfig.Lighthouse.Hosts, cleanIp(l.IP))
+ }
+
finalConfig.Relay.AMRelay = overwriteConfig.NebulaConfig.Relay.AMRelay
+ finalConfig.Relay.Relays = []string{}
+ for _, l := range lh {
+ if l.IsRelay && l.IsLighthouse {
+ finalConfig.Relay.Relays = append(finalConfig.Relay.Relays, cleanIp(l.IP))
+ }
+ }
+
// Marshal the combined config to YAML
yamlData, err := yaml.Marshal(finalConfig)
if err != nil {
@@ -118,7 +192,7 @@ func ExportConfigToYAML(overwriteConfig utils.ConstellationConfig, outputPath st
return nil
}
-func getYAMLClientConfig(name, configPath, capki, cert, key string) (string, error) {
+func getYAMLClientConfig(name, configPath, capki, cert, key string, device utils.ConstellationDevice) (string, error) {
utils.Log("Exporting YAML config for " + name + " with file " + configPath)
// Read the YAML config file
@@ -134,21 +208,38 @@ func getYAMLClientConfig(name, configPath, capki, cert, key string) (string, err
return "", err
}
+ lh, err := GetAllLightHouses()
+ if err != nil {
+ return "", err
+ }
+
if staticHostMap, ok := configMap["static_host_map"].(map[interface{}]interface{}); ok {
staticHostMap["192.168.201.1"] = []string{
- utils.GetMainConfig().HTTPConfig.Hostname + ":4242",
+ utils.GetMainConfig().ConstellationConfig.ConstellationHostname + ":4242",
+ }
+
+ for _, l := range lh {
+ staticHostMap[cleanIp(l.IP)] = []string{
+ l.PublicHostname + ":" + l.Port,
+ }
}
} else {
return "", errors.New("static_host_map not found in nebula.yml")
}
- // set lightHouse to false
+ // set lightHouse
if lighthouseMap, ok := configMap["lighthouse"].(map[interface{}]interface{}); ok {
- lighthouseMap["am_lighthouse"] = false
-
+ lighthouseMap["am_lighthouse"] = device.IsLighthouse
+
lighthouseMap["hosts"] = []string{
"192.168.201.1",
}
+
+ for _, l := range lh {
+ if cleanIp(l.IP) != cleanIp(device.IP) {
+ lighthouseMap["hosts"] = append(lighthouseMap["hosts"].([]string), cleanIp(l.IP))
+ }
+ }
} else {
return "", errors.New("lighthouse not found in nebula.yml")
}
@@ -162,13 +253,34 @@ func getYAMLClientConfig(name, configPath, capki, cert, key string) (string, err
}
if relayMap, ok := configMap["relay"].(map[interface{}]interface{}); ok {
- relayMap["am_relay"] = false
- relayMap["relays"] = []string{"192.168.201.1"}
+ relayMap["am_relay"] = device.IsRelay && device.IsLighthouse
+ relayMap["relays"] = []string{}
+ if utils.GetMainConfig().ConstellationConfig.NebulaConfig.Relay.AMRelay {
+ relayMap["relays"] = append(relayMap["relays"].([]string), "192.168.201.1")
+ }
+
+ for _, l := range lh {
+ if l.IsRelay && l.IsLighthouse && cleanIp(l.IP) != cleanIp(device.IP) {
+ relayMap["relays"] = append(relayMap["relays"].([]string), cleanIp(l.IP))
+ }
+ }
} else {
return "", errors.New("relay not found in nebula.yml")
}
+
+ if listen, ok := configMap["listen"].(map[interface{}]interface{}); ok {
+ if device.Port != "" {
+ listen["port"] = device.Port
+ } else {
+ listen["port"] = "4242"
+ }
+ } else {
+ return "", errors.New("listen not found in nebula.yml")
+ }
configMap["deviceName"] = name
+ configMap["local_dns_overwrite"] = "192.168.201.1"
+ configMap["public_hostname"] = device.PublicHostname
// export configMap as YML
yamlData, err = yaml.Marshal(configMap)
diff --git a/src/httpServer.go b/src/httpServer.go
index 76c2fc9..845fd66 100644
--- a/src/httpServer.go
+++ b/src/httpServer.go
@@ -334,6 +334,8 @@ func InitServer() *mux.Router {
srapi.HandleFunc("/api/constellation/devices", constellation.ConstellationAPIDevices)
srapi.HandleFunc("/api/constellation/restart", constellation.API_Restart)
+ srapi.HandleFunc("/api/constellation/reset", constellation.API_Reset)
+ srapi.HandleFunc("/api/constellation/connect", constellation.API_ConnectToExisting)
srapi.HandleFunc("/api/constellation/config", constellation.API_GetConfig)
srapi.HandleFunc("/api/constellation/logs", constellation.API_GetLogs)
diff --git a/src/proxy/routeTo.go b/src/proxy/routeTo.go
index e139507..cf98a5f 100644
--- a/src/proxy/routeTo.go
+++ b/src/proxy/routeTo.go
@@ -46,7 +46,7 @@ func joinURLPath(a, b *url.URL) (path, rawpath string) {
// NewProxy takes target host and creates a reverse proxy
-func NewProxy(targetHost string, AcceptInsecureHTTPSTarget bool, VerboseForwardHeader bool, DisableHeaderHardening bool, CORSOrigin string) (*httputil.ReverseProxy, error) {
+func NewProxy(targetHost string, AcceptInsecureHTTPSTarget bool, VerboseForwardHeader bool, DisableHeaderHardening bool, CORSOrigin string, route utils.ProxyRouteConfig) (*httputil.ReverseProxy, error) {
url, err := url.Parse(targetHost)
if err != nil {
return nil, err
@@ -76,15 +76,28 @@ func NewProxy(targetHost string, AcceptInsecureHTTPSTarget bool, VerboseForwardH
req.Header.Set("X-Forwarded-Ssl", "on")
}
- if CORSOrigin != "" {
- req.Header.Set("X-Forwarded-Host", url.Host)
+ req.Header.Del("X-Origin-Host")
+ req.Header.Del("X-Forwarded-Host")
+ req.Header.Del("X-Forwarded-For")
+ req.Header.Del("X-Real-Ip")
+ req.Header.Del("Host")
+
+ hostname := utils.GetMainConfig().HTTPConfig.Hostname
+ if route.Host != "" && route.UseHost {
+ hostname = route.Host
+ }
+ if route.UsePathPrefix {
+ hostname = hostname + route.PathPrefix
}
if VerboseForwardHeader {
- req.Header.Set("X-Origin-Host", url.Host)
- req.Header.Set("Host", url.Host)
+ req.Header.Set("X-Origin-Host", hostname)
+ req.Header.Set("Host", hostname)
+ req.Header.Set("X-Forwarded-Host", hostname)
req.Header.Set("X-Forwarded-For", utils.GetClientIP(req))
req.Header.Set("X-Real-IP", utils.GetClientIP(req))
+ } else {
+ req.Host = url.Host
}
}
@@ -100,8 +113,6 @@ func NewProxy(targetHost string, AcceptInsecureHTTPSTarget bool, VerboseForwardH
if CORSOrigin != "" {
resp.Header.Del("Access-Control-Allow-Origin")
- resp.Header.Del("Access-Control-Allow-Methods")
- resp.Header.Del("Access-Control-Allow-Headers")
resp.Header.Del("Access-Control-Allow-Credentials")
}
@@ -126,7 +137,7 @@ func RouteTo(route utils.ProxyRouteConfig) http.Handler {
routeType := route.Mode
if(routeType == "SERVAPP" || routeType == "PROXY") {
- proxy, err := NewProxy(destination, route.AcceptInsecureHTTPSTarget, route.VerboseForwardHeader, route.DisableHeaderHardening, route.CORSOrigin)
+ proxy, err := NewProxy(destination, route.AcceptInsecureHTTPSTarget, route.VerboseForwardHeader, route.DisableHeaderHardening, route.CORSOrigin, route)
if err != nil {
utils.Error("Create Route", err)
}
diff --git a/src/proxy/routerGen.go b/src/proxy/routerGen.go
index f4aef7c..8dd32ae 100644
--- a/src/proxy/routerGen.go
+++ b/src/proxy/routerGen.go
@@ -30,12 +30,12 @@ func tokenMiddleware(enabled bool, adminOnly bool) func(next http.Handler) http.
r.Header.Set("x-cosmos-mfa", strconv.Itoa((int)(u.MFAState)))
ogcookies := r.Header.Get("Cookie")
- cookieRemoveRegex := regexp.MustCompile(`jwttoken=[^;]*;`)
+ cookieRemoveRegex := regexp.MustCompile(`\s?jwttoken=[^;]*;?\s?`)
cookies := cookieRemoveRegex.ReplaceAllString(ogcookies, "")
r.Header.Set("Cookie", cookies)
// Replace the token with a application speicfic one
- r.Header.Set("x-cosmos-token", "1234567890")
+ //r.Header.Set("x-cosmos-token", "1234567890")
if enabled && adminOnly {
if errT := utils.AdminOnlyWithRedirect(w, r); errT != nil {
diff --git a/src/utils/middleware.go b/src/utils/middleware.go
index e06bf40..b2ff755 100644
--- a/src/utils/middleware.go
+++ b/src/utils/middleware.go
@@ -82,8 +82,6 @@ func CORSHeader(origin string) func(next http.Handler) http.Handler {
if origin != "" {
w.Header().Set("Access-Control-Allow-Origin", origin)
- w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
- w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
w.Header().Set("Access-Control-Allow-Credentials", "true")
}
diff --git a/src/utils/types.go b/src/utils/types.go
index 8310cba..8d4460a 100644
--- a/src/utils/types.go
+++ b/src/utils/types.go
@@ -211,6 +211,7 @@ type MarketSource struct {
type ConstellationConfig struct {
Enabled bool
+ SlaveMode bool
DNS bool
DNSPort string
DNSFallback string
@@ -218,6 +219,18 @@ type ConstellationConfig struct {
DNSAdditionalBlocklists []string
CustomDNSEntries map[string]string
NebulaConfig NebulaConfig
+ ConstellationHostname string
+}
+
+type ConstellationDevice struct {
+ Nickname string `json:"nickname"`
+ DeviceName string `json:"deviceName"`
+ PublicKey string `json:"publicKey"`
+ IP string `json:"ip"`
+ IsLighthouse bool `json:"isLighthouse"`
+ IsRelay bool `json:"isRelay"`
+ PublicHostname string `json:"publicHostname"`
+ Port string `json:"port"`
}
type NebulaFirewallRule struct {
diff --git a/src/utils/utils.go b/src/utils/utils.go
index f131fa2..1a32bed 100644
--- a/src/utils/utils.go
+++ b/src/utils/utils.go
@@ -214,6 +214,15 @@ func LoadBaseMainConfig(config Config) {
if MainConfig.DockerConfig.DefaultDataPath == "" {
MainConfig.DockerConfig.DefaultDataPath = "/usr"
}
+
+ if MainConfig.ConstellationConfig.ConstellationHostname == "" {
+ // if hostname is a domain add vpn. suffix otherwise use hostname
+ if IsDomain(MainConfig.HTTPConfig.Hostname) {
+ MainConfig.ConstellationConfig.ConstellationHostname = "vpn." + MainConfig.HTTPConfig.Hostname
+ } else {
+ MainConfig.ConstellationConfig.ConstellationHostname = MainConfig.HTTPConfig.Hostname
+ }
+ }
}
func GetMainConfig() Config {
@@ -577,4 +586,12 @@ func GetClientIP(req *http.Request) string {
ip = req.RemoteAddr
}*/
return req.RemoteAddr
+}
+
+func IsDomain(domain string) bool {
+ // contains . and at least a letter and no special characters invalid in a domain
+ if strings.Contains(domain, ".") && strings.ContainsAny(domain, "abcdefghijklmnopqrstuvwxyz") && !strings.ContainsAny(domain, " !@#$%^&*()+=[]{}\\|;:'\",/<>?") {
+ return true
+ }
+ return false
}
\ No newline at end of file