Little-known XRay features that are impossible to keep silent about

In this post I will briefly share about various interesting features of XRay (client and server for VMess, VLESS, Trojan and other protocols, including XTLS-Reality and XTLS-Vision), which, it seems, few people know about, but which can be very useful.

Table of contents:

  1. Query fragmentation

  2. QUIC transport

  3. Monitoring and automatic selection of outbound

  4. Statistics collection

  5. Browser dialer

Fragment

In the wake of recent events (Roskomnadzor slowing down YouTube), many have discovered tools like GoodbyeDPI And similar. They work by using various tricks when transmitting data via TCP protocol, due to which the censoring equipment (the so-called TSPU) when processing TLS-handshake is confused and does not understand which site you are trying to get to (because it cannot read the “SNI” parameter when establishing a TLS-connection), and as a result blocking/slowing down does not work. XRay can do something like this, it is called “Fragment” there.

The simplest XRay config for this would look something like this:

local proxy with fragment
{
  "inbounds": [
    {
      "listen": "127.0.0.1",
      "port": 1080,
      "protocol": "socks",
      "tag": "socks"
    }
  ],
  "outbounds": [
    {
      "tag": "fragment",
      "protocol": "freedom",
      "settings": {
        "fragment": {
          "packets": "tlshello",
          "length": "100-200",
          "interval": "10-20"
        }
      },
      "streamSettings": {
        "sockopt": {
          "tcpNoDelay": true
        }
      }
    }
  ]
}

By running XRay with this config and setting the browser to use a SOCKS proxy on local (127.0.0.1) port 1080, XRay will fragment the ClientHello header of the TLS protocol. Available parameters:

"packets":can be “1-3” for TCP-level fragmentation (first 1-3 data transmissions from client to server) or “tlshello” for TLS handshake fragmentation;

"length": what size pieces to split the sent data into;

"interval": pause between sending fragments in milliseconds;

The specific required parameter values ​​may differ depending on the type (and even software version) of the DPI equipment used by the provider, so you may have to experiment with them to get the desired effect.

Fragment can also be combined with the use of proxy protocols such as VLESS and others, an example of a config is on github.

QUIC transport

Another thing that has been discussed recently in the context of YouTube slowdown is the QUIC protocol. This is a Google development that uses UDP instead of TCP when accessing web servers, aimed at reducing connection setup time and improving access speed due to more efficient data transfer. In the case of YouTube slowdown, for some users, enabling QUIC in the browser helped solve the problem (and for others, on the contrary, disabling it). There may be several reasons for this. Firstly, there is reason to believe that certain TSPU equipment… cannot normally parse QUIC protocol headers (and because of this, Roskomnadzor even cut such connections at one time). Secondly, in the Chrome browser, developers intentionally added data fragmentation when establishing a connection – they were thus trying to force web server developers to fully comply with the protocol standard, and this fragmentation gave a very useful side effect for bypassing censorship (see the previous part of the article). Thirdly, QUIC works very effectively with packet loss, and RKN equipment slows down YouTube in exactly this way.

And XRay can use QUIC as a transport for its protocols (VLESS, VMess, Trojan and others). You may ask why use QUIC transport when you can use VLESS without any transport (over regular TLS), I will answer – it is always useful to have a backup alternative, besides, QUIC in XRay can be used in different ways.

Option one – in its pure form. With QUIC, unfortunately, you won't be able to do something like XTLS-Reality, when you hide behind someone else's domain. In theory, it's possible, but so far no one has implemented it. Therefore, you will need your own domain and a TLS certificate for it (even a free domain from dynu.com and a certificate from LetsEncrypt will do). XRay config for example:

