diff --git a/client/core/controllers/selfhosted/installController.cpp b/client/core/controllers/selfhosted/installController.cpp index f87f6dbd2..0a7b8e14a 100644 --- a/client/core/controllers/selfhosted/installController.cpp +++ b/client/core/controllers/selfhosted/installController.cpp @@ -835,6 +835,20 @@ ErrorCode InstallController::installDockerWorker(const ServerCredentials &creden qDebug().noquote() << "InstallController::installDockerWorker" << stdOut; + if (container == DockerContainer::MtProxy || container == DockerContainer::Telemt) { + QString conntrackOut; + auto cbConntrack = [&](const QString &data, libssh::Client &) { + conntrackOut += data + "\n"; + return ErrorCode::NoError; + }; + sshSession.runScript( + credentials, + sshSession.replaceVars(amnezia::scriptData(SharedScriptType::install_conntrack), + amnezia::genBaseVars(credentials, DockerContainer::None, QString(), QString())), + cbConntrack, cbConntrack); + qDebug().noquote() << "InstallController::installDockerWorker install_conntrack:" << conntrackOut; + } + if (container == DockerContainer::Awg2) { QRegularExpression regex(R"(Linux\s+(\d+)\.(\d+)[^\d]*)"); QRegularExpressionMatch match = regex.match(stdOut); diff --git a/client/core/installers/mtProxyInstaller.cpp b/client/core/installers/mtProxyInstaller.cpp index 937dab0db..149bac886 100644 --- a/client/core/installers/mtProxyInstaller.cpp +++ b/client/core/installers/mtProxyInstaller.cpp @@ -71,48 +71,62 @@ ErrorCode MtProxyInstaller::queryDiagnostics(SshSession &sshSession, const Serve DockerContainer container, int listenPort, MtProxyContainerDiagnostics &out) { - out = {}; - if (container != DockerContainer::MtProxy && container != DockerContainer::Telemt) { - return ErrorCode::InternalError; - } - const QString containerName = ContainerUtils::containerToString(container); - const QString script = - QStringLiteral( - "PORT_OK=$(sudo docker exec %1 sh -c 'ss -tlnp 2>/dev/null | grep -q :%2 && echo yes || echo no' 2>/dev/null || echo no); " - "TG_OK=$(curl -s --max-time 5 -o /dev/null -w '%%{http_code}' https://core.telegram.org/getProxySecret 2>/dev/null | grep -q '200' && echo yes || echo no); " - "CLIENTS=$(sudo docker exec amnezia-mtproxy sh -c 'curl -s --max-time 3 http://localhost:2398/stats 2>/dev/null | grep -o \"total_special_connections:[0-9]*\" | cut -d: -f2' 2>/dev/null); " - "CONF_TIME=$(sudo docker exec amnezia-mtproxy sh -c 'stat -c \"%%y\" /data/proxy-multi.conf 2>/dev/null | cut -d. -f1' 2>/dev/null || echo unknown); " - "echo \"PORT_OK=${PORT_OK}\"; " - "echo \"TG_OK=${TG_OK}\"; " - "echo \"CLIENTS=${CLIENTS:-0}\"; " - "echo \"CONF_TIME=${CONF_TIME}\"; " - "echo \"STATS=http://localhost:2398/stats\";") - .arg(containerName) - .arg(listenPort); + out = { }; + if (container == DockerContainer::MtProxy || container == DockerContainer::Telemt) { + const QString containerName = ContainerUtils::containerToString(container); + const bool isTelemt = container == DockerContainer::Telemt; - QString stdOut; - auto cbReadStdOut = [&](const QString &data, libssh::Client &) { - stdOut += data; - return ErrorCode::NoError; - }; - const ErrorCode errorCode = sshSession.runScript(credentials, script, cbReadStdOut); - if (errorCode != ErrorCode::NoError) { - return errorCode; - } - for (const QString &line : stdOut.split('\n', Qt::SkipEmptyParts)) { - if (line.startsWith(QLatin1String("PORT_OK="))) { - out.portReachable = line.mid(8).trimmed() == QLatin1String("yes"); - } else if (line.startsWith(QLatin1String("TG_OK="))) { - out.upstreamReachable = line.mid(6).trimmed() == QLatin1String("yes"); - } else if (line.startsWith(QLatin1String("CLIENTS="))) { - out.clientsConnected = line.mid(8).trimmed().toInt(); - } else if (line.startsWith(QLatin1String("CONF_TIME="))) { - out.lastConfigRefresh = line.mid(10).trimmed(); - } else if (line.startsWith(QLatin1String("STATS="))) { - out.statsEndpoint = line.mid(6).trimmed(); + const QString sportFilter = QString::number(listenPort); + const QString peersCmd = QStringLiteral("sudo conntrack -L -p tcp --dport ") + sportFilter + + QStringLiteral(" 2>/dev/null | grep ESTABLISHED | awk '{for(i=1;i<=NF;i++) if($i ~ /^src=/){print " + "substr($i,5); break}}'"); + const QString publicFilter = QStringLiteral(" | grep -vE " + "'^(10\\.|127\\.|169\\.254\\.|192\\.168\\.|172\\.(1[6-9]|2[0-9]|3[" + "01])\\.|::1$|fe80:|f[cd][0-9a-f][0-9a-f]:)'"); + const QString clientsCmd = + QStringLiteral("CLIENTS=$(") + peersCmd + publicFilter + QStringLiteral(" | sort -u | grep -c .); "); + const QString confFile = + isTelemt ? QStringLiteral("/data/config.toml") : QStringLiteral("/data/proxy-multi.conf"); + const QString statsUrl = QString(); + + const QString script = QStringLiteral("CN=") + containerName + QStringLiteral("; ") + + QStringLiteral("PORT_OK=$(sudo ss -tlnp 2>/dev/null | grep -q :") + QString::number(listenPort) + + QStringLiteral(" && echo yes || echo no); ") + + QStringLiteral("TG_OK=$(curl -s --max-time 5 -o /dev/null -w '%{http_code}' " + "https://core.telegram.org/getProxySecret 2>/dev/null | grep -q '200' && echo yes || " + "echo no); ") + + clientsCmd + QStringLiteral("CONF_TIME=$(sudo docker exec \"$CN\" sh -c 'stat -c \"%y\" ") + confFile + + QStringLiteral(" 2>/dev/null | cut -d. -f1' 2>/dev/null || echo unknown); ") + + QStringLiteral("echo \"PORT_OK=${PORT_OK}\"; ") + QStringLiteral("echo \"TG_OK=${TG_OK}\"; ") + + QStringLiteral("echo \"CLIENTS=${CLIENTS:-0}\"; ") + QStringLiteral("echo \"CONF_TIME=${CONF_TIME}\"; ") + + QStringLiteral("echo \"STATS=") + statsUrl + QStringLiteral("\";"); + + QString stdOut; + auto cbReadStdOut = [&](const QString &data, libssh::Client &) { + stdOut += data; + return ErrorCode::NoError; + }; + const ErrorCode errorCode = sshSession.runScript(credentials, script, cbReadStdOut); + if (errorCode != ErrorCode::NoError) { + return errorCode; } + for (const QString &line : stdOut.split('\n', Qt::SkipEmptyParts)) { + if (line.startsWith(QLatin1String("PORT_OK="))) { + out.portReachable = line.mid(8).trimmed() == QLatin1String("yes"); + } else if (line.startsWith(QLatin1String("TG_OK="))) { + out.upstreamReachable = line.mid(6).trimmed() == QLatin1String("yes"); + } else if (line.startsWith(QLatin1String("CLIENTS="))) { + out.clientsConnected = line.mid(8).trimmed().toInt(); + } else if (line.startsWith(QLatin1String("CONF_TIME="))) { + out.lastConfigRefresh = line.mid(10).trimmed(); + } else if (line.startsWith(QLatin1String("STATS="))) { + out.statsEndpoint = line.mid(6).trimmed(); + } + } + return ErrorCode::NoError; } - return ErrorCode::NoError; + + return ErrorCode::InternalError; } void MtProxyInstaller::uploadClientSettingsSnapshot(SshSession &sshSession, const ServerCredentials &credentials, diff --git a/client/core/utils/selfhosted/scriptsRegistry.cpp b/client/core/utils/selfhosted/scriptsRegistry.cpp index 4e07ae752..3da6b8219 100644 --- a/client/core/utils/selfhosted/scriptsRegistry.cpp +++ b/client/core/utils/selfhosted/scriptsRegistry.cpp @@ -50,6 +50,7 @@ QString amnezia::scriptName(SharedScriptType type) switch (type) { case SharedScriptType::prepare_host: return QLatin1String("prepare_host.sh"); case SharedScriptType::install_docker: return QLatin1String("install_docker.sh"); + case SharedScriptType::install_conntrack: return QLatin1String("install_conntrack.sh"); case SharedScriptType::build_container: return QLatin1String("build_container.sh"); case SharedScriptType::remove_container: return QLatin1String("remove_container.sh"); case SharedScriptType::remove_all_containers: return QLatin1String("remove_all_containers.sh"); diff --git a/client/core/utils/selfhosted/scriptsRegistry.h b/client/core/utils/selfhosted/scriptsRegistry.h index f63b850a6..a1e5d202d 100644 --- a/client/core/utils/selfhosted/scriptsRegistry.h +++ b/client/core/utils/selfhosted/scriptsRegistry.h @@ -21,6 +21,7 @@ enum SharedScriptType { // General scripts prepare_host, install_docker, + install_conntrack, build_container, remove_container, remove_all_containers, diff --git a/client/server_scripts/install_conntrack.sh b/client/server_scripts/install_conntrack.sh new file mode 100644 index 000000000..c372fee08 --- /dev/null +++ b/client/server_scripts/install_conntrack.sh @@ -0,0 +1,10 @@ +if command -v conntrack > /dev/null 2>&1; then echo "conntrack already installed"; exit 0; fi;\ +if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); silent_inst="-yq install --install-recommends"; check_pkgs="-yq update"; conntrack_pkg="conntrack"; dist="debian";\ +elif which dnf > /dev/null 2>&1; then pm=$(which dnf); silent_inst="-yq install"; check_pkgs="-yq check-update"; conntrack_pkg="conntrack-tools"; dist="fedora";\ +elif which yum > /dev/null 2>&1; then pm=$(which yum); silent_inst="-y -q install"; check_pkgs="-y -q check-update"; conntrack_pkg="conntrack-tools"; dist="centos";\ +elif which zypper > /dev/null 2>&1; then pm=$(which zypper); silent_inst="-nq install"; check_pkgs="-nq refresh"; conntrack_pkg="conntrack-tools"; dist="opensuse";\ +elif which pacman > /dev/null 2>&1; then pm=$(which pacman); silent_inst="-S --noconfirm --noprogressbar --quiet"; check_pkgs="-Sup"; conntrack_pkg="conntrack-tools"; dist="archlinux";\ +else echo "Packet manager not found"; exit 0; fi;\ +if [ "$dist" = "debian" ]; then export DEBIAN_FRONTEND=noninteractive; fi;\ +sudo $pm $check_pkgs; sudo $pm $silent_inst $conntrack_pkg;\ +command -v conntrack > /dev/null 2>&1 && echo "conntrack installed" || echo "conntrack install failed" diff --git a/client/server_scripts/serverScripts.qrc b/client/server_scripts/serverScripts.qrc index 278e16953..2d1ae6059 100644 --- a/client/server_scripts/serverScripts.qrc +++ b/client/server_scripts/serverScripts.qrc @@ -18,6 +18,7 @@ dns/Dockerfile dns/run_container.sh install_docker.sh + install_conntrack.sh ipsec/configure_container.sh ipsec/Dockerfile ipsec/mobileconfig.plist