mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-07-13 07:09:11 +00:00
Root config: Add env config (#6400)
https://github.com/XTLS/Xray-core/pull/6400#issuecomment-4880198100 https://github.com/XTLS/Xray-core/pull/6400#issuecomment-4916892425 https://github.com/XTLS/Xray-core/pull/6400#issuecomment-4929136670
This commit is contained in:
@@ -37,6 +37,7 @@ RUN echo '{}' >/tmp/usr/local/etc/xray/08_fakedns.json
|
||||
RUN echo '{}' >/tmp/usr/local/etc/xray/09_metrics.json
|
||||
RUN echo '{}' >/tmp/usr/local/etc/xray/10_observatory.json
|
||||
RUN echo '{}' >/tmp/usr/local/etc/xray/11_geodata.json
|
||||
RUN echo '{}' >/tmp/usr/local/etc/xray/12_env.json
|
||||
RUN echo '{}' >/tmp/usr/local/etc/xray/99_version.json
|
||||
|
||||
# Create log files
|
||||
|
||||
@@ -37,6 +37,7 @@ RUN echo '{}' >/tmp/usr/local/etc/xray/08_fakedns.json
|
||||
RUN echo '{}' >/tmp/usr/local/etc/xray/09_metrics.json
|
||||
RUN echo '{}' >/tmp/usr/local/etc/xray/10_observatory.json
|
||||
RUN echo '{}' >/tmp/usr/local/etc/xray/11_geodata.json
|
||||
RUN echo '{}' >/tmp/usr/local/etc/xray/12_env.json
|
||||
RUN echo '{}' >/tmp/usr/local/etc/xray/99_version.json
|
||||
|
||||
# Create log files
|
||||
|
||||
+1
-1
@@ -122,7 +122,7 @@ func NewReader(reader io.Reader) Reader {
|
||||
}
|
||||
|
||||
_, isFile := reader.(*os.File)
|
||||
if !isFile && useReadv {
|
||||
if !isFile && useReadV() {
|
||||
if sc, ok := reader.(syscall.Conn); ok {
|
||||
rawConn, err := sc.SyscallConn()
|
||||
if err != nil {
|
||||
|
||||
@@ -5,6 +5,7 @@ package buf
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
|
||||
"github.com/xtls/xray-core/common/platform"
|
||||
@@ -143,13 +144,24 @@ func (r *ReadVReader) ReadMultiBuffer() (MultiBuffer, error) {
|
||||
return mb, nil
|
||||
}
|
||||
|
||||
var useReadv bool
|
||||
var useReadv atomic.Bool
|
||||
|
||||
func init() {
|
||||
func useReadV() bool {
|
||||
return useReadv.Load()
|
||||
}
|
||||
|
||||
func reloadEnvSettings() error {
|
||||
const defaultFlagValue = "NOT_DEFINED_AT_ALL"
|
||||
value := platform.NewEnvFlag(platform.UseReadV).GetValue(func() string { return defaultFlagValue })
|
||||
enabled := false
|
||||
switch value {
|
||||
case defaultFlagValue, "auto", "enable":
|
||||
useReadv = true
|
||||
enabled = true
|
||||
}
|
||||
useReadv.Store(enabled)
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
platform.RegisterEnvReload(reloadEnvSettings)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,9 @@ import (
|
||||
"github.com/xtls/xray-core/features/stats"
|
||||
)
|
||||
|
||||
const useReadv = false
|
||||
func useReadV() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func NewReadVReader(reader io.Reader, rawConn syscall.RawConn, counter stats.Counter) Reader {
|
||||
panic("not implemented")
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var envReloadRegistry = struct {
|
||||
sync.RWMutex
|
||||
handlers []func() error
|
||||
}{}
|
||||
|
||||
// RegisterEnvReload registers an environment reload handler and runs it once
|
||||
// immediately so package defaults keep the same behavior as init-time reads.
|
||||
func RegisterEnvReload(handler func() error) {
|
||||
if handler == nil {
|
||||
return
|
||||
}
|
||||
envReloadRegistry.Lock()
|
||||
envReloadRegistry.handlers = append(envReloadRegistry.handlers, handler)
|
||||
envReloadRegistry.Unlock()
|
||||
if err := handler(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReloadEnvSettings refreshes all registered environment-backed package state.
|
||||
func ReloadEnvSettings() error {
|
||||
envReloadRegistry.RLock()
|
||||
handlers := append([]func() error{}, envReloadRegistry.handlers...)
|
||||
envReloadRegistry.RUnlock()
|
||||
|
||||
var errs []error
|
||||
for _, handler := range handlers {
|
||||
if err := handler(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
+33
-19
@@ -8,7 +8,7 @@ import (
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
@@ -27,25 +27,39 @@ var AddrParser = protocol.NewAddressParser(
|
||||
)
|
||||
|
||||
var (
|
||||
Show bool
|
||||
BaseKey []byte
|
||||
Show atomic.Bool
|
||||
baseKey atomic.Value
|
||||
)
|
||||
|
||||
func init() {
|
||||
if strings.ToLower(platform.NewEnvFlag(platform.XUDPLog).GetValue(func() string { return "" })) == "true" {
|
||||
Show = true
|
||||
func reloadEnvSettings() error {
|
||||
Show.Store(strings.ToLower(platform.NewEnvFlag(platform.XUDPLog).GetValue(func() string { return "" })) == "true")
|
||||
raw := platform.NewEnvFlag(platform.XUDPBaseKey).GetValue(func() string { return "" })
|
||||
if raw == "" {
|
||||
ensureBaseKey()
|
||||
return nil
|
||||
}
|
||||
BaseKey = make([]byte, 32)
|
||||
rand.Read(BaseKey)
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) // this is not nice, but need to give some time for Android to setup ENV
|
||||
if raw := platform.NewEnvFlag(platform.XUDPBaseKey).GetValue(func() string { return "" }); raw != "" {
|
||||
if BaseKey, _ = base64.RawURLEncoding.DecodeString(raw); len(BaseKey) == 32 {
|
||||
return
|
||||
}
|
||||
panic(platform.XUDPBaseKey + ": invalid value (BaseKey must be 32 bytes): " + raw + " len " + strconv.Itoa(len(BaseKey)))
|
||||
}
|
||||
}()
|
||||
key, _ := base64.RawURLEncoding.DecodeString(raw)
|
||||
if len(key) != 32 {
|
||||
return errors.New(platform.XUDPBaseKey + ": invalid value (BaseKey must be 32 bytes): " + raw + " len " + strconv.Itoa(len(key)))
|
||||
}
|
||||
baseKey.Store(append([]byte(nil), key...))
|
||||
return nil
|
||||
}
|
||||
|
||||
func ensureBaseKey() []byte {
|
||||
if key := baseKey.Load(); key != nil {
|
||||
return key.([]byte)
|
||||
}
|
||||
key := make([]byte, 32)
|
||||
if _, err := rand.Read(key); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
baseKey.Store(key)
|
||||
return key
|
||||
}
|
||||
|
||||
func init() {
|
||||
platform.RegisterEnvReload(reloadEnvSettings)
|
||||
}
|
||||
|
||||
func GetGlobalID(ctx context.Context) (globalID [8]byte) {
|
||||
@@ -54,10 +68,10 @@ func GetGlobalID(ctx context.Context) (globalID [8]byte) {
|
||||
}
|
||||
if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Source.Network == net.Network_UDP &&
|
||||
(inbound.Name == "dokodemo-door" || inbound.Name == "socks" || inbound.Name == "shadowsocks" || inbound.Name == "tun") {
|
||||
h := blake3.New(8, BaseKey)
|
||||
h := blake3.New(8, ensureBaseKey())
|
||||
h.Write([]byte(inbound.Source.String()))
|
||||
copy(globalID[:], h.Sum(nil))
|
||||
if Show {
|
||||
if Show.Load() {
|
||||
errors.LogInfo(ctx, fmt.Sprintf("XUDP inbound.Source.String(): %v\tglobalID: %v\n", inbound.Source.String(), globalID))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,6 +187,9 @@ func NewWithContext(ctx context.Context, config *Config) (*Instance, error) {
|
||||
}
|
||||
|
||||
func initInstanceWithConfig(config *Config, server *Instance) (bool, error) {
|
||||
if err := platform.ReloadEnvSettings(); err != nil {
|
||||
return true, errors.New("failed to reload environment settings").Base(err)
|
||||
}
|
||||
server.ctx = context.WithValue(server.ctx, "cone",
|
||||
platform.NewEnvFlag(platform.UseCone).GetValue(func() string { return "" }) != "true")
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package policy
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common/platform"
|
||||
@@ -82,32 +83,41 @@ func ManagerType() interface{} {
|
||||
return (*Manager)(nil)
|
||||
}
|
||||
|
||||
var defaultBufferSize int32
|
||||
var defaultBufferSize atomic.Int32
|
||||
|
||||
func init() {
|
||||
func reloadEnvSettings() error {
|
||||
defaultBufferSize.Store(readDefaultBufferSize())
|
||||
return nil
|
||||
}
|
||||
|
||||
func readDefaultBufferSize() int32 {
|
||||
const defaultValue = -17
|
||||
size := platform.NewEnvFlag(platform.BufferSize).GetValueAsInt(defaultValue)
|
||||
|
||||
switch size {
|
||||
case 0:
|
||||
defaultBufferSize = -1 // For pipe to use unlimited size
|
||||
return -1 // For pipe to use unlimited size
|
||||
case defaultValue: // Env flag not defined. Use default values per CPU-arch.
|
||||
switch runtime.GOARCH {
|
||||
case "arm", "mips", "mipsle":
|
||||
defaultBufferSize = 0
|
||||
return 0
|
||||
case "arm64", "mips64", "mips64le":
|
||||
defaultBufferSize = 4 * 1024 // 4k cache for low-end devices
|
||||
return 4 * 1024 // 4k cache for low-end devices
|
||||
default:
|
||||
defaultBufferSize = 512 * 1024
|
||||
return 512 * 1024
|
||||
}
|
||||
default:
|
||||
defaultBufferSize = int32(size) * 1024 * 1024
|
||||
return int32(size) * 1024 * 1024
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
platform.RegisterEnvReload(reloadEnvSettings)
|
||||
}
|
||||
|
||||
func defaultBufferPolicy() Buffer {
|
||||
return Buffer{
|
||||
PerConnection: defaultBufferSize,
|
||||
PerConnection: defaultBufferSize.Load(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRootEnvAppliesArbitraryValues(t *testing.T) {
|
||||
const (
|
||||
valueKey = "XRAY_TEST_CONFIG_ENV"
|
||||
emptyKey = "XRAY_TEST_CONFIG_EMPTY"
|
||||
)
|
||||
t.Setenv(valueKey, "before")
|
||||
t.Setenv(emptyKey, "before")
|
||||
|
||||
config := new(Config)
|
||||
if err := json.Unmarshal([]byte(`{
|
||||
"env": {
|
||||
"XRAY_TEST_CONFIG_ENV": "configured",
|
||||
"XRAY_TEST_CONFIG_EMPTY": ""
|
||||
}
|
||||
}`), config); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := config.Build(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if got := os.Getenv(valueKey); got != "configured" {
|
||||
t.Fatalf("env %q = %q, want %q", valueKey, got, "configured")
|
||||
}
|
||||
if got := os.Getenv(emptyKey); got != "" {
|
||||
t.Fatalf("env %q = %q, want empty", emptyKey, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvConfigOverride(t *testing.T) {
|
||||
base := EnvConfig{
|
||||
"ONE": "one",
|
||||
"TWO": "old",
|
||||
}
|
||||
override := EnvConfig{
|
||||
"TWO": "new",
|
||||
"THREE": "three",
|
||||
}
|
||||
base.Override(override)
|
||||
|
||||
want := map[string]string{
|
||||
"ONE": "one",
|
||||
"TWO": "new",
|
||||
"THREE": "three",
|
||||
}
|
||||
for key, value := range want {
|
||||
if got := base[key]; got != value {
|
||||
t.Fatalf("env %q = %q, want %q", key, got, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package conf
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -379,11 +380,20 @@ func (c *StatsConfig) Build() (*stats.Config, error) {
|
||||
return &stats.Config{}, nil
|
||||
}
|
||||
|
||||
type EnvConfig map[string]string
|
||||
|
||||
func (c EnvConfig) Override(o EnvConfig) {
|
||||
for key, value := range o {
|
||||
c[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
// Deprecated: Global transport config is no longer used
|
||||
// left for returning error
|
||||
Transport map[string]json.RawMessage `json:"transport"`
|
||||
|
||||
Env EnvConfig `json:"env"`
|
||||
LogConfig *LogConfig `json:"log"`
|
||||
RouterConfig *RouterConfig `json:"routing"`
|
||||
DNSConfig *DNSConfig `json:"dns"`
|
||||
@@ -439,6 +449,12 @@ func (c *Config) Override(o *Config, fn string) {
|
||||
if o.Transport != nil {
|
||||
c.Transport = o.Transport
|
||||
}
|
||||
if o.Env != nil {
|
||||
if c.Env == nil {
|
||||
c.Env = EnvConfig{}
|
||||
}
|
||||
c.Env.Override(o.Env)
|
||||
}
|
||||
if o.Policy != nil {
|
||||
c.Policy = o.Policy
|
||||
}
|
||||
@@ -514,6 +530,12 @@ func (c *Config) Override(o *Config, fn string) {
|
||||
|
||||
// Build implements Buildable.
|
||||
func (c *Config) Build() (*core.Config, error) {
|
||||
for key, value := range c.Env {
|
||||
if err := os.Setenv(key, value); err != nil {
|
||||
return nil, errors.New("failed to apply environment configuration").Base(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := PostProcessConfigureFile(c); err != nil {
|
||||
return nil, errors.New("failed to post-process configuration file").Base(err)
|
||||
}
|
||||
|
||||
@@ -36,6 +36,10 @@ The -confdir=dir flag sets a dir with multiple json config
|
||||
The -format=json flag sets the format of config files.
|
||||
Default "auto".
|
||||
|
||||
The config root env object sets process environment variables after all config
|
||||
files are parsed. Variables needed to locate or parse config files must be set
|
||||
in the process environment before Xray starts.
|
||||
|
||||
The -test flag tells Xray to test config files only,
|
||||
without launching the server.
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pires/go-proxyproto"
|
||||
@@ -31,12 +32,24 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
useSplice bool
|
||||
useSplice atomic.Bool
|
||||
allNetworks [8]bool
|
||||
defaultBlockPrivateRule *FinalRule
|
||||
defaultBlockAllRule *FinalRule
|
||||
)
|
||||
|
||||
func reloadEnvSettings() error {
|
||||
const defaultFlagValue = "NOT_DEFINED_AT_ALL"
|
||||
value := platform.NewEnvFlag(platform.UseFreedomSplice).GetValue(func() string { return defaultFlagValue })
|
||||
enabled := false
|
||||
switch value {
|
||||
case defaultFlagValue, "auto", "enable":
|
||||
enabled = true
|
||||
}
|
||||
useSplice.Store(enabled)
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||
h := new(Handler)
|
||||
@@ -48,12 +61,7 @@ func init() {
|
||||
return h, nil
|
||||
}))
|
||||
|
||||
const defaultFlagValue = "NOT_DEFINED_AT_ALL"
|
||||
value := platform.NewEnvFlag(platform.UseFreedomSplice).GetValue(func() string { return defaultFlagValue })
|
||||
switch value {
|
||||
case defaultFlagValue, "auto", "enable":
|
||||
useSplice = true
|
||||
}
|
||||
platform.RegisterEnvReload(reloadEnvSettings)
|
||||
|
||||
for i := range allNetworks {
|
||||
allNetworks[i] = true
|
||||
@@ -422,7 +430,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
|
||||
responseDone := func() error {
|
||||
defer timer.SetTimeout(plcy.Timeouts.UplinkOnly)
|
||||
if destination.Network == net.Network_TCP && useSplice && proxy.IsRAWTransportWithoutSecurity(conn) { // it would be tls conn in special use case of MITM, we need to let link handle traffic
|
||||
if destination.Network == net.Network_TCP && useSplice.Load() && proxy.IsRAWTransportWithoutSecurity(conn) { // it would be tls conn in special use case of MITM, we need to let link handle traffic
|
||||
var writeConn net.Conn
|
||||
var inTimer *signal.ActivityTimer
|
||||
if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Conn != nil {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"hash/crc64"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
@@ -218,10 +219,17 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
return nil
|
||||
}
|
||||
|
||||
var enablePadding = false
|
||||
var enablePadding atomic.Bool
|
||||
|
||||
func shouldEnablePadding(s protocol.SecurityType) bool {
|
||||
return enablePadding || s == protocol.SecurityType_AES128_GCM || s == protocol.SecurityType_CHACHA20_POLY1305 || s == protocol.SecurityType_AUTO
|
||||
return enablePadding.Load() || s == protocol.SecurityType_AES128_GCM || s == protocol.SecurityType_CHACHA20_POLY1305 || s == protocol.SecurityType_AUTO
|
||||
}
|
||||
|
||||
func reloadEnvSettings() error {
|
||||
const defaultFlagValue = "NOT_DEFINED_AT_ALL"
|
||||
paddingValue := platform.NewEnvFlag(platform.UseVmessPadding).GetValue(func() string { return defaultFlagValue })
|
||||
enablePadding.Store(paddingValue != defaultFlagValue)
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -229,10 +237,5 @@ func init() {
|
||||
return New(ctx, config.(*Config))
|
||||
}))
|
||||
|
||||
const defaultFlagValue = "NOT_DEFINED_AT_ALL"
|
||||
|
||||
paddingValue := platform.NewEnvFlag(platform.UseVmessPadding).GetValue(func() string { return defaultFlagValue })
|
||||
if paddingValue != defaultFlagValue {
|
||||
enablePadding = true
|
||||
}
|
||||
platform.RegisterEnvReload(reloadEnvSettings)
|
||||
}
|
||||
|
||||
@@ -27,9 +27,10 @@ type task struct {
|
||||
}
|
||||
|
||||
var (
|
||||
conns chan *websocket.Conn
|
||||
server *http.Server
|
||||
mu sync.Mutex
|
||||
conns chan *websocket.Conn
|
||||
server *http.Server
|
||||
currentAddr string
|
||||
mu sync.Mutex
|
||||
)
|
||||
|
||||
var upgrader = &websocket.Upgrader{
|
||||
@@ -47,8 +48,13 @@ func Reload() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
if addr == currentAddr && (addr == "" || server != nil) {
|
||||
return
|
||||
}
|
||||
|
||||
if server != nil {
|
||||
server.Close()
|
||||
server = nil
|
||||
}
|
||||
if HasBrowserDialer() {
|
||||
for len(conns) > 0 {
|
||||
@@ -60,6 +66,7 @@ func Reload() {
|
||||
}
|
||||
conns = nil
|
||||
}
|
||||
currentAddr = addr
|
||||
if addr != "" {
|
||||
token := uuid.New()
|
||||
csrfToken := token.String()
|
||||
@@ -220,5 +227,8 @@ func CheckOK(conn *websocket.Conn) error {
|
||||
}
|
||||
|
||||
func init() {
|
||||
Reload()
|
||||
platform.RegisterEnvReload(func() error {
|
||||
Reload()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user