You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ni...@apache.org on 2021/11/30 07:40:09 UTC

[ignite] branch ignite-2.12 updated: IGNITE-16019 Fix check of SERVICE_DEPLOY permission (#9616)

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

nizhikov pushed a commit to branch ignite-2.12
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/ignite-2.12 by this push:
     new 0015b96  IGNITE-16019 Fix check of SERVICE_DEPLOY permission (#9616)
0015b96 is described below

commit 0015b966653ac6e0080ec92cbab5fd51e967b67f
Author: Nikolay <ni...@apache.org>
AuthorDate: Tue Nov 30 10:23:10 2021 +0300

    IGNITE-16019 Fix check of SERVICE_DEPLOY permission (#9616)
    
    (cherry picked from commit 48ed770683a69baab3ff93e7fb03d76e83fd95bd)
---
 .../processors/service/IgniteServiceProcessor.java | 129 +++++++++++++++------
 .../security/service/ServiceAuthorizationTest.java |  81 +++++++------
 .../security/service/ServiceStaticConfigTest.java  | 105 +++++++++++++++++
 .../ignite/testsuites/SecurityTestSuite.java       |   4 +-
 4 files changed, 248 insertions(+), 71 deletions(-)

diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java
index 63e7937..cf8900b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/service/IgniteServiceProcessor.java
@@ -61,11 +61,13 @@ import org.apache.ignite.internal.managers.systemview.walker.ServiceViewWalker;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.DynamicCacheChangeBatch;
 import org.apache.ignite.internal.processors.cache.DynamicCacheChangeRequest;
+import org.apache.ignite.internal.processors.cache.ValidationOnNodeJoinUtils;
 import org.apache.ignite.internal.processors.cluster.ChangeGlobalStateMessage;
 import org.apache.ignite.internal.processors.cluster.DiscoveryDataClusterState;
 import org.apache.ignite.internal.processors.cluster.IgniteChangeGlobalStateSupport;
 import org.apache.ignite.internal.processors.platform.services.PlatformService;
 import org.apache.ignite.internal.processors.security.OperationSecurityContext;
+import org.apache.ignite.internal.processors.security.SecurityContext;
 import org.apache.ignite.internal.util.future.GridCompoundFuture;
 import org.apache.ignite.internal.util.future.GridFinishedFuture;
 import org.apache.ignite.internal.util.future.GridFutureAdapter;
@@ -83,6 +85,7 @@ import org.apache.ignite.services.Service;
 import org.apache.ignite.services.ServiceConfiguration;
 import org.apache.ignite.services.ServiceDeploymentException;
 import org.apache.ignite.services.ServiceDescriptor;
+import org.apache.ignite.spi.IgniteNodeValidationResult;
 import org.apache.ignite.spi.communication.CommunicationSpi;
 import org.apache.ignite.spi.discovery.DiscoveryDataBag;
 import org.apache.ignite.spi.discovery.DiscoverySpi;
@@ -97,6 +100,7 @@ import static org.apache.ignite.configuration.DeploymentMode.ISOLATED;
 import static org.apache.ignite.configuration.DeploymentMode.PRIVATE;
 import static org.apache.ignite.events.EventType.EVT_NODE_JOINED;
 import static org.apache.ignite.internal.GridComponent.DiscoveryDataExchangeType.SERVICE_PROC;
+import static org.apache.ignite.internal.processors.security.SecurityUtils.nodeSecurityContext;
 import static org.apache.ignite.plugin.security.SecurityPermission.SERVICE_DEPLOY;
 
 /**
@@ -364,6 +368,24 @@ public class IgniteServiceProcessor extends ServiceProcessorAdapter implements I
     }
 
     /** {@inheritDoc} */
+    @Override public @Nullable IgniteNodeValidationResult validateNode(
+        ClusterNode node,
+        DiscoveryDataBag.JoiningNodeDiscoveryData data
+    ) {
+        if (data.joiningNodeData() == null || !ctx.security().enabled())
+            return null;
+
+        List<ServiceInfo> svcs = ((ServiceProcessorJoinNodeDiscoveryData)data.joiningNodeData()).services();
+
+        SecurityException err = checkDeployPermissionDuringJoin(node, svcs);
+
+        if (err != null)
+            return new IgniteNodeValidationResult(node.id(), err.getMessage());
+
+        return null;
+    }
+
+    /** {@inheritDoc} */
     @Override public void onJoiningNodeDataReceived(DiscoveryDataBag.JoiningNodeDiscoveryData data) {
         if (data.joiningNodeData() == null)
             return;
@@ -373,16 +395,6 @@ public class IgniteServiceProcessor extends ServiceProcessorAdapter implements I
         for (ServiceInfo desc : joinData.services()) {
             assert desc.topologySnapshot().isEmpty();
 
-            try (OperationSecurityContext ignored = ctx.security().withContext(data.joiningNodeId())) {
-                if (checkPermissions(desc.name(), SERVICE_DEPLOY) != null) {
-                    U.warn(log, "Failed to register service configuration received from joining node :" +
-                        " [nodeId=" + data.joiningNodeId() + ", cfgName=" + desc.name() + "]." +
-                        " Joining node is not authorized to deploy the service.");
-
-                    continue;
-                }
-            }
-
             ServiceInfo oldDesc = registeredServices.get(desc.serviceId());
 
             if (oldDesc != null) { // In case of a collision of IgniteUuid.randomUuid() (almost impossible case)
@@ -599,9 +611,6 @@ public class IgniteServiceProcessor extends ServiceProcessorAdapter implements I
                 err = e;
             }
 
-            if (err == null && (isLocalNodeCoordinator() || ctx.discovery().localJoinFuture().isDone()))
-                err = checkPermissions(cfg.getName(), SERVICE_DEPLOY);
-
             if (err == null) {
                 try {
                     byte[] srvcBytes = U.marshal(marsh, cfg.getService());
@@ -1526,6 +1535,13 @@ public class IgniteServiceProcessor extends ServiceProcessorAdapter implements I
             // First node start, method onGridDataReceived(DiscoveryDataBag.GridDiscoveryData) has not been called.
             ArrayList<ServiceInfo> staticServicesInfo = staticallyConfiguredServices(false);
 
+            if (ctx.security().enabled()) {
+                SecurityException err = checkDeployPermissionDuringJoin(evt.node(), staticServicesInfo);
+
+                if (err != null)
+                    throw err;
+            }
+
             staticServicesInfo.forEach(this::registerService);
         }
 
@@ -1614,7 +1630,7 @@ public class IgniteServiceProcessor extends ServiceProcessorAdapter implements I
             ServiceInfo oldDesc = registeredServices.get(reqSrvcId);
 
             if (req instanceof ServiceDeploymentRequest) {
-                IgniteCheckedException err = null;
+                Exception err = null;
 
                 if (oldDesc != null) { // In case of a collision of IgniteUuid.randomUuid() (almost impossible case)
                     err = new IgniteCheckedException("Failed to deploy service. Service with generated id already" +
@@ -1623,36 +1639,41 @@ public class IgniteServiceProcessor extends ServiceProcessorAdapter implements I
                 else {
                     ServiceConfiguration cfg = ((ServiceDeploymentRequest)req).configuration();
 
-                    oldDesc = lookupInRegisteredServices(cfg.getName());
+                    if (ctx.security().enabled())
+                        err = checkPermissions(((ServiceDeploymentRequest)req).configuration().getName(), SERVICE_DEPLOY);
 
-                    if (oldDesc == null) {
-                        if (cfg.getCacheName() != null && ctx.cache().cacheDescriptor(cfg.getCacheName()) == null) {
-                            err = new IgniteCheckedException("Failed to deploy service, " +
-                                "affinity cache is not found, cfg=" + cfg);
-                        }
-                        else {
-                            ServiceInfo desc = new ServiceInfo(snd.id(), reqSrvcId, cfg);
+                    if (err == null) {
+                        oldDesc = lookupInRegisteredServices(cfg.getName());
 
-                            registerService(desc);
+                        if (oldDesc == null) {
+                            if (cfg.getCacheName() != null && ctx.cache().cacheDescriptor(cfg.getCacheName()) == null) {
+                                err = new IgniteCheckedException("Failed to deploy service, " +
+                                    "affinity cache is not found, cfg=" + cfg);
+                            }
+                            else {
+                                ServiceInfo desc = new ServiceInfo(snd.id(), reqSrvcId, cfg);
 
-                            toDeploy.put(reqSrvcId, desc);
-                        }
-                    }
-                    else {
-                        if (!oldDesc.configuration().equalsIgnoreNodeFilter(cfg)) {
-                            err = new IgniteCheckedException("Failed to deploy service " +
-                                "(service already exists with different configuration) : " +
-                                "[deployed=" + oldDesc.configuration() + ", new=" + cfg + ']');
+                                registerService(desc);
+
+                                toDeploy.put(reqSrvcId, desc);
+                            }
                         }
                         else {
-                            GridServiceDeploymentFuture<IgniteUuid> fut = depFuts.remove(reqSrvcId);
+                            if (!oldDesc.configuration().equalsIgnoreNodeFilter(cfg)) {
+                                err = new IgniteCheckedException("Failed to deploy service " +
+                                    "(service already exists with different configuration) : " +
+                                    "[deployed=" + oldDesc.configuration() + ", new=" + cfg + ']');
+                            }
+                            else {
+                                GridServiceDeploymentFuture<IgniteUuid> fut = depFuts.remove(reqSrvcId);
 
-                            if (fut != null) {
-                                fut.onDone();
+                                if (fut != null) {
+                                    fut.onDone();
 
-                                if (log.isDebugEnabled()) {
-                                    log.debug("Service sent to deploy is already deployed : " +
-                                        "[srvcId=" + oldDesc.serviceId() + ", cfg=" + oldDesc.configuration());
+                                    if (log.isDebugEnabled()) {
+                                        log.debug("Service sent to deploy is already deployed : " +
+                                            "[srvcId=" + oldDesc.serviceId() + ", cfg=" + oldDesc.configuration());
+                                    }
                                 }
                             }
                         }
@@ -1833,4 +1854,38 @@ public class IgniteServiceProcessor extends ServiceProcessorAdapter implements I
     private void leaveBusy() {
         opsLock.readLock().unlock();
     }
+
+    /**
+     * Checks {@link SecurityPermission#SERVICE_DEPLOY} for each service.
+     * This method must use {@link SecurityContext} from node attributes because join not finished in time of validation.
+     * This mean SecurityProcessor doesn't know about joining node and can't return it security context based on node id.
+     *
+     * @param node Node to check.
+     * @param svcs Statically configured services.
+     * @return {@code SecurityException} in case node permissions not enough.
+     * @see ValidationOnNodeJoinUtils
+     */
+    private SecurityException checkDeployPermissionDuringJoin(ClusterNode node, List<ServiceInfo> svcs) {
+        SecurityContext secCtx;
+
+        try {
+            secCtx = nodeSecurityContext(marsh, U.resolveClassLoader(ctx.config()), node);
+
+            assert secCtx != null;
+        }
+        catch (SecurityException err) {
+            return err;
+        }
+
+        try (OperationSecurityContext ignored = ctx.security().withContext(secCtx)) {
+            for (ServiceInfo desc : svcs) {
+                SecurityException err = checkPermissions(desc.name(), SERVICE_DEPLOY);
+
+                if (err != null)
+                    return err;
+            }
+        }
+
+        return null;
+    }
 }
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/service/ServiceAuthorizationTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/service/ServiceAuthorizationTest.java
index 2c156f6..bf7d0f4 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/service/ServiceAuthorizationTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/service/ServiceAuthorizationTest.java
@@ -20,14 +20,20 @@ package org.apache.ignite.internal.processors.security.service;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteServices;
 import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.failure.FailureContext;
+import org.apache.ignite.failure.FailureHandler;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.processors.security.AbstractSecurityTest;
-import org.apache.ignite.internal.processors.security.AbstractTestSecurityPluginProvider;
 import org.apache.ignite.internal.processors.security.impl.TestSecurityPluginProvider;
 import org.apache.ignite.internal.util.typedef.G;
 import org.apache.ignite.internal.util.typedef.X;
@@ -38,8 +44,6 @@ import org.apache.ignite.services.ServiceConfiguration;
 import org.apache.ignite.services.ServiceContext;
 import org.apache.ignite.services.ServiceDeploymentException;
 import org.apache.ignite.testframework.GridTestUtils.RunnableX;
-import org.apache.ignite.testframework.ListeningTestLogger;
-import org.apache.ignite.testframework.LogListener;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -54,8 +58,8 @@ import static org.apache.ignite.plugin.security.SecurityPermission.TASK_EXECUTE;
 import static org.apache.ignite.plugin.security.SecurityPermissionSetBuilder.create;
 import static org.apache.ignite.testframework.GridTestUtils.assertThrowsAnyCause;
 import static org.apache.ignite.testframework.GridTestUtils.assertThrowsWithCause;
+import static org.apache.ignite.testframework.GridTestUtils.runAsync;
 import static org.apache.ignite.testframework.GridTestUtils.waitForCondition;
-import static org.apache.ignite.testframework.LogListener.matches;
 
 /** Tests permissions that are required to perform service operations. */
 @RunWith(Parameterized.class)
@@ -73,8 +77,8 @@ public class ServiceAuthorizationTest extends AbstractSecurityTest {
     /** Index of the node that is forbidden to perform test operation. */
     private static final int FORBIDDEN_NODE_IDX = 2;
 
-    /** Instance of the test logger to check logged messages. */
-    private static ListeningTestLogger listeningLog;
+    /** */
+    private CountDownLatch authErrLatch;
 
     /** Whether a client node is an initiator of the test operations. */
     @Parameterized.Parameter()
@@ -87,20 +91,6 @@ public class ServiceAuthorizationTest extends AbstractSecurityTest {
     }
 
     /** {@inheritDoc} */
-    @Override protected IgniteConfiguration getConfiguration(
-        String instanceName,
-        AbstractTestSecurityPluginProvider pluginProv
-    ) throws Exception {
-        return super.getConfiguration(instanceName, pluginProv)
-            .setGridLogger(listeningLog);
-    }
-
-    /** {@inheritDoc} */
-    @Override protected void beforeTestsStarted() throws Exception {
-        listeningLog = new ListeningTestLogger(log);
-    }
-
-    /** {@inheritDoc} */
     @Override protected void beforeTest() throws Exception {
         super.beforeTest();
 
@@ -196,16 +186,13 @@ public class ServiceAuthorizationTest extends AbstractSecurityTest {
      * {@link SecurityPermission#SERVICE_DEPLOY} permission.
      */
     @Test
-    public void testPreconfiguredServiceDeployment() throws Exception {
+    public void testStartServiceDeployment() throws Exception {
         startClientAllowAll(getTestIgniteInstanceName(1));
 
-        LogListener srvcDeploymentFailedLogLsnr = matches(DEPLOYMENT_AUTHORIZATION_FAILED_ERR).times(2).build();
-
-        listeningLog.registerListener(srvcDeploymentFailedLogLsnr);
-
-        startGrid(configuration(2, SERVICE_INVOKE).setServiceConfiguration(serviceConfiguration()));
-
-        srvcDeploymentFailedLogLsnr.check(getTestTimeout());
+        assertThrowsWithCause(
+            () -> startGrid(configuration(2, SERVICE_INVOKE).setServiceConfiguration(serviceConfiguration())),
+            IgniteCheckedException.class
+        );
 
         checkServiceOnAllNodes(TEST_SERVICE_NAME, false);
 
@@ -230,13 +217,23 @@ public class ServiceAuthorizationTest extends AbstractSecurityTest {
 
             stopGrid(0);
 
-            srvcDeploymentFailedLogLsnr = matches(DEPLOYMENT_AUTHORIZATION_FAILED_ERR).times(1).build();
+            authErrLatch = new CountDownLatch(1);
 
-            listeningLog.registerListener(srvcDeploymentFailedLogLsnr);
+            IgniteInternalFuture<IgniteEx> fut = null;
 
-            startGrid(configuration(0, SERVICE_INVOKE).setServiceConfiguration(serviceConfiguration()));
+            try {
+                fut = runAsync(
+                    () -> startGrid(configuration(2, SERVICE_INVOKE).setServiceConfiguration(serviceConfiguration()))
+                );
 
-            srvcDeploymentFailedLogLsnr.check(getTestTimeout());
+                assertTrue(authErrLatch.await(5, TimeUnit.SECONDS));
+            }
+            finally {
+                authErrLatch = null;
+
+                if (fut != null)
+                    fut.cancel();
+            }
 
             checkServiceOnAllNodes(TEST_SERVICE_NAME, false);
         }
@@ -257,7 +254,7 @@ public class ServiceAuthorizationTest extends AbstractSecurityTest {
     private IgniteConfiguration configuration(int idx, SecurityPermission... perms) throws Exception {
         String name = getTestIgniteInstanceName(idx);
 
-        return getConfiguration(
+        IgniteConfiguration cfg = getConfiguration(
             name,
             new TestSecurityPluginProvider(
                 name,
@@ -275,6 +272,24 @@ public class ServiceAuthorizationTest extends AbstractSecurityTest {
                 false
             )
         ).setClientMode(isClient);
+
+        if (authErrLatch != null) {
+            cfg.setFailureHandler(new FailureHandler() {
+                @Override public boolean onFailure(Ignite ignite, FailureContext failureCtx) {
+                    assertTrue(failureCtx.error() instanceof SecurityException);
+
+                    assertTrue(failureCtx.error().getMessage().startsWith(
+                        "Authorization failed [perm=SERVICE_DEPLOY, name=test-service-name"
+                    ));
+
+                    authErrLatch.countDown();
+
+                    return true;
+                }
+            });
+        }
+
+        return cfg;
     }
 
     /** Checks that service with specified service name is deployed or not on all nodes. */
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/service/ServiceStaticConfigTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/service/ServiceStaticConfigTest.java
new file mode 100644
index 0000000..22a57bd
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/service/ServiceStaticConfigTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.ignite.internal.processors.security.service;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.cluster.ClusterState;
+import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.processors.security.AbstractSecurityTest;
+import org.apache.ignite.internal.util.typedef.G;
+import org.apache.ignite.services.Service;
+import org.apache.ignite.services.ServiceConfiguration;
+import org.apache.ignite.services.ServiceContext;
+import org.junit.Test;
+
+/** */
+public class ServiceStaticConfigTest extends AbstractSecurityTest {
+    /** */
+    private static final String SVC_NAME = "CounterService";
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        cfg.setAuthenticationEnabled(true);
+
+        cfg.setDataStorageConfiguration(new DataStorageConfiguration()
+            .setDefaultDataRegionConfiguration(new DataRegionConfiguration()
+                .setPersistenceEnabled(true)));
+
+        ServiceConfiguration srvcCfg = new ServiceConfiguration();
+
+        srvcCfg.setName(SVC_NAME);
+        srvcCfg.setMaxPerNodeCount(1);
+        srvcCfg.setService(new CounterService());
+
+        cfg.setServiceConfiguration(srvcCfg);
+
+        return cfg;
+    }
+
+    /** */
+    @Test
+    public void testNodeStarted() throws Exception {
+        startGrid(0);
+
+        startGrid(1).cluster().state(ClusterState.ACTIVE);
+
+        assertEquals(2, G.allGrids().size());
+
+        for (Ignite ignite : G.allGrids()) {
+            CounterService svc = ignite.services().service(SVC_NAME);
+
+            assertNotNull(svc);
+            assertNotNull(svc.execLatch);
+            assertTrue(svc.execLatch.await(5, TimeUnit.SECONDS));
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        super.beforeTest();
+
+        cleanPersistenceDir();
+    }
+
+    /** */
+    public static class CounterService implements Service {
+        /** */
+        transient CountDownLatch execLatch;
+
+        /** {@inheritDoc} */
+        @Override public void init(ServiceContext ctx) throws Exception {
+            execLatch = new CountDownLatch(1);
+        }
+
+        /** {@inheritDoc} */
+        @Override public void execute(ServiceContext ctx) throws Exception {
+            execLatch.countDown();
+        }
+
+        /** {@inheritDoc} */
+        @Override public void cancel(ServiceContext ctx) {
+            // No-op.
+        }
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/SecurityTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/SecurityTestSuite.java
index 2a39956..5400f0d 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/SecurityTestSuite.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/SecurityTestSuite.java
@@ -65,6 +65,7 @@ import org.apache.ignite.internal.processors.security.sandbox.SchedulerSandboxTe
 import org.apache.ignite.internal.processors.security.sandbox.SecuritySubjectPermissionsTest;
 import org.apache.ignite.internal.processors.security.scheduler.SchedulerRemoteSecurityContextCheckTest;
 import org.apache.ignite.internal.processors.security.service.ServiceAuthorizationTest;
+import org.apache.ignite.internal.processors.security.service.ServiceStaticConfigTest;
 import org.apache.ignite.internal.processors.security.snapshot.SnapshotPermissionCheckTest;
 import org.apache.ignite.ssl.MultipleSSLContextsTest;
 import org.apache.ignite.tools.junit.JUnitTeamcityReporter;
@@ -130,7 +131,8 @@ import org.junit.runners.Suite;
     MultipleSSLContextsTest.class,
     MaintenanceModeNodeSecurityTest.class,
     DaemonNodeBasicSecurityTest.class,
-    ServiceAuthorizationTest.class
+    ServiceAuthorizationTest.class,
+    ServiceStaticConfigTest.class
 })
 public class SecurityTestSuite {
     /** */