Skip to content

Commit d702dc0

Browse files
authored
fix: various fixes for Telemt, XRay (VLESS), Captcha (#2903)
* fix worker & fix captha * fixed read config (xxd) snapshot * remove comment * fix snapshot * fixed xray save * move subtitleText -> hint (Xray) * xray config apply via reinstall * apply server config via container reinstall * transport compatibility and xray-core v26 config * seed reality defaults on fresh deploy * server scripts for xray-core v26 (keys, udp, image) * placeholder defaults and hint tooltip control * wire placeholders and hints into settings pages * gate flow and security by transport in UI * encode raw tg link in QR * remove comment * restore telegram-proxy transport mode and domain on server re-scan * remove drop SessionKey, add edit * fixed xray/mtproxy/telemt
1 parent 2ad6721 commit d702dc0

40 files changed

Lines changed: 787 additions & 506 deletions

client/core/configurators/xrayConfigurator.cpp

Lines changed: 200 additions & 44 deletions
Large diffs are not rendered by default.

client/core/configurators/xrayConfigurator.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,16 @@ class XrayConfigurator : public ConfiguratorBase
3030
bool appendNewClient,
3131
QString *outClientId = nullptr);
3232

33+
amnezia::ErrorCode writeServerConfigForSetup(const amnezia::ServerCredentials &credentials,
34+
amnezia::DockerContainer container,
35+
amnezia::ContainerConfig &containerConfig,
36+
const amnezia::DnsSettings &dnsSettings);
37+
3338
private:
39+
amnezia::ErrorCode readContainerKeyFile(amnezia::DockerContainer container,
40+
const amnezia::ServerCredentials &credentials,
41+
const QString &path, QString &out) const;
42+
3443
QString prepareServerConfig(const amnezia::ServerCredentials &credentials, amnezia::DockerContainer container, const amnezia::ContainerConfig &containerConfig,
3544
const amnezia::DnsSettings &dnsSettings,
3645
amnezia::ErrorCode &errorCode);

client/core/controllers/selfhosted/installController.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <QtConcurrent>
1212

1313
#include "core/configurators/configuratorBase.h"
14+
#include "core/configurators/xrayConfigurator.h"
1415
#include "core/utils/containerEnum.h"
1516
#include "core/utils/containers/containerUtils.h"
1617
#include "core/utils/protocolEnum.h"
@@ -152,6 +153,15 @@ ErrorCode InstallController::setupContainer(const ServerCredentials &credentials
152153
return e;
153154
qDebug().noquote() << "InstallController::setupContainer configureContainerWorker finished";
154155

156+
if (container == DockerContainer::Xray || container == DockerContainer::SSXray) {
157+
DnsSettings dnsSettings = { m_appSettingsRepository->primaryDns(), m_appSettingsRepository->secondaryDns() };
158+
XrayConfigurator xrayConfigurator(&sshSession);
159+
e = xrayConfigurator.writeServerConfigForSetup(credentials, container, config, dnsSettings);
160+
if (e)
161+
return e;
162+
qDebug().noquote() << "InstallController::setupContainer xray writeServerConfigForSetup finished";
163+
}
164+
155165
setupServerFirewall(credentials, sshSession);
156166
qDebug().noquote() << "InstallController::setupContainer setupServerFirewall finished";
157167

@@ -191,6 +201,9 @@ ErrorCode InstallController::updateServerConfig(const QString &serverId, DockerC
191201
SshSession sshSession;
192202

193203
bool reinstallRequired = isReinstallContainerRequired(container, oldConfig, newConfig);
204+
if (container == DockerContainer::Xray || container == DockerContainer::SSXray) {
205+
reinstallRequired = true;
206+
}
194207
qDebug() << "InstallController::updateServerConfig for container" << container << "reinstall required is" << reinstallRequired;
195208

196209
ErrorCode errorCode = ErrorCode::NoError;
@@ -404,6 +417,11 @@ ErrorCode InstallController::prepareContainerConfig(DockerContainer container, c
404417
}
405418

406419
if (ContainerUtils::containerService(container) != ServiceType::Other) {
420+
if ((container == DockerContainer::Xray || container == DockerContainer::SSXray)
421+
&& containerConfig.protocolConfig.hasClientConfig()) {
422+
return ErrorCode::NoError;
423+
}
424+
407425
Proto protocol = ContainerUtils::defaultProtocol(container);
408426

409427
DnsSettings dnsSettings = {
@@ -491,6 +509,12 @@ ErrorCode InstallController::buildContainerWorker(const ServerCredentials &crede
491509
if (stdOut.contains("have reached") && stdOut.contains("pull rate limit"))
492510
return ErrorCode::DockerPullRateLimit;
493511

512+
if (stdOut.contains("returned a non-zero code")
513+
|| stdOut.contains("failed to solve")
514+
|| stdOut.contains("Unable to find image")
515+
|| stdOut.contains("Couldn't connect to server"))
516+
return ErrorCode::ServerDockerFailedError;
517+
494518
return error;
495519
}
496520

@@ -515,6 +539,8 @@ ErrorCode InstallController::runContainerWorker(const ServerCredentials &credent
515539
return ErrorCode::ServerPortAlreadyAllocatedError;
516540
if (stdOut.contains("invalid publish"))
517541
return ErrorCode::ServerDockerFailedError;
542+
if (stdOut.contains("Unable to find image") || stdOut.contains("No such image"))
543+
return ErrorCode::ServerDockerFailedError;
518544

519545
return e;
520546
}

client/core/installers/installerBase.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,16 @@ ContainerConfig InstallerBase::createBaseConfig(DockerContainer container, int p
7878
case Proto::Xray:
7979
case Proto::SSXray: {
8080
XrayProtocolConfig xrayConfig;
81-
xrayConfig.serverConfig.port = portStr;
82-
xrayConfig.serverConfig.transportProto = transportProtoStr;
81+
XrayServerConfig &srv = xrayConfig.serverConfig;
82+
srv.port = portStr;
83+
srv.transportProto = transportProtoStr;
84+
srv.transport = protocols::xray::defaultTransport;
85+
srv.security = protocols::xray::defaultSecurity;
86+
srv.flow = protocols::xray::defaultFlow;
87+
srv.site = protocols::xray::defaultSite;
88+
srv.sni = protocols::xray::defaultSni;
89+
srv.fingerprint = protocols::xray::defaultFingerprint;
90+
srv.alpn = protocols::xray::defaultAlpn;
8391
config.protocolConfig = xrayConfig;
8492
break;
8593
}

client/core/installers/mtProxyInstaller.cpp

Lines changed: 91 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "core/utils/containerEnum.h"
44
#include "core/utils/containers/containerUtils.h"
5+
#include "core/utils/constants/protocolConstants.h"
56
#include "core/utils/protocolEnum.h"
67
#include "core/utils/selfhosted/sshSession.h"
78
#include "core/models/containerConfig.h"
@@ -20,6 +21,8 @@ namespace {
2021
constexpr QLatin1String kMtProxyClientJsonPath("/data/amnezia-mtproxy-client.json");
2122
constexpr QLatin1String kMtProxyClientJsonUploadPath("data/amnezia-mtproxy-client.json");
2223
constexpr QLatin1String kMtProxySecretPath("/data/secret");
24+
constexpr QLatin1String kMtProxyMetaPath("/data/mtproxy-meta");
25+
constexpr QLatin1String kMtProxyStartScriptPath("/opt/amnezia/start.sh");
2326
}
2427

2528
MtProxyInstaller::MtProxyInstaller(QObject *parent)
@@ -53,14 +56,98 @@ ErrorCode MtProxyInstaller::extractConfigFromContainer(DockerContainer container
5356
}
5457
}
5558

59+
static const QRegularExpression hex32(QStringLiteral("^[0-9a-fA-F]{32}$"));
60+
const auto addExtra = [&](const QString &s) {
61+
if (hex32.match(s).hasMatch() && !mt->additionalSecrets.contains(s)) {
62+
mt->additionalSecrets.append(s);
63+
}
64+
};
65+
5666
ErrorCode secretErr = ErrorCode::NoError;
5767
const QByteArray secretRaw =
5868
sshSession->getTextFileFromContainer(container, credentials, QString(kMtProxySecretPath), secretErr);
5969
const QString sec = QString::fromUtf8(secretRaw).trimmed();
60-
if (sec.length() == 32) {
61-
static const QRegularExpression hex32(QStringLiteral("^[0-9a-fA-F]{32}$"));
62-
if (hex32.match(sec).hasMatch()) {
63-
mt->secret = sec;
70+
if (sec.length() == 32 && hex32.match(sec).hasMatch()) {
71+
mt->secret = sec;
72+
}
73+
74+
bool metaRestored = false;
75+
ErrorCode metaErr = ErrorCode::NoError;
76+
const QByteArray metaRaw =
77+
sshSession->getTextFileFromContainer(container, credentials, QString(kMtProxyMetaPath), metaErr);
78+
if (metaErr == ErrorCode::NoError && !metaRaw.trimmed().isEmpty()) {
79+
QString mode, domain, workersMode, workers, natInternal, natExternal;
80+
bool natEnabled = false;
81+
const QList<QByteArray> lines = metaRaw.split('\n');
82+
for (const QByteArray &rawLine : lines) {
83+
const QString line = QString::fromUtf8(rawLine).trimmed();
84+
const int eq = line.indexOf('=');
85+
if (eq < 0) {
86+
continue;
87+
}
88+
const QString key = line.left(eq);
89+
const QString val = line.mid(eq + 1).trimmed();
90+
if (key == QLatin1String("mode")) mode = val;
91+
else if (key == QLatin1String("domain")) domain = val;
92+
else if (key == QLatin1String("tag")) { if (mt->tag.isEmpty()) mt->tag = val; }
93+
else if (key == QLatin1String("additional")) {
94+
for (const QString &s : val.split(',', Qt::SkipEmptyParts)) addExtra(s.trimmed());
95+
}
96+
else if (key == QLatin1String("workers_mode")) workersMode = val;
97+
else if (key == QLatin1String("workers")) workers = val;
98+
else if (key == QLatin1String("nat_enabled")) natEnabled = (val == QLatin1String("1"));
99+
else if (key == QLatin1String("nat_internal")) natInternal = val;
100+
else if (key == QLatin1String("nat_external")) natExternal = val;
101+
else if (key == QLatin1String("public_host")) { if (mt->publicHost.isEmpty()) mt->publicHost = val; }
102+
}
103+
if (!mode.isEmpty()) {
104+
mt->transportMode = mode;
105+
if (!domain.isEmpty()) mt->tlsDomain = domain;
106+
if (!workersMode.isEmpty()) mt->workersMode = workersMode;
107+
if (workersMode == QLatin1String(protocols::mtProxy::workersModeManual) && !workers.isEmpty()) {
108+
mt->workers = workers;
109+
}
110+
if (natEnabled) {
111+
mt->natEnabled = true;
112+
mt->natInternalIp = natInternal;
113+
mt->natExternalIp = natExternal;
114+
}
115+
metaRestored = true;
116+
}
117+
}
118+
if (!metaRestored) {
119+
ErrorCode startErr = ErrorCode::NoError;
120+
const QByteArray startRaw =
121+
sshSession->getTextFileFromContainer(container, credentials, QString(kMtProxyStartScriptPath), startErr);
122+
if (startErr == ErrorCode::NoError && !startRaw.trimmed().isEmpty()) {
123+
const QString start = QString::fromUtf8(startRaw);
124+
static const QRegularExpression modeRe(QStringLiteral("\\[ \"(standard|faketls)\" = \"faketls\" \\]"));
125+
const QRegularExpressionMatch m = modeRe.match(start);
126+
if (m.hasMatch()) {
127+
mt->transportMode = m.captured(1);
128+
if (m.captured(1) == QLatin1String(protocols::mtProxy::transportModeFakeTLS)) {
129+
static const QRegularExpression domRe(QStringLiteral("--domain ([A-Za-z0-9.\\-]+)"));
130+
const QRegularExpressionMatch dm = domRe.match(start);
131+
if (dm.hasMatch()) mt->tlsDomain = dm.captured(1);
132+
}
133+
}
134+
static const QRegularExpression tagRe(QStringLiteral("-P ([0-9a-fA-F]{32})"));
135+
const QRegularExpressionMatch tm = tagRe.match(start);
136+
if (tm.hasMatch() && mt->tag.isEmpty()) mt->tag = tm.captured(1);
137+
138+
static const QRegularExpression addRe(QStringLiteral("echo \"([0-9a-fA-F,]+)\" \\| tr ',' ' '"));
139+
const QRegularExpressionMatch am = addRe.match(start);
140+
if (am.hasMatch()) {
141+
for (const QString &s : am.captured(1).split(',', Qt::SkipEmptyParts)) addExtra(s.trimmed());
142+
}
143+
144+
static const QRegularExpression natRe(QStringLiteral("NAT_VALUE=\"([0-9.]+):([0-9.]+)\""));
145+
const QRegularExpressionMatch nm = natRe.match(start);
146+
if (nm.hasMatch()) {
147+
mt->natEnabled = true;
148+
mt->natInternalIp = nm.captured(1);
149+
mt->natExternalIp = nm.captured(2);
150+
}
64151
}
65152
}
66153

client/core/installers/telemtInstaller.cpp

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "core/utils/containerEnum.h"
44
#include "core/utils/containers/containerUtils.h"
5+
#include "core/utils/constants/protocolConstants.h"
56
#include "core/utils/selfhosted/sshSession.h"
67
#include "core/models/containerConfig.h"
78
#include "core/models/protocols/telemtProtocolConfig.h"
@@ -19,6 +20,7 @@ namespace {
1920
constexpr QLatin1String kTelemtClientJsonPath("/data/amnezia-telemt-client.json");
2021
constexpr QLatin1String kTelemtClientJsonUploadPath("data/amnezia-telemt-client.json");
2122
constexpr QLatin1String kTelemtSecretPath("/data/secret");
23+
constexpr QLatin1String kTelemtConfigTomlPath("/data/config.toml");
2224
}
2325

2426
TelemtInstaller::TelemtInstaller(QObject *parent) : InstallerBase(parent) {}
@@ -54,10 +56,83 @@ ErrorCode TelemtInstaller::extractConfigFromContainer(DockerContainer container,
5456
const QByteArray secretRaw =
5557
sshSession->getTextFileFromContainer(container, credentials, QString(kTelemtSecretPath), secretErr);
5658
const QString sec = QString::fromUtf8(secretRaw).trimmed();
57-
if (sec.length() == 32) {
58-
static const QRegularExpression hex32(QStringLiteral("^[0-9a-fA-F]{32}$"));
59-
if (hex32.match(sec).hasMatch()) {
60-
tc->secret = sec;
59+
static const QRegularExpression hex32(QStringLiteral("^[0-9a-fA-F]{32}$"));
60+
if (sec.length() == 32 && hex32.match(sec).hasMatch()) {
61+
tc->secret = sec;
62+
}
63+
64+
ErrorCode tomlErr = ErrorCode::NoError;
65+
const QByteArray tomlRaw =
66+
sshSession->getTextFileFromContainer(container, credentials, QString(kTelemtConfigTomlPath), tomlErr);
67+
if (tomlErr == ErrorCode::NoError && !tomlRaw.trimmed().isEmpty()) {
68+
QString section;
69+
bool userNameSet = false;
70+
const QList<QByteArray> lines = tomlRaw.split('\n');
71+
for (const QByteArray &rawLine : lines) {
72+
const QString line = QString::fromUtf8(rawLine).trimmed();
73+
if (line.isEmpty() || line.startsWith('#')) {
74+
continue;
75+
}
76+
if (line.startsWith('[')) {
77+
section = line;
78+
continue;
79+
}
80+
const int eq = line.indexOf('=');
81+
if (eq < 0) {
82+
continue;
83+
}
84+
const QString key = line.left(eq).trimmed();
85+
QString val = line.mid(eq + 1).trimmed();
86+
if (val.startsWith('"')) {
87+
const int last = val.lastIndexOf('"');
88+
val = (last > 0) ? val.mid(1, last - 1) : val.mid(1);
89+
} else {
90+
const int inlineComment = val.indexOf(QLatin1String(" #"));
91+
if (inlineComment >= 0) {
92+
val = val.left(inlineComment).trimmed();
93+
}
94+
}
95+
96+
if (section == QLatin1String("[access.users]")) {
97+
if (key.startsWith(QLatin1String("extra"))) {
98+
if (hex32.match(val).hasMatch() && !tc->additionalSecrets.contains(val)) {
99+
tc->additionalSecrets.append(val);
100+
}
101+
} else if (!userNameSet) {
102+
tc->userName = key;
103+
userNameSet = true;
104+
if (tc->secret.isEmpty() && hex32.match(val).hasMatch()) {
105+
tc->secret = val;
106+
}
107+
}
108+
continue;
109+
}
110+
111+
if (key == QLatin1String("tls") && section == QLatin1String("[general.modes]")) {
112+
tc->transportMode = (val == QLatin1String("true"))
113+
? QString::fromUtf8(protocols::telemt::transportModeFakeTLS)
114+
: QString::fromUtf8(protocols::telemt::transportModeStandard);
115+
} else if (key == QLatin1String("tls_domain") && section == QLatin1String("[censorship]")) {
116+
tc->tlsDomain = val;
117+
} else if (key == QLatin1String("mask") && section == QLatin1String("[censorship]")) {
118+
tc->maskEnabled = (val == QLatin1String("true"));
119+
} else if (key == QLatin1String("tls_emulation") && section == QLatin1String("[censorship]")) {
120+
tc->tlsEmulation = (val == QLatin1String("true"));
121+
} else if (key == QLatin1String("use_middle_proxy") && section == QLatin1String("[general]")) {
122+
tc->useMiddleProxy = (val == QLatin1String("true"));
123+
} else if (key == QLatin1String("middle_proxy_nat_ip") && section == QLatin1String("[general]")) {
124+
if (!val.isEmpty()) {
125+
tc->natExternalIp = val;
126+
tc->natEnabled = true;
127+
}
128+
} else if (key == QLatin1String("ad_tag") && section == QLatin1String("[general]") && tc->tag.isEmpty()) {
129+
tc->tag = val;
130+
} else if (key == QLatin1String("public_host") && section == QLatin1String("[general.links]")
131+
&& tc->publicHost.isEmpty()) {
132+
tc->publicHost = val;
133+
} else if (key == QLatin1String("port") && section == QLatin1String("[server]") && tc->port.isEmpty()) {
134+
tc->port = val;
135+
}
61136
}
62137
}
63138

0 commit comments

Comments
 (0)