mirror of
https://github.com/azukaar/Cosmos-Server.git
synced 2026-10-12 02:11:26 +00:00
[release] v0.22.0-unstable01
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
## Version 0.22
|
||||
- Support for mounting network disk
|
||||
- Fix issue with Constellation sometime not connecting right away on new network
|
||||
|
||||
## Version 0.21.7
|
||||
- Fix issue with Constellation routing HTTP tunnels to the wrong protocol
|
||||
|
||||
## Version 0.21.6
|
||||
- Prevent potential double counting of server by wrong timing
|
||||
|
||||
@@ -117,7 +117,8 @@ const ProxyManagement = () => {
|
||||
API.constellation.tunnels().then((res) => {
|
||||
setTunnels((res.data || []).map(r => {
|
||||
let route = r.Route;
|
||||
route._from = r.From;
|
||||
route._from = (r.Targets || []).map(t => t.deviceName);
|
||||
route._targets = r.Targets || [];
|
||||
return route;
|
||||
}));
|
||||
});
|
||||
@@ -276,7 +277,10 @@ const ProxyManagement = () => {
|
||||
</div> : <div></div>
|
||||
},
|
||||
{ title: t('mgmt.config.proxy.originTitle'), screenMin: 'md', clickable:true, search: (r) => r.Host + ' ' + r.PathPrefix, field: (r) => <HostChip ellipsis route={r} /> },
|
||||
{ title: t('global.target'), screenMin: 'md', search: (r) => r.Target, field: (r) => <><RouteMode route={r} /> <Chip label={r.Target} /></> },
|
||||
{ title: t('global.target'), screenMin: 'md', search: (r) => r._IsTunnel ? (r._targets || []).map(t => t.targetURL).join(' ') : r.Target, field: (r) => r._IsTunnel && r._targets && r._targets.length > 0
|
||||
? <><RouteMode route={r} /> {r._targets.map((t, i) => <Chip key={i} label={t.targetURL + ' (' + t.deviceName + ')'} style={{marginBottom: 2}} />)}</>
|
||||
: <><RouteMode route={r} /> <Chip label={r.Target} /></>
|
||||
},
|
||||
{ title: t('global.securityTitle'), screenMin: 'lg', field: (r) => <RouteSecurity route={r} />,
|
||||
style: {minWidth: '70px'} },
|
||||
{ title: '', clickable:true, field: (r, k) => r._IsTunnel ? <Tooltip title={t('tooltip.route.tunnelWarn')}>
|
||||
|
||||
@@ -438,12 +438,16 @@ export const ConstellationVPN = ({ freeVersion }) => {
|
||||
</Tooltip>
|
||||
</Stack>} />}
|
||||
|
||||
{!freeVersion && !config.ConstellationConfig.ThisDeviceName && <CosmosCheckbox disabled={!isAdmin} formik={formik} name="IsLighthouse" label={<Stack direction="row" spacing={0.5} alignItems="center" component="span">
|
||||
{!freeVersion && !config.ConstellationConfig.ThisDeviceName && <>
|
||||
<Alert severity="warning">
|
||||
{t('mgmt.constellation.setup.lighthouseInfo')}
|
||||
</Alert>
|
||||
<CosmosCheckbox disabled={!isAdmin} formik={formik} name="IsLighthouse" label={<Stack direction="row" spacing={0.5} alignItems="center" component="span">
|
||||
<span>{t('mgmt.constellation.setup.isLighthouse.label')}</span>
|
||||
<Tooltip title={t('mgmt.constellation.setup.isLighthouse.tooltip')}>
|
||||
<QuestionCircleOutlined style={{ fontSize: 14, cursor: 'help', opacity: 0.6 }} />
|
||||
</Tooltip>
|
||||
</Stack>} />}
|
||||
</Stack>} /></>}
|
||||
{constellationEnabled && formik.values.IsLighthouse && <>
|
||||
<CosmosCheckbox disabled={!isAdmin} formik={formik} name="IsRelay" label={<Stack direction="row" spacing={0.5} alignItems="center" component="span">
|
||||
<span>{t('mgmt.constellation.setup.relayRequests.label')}</span>
|
||||
|
||||
@@ -272,6 +272,7 @@
|
||||
"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.lighthouseInfo": "This is important! If you are behind CGNAT or do not wish to open any port on your server, you have to uncheck this!",
|
||||
"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:",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cosmos-server",
|
||||
"version": "0.21.7",
|
||||
"version": "0.22.0-unstable01",
|
||||
"description": "",
|
||||
"main": "test-server.js",
|
||||
"bugs": {
|
||||
|
||||
@@ -80,7 +80,7 @@ func Init() {
|
||||
oauth2 = compose.ComposeAllEnabled(foconfig, store, AuthPrivateKey)
|
||||
|
||||
routes := config.HTTPConfig.ProxyConfig.Routes
|
||||
routes = append(routes, config.ConstellationConfig.Tunnels...)
|
||||
routes = append(routes, utils.GetConstellationTunnelRoutes()...)
|
||||
|
||||
// Add proxy route clients
|
||||
for _, route := range routes {
|
||||
|
||||
@@ -226,6 +226,11 @@ func StartNATS() {
|
||||
utils.Error("[NATS] Failed to get cluster IPs", err)
|
||||
}
|
||||
|
||||
utils.Debug("[NATS] Cluster IPs: ")
|
||||
for _, cip := range cips {
|
||||
utils.Debug("[NATS] Cluster IP: " + cip.String())
|
||||
}
|
||||
|
||||
// if only one, abort
|
||||
if (len(cips) == 0) {
|
||||
utils.Warn("[NATS] No cluster IPs found, NATS server will not start")
|
||||
@@ -296,6 +301,8 @@ func StartNATS() {
|
||||
utils.Debug("[NATS] Retrying to start NATS server")
|
||||
}
|
||||
|
||||
NATSStarted = true
|
||||
|
||||
if err != nil {
|
||||
utils.MajorError("[NATS] Error starting NATS server", err)
|
||||
} else {
|
||||
@@ -312,6 +319,7 @@ func StopNATS() {
|
||||
ns.WaitForShutdown()
|
||||
ns = nil
|
||||
}
|
||||
NATSStarted = false
|
||||
}
|
||||
|
||||
// sync lock - RWMutex allows multiple readers, single writer
|
||||
@@ -320,6 +328,11 @@ var NATSClientTopic = ""
|
||||
var nc *nats.Conn
|
||||
var js nats.JetStreamContext
|
||||
func InitNATSClient() error {
|
||||
if !NATSStarted {
|
||||
utils.Warn("[NATS] NATS server not started, cannot initialize client")
|
||||
return errors.New("NATS server not started, cannot initialize client")
|
||||
}
|
||||
|
||||
clientConfigLock.Lock()
|
||||
defer clientConfigLock.Unlock()
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ func DeviceBlock(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
go func() {
|
||||
SendNewDBSyncMessage()
|
||||
go SendNewDBSyncMessage()
|
||||
time.Sleep(2 * time.Second)
|
||||
RestartNebula()
|
||||
}()
|
||||
|
||||
@@ -168,7 +168,7 @@ func DeviceCreate_API(w http.ResponseWriter, req *http.Request) {
|
||||
})
|
||||
|
||||
go func() {
|
||||
SendNewDBSyncMessage()
|
||||
go SendNewDBSyncMessage()
|
||||
time.Sleep(2 * time.Second)
|
||||
RestartNebula()
|
||||
}()
|
||||
|
||||
@@ -89,7 +89,7 @@ func DeviceEdit_API(w http.ResponseWriter, req *http.Request) {
|
||||
})
|
||||
|
||||
go func() {
|
||||
SendNewDBSyncMessage()
|
||||
go SendNewDBSyncMessage()
|
||||
time.Sleep(2 * time.Second)
|
||||
RestartNebula()
|
||||
}()
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
var NebulaStarted = false
|
||||
var NebulaHasStarted = false
|
||||
var NATSStarted = false
|
||||
var CachedDeviceNames = map[string]string{}
|
||||
var CachedDevices = map[string]utils.ConstellationDevice{}
|
||||
var needToSyncCA = false
|
||||
@@ -15,6 +16,15 @@ func resyncConstellationNodes() {
|
||||
SendNewDBSyncMessage()
|
||||
}
|
||||
|
||||
func getConstellationTunnelRoutes() []utils.ProxyRouteConfig {
|
||||
tunnels := GetLocalTunnelCache()
|
||||
routes := make([]utils.ProxyRouteConfig, len(tunnels))
|
||||
for i, t := range tunnels {
|
||||
routes[i] = t.Route
|
||||
}
|
||||
return routes
|
||||
}
|
||||
|
||||
func GetDefaultHostnames() []string {
|
||||
hostnames, _ := utils.ListIps(true)
|
||||
httpHostname := utils.GetMainConfig().HTTPConfig.Hostname
|
||||
@@ -71,8 +81,10 @@ func Init() {
|
||||
utils.IsConstellationIP = IsConstellationIP
|
||||
|
||||
utils.ResyncConstellationNodes = resyncConstellationNodes
|
||||
utils.GetConstellationTunnelRoutes = getConstellationTunnelRoutes
|
||||
|
||||
NebulaStarted = false
|
||||
NATSStarted = false
|
||||
|
||||
var err error
|
||||
|
||||
|
||||
@@ -81,7 +81,10 @@ func startNebula() error {
|
||||
|
||||
UpdateFirewallBlockedClients()
|
||||
AdjustDNS(logBuffer)
|
||||
ExportLighthouseFromDB()
|
||||
e := ExportLighthouseFromDB()
|
||||
if e != nil {
|
||||
utils.Error("Failed to export lighthouse config from DB", e)
|
||||
}
|
||||
|
||||
// Handle existing PID file
|
||||
pidFile := utils.CONFIGFOLDER + "nebula.pid"
|
||||
@@ -216,6 +219,7 @@ func monitorNebulaProcess(proc *exec.Cmd) {
|
||||
defer ProcessMux.Unlock()
|
||||
process = nil
|
||||
NebulaStarted = false
|
||||
NATSStarted = false
|
||||
}
|
||||
|
||||
func stop() {
|
||||
@@ -244,6 +248,7 @@ func stop() {
|
||||
}
|
||||
|
||||
NebulaStarted = false
|
||||
NATSStarted = false
|
||||
cachedCurrentDevice = nil
|
||||
CachedDevices = map[string]utils.ConstellationDevice{}
|
||||
CachedDeviceNames = map[string]string{}
|
||||
@@ -293,6 +298,7 @@ func ResetNebula() error {
|
||||
config.ConstellationConfig.DNSDisabled = false
|
||||
config.ConstellationConfig.FirewallBlockedClients = []string{}
|
||||
config.ConstellationConfig.ThisDeviceName = ""
|
||||
config.ConstellationConfig.IPRange = ""
|
||||
config.ConstellationConfig.ConstellationHostname = strings.Join(GetDefaultHostnames(), ",")
|
||||
|
||||
if config.Licence == "" {
|
||||
|
||||
@@ -54,7 +54,12 @@ func GetAllTunneledRoutes() []utils.ProxyRouteConfig {
|
||||
protocol = "http://"
|
||||
}
|
||||
|
||||
route.Target = protocol + thisIp + ":" + port
|
||||
route.Target = protocol + thisIp
|
||||
|
||||
if port != "" {
|
||||
route.Target += ":" + port
|
||||
}
|
||||
|
||||
route.Mode = "PROXY"
|
||||
|
||||
if route.TunneledHost != "" {
|
||||
@@ -126,7 +131,7 @@ func ClientHeartbeatInit() {
|
||||
|
||||
utils.Debug("[NATS] Key-Value store 'constellation-nodes' ready")
|
||||
|
||||
go UpdateLocalTunnelCache();
|
||||
UpdateLocalTunnelCache()
|
||||
|
||||
heartbeatStopChan = make(chan struct{})
|
||||
heartbeatTicker = time.NewTicker(2 * time.Second)
|
||||
@@ -135,6 +140,55 @@ func ClientHeartbeatInit() {
|
||||
stopChan := heartbeatStopChan
|
||||
ticker := heartbeatTicker
|
||||
|
||||
// Watch KV for changes and refresh tunnel cache
|
||||
go func() {
|
||||
err := ClientConnectToJS()
|
||||
if err != nil {
|
||||
utils.Warn("[NATS] Error connecting to JetStream for KV watcher: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
clientConfigLock.RLock()
|
||||
if js == nil {
|
||||
clientConfigLock.RUnlock()
|
||||
utils.Warn("[NATS] JetStream context is nil for KV watcher")
|
||||
return
|
||||
}
|
||||
kv, err := js.KeyValue("constellation-nodes")
|
||||
clientConfigLock.RUnlock()
|
||||
if err != nil {
|
||||
utils.Warn("[NATS] Error getting KV store for watcher: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
watcher, err := kv.WatchAll()
|
||||
if err != nil {
|
||||
utils.Warn("[NATS] Error creating KV watcher: " + err.Error())
|
||||
return
|
||||
}
|
||||
defer watcher.Stop()
|
||||
|
||||
utils.Log("[NATS] KV watcher started for tunnel cache updates")
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-stopChan:
|
||||
utils.Log("[NATS] KV watcher stopped")
|
||||
return
|
||||
case entry, ok := <-watcher.Updates():
|
||||
if !ok {
|
||||
utils.Warn("[NATS] KV watcher channel closed")
|
||||
return
|
||||
}
|
||||
if entry == nil {
|
||||
// nil marks end of initial values
|
||||
continue
|
||||
}
|
||||
GetLocalTunnelCache()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
@@ -288,6 +342,13 @@ func UpdateLocalTunnelCache() {
|
||||
|
||||
tunnels := make([]utils.ConstellationTunnel, 0, len(byName))
|
||||
for _, t := range byName {
|
||||
// Ensure local node is always first in targets
|
||||
for i, target := range t.Targets {
|
||||
if target.DeviceName == currentDeviceName && i != 0 {
|
||||
t.Targets[0], t.Targets[i] = t.Targets[i], t.Targets[0]
|
||||
break
|
||||
}
|
||||
}
|
||||
tunnels = append(tunnels, *t)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
)
|
||||
|
||||
func BuildFromConfig(router *mux.Router, config utils.ProxyConfig) *mux.Router {
|
||||
|
||||
router.HandleFunc("/_health", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("OK"))
|
||||
|
||||
+20
-7
@@ -241,16 +241,29 @@ func TunnelRouteTo(tunnel utils.ConstellationTunnel, lb *TunnelLoadBalancer) htt
|
||||
handler http.Handler
|
||||
}
|
||||
|
||||
// For self-tunnel, use the original route handler to avoid proxy loop
|
||||
currentDeviceName, _ := constellation.GetCurrentDeviceName()
|
||||
|
||||
proxies := make([]targetProxy, 0, len(tunnel.Targets))
|
||||
for _, t := range tunnel.Targets {
|
||||
route := tunnel.Route
|
||||
route.Target = t.TargetURL
|
||||
proxy, err := NewProxy(t.TargetURL, route.AcceptInsecureHTTPSTarget, route.DisableHeaderHardening, route)
|
||||
if err != nil {
|
||||
utils.Error("Create Tunnel Route for "+t.DeviceName, err)
|
||||
continue
|
||||
if t.DeviceName == currentDeviceName {
|
||||
// Find the original config route and use RouteTo directly
|
||||
for _, cfgRoute := range utils.GetMainConfig().HTTPConfig.ProxyConfig.Routes {
|
||||
if cfgRoute.Name == tunnel.Route.Name {
|
||||
proxies = append(proxies, targetProxy{t, RouteTo(cfgRoute)})
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
route := tunnel.Route
|
||||
route.Target = t.TargetURL
|
||||
proxy, err := NewProxy(t.TargetURL, route.AcceptInsecureHTTPSTarget, route.DisableHeaderHardening, route)
|
||||
if err != nil {
|
||||
utils.Error("Create Tunnel Route for "+t.DeviceName, err)
|
||||
continue
|
||||
}
|
||||
proxies = append(proxies, targetProxy{t, proxy})
|
||||
}
|
||||
proxies = append(proxies, targetProxy{t, proxy})
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package proxy
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"strings"
|
||||
|
||||
"github.com/azukaar/cosmos-server/src/constellation"
|
||||
"github.com/azukaar/cosmos-server/src/utils"
|
||||
@@ -45,6 +46,7 @@ func (lb *TunnelLoadBalancer) SelectTarget(targets []utils.TunnelTarget, routeNa
|
||||
|
||||
// Mode selection
|
||||
var selected *utils.TunnelTarget
|
||||
mode = strings.ToLower(mode)
|
||||
switch mode {
|
||||
case "round_robin":
|
||||
c := lb.getCounter(routeName)
|
||||
|
||||
+3
-3
@@ -262,9 +262,9 @@ type ProxyRouteConfig struct {
|
||||
ExtraHeaders map[string]string `yaml:"extra_headers,omitempty"`
|
||||
SkipURLClean bool `yaml:"skip_url_clean"`
|
||||
UseH2C bool `yaml:"use_h2c"`
|
||||
LBMode string `yaml:"lb_mode" json:"lb_mode,omitempty"`
|
||||
LBStickyMode bool `yaml:"lb_sticky_mode" json:"lb_sticky_mode,omitempty"`
|
||||
Const_IsTunneled bool `yaml:"-", json:"-"`
|
||||
LBMode string `yaml:"lb_mode" json:"LBMode,omitempty"`
|
||||
LBStickyMode bool `yaml:"lb_sticky_mode" json:"LBStickyMode,omitempty"`
|
||||
Const_IsTunneled bool `yaml:"-" json:"-"`
|
||||
}
|
||||
|
||||
type EmailConfig struct {
|
||||
|
||||
+3
-2
@@ -66,6 +66,7 @@ var StopAllRCloneProcess func(bool)
|
||||
var InitPremiumFeatures func()
|
||||
|
||||
var ResyncConstellationNodes = func() {}
|
||||
var GetConstellationTunnelRoutes = func() []ProxyRouteConfig { return []ProxyRouteConfig{} }
|
||||
|
||||
var LetsEncryptErrors = []string{}
|
||||
|
||||
@@ -560,8 +561,8 @@ func GetAllHostnames(applyWildCard bool, removePorts bool) []string {
|
||||
mainHostname,
|
||||
}
|
||||
|
||||
proxies := GetMainConfig().ConstellationConfig.Tunnels
|
||||
proxies = append(proxies, GetMainConfig().HTTPConfig.ProxyConfig.Routes...)
|
||||
proxies := GetMainConfig().HTTPConfig.ProxyConfig.Routes
|
||||
proxies = append(proxies, GetConstellationTunnelRoutes()...)
|
||||
|
||||
for _, proxy := range proxies {
|
||||
if proxy.UseHost && proxy.Host != "" && !strings.Contains(proxy.Host, ",") && !strings.Contains(proxy.Host, " ") {
|
||||
|
||||
Reference in New Issue
Block a user