Compare commits

..

8 Commits

Author SHA1 Message Date
Shtorm
b19e232df2 Merge pull request #105 from Arsolitt/feat/provider-custom-headers
feat(provider): add custom HTTP headers support for remote providers
2026-07-03 09:52:06 +03:00
Arsolitt
1e826a201d feat(provider): add custom HTTP headers support for remote providers
Some subscription panels (e.g. Remnawave with incy/happ integration)
require device-identification headers to return the real server list
instead of a dummy config. Add a 'headers' field to the remote provider
options so users can attach arbitrary HTTP headers to subscription
fetch requests.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-07-01 21:58:01 +03:00
Shtorm
a27453e4f7 Merge pull request #97 from v14d4n/feat/masque-configurable-sni
Make masque TLS SNI configurable via server_name
2026-06-26 14:39:20 +03:00
Shtorm
2a77348470 Update README.md 2026-06-26 04:04:01 +03:00
Shtorm
1ae82a483a Merge branch 'extended' of https://github.com/shtorm-7/sing-box-extended into extended 2026-06-26 03:51:16 +03:00
Shtorm
31acf60eca Fix examples
Signed-off-by: Shtorm <108103062+shtorm-7@users.noreply.github.com>
2026-06-25 15:54:57 +03:00
v14d4n
7bfad732e2 feat(masque): make TLS SNI configurable via server_name
MASQUE outbound previously hardcoded the TLS SNI to consumer-masque.cloudflareclient.com. Add a server_name field to the MASQUE outbound TLS options. When empty it falls back to the existing default (cloudflare.ConnectSNI), so existing configs are unaffected.
2026-06-22 14:12:40 +04:00
Shtorm
2cbc7691f0 Update Telegram badge from channel to chat
Signed-off-by: Shtorm <108103062+shtorm-7@users.noreply.github.com>
2026-06-15 10:43:03 +03:00
8 changed files with 40 additions and 3 deletions

View File

