From 8e83fde02ef5c5912f0b6786aa815609bf785e88 Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Tue, 22 Aug 2023 15:39:06 +0100 Subject: [PATCH] [release] v0.10.0-unstable2 --- client/src/index.css | 3 +- .../src/pages/config/routes/routeoverview.jsx | 4 +- .../src/pages/config/users/formShortcuts.jsx | 2 +- client/src/pages/constellation/addDevice.jsx | 75 ++++++++++++++++--- client/src/pages/constellation/index.jsx | 52 ++++++++++++- package.json | 2 +- src/configapi/set.go | 2 + src/constellation/api_devices_create.go | 5 +- src/constellation/index.go | 2 +- src/constellation/nebula.go | 47 ++++++++---- 10 files changed, 158 insertions(+), 36 deletions(-) diff --git a/client/src/index.css b/client/src/index.css index e7d1479..26dece5 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -123,8 +123,9 @@ align-items: center; } -.loading-image { +.loading-image:empty { background: url('assets/images/icons/cosmos_gray.png') no-repeat center center; + background-size: contain; } .raw-table table { diff --git a/client/src/pages/config/routes/routeoverview.jsx b/client/src/pages/config/routes/routeoverview.jsx index b17ca2b..f7829b1 100644 --- a/client/src/pages/config/routes/routeoverview.jsx +++ b/client/src/pages/config/routes/routeoverview.jsx @@ -1,7 +1,7 @@ import * as React from 'react'; import MainCard from '../../../components/MainCard'; import RestartModal from '../users/restart'; -import { Chip, Divider, Stack, useMediaQuery } from '@mui/material'; +import { Checkbox, Chip, Divider, FormControlLabel, Stack, useMediaQuery } from '@mui/material'; import HostChip from '../../../components/hostChip'; import { RouteMode, RouteSecurity } from '../../../components/routeComponents'; import { getFaviconURL } from '../../../utils/routes'; @@ -9,6 +9,8 @@ import * as API from '../../../api'; import { CheckOutlined, ClockCircleOutlined, DashboardOutlined, DeleteOutlined, DownOutlined, LockOutlined, UpOutlined } from "@ant-design/icons"; import IsLoggedIn from '../../../isLoggedIn'; import { redirectToLocal } from '../../../utils/indexs'; +import { CosmosCheckbox } from '../users/formShortcuts'; +import { Field } from 'formik'; const info = { backgroundColor: 'rgba(0, 0, 0, 0.1)', diff --git a/client/src/pages/config/users/formShortcuts.jsx b/client/src/pages/config/users/formShortcuts.jsx index 78d9c43..850bf8e 100644 --- a/client/src/pages/config/users/formShortcuts.jsx +++ b/client/src/pages/config/users/formShortcuts.jsx @@ -206,7 +206,7 @@ export const CosmosCollapse = ({ children, title }) => { export function CosmosFormDivider({title}) { return - + {title && } } diff --git a/client/src/pages/constellation/addDevice.jsx b/client/src/pages/constellation/addDevice.jsx index 85de900..22b47dc 100644 --- a/client/src/pages/constellation/addDevice.jsx +++ b/client/src/pages/constellation/addDevice.jsx @@ -12,20 +12,56 @@ import { PlusCircleFilled } from '@ant-design/icons'; import { Formik } from 'formik'; import * as yup from 'yup'; import * as API from '../../api'; -import { CosmosInputText, CosmosSelect } from '../config/users/formShortcuts'; +import { CosmosFormDivider, CosmosInputText, CosmosSelect } from '../config/users/formShortcuts'; import { DownloadFile } from '../../api/downloadButton'; +import QRCode from 'qrcode'; -const AddDeviceModal = ({ config, isAdmin, refreshConfig, devices }) => { +const AddDeviceModal = ({ users, config, isAdmin, refreshConfig, devices }) => { const [openModal, setOpenModal] = useState(false); const [isDone, setIsDone] = useState(null); + const canvasRef = React.useRef(null); + + let firstIP = "192.168.201.1/24"; + if (devices && devices.length > 0) { + const isIpFree = (ip) => { + return devices.filter((d) => d.ip === ip).length === 0; + } + let i = 1; + let j = 201; + while (!isIpFree(firstIP)) { + i++; + if (i > 254) { + i = 0; + j++; + } + firstIP = "192.168." + j + "." + i + "/24"; + } + } + + const renderCanvas = (data) => { + if (!canvasRef.current) return setTimeout(() => { + renderCanvas(data); + }, 500); + + QRCode.toCanvas(canvasRef.current, JSON.stringify(data), + { + width: 600, + color: { + dark: "#000", + light: '#fff' + } + }, function (error) { + if (error) console.error(error) + }) + } return <> setOpenModal(false)}> { return API.constellation.addDevice(values).then(({data}) => { setIsDone(data); refreshConfig(); + renderCanvas(data); }).catch((err) => { setErrors(err.response.data); }); @@ -43,16 +80,30 @@ const AddDeviceModal = ({ config, isAdmin, refreshConfig, devices }) => { > {(formik) => (
- Manually Add Device + Add Device {isDone ?

Device added successfully! - Download the private and public keys to your device along side the config and network certificate to connect: + 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:

+ +
+ +
+ + + + {
: -

Manually add a device to the constellation. It is recommended that you use the Cosmos app instead. Use this form to add another Nebula device manually

+

Add a device to the constellation using either the Cosmos or Nebula client

{ label="Owner" formik={formik} // disabled={!isAdmin} - options={[ - ["admin", "admin"] - ]} + options={ + users.map((u) => { + return [u.nickname, u.nickname] + }) + } /> { variant="contained" startIcon={} > - Manually Add Device + Add Device ; }; diff --git a/client/src/pages/constellation/index.jsx b/client/src/pages/constellation/index.jsx index d0550bb..7c447d1 100644 --- a/client/src/pages/constellation/index.jsx +++ b/client/src/pages/constellation/index.jsx @@ -6,10 +6,16 @@ import PrettyTableView from "../../components/tableView/prettyTableView"; import { DeleteButton } from "../../components/delete"; import { CloudOutlined, 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 MainCard from "../../components/MainCard"; +import { Formik } from "formik"; +import { LoadingButton } from "@mui/lab"; export const ConstellationIndex = () => { const [isAdmin, setIsAdmin] = useState(false); const [config, setConfig] = useState(null); + const [users, setUsers] = useState(null); const [devices, setDevices] = useState(null); const refreshConfig = async () => { @@ -17,6 +23,7 @@ export const ConstellationIndex = () => { setConfig(configAsync.data); setIsAdmin(configAsync.isAdmin); setDevices((await API.constellation.list()).data || []); + setUsers((await API.users.list()).data || []); }; useEffect(() => { @@ -40,12 +47,48 @@ export const ConstellationIndex = () => { return <> - {devices && config && <> + {(devices && config && users) ? <> + +
+ + + { + let newConfig = { ...config }; + newConfig.ConstellationConfig.Enabled = values.Enabled; + return API.config.set(newConfig); + }} + > + {(formik) => ( + + + + + Save + + + + )} + + + +
+ r.deviceName} buttons={[ - + ]} columns={[ { @@ -76,6 +119,9 @@ export const ConstellationIndex = () => { } ]} /> - } +
+ :
+ +
} }; \ No newline at end of file diff --git a/package.json b/package.json index 649a5f8..11dd3e5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.10.0-unstable1", + "version": "0.10.0-unstable2", "description": "", "main": "test-server.js", "bugs": { diff --git a/src/configapi/set.go b/src/configapi/set.go index 85b5624..a2aad63 100644 --- a/src/configapi/set.go +++ b/src/configapi/set.go @@ -5,6 +5,7 @@ import ( "encoding/json" "github.com/azukaar/cosmos-server/src/utils" "github.com/azukaar/cosmos-server/src/authorizationserver" + "github.com/azukaar/cosmos-server/src/constellation" ) func ConfigApiSet(w http.ResponseWriter, req *http.Request) { @@ -43,6 +44,7 @@ func ConfigApiSet(w http.ResponseWriter, req *http.Request) { utils.DisconnectDB() authorizationserver.Init() utils.RestartHTTPServer() + constellation.RestartNebula() json.NewEncoder(w).Encode(map[string]interface{}{ "status": "OK", diff --git a/src/constellation/api_devices_create.go b/src/constellation/api_devices_create.go index 014132c..f18c6f3 100644 --- a/src/constellation/api_devices_create.go +++ b/src/constellation/api_devices_create.go @@ -16,7 +16,6 @@ type DeviceCreateRequestJSON struct { } func DeviceCreate(w http.ResponseWriter, req *http.Request) { - if(req.Method == "POST") { var request DeviceCreateRequestJSON err1 := json.NewDecoder(req.Body).Decode(&request) @@ -58,7 +57,8 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) { }).Decode(&device) if err2 == mongo.ErrNoDocuments { - cert, key, err := generateNebulaCert(deviceName, request.IP, false) + + cert, key, err := generateNebulaCert(deviceName, request.IP, request.PublicKey, false) if err != nil { utils.Error("DeviceCreation: Error while creating Device", err) @@ -71,7 +71,6 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) { "Nickname": nickname, "DeviceName": deviceName, "PublicKey": cert, - "PrivateKey": key, "IP": request.IP, }) diff --git a/src/constellation/index.go b/src/constellation/index.go index 07426ba..58299c1 100644 --- a/src/constellation/index.go +++ b/src/constellation/index.go @@ -23,7 +23,7 @@ func Init() { 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.0/24", true) + generateNebulaCert("cosmos", "192.168.201.0/24", "", true) } // export nebula.yml diff --git a/src/constellation/nebula.go b/src/constellation/nebula.go index 227b331..bb5d306 100644 --- a/src/constellation/nebula.go +++ b/src/constellation/nebula.go @@ -58,7 +58,7 @@ func stop() error { defer processMux.Unlock() if process == nil { - return errors.New("nebula is not running") + return nil } if err := process.Process.Kill(); err != nil { @@ -69,11 +69,9 @@ func stop() error { return nil } -func restart() error { - if err := stop(); err != nil { - return err - } - return startNebulaInBackground() +func RestartNebula() { + stop() + Init() } func ExportConfigToYAML(overwriteConfig utils.ConstellationConfig, outputPath string) error { @@ -192,15 +190,36 @@ func killAllNebulaInstances() error { return nil } -func generateNebulaCert(name, ip string, saveToFile bool) (string, string, error) { +func generateNebulaCert(name, ip, PK string, saveToFile bool) (string, string, error) { // Run the nebula-cert command - cmd := exec.Command(binaryToRun() + "-cert", - "sign", - "-ca-crt", utils.CONFIGFOLDER + "ca.crt", - "-ca-key", utils.CONFIGFOLDER + "ca.key", - "-name", name, - "-ip", ip, - ) + var cmd *exec.Cmd + + if(PK == "") { + cmd = exec.Command(binaryToRun() + "-cert", + "sign", + "-ca-crt", utils.CONFIGFOLDER + "ca.crt", + "-ca-key", utils.CONFIGFOLDER + "ca.key", + "-name", name, + "-ip", ip, + ) + } else { + // write PK to temp.cert + err := ioutil.WriteFile("./temp.cert", []byte(PK), 0644) + if err != nil { + return "", "", fmt.Errorf("failed to write temp.cert: %s", err) + } + cmd = exec.Command(binaryToRun() + "-cert", + "sign", + "-ca-crt", utils.CONFIGFOLDER + "ca.crt", + "-ca-key", utils.CONFIGFOLDER + "ca.key", + "-name", name, + "-ip", ip, + "-in-pub", "./temp.cert", + ) + // delete temp.cert + defer os.Remove("./temp.cert") + } + utils.Debug(cmd.String()) cmd.Stderr = os.Stderr