From 941de2fe43daffd4e1450fcb9e98994bd4a7ec82 Mon Sep 17 00:00:00 2001 From: naughtyGitCat Date: Fri, 11 Sep 2026 16:17:41 +0800 Subject: [PATCH] feat: add enable_client_ip_attr_trust to stop trusting client-reported __client_ip When enable_client_ip_checkout is on, rewrite_handshake_response_packet first writes the peer address detected by obproxy into client_ip_buf_, then lets a __client_ip connect attribute sent by the client overwrite it unconditionally. OceanBase Connector/J (2.4.0 ~ 2.4.19) always reports socket.getLocalAddress() in __client_ip. For an application running inside a container that is the container address (e.g. 172.18.0.7), so observer applies ob_tcp_invited_nodes and the user@host match against an address that never reaches the proxy, and the login fails with ERROR 1227 Access denied although the TCP peer is in the whitelist. It also means any client can claim an arbitrary source ip. Add config item enable_client_ip_attr_trust (default true, behaviour unchanged). When set to false, obproxy forwards the detected peer address and only accepts __client_ip from an upstream obproxy, identified by __mysql_client_type=__ob_proxy in the login packet, so proxy chaining keeps working. Ignored self-reported values are logged at DEBUG level. Co-Authored-By: Claude Fable 5.1 --- src/obproxy/obutils/ob_proxy_config.h | 1 + src/obproxy/proxy/mysql/ob_mysql_transact.cpp | 1 + .../mysqllib/ob_mysql_config_processor.cpp | 4 +- .../mysqllib/ob_mysql_config_processor.h | 1 + .../mysqllib/ob_mysql_packet_rewriter.cpp | 40 ++++++++++++++++--- .../proxy/mysqllib/ob_mysql_packet_rewriter.h | 1 + 6 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/obproxy/obutils/ob_proxy_config.h b/src/obproxy/obutils/ob_proxy_config.h index ded12f80..e01eb05d 100644 --- a/src/obproxy/obutils/ob_proxy_config.h +++ b/src/obproxy/obutils/ob_proxy_config.h @@ -213,6 +213,7 @@ class ObProxyConfig : public common::ObCommonConfig DEF_BOOL(enable_cluster_checkout, "true", "if enable cluster checkout, proxy will send cluster name when login and server will check it", CFG_NO_NEED_REBOOT, CFG_SECTION_OBPROXY, CFG_VISIBLE_LEVEL_USER, CFG_MULTI_LEVEL_GLOBAL); DEF_BOOL(enable_proxy_scramble, "false", "if enable proxy scramble, proxy will send client its variable scramble num, not support old observer", CFG_NO_NEED_REBOOT, CFG_SECTION_OBPROXY, CFG_VISIBLE_LEVEL_SYS, CFG_MULTI_LEVEL_GLOBAL); DEF_BOOL(enable_client_ip_checkout, "true", "if enabled, proxy send client ip when login", CFG_NO_NEED_REBOOT, CFG_SECTION_OBPROXY, CFG_VISIBLE_LEVEL_USER, CFG_MULTI_LEVEL_GLOBAL); + DEF_BOOL(enable_client_ip_attr_trust, "true", "if enabled, proxy trusts the __client_ip connect attribute reported by the client itself and forwards it to observer; if disabled, proxy forwards the peer address it detected, and only accepts __client_ip from an upstream obproxy (__mysql_client_type=__ob_proxy). Only effective when enable_client_ip_checkout is true", CFG_NO_NEED_REBOOT, CFG_SECTION_OBPROXY, CFG_VISIBLE_LEVEL_USER, CFG_MULTI_LEVEL_GLOBAL); //connection related DEF_INT(connect_observer_max_retries, "3", "[2,5]", "max retries to do connect", CFG_NO_NEED_REBOOT, CFG_SECTION_OBPROXY, CFG_VISIBLE_LEVEL_USER, CFG_MULTI_LEVEL_GLOBAL); diff --git a/src/obproxy/proxy/mysql/ob_mysql_transact.cpp b/src/obproxy/proxy/mysql/ob_mysql_transact.cpp index 92ec1226..15d91ad3 100644 --- a/src/obproxy/proxy/mysql/ob_mysql_transact.cpp +++ b/src/obproxy/proxy/mysql/ob_mysql_transact.cpp @@ -5241,6 +5241,7 @@ inline int ObMysqlTransact::handle_oceanbase_handshake_pkt(ObTransState &s, uint param.use_ob_protocol_v2_compress_ = (s.sm_->compression_algorithm_.level_ != 0 && use_ob_protocol_v2); param.use_ssl_ = use_ssl; param.enable_client_ip_checkout_ = enable_client_ip_checkout; + param.enable_client_ip_attr_trust_ = s.mysql_config_params_->enable_client_ip_attr_trust_; param.cs_id_version_ = client_session->get_cs_id_version(); param.enable_full_link_trace_ = s.sm_->enable_full_link_trace_; if (is_binlog_request(s) && OB_NOT_NULL(s.sm_->multi_level_config_)) { diff --git a/src/obproxy/proxy/mysqllib/ob_mysql_config_processor.cpp b/src/obproxy/proxy/mysqllib/ob_mysql_config_processor.cpp index b45eede0..6fbeb024 100644 --- a/src/obproxy/proxy/mysqllib/ob_mysql_config_processor.cpp +++ b/src/obproxy/proxy/mysqllib/ob_mysql_config_processor.cpp @@ -82,6 +82,7 @@ ObMysqlConfigParams::ObMysqlConfigParams() enable_bad_route_reject_(false), enable_cluster_checkout_(true), enable_client_ip_checkout_(true), + enable_client_ip_attr_trust_(true), enable_proxy_scramble_(false), enable_compression_protocol_(false), enable_ob_protocol_v2_(false), @@ -189,6 +190,7 @@ int ObMysqlConfigParams::assign_config(const ObProxyConfig &proxy_config) CONFIG_ITEM_ASSIGN(enable_bad_route_reject); CONFIG_ITEM_ASSIGN(enable_cluster_checkout); CONFIG_ITEM_ASSIGN(enable_client_ip_checkout); + CONFIG_ITEM_ASSIGN(enable_client_ip_attr_trust); CONFIG_ITEM_ASSIGN(enable_proxy_scramble); CONFIG_ITEM_ASSIGN(enable_compression_protocol); CONFIG_ITEM_ASSIGN(enable_ob_protocol_v2); @@ -347,7 +349,7 @@ DEF_TO_STRING(ObMysqlConfigParams) K_(proxy_service_mode), K_(server_routing_mode), K_(proxy_id), K_(proxy_idc_name), K_(client_max_memory_size), K_(enable_cpu_isolate), K_(default_inactivity_timeout), K_(enable_partition_table_route), K_(enable_pl_route), - K_(enable_cluster_checkout), K_(enable_client_ip_checkout), K_(enable_proxy_scramble), + K_(enable_cluster_checkout), K_(enable_client_ip_checkout), K_(enable_client_ip_attr_trust), K_(enable_proxy_scramble), K_(enable_compression_protocol), K_(enable_ob_protocol_v2), K_(enable_reroute), K_(enable_weak_reroute), K_(enable_index_route), K_(enable_causal_order_read), K_(ip_listen_mode)); diff --git a/src/obproxy/proxy/mysqllib/ob_mysql_config_processor.h b/src/obproxy/proxy/mysqllib/ob_mysql_config_processor.h index 802ab58f..c2af13e1 100644 --- a/src/obproxy/proxy/mysqllib/ob_mysql_config_processor.h +++ b/src/obproxy/proxy/mysqllib/ob_mysql_config_processor.h @@ -109,6 +109,7 @@ struct ObMysqlConfigParams : public ObConfigInfo CfgBool enable_bad_route_reject_; CfgBool enable_cluster_checkout_; CfgBool enable_client_ip_checkout_; + CfgBool enable_client_ip_attr_trust_; CfgBool enable_proxy_scramble_; CfgBool enable_compression_protocol_; CfgBool enable_ob_protocol_v2_; diff --git a/src/obproxy/proxy/mysqllib/ob_mysql_packet_rewriter.cpp b/src/obproxy/proxy/mysqllib/ob_mysql_packet_rewriter.cpp index 9e23b3c1..0a482fde 100644 --- a/src/obproxy/proxy/mysqllib/ob_mysql_packet_rewriter.cpp +++ b/src/obproxy/proxy/mysqllib/ob_mysql_packet_rewriter.cpp @@ -34,7 +34,7 @@ int64_t ObHandshakeResponseParam::to_string(char *buf, const int64_t buf_len) co J_OBJ_START(); J_KV(K_(is_saved_login), K_(cluster_name), K_(proxy_scramble), K_(conn_id_buf), K_(proxy_conn_id_buf), K_(global_vars_version_buf), - K_(cap_buf), K_(proxy_version_buf), K_(client_ip_buf), + K_(cap_buf), K_(proxy_version_buf), K_(client_ip_buf), K_(enable_client_ip_attr_trust), K_(proxy_idc_name), K_(proxy_service_name), K_(proxy_failover_mode)); J_OBJ_END(); return pos; @@ -268,8 +268,32 @@ int ObMysqlPacketRewriter::rewrite_handshake_response_packet( tg_hsr.set_database(ObString::make_empty_string()); } - // find client_ip + // Decide whether the __client_ip connect attribute reported by the client can be trusted. + // param.client_ip_buf_ already holds the peer address detected by proxy (write_client_addr_buf). + // The self-reported value is only needed when the peer address is not the real client, i.e. + // when the client is an upstream obproxy (__mysql_client_type=__ob_proxy) or the deployment + // sits behind a NAT load balancer. Drivers such as OceanBase Connector/J report their local + // socket address in __client_ip; inside a container that is the container address, which then + // fails ob_tcp_invited_nodes / user host checks on observer. With enable_client_ip_attr_trust + // disabled, only an upstream obproxy may override the detected address. + bool trust_client_ip_attr = false; if (RUN_MODE_PROXY == g_run_mode && param.enable_client_ip_checkout_) { + if (param.enable_client_ip_attr_trust_) { + trust_client_ip_attr = true; + } else { + ObStringKV string_kv; + for (int64_t i = 0; OB_SUCC(ret) && !trust_client_ip_attr && i < tg_hsr.get_connect_attrs().count(); ++i) { + string_kv = tg_hsr.get_connect_attrs().at(i); + if (0 == string_kv.key_.case_compare(OB_MYSQL_CLIENT_MODE) + && 0 == string_kv.value_.compare(OB_MYSQL_CLIENT_OBPROXY_MODE)) { + trust_client_ip_attr = true; + } + } + } + } + + // find client_ip + if (trust_client_ip_attr) { ObStringKV string_kv; for (int64_t i = 0; OB_SUCC(ret) && i < tg_hsr.get_connect_attrs().count(); ++i) { string_kv = tg_hsr.get_connect_attrs().at(i); @@ -296,11 +320,15 @@ int ObMysqlPacketRewriter::rewrite_handshake_response_packet( if (OB_FAIL(tg_hsr.get_connect_attrs().push_back(kv))) { LOG_WDIAG("fail push back transparent transmit connect attrs", K(kv), K(ret)); } else { /* succ */ } - } else if (!find_client_ip - && param.enable_client_ip_checkout_ + } else if (!find_client_ip && 0 == kv.key_.case_compare(OB_MYSQL_CLIENT_IP) - && !kv.value_.empty()){ - snprintf(param.client_ip_buf_, MAX_IP_ADDR_LENGTH, "%.*s", kv.value_.length(), kv.value_.ptr()); + && !kv.value_.empty()) { + if (trust_client_ip_attr) { + snprintf(param.client_ip_buf_, MAX_IP_ADDR_LENGTH, "%.*s", kv.value_.length(), kv.value_.ptr()); + } else { + LOG_DEBUG("ignore client reported __client_ip, use detected peer address instead", + "reported_client_ip", kv.value_, "detected_client_ip", param.client_ip_buf_); + } find_client_ip = true; } else { /* do nothing */ } } diff --git a/src/obproxy/proxy/mysqllib/ob_mysql_packet_rewriter.h b/src/obproxy/proxy/mysqllib/ob_mysql_packet_rewriter.h index 423e2696..9e3f3a51 100644 --- a/src/obproxy/proxy/mysqllib/ob_mysql_packet_rewriter.h +++ b/src/obproxy/proxy/mysqllib/ob_mysql_packet_rewriter.h @@ -75,6 +75,7 @@ struct ObHandshakeResponseParam int64_t cluster_id_; bool use_ssl_; bool enable_client_ip_checkout_; + bool enable_client_ip_attr_trust_; bool enable_full_link_trace_; ObClientSessionIDVersion cs_id_version_; common::ObString cluster_name_;