VLESS with QUIC transport
{
  "inbounds": [
    {
      "port": 443,
      "protocol": "vless",
      "tag": "quic",
      "settings": {
        "clients": [
          {
            "id": "4c3fe585-bb09-89df-b284-e2dada14150f",
            "email": "user1"
          }
        ],
        "decryption": "none"
      },
      "streamSettings": {
        "network": "quic",
        "quicSettings": {},
        "security": "tls",
        "tlsSettings": {
          "certificates": [
          {
            "certificateFile": "/etc/letsencrypt/live/yourdomain/fullchain.pem",
            "keyFile": "/etc/letsencrypt/live/yourdomain/privkey.pem"
          }]
        }
      },
      "sniffing": {
        "enabled": true,
        "destOverride": [
          "quic",
          "http",
          "tls"
        ]
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",
      "tag": "direct"
    },
    {
      "protocol": "blackhole",
      "tag": "block"
    }
  ]
}

Then in the client you configure a VLESS connection with the required UUID, enter the address (domain) of your server, select QUIC as the transport – and everything works. Such a connection will look like a real QUIC on the 443 UDP port, but, unfortunately, there is no way to configure a fallback (so that when entering UDP/443 a fake website opens) in XRay.

The second option is to use obfuscated QUIC. The domain and certificate are no longer needed for this (nobody will see them anyway). From the outside, the data transfer will look like “something incomprehensible over UDP”, approximately as in the case of AmneziaWG.

VLESS + obfuscated QUIC (something incomprehensible over UDP)
{
  "log": {
    "loglevel": "debug"
  },
  "inbounds": [
    {
      "port": 8443,
      "protocol": "vless",
      "tag": "quic",
      "settings": {
        "clients": [
          {
            "id": "4c3fe585-bb09-89df-b284-e2dada14150f",
            "email": "user1"
          }
        ],
        "decryption": "none"
      },
      "streamSettings": {
        "network": "quic",
        "quicSettings": {
           "security": "aes-128-gcm",
           "key": "hellohabr"
        }
      },
      "sniffing": {
        "enabled": true,
        "destOverride": [
          "quic",
          "http",
          "tls"
        ]
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",
      "tag": "direct"
    },
    {
      "protocol": "blackhole",
      "tag": "block"
    }
  ]
}

In this case, the cipher is used aes-128-gcm (another possible option is – chacha20-poly1305) with the key “hellohabr”. Clients are configured similarly to the previous case, only you need to disable TLS, and instead select the cipher and key.

And another option, close to the second one, is to disguise QUIC as DTLS (WebRTC, calls in messengers), SRTP (FaceTime) or uTP (BitTorrent). To do this, you need to add the header parameter:

...
      "streamSettings": {
        "network": "quic",
        "quicSettings": {
           "security": "aes-128-gcm",
           "key": "hellohabr",
           "header": {
             "type": "dtls"
           }
        }
...

type” can be “srtp”, “utp”, “dtls”, “wechat-video” and “wireguard”. Don't forget to select the same on the client.

Remember, in my article “Reliable bypass of blockings in 2024: protocols, clients and server setup from simple to complex” (blocked in the territory of the Russian Federation, but accessible through foreign VPN/proxy) I recommended mKCP as a backup option, working via UDP? Unfortunately, starting with some version of Xray it is broken and slows down in certain scenarios – eats up memory, eats up the processor and slows down, up to the point of complete impossibility of work. QUIC transport in XRay works much more stably, and has all the same obfuscation capabilities.

There are two disadvantages. First, as I already said, when you disguise yourself as “pure” QUIC, you cannot add a disguise site. Second – all this beauty works only with clients based on XRay-core, and those based on Sing-box are out of luck (more precisely, sing-box does not support obfuscated QUIC, pure in theory should work, but did not work).

If you need a proxy over QUIC with a disguise as a site or as “I don't understand what” and working with Sing-box, use Hysteria2.

Selectors and observatory – monitoring and automatic selection of outbound

Some people use XRay to build proxy chains. For example, if you are in Russia, you first connect to a proxy server located in Russia, and then from there to somewhere abroad. XRay allows you to control the availability of upstream proxy servers (if you have several) and balance requests between them. The features are called selectors and observatories. Documentation Here.

I'll tell you about the config briefly. At the top level of your config, add the “observatory” object:

  "observatory": {
    "subjectSelector": [ "outbound1", "outbound2" ],
    "probeURL": "http://www.gstatic.com/generate_204",
    "probeInterval": "300s",
    "enableConcurrency": true
  },

In the “subjectSelector” list, list the names of outbounds that need to be checked, in “probeURL” set the address for the test, in “probeInterval” the frequency.

And then in the “routing” object you describe the “balancers” object, for example like this:

  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "balancers": [
      {
        "tag": "mybalancer",
        "selector": [ "outbound1", "outbound2" ],
        "strategy": {
          "type": "leastPing"
        },
        "fallbackTag": "outbound3"
      },

In “selector” you list outbounds through which you can connect “outside”, in fallbackTag you can specify an outbound which XRay will try to use if all of the above are unavailable (and it will be used immediately after startup until they have passed the checks). And then you can use your “mybalancer” as a regular outbound – for example, specify it explicitly in the routing rules.

Statistics collection

You can get statistics in JSON format – which user downloaded how much, for which inbounds/outbounds, and if you use observables from the previous section – see their availability and ping time. Convenient for integration into monitoring systems like Munin, Zabbix or Prometheus. Documentation here: Metrics | Project X (xtls.github.io)

Browser dialer

Remember the TLS fingerprint? Each browser and each TLS library has a unique one. In some countries, censors already use it to detect connections from proxy clients, because the standard TLS library of the Go language does not look like fingerprints of popular browsers. XRay can use uTLS – a specially modified library that can “pretend” to be browsers, such as Firefox and Chrome, copying their fingerprints. There is only one problem – if you have not updated the client for a long time, its fingerprint may become outdated, lagging behind the current versions of browsers (in some countries, Cloak was blocked this way, using the TLS fingerprint of some old version of Firefox), and secondly, even if the fingerprint is similar, some nuances of behavior may still differ, and censors can catch you on this sooner or later.

You may have heard about naiveproxywhose developer, in pursuit of a TLS fingerprint that would be as close to the “real” as possible, came up with the brilliant idea of ​​simply taking part of the code of the real Chromium browser and using it to establish a connection. XRay has an idea no worse – it can use the real browser entirely 🙂 It will only work with websocket or SplitHttp transport, but it is the most authentic disguise.

It's pretty easy to use. Configure XRay as usual for websockets or splithttp (you can create a connection in Nekoray and export the config to make it easier), and then run XRay with an environment variable like XRAY_BROWSER_DIALER=127.0.0.1:8080 . In Linux it can be inserted immediately before the xray launch command, in Windows it can be set with the “set” command before launch.

After that, open the address http://127.0.0.1:8080 in your browser (it looks like a blank page), and XRay starts using it as an intermediate point for connecting to a remote proxy server – you can see in “Developer Tools -> Network” websocket connections in pairs, half to the local XRay, and half to the remote proxy server.

Two important nuances: 1) the browser you use as an intermediate transit should not have a proxy specified in the settings (otherwise the request to the remote proxy will go to XRay again, and you will get an infinite loop); if you use the TUN interface, then clients usually make an exception in the routes for the destination proxy address, but there may be some tricks. 2) the number of simultaneous web socket connections in the browser is limited, so it is recommended to use multiplexing (mux) in XRay. Meanwhile, nothing prevents you from opening several tabs in the browser, they will be used for proxying simultaneously.

And finally, it is not necessary to open a browser window, Chrome can be launched in headless mode (without a visible window), for example, something like this:

"c:\Program Files\Google\Chrome\Application\chrome.exe" --headless=new --remote-debugging-port=0   --no-proxy-server http://127.0.0.1:8080/

Here we launch it in headless mode with one command, sending it directly to the local XRay address, disable the use of a proxy, and enable remote debuggingso that you can check if something suddenly goes wrong.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *