Волк ищет ip сервера? Передайте пирожок через рабочего

Страницы:  1

Ответить
 

Professor Seleznov


pic
Проблема с мобильными устройствами в том, что они по умолчанию находятся в агрессивно-недружелюбной среде, и их подключения могут стать известны кому угодно. Как минимум они известны персоналу провайдеров wi-fi или LTE, а это может поставить под угрозу атаки сервер приложений (или входящий прокси этого сервера).
Хорошо если сервер можно спрятать за Cloudflare и организовать доступ через оптимальный "чистый" ip Cloudflare, без использования dns запросов. Маршрутизация при этом осуществляется самим cloudflare по SNI специально выделенному для него, и handshake остаётся открытым - чтобы никого не смущать.
В случае, когда этот вариант не подходит мы можем сделать аналогичный механизм самиж и как вы уже наверное догадались по картинке - я предлагаю вариант через мост с использованием websockets и с cloudflare worker.
Не думаю, что открою страшный секрет, но для SNI в этом случае подходит любой домен с dns cloudflare, в том числе например и бесплатные поддомены от DigitalPlat.
Входной websocket поток этот worker может перебрасывать уже и на сервер приложений с фиксированным ip адресом, тем-более, что Lets Encrypt теперь выдаёт сертификаты на ip, и шифрование не пострадает.
А чтобы ни у кого не возникло желания обидеть сервер приложений, узнав его адрес из исходящих запросов - для них при желании выделяется отдельный сервер с отдельным ip и они делаются через него.
Таким образом - никакой связи между исходящими соединениями мобильного устройства и запросами с сервера приложений, как и собственно ip сервера приложений, как мне кажется, установить невозможно, но остаётся открытым вопрос - а какие ещё системы кроме Cloudflare позволяют делать подобное на уровне pet-проектов бесплатно или почти бесплатно, и нет ли более изящного решения?
Для желающих повторить мой эксперимент - вот пример (прошу простить за сырость) кода worker с фильтрацией разрешенных адресов для подключения и dummy адресом сервера приложений - поменять не забудьте их пожалуйста.
import { connect } from 'cloudflare:sockets';
export default {
async fetch(request, env, ctx) {
// 1. Извлекаем IP
const clientIP = request.headers.get("CF-Connecting-IP");
// 2. Проверяем
const allowedIPv4 = "127.0.0.1"; // Поменять не забудьте
const allowedIPv6Prefix = "fe80:"; // Обратите внимание на двоеточие в конце и поменять не забудьте
if (clientIP !== allowedIPv4 && !clientIP.startsWith(allowedIPv6Prefix)) {
return new Response(clientIP + " -> Вы кто такие?", { status: 403 });
}
const upgradeHeader = request.headers.get('Upgrade');
if (!upgradeHeader || upgradeHeader !== 'websocket') {
return new Response('Bridge Active.', { status: 200 });
}
const vlConfig = {
address: '10.0.0.1',
port: 8080,
}; // не забудьте поменять адрес и порт на ваш
const webSocketPair = new WebSocketPair();
const [client, server] = Object.values(webSocketPair);
server.accept();
handleProxy(server, vlConfig);
return new Response(null, {
status: 101,
webSocket: client,
});
}
};
async function handleProxy(ws, config) {
let tcpSocket = null;
let writer = null;
ws.addEventListener('message', async (event) => {
try {
const data = event.data instanceof ArrayBuffer ? event.data : await new Response(event.data).arrayBuffer();
if (!tcpSocket) {
// Инициализация соединения
tcpSocket = connect({ hostname: config.address, port: config.port });
writer = tcpSocket.writable.getWriter();
// Запуск высокопроизводительного чтения
copyTcpToWs(tcpSocket.readable, ws);
}
await writer.write(new Uint8Array(data));
} catch (err) {
ws.close(1011);
}
});
ws.addEventListener('close', () => {
if (tcpSocket) tcpSocket.close();
});
ws.addEventListener('error', () => {
if (tcpSocket) tcpSocket.close();
});
}
/**
* Оптимизированная передача данных из TCP в WebSocket
*/
async function copyTcpToWs(tcpReadable, ws) {
try {
// Используем встроенный механизм стримов для минимизации нагрузки на CPU
await tcpReadable.pipeTo(new WritableStream({
write(chunk) {
ws.send(chunk);
},
close() {
ws.close();
},
abort(reason) {
ws.close(1011);
}
}));
} catch (err) {
try { ws.close(); } catch (e) { }
}
}
С мобильного устройства к worker можно подключиться например используя hiddify с вот таким конфигом
{
"outbounds": [
{
"type": "vless",
"tag": "Worker-Bridge",
"server": "yoursubdomain.dpdns.org",
"server_port": 443,
"uuid": "UUID4REALPLZ",
"tls": {
"enabled": true,
"server_name": "yoursubdomain.dpdns.org",
"alpn": "http/1.1"
},
"transport": {
"type": "ws",
"path": "/",
"headers": {
"Host": "yoursubdomain.dpdns.org"
},
"early_data_header_name": "Sec-WebSocket-Protocol"
},
"packet_encoding": "xudp"
}
],
"endpoints": []
Трафик между worker и следующим сервером в цепочке - для уменьшения оверхеда и от бесстыдной лени автора статьи не шифруется, но его и читать некому, а после сервера зашифровать никто не мешает, уникальность подключения обеспечивает UUID.-Источник
 
Loading...
Error