-
Notifications
You must be signed in to change notification settings - Fork 3.9k
[improvement](fe) add fe_meta_auth_token for FE meta-service internal HTTP auth #65551
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -484,7 +484,7 @@ public class Config extends ConfigBase { | |
| + "starts for the first time. You can also specify one."}) | ||
| public static int cluster_id = -1; | ||
|
|
||
| @ConfField(description = {"Cluster token used for internal authentication."}) | ||
| @ConfField(sensitive = true, description = {"Cluster token used for internal authentication."}) | ||
| public static String auth_token = ""; | ||
|
|
||
| @ConfField(mutable = true, masterOnly = true, | ||
|
|
@@ -818,6 +818,18 @@ public class Config extends ConfigBase { | |
| // check token when download image file. | ||
| @ConfField public static boolean enable_token_check = true; | ||
|
|
||
| @ConfField(sensitive = true, description = {"Cluster token for FE meta-service internal HTTP authentication. " | ||
| + "When set (non-empty), FE meta-service endpoints (such as image/role/check/put/journal_id) " | ||
| + "additionally require the caller to present a matching token header, on top of the existing " | ||
| + "node-host check. Empty (default) keeps the legacy behavior of node-host check only, so " | ||
| + "existing clusters and rolling upgrades are unaffected. Must be identical on all FEs and " | ||
| + "provisioned in fe.conf before enabling, otherwise FEs will reject each other.", | ||
| "FE meta-service 内部 HTTP 鉴权使用的集群 token。设置(非空)后,meta-service 端点(如 " | ||
| + "image/role/check/put/journal_id)在原有 node-host 校验之上,额外要求调用方携带匹配的 token 头。" | ||
| + "为空(默认)时维持仅 node-host 校验的旧行为,存量集群与滚动升级不受影响。必须在所有 FE 上取值一致," | ||
| + "并在启用前写入 fe.conf,否则 FE 之间会互相拒绝。"}) | ||
| public static String fe_meta_auth_token = ""; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This token is added as a regular |
||
|
|
||
| /** | ||
| * Set to true if you deploy Palo using thirdparty deploy manager | ||
| * Valid options are: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -52,6 +52,10 @@ public class ConfigBase { | |
|
|
||
| boolean masterOnly() default false; | ||
|
|
||
| // If true, the value is a secret (e.g. a token or password) and is masked in every | ||
| // config dump API (Config.dump / getConfigInfo), so it is never returned in plaintext. | ||
| boolean sensitive() default false; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because this new masking is opt-in, marking only |
||
|
|
||
| String comment() default ""; | ||
|
|
||
| VariableAnnotation varType() default VariableAnnotation.NONE; | ||
|
|
@@ -191,13 +195,26 @@ private void warnUnknownConfigKeys(String confFile, Properties props) { | |
| } | ||
| } | ||
|
|
||
| // Placeholder returned instead of a sensitive config's real value in any dump API. | ||
| public static final String SENSITIVE_CONF_MASK = "********"; | ||
|
|
||
| // Mask the value of a sensitive config (a non-empty secret) so it is never dumped in plaintext. | ||
| // An empty value is left as-is: it reveals nothing and keeps "unset" visible. | ||
| private static String maskIfSensitive(Field field, String value) { | ||
| ConfField anno = field.getAnnotation(ConfField.class); | ||
| if (anno != null && anno.sensitive() && !Strings.isNullOrEmpty(value)) { | ||
| return SENSITIVE_CONF_MASK; | ||
| } | ||
| return value; | ||
| } | ||
|
|
||
| public static HashMap<String, String> dump() { | ||
| HashMap<String, String> map = new HashMap<>(); | ||
| Field[] fields = confClass.getFields(); | ||
| for (Field f : fields) { | ||
| ConfField anno = f.getAnnotation(ConfField.class); | ||
| if (anno != null) { | ||
| map.put(f.getName(), getConfValue(f)); | ||
| map.put(f.getName(), maskIfSensitive(f, getConfValue(f))); | ||
| } | ||
| } | ||
| return map; | ||
|
|
@@ -441,6 +458,7 @@ public static synchronized List<List<String>> getConfigInfo(PatternMatcher match | |
| if (confKey.equals("sys_log_dir") && Strings.isNullOrEmpty(value)) { | ||
| value = System.getenv("DORIS_HOME") + "/log"; | ||
| } | ||
| value = maskIfSensitive(f, value); | ||
| config.add(value); | ||
| config.add(f.getType().getSimpleName()); | ||
| config.add(String.valueOf(confField.mutable())); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,8 +20,10 @@ | |
| import org.apache.doris.catalog.Env; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I remember there is another util like httpurlutil, InternalHttpsUtils.java
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. InternalHttpsUtils only provides the shared SSLContext/truststore for TLS; HttpURLUtil builds the JDK HttpURLConnection and adds the node-ident + token headers, delegating TLS to InternalHttpsUtils. They're layered, not duplicated. The token is an auth header on the checkFromValidFe-protected meta endpoints, all of which go through HttpURLUtil, and it is sent unconditionally so it rides over HTTPS as well — so InternalHttpsUtils is orthogonal to this change and HTTPS works. |
||
| import org.apache.doris.cloud.security.SecurityChecker; | ||
| import org.apache.doris.common.Config; | ||
| import org.apache.doris.httpv2.meta.MetaBaseAction; | ||
| import org.apache.doris.system.SystemInfoService.HostInfo; | ||
|
|
||
| import com.google.common.base.Strings; | ||
| import com.google.common.collect.Maps; | ||
| import org.apache.http.conn.ssl.NoopHostnameVerifier; | ||
|
|
||
|
|
@@ -50,6 +52,10 @@ public static HttpURLConnection getConnectionWithNodeIdent(String request) throw | |
| HostInfo selfNode = Env.getServingEnv().getSelfNode(); | ||
| conn.setRequestProperty(Env.CLIENT_NODE_HOST_KEY, selfNode.getHost()); | ||
| conn.setRequestProperty(Env.CLIENT_NODE_PORT_KEY, selfNode.getPort() + ""); | ||
| String token = Config.fe_meta_auth_token; | ||
| if (!Strings.isNullOrEmpty(token)) { | ||
| conn.setRequestProperty(MetaBaseAction.TOKEN, token); | ||
| } | ||
| return conn; | ||
| } catch (Exception e) { | ||
| throw new IOException(e); | ||
|
|
@@ -65,6 +71,10 @@ public static Map<String, String> getNodeIdentHeaders() throws IOException { | |
| HostInfo selfNode = Env.getServingEnv().getSelfNode(); | ||
| headers.put(Env.CLIENT_NODE_HOST_KEY, selfNode.getHost()); | ||
| headers.put(Env.CLIENT_NODE_PORT_KEY, selfNode.getPort() + ""); | ||
| String token = Config.fe_meta_auth_token; | ||
| if (!Strings.isNullOrEmpty(token)) { | ||
| headers.put(MetaBaseAction.TOKEN, token); | ||
| } | ||
| return headers; | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.