Skip to content

SSL config does not watch for symlink file changes #44807

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # 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 !

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

No branches or pull requests

5 participants