diff --git a/.editorConfig b/.editorConfig index b59ec24..c9dfd77 100644 --- a/.editorConfig +++ b/.editorConfig @@ -6,6 +6,9 @@ indent_size=4 end_of_line=lf charset=utf-8 trim_trailing_whitespace=true +[*.php] insert_final_newline=true [makefile] indent_style=tab +[config/Makefile] +indent_style=tab diff --git a/.env b/.env index b4d105b..35f8354 100644 --- a/.env +++ b/.env @@ -2,5 +2,6 @@ TZ=Europe/Samara WGADDRESS=10.0.1.1/24 WGPORT=51820 SSPORT=8388 +TGPORT=4443 SYSTEM=ubuntu RELEASE=18.04 diff --git a/app/bot.php b/app/bot.php index 0b88294..3a8c072 100644 --- a/app/bot.php +++ b/app/bot.php @@ -120,6 +120,15 @@ class Bot case preg_match('~^/id$~', $this->input['message'], $m): $this->send($this->input['chat'], $this->input['from'], $this->input['message_id']); break; + case preg_match('~^/mtproto$~', $this->input['callback'], $m): + $this->mtproto(); + break; + case preg_match('~^/generateSecret$~', $this->input['callback'], $m): + $this->generateSecret(); + break; + case preg_match('~^/setSecret$~', $this->input['callback'], $m): + $this->setSecret(); + break; case preg_match('~^/subnet (?P\d+(?:_(?:-)?\d+)?)$~', $this->input['callback'], $m): $this->subnet(...explode('_', $m['arg'])); break; @@ -277,6 +286,90 @@ class Bot } } + public function generateSecret() + { + $this->send($this->input['chat'], trim($this->ssh('head -c 16 /dev/urandom | xxd -ps', 'tg'))); + } + + public function setSecret() + { + $r = $this->send( + $this->input['chat'], + "@{$this->input['username']} enter url", + $this->input['message_id'], + reply: 'enter url', + ); + $_SESSION['reply'][$r['result']['message_id']] = [ + 'start_message' => $this->input['message_id'], + 'start_callback' => $this->input['callback_id'], + 'callback' => 'secretSet', + 'args' => [], + ]; + } + + public function secretSet($secret) + { + file_put_contents('/config/mtprotosecret', $secret ?: ''); + $this->restartTG($secret); + $this->mtproto(); + } + + public function restartTG($secret) + { + $this->ssh('pkill mtproto-proxy', 'tg'); + if (preg_match('~^\w{32}$~', $secret)) { + $p = getenv('TGPORT'); + $this->ssh("/MTProxy/objs/bin/mtproto-proxy -u nobody -H $p --nat-info 10.10.0.8:{$this->ip} -S $secret --aes-pwd /proxy-secret /proxy-multi.conf -M 1 >/dev/null 2>&1 &", 'tg'); + } + } + + public function mtproto() + { + $s = file_get_contents('/config/mtprotosecret'); + $p = getenv('TGPORT'); + $ip = $this->getPacConf()['domain'] ?: $this->ip; + $st = $this->ssh('pgrep mtproto-proxy', 'tg') ? 'on' : 'off'; + $text[] = "Menu -> MTProto\n"; + $text[] = "status: $st\n"; + if ($st == 'on') { + $text[] = "https://t.me/proxy?server=$ip&port=$p&secret=$s\n\ntg://proxy?server=$ip&port=$p&secret=$s"; + $data[] = [ + [ + 'text' => "https://t.me/proxy", + 'url' => "https://t.me/proxy?server=$ip&port=$p&secret=$s", + ], + [ + 'text' => "tg://proxy", + 'url' => "tg://proxy?server=$ip&port=$p&secret=$s", + ], + ]; + } + $data[] = [ + [ + 'text' => $this->i18n('generateSecret'), + 'callback_data' => "/generateSecret", + ], + ]; + $data[] = [ + [ + 'text' => $this->i18n('setSecret') . ($s ? ": $s" : ''), + 'callback_data' => "/setSecret", + ], + ]; + $data[] = [ + [ + 'text' => $this->i18n('back'), + 'callback_data' => "/menu", + ], + ]; + $this->update( + $this->input['chat'], + $this->input['message_id'], + implode("\n", $text ?: ['...']), + $data ?: false, + ); + } + public function setLang($lang) { $conf = $this->getPacConf(); @@ -496,6 +589,7 @@ class Bot 'private' => file_exists('/certs/cert_private') ? file_get_contents('/certs/cert_private') : false, 'public' => file_exists('/certs/cert_public') ? file_get_contents('/certs/cert_public') : false, ], + 'mtproto' => file_get_contents('/config/mtprotosecret'), ]; $this->upload('vpnbot_export_' . date('d_m_Y_H_i') . '.json', json_encode($conf, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); @@ -568,6 +662,12 @@ class Bot file_put_contents('/config/sslocal.json', json_encode($json['sl'], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); $this->ssh('/sslocal -v -d -c /config.json', 'proxy'); } + // mtproto + if (!empty($json['mtproto'])) { + $out[] = 'update mtproto'; + $this->update($this->input['chat'], $this->input['message_id'], implode("\n", $out)); + $this->restartTG($json['mtproto']); + } // nginx $out[] = 'reset nginx'; $this->update($this->input['chat'], $this->input['message_id'], implode("\n", $out)); @@ -2098,7 +2198,7 @@ DNS-over-HTTPS with IP: public function i18n(string $menu): string { - return $this->i18n[$menu][$this->language] ?: 'no translate'; + return $this->i18n[$menu][$this->language] ?: $menu; } public function menu($type = false, $arg = false, $return = false) @@ -2127,6 +2227,12 @@ DNS-over-HTTPS with IP: 'callback_data' => "/menu pac", ], ], + [ + [ + 'text' => $this->i18n('mtproto'), + 'callback_data' => "/mtproto", + ] + ], [ [ 'text' => $this->i18n('config'), diff --git a/app/i18n.php b/app/i18n.php index 776d26c..7c14785 100644 --- a/app/i18n.php +++ b/app/i18n.php @@ -221,4 +221,16 @@ $i = [ 'en' => 'calc CIDR', 'ru' => 'calc CIDR', ], + 'mtproto' => [ + 'en' => 'MTProto', + 'ru' => 'MTProto', + ], + 'generateSecret' => [ + 'en' => 'generateSecret', + 'ru' => 'сгенерировать ключ', + ], + 'setSecret' => [ + 'en' => 'setSecret', + 'ru' => 'установить ключ', + ], ]; diff --git a/config/Makefile b/config/Makefile new file mode 100644 index 0000000..cab32db --- /dev/null +++ b/config/Makefile @@ -0,0 +1,102 @@ +OBJ = objs +DEP = dep +EXE = ${OBJ}/bin + +COMMIT := $(shell git log -1 --pretty=format:"%H") + +ARCH = +ifeq ($m, 32) +ARCH = -m32 +endif +ifeq ($m, 64) +ARCH = -m64 +endif + +CFLAGS = $(ARCH) -O3 -std=gnu11 -Wall -mpclmul -march=core2 -mfpmath=sse -mssse3 -fno-strict-aliasing -fno-strict-overflow -fwrapv -DAES=1 -DCOMMIT=\"${COMMIT}\" -D_GNU_SOURCE=1 -D_FILE_OFFSET_BITS=64 -fcommon +LDFLAGS = $(ARCH) -ggdb -rdynamic -lm -lrt -lcrypto -lz -lpthread -lcrypto + +LIB = ${OBJ}/lib +CINCLUDE = -iquote common -iquote . + +LIBLIST = ${LIB}/libkdb.a + +PROJECTS = common jobs mtproto net crypto engine + +OBJDIRS := ${OBJ} $(addprefix ${OBJ}/,${PROJECTS}) ${EXE} ${LIB} +DEPDIRS := ${DEP} $(addprefix ${DEP}/,${PROJECTS}) +ALLDIRS := ${DEPDIRS} ${OBJDIRS} + + +.PHONY: all clean + +EXELIST := ${EXE}/mtproto-proxy + + +OBJECTS = \ + ${OBJ}/mtproto/mtproto-proxy.o ${OBJ}/mtproto/mtproto-config.o ${OBJ}/net/net-tcp-rpc-ext-server.o + +DEPENDENCE_CXX := $(subst ${OBJ}/,${DEP}/,$(patsubst %.o,%.d,${OBJECTS_CXX})) +DEPENDENCE_STRANGE := $(subst ${OBJ}/,${DEP}/,$(patsubst %.o,%.d,${OBJECTS_STRANGE})) +DEPENDENCE_NORM := $(subst ${OBJ}/,${DEP}/,$(patsubst %.o,%.d,${OBJECTS})) + +LIB_OBJS_NORMAL := \ + ${OBJ}/common/crc32c.o \ + ${OBJ}/common/pid.o \ + ${OBJ}/common/sha1.o \ + ${OBJ}/common/sha256.o \ + ${OBJ}/common/md5.o \ + ${OBJ}/common/resolver.o \ + ${OBJ}/common/parse-config.o \ + ${OBJ}/crypto/aesni256.o \ + ${OBJ}/jobs/jobs.o ${OBJ}/common/mp-queue.o \ + ${OBJ}/net/net-events.o ${OBJ}/net/net-msg.o ${OBJ}/net/net-msg-buffers.o \ + ${OBJ}/net/net-config.o ${OBJ}/net/net-crypto-aes.o ${OBJ}/net/net-crypto-dh.o ${OBJ}/net/net-timers.o \ + ${OBJ}/net/net-connections.o \ + ${OBJ}/net/net-rpc-targets.o \ + ${OBJ}/net/net-tcp-connections.o ${OBJ}/net/net-tcp-rpc-common.o ${OBJ}/net/net-tcp-rpc-client.o ${OBJ}/net/net-tcp-rpc-server.o \ + ${OBJ}/net/net-http-server.o \ + ${OBJ}/common/tl-parse.o ${OBJ}/common/common-stats.o \ + ${OBJ}/engine/engine.o ${OBJ}/engine/engine-signals.o \ + ${OBJ}/engine/engine-net.o \ + ${OBJ}/engine/engine-rpc.o \ + ${OBJ}/engine/engine-rpc-common.o \ + ${OBJ}/net/net-thread.o ${OBJ}/net/net-stats.o ${OBJ}/common/proc-stat.o \ + ${OBJ}/common/kprintf.o \ + ${OBJ}/common/precise-time.o ${OBJ}/common/cpuid.o \ + ${OBJ}/common/server-functions.o ${OBJ}/common/crc32.o \ + +LIB_OBJS := ${LIB_OBJS_NORMAL} + +DEPENDENCE_LIB := $(subst ${OBJ}/,${DEP}/,$(patsubst %.o,%.d,${LIB_OBJS})) + +DEPENDENCE_ALL := ${DEPENDENCE_NORM} ${DEPENDENCE_STRANGE} ${DEPENDENCE_LIB} + +OBJECTS_ALL := ${OBJECTS} ${LIB_OBJS} + +all: ${ALLDIRS} ${EXELIST} +dirs: ${ALLDIRS} +create_dirs_and_headers: ${ALLDIRS} + +${ALLDIRS}: + @test -d $@ || mkdir -p $@ + +-include ${DEPENDENCE_ALL} + +${OBJECTS}: ${OBJ}/%.o: %.c | create_dirs_and_headers + ${CC} ${CFLAGS} ${CINCLUDE} -c -MP -MD -MF ${DEP}/$*.d -MQ ${OBJ}/$*.o -o $@ $< + +${LIB_OBJS_NORMAL}: ${OBJ}/%.o: %.c | create_dirs_and_headers + ${CC} ${CFLAGS} -fpic ${CINCLUDE} -c -MP -MD -MF ${DEP}/$*.d -MQ ${OBJ}/$*.o -o $@ $< + +${EXELIST}: ${LIBLIST} + +${EXE}/mtproto-proxy: ${OBJ}/mtproto/mtproto-proxy.o ${OBJ}/mtproto/mtproto-config.o ${OBJ}/net/net-tcp-rpc-ext-server.o + ${CC} -o $@ $^ ${LIB}/libkdb.a ${LDFLAGS} + +${LIB}/libkdb.a: ${LIB_OBJS} + rm -f $@ && ar rcs $@ $^ + +clean: + rm -rf ${OBJ} ${DEP} ${EXE} || true + +force-clean: clean diff --git a/config/mtprotosecret b/config/mtprotosecret new file mode 100644 index 0000000..e69de29 diff --git a/docker-compose.yml b/docker-compose.yml index 925d38d..a6d77b5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -62,6 +62,7 @@ services: - ./config/nginx_default.conf:/config/nginx_default.conf - ./config/ssserver.json:/config/ssserver.json - ./config/sslocal.json:/config/sslocal.json + - ./config/mtprotosecret:/config/mtprotosecret - ./certs/:/certs/ - type: volume target: /config/adguard @@ -73,9 +74,11 @@ services: - ./scripts/check_file.sh:/check_file.sh environment: TZ: ${TZ} + IP: ${IP} ADDRESS: ${WGADDRESS} WGPORT: ${WGPORT} SSPORT: ${SSPORT} + TGPORT: ${TGPORT} hostname: unit restart: unless-stopped stop_grace_period: 1s @@ -204,3 +207,30 @@ services: default: ipv4_address: 10.10.0.6 logging: *default-logging + tg: + build: + dockerfile: dockerfile/telegram.dockerfile + args: + RELEASE: ${RELEASE} + SYSTEM: ${SYSTEM} + volumes: + - ./config/.profile:/root/.bashrc:ro + - ./ssh:/ssh + - ./scripts/start_tg.sh:/start_tg.sh + - ./config/mtprotosecret:/mtprotosecret + hostname: telegram + depends_on: + unit: + condition: service_healthy + ports: + - ${TGPORT}:${TGPORT} + environment: + TZ: ${TZ} + IP: ${IP} + TGPORT: ${TGPORT} + stop_grace_period: 1s + command: ["/bin/sh", "/start_tg.sh"] + networks: + default: + ipv4_address: 10.10.0.8 + logging: *default-logging diff --git a/dockerfile/telegram.dockerfile b/dockerfile/telegram.dockerfile new file mode 100644 index 0000000..0b12220 --- /dev/null +++ b/dockerfile/telegram.dockerfile @@ -0,0 +1,14 @@ +arg SYSTEM +arg RELEASE +from ${SYSTEM}:${RELEASE} +ENV DEBIAN_FRONTEND noninteractive +run apt update && \ +apt install -y git curl build-essential libssl-dev zlib1g-dev xxd ssh && \ +apt clean autoclean && \ +apt autoremove -y && \ +mkdir /root/.ssh +run git clone https://github.com/TelegramMessenger/MTProxy +copy config/Makefile /MTProxy/Makefile +run cd /MTProxy && make +env PATH="$PATH:/MTProxy/objs/bin" + diff --git a/makefile b/makefile index 3af2f0a..ad42262 100644 --- a/makefile +++ b/makefile @@ -5,7 +5,7 @@ hosts: unhosts # маппинг доменов на локалку unhosts: sed -i '/test.ru/d' /mnt/c/Windows/System32/drivers/etc/hosts u: # запуск контейнеров - RELEASE=$(shell lsb_release -rs) SYSTEM=$(shell lsb_release -is | tr '[:upper:]' '[:lower:]') docker compose up -d --build --force-recreate + IP=$(shell ip -4 addr | sed -ne 's|^.* inet \([^/]*\)/.* scope global.*$$|\1|p' | awk '{print $1}' | head -1) RELEASE=$(shell lsb_release -rs) SYSTEM=$(shell lsb_release -is | tr '[:upper:]' '[:lower:]') docker compose up -d --build --force-recreate # sleep 1 # docker compose logs unit wg ss proxy d: # остановка контейнеров @@ -29,3 +29,5 @@ ad: # консоль сервиса docker compose exec ad bash proxy: # консоль сервиса docker compose exec proxy bash +tg: # консоль сервиса + docker compose exec tg bash diff --git a/scripts/init.sh b/scripts/init.sh index 10fec61..e79df89 100644 --- a/scripts/init.sh +++ b/scripts/init.sh @@ -7,6 +7,7 @@ apt install -y \ make \ git \ iptables \ + iproute2 \ xtables-addons-common \ xtables-addons-dkms mkdir -p /etc/apt/keyrings diff --git a/scripts/start_tg.sh b/scripts/start_tg.sh new file mode 100644 index 0000000..aed19f4 --- /dev/null +++ b/scripts/start_tg.sh @@ -0,0 +1,12 @@ +cat /ssh/key.pub > /root/.ssh/authorized_keys +echo 'HostKeyAlgorithms +ssh-rsa' >> /etc/ssh/sshd_config +echo 'PubkeyAcceptedKeyTypes +ssh-rsa' >> /etc/ssh/sshd_config +service ssh start +curl -s https://core.telegram.org/getProxySecret -o proxy-secret +curl -s https://core.telegram.org/getProxyConfig -o proxy-multi.conf +if [ $(cat /mtprotosecret | wc -c) -gt 0 ] +then + SECRET=$(cat /mtprotosecret) + mtproto-proxy -u nobody -H $TGPORT --nat-info 10.10.0.8:$IP -S $SECRET --aes-pwd /proxy-secret /proxy-multi.conf -M 1 +fi +tail -f /dev/null diff --git a/scripts/start_unit.sh b/scripts/start_unit.sh index 3e9ab42..c75de82 100644 --- a/scripts/start_unit.sh +++ b/scripts/start_unit.sh @@ -1,4 +1,3 @@ -export IP=$(curl https://ipinfo.io/ip) rm /ssh/key* ssh-keygen -m PEM -t rsa -f /ssh/key -N '' openssl req -newkey rsa:2048 -sha256 -nodes -x509 -days 365 -keyout /certs/self_private -out /certs/self_public -subj "/C=NN/ST=N/L=N/O=N/CN=$IP"