This is an automated email from the ASF dual-hosted git repository.

jamesnetherton pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git


The following commit(s) were added to refs/heads/main by this push:
     new 6d23795cb5 Harden Jolokia extension security defaults
6d23795cb5 is described below

commit 6d23795cb5ac615a016ba8af577b2e1ffa50eeb2
Author: James Netherton <[email protected]>
AuthorDate: Tue Aug 4 19:24:41 2026 +0100

    Harden Jolokia extension security defaults
    
    Co-authored-by: Claude Opus 4.6 <[email protected]>
---
 .../modules/ROOT/pages/migration-guide/3.39.0.adoc |  37 +++++
 docs/modules/ROOT/pages/migration-guide/index.adoc |   1 +
 .../ROOT/pages/reference/extensions/jolokia.adoc   | 121 +++++++++++++-
 .../jolokia/deployment/JolokiaProcessor.java       |  14 +-
 .../CamelJolokiaRestrictorFilePolicyTest.java      |  80 +++++++++
 ...CamelJolokiaRestrictorPolicyDelegationTest.java | 158 ++++++++++++++++++
 ...elJolokiaRestrictorRemoteAccessEnabledTest.java |  76 +++++++++
 .../jolokia/CamelJolokiaRestrictorTest.java        | 179 +++++++++++++++++++++
 .../jolokia/JolokiaDefaultHostLocalhostTest.java   |  47 ++++++
 .../JolokiaKubernetesClientAuthDisabledTest.java   |  43 +++++
 .../JolokiaKubernetesNoPrincipalWarningTest.java   |  59 +++++++
 .../jolokia/JolokiaKubernetesTlsFailureTest.java   |  42 +++++
 .../JolokiaKubernetesTlsLoopbackFallbackTest.java  |  42 +++++
 .../camel/quarkus/jolokia/JolokiaRecorderTest.java |  95 +++++++++++
 .../jolokia/JolokiaRemoteAccessDefaultTest.java}   |  37 ++---
 .../jolokia/JolokiaRemoteAccessEnabledTest.java}   |  38 ++---
 extensions/jolokia/runtime/src/main/doc/usage.adoc |  99 +++++++++++-
 .../jolokia/CamelQuarkusJolokiaLogHandler.java     |   2 +-
 .../camel/quarkus/jolokia/JolokiaRecorder.java     |  32 +++-
 .../jolokia/config/JolokiaRuntimeConfig.java       |  16 +-
 .../jolokia/restrictor/CamelJolokiaRestrictor.java | 108 ++++++++++++-
 .../quarkus/jolokia/util/JolokiaHostUtils.java     |  66 ++++++++
 .../component/jolokia/it/CustomRestrictor.java     |   4 +-
 .../jolokia/it/JolokiaRemoteAccessAllowedIT.java}  |  17 +-
 .../jolokia/it/JolokiaRemoteAccessAllowedTest.java |  62 +++++++
 .../quarkus/component/jolokia/it/JolokiaTest.java  |  24 +++
 26 files changed, 1412 insertions(+), 87 deletions(-)

diff --git a/docs/modules/ROOT/pages/migration-guide/3.39.0.adoc 
b/docs/modules/ROOT/pages/migration-guide/3.39.0.adoc
new file mode 100644
index 0000000000..a4462f9a7e
--- /dev/null
+++ b/docs/modules/ROOT/pages/migration-guide/3.39.0.adoc
@@ -0,0 +1,37 @@
+= Camel Quarkus 3.39.0 Migration Guide
+
+The following guide outlines how to adapt your code to changes that were made 
in Camel Quarkus 3.39.0.
+
+== Jolokia extension changes
+
+=== Default bind address changed to localhost
+
+The Jolokia agent HTTP server now binds to `localhost` by default in 
production mode (previously `0.0.0.0`). Dev and test modes already defaulted to 
`localhost`. Remote dev mode and WSL environments continue to default to 
`0.0.0.0`. This means Jolokia is only accessible from the local machine in 
production unless explicitly configured otherwise.
+
+=== Remote access blocked by default
+
+The default Camel Jolokia restrictor now blocks connections from non-loopback 
(remote) addresses. This provides defense-in-depth: even if the server host is 
configured to bind to all interfaces, remote requests are rejected unless the 
`remote-access-allowed` property is explicitly set to `true`.
+
+=== Cross-origin requests (CORS) blocked by default
+
+The default Camel Jolokia restrictor now denies all cross-origin requests from 
non-loopback origins. If you rely on browser-based monitoring tools connecting 
cross-origin, configure a 
https://jolokia.org/reference/html/manual/security.html[Jolokia access policy] 
(`jolokia-access.xml`) with a `<cors>` section, or use a custom restrictor. See 
the xref:reference/extensions/jolokia.adoc[Jolokia extension documentation] for 
details.
+
+=== CamelJolokiaRestrictor no longer extends AllowAllRestrictor
+
+`CamelJolokiaRestrictor` now implements `Restrictor` directly instead of 
extending Jolokia's `AllowAllRestrictor`. If you have a custom restrictor that 
extends `AllowAllRestrictor`, consider switching to `extends 
CamelJolokiaRestrictor` to inherit the secure defaults (remote access control, 
CORS blocking, MBean domain filtering, `jolokia-access.xml` policy delegation).
+
+=== Jolokia access policy (jolokia-access.xml) delegation
+
+The default Camel Jolokia restrictor now loads `jolokia-access.xml` from the 
classpath if present and delegates remote access, CORS, HTTP method, and 
request type checks to it for non-loopback requests. This allows fine-grained 
access control alongside the Camel MBean domain filtering. The policy location 
can be configured via 
`quarkus.camel.jolokia.additional-properties."policyLocation"`.
+
+=== Required configuration for Kubernetes remote management
+
+If you use tools such as https://hawt.io/[Hawtio] or 
https://github.com/hawtio/hawtio-online[hawtio-online] to connect to Jolokia 
running on Kubernetes or OpenShift, you must add the following configuration to 
`application.properties`.
+
+[source]
+----
+quarkus.camel.jolokia.server.host=0.0.0.0
+quarkus.camel.jolokia.remote-access-allowed=true
+----
+
+This is safe when combined with SSL client authentication (enabled by default 
in Kubernetes environments), which ensures only clients presenting a valid 
certificate can connect. Refer to the 
xref:reference/extensions/jolokia.adoc[Jolokia extension documentation] for 
full details.
diff --git a/docs/modules/ROOT/pages/migration-guide/index.adoc 
b/docs/modules/ROOT/pages/migration-guide/index.adoc
index 6a4f52a81f..cf05efe3e0 100644
--- a/docs/modules/ROOT/pages/migration-guide/index.adoc
+++ b/docs/modules/ROOT/pages/migration-guide/index.adoc
@@ -4,6 +4,7 @@ We do frequent releases, a release almost every month, and even 
though we strive
 
 Listed here are guides on how to migrate between major versions and anything 
of significance to watch for when upgrading from minor versions.
 
+* xref:migration-guide/3.39.0.adoc[Camel Quarkus 3.38.x to Camel Quarkus 
3.39.0 migration guide]
 * xref:migration-guide/3.38.0.adoc[Camel Quarkus 3.36.x to Camel Quarkus 
3.38.0 migration guide]
 * xref:migration-guide/3.36.0.adoc[Camel Quarkus 3.35.x to Camel Quarkus 
3.36.0 migration guide]
 * xref:migration-guide/3.35.0.adoc[Camel Quarkus 3.33.x to Camel Quarkus 
3.35.0 migration guide]
diff --git a/docs/modules/ROOT/pages/reference/extensions/jolokia.adoc 
b/docs/modules/ROOT/pages/reference/extensions/jolokia.adoc
index b39c8db3ec..501b9ca3a9 100644
--- a/docs/modules/ROOT/pages/reference/extensions/jolokia.adoc
+++ b/docs/modules/ROOT/pages/reference/extensions/jolokia.adoc
@@ -44,11 +44,11 @@ This extension adds https://jolokia.org/[Jolokia] support 
to your application.
 [id="extensions-jolokia-usage-jolokia-http-endpoints"]
 === Jolokia HTTP endpoints
 
-In prod mode, Jolokia is accessible at the following URLs.
+Jolokia is accessible at the following URL.
 
-* http://0.0.0.0:8778/jolokia/
+* http://localhost:8778/jolokia/
 
-In dev and test modes Jolokia is bound only to `localhost`.
+By default, the Jolokia agent HTTP server binds to `localhost`. Remote dev 
mode and WSL environments default to `0.0.0.0`.
 
 If you want to disable Jolokia entirely, then add the following configuration 
to `application.properties`.
 
@@ -105,11 +105,11 @@ quarkus.camel.jolokia.register-camel-restrictor=false
 [id="extensions-jolokia-usage-create-a-custom-restrictor"]
 ==== Create a custom restrictor
 
-You can create your own restrictor class and register it with Jolokia.
+You can create your own restrictor class and register it with Jolokia. 
Extending `CamelJolokiaRestrictor` inherits the secure defaults (remote access 
control, CORS blocking, MBean domain filtering).
 
 [source,java]
 ----
-public class CustomRestrictor extends AllowAllRestrictor {
+public class CustomRestrictor extends CamelJolokiaRestrictor {
     // Override methods to apply custom restrictions
 }
 ----
@@ -149,13 +149,106 @@ This functionality can be disabled by adding the 
following configuration to `app
 quarkus.camel.jolokia.kubernetes.client-authentication-enabled=false
 ----
 
-Note that if you choose to use 
https://github.com/hawtio/hawtio-online[hawtio-online] to connect to your 
running application, then you must configure the Jolokia client principal.
+[id="extensions-jolokia-usage-configuring-jolokia-for-remote-management-tools"]
+==== Configuring Jolokia for remote management tools
+
+Tools such as https://hawt.io/[Hawtio] and 
https://github.com/hawtio/hawtio-online[hawtio-online] connect to Jolokia via 
the pod IP address, which is a non-loopback address. By default, the Jolokia 
agent binds to `localhost` and the default restrictor blocks connections from 
non-loopback addresses.
+
+To allow remote management tools to connect to Jolokia in Kubernetes, add the 
following configuration to `application.properties`.
+
+[source]
+----
+quarkus.camel.jolokia.server.host=0.0.0.0
+quarkus.camel.jolokia.remote-access-allowed=true
+----
+
+This is safe when combined with SSL client authentication (enabled by default 
in Kubernetes), which ensures that only clients presenting a valid certificate 
signed by the service CA can connect. If you use hawtio-online, you must also 
configure the Jolokia client principal.
 
 [source]
 ----
 quarkus.camel.jolokia.kubernetes.client-principal=cn=hawtio-online.hawtio.svc
 ----
 
+[id="extensions-jolokia-usage-security"]
+=== Security
+
+NOTE: Even when bound to `localhost`, Jolokia does not require authentication 
by default. On shared hosts or in container environments where localhost may be 
reachable from other containers, consider using a custom restrictor or Jolokia 
access policy to restrict access further.
+
+[id="extensions-jolokia-usage-network-binding"]
+==== Network binding
+
+By default, the Jolokia agent HTTP server binds to `localhost`, making it 
accessible only from the local machine. If you need to expose Jolokia to remote 
hosts, you must explicitly configure the bind address and enable remote access.
+
+[source]
+----
+quarkus.camel.jolokia.server.host=0.0.0.0
+quarkus.camel.jolokia.remote-access-allowed=true
+----
+
+[id="extensions-jolokia-usage-remote-access-control"]
+==== Remote access control
+
+The default Camel Jolokia restrictor blocks connections from non-loopback 
addresses. This is controlled by the 
`quarkus.camel.jolokia.remote-access-allowed` property (default `false`). When 
set to `false`, only connections from loopback addresses (e.g. `127.0.0.1`, 
`::1`) are accepted, regardless of the server bind address.
+
+[id="extensions-jolokia-usage-cross-origin-requests-cors"]
+==== Cross-origin requests (CORS)
+
+The default Camel Jolokia restrictor denies all cross-origin requests. If you 
need to allow specific origins (e.g. for a browser-based monitoring tool 
connecting cross-origin), you can use a 
https://jolokia.org/reference/html/manual/security.html[Jolokia access policy] 
(`jolokia-access.xml`) with a `<cors>` section, or configure a custom 
restrictor that overrides `isOriginAllowed()`.
+
+[id="extensions-jolokia-usage-jolokia-access-policy"]
+==== Jolokia access policy
+
+Jolokia supports fine-grained access control via an XML policy file 
(`jolokia-access.xml`). This can restrict access by IP address, CORS origin, 
HTTP method, and MBean operation.
+
+The default Camel restrictor automatically loads `jolokia-access.xml` from the 
classpath if present. Place the file in `src/main/resources/jolokia-access.xml` 
to use it alongside the Camel restrictor. The Camel restrictor always allows 
loopback connections. For non-loopback requests, it delegates remote access, 
CORS, allowed request types (`<commands>`), and HTTP method (`<http>`) checks 
to the policy file, while continuing to enforce MBean domain filtering.
+
+For example, the following `jolokia-access.xml` allows access from the 
`10.0.0.0/8` subnet, CORS requests from a monitoring tool, and restricts 
Jolokia to read-only operations over HTTP GET.
+
+[source,xml]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<restrict>
+    <remote>
+        <host>10.0.0.0/8</host>
+    </remote>
+    <cors>
+        <allow-origin>http://monitoring.example.com</allow-origin>
+    </cors>
+    <commands>
+        <command>read</command>
+        <command>list</command>
+        <command>version</command>
+        <command>search</command>
+    </commands>
+    <http>
+        <method>get</method>
+    </http>
+</restrict>
+----
+
+Refer to the https://jolokia.org/reference/html/manual/security.html[Jolokia 
security documentation] for the full policy file format.
+
+To load the policy file from a different location, such as a file path mounted 
from a Kubernetes ConfigMap, configure the `policyLocation` property.
+
+[source]
+----
+quarkus.camel.jolokia.additional-properties."policyLocation"=file:/etc/jolokia/jolokia-access.xml
+----
+
+As an alternative, you can disable the default Camel restrictor entirely and 
let Jolokia manage the policy file directly. This gives full control to 
`jolokia-access.xml` (including MBean-level rules) but loses the Camel MBean 
domain filtering.
+
+[source]
+----
+quarkus.camel.jolokia.register-camel-restrictor=false
+----
+
+If you are building a native executable, the policy file must be explicitly 
included as a native resource.
+
+[source]
+----
+quarkus.native.resources.includes=jolokia-access.xml
+----
+
 
 [id="extensions-jolokia-camel-quarkus-limitations"]
 == Camel Quarkus limitations
@@ -212,8 +305,8 @@ This can be done via `@Inject CamelQuarkusJolokiaServer` 
and then invoking the `
 a| 
[[quarkus-camel-jolokia-server-host]]`link:#quarkus-camel-jolokia-server-host[quarkus.camel.jolokia.server.host]`
 
 The host address to which the Jolokia agent HTTP server should bind.
-When unspecified, the default is localhost for dev and test mode.
-In prod mode the default is to bind to all interfaces at 0.0.0.0.
+When unspecified, the default is localhost in all modes except remote dev,
+where it defaults to 0.0.0.0.
 | `string`
 | 
 
@@ -271,6 +364,18 @@ following MBean domains.
 Note that this option has no effect if 
`quarkus.camel.jolokia.additional-properties."restrictorClass"` is set.
 | `boolean`
 | `true`
+
+a| 
[[quarkus-camel-jolokia-remote-access-allowed]]`link:#quarkus-camel-jolokia-remote-access-allowed[quarkus.camel.jolokia.remote-access-allowed]`
+
+When `true`, the default Camel Jolokia restrictor allows connections from 
non-loopback (remote) addresses.
+When `false` (the default), only connections from loopback addresses (e.g. 
127.0.0.1, ::1) are permitted.
+This provides defense-in-depth: even if the server host is configured to bind 
to all interfaces,
+remote requests are rejected unless this property is explicitly set to `true`.
+
+This option only takes effect when `register-camel-restrictor` is `true` and a 
custom restrictor class
+is not configured via 
`quarkus.camel.jolokia.additional-properties."restrictorClass"`.
+| `boolean`
+| `false`
 |===
 
 [.configuration-legend]
diff --git 
a/extensions/jolokia/deployment/src/main/java/org/apache/camel/quarkus/jolokia/deployment/JolokiaProcessor.java
 
b/extensions/jolokia/deployment/src/main/java/org/apache/camel/quarkus/jolokia/deployment/JolokiaProcessor.java
index 79e5a0285a..43c598f426 100644
--- 
a/extensions/jolokia/deployment/src/main/java/org/apache/camel/quarkus/jolokia/deployment/JolokiaProcessor.java
+++ 
b/extensions/jolokia/deployment/src/main/java/org/apache/camel/quarkus/jolokia/deployment/JolokiaProcessor.java
@@ -61,6 +61,7 @@ import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.StringHelper;
 import org.jboss.jandex.ClassInfo;
 import org.jboss.jandex.DotName;
+import org.jboss.jandex.IndexView;
 import org.jolokia.core.api.LogHandler;
 import org.jolokia.server.core.service.api.Restrictor;
 import org.jolokia.server.core.service.impl.QuietLogHandler;
@@ -161,8 +162,10 @@ public class JolokiaProcessor {
                         .methods(true)
                         .build());
 
+        IndexView index = combinedIndex.getIndex();
+
         // Register custom (non-OSGi) Jolokia Restrictor impls for reflection
-        Set<String> jolokiaRestrictorClasses = combinedIndex.getIndex()
+        Set<String> jolokiaRestrictorClasses = index
                 .getAllKnownImplementations(Restrictor.class)
                 .stream()
                 .map(ClassInfo::name)
@@ -170,11 +173,18 @@ public class JolokiaProcessor {
                 .filter(className -> 
!className.startsWith("org.jolokia.server.core.osgi"))
                 .collect(Collectors.toSet());
 
+        // Register the builtin CamelJolokiaRestrictor and subclasses
         jolokiaRestrictorClasses.add(CamelJolokiaRestrictor.class.getName());
+        index.getAllKnownSubclasses(CamelJolokiaRestrictor.class)
+                .stream()
+                .map(ClassInfo::name)
+                .map(DotName::toString)
+                .forEach(jolokiaRestrictorClasses::add);
+
         
reflectiveClass.produce(ReflectiveClassBuildItem.builder(jolokiaRestrictorClasses.toArray(new
 String[0])).build());
 
         // Register custom LogHandler classes for reflection
-        Set<String> jolokiaLogHandlerClasses = combinedIndex.getIndex()
+        Set<String> jolokiaLogHandlerClasses = index
                 .getAllKnownImplementations(LogHandler.class)
                 .stream()
                 .map(ClassInfo::name)
diff --git 
a/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/CamelJolokiaRestrictorFilePolicyTest.java
 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/CamelJolokiaRestrictorFilePolicyTest.java
new file mode 100644
index 0000000000..88d1005997
--- /dev/null
+++ 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/CamelJolokiaRestrictorFilePolicyTest.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.jolokia;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+
+import io.quarkus.test.QuarkusUnitTest;
+import org.apache.camel.quarkus.jolokia.restrictor.CamelJolokiaRestrictor;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class CamelJolokiaRestrictorFilePolicyTest {
+
+    private static final String JOLOKIA_ACCESS_XML = """
+            <?xml version="1.0" encoding="UTF-8"?>
+            <restrict>
+                <remote>
+                    <host>10.0.0.0/8</host>
+                </remote>
+            </restrict>
+            """;
+
+    static final File POLICY_FILE;
+
+    static {
+        try {
+            POLICY_FILE = Files.createTempFile("jolokia-access", 
".xml").toFile();
+            POLICY_FILE.deleteOnExit();
+            Files.writeString(POLICY_FILE.toPath(), JOLOKIA_ACCESS_XML);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @RegisterExtension
+    static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+            .overrideConfigKey("quarkus.camel.jolokia.server.port", "0")
+            
.overrideConfigKey("quarkus.camel.jolokia.additional-properties.policyLocation",
+                    POLICY_FILE.toURI().toString());
+
+    @Test
+    void filePolicyAllowsConfiguredSubnet() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertTrue(restrictor.isRemoteAccessAllowed("10.0.0.1"));
+        assertTrue(restrictor.isRemoteAccessAllowed("10.255.255.255"));
+    }
+
+    @Test
+    void filePolicyDeniesOtherAddresses() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertFalse(restrictor.isRemoteAccessAllowed("192.168.1.1"));
+        assertFalse(restrictor.isRemoteAccessAllowed("172.16.0.1"));
+    }
+
+    @Test
+    void loopbackStillAllowedRegardlessOfPolicy() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertTrue(restrictor.isRemoteAccessAllowed("127.0.0.1"));
+        assertTrue(restrictor.isRemoteAccessAllowed("::1"));
+    }
+}
diff --git 
a/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/CamelJolokiaRestrictorPolicyDelegationTest.java
 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/CamelJolokiaRestrictorPolicyDelegationTest.java
new file mode 100644
index 0000000000..22d9324dc5
--- /dev/null
+++ 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/CamelJolokiaRestrictorPolicyDelegationTest.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.jolokia;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import io.quarkus.test.QuarkusUnitTest;
+import org.apache.camel.quarkus.jolokia.restrictor.CamelJolokiaRestrictor;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.jolokia.server.core.util.HttpMethod;
+import org.jolokia.server.core.util.RequestType;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class CamelJolokiaRestrictorPolicyDelegationTest {
+
+    private static final String JOLOKIA_ACCESS_XML = """
+            <?xml version="1.0" encoding="UTF-8"?>
+            <restrict>
+                <remote>
+                    <host>10.0.0.0/8</host>
+                </remote>
+                <cors>
+                    <allow-origin>http://example.host.com</allow-origin>
+                </cors>
+                <commands>
+                    <command>list</command>
+                    <command>version</command>
+                    <command>search</command>
+                </commands>
+                <http>
+                    <method>get</method>
+                </http>
+            </restrict>
+            """;
+
+    @RegisterExtension
+    static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+            .overrideConfigKey("quarkus.camel.jolokia.server.port", "0")
+            .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
+                    .addAsResource(new StringAsset(JOLOKIA_ACCESS_XML), 
"jolokia-access.xml"));
+
+    @Test
+    void loopbackAlwaysAllowed() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertTrue(restrictor.isRemoteAccessAllowed("127.0.0.1"));
+        assertTrue(restrictor.isRemoteAccessAllowed("::1"));
+    }
+
+    @Test
+    void policyAllowedSubnetAllowed() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertTrue(restrictor.isRemoteAccessAllowed("10.0.0.1"));
+        assertTrue(restrictor.isRemoteAccessAllowed("10.255.255.255"));
+    }
+
+    @Test
+    void nonPolicyRemoteStillDenied() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertFalse(restrictor.isRemoteAccessAllowed("192.168.1.1"));
+        assertFalse(restrictor.isRemoteAccessAllowed("172.16.0.1"));
+    }
+
+    @Test
+    void policyAllowedCorsOriginAllowed() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertTrue(restrictor.isOriginAllowed("http://example.host.com";, 
false));
+    }
+
+    @Test
+    void nonPolicyCorsOriginDenied() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertFalse(restrictor.isOriginAllowed("http://untrusted.example";, 
false));
+    }
+
+    @Test
+    void nullOriginStillAllowed() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertTrue(restrictor.isOriginAllowed(null, false));
+        assertTrue(restrictor.isOriginAllowed(null, true));
+    }
+
+    @Test
+    void loopbackOriginStillAllowed() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertTrue(restrictor.isOriginAllowed("http://localhost:8080";, false));
+        assertTrue(restrictor.isOriginAllowed("http://127.0.0.1:9090";, false));
+    }
+
+    @Test
+    void policyAllowedRequestTypesAllowed() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertTrue(restrictor.isTypeAllowed(RequestType.LIST));
+        assertTrue(restrictor.isTypeAllowed(RequestType.VERSION));
+        assertTrue(restrictor.isTypeAllowed(RequestType.SEARCH));
+    }
+
+    @Test
+    void policyDeniedRequestTypesDenied() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertFalse(restrictor.isTypeAllowed(RequestType.READ));
+        assertFalse(restrictor.isTypeAllowed(RequestType.WRITE));
+        assertFalse(restrictor.isTypeAllowed(RequestType.EXEC));
+    }
+
+    @Test
+    void policyDeniedReadBlocksAttributeRead() throws 
MalformedObjectNameException {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        ObjectName camelMBean = new 
ObjectName("org.apache.camel:context=camel-1,type=context,name=\"camel-1\"");
+        assertFalse(restrictor.isAttributeReadAllowed(camelMBean, "Uptime"));
+    }
+
+    @Test
+    void policyDeniedWriteBlocksAttributeWrite() throws 
MalformedObjectNameException {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        ObjectName camelMBean = new 
ObjectName("org.apache.camel:context=camel-1,type=context,name=\"camel-1\"");
+        assertFalse(restrictor.isAttributeWriteAllowed(camelMBean, "Tracing"));
+    }
+
+    @Test
+    void policyDeniedExecBlocksOperation() throws MalformedObjectNameException 
{
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        ObjectName camelMBean = new 
ObjectName("org.apache.camel:context=camel-1,type=context,name=\"camel-1\"");
+        assertFalse(restrictor.isOperationAllowed(camelMBean, "getUptime"));
+    }
+
+    @Test
+    void policyAllowedHttpMethodAllowed() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertTrue(restrictor.isHttpMethodAllowed(HttpMethod.GET));
+    }
+
+    @Test
+    void policyDeniedHttpMethodDenied() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertFalse(restrictor.isHttpMethodAllowed(HttpMethod.POST));
+    }
+}
diff --git 
a/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/CamelJolokiaRestrictorRemoteAccessEnabledTest.java
 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/CamelJolokiaRestrictorRemoteAccessEnabledTest.java
