mirror of https://github.com/telemt/telemt.git
Add per-user ad_tag with global fallback and hot-reload
- Per-user ad_tag in [access.user_ad_tags], global fallback in general.ad_tag - User tag overrides global; if no user tag, general.ad_tag is used - Both general.ad_tag and user_ad_tags support hot-reload (no restart)
This commit is contained in:
parent
338636ede6
commit
bc432f06e2
|
|
@ -2087,7 +2087,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "telemt"
|
||||
version = "3.0.13"
|
||||
version = "3.1.3"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"anyhow",
|
||||
|
|
|
|||
|
|
@ -215,10 +215,12 @@ hello = "00000000000000000000000000000000"
|
|||
|
||||
```
|
||||
### Advanced
|
||||
#### Adtag
|
||||
To use channel advertising and usage statistics from Telegram, get Adtag from [@mtproxybot](https://t.me/mtproxybot), add this parameter to section `[General]`
|
||||
#### Adtag (per-user)
|
||||
To use channel advertising and usage statistics from Telegram, get an Adtag from [@mtproxybot](https://t.me/mtproxybot). Set it per user in `[access.user_ad_tags]` (32 hex chars):
|
||||
```toml
|
||||
ad_tag = "00000000000000000000000000000000" # Replace zeros to your adtag from @mtproxybot
|
||||
[access.user_ad_tags]
|
||||
username1 = "11111111111111111111111111111111" # Replace with your tag from @mtproxybot
|
||||
username2 = "22222222222222222222222222222222"
|
||||
```
|
||||
#### Listening and Announce IPs
|
||||
To specify listening address and/or address in links, add to section `[[server.listeners]]` of config.toml:
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@
|
|||
# === General Settings ===
|
||||
[general]
|
||||
use_middle_proxy = false
|
||||
# Global ad_tag fallback when user has no per-user tag in [access.user_ad_tags]
|
||||
# ad_tag = "00000000000000000000000000000000"
|
||||
# Per-user ad_tag in [access.user_ad_tags] (32 hex from @MTProxybot)
|
||||
|
||||
# === Log Level ===
|
||||
# Log level: debug | verbose | normal | silent
|
||||
|
|
|
|||
|
|
@ -4,21 +4,22 @@
|
|||
//!
|
||||
//! # What can be reloaded without restart
|
||||
//!
|
||||
//! | Section | Field | Effect |
|
||||
//! |-----------|-------------------------------|-----------------------------------|
|
||||
//! | `general` | `log_level` | Filter updated via `log_level_tx` |
|
||||
//! | `general` | `ad_tag` | Passed on next connection |
|
||||
//! | `general` | `middle_proxy_pool_size` | Passed on next connection |
|
||||
//! | `general` | `me_keepalive_*` | Passed on next connection |
|
||||
//! | `general` | `desync_all_full` | Applied immediately |
|
||||
//! | `general` | `update_every` | Applied to ME updater immediately |
|
||||
//! | `general` | `hardswap` | Applied on next ME map update |
|
||||
//! | `general` | `me_pool_drain_ttl_secs` | Applied on next ME map update |
|
||||
//! | `general` | `me_pool_min_fresh_ratio` | Applied on next ME map update |
|
||||
//! | `general` | `me_reinit_drain_timeout_secs`| Applied on next ME map update |
|
||||
//! | `general` | `telemetry` / `me_*_policy` | Applied immediately |
|
||||
//! | `network` | `dns_overrides` | Applied immediately |
|
||||
//! | `access` | All user/quota fields | Effective immediately |
|
||||
//! | Section | Field | Effect |
|
||||
//! |-----------|--------------------------------|------------------------------------------------|
|
||||
//! | `general` | `log_level` | Filter updated via `log_level_tx` |
|
||||
//! | `access` | `user_ad_tags` | Passed on next connection |
|
||||
//! | `general` | `ad_tag` | Passed on next connection (fallback per-user) |
|
||||
//! | `general` | `middle_proxy_pool_size` | Passed on next connection |
|
||||
//! | `general` | `me_keepalive_*` | Passed on next connection |
|
||||
//! | `general` | `desync_all_full` | Applied immediately |
|
||||
//! | `general` | `update_every` | Applied to ME updater immediately |
|
||||
//! | `general` | `hardswap` | Applied on next ME map update |
|
||||
//! | `general` | `me_pool_drain_ttl_secs` | Applied on next ME map update |
|
||||
//! | `general` | `me_pool_min_fresh_ratio` | Applied on next ME map update |
|
||||
//! | `general` | `me_reinit_drain_timeout_secs` | Applied on next ME map update |
|
||||
//! | `general` | `telemetry` / `me_*_policy` | Applied immediately |
|
||||
//! | `network` | `dns_overrides` | Applied immediately |
|
||||
//! | `access` | All user/quota fields | Effective immediately |
|
||||
//!
|
||||
//! Fields that require re-binding sockets (`server.port`, `censorship.*`,
|
||||
//! `network.*`, `use_middle_proxy`) are **not** applied; a warning is emitted.
|
||||
|
|
@ -207,14 +208,17 @@ fn log_changes(
|
|||
log_tx.send(new_hot.log_level.clone()).ok();
|
||||
}
|
||||
|
||||
if old_hot.ad_tag != new_hot.ad_tag {
|
||||
if old_hot.access.user_ad_tags != new_hot.access.user_ad_tags {
|
||||
info!(
|
||||
"config reload: ad_tag: {} → {}",
|
||||
old_hot.ad_tag.as_deref().unwrap_or("none"),
|
||||
new_hot.ad_tag.as_deref().unwrap_or("none"),
|
||||
"config reload: user_ad_tags updated ({} entries)",
|
||||
new_hot.access.user_ad_tags.len(),
|
||||
);
|
||||
}
|
||||
|
||||
if old_hot.ad_tag != new_hot.ad_tag {
|
||||
info!("config reload: general.ad_tag updated (applied on next connection)");
|
||||
}
|
||||
|
||||
if old_hot.dns_overrides != new_hot.dns_overrides {
|
||||
info!(
|
||||
"config reload: network.dns_overrides updated ({} entries)",
|
||||
|
|
|
|||
|
|
@ -532,7 +532,7 @@ impl ProxyConfig {
|
|||
)));
|
||||
}
|
||||
|
||||
if let Some(tag) = &self.general.ad_tag {
|
||||
for (user, tag) in &self.access.user_ad_tags {
|
||||
let zeros = "00000000000000000000000000000000";
|
||||
if !is_valid_ad_tag(tag) {
|
||||
return Err(ProxyError::Config(
|
||||
|
|
@ -540,7 +540,7 @@ impl ProxyConfig {
|
|||
));
|
||||
}
|
||||
if tag == zeros {
|
||||
warn!("ad_tag is all zeros; register a valid proxy tag via @MTProxybot to enable sponsored channel");
|
||||
warn!(user = %user, "user ad_tag is all zeros; register a valid proxy tag via @MTProxybot to enable sponsored channel");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -247,14 +247,15 @@ pub struct GeneralConfig {
|
|||
#[serde(default = "default_true")]
|
||||
pub use_middle_proxy: bool,
|
||||
|
||||
#[serde(default)]
|
||||
pub ad_tag: Option<String>,
|
||||
|
||||
/// Path to proxy-secret binary file (auto-downloaded if absent).
|
||||
/// Infrastructure secret from https://core.telegram.org/getProxySecret.
|
||||
#[serde(default = "default_proxy_secret_path")]
|
||||
pub proxy_secret_path: Option<String>,
|
||||
|
||||
/// Global ad_tag (32 hex chars from @MTProxybot). Fallback when user has no per-user tag in access.user_ad_tags.
|
||||
#[serde(default)]
|
||||
pub ad_tag: Option<String>,
|
||||
|
||||
/// Public IP override for middle-proxy NAT environments.
|
||||
/// When set, this IP is used in ME key derivation and RPC_PROXY_REQ "our_addr".
|
||||
#[serde(default)]
|
||||
|
|
@ -807,6 +808,10 @@ pub struct AccessConfig {
|
|||
#[serde(default = "default_access_users")]
|
||||
pub users: HashMap<String, String>,
|
||||
|
||||
/// Per-user ad_tag (32 hex chars from @MTProxybot).
|
||||
#[serde(default)]
|
||||
pub user_ad_tags: HashMap<String, String>,
|
||||
|
||||
#[serde(default)]
|
||||
pub user_max_tcp_conns: HashMap<String, usize>,
|
||||
|
||||
|
|
@ -833,6 +838,7 @@ impl Default for AccessConfig {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
users: default_access_users(),
|
||||
user_ad_tags: HashMap::new(),
|
||||
user_max_tcp_conns: HashMap::new(),
|
||||
user_expirations: HashMap::new(),
|
||||
user_data_quota: HashMap::new(),
|
||||
|
|
|
|||
|
|
@ -448,7 +448,7 @@ async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
|
|||
info!("Middle-proxy STUN probing disabled by network.stun_use=false");
|
||||
}
|
||||
|
||||
// ad_tag (proxy_tag) for advertising
|
||||
// Global ad_tag (pool default). Used when user has no per-user tag in access.user_ad_tags.
|
||||
let proxy_tag = config
|
||||
.general
|
||||
.ad_tag
|
||||
|
|
|
|||
|
|
@ -238,7 +238,22 @@ where
|
|||
stats.increment_user_connects(&user);
|
||||
stats.increment_user_curr_connects(&user);
|
||||
|
||||
let proto_flags = proto_flags_for_tag(proto_tag, me_pool.has_proxy_tag());
|
||||
// Per-user ad_tag from access.user_ad_tags; fallback to general.ad_tag (hot-reloadable)
|
||||
let user_tag: Option<Vec<u8>> = config
|
||||
.access
|
||||
.user_ad_tags
|
||||
.get(&user)
|
||||
.and_then(|s| hex::decode(s).ok())
|
||||
.filter(|v| v.len() == 16);
|
||||
let global_tag: Option<Vec<u8>> = config
|
||||
.general
|
||||
.ad_tag
|
||||
.as_ref()
|
||||
.and_then(|s| hex::decode(s).ok())
|
||||
.filter(|v| v.len() == 16);
|
||||
let effective_tag = user_tag.or(global_tag);
|
||||
|
||||
let proto_flags = proto_flags_for_tag(proto_tag, effective_tag.is_some());
|
||||
debug!(
|
||||
trace_id = format_args!("0x{:016x}", trace_id),
|
||||
user = %user,
|
||||
|
|
@ -256,6 +271,7 @@ where
|
|||
|
||||
let (c2me_tx, mut c2me_rx) = mpsc::channel::<C2MeCommand>(C2ME_CHANNEL_CAPACITY);
|
||||
let me_pool_c2me = me_pool.clone();
|
||||
let effective_tag = effective_tag;
|
||||
let c2me_sender = tokio::spawn(async move {
|
||||
let mut sent_since_yield = 0usize;
|
||||
while let Some(cmd) = c2me_rx.recv().await {
|
||||
|
|
@ -268,6 +284,7 @@ where
|
|||
translated_local_addr,
|
||||
&payload,
|
||||
flags,
|
||||
effective_tag.as_deref(),
|
||||
).await?;
|
||||
sent_since_yield = sent_since_yield.saturating_add(1);
|
||||
if should_yield_c2me_sender(sent_since_yield, !c2me_rx.is_empty()) {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ use rand::seq::SliceRandom;
|
|||
use super::registry::ConnMeta;
|
||||
|
||||
impl MePool {
|
||||
/// Send RPC_PROXY_REQ. `tag_override`: per-user ad_tag (from access.user_ad_tags); if None, uses pool default.
|
||||
pub async fn send_proxy_req(
|
||||
self: &Arc<Self>,
|
||||
conn_id: u64,
|
||||
|
|
@ -26,13 +27,15 @@ impl MePool {
|
|||
our_addr: SocketAddr,
|
||||
data: &[u8],
|
||||
proto_flags: u32,
|
||||
tag_override: Option<&[u8]>,
|
||||
) -> Result<()> {
|
||||
let tag = tag_override.or(self.proxy_tag.as_deref());
|
||||
let payload = build_proxy_req_payload(
|
||||
conn_id,
|
||||
client_addr,
|
||||
our_addr,
|
||||
data,
|
||||
self.proxy_tag.as_deref(),
|
||||
tag,
|
||||
proto_flags,
|
||||
);
|
||||
let meta = ConnMeta {
|
||||
|
|
|
|||
Loading…
Reference in New Issue