[release] v0.21.0-unstable9

This commit is contained in:
Yann Stepienik
2026-02-16 18:47:56 +00:00
parent 74133d0b74
commit fc9d69baa8
13 changed files with 124 additions and 51 deletions
+2 -1
View File
@@ -5,8 +5,9 @@
- Support for tempFS
- Improve support for 0.0.0.0 routes
- Reworked the setting page for better clarity
- Added support for HTTP2 Cleartext (H2C)
- Added advanced DNS options (challenge related, thanks @moham96)
- Added trusted proxy option (Thanks @InterN0te)
- Added trusted proxy option for X-Forwarded-For (Thanks @InterN0te)
- Reduce SmartShield false positive on the server panel by having two level of strictness (UI/panel vs. login)
- Rclone rework, no more sub processes (increased performance and reliability)
- Added the ability to manage Restic locks from the UI
+9 -1
View File
@@ -81,6 +81,7 @@ const RouteManagement = ({ routeConfig, routeNames, config, TargetContainer, noC
UseHost: routeConfig.UseHost,
Host: routeConfig.Host,
AcceptInsecureHTTPSTarget: routeConfig.AcceptInsecureHTTPSTarget === true,
UseH2C: routeConfig.UseH2C === true,
UsePathPrefix: routeConfig.UsePathPrefix,
PathPrefix: routeConfig.PathPrefix,
StripPathPrefix: routeConfig.StripPathPrefix,
@@ -390,7 +391,14 @@ const RouteManagement = ({ routeConfig, routeNames, config, TargetContainer, noC
name="SkipURLClean"
label={t('mgmt.urls.edit.advancedSettings.skipURLCleanCheckbox.skipURLCleanLabel')}
formik={formik}
/></>}
/>
{(formik.values.Mode === "SERVAPP" || formik.values.Mode === "PROXY") && <CosmosCheckbox
name="UseH2C"
label={t('mgmt.urls.edit.useH2CCheckbox.useH2CLabel')}
formik={formik}
/>}
</>}
<Alert severity='warning'>
{t('mgmt.urls.edit.advancedSettings.filterIpWarning')}
+2 -1
View File
@@ -221,7 +221,7 @@
"mgmt.config.security.adminRestrictions.adminRestrictionsInfo": "Use those options to restrict access to the admin panel. Be careful, if you lock yourself out, you will need to manually edit the config file. To restrict the access to your local network, you can use the 'Admin Whitelist' with the IP range 192.168.0.0/16",
"mgmt.config.security.adminRestrictions.adminWhitelistInput.adminWhitelistHelperText": "Comma separated list of IPs that will be allowed to access the admin panel",
"mgmt.config.security.adminRestrictions.adminWhitelistInput.adminWhitelistLabel": "Admin Whitelist Inbound IPs and/or IP ranges (comma separated)",
"mgmt.config.security.trustedProxiesInput.trustedProxiesLabel": "Trusted proxies allow X-Forwarded-For from IP/IP range",
"mgmt.config.security.trustedProxiesInput.trustedProxiesLabel": "Comma separated trusted proxies allow X-Forwarded-For from IP/IP range (can also be a URL to a list of IPs like the cloudflare list)",
"mgmt.config.security.trustedProxiesInput.trustedProxiesHelperText": "Comma separated list of IPs/IP ranges of upstream proxies that are trusted to provide the real client IP via X-Forwarded-For header",
"mgmt.config.security.adminRestrictionsTitle": "Admin Restrictions",
"mgmt.config.security.encryption.authPubKeyTitle": "Authentication Public Key",
@@ -679,6 +679,7 @@
"mgmt.urls.edit.basicSecurity.smartShield.noReffererCheckbox.noReffererLabel": "Block requests without Referer header",
"mgmt.urls.edit.basicSecurityTitle": "Basic Security",
"mgmt.urls.edit.insecureHttpsCheckbox.insecureHttpsLabel": "Accept Insecure HTTPS Target (not recommended)",
"mgmt.urls.edit.useH2CCheckbox.useH2CLabel": "Use HTTP/2 Cleartext (H2C)",
"mgmt.urls.edit.newUrlTitle": "new URL",
"mgmt.urls.edit.updateSuccess": "Route updated successfully",
"mgmt.urls.edit.nameValidation": "Route Name already exists. Name must be unique.",
@@ -217,7 +217,6 @@
"mgmt.config.security.geoBlockSelection.varBlock": "bloquer",
"mgmt.config.security.geoblock.resetToDefaultButton": "Réinitialiser par défaut (pays les plus dangereux)",
"mgmt.config.security.invertBlacklistCheckbox.invertBlacklistLabel": "Utiliser la liste comme liste blanche au lieu de liste noire",
"mgmt.config.security.trustedProxiesInput.trustedProxiesLabel": "Les proxies de confiance autorisent X-Forwarded-For depuis une IP/plage d'IP",
"mgmt.config.security.trustedProxiesInput.trustedProxiesHelperText": "Liste d'IPs/plages d'IPs séparées par des virgules des proxies en amont auxquels on fait confiance pour fournir l'IP réelle du client via l'en-tête X-Forwarded-For",
"mgmt.constellation.dns.resetButton": "Réinitialiser",
"mgmt.constellation.dnsBlocklistsTitle": "Listes de blocage DNS",
+1
View File
@@ -20,6 +20,7 @@ VOLUME /config
RUN apt-get update \
&& apt-get install -y ca-certificates openssl fdisk mergerfs snapraid \
curl unzip wget avahi-daemon avahi-utils samba fuse3 \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
+2 -1
View File
@@ -10,7 +10,8 @@ WORKDIR /app
ENV PATH=$PATH:/usr/local/go/bin
RUN apt-get update && apt-get install -y ca-certificates openssl fdisk mergerfs snapraid && \
RUN apt-get update && apt-get install -y ca-certificates openssl fdisk mergerfs snapraid \
avahi-daemon avahi-utils samba fuse3 unzip && \
apt-get install -y --no-install-recommends wget curl && \
apt-get install -y --no-install-recommends nodejs && \
wget https://golang.org/dl/go1.23.2.linux-amd64.tar.gz && \
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "cosmos-server",
"version": "0.21.0-unstable8",
"version": "0.21.0-unstable9",
"description": "",
"main": "test-server.js",
"bugs": {
+1 -1
View File
@@ -15,7 +15,7 @@ func ConfigApiRestart(w http.ResponseWriter, req *http.Request) {
json.NewEncoder(w).Encode(map[string]interface{}{
"status": "OK",
})
utils.RestartServer()
utils.RestartServer(0)
} else {
utils.Error("Restart: Method not allowed" + req.Method, nil)
utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001")
+5 -1
View File
@@ -136,7 +136,11 @@ func startNebula() error {
}
}
if err != nil {
return fmt.Errorf("failed to create nebula instance: %w", err)
utils.Error("failed to create nebula instance, need to restart the process", err)
// TODO: need a way to avoid restart loops (check if Nebula ever started?)
// We need a full restart
// utils.RestartServer(1)
}
// Actually start the nebula service (brings up TUN interface)
-1
View File
@@ -69,7 +69,6 @@ func Connect() error {
DockerIsConnected = false
return err
}
defer client.Close()
DockerClient = client
DockerContext = ctx
+43 -38
View File
@@ -16,6 +16,8 @@ import (
"github.com/azukaar/cosmos-server/src/utils"
"github.com/azukaar/cosmos-server/src/docker"
"github.com/azukaar/cosmos-server/src/constellation"
"golang.org/x/net/http2"
)
@@ -58,56 +60,59 @@ func NewProxy(targetHost string, AcceptInsecureHTTPSTarget bool, DisableHeaderHa
var transport http.RoundTripper
var targetURL *url.URL
var err error
// if strings.HasPrefix(targetHost, "http://unix://") {
// // Unix socket handling
// socketPath := strings.TrimPrefix(targetHost, "http://unix://")
// transport = &http.Transport{
// DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
// return net.Dial("unix", socketPath)
// },
// }
// // Use a dummy URL for the director
// targetURL, _ = url.Parse("http://unix-socket")
// } else {
// Regular HTTP/HTTPS handling
targetURL, err = url.Parse(targetHost)
if err != nil {
return nil, err
}
targetURL, err = url.Parse(targetHost)
if err != nil {
return nil, err
}
customTransport := &http.Transport{}
dialer := &net.Dialer{
Timeout: 5 * time.Second,
KeepAlive: 5 * time.Second,
}
if constellation.ConstellationConnected() {
currIp, err := constellation.GetCurrentDeviceIP()
if err != nil {
utils.Error("Custom tunnel transport", err)
} else {
customTransport.DialContext = (&net.Dialer{
Timeout: 5 * time.Second,
KeepAlive: 5 * time.Second,
Resolver: &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
return net.Dial(network, currIp + ":53")
},
},
}).DialContext
if utils.GetMainConfig().ConstellationConfig.Enabled {
dialer.Resolver = &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
currConIp, err := constellation.GetCurrentDeviceIP()
if err == nil {
// Try Constellation DNS first
conn, err := net.Dial(network, currConIp+":53")
if err == nil {
return conn, nil
}
}
// Fallback to system DNS
return net.Dial(network, address)
},
}
}
}
if route.UseH2C {
transport = &http2.Transport{
AllowHTTP: true,
DialTLSContext: func(ctx context.Context, network, addr string, _ *tls.Config) (net.Conn, error) {
return dialer.DialContext(ctx, network, addr)
},
}
} else {
customTransport := &http.Transport{
DialContext: dialer.DialContext,
}
if AcceptInsecureHTTPSTarget {
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
transport = customTransport
// }
}
proxy := &httputil.ReverseProxy{
Transport: transport,
Transport: transport,
FlushInterval: -1,
}
proxy.Director = func(req *http.Request) {
originalScheme := "http"
if utils.IsHTTPS {
+1
View File
@@ -261,6 +261,7 @@ type ProxyRouteConfig struct {
TunneledHost string `yaml:"tunneled_host,omitempty"`
ExtraHeaders map[string]string `yaml:"extra_headers,omitempty"`
SkipURLClean bool `yaml:"skip_url_clean"`
UseH2C bool `yaml:"use_h2c"`
Const_IsTunneled bool `yaml:"-", json:"-"`
}
+57 -4
View File
@@ -439,13 +439,13 @@ func SaveConfigTofile(config Config) {
Log("Config file saved.")
}
func RestartServer() {
func RestartServer(code int) {
Log("Restarting server...")
WaitForAllJobs()
if StopAllRCloneProcess != nil {
StopAllRCloneProcess(false)
}
os.Exit(0)
os.Exit(code)
}
func SoftRestartServer() {
@@ -897,10 +897,63 @@ func GetClientIP(req *http.Request) string {
return remoteAddr
}
var (
trustedProxyURLCache = map[string][]string{}
trustedProxyURLCacheLock sync.Mutex
)
func fetchIPRangesFromURL(url string) ([]string, error) {
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var ranges []string
for _, line := range strings.Split(string(body), "\n") {
line = strings.TrimSpace(line)
if line != "" {
ranges = append(ranges, line)
}
}
return ranges, nil
}
func getTrustedProxyRanges(url string) []string {
trustedProxyURLCacheLock.Lock()
defer trustedProxyURLCacheLock.Unlock()
if cached, ok := trustedProxyURLCache[url]; ok {
return cached
}
ranges, err := fetchIPRangesFromURL(url)
if err != nil {
Error("IsTrustedProxy: failed to fetch URL "+url, err)
return nil
}
trustedProxyURLCache[url] = ranges
return ranges
}
func IsTrustedProxy(ip string) bool {
for _, trustedProxy := range GetMainConfig().HTTPConfig.TrustedProxies {
if isInRange, _ := IPInRange(ip, trustedProxy); isInRange {
return true
if strings.HasPrefix(trustedProxy, "http://") || strings.HasPrefix(trustedProxy, "https://") {
for _, r := range getTrustedProxyRanges(trustedProxy) {
if isInRange, _ := IPInRange(ip, r); isInRange {
return true
}
}
} else {
if isInRange, _ := IPInRange(ip, trustedProxy); isInRange {
return true
}
}
}
return false