new file mode 100644
index 0000000000..af213c184a
--- /dev/null
+++ 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/CamelJolokiaRestrictorRemoteAccessEnabledTest.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.jolokia;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import io.quarkus.test.QuarkusUnitTest;
+import org.apache.camel.quarkus.jolokia.restrictor.CamelJolokiaRestrictor;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class CamelJolokiaRestrictorRemoteAccessEnabledTest {
+
+    @RegisterExtension
+    static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+            .withEmptyApplication()
+            .overrideConfigKey("quarkus.camel.jolokia.remote-access-allowed", 
"true");
+
+    @Test
+    void allRemoteAddressesAllowed() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertTrue(restrictor.isRemoteAccessAllowed("192.168.1.1"));
+        assertTrue(restrictor.isRemoteAccessAllowed("10.0.0.1"));
+        assertTrue(restrictor.isRemoteAccessAllowed("172.16.0.1"));
+    }
+
+    @Test
+    void loopbackStillAllowed() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertTrue(restrictor.isRemoteAccessAllowed("127.0.0.1"));
+        assertTrue(restrictor.isRemoteAccessAllowed("::1"));
+    }
+
+    @Test
+    void allOriginsAllowed() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertTrue(restrictor.isOriginAllowed("http://untrusted.example";, 
false));
+        assertTrue(restrictor.isOriginAllowed("http://192.168.1.1:8080";, 
false));
+        assertTrue(restrictor.isOriginAllowed("http://example.com";, true));
+    }
+
+    @Test
+    void nullOriginAllowed() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertTrue(restrictor.isOriginAllowed(null, false));
+        assertTrue(restrictor.isOriginAllowed(null, true));
+    }
+
+    @Test
+    void mbeanDomainFilteringStillEnforced() throws 
MalformedObjectNameException {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        ObjectName camelMBean = new 
ObjectName("org.apache.camel:context=camel-1,type=context,name=\"camel-1\"");
+        assertTrue(restrictor.isOperationAllowed(camelMBean, "getUptime"));
+
+        ObjectName disallowedMBean = new ObjectName("com.example:type=Test");
+        assertFalse(restrictor.isOperationAllowed(disallowedMBean, 
"doSomething"));
+    }
+}
diff --git 
a/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/CamelJolokiaRestrictorTest.java
 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/CamelJolokiaRestrictorTest.java