@@ -3,7 +3,7 @@
[![license](https://img.shields.io/badge/license-GPLv3-blue.svg)](LICENSE)
[![go](https://img.shields.io/badge/go-1.26-00ADD8.svg)](go.mod)
[![codeberg](https://img.shields.io/badge/mirror-codeberg-2185D0.svg)](https://codeberg.org/shtorm-7/sing-box-extended)
[![telegram](https://img.shields.io/badge/telegram-channel-26A5E4.svg)](https://t.me/sing_box_extended)
[![telegram](https://img.shields.io/badge/telegram-chat-26A5E4.svg)](https://t.me/sing_box_extended)
Sing-box with extended features.
@@ -17,6 +17,7 @@ Sing-box with extended features.
- **OpenVPN** — OpenVPN client with tls-auth, tls-crypt and tls-crypt-v2 support
- **TrustTunnel** — AdGuard's obfuscated VPN protocol, indistinguishable from HTTPS traffic
- **Sudoku** — Traffic obfuscation protocol based on 4×4 Sudoku puzzles with low-entropy fingerprints
- **Snell** — Lightweight encrypted proxy (v1v5) with TLS / HTTP obfuscation
- **SSH** — SSH client and server with certificate authentication and upstream fallback
- **VPN** — Routed tunnel over any TCP sing-box protocol
- **Bond** — Link aggregation for increasing throughput

View File

@@ -66,7 +66,7 @@
"action": "hijack-dns"
}
],
"final": "connection-limiter"
"final": "traffic-limiter"
},
"services": [
{

View File

@@ -42,6 +42,7 @@
"congestion_controller": "bbr",
"cwnd": 0,
"tls": { // TLS fields for HTTP2
"server_name": "", // SNI; empty = default "consumer-masque.cloudflareclient.com"
"insecure": false,
"cipher_suites": [],
"curve_preferences": [],

View File

@@ -47,6 +47,30 @@
// shareable links, or plain link list.
"url": "https://example.com/subscription.txt",
"user_agent": "sing-box",
// Custom HTTP headers sent with each subscription request.
// Some subscription panels (e.g. Remnawave with incy/happ
// integration) require device-identification headers to
// return the real server list instead of a dummy config.
//
// !!! DO NOT COPY THE VALUE BELOW !!!
// x-hwid must be generated per-device. A random UUID will
// be rejected by the panel. Generate yours on Linux:
//
// python3 -c "import hashlib,socket,getpass;m=open('/etc/machine-id').read().strip();d=hashlib.sha256(f'{m}|{socket.gethostname()}|Linux|amd64|{getpass.getuser()}'.encode()).hexdigest();h=hashlib.sha256(('incy_hwid_'+d).encode()).hexdigest();print(f'{h[:8]}-{h[8:12]}-{h[12:16]}-{h[16:20]}-{h[20:32]}'.upper())"
//
// Algorithm: SHA256("incy_hwid_" + SHA256(deviceId))
// Linux deviceId: "machineId|hostname|Linux|amd64|user"
// Android deviceId: "androidId|manufacturer|model|brand|device|product|board|hardware"
// Ref: https://github.com/INCY-DEV/incy-docs/blob/main/ru/dev-docs/hwid.md
"headers": {
"x-hwid": ["REPLACE_WITH_GENERATED_HWID"],
// Platform: linux, android, ios, windows, macos
"x-device-os": ["linux"],
// OS version — on Linux: uname -r
"x-ver-os": ["6.12.0-arch1-1"],
// Device model — on Linux: uname -m
"x-device-model": ["x86_64"]
},
// Fetch the subscription through this outbound instead of the
// default route (useful when the subscription host is blocked).
"download_detour": "direct",

View File

@@ -24,6 +24,7 @@ type MASQUEOutboundOptions struct {
}
type MASQUEOutboundTLSOptions struct {
ServerName string `json:"server_name,omitempty"`
Insecure bool `json:"insecure,omitempty"`
CipherSuites badoption.Listable[string] `json:"cipher_suites,omitempty"`
CurvePreferences badoption.Listable[CurvePreference] `json:"curve_preferences,omitempty"`

View File

@@ -55,6 +55,7 @@ type ProviderLocalOptions struct {
type ProviderRemoteOptions struct {
URL string `json:"url"`
UserAgent string `json:"user_agent,omitempty"`
Headers badoption.HTTPHeader `json:"headers,omitempty"`
DownloadDetour string `json:"download_detour,omitempty"`
UpdateInterval badoption.Duration `json:"update_interval,omitempty"`

View File

@@ -102,7 +102,11 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
logger.ErrorContext(ctx, E.New("failed to generate cert: ", err))
return
}
tlsConfig, err := tls.NewMASQUEClient(ctx, logger, "consumer-masque.cloudflareclient.com", cert, privKey, peerPubKey, common.PtrValueOrDefault(options.TLS))
serverName := cloudflare.ConnectSNI
if options.TLS != nil && options.TLS.ServerName != "" {
serverName = options.TLS.ServerName
}
tlsConfig, err := tls.NewMASQUEClient(ctx, logger, serverName, cert, privKey, peerPubKey, common.PtrValueOrDefault(options.TLS))
if err != nil {
logger.ErrorContext(ctx, E.New("failed to prepare TLS config: ", err))
return

View File

@@ -59,6 +59,7 @@ type ProviderRemote struct {
updateInterval time.Duration
exclude *regexp.Regexp
include *regexp.Regexp
headers http.Header
}
func NewProviderRemote(ctx context.Context, router adapter.Router, logFactory log.Factory, tag string, options option.ProviderRemoteOptions) (adapter.Provider, error) {
@@ -94,6 +95,7 @@ func NewProviderRemote(ctx context.Context, router adapter.Router, logFactory lo
url: options.URL,
userAgent: userAgent,
downloadDetour: options.DownloadDetour,
headers: options.Headers.Build(),
updateInterval: updateInterval,
exclude: (*regexp.Regexp)(options.Exclude),
include: (*regexp.Regexp)(options.Include),
@@ -191,6 +193,9 @@ func (s *ProviderRemote) fetch(ctx context.Context) error {
req.Header.Set("If-None-Match", s.lastEtag)
}
req.Header.Set("User-Agent", s.userAgent)
for name, values := range s.headers {
req.Header[name] = values
}
resp, err := client.Do(req.WithContext(ctx))
if err != nil {
return err