This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch 9.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/9.0.x by this push: new 9c6830842a Fix BZ 69598 - release service account token if it changes 9c6830842a is described below commit 9c6830842a8514a645212bb5fc98aa49c56e016a Author: Mark Thomas <ma...@apache.org> AuthorDate: Thu Feb 27 08:49:20 2025 +0000 Fix BZ 69598 - release service account token if it changes --- .../cloud/KubernetesMembershipProvider.java | 41 ++++++++++++++++++++-- .../membership/cloud/LocalStrings.properties | 1 + .../membership/cloud/TokenStreamProvider.java | 9 ++++- webapps/docs/changelog.xml | 9 +++++ 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/java/org/apache/catalina/tribes/membership/cloud/KubernetesMembershipProvider.java b/java/org/apache/catalina/tribes/membership/cloud/KubernetesMembershipProvider.java index 4d4d35e5bd..59ff620b23 100644 --- a/java/org/apache/catalina/tribes/membership/cloud/KubernetesMembershipProvider.java +++ b/java/org/apache/catalina/tribes/membership/cloud/KubernetesMembershipProvider.java @@ -24,6 +24,8 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.nio.file.FileSystems; import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; @@ -43,8 +45,13 @@ import org.apache.tomcat.util.json.JSONParser; */ public class KubernetesMembershipProvider extends CloudMembershipProvider { + private static final Log log = LogFactory.getLog(KubernetesMembershipProvider.class); + private Path saTokenPath; + private FileTime saTokenLastModifiedTime; + + @Override public void start(int level) throws Exception { if ((level & MembershipService.MBR_RX) == 0) { @@ -80,8 +87,10 @@ public class KubernetesMembershipProvider extends CloudMembershipProvider { saTokenFile = "/var/run/secrets/kubernetes.io/serviceaccount/token"; } try { - byte[] bytes = Files.readAllBytes(FileSystems.getDefault().getPath(saTokenFile)); + saTokenPath = FileSystems.getDefault().getPath(saTokenFile); + byte[] bytes = Files.readAllBytes(saTokenPath); streamProvider = new TokenStreamProvider(new String(bytes, StandardCharsets.US_ASCII), caCertFile); + saTokenLastModifiedTime = Files.getLastModifiedTime(saTokenPath); } catch (IOException e) { log.error(sm.getString("kubernetesMembershipProvider.streamError"), e); } @@ -122,6 +131,7 @@ public class KubernetesMembershipProvider extends CloudMembershipProvider { heartbeat(); } + @Override public boolean stop(int level) throws Exception { try { @@ -131,12 +141,15 @@ public class KubernetesMembershipProvider extends CloudMembershipProvider { } } + @Override protected Member[] fetchMembers() { if (streamProvider == null) { return new Member[0]; } + reloadSaTokenIfChanged(); + List<MemberImpl> members = new ArrayList<>(); try (InputStream stream = streamProvider.openStream(url, headers, connectionTimeout, readTimeout); @@ -149,6 +162,31 @@ public class KubernetesMembershipProvider extends CloudMembershipProvider { return members.toArray(new Member[0]); } + + private void reloadSaTokenIfChanged() { + if (saTokenPath == null) { + // Service account token not being used. + return; + } + if (!Files.exists(saTokenPath)) { + // If the service account token is being used, this path should exist + log.warn(sm.getString("kubernetesMembershipProvider.serviceAccountTokenMissing", saTokenPath)); + return; + } + try { + FileTime oldSaTokenLastModifiedTime = saTokenLastModifiedTime; + saTokenLastModifiedTime = Files.getLastModifiedTime(saTokenPath); + // Use != to protect against clock issues + if (!saTokenLastModifiedTime.equals(oldSaTokenLastModifiedTime)) { + byte[] bytes = Files.readAllBytes(saTokenPath); + ((TokenStreamProvider)streamProvider).setToken(new String(bytes, StandardCharsets.US_ASCII)); + } + } catch (IOException e) { + log.error(sm.getString("kubernetesMembershipProvider.streamError"), e); + } + } + + @SuppressWarnings("unchecked") protected void parsePods(Reader reader, List<MemberImpl> members) { JSONParser parser = new JSONParser(reader); @@ -239,5 +277,4 @@ public class KubernetesMembershipProvider extends CloudMembershipProvider { log.error(sm.getString("kubernetesMembershipProvider.jsonError"), e); } } - } diff --git a/java/org/apache/catalina/tribes/membership/cloud/LocalStrings.properties b/java/org/apache/catalina/tribes/membership/cloud/LocalStrings.properties index da3463569f..f7e6e6e872 100644 --- a/java/org/apache/catalina/tribes/membership/cloud/LocalStrings.properties +++ b/java/org/apache/catalina/tribes/membership/cloud/LocalStrings.properties @@ -38,6 +38,7 @@ kubernetesMembershipProvider.jsonError=JSON error kubernetesMembershipProvider.memberError=Error creating member kubernetesMembershipProvider.noKey=Client certificate key was not specified in the environment kubernetesMembershipProvider.noNamespace=Namespace not set +kubernetesMembershipProvider.serviceAccountTokenMissing=Service account token not found at [{0}] kubernetesMembershipProvider.streamError=Failed to open stream tokenStream.failedConnection=Failed connection to [{0}] with token [{1}] diff --git a/java/org/apache/catalina/tribes/membership/cloud/TokenStreamProvider.java b/java/org/apache/catalina/tribes/membership/cloud/TokenStreamProvider.java index 0c66e5e4d9..c1efc7a6e6 100644 --- a/java/org/apache/catalina/tribes/membership/cloud/TokenStreamProvider.java +++ b/java/org/apache/catalina/tribes/membership/cloud/TokenStreamProvider.java @@ -29,6 +29,7 @@ public class TokenStreamProvider extends AbstractStreamProvider { private String token; private SSLSocketFactory factory; + TokenStreamProvider(String token, String caCertFile) throws Exception { this.token = token; TrustManager[] trustManagers = configureCaCert(caCertFile); @@ -37,11 +38,18 @@ public class TokenStreamProvider extends AbstractStreamProvider { this.factory = context.getSocketFactory(); } + @Override protected SSLSocketFactory getSocketFactory() { return factory; } + + protected void setToken(String token) { + this.token = token; + } + + @Override public InputStream openStream(String url, Map<String,String> headers, int connectTimeout, int readTimeout) throws IOException { @@ -56,5 +64,4 @@ public class TokenStreamProvider extends AbstractStreamProvider { throw new IOException(sm.getString("tokenStream.failedConnection", url, token), e); } } - } \ No newline at end of file diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 22e18dd546..5782bb666d 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -154,6 +154,15 @@ </fix> </changelog> </subsection> + <subsection name="Cluster"> + <changelog> + <add> + <bug>69598</bug>: Add detection of service account token changes to the + <code>KubernetesMembershipProvider</code> implementation and reload the + token if it changes. Based on a patch by Miroslav Jezbera. (markt) + </add> + </changelog> + </subsection> <subsection name="Other"> <changelog> <add> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org