Skip to content

SSL config does not watch for symlink file changes #44807

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

Closed
amitej27 opened this issue Mar 20, 2025 · 9 comments
Closed

SSL config does not watch for symlink file changes #44807

amitej27 opened this issue Mar 20, 2025 · 9 comments
Assignees
Labels
type: bug A general bug
Milestone

Comments

@amitej27
Copy link

Spring-boot version 3.3.9

I have configured a springboot application to use SSL bundles and enabled the hot reload functionality as below:

application.properties
#ssl bundle config
spring.ssl.bundle.pem.server.reload-on-update=true
spring.ssl.bundle.pem.server.keystore.certificate=file:/secret/tls.crt
spring.ssl.bundle.pem.server.keystore.private-key=file:/secret/tls.key
spring.ssl.bundle.pem.server.truststore.certificate=file:/secret/ca.crt
server.ssl.bundle=server 

Certificates are generated by certmanager and stored as kubernetes secrets which are then mounted into the application pods at the volume paths below:

volumeMounts:
  - mountPath: /secret
    name: volume-secret
    readOnly: true 
volumes:
  - name: volume-secret
    projected:
      defaultMode: 420
      sources:
      - secret:
          name: secret-tls-springboot-app

Observation:

  1. On start up, cert-manager provisions the certs in a Kubernetes Secret and they are mounted on the pod at /secret and the application starts up just fine.
  2. When the certificate is auto renewed by the cert-manager first time the springboot SSL hot reload functionality picks up the latest changes to the certs:
{"@timestamp":"2025-03-18T16:47:19.008+00:00","classname":"org.springframework.boot.web.embedded.tomcat.SslConnectorCustomizer","method":"update","file":"SslConnectorCustomizer.java","line":63,"thread":"ssl-bundle-watcher","level":"DEBUG","component":"springboot-app","message":"SSL Bundle for host _default_ has been updated, reloading SSL configuration","exception":""}
{"@timestamp":"2025-03-18T16:47:19.156+00:00","classname":"org.apache.juli.logging.DirectJDKLog","method":"log","file":"DirectJDKLog.java","line":173,"thread":"ssl-bundle-watcher","level":"INFO","component":"springboot-app","message":"Connector [https-jsse-nio-8443], TLS virtual host [_default_], certificate type [UNDEFINED] configured from keystore [/opt/dockeruser/.keystore] using alias [tomcat] with trust store [null]","exception":""}`
  1. When the certificate is auto renewed by cert-manager for a second time, the springboot hot reload functionality does not pick up the changes and application still refers to old certificates. No logs are printed and the ssl-bundle-watcher does not seem to be triggered.
    Question:

Why would the SSL hot reload functionality pick up the first change to the certificate files but not pick up the second one or any further changes?

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Mar 20, 2025
@wilkinsona
Copy link
Member

This looks very similar to the situation described in #43989 (comment).

@bclozel bclozel self-assigned this Mar 20, 2025
@bclozel
Copy link
Member

bclozel commented Mar 20, 2025

I think I'm missing something because I cannot reproduce this in our tests.

I've updated our org.springframework.boot.autoconfigure.ssl.FileWatcherTests with a new test:

	@Test
	void shouldTriggerOnManyConfigMapUpdates(@TempDir Path tempDir) throws Exception {
		Path configMap1 = createConfigMap(tempDir, "secret.txt");
		Path configMap2 = createConfigMap(tempDir, "secret.txt");
		Path data = tempDir.resolve("..data");
		Files.createSymbolicLink(data, configMap1);
		Path secretFile = tempDir.resolve("secret.txt");
		Files.createSymbolicLink(secretFile, data.resolve("secret.txt"));
		try {
			WaitingCallback callback = new WaitingCallback();
			this.fileWatcher.watch(Set.of(secretFile), callback);
			// first update
			Files.delete(data);
			Files.createSymbolicLink(data, configMap2);
			callback.expectChanges();

			// reset callback state
			callback.reset();

			// second update
			Files.delete(data);
			Files.createSymbolicLink(data, configMap1);
			callback.expectChanges();
		}
		finally {
			FileSystemUtils.deleteRecursively(configMap1);
			FileSystemUtils.deleteRecursively(configMap2);
			Files.delete(data);
			Files.delete(secretFile);
		}
	}

	Path createConfigMap(Path parentDir, String secretFileName) throws IOException {
		Path configMapFolder = parentDir.resolve(".." + UUID.randomUUID());
		Files.createDirectory(configMapFolder);
		Path secret = configMapFolder.resolve(secretFileName);
		Files.createFile(secret);
		return configMapFolder;
	}

	private static final class WaitingCallback implements Runnable {

		private CountDownLatch latch = new CountDownLatch(1);

		volatile boolean changed = false;

		@Override
		public void run() {
			this.changed = true;
			this.latch.countDown();
		}

		void expectChanges() throws InterruptedException {
			waitForChanges(true);
			assertThat(this.changed).as("changed").isTrue();
		}

		void expectNoChanges() throws InterruptedException {
			waitForChanges(false);
			assertThat(this.changed).as("changed").isFalse();
		}

		void waitForChanges(boolean fail) throws InterruptedException {
			if (!this.latch.await(5, TimeUnit.SECONDS)) {
				if (fail) {
					fail("Timeout while waiting for changes");
				}
			}
		}

		void reset() {
			this.latch = new CountDownLatch(1);
			this.changed = false;
		}

	}

Maybe this test is invalid and the file system changes we are simulating are not what they are in reality? Here's what the test is doing:

➜ mkdir app config1 config2
➜ touch config1/secret.txt config2/secret.txt
➜ ln -s config1 data
➜ tree
.
├── app
├── config1
│   └── secret.txt
├── config2
│   └── secret.txt
└── data -> config1

5 directories, 2 files

➜ echo "simulating config update"
simulating config update
➜ rm data
➜ ln -s config2 data
➜ tree
.
├── app
├── config1
│   └── secret.txt
├── config2
│   └── secret.txt
└── data -> config2

5 directories, 2 files

➜ echo "changes detected"
changes detected

Can you share a set of bash commands that would simulate the tree structure and runtime filesystem changes that happen?

@bclozel bclozel added the status: waiting-for-feedback We need additional information before we can continue label Mar 20, 2025
@amitej27
Copy link
Author

amitej27 commented Mar 21, 2025

From what I could find, Kubernetes uses rename2 to update symbolic links which says:
If oldpath refers to a symbolic link, the link is renamed; if newpath refers to a symbolic link, the link will be overwritten.

This is what I think is used: https://github.com/kubernetes/kubernetes/blob/71eb04295ad273d6de3bc484f2280c5c3954a4b3/pkg/volume/util/atomic_writer.go#L139

Reference: https://kubernetes.io/docs/tasks/inject-data-application/downward-api-volume-expose-pod-information/#store-pod-fields

Perhaps this test reproduces the issue?

        @Test
	void shouldTriggerOnSecretUpdates(@TempDir Path tempDir) throws Exception {
		Path configMap1 = createConfigMap(tempDir, "secret.txt");
		Path configMap2 = createConfigMap(tempDir, "secret.txt");
		Path configMap3 = createConfigMap(tempDir, "secret.txt");
		Path data = tempDir.resolve("..data");
		Files.createSymbolicLink(data, configMap1);
		Path secretFile = tempDir.resolve("secret.txt");
		Files.createSymbolicLink(secretFile, data.resolve("secret.txt"));
		try {
			WaitingCallback callback = new WaitingCallback();
			this.fileWatcher.watch(Set.of(secretFile), callback);
			Path dataTmp = tempDir.resolve("..data_tmp");
			Path tmpSymlink = Files.createSymbolicLink(dataTmp, configMap2);
			Files.move(tmpSymlink, data, StandardCopyOption.ATOMIC_MOVE);
			FileSystemUtils.deleteRecursively(configMap1);
			callback.expectChanges();

			// reset callback state
			callback.reset();

			// second update
			dataTmp = tempDir.resolve("..data_tmp");
			tmpSymlink = Files.createSymbolicLink(dataTmp, configMap3);
			Files.move(tmpSymlink, data, StandardCopyOption.ATOMIC_MOVE);
			FileSystemUtils.deleteRecursively(configMap2);
			callback.expectChanges();
		}
		finally {
			FileSystemUtils.deleteRecursively(configMap1);
			FileSystemUtils.deleteRecursively(configMap2);
			FileSystemUtils.deleteRecursively(configMap3);
			Files.delete(data);
			Files.delete(secretFile);
		}
	}

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Mar 21, 2025
@nosan
Copy link
Contributor

nosan commented Mar 21, 2025

I created a small application, installed cert-manager, and deployed the app to a local Kubernetes (Docker):

Registration:
        - /secret/..data/tls.key
        - /secret/..data/tls.crt
        - /secret/..data/ca.crt
        
Directory to watch:
        -  '/secret/..data'        

The directory structure and logs before running cmctl renew:

.
├── ..2025_03_23_10_37_48.3920370220
│   ├── ca.crt
│   ├── keystore.jks
│   ├── tls.crt
│   ├── tls.key
│   └── truststore.jks
├── ..data -> ..2025_03_23_10_37_48.3920370220
├── ca.crt -> ..data/ca.crt
├── keystore.jks -> ..data/keystore.jks
├── tls.crt -> ..data/tls.crt
├── tls.key -> ..data/tls.key
└── truststore.jks -> ..data/truststore.jks

2025-03-23T10:37:50.496Z  INFO 1 --- [           main] task.gh44807.Gh44807Application          : Starting Gh44807Application v0.0.1-SNAPSHOT using Java 17.0.7 with PID 1 (/app.jar started by root in /)
2025-03-23T10:37:50.500Z  INFO 1 --- [           main] task.gh44807.Gh44807Application          : No active profile set, falling back to 1 default profile: "default"
2025-03-23T10:37:51.014Z  INFO 1 --- [           main] o.s.boot.autoconfigure.ssl.FileWatcher   : Registering '/secret/..data'
2025-03-23T10:37:51.014Z  INFO 1 --- [           main] o.s.boot.autoconfigure.ssl.FileWatcher   : Registering '/secret/..data'
2025-03-23T10:37:51.015Z  INFO 1 --- [           main] o.s.boot.autoconfigure.ssl.FileWatcher   : Registering '/secret/..data'
2025-03-23T10:37:51.160Z  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (https)
2025-03-23T10:37:51.170Z  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2025-03-23T10:37:51.170Z  INFO 1 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.39]
2025-03-23T10:37:51.189Z  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2025-03-23T10:37:51.190Z  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 658 ms
2025-03-23T10:37:51.486Z  INFO 1 --- [           main] o.a.t.util.net.NioEndpoint.certificate   : Connector [https-jsse-nio-8080], TLS virtual host [_default_], certificate type [UNDEFINED] configured from keystore [/root/.keystore] using alias [tomcat] with trust store [null]
2025-03-23T10:37:51.493Z  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (https) with context path '/'
2025-03-23T10:37:51.502Z  INFO 1 --- [           main] task.gh44807.Gh44807Application          : Started Gh44807Application in 1.299 seconds (process running for 1.577)

First run cmctl renew

The directory structure and logs after running cmctl renew:

/secret # tree -a
.
├── ..2025_03_23_10_41_35.2553489679
│   ├── ca.crt
│   ├── keystore.jks
│   ├── tls.crt
│   ├── tls.key
│   └── truststore.jks
├── ..data -> ..2025_03_23_10_41_35.2553489679
├── ca.crt -> ..data/ca.crt
├── keystore.jks -> ..data/keystore.jks
├── tls.crt -> ..data/tls.crt
├── tls.key -> ..data/tls.key
└── truststore.jks -> ..data/truststore.jks

2025-03-23T10:41:35.204Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Changes detected
2025-03-23T10:41:35.205Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Event kind: ENTRY_DELETE, file affected: /secret/..data/truststore.jks
2025-03-23T10:41:35.219Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Event kind: ENTRY_DELETE, file affected: /secret/..data/ca.crt
2025-03-23T10:41:35.219Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Event kind: ENTRY_DELETE, file affected: /secret/..data/keystore.jks
2025-03-23T10:41:35.219Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Event kind: ENTRY_DELETE, file affected: /secret/..data/tls.crt
2025-03-23T10:41:35.219Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Event kind: ENTRY_DELETE, file affected: /secret/..data/tls.key
2025-03-23T10:41:45.248Z  INFO 1 --- [-bundle-watcher] o.a.t.util.net.NioEndpoint.certificate   : Connector [https-jsse-nio-8080], TLS virtual host [_default_], certificate type [UNDEFINED] configured from keystore [/root/.keystore] using alias [tomcat] with trust store [null]

Second run cmctl renew

The directory structure and logs after running cmctl renew:

/secret # tree -a
.
├── ..2025_03_23_10_48_49.1496188540
│   ├── ca.crt
│   ├── keystore.jks
│   ├── tls.crt
│   ├── tls.key
│   └── truststore.jks
├── ..data -> ..2025_03_23_10_48_49.1496188540
├── ca.crt -> ..data/ca.crt
├── keystore.jks -> ..data/keystore.jks
├── tls.crt -> ..data/tls.crt
├── tls.key -> ..data/tls.key
└── truststore.jks -> ..data/truststore.jks

2025-03-23T10:47:45.357Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : No changes detected
2025-03-23T10:47:55.361Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : No changes detected
2025-03-23T10:48:05.363Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : No changes detected
2025-03-23T10:49:35.389Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : No changes detected

The second update does not have any effect as the FileWatcher fails to detect the changes. Out of curiosity, I registered an additional watcher for the /secret directory.

Registration:
        - /secret/..data/tls.key
        - /secret/..data/tls.crt
        - /secret/..data/ca.crt
        
Directory to watch:
        -  '/secret'
        -  '/secret/..data'        

The directory structure and logs before running cmctl renew:

.
├── ..2025_03_23_10_56_26.3025522241
│   ├── ca.crt
│   ├── keystore.jks
│   ├── tls.crt
│   ├── tls.key
│   └── truststore.jks
├── ..data -> ..2025_03_23_10_56_26.3025522241
├── ca.crt -> ..data/ca.crt
├── keystore.jks -> ..data/keystore.jks
├── tls.crt -> ..data/tls.crt
├── tls.key -> ..data/tls.key
└── truststore.jks -> ..data/truststore.jks

2025-03-23T10:56:29.127Z  INFO 1 --- [           main] task.gh44807.Gh44807Application          : Starting Gh44807Application v0.0.1-SNAPSHOT using Java 17.0.7 with PID 1 (/app.jar started by root in /)
2025-03-23T10:56:29.130Z  INFO 1 --- [           main] task.gh44807.Gh44807Application          : No active profile set, falling back to 1 default profile: "default"
2025-03-23T10:56:29.558Z  INFO 1 --- [           main] o.s.boot.autoconfigure.ssl.FileWatcher   : Registering [/secret/..data/tls.key, /secret/..data/tls.crt, /secret/..data/ca.crt]
2025-03-23T10:56:29.558Z  INFO 1 --- [           main] o.s.boot.autoconfigure.ssl.FileWatcher   : Registering '/secret/..data'
2025-03-23T10:56:29.559Z  INFO 1 --- [           main] o.s.boot.autoconfigure.ssl.FileWatcher   : Registering '/secret'
2025-03-23T10:56:29.559Z  INFO 1 --- [           main] o.s.boot.autoconfigure.ssl.FileWatcher   : Registering '/secret/..data'
2025-03-23T10:56:29.559Z  INFO 1 --- [           main] o.s.boot.autoconfigure.ssl.FileWatcher   : Registering '/secret'
2025-03-23T10:56:29.559Z  INFO 1 --- [           main] o.s.boot.autoconfigure.ssl.FileWatcher   : Registering '/secret/..data'
2025-03-23T10:56:29.559Z  INFO 1 --- [           main] o.s.boot.autoconfigure.ssl.FileWatcher   : Registering '/secret'
2025-03-23T10:56:29.705Z  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (https)
2025-03-23T10:56:29.716Z  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2025-03-23T10:56:29.716Z  INFO 1 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.39]
2025-03-23T10:56:29.737Z  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2025-03-23T10:56:29.738Z  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 580 ms
2025-03-23T10:56:30.015Z  INFO 1 --- [           main] o.a.t.util.net.NioEndpoint.certificate   : Connector [https-jsse-nio-8080], TLS virtual host [_default_], certificate type [UNDEFINED] configured from keystore [/root/.keystore] using alias [tomcat] with trust store [null]
2025-03-23T10:56:30.024Z  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (https) with context path '/'
2025-03-23T10:56:30.032Z  INFO 1 --- [           main] task.gh44807.Gh44807Application          : Started Gh44807Application in 1.157 seconds (process running for 1.424)

First run cmctl renew

The directory structure and logs after running cmctl renew:


/secret # tree -a
.
├── ..2025_03_23_11_03_05.2855552273
│   ├── ca.crt
│   ├── keystore.jks
│   ├── tls.crt
│   ├── tls.key
│   └── truststore.jks
├── ..data -> ..2025_03_23_11_03_05.2855552273
├── ca.crt -> ..data/ca.crt
├── keystore.jks -> ..data/keystore.jks
├── tls.crt -> ..data/tls.crt
├── tls.key -> ..data/tls.key
└── truststore.jks -> ..data/truststore.jks

2025-03-23T11:03:05.224Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Changes detected
2025-03-23T11:03:05.225Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Event kind: ENTRY_CREATE, file affected: /secret/..2025_03_23_11_03_05.2855552273
2025-03-23T11:03:05.228Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Event kind: ENTRY_MODIFY, file affected: /secret/..2025_03_23_11_03_05.2855552273
2025-03-23T11:03:05.228Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Event kind: ENTRY_CREATE, file affected: /secret/..data_tmp
2025-03-23T11:03:05.228Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Event kind: ENTRY_DELETE, file affected: /secret/..data_tmp
2025-03-23T11:03:05.228Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Event kind: ENTRY_CREATE, file affected: /secret/..data
2025-03-23T11:03:05.228Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Event kind: ENTRY_DELETE, file affected: /secret/..2025_03_23_10_56_26.3025522241
2025-03-23T11:03:05.228Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Changes detected
2025-03-23T11:03:05.229Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Event kind: ENTRY_DELETE, file affected: /secret/..data/truststore.jks
2025-03-23T11:03:05.229Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Event kind: ENTRY_DELETE, file affected: /secret/..data/ca.crt
2025-03-23T11:03:05.229Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Event kind: ENTRY_DELETE, file affected: /secret/..data/keystore.jks
2025-03-23T11:03:05.229Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Event kind: ENTRY_DELETE, file affected: /secret/..data/tls.crt
2025-03-23T11:03:05.229Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Event kind: ENTRY_DELETE, file affected: /secret/..data/tls.key
2025-03-23T11:03:15.232Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : No changes detected
2025-03-23T11:03:15.255Z  INFO 1 --- [-bundle-watcher] o.a.t.util.net.NioEndpoint.certificate   : Connector [https-jsse-nio-8080], TLS virtual host [_default_], certificate type [UNDEFINED] configured from keystore [/root/.keystore] using alias [tomcat] with trust store [null]
2025-03-23T11:03:25.254Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : No changes detected
2025-03-23T11:03:35.257Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : No changes detected

Second run cmctl renew

The directory structure and logs after running cmctl renew:

.
├── ..2025_03_23_11_07_03.3960558961
│  ├── ca.crt
│  ├── keystore.jks
│  ├── tls.crt
│  ├── tls.key
│  └── truststore.jks
├── ..data -> ..2025_03_23_11_07_03.3960558961
├── ca.crt -> ..data/ca.crt
├── keystore.jks -> ..data/keystore.jks
├── tls.crt -> ..data/tls.crt
├── tls.key -> ..data/tls.key
└── truststore.jks -> ..data/truststore.jks

2025-03-23T11:07:03.190Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Changes detected
2025-03-23T11:07:03.192Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Event kind: ENTRY_CREATE, file affected: /secret/..2025_03_23_11_07_03.3960558961
2025-03-23T11:07:03.193Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Event kind: ENTRY_MODIFY, file affected: /secret/..2025_03_23_11_07_03.3960558961
2025-03-23T11:07:03.194Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Event kind: ENTRY_CREATE, file affected: /secret/..data_tmp
2025-03-23T11:07:03.194Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Event kind: ENTRY_DELETE, file affected: /secret/..data_tmp
2025-03-23T11:07:03.195Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Event kind: ENTRY_CREATE, file affected: /secret/..data
2025-03-23T11:07:03.195Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : Event kind: ENTRY_DELETE, file affected: /secret/..2025_03_23_11_03_05.2855552273
2025-03-23T11:07:13.200Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : No changes detected
2025-03-23T11:07:23.204Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : No changes detected
2025-03-23T11:07:33.206Z  INFO 1 --- [-bundle-watcher] o.s.boot.autoconfigure.ssl.FileWatcher   : No changes detected

@nosan
Copy link
Contributor

nosan commented Mar 23, 2025

A branch with a test to replicate the issue. The issue does not occur on macOS but fails on both Linux and Windows.

@nosan
Copy link
Contributor

nosan commented Mar 23, 2025

This branch contains both test and fix.

@bclozel
Copy link
Member

bclozel commented Mar 25, 2025

This looks great @nosan , thanks a lot for looking into this.
If you agree I can cherry-pick your commits and use them to resolve this issue, unless you'd prefer submitting a dedicated PR for this. Let me know what works best for you!

@nosan
Copy link
Contributor

nosan commented Mar 25, 2025

Thanks, @bclozel
Feel free to cherry-pick I'm perfectly fine with it. 👍

@bclozel bclozel added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged status: feedback-provided Feedback has been provided labels Mar 25, 2025
@bclozel bclozel added this to the 3.3.11 milestone Mar 25, 2025
@bclozel bclozel changed the title SSL Hot Reload reloads certificates only once on Kubernetes SSL config does not watch for symlink file changes Mar 25, 2025
@bclozel
Copy link
Member

bclozel commented Mar 25, 2025

Thanks again @nosan !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

No branches or pull requests

5 participants