new file mode 100644
index 0000000000..66f88b03d3
--- /dev/null
+++ 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/CamelJolokiaRestrictorTest.java
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.jolokia;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import io.quarkus.test.QuarkusUnitTest;
+import org.apache.camel.quarkus.jolokia.restrictor.CamelJolokiaRestrictor;
+import org.jolokia.server.core.util.HttpMethod;
+import org.jolokia.server.core.util.RequestType;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class CamelJolokiaRestrictorTest {
+    @RegisterExtension
+    static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+            .withEmptyApplication();
+
+    @Test
+    void ipv4LoopbackAllowed() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertTrue(restrictor.isRemoteAccessAllowed("127.0.0.1"));
+    }
+
+    @Test
+    void ipv6LoopbackAllowed() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertTrue(restrictor.isRemoteAccessAllowed("::1"));
+    }
+
+    @Test
+    void nonLoopbackRejectedByDefault() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertFalse(restrictor.isRemoteAccessAllowed("192.168.1.1"));
+    }
+
+    @Test
+    void allInterfacesRejectedByDefault() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertFalse(restrictor.isRemoteAccessAllowed("0.0.0.0"));
+    }
+
+    @Test
+    void privateNetworkRejectedByDefault() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertFalse(restrictor.isRemoteAccessAllowed("10.0.0.1"));
+    }
+
+    @Test
+    void nullArgsRejected() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertFalse(restrictor.isRemoteAccessAllowed((String[]) null));
+    }
+
+    @Test
+    void emptyArgsRejected() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertFalse(restrictor.isRemoteAccessAllowed());
+    }
+
+    @Test
+    void remoteOriginRejectedByDefault() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertFalse(restrictor.isOriginAllowed("http://untrusted.example";, 
false));
+        assertFalse(restrictor.isOriginAllowed("http://192.168.1.1:8080";, 
false));
+    }
+
+    @Test
+    void loopbackOriginAllowed() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertTrue(restrictor.isOriginAllowed("http://localhost:8080";, false));
+        assertTrue(restrictor.isOriginAllowed("http://127.0.0.1:9090";, false));
+    }
+
+    @Test
+    void noOriginHeaderAllowedWithoutStrictCheck() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertTrue(restrictor.isOriginAllowed(null, false));
+    }
+
+    @Test
+    void noOriginHeaderAllowedWithStrictCheck() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertTrue(restrictor.isOriginAllowed(null, true));
+    }
+
+    @Test
+    void loopbackOriginAllowedWithStrictCheck() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertTrue(restrictor.isOriginAllowed("http://localhost:8080";, true));
+        assertTrue(restrictor.isOriginAllowed("http://127.0.0.1:9090";, true));
+    }
+
+    @Test
+    void remoteOriginRejectedWithStrictCheck() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertFalse(restrictor.isOriginAllowed("http://untrusted.example";, 
true));
+    }
+
+    @Test
+    void malformedOriginRejected() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertFalse(restrictor.isOriginAllowed("not a valid uri", false));
+    }
+
+    @Test
+    void httpGetAllowed() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertTrue(restrictor.isHttpMethodAllowed(HttpMethod.GET));
+    }
+
+    @Test
+    void httpPostAllowed() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertTrue(restrictor.isHttpMethodAllowed(HttpMethod.POST));
+    }
+
+    @Test
+    void requestTypesAllowed() {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertTrue(restrictor.isTypeAllowed(RequestType.READ));
+        assertTrue(restrictor.isTypeAllowed(RequestType.EXEC));
+        assertTrue(restrictor.isTypeAllowed(RequestType.LIST));
+        assertTrue(restrictor.isTypeAllowed(RequestType.SEARCH));
+        assertTrue(restrictor.isTypeAllowed(RequestType.VERSION));
+    }
+
+    @Test
+    void allowedDomainReadWriteExecAllowed() throws 
MalformedObjectNameException {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        ObjectName camelMBean = new 
ObjectName("org.apache.camel:context=camel-1,type=context,name=\"camel-1\"");
+        assertTrue(restrictor.isAttributeReadAllowed(camelMBean, "Uptime"));
+        assertTrue(restrictor.isAttributeWriteAllowed(camelMBean, "Tracing"));
+        assertTrue(restrictor.isOperationAllowed(camelMBean, 
"sendStringBody"));
+    }
+
+    @Test
+    void disallowedDomainReadWriteExecDenied() throws 
MalformedObjectNameException {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        ObjectName disallowed = new ObjectName("com.example:type=Test");
+        assertFalse(restrictor.isAttributeReadAllowed(disallowed, "Value"));
+        assertFalse(restrictor.isAttributeWriteAllowed(disallowed, "Value"));
+        assertFalse(restrictor.isOperationAllowed(disallowed, "doSomething"));
+    }
+
+    @Test
+    void allowedDomainNotHidden() throws MalformedObjectNameException {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertFalse(restrictor.isObjectNameHidden(
+                new 
ObjectName("org.apache.camel:context=camel-1,type=context,name=\"camel-1\"")));
+        assertFalse(restrictor.isObjectNameHidden(new 
ObjectName("java.lang:type=Runtime")));
+        assertFalse(restrictor.isObjectNameHidden(new 
ObjectName("java.nio:type=BufferPool,name=direct")));
+    }
+
+    @Test
+    void disallowedDomainIsHidden() throws MalformedObjectNameException {
+        CamelJolokiaRestrictor restrictor = new CamelJolokiaRestrictor();
+        assertTrue(restrictor.isObjectNameHidden(new 
ObjectName("com.example:type=Test")));
+        assertTrue(restrictor.isObjectNameHidden(new 
ObjectName("java.util.logging:type=Logging")));
+    }
+}
diff --git 
a/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaDefaultHostLocalhostTest.java
 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaDefaultHostLocalhostTest.java
new file mode 100644
index 0000000000..4b417c4c9d
--- /dev/null
+++ 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaDefaultHostLocalhostTest.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.jolokia;
+
+import io.quarkus.test.QuarkusUnitTest;
+import io.restassured.RestAssured;
+import org.apache.camel.quarkus.jolokia.util.JolokiaHostUtils;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class JolokiaDefaultHostLocalhostTest {
+    @RegisterExtension
+    static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+            .withEmptyApplication();
+
+    @Test
+    void defaultHostIsLoopback() {
+        RestAssured.port = 8778;
+        String url = RestAssured.get("/jolokia/")
+                .then()
+                .statusCode(200)
+                .extract()
+                .body()
+                .jsonPath()
+                .getString("value.details.url");
+
+        String host = url.replaceFirst("https?://", 
"").replaceFirst("[:\\[].*", "");
+        assertTrue(JolokiaHostUtils.isLoopbackAddress(host),
+                "Default host should resolve to a loopback address, but got: " 
+ host);
+    }
+}
diff --git 
a/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaKubernetesClientAuthDisabledTest.java
 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaKubernetesClientAuthDisabledTest.java
new file mode 100644
index 0000000000..7ca25eaafe
--- /dev/null
+++ 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaKubernetesClientAuthDisabledTest.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.jolokia;
+
+import io.quarkus.test.QuarkusUnitTest;
+import io.restassured.RestAssured;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.hamcrest.Matchers.equalTo;
+
+class JolokiaKubernetesClientAuthDisabledTest {
+    @RegisterExtension
+    static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+            .withEmptyApplication()
+            .overrideConfigKey("kubernetes.service.host", "fake-host")
+            
.overrideConfigKey("quarkus.camel.jolokia.kubernetes.service-ca-cert", 
"/non/existent/ca.crt")
+            
.overrideConfigKey("quarkus.camel.jolokia.kubernetes.client-authentication-enabled",
 "false")
+            .overrideConfigKey("quarkus.camel.jolokia.server.host", "0.0.0.0");
+
+    @Test
+    void applicationStartsWhenClientAuthDisabled() {
+        RestAssured.port = 8778;
+        RestAssured.get("/jolokia/")
+                .then()
+                .statusCode(200)
+                .body("status", equalTo(200));
+    }
+}
diff --git 
a/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaKubernetesNoPrincipalWarningTest.java
 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaKubernetesNoPrincipalWarningTest.java
new file mode 100644
index 0000000000..d06b65c5d5
--- /dev/null
+++ 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaKubernetesNoPrincipalWarningTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.jolokia;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.logging.Level;
+
+import io.quarkus.test.QuarkusUnitTest;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class JolokiaKubernetesNoPrincipalWarningTest {
+
+    static final File CA_CERT;
+
+    static {
+        try {
+            CA_CERT = Files.createTempFile("fake-ca", ".crt").toFile();
+            CA_CERT.deleteOnExit();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @RegisterExtension
+    static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+            .withEmptyApplication()
+            .overrideConfigKey("kubernetes.service.host", "fake-host")
+            
.overrideConfigKey("quarkus.camel.jolokia.kubernetes.service-ca-cert", 
CA_CERT.getAbsolutePath())
+            .setLogRecordPredicate(record -> 
record.getLevel().equals(Level.WARNING))
+            .assertLogRecords(records -> assertTrue(
+                    records.stream().anyMatch(r -> r.getMessage().contains("no 
client principal is configured")),
+                    "Expected a warning about missing client principal"));
+
+    @Test
+    void applicationStartsWithClientPrincipalWarning() {
+        // The assertLogRecords callback above verifies the warning was logged.
+        // The Jolokia server is configured with HTTPS (due to the fake CA 
cert),
+        // so we cannot make a plain HTTP request — app startup alone is 
sufficient.
+    }
+}
diff --git 
a/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaKubernetesTlsFailureTest.java
 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaKubernetesTlsFailureTest.java
new file mode 100644
index 0000000000..533ce1984e
--- /dev/null
+++ 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaKubernetesTlsFailureTest.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.jolokia;
+
+import io.quarkus.test.QuarkusUnitTest;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+class JolokiaKubernetesTlsFailureTest {
+    @RegisterExtension
+    static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+            .withEmptyApplication()
+            .overrideConfigKey("kubernetes.service.host", "fake-host")
+            
.overrideConfigKey("quarkus.camel.jolokia.kubernetes.service-ca-cert", 
"/non/existent/ca.crt")
+            .overrideConfigKey("quarkus.camel.jolokia.server.host", "0.0.0.0")
+            .assertException(t -> {
+                if (t.getMessage() == null
+                        || !t.getMessage().contains("Kubernetes SSL client 
authentication is enabled")) {
+                    throw new AssertionError(
+                            "Expected RuntimeException about Kubernetes SSL 
client authentication failure, got: " + t, t);
+                }
+            });
+
+    @Test
+    void applicationShouldFailToStart() {
+        // The application should not start — the assertException above 
validates the failure
+    }
+}
diff --git 
a/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaKubernetesTlsLoopbackFallbackTest.java
 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaKubernetesTlsLoopbackFallbackTest.java
new file mode 100644
index 0000000000..a2a13e1c42
--- /dev/null
+++ 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaKubernetesTlsLoopbackFallbackTest.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.jolokia;
+
+import io.quarkus.test.QuarkusUnitTest;
+import io.restassured.RestAssured;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.hamcrest.Matchers.equalTo;
+
+class JolokiaKubernetesTlsLoopbackFallbackTest {
+    @RegisterExtension
+    static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+            .withEmptyApplication()
+            .overrideConfigKey("kubernetes.service.host", "fake-host")
+            
.overrideConfigKey("quarkus.camel.jolokia.kubernetes.service-ca-cert", 
"/non/existent/ca.crt")
+            .overrideConfigKey("quarkus.camel.jolokia.server.host", 
"localhost");
+
+    @Test
+    void applicationStartsWithWarningWhenCaCertMissingAndHostIsLoopback() {
+        RestAssured.port = 8778;
+        RestAssured.get("/jolokia/")
+                .then()
+                .statusCode(200)
+                .body("status", equalTo(200));
+    }
+}
diff --git 
a/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaRecorderTest.java
 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaRecorderTest.java
new file mode 100644
index 0000000000..10138213dc
--- /dev/null
+++ 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaRecorderTest.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.jolokia;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class JolokiaRecorderTest {
+
+    @Test
+    void localhostIsLoopback() {
+        assertTrue(JolokiaRecorder.isLoopbackHost("localhost"));
+    }
+
+    @Test
+    void ipv4LoopbackIsLoopback() {
+        assertTrue(JolokiaRecorder.isLoopbackHost("127.0.0.1"));
+    }
+
+    @Test
+    void ipv6LoopbackIsLoopback() {
+        assertTrue(JolokiaRecorder.isLoopbackHost("::1"));
+    }
+
+    @Test
+    void allInterfacesIsNotLoopback() {
+        assertFalse(JolokiaRecorder.isLoopbackHost("0.0.0.0"));
+    }
+
+    @Test
+    void nonLoopbackIpIsNotLoopback() {
+        assertFalse(JolokiaRecorder.isLoopbackHost("192.168.1.1"));
+    }
+
+    @Test
+    void ipv4NonLoopbackRangeIsNotLoopback() {
+        assertFalse(JolokiaRecorder.isLoopbackHost("10.0.0.1"));
+    }
+
+    @Test
+    void ipv6BracketWrappedIsLoopback() {
+        assertTrue(JolokiaRecorder.isLoopbackHost("[::1]"));
+    }
+
+    @Test
+    void localhostLocaldomainIsLoopback() {
+        assertTrue(JolokiaRecorder.isLoopbackHost("localhost.localdomain"));
+    }
+
+    @Test
+    void ipv4FullLoopbackRangeIsLoopback() {
+        assertTrue(JolokiaRecorder.isLoopbackHost("127.255.255.255"));
+    }
+
+    @Test
+    void ipv6LongFormLoopbackIsLoopback() {
+        assertTrue(JolokiaRecorder.isLoopbackHost("0:0:0:0:0:0:0:1"));
+    }
+
+    @Test
+    void nullIsNotLoopback() {
+        assertFalse(JolokiaRecorder.isLoopbackHost(null));
+    }
+
+    @Test
+    void emptyStringIsNotLoopback() {
+        assertFalse(JolokiaRecorder.isLoopbackHost(""));
+    }
+
+    @Test
+    void ipv4LoopbackWithExtraOctetsIsNotLoopback() {
+        
assertFalse(JolokiaRecorder.isLoopbackHost("127.0.0.1.untrusted.example"));
+    }
+
+    @Test
+    void ipv4LoopbackOctetOverflowIsNotLoopback() {
+        assertFalse(JolokiaRecorder.isLoopbackHost("127.0.0.256"));
+    }
+}
diff --git 
a/extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/CamelQuarkusJolokiaLogHandler.java
 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaRemoteAccessDefaultTest.java
similarity index 58%
copy from 
extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/CamelQuarkusJolokiaLogHandler.java
copy to 
extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaRemoteAccessDefaultTest.java
index d70297a2f2..d5784f4b0a 100644
--- 
a/extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/CamelQuarkusJolokiaLogHandler.java
+++ 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaRemoteAccessDefaultTest.java
@@ -16,29 +16,24 @@
  */
 package org.apache.camel.quarkus.jolokia;
 
-import org.jboss.logging.Logger;
-import org.jolokia.core.api.LogHandler;
+import io.quarkus.test.QuarkusUnitTest;
+import io.restassured.RestAssured;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
 
-final class CamelQuarkusJolokiaLogHandler implements LogHandler {
-    private static final Logger LOG = 
Logger.getLogger(CamelQuarkusJolokiaLogHandler.class);
+import static org.hamcrest.Matchers.equalTo;
 
-    @Override
-    public void debug(String s) {
-        LOG.debug(s);
-    }
-
-    @Override
-    public void info(String s) {
-        LOG.info(s);
-    }
-
-    @Override
-    public void error(String s, Throwable throwable) {
-        LOG.error(s);
-    }
+class JolokiaRemoteAccessDefaultTest {
+    @RegisterExtension
+    static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+            .withEmptyApplication();
 
-    @Override
-    public boolean isDebug() {
-        return LOG.isDebugEnabled();
+    @Test
+    void loopbackConnectionAllowedByDefault() {
+        RestAssured.port = 8778;
+        RestAssured.get("/jolokia/")
+                .then()
+                .statusCode(200)
+                .body("status", equalTo(200));
     }
 }
diff --git 
a/extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/CamelQuarkusJolokiaLogHandler.java
 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaRemoteAccessEnabledTest.java
similarity index 55%
copy from 
extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/CamelQuarkusJolokiaLogHandler.java
copy to 
extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaRemoteAccessEnabledTest.java
index d70297a2f2..bba2ef859d 100644
--- 
a/extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/CamelQuarkusJolokiaLogHandler.java
+++ 
b/extensions/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaRemoteAccessEnabledTest.java
@@ -16,29 +16,25 @@
  */
 package org.apache.camel.quarkus.jolokia;
 
-import org.jboss.logging.Logger;
-import org.jolokia.core.api.LogHandler;
+import io.quarkus.test.QuarkusUnitTest;
+import io.restassured.RestAssured;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
 
-final class CamelQuarkusJolokiaLogHandler implements LogHandler {
-    private static final Logger LOG = 
Logger.getLogger(CamelQuarkusJolokiaLogHandler.class);
+import static org.hamcrest.Matchers.equalTo;
 
-    @Override
-    public void debug(String s) {
-        LOG.debug(s);
-    }
-
-    @Override
-    public void info(String s) {
-        LOG.info(s);
-    }
-
-    @Override
-    public void error(String s, Throwable throwable) {
-        LOG.error(s);
-    }
+class JolokiaRemoteAccessEnabledTest {
+    @RegisterExtension
+    static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+            .withEmptyApplication()
+            .overrideConfigKey("quarkus.camel.jolokia.remote-access-allowed", 
"true");
 
-    @Override
-    public boolean isDebug() {
-        return LOG.isDebugEnabled();
+    @Test
+    void connectionAllowedWhenRemoteAccessEnabled() {
+        RestAssured.port = 8778;
+        RestAssured.get("/jolokia/")
+                .then()
+                .statusCode(200)
+                .body("status", equalTo(200));
     }
 }
diff --git a/extensions/jolokia/runtime/src/main/doc/usage.adoc 
b/extensions/jolokia/runtime/src/main/doc/usage.adoc
index 1299ab8e0a..44f1dfbe3c 100644
--- a/extensions/jolokia/runtime/src/main/doc/usage.adoc
+++ b/extensions/jolokia/runtime/src/main/doc/usage.adoc
@@ -2,11 +2,11 @@ This extension adds https://jolokia.org/[Jolokia] support to 
your application.
 
 === Jolokia HTTP endpoints
 
-In prod mode, Jolokia is accessible at the following URLs.
+Jolokia is accessible at the following URL.
 
-* http://0.0.0.0:8778/jolokia/
+* http://localhost:8778/jolokia/
 
-In dev and test modes Jolokia is bound only to `localhost`.
+By default, the Jolokia agent HTTP server binds to `localhost`. Remote dev 
mode and WSL environments default to `0.0.0.0`.
 
 If you want to disable Jolokia entirely, then add the following configuration 
to `application.properties`.
 
@@ -58,11 +58,11 @@ quarkus.camel.jolokia.register-camel-restrictor=false
 
 ==== Create a custom restrictor
 
-You can create your own restrictor class and register it with Jolokia.
+You can create your own restrictor class and register it with Jolokia. 
Extending `CamelJolokiaRestrictor` inherits the secure defaults (remote access 
control, CORS blocking, MBean domain filtering).
 
 [source,java]
 ----
-public class CustomRestrictor extends AllowAllRestrictor {
+public class CustomRestrictor extends CamelJolokiaRestrictor {
     // Override methods to apply custom restrictions
 }
 ----
@@ -99,9 +99,96 @@ This functionality can be disabled by adding the following 
configuration to `app
 quarkus.camel.jolokia.kubernetes.client-authentication-enabled=false
 ----
 
-Note that if you choose to use 
https://github.com/hawtio/hawtio-online[hawtio-online] to connect to your 
running application, then you must configure the Jolokia client principal.
+==== Configuring Jolokia for remote management tools
+
+Tools such as https://hawt.io/[Hawtio] and 
https://github.com/hawtio/hawtio-online[hawtio-online] connect to Jolokia via 
the pod IP address, which is a non-loopback address. By default, the Jolokia 
agent binds to `localhost` and the default restrictor blocks connections from 
non-loopback addresses.
+
+To allow remote management tools to connect to Jolokia in Kubernetes, add the 
following configuration to `application.properties`.
+
+[source]
+----
+quarkus.camel.jolokia.server.host=0.0.0.0
+quarkus.camel.jolokia.remote-access-allowed=true
+----
+
+This is safe when combined with SSL client authentication (enabled by default 
in Kubernetes), which ensures that only clients presenting a valid certificate 
signed by the service CA can connect. If you use hawtio-online, you must also 
configure the Jolokia client principal.
 
 [source]
 ----
 quarkus.camel.jolokia.kubernetes.client-principal=cn=hawtio-online.hawtio.svc
 ----
+
+=== Security
+
+NOTE: Even when bound to `localhost`, Jolokia does not require authentication 
by default. On shared hosts or in container environments where localhost may be 
reachable from other containers, consider using a custom restrictor or Jolokia 
access policy to restrict access further.
+
+==== Network binding
+
+By default, the Jolokia agent HTTP server binds to `localhost`, making it 
accessible only from the local machine. If you need to expose Jolokia to remote 
hosts, you must explicitly configure the bind address and enable remote access.
+
+[source]
+----
+quarkus.camel.jolokia.server.host=0.0.0.0
+quarkus.camel.jolokia.remote-access-allowed=true
+----
+
+==== Remote access control
+
+The default Camel Jolokia restrictor blocks connections from non-loopback 
addresses. This is controlled by the 
`quarkus.camel.jolokia.remote-access-allowed` property (default `false`). When 
set to `false`, only connections from loopback addresses (e.g. `127.0.0.1`, 
`::1`) are accepted, regardless of the server bind address.
+
+==== Cross-origin requests (CORS)
+
+The default Camel Jolokia restrictor denies all cross-origin requests. If you 
need to allow specific origins (e.g. for a browser-based monitoring tool 
connecting cross-origin), you can use a 
https://jolokia.org/reference/html/manual/security.html[Jolokia access policy] 
(`jolokia-access.xml`) with a `<cors>` section, or configure a custom 
restrictor that overrides `isOriginAllowed()`.
+
+==== Jolokia access policy
+
+Jolokia supports fine-grained access control via an XML policy file 
(`jolokia-access.xml`). This can restrict access by IP address, CORS origin, 
HTTP method, and MBean operation.
+
+The default Camel restrictor automatically loads `jolokia-access.xml` from the 
classpath if present. Place the file in `src/main/resources/jolokia-access.xml` 
to use it alongside the Camel restrictor. The Camel restrictor always allows 
loopback connections. For non-loopback requests, it delegates remote access, 
CORS, allowed request types (`<commands>`), and HTTP method (`<http>`) checks 
to the policy file, while continuing to enforce MBean domain filtering.
+
+For example, the following `jolokia-access.xml` allows access from the 
`10.0.0.0/8` subnet, CORS requests from a monitoring tool, and restricts 
Jolokia to read-only operations over HTTP GET.
+
+[source,xml]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<restrict>
+    <remote>
+        <host>10.0.0.0/8</host>
+    </remote>
+    <cors>
+        <allow-origin>http://monitoring.example.com</allow-origin>
+    </cors>
+    <commands>
+        <command>read</command>
+        <command>list</command>
+        <command>version</command>
+        <command>search</command>
+    </commands>
+    <http>
+        <method>get</method>
+    </http>
+</restrict>
+----
+
+Refer to the https://jolokia.org/reference/html/manual/security.html[Jolokia 
security documentation] for the full policy file format.
+
+To load the policy file from a different location, such as a file path mounted 
from a Kubernetes ConfigMap, configure the `policyLocation` property.
+
+[source]
+----
+quarkus.camel.jolokia.additional-properties."policyLocation"=file:/etc/jolokia/jolokia-access.xml
+----
+
+As an alternative, you can disable the default Camel restrictor entirely and 
let Jolokia manage the policy file directly. This gives full control to 
`jolokia-access.xml` (including MBean-level rules) but loses the Camel MBean 
domain filtering.
+
+[source]
+----
+quarkus.camel.jolokia.register-camel-restrictor=false
+----
+
+If you are building a native executable, the policy file must be explicitly 
included as a native resource.
+
+[source]
+----
+quarkus.native.resources.includes=jolokia-access.xml
+----
diff --git 
a/extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/CamelQuarkusJolokiaLogHandler.java
 
b/extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/CamelQuarkusJolokiaLogHandler.java
index d70297a2f2..f19619523c 100644
--- 
a/extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/CamelQuarkusJolokiaLogHandler.java
+++ 
b/extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/CamelQuarkusJolokiaLogHandler.java
@@ -34,7 +34,7 @@ final class CamelQuarkusJolokiaLogHandler implements 
LogHandler {
 
     @Override
     public void error(String s, Throwable throwable) {
-        LOG.error(s);
+        LOG.error(s, throwable);
     }
 
     @Override
diff --git 
a/extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/JolokiaRecorder.java
 
b/extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/JolokiaRecorder.java
index 56e08e980c..e697b108e6 100644
--- 
a/extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/JolokiaRecorder.java
+++ 
b/extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/JolokiaRecorder.java
@@ -31,6 +31,7 @@ import 
org.apache.camel.quarkus.jolokia.config.JolokiaRuntimeConfig.DiscoveryEna
 import org.apache.camel.quarkus.jolokia.config.JolokiaRuntimeConfig.Kubernetes;
 import org.apache.camel.quarkus.jolokia.config.JolokiaRuntimeConfig.Server;
 import org.apache.camel.quarkus.jolokia.restrictor.CamelJolokiaRestrictor;
+import org.apache.camel.quarkus.jolokia.util.JolokiaHostUtils;
 import org.apache.camel.util.CollectionHelper;
 import org.apache.camel.util.ObjectHelper;
 import org.eclipse.microprofile.config.ConfigProvider;
@@ -73,7 +74,7 @@ public class JolokiaRecorder {
                     host = ALL_INTERFACES;
                 }
             } else {
-                host = ALL_INTERFACES;
+                host = LOCALHOST;
             }
         }
 
@@ -92,10 +93,27 @@ public class JolokiaRecorder {
                 serverOptions.put("useSslClientAuthentication", "true");
                 serverOptions.put("extendedClientCheck", "true");
                 serverOptions.put("caCert", 
kubernetes.serviceCaCert().getAbsolutePath());
-                kubernetes.clientPrincipal()
-                        .ifPresent(clientPrincipal -> 
serverOptions.put("clientPrincipal", clientPrincipal));
-            } else {
-                LOG.warnf("Kubernetes service CA certificate %s does not 
exist", kubernetes.serviceCaCert());
+                if (kubernetes.clientPrincipal().isPresent()) {
+                    serverOptions.put("clientPrincipal", 
kubernetes.clientPrincipal().get());
+                } else {
+                    LOG.warn("Kubernetes SSL client authentication is enabled 
but no client principal is configured"
+                            + " 
('quarkus.camel.jolokia.kubernetes.client-principal'). Any pod presenting a 
valid"
+                            + " certificate signed by the service CA can 
access Jolokia. Set a client principal"
+                            + " to restrict access to a specific service 
identity.");
+                }
+            } else if (kubernetes.clientAuthenticationEnabled()) {
+                if (!isLoopbackHost(host)) {
+                    throw new RuntimeException(
+                            String.format("Kubernetes SSL client 
authentication is enabled but the service CA certificate"
+                                    + " '%s' does not exist. The Jolokia 
server is configured to bind to '%s'"
+                                    + " which would expose it without 
authentication. Either provide the CA certificate,"
+                                    + " set 
'quarkus.camel.jolokia.kubernetes.client-authentication-enabled=false',"
+                                    + " or bind to localhost with 
'quarkus.camel.jolokia.server.host=localhost'.",
+                                    kubernetes.serviceCaCert(), host));
+                }
+                LOG.warnf("Kubernetes SSL client authentication is enabled but 
the service CA certificate"
+                        + " '%s' does not exist. Proceeding without TLS since 
the Jolokia server is bound to"
+                        + " the loopback interface.", 
kubernetes.serviceCaCert());
             }
         }
 
@@ -152,6 +170,10 @@ public class JolokiaRecorder {
         return new RuntimeValue<>(new 
CamelQuarkusJolokiaServer(jolokiaServer.getValue()));
     }
 
+    static boolean isLoopbackHost(String host) {
+        return JolokiaHostUtils.isLoopbackAddress(host);
+    }
+
     static final class CamelQuarkusJolokiaAgent extends JolokiaServer {
         CamelQuarkusJolokiaAgent(JolokiaServerConfig config) throws 
IOException {
             super(config);
diff --git 
a/extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/config/JolokiaRuntimeConfig.java
 
b/extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/config/JolokiaRuntimeConfig.java
index 44097ac703..8561d9d4f4 100644
--- 
a/extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/config/JolokiaRuntimeConfig.java
+++ 
b/extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/config/JolokiaRuntimeConfig.java
@@ -58,6 +58,18 @@ public interface JolokiaRuntimeConfig {
     @WithDefault("true")
     boolean registerCamelRestrictor();
 
+    /**
+     * When `true`, the default Camel Jolokia restrictor allows connections 
from non-loopback (remote) addresses.
+     * When `false` (the default), only connections from loopback addresses 
(e.g. 127.0.0.1, ::1) are permitted.
+     * This provides defense-in-depth: even if the server host is configured 
to bind to all interfaces,
+     * remote requests are rejected unless this property is explicitly set to 
`true`.
+     *
+     * This option only takes effect when `register-camel-restrictor` is 
`true` and a custom restrictor class
+     * is not configured via 
`quarkus.camel.jolokia.additional-properties."restrictorClass"`.
+     */
+    @WithDefault("false")
+    boolean remoteAccessAllowed();
+
     interface Server {
         /**
          * Whether the Jolokia agent HTTP server should be started 
automatically.
@@ -69,8 +81,8 @@ public interface JolokiaRuntimeConfig {
 
         /**
          * The host address to which the Jolokia agent HTTP server should bind.
-         * When unspecified, the default is localhost for dev and test mode.
-         * In prod mode the default is to bind to all interfaces at 0.0.0.0.
+         * When unspecified, the default is localhost in all modes except 
remote dev,
+         * where it defaults to 0.0.0.0.
          */
         Optional<String> host();
 
diff --git 
a/extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/restrictor/CamelJolokiaRestrictor.java
 
b/extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/restrictor/CamelJolokiaRestrictor.java
index 5f902163fe..aa2f8dec81 100644
--- 
a/extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/restrictor/CamelJolokiaRestrictor.java
+++ 
b/extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/restrictor/CamelJolokiaRestrictor.java
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.quarkus.jolokia.restrictor;
 
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.util.Collections;
 import java.util.Set;
 
@@ -23,29 +25,109 @@ import javax.management.ObjectName;
 
 import io.smallrye.config.SmallRyeConfig;
 import org.apache.camel.quarkus.jolokia.config.JolokiaBuildTimeConfig;
+import org.apache.camel.quarkus.jolokia.util.JolokiaHostUtils;
 import org.eclipse.microprofile.config.ConfigProvider;
-import org.jolokia.server.core.restrictor.AllowAllRestrictor;
+import org.jboss.logging.Logger;
+import org.jolokia.server.core.restrictor.RestrictorFactory;
+import org.jolokia.server.core.restrictor.policy.PolicyRestrictor;
+import org.jolokia.server.core.service.api.Restrictor;
+import org.jolokia.server.core.util.HttpMethod;
+import org.jolokia.server.core.util.RequestType;
+
+public class CamelJolokiaRestrictor implements Restrictor {
+    private static final String DEFAULT_POLICY_LOCATION = 
"classpath:/jolokia-access.xml";
+    private static final Logger LOG = 
Logger.getLogger(CamelJolokiaRestrictor.class);
 
-public final class CamelJolokiaRestrictor extends AllowAllRestrictor {
     private final Set<String> ALLOWED_DOMAINS = Collections.unmodifiableSet(
             ConfigProvider.getConfig()
                     .unwrap(SmallRyeConfig.class)
                     .getConfigMapping(JolokiaBuildTimeConfig.class)
                     .camelRestrictorAllowedMbeanDomains());
 
+    private final boolean remoteAccessAllowed = ConfigProvider.getConfig()
+            .getOptionalValue("quarkus.camel.jolokia.remote-access-allowed", 
Boolean.class)
+            .orElse(false);
+
+    private final PolicyRestrictor policyRestrictor;
+
+    public CamelJolokiaRestrictor() {
+        this.policyRestrictor = loadPolicyRestrictor();
+    }
+
+    @Override
+    public boolean isRemoteAccessAllowed(String... hostOrAddress) {
+        if (remoteAccessAllowed) {
+            return true;
+        }
+        if (hostOrAddress != null) {
+            for (String addr : hostOrAddress) {
+                if (isLoopbackAddress(addr)) {
+                    return true;
+                }
+            }
+        }
+        if (policyRestrictor != null) {
+            return policyRestrictor.isRemoteAccessAllowed(hostOrAddress);
+        }
+        return false;
+    }
+
+    @Override
+    public boolean isHttpMethodAllowed(HttpMethod method) {
+        if (policyRestrictor != null) {
+            return policyRestrictor.isHttpMethodAllowed(method);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean isTypeAllowed(RequestType type) {
+        if (policyRestrictor != null) {
+            return policyRestrictor.isTypeAllowed(type);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean isOriginAllowed(String origin, boolean strictCheck) {
+        if (origin == null || remoteAccessAllowed) {
+            return true;
+        }
+        try {
+            String host = new URI(origin).getHost();
+            if (isLoopbackAddress(host)) {
+                return true;
+            }
+        } catch (URISyntaxException e) {
+            return false;
+        }
+        if (policyRestrictor != null) {
+            return policyRestrictor.isOriginAllowed(origin, strictCheck);
+        }
+        return false;
+    }
+
+    @Override
+    public boolean ignoreScheme() {
+        if (policyRestrictor != null) {
+            return policyRestrictor.ignoreScheme();
+        }
+        return false;
+    }
+
     @Override
     public boolean isAttributeReadAllowed(ObjectName objectName, String 
attribute) {
-        return isAllowedDomain(objectName);
+        return isAllowedDomain(objectName) && isTypeAllowed(RequestType.READ);
     }
 
     @Override
     public boolean isAttributeWriteAllowed(ObjectName objectName, String 
attribute) {
-        return isAllowedDomain(objectName);
+        return isAllowedDomain(objectName) && isTypeAllowed(RequestType.WRITE);
     }
 
     @Override
     public boolean isOperationAllowed(ObjectName objectName, String operation) 
{
-        return isAllowedDomain(objectName);
+        return isAllowedDomain(objectName) && isTypeAllowed(RequestType.EXEC);
     }
 
     @Override
@@ -56,4 +138,20 @@ public final class CamelJolokiaRestrictor extends 
AllowAllRestrictor {
     private boolean isAllowedDomain(ObjectName objectName) {
         return ALLOWED_DOMAINS.contains(objectName.getDomain());
     }
+
+    private static boolean isLoopbackAddress(String hostOrAddress) {
+        return JolokiaHostUtils.isLoopbackAddress(hostOrAddress);
+    }
+
+    private static PolicyRestrictor loadPolicyRestrictor() {
+        String location = ConfigProvider.getConfig()
+                
.getOptionalValue("quarkus.camel.jolokia.additional-properties.policyLocation", 
String.class)
+                .orElse(DEFAULT_POLICY_LOCATION);
+        try {
+            return RestrictorFactory.lookupPolicyRestrictor(location);
+        } catch (Exception e) {
+            LOG.warnf("Failed to load Jolokia policy file from %s, policy 
delegation disabled", location, e);
+            return null;
+        }
+    }
 }
diff --git 
a/extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/util/JolokiaHostUtils.java
 
b/extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/util/JolokiaHostUtils.java
new file mode 100644
index 0000000000..f452d4a8d6
--- /dev/null
+++ 
b/extensions/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/util/JolokiaHostUtils.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.jolokia.util;
+
+public final class JolokiaHostUtils {
+
+    private JolokiaHostUtils() {
+        // Utility class
+    }
+
+    public static boolean isLoopbackAddress(String hostOrAddress) {
+        if (hostOrAddress == null || hostOrAddress.isEmpty()) {
+            return false;
+        }
+
+        String host = hostOrAddress;
+        if (host.startsWith("[") && host.endsWith("]")) {
+            host = host.substring(1, host.length() - 1);
+        }
+
+        if ("localhost".equalsIgnoreCase(host) || 
"localhost.localdomain".equalsIgnoreCase(host)) {
+            return true;
+        }
+
+        if (host.startsWith("127.")) {
+            return isIPv4Loopback(host);
+        }
+
+        return "::1".equals(host) || "0:0:0:0:0:0:0:1".equals(host);
+    }
+
+    private static boolean isIPv4Loopback(String ip) {
+        String[] parts = ip.split("\\.");
+        if (parts.length != 4) {
+            return false;
+        }
+        try {
+            if (Integer.parseInt(parts[0]) != 127) {
+                return false;
+            }
+            for (int i = 1; i < 4; i++) {
+                int octet = Integer.parseInt(parts[i]);
+                if (octet < 0 || octet > 255) {
+                    return false;
+                }
+            }
+            return true;
+        } catch (NumberFormatException e) {
+            return false;
+        }
+    }
+}
diff --git 
a/integration-tests/jolokia/src/main/java/org/apache/camel/quarkus/component/jolokia/it/CustomRestrictor.java
 
b/integration-tests/jolokia/src/main/java/org/apache/camel/quarkus/component/jolokia/it/CustomRestrictor.java
index 65fa5bf047..d779bcbe5b 100644
--- 
a/integration-tests/jolokia/src/main/java/org/apache/camel/quarkus/component/jolokia/it/CustomRestrictor.java
+++ 
b/integration-tests/jolokia/src/main/java/org/apache/camel/quarkus/component/jolokia/it/CustomRestrictor.java
@@ -18,12 +18,12 @@ package org.apache.camel.quarkus.component.jolokia.it;
 
 import javax.management.ObjectName;
 
-import org.jolokia.server.core.restrictor.AllowAllRestrictor;
+import org.apache.camel.quarkus.jolokia.restrictor.CamelJolokiaRestrictor;
 
 /**
  * Only allows MBean operation sendStringBody.
  */
-public class CustomRestrictor extends AllowAllRestrictor {
+public class CustomRestrictor extends CamelJolokiaRestrictor {
     @Override
     public boolean isOperationAllowed(ObjectName pName, String pOperation) {
         if (pOperation.startsWith("sendStringBody")) {
diff --git 
a/integration-tests/jolokia/src/main/java/org/apache/camel/quarkus/component/jolokia/it/CustomRestrictor.java
 
b/integration-tests/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaRemoteAccessAllowedIT.java
similarity index 67%
copy from 
integration-tests/jolokia/src/main/java/org/apache/camel/quarkus/component/jolokia/it/CustomRestrictor.java
copy to 
integration-tests/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaRemoteAccessAllowedIT.java
index 65fa5bf047..d25b0b010e 100644
--- 
a/integration-tests/jolokia/src/main/java/org/apache/camel/quarkus/component/jolokia/it/CustomRestrictor.java
+++ 
b/integration-tests/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaRemoteAccessAllowedIT.java
@@ -16,19 +16,8 @@
  */
 package org.apache.camel.quarkus.component.jolokia.it;
 
-import javax.management.ObjectName;
+import io.quarkus.test.junit.QuarkusIntegrationTest;
 
-import org.jolokia.server.core.restrictor.AllowAllRestrictor;
-
-/**
- * Only allows MBean operation sendStringBody.
- */
-public class CustomRestrictor extends AllowAllRestrictor {
-    @Override
-    public boolean isOperationAllowed(ObjectName pName, String pOperation) {
-        if (pOperation.startsWith("sendStringBody")) {
-            return true;
-        }
-        return false;
-    }
+@QuarkusIntegrationTest
+class JolokiaRemoteAccessAllowedIT extends JolokiaRemoteAccessAllowedTest {
 }
diff --git 
a/integration-tests/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaRemoteAccessAllowedTest.java
 
b/integration-tests/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaRemoteAccessAllowedTest.java
new file mode 100644
index 0000000000..823bb748b7
--- /dev/null
+++ 
b/integration-tests/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaRemoteAccessAllowedTest.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.component.jolokia.it;
+
+import java.util.Map;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.QuarkusTestProfile;
+import io.quarkus.test.junit.TestProfile;
+import io.restassured.RestAssured;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.Matchers.equalTo;
+
+@TestProfile(JolokiaRemoteAccessAllowedTest.RemoteAccessAllowedProfile.class)
+@QuarkusTest
+class JolokiaRemoteAccessAllowedTest {
+    @BeforeEach
+    public void beforeEach() {
+        RestAssured.port = 8778;
+    }
+
+    @Test
+    void jolokiaAccessibleWithRemoteAccessAllowed() {
+        RestAssured.given()
+                .get("/jolokia/")
+                .then()
+                .statusCode(200)
+                .body("status", equalTo(200));
+    }
+
+    @Test
+    void mbeanAccessStillRestrictedByDomain() {
+        RestAssured.given()
+                
.get("/jolokia/read/java.util.logging:type=Logging/LoggerNames")
+                .then()
+                .statusCode(200)
+                .body("status", equalTo(403));
+    }
+
+    public static final class RemoteAccessAllowedProfile implements 
QuarkusTestProfile {
+        @Override
+        public Map<String, String> getConfigOverrides() {
+            return Map.of("quarkus.camel.jolokia.remote-access-allowed", 
"true");
+        }
+    }
+}
diff --git 
a/integration-tests/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaTest.java
 
b/integration-tests/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaTest.java
index 430d05c22a..c00032b8bf 100644
--- 
a/integration-tests/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaTest.java
+++ 
b/integration-tests/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaTest.java
@@ -112,4 +112,28 @@ class JolokiaTest {
                 .body(
                         "status", equalTo(403));
     }
+
+    @Test
+    void disallowedDomainExecDenied() {
+        String payload = 
"{\"type\":\"exec\",\"mbean\":\"java.util.logging:type=Logging\",\"operation\":\"getLoggerLevel\",\"arguments\":[\"\"]}";
+        RestAssured.given()
+                .contentType(ContentType.JSON)
+                .body(payload)
+                .post("/jolokia/")
+                .then()
+                .statusCode(200)
+                .body("status", equalTo(403));
+    }
+
+    @Test
+    void disallowedDomainWriteDenied() {
+        String payload = 
"{\"type\":\"write\",\"mbean\":\"java.util.logging:type=Logging\",\"attribute\":\"LoggerNames\",\"value\":\"test\"}";
+        RestAssured.given()
+                .contentType(ContentType.JSON)
+                .body(payload)
+                .post("/jolokia/")
+                .then()
+                .statusCode(200)
+                .body("status", equalTo(403));
+    }
 }

Reply via email to