Everything described below is the result of a technical experiment. The material is not advertising, does not call for any action, is provided solely for informational purposes, and was prepared as part of research.

What you will need:

  • Keenetic with USB support;
  • a drive of at least 1 GB;
  • current KeeneticOS and the Entware repository package system installed;

The last point is well described on the Keenetic website.

OpenSSH

After the system has started and you can log in to the CLI, you can clean up some elements. You can skip this if the only task is to launch Xray.

  1. Update and install the required packages
opkg update && opkg install mc ss bash curl ip-full iptables sudo micro
  1. Install OpenSSH, because Dropbear is used by default (can be skipped)
opkg install openssh-server && ssh-keygen -A

Edit the configuration

mcedit /opt/etc/ssh/sshd_config

Here everyone can configure it as they prefer, but the port definitely needs to be adjusted:

Port 777
AddressFamily inet
ListenAddress 192.168.1.1 
LoginGraceTime 1m
PermitRootLogin yes
X11Forwarding no
MaxAuthTries 2
MaxSessions 2
TCPKeepAlive yes
ClientAliveInterval 60
ClientAliveCountMax 2
MaxStartups 2:50:4
Subsystem sftp internal-sftp
Match group root
AllowTcpForwarding no
PasswordAuthentication yes

Create a user

adduser -D -H -s /bin/false sshd

Generate keys

ssh-keygen -t rsa -f /opt/etc/ssh/ssh_host_rsa_key -N "" &&
ssh-keygen -t ed25519 -f /opt/etc/ssh/ssh_host_ed25519_key -N ""

Set permissions

chmod 600 /opt/etc/ssh/ssh_host_*_key

Restart and check the status

/opt/etc/init.d/S40sshd restart && /opt/etc/init.d/S40sshd status

Log in again through SSH using the new port. If everything is OK, go to the Keenetic dashboard and remove the default SSH component, then remove Dropbear.

opkg remove dropbear

Xray

The ready-made tool used here is the fork by jameszeroX published on GitHub. It is worth separately noting and thanking the author for developing this Shell (Bash) tool.

How to deploy an Xray server was described in a note.

Installing Xkeen

opkg update && opkg upgrade && opkg install curl tar && cd /tmp
url="https://raw.githubusercontent.com/jameszeroX/XKeen/main/install.sh"
curl -OL --connect-timeout 10 -m 60 "$url"
chmod +x install.sh
./install.sh

Then there will be a series of questions, additional installations, and configurations:

  • install missing GeoIP
  • install missing GeoSite
  • enable automatic update tasks

Router Configuration

Open the Keenetic home page

Go to “Internet” -> “Connection priorities” -> “Internet access policies” -> “Add policy”

Create an “Xkeen” policy

Enable the “Multipath transmission” checkbox if you need to combine several providers

Select providers with internet access

Go to “Internet” -> “Connection priorities” -> “Policy application”

Select all connected devices that should work through Xray

Move them to the “Xkeen” policy

Go to “Internet” -> “Internet provider name”:

  • Enable the “Ignore internet provider DNSv4” checkbox

  • Add two DNS servers: 1.1.1.1 and 8.8.8.8

  • Set IPv6 parameters to “Not used”

Go to the router CLI. To do this, click the gear icon in the upper-right corner and select “Command line”

Move Keenetic services from port 443 to any of these ports:

  • 5083
  • 5443
  • 8083
  • 8443
  • 65083

To do this, enter the command:

ip http ssl port {port}

Replace {port} with any of the ports.

Allow opkg to manage DNS:

opkg dns-override 

Save the configuration

system configuration save

Xray Configuration

  • vless and reality will be used for transport, as in the previously described server configuration note;
  • EXAMPLE-SITE.COM - server address;
  • 10.10.10.10 - server IP address.
  • UUID_FOR_KEENETIC - UUID for Keenetic received on the server
  • PASSWORD - password received on the server
  • SHORT_ID - shortid received on the server

Log in to the console through SSH and move to the configuration directory:

cd /opt/etc/xray/configs/

Start editing the configurations:

log

mcedit 01_log.json

Insert

{
  "log": {
    "access": "/opt/var/log/xray/access.log",
    "error": "/opt/var/log/xray/error.log",
    "loglevel": "warning"
  }
}

dns

mcedit 02_dns.json

Insert

{
    "dns": {
    "hosts": {
      "EXAMPLE-SITE.COM": "10.10.10.10"
    },
    "servers": [
        "https://8.8.8.8/dns-query",
        "https://1.1.1.1/dns-query",
        {
            "address": "77.88.8.8",
            "port": 53,
            "domains": ["ext:geosite_v2fly.dat:category-ru"]
        }
    ]
  }
}

inbounds

mcedit 03_inbounds.json

Insert

{
  "inbounds": [
    {
    "tag": "dns-in",
    "protocol": "dokodemo-door",
    "port": 53,
    "listen": "127.0.0.1",
    "settings":
        {
        "network": "udp,tcp",
        "followRedirect": false
        }
    },
    {
      "port": 1181,
      "protocol": "dokodemo-door",
      "settings": {
        "network": "tcp",
        "followRedirect": true
      },
      "sniffing": {
        "enabled": true,
        "routeOnly": true,
        "destOverride": ["http","tls"]
      },
      "tag": "redirect"
    },
    {
      "port": 1181,
      "protocol": "dokodemo-door",
      "settings": {
        "network": "udp",
        "followRedirect": true
      },
      "streamSettings": {
        "sockopt": {"tproxy": "tproxy"}
      },
      "sniffing": {
        "enabled": true,
        "routeOnly": true,
        "destOverride": ["quic"]
      },
      "tag": "tproxy"
    }
  ]
}

outbounds

mcedit 04_outbounds.json

Insert

{
  "outbounds": [
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {
        "domainStrategy": "UseIPv4"
      },
      "streamSettings": {}
    },

    {
      "tag": "vless-reality",
      "protocol": "vless",
      "settings": {
        "address": "EXAMPLE-SITE.COM",
        "port": 443,
        "id": "UUID_FOR_KEENETIC",
        "encryption": "none",
        "flow": "xtls-rprx-vision",
        "level": 0
      },
      "streamSettings": {
        "network": "raw",
        "security": "reality",
        "realitySettings": {
          "serverName": "EXAMPLE-SITE.COM",
          "fingerprint": "chrome",
          "password": "PASSWORD",
          "shortId": "SHORT_ID"
        }
      }
    },

    {
      "tag": "block",
      "protocol": "blackhole",
      "settings": {
        "response": {
          "type": "http"
        }
      }
    },

    {
      "tag": "dns-out",
      "protocol": "dns",
      "settings": {
        "network": "tcp,udp",
        "nonIPQuery": "drop"
      }
    }
  ]
}

routing

mcedit 05_routing.json

Insert

{
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "type": "field",
        "inboundTag": ["redirect", "tproxy"],
        "outboundTag": "block",
        "network": "udp",
        "port": "135,137,138,139,443"
      },
      {
        "inboundTag": ["redirect", "tproxy"],
        "type": "field",
        "domain": ["ext:geosite_v2fly.dat:category-ads-all"],
        "outboundTag": "block"
      },
      {
        "type": "field",
        "inboundTag": ["redirect", "tproxy", "dns-in"],
        "port": 53,
        "outboundTag": "dns-out"
      },
      {
        "inboundTag": ["redirect", "tproxy"],
        "type": "field",
        "domain": ["ext:geosite_v2fly.dat:private", "ext:geosite_v2fly.dat:category-ru"],
        "outboundTag": "direct"
      },
      {
        "inboundTag": ["redirect", "tproxy"],
        "type": "field",
        "ip": ["ext:geoip_v2fly.dat:ru"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "outboundTag": "vless-reality",
        "network": "tcp,udp"
      }
    ]
  }
}

policy

Leave unchanged

After making the changes, reload Xkeen and restart the router:

xkeen -restrart && reboot