ignite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ale...@apache.org
Subject [ignite] branch master updated: IGNITE-11410 Sandbox for user-defined code - Fixes #6707.
Date Mon, 09 Dec 2019 06:55:23 GMT
This is an automated email from the ASF dual-hosted git repository.

alexpl pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new 3ac9a21  IGNITE-11410 Sandbox for user-defined code - Fixes #6707.
3ac9a21 is described below

commit 3ac9a210d0e720e6e1daeda6f4d13296f7b149b8
Author: d.garus <garus.d.g@gmail.com>
AuthorDate: Mon Dec 9 09:35:06 2019 +0300

    IGNITE-11410 Sandbox for user-defined code - Fixes #6707.
    
    Signed-off-by: Aleksey Plekhanov <plehanov.alex@gmail.com>
---
 .../internal/cluster/ClusterGroupAdapter.java      |  12 +-
 .../deployment/GridDeploymentClassLoader.java      |  17 +-
 .../processors/cache/GridCacheAdapter.java         |  16 +-
 .../processors/cache/GridCacheMapEntry.java        |   4 +-
 .../cache/distributed/dht/GridDhtCacheAdapter.java |  12 +-
 .../cache/query/GridCacheQueryManager.java         |   7 +-
 .../datastreamer/DataStreamerUpdateJob.java        |  13 +-
 .../internal/processors/job/GridJobWorker.java     |   3 +
 .../processors/resource/GridResourceProcessor.java |  12 ++
 .../GridResourceProxiedIgniteInjector.java         |  88 ++++++++
 .../processors/security/GridSecurityProcessor.java |  12 ++
 .../processors/security/IgniteSecurity.java        |  11 +-
 .../security/IgniteSecurityProcessor.java          |  25 +++
 .../security/NoOpIgniteSecurityProcessor.java      |  11 +
 .../processors/security/SecurityUtils.java         | 122 +++++++++++
 .../security/sandbox/AccessControllerSandbox.java  |  74 +++++++
 .../security/sandbox/IgniteDomainCombiner.java     |  52 +++++
 .../processors/security/sandbox/IgniteSandbox.java |  46 ++++
 .../processors/security/sandbox/NoOpSandbox.java   |  36 ++++
 .../sandbox/SandboxIgniteComponentProxy.java       | 110 ++++++++++
 .../ignite/plugin/security/SecuritySubject.java    |  12 ++
 .../processors/security/AbstractSecurityTest.java  | 107 +++++++++-
 .../CacheLoadRemoteSecurityContextCheckTest.java   |  43 +---
 .../compute/ComputePermissionCheckTest.java        |  53 +----
 .../processors/security/impl/TestSecurityData.java |  22 +-
 .../security/impl/TestSecurityPluginProvider.java  |  17 +-
 .../security/impl/TestSecurityProcessor.java       |  21 +-
 .../security/impl/TestSecuritySubject.java         |  16 ++
 .../security/sandbox/AbstractSandboxTest.java      | 128 +++++++++++
 .../security/sandbox/CacheSandboxTest.java         | 134 ++++++++++++
 .../security/sandbox/ComputeSandboxTest.java       | 146 +++++++++++++
 .../security/sandbox/DataStreamerSandboxTest.java  |  57 +++++
 .../sandbox/DoPrivilegedOnRemoteNodeTest.java      | 180 ++++++++++++++++
 .../sandbox/IgniteOperationsInsideSandboxTest.java | 234 +++++++++++++++++++++
 .../sandbox/SecuritySubjectPermissionsTest.java    | 132 ++++++++++++
 .../ignite/testsuites/IgniteBasicTestSuite.java    |   2 -
 .../ignite/testsuites/SecurityTestSuite.java       |  15 +-
 37 files changed, 1877 insertions(+), 125 deletions(-)

diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cluster/ClusterGroupAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/cluster/ClusterGroupAdapter.java
index e5bb47d..7579ea2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cluster/ClusterGroupAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cluster/ClusterGroupAdapter.java
@@ -769,8 +769,7 @@ public class ClusterGroupAdapter implements ClusterGroupEx, Externalizable {
         private boolean clients;
 
         /** Injected Ignite instance. */
-        @IgniteInstanceResource
-        private transient Ignite ignite;
+        private transient IgniteKernal ignite;
 
         /**
          * @param cacheName Cache name.
@@ -785,7 +784,7 @@ public class ClusterGroupAdapter implements ClusterGroupEx, Externalizable {
         /** {@inheritDoc} */
         @SuppressWarnings("RedundantIfStatement")
         @Override public boolean apply(ClusterNode n) {
-            GridDiscoveryManager disco = ((IgniteKernal)ignite).context().discovery();
+            GridDiscoveryManager disco = ignite.context().discovery();
 
             if (affNodes && disco.cacheAffinityNode(n, cacheName))
                 return true;
@@ -801,6 +800,13 @@ public class ClusterGroupAdapter implements ClusterGroupEx, Externalizable {
 
             return false;
         }
+
+        /** */
+        @IgniteInstanceResource
+        private void ignite(Ignite ignite) {
+            if (ignite != null)
+                this.ignite = ignite instanceof IgniteKernal ? (IgniteKernal)ignite : IgnitionEx.gridx(ignite.name());
+        }
     }
 
     /**
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentClassLoader.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentClassLoader.java
index 346c4d2..29fb531 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentClassLoader.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/deployment/GridDeploymentClassLoader.java
@@ -19,6 +19,8 @@ package org.apache.ignite.internal.managers.deployment;
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
+import java.security.Permissions;
+import java.security.ProtectionDomain;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -54,6 +56,17 @@ import org.jetbrains.annotations.Nullable;
  */
 @SuppressWarnings({"CustomClassloader"})
 class GridDeploymentClassLoader extends ClassLoader implements GridDeploymentInfo {
+    /** */
+    private static final ProtectionDomain PROTECTION_DOMAIN;
+
+    static {
+        Permissions perms = new Permissions();
+
+        perms.setReadOnly();
+
+        PROTECTION_DOMAIN = new ProtectionDomain(null, perms);
+    }
+
     /** Class loader ID. */
     private final IgniteUuid id;
 
@@ -520,7 +533,9 @@ class GridDeploymentClassLoader extends ClassLoader implements GridDeploymentInf
                 if (byteMap != null)
                     byteMap.put(path, byteSrc.array());
 
-                cls = defineClass(name, byteSrc.internalArray(), 0, byteSrc.size());
+                cls = ctx.security().sandbox().enabled()
+                    ? defineClass(name, byteSrc.internalArray(), 0, byteSrc.size(), PROTECTION_DOMAIN)
+                    : defineClass(name, byteSrc.internalArray(), 0, byteSrc.size());
 
                 /* Define package in classloader. See URLClassLoader.defineClass(). */
                 int i = name.lastIndexOf('.');
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
index 020b3a2..2789357 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
@@ -6687,8 +6687,7 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
         protected ComputeJobContext jobCtx;
 
         /** Injected grid instance. */
-        @IgniteInstanceResource
-        protected Ignite ignite;
+        protected IgniteEx ignite;
 
         /** Affinity topology version. */
         protected final AffinityTopologyVersion topVer;
@@ -6712,7 +6711,7 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
             if (!waitAffinityReadyFuture())
                 return null;
 
-            IgniteInternalCache cache = ((IgniteEx)ignite).context().cache().cache(cacheName);
+            IgniteInternalCache cache = ignite.context().cache().cache(cacheName);
 
             return localExecute(cache);
         }
@@ -6729,7 +6728,7 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
          * @return {@code True} if topology check passed.
          */
         private boolean waitAffinityReadyFuture() {
-            GridCacheProcessor cacheProc = ((IgniteEx)ignite).context().cache();
+            GridCacheProcessor cacheProc = ignite.context().cache();
 
             AffinityTopologyVersion locTopVer = cacheProc.context().exchange().readyAffinityVersion();
 
@@ -6741,7 +6740,7 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
 
                     fut.listen(new CI1<IgniteInternalFuture<?>>() {
                         @Override public void apply(IgniteInternalFuture<?> t) {
-                            ((IgniteEx)ignite).context().closure().runLocalSafe(new Runnable() {
+                            ignite.context().closure().runLocalSafe(new Runnable() {
                                 @Override public void run() {
                                     jobCtx.callcc();
                                 }
@@ -6755,6 +6754,13 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
 
             return true;
         }
+
+        /** */
+        @IgniteInstanceResource
+        private void ignite(Ignite ignite) {
+            if (ignite != null)
+                this.ignite = ignite instanceof IgniteEx ? (IgniteEx)ignite : IgnitionEx.gridx(ignite.name());
+        }
     }
 
     /**
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
index a064503..b55c121 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
@@ -81,6 +81,7 @@ import org.apache.ignite.internal.processors.cache.version.GridCacheVersionedEnt
 import org.apache.ignite.internal.processors.dr.GridDrType;
 import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheFilter;
 import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitorClosure;
+import org.apache.ignite.internal.processors.security.SecurityUtils;
 import org.apache.ignite.internal.transactions.IgniteTxDuplicateKeyCheckedException;
 import org.apache.ignite.internal.transactions.IgniteTxSerializationCheckedException;
 import org.apache.ignite.internal.util.IgniteTree;
@@ -6693,7 +6694,8 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
          * @return Entry processor return value.
          */
         private IgniteBiTuple<Object, Exception> runEntryProcessor(CacheInvokeEntry<Object, Object> invokeEntry) {
-            EntryProcessor<Object, Object, ?> entryProcessor = (EntryProcessor<Object, Object, ?>)writeObj;
+            EntryProcessor<Object, Object, ?> entryProcessor = SecurityUtils.sandboxedProxy(
+                entry.context().kernalContext(), EntryProcessor.class, (EntryProcessor<Object, Object, ?>)writeObj);
 
             IgniteThread.onEntryProcessorEntered(true);
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheAdapter.java
index da03d00..139c67c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheAdapter.java
@@ -75,6 +75,7 @@ import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
 import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
 import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
 import org.apache.ignite.internal.processors.platform.cache.PlatformCacheEntryFilter;
+import org.apache.ignite.internal.processors.security.SecurityUtils;
 import org.apache.ignite.internal.util.future.GridCompoundFuture;
 import org.apache.ignite.internal.util.future.GridFutureAdapter;
 import org.apache.ignite.internal.util.typedef.CI1;
@@ -624,15 +625,22 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
 
         final ExpiryPolicy plc = plc0 != null ? plc0 : ctx.expiry();
 
-        if (p != null)
+        final IgniteBiPredicate<K, V> pred;
+
+        if (p != null) {
             ctx.kernalContext().resource().injectGeneric(p);
 
+            pred = SecurityUtils.sandboxedProxy(ctx.kernalContext(), IgniteBiPredicate.class, p);
+        }
+        else
+            pred = null;
+
         try {
             ctx.store().loadCache(new CI3<KeyCacheObject, Object, GridCacheVersion>() {
                 @Override public void apply(KeyCacheObject key, Object val, @Nullable GridCacheVersion ver) {
                     assert ver == null;
 
-                    loadEntry(key, val, ver0, p, topVer, replicate, plc);
+                    loadEntry(key, val, ver0, pred, topVer, replicate, plc);
                 }
             }, args);
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java
index 39ce35b9..298841d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java
@@ -88,6 +88,7 @@ import org.apache.ignite.internal.processors.query.GridQueryIndexDescriptor;
 import org.apache.ignite.internal.processors.query.GridQueryProcessor;
 import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
 import org.apache.ignite.internal.processors.query.QueryUtils;
+import org.apache.ignite.internal.processors.security.SecurityUtils;
 import org.apache.ignite.internal.processors.task.GridInternal;
 import org.apache.ignite.internal.util.GridBoundedPriorityQueue;
 import org.apache.ignite.internal.util.GridCloseableIteratorAdapter;
@@ -843,8 +844,10 @@ public abstract class GridCacheQueryManager<K, V> extends GridCacheManagerAdapte
                     qry.mvccSnapshot(), qry.isDataPageScanEnabled());
             }
 
-            ScanQueryIterator iter = new ScanQueryIterator(it, qry, topVer, locPart, keyValFilter, transformer, locNode,
-                locNode ? locIters : null, cctx, log);
+            ScanQueryIterator iter = new ScanQueryIterator(it, qry, topVer, locPart,
+                SecurityUtils.sandboxedProxy(cctx.kernalContext(), IgniteBiPredicate.class, keyValFilter),
+                SecurityUtils.sandboxedProxy(cctx.kernalContext(), IgniteClosure.class, transformer),
+                locNode, locNode ? locIters : null, cctx, log);
 
             if (locNode) {
                 ScanQueryIterator old = locIters.addx(iter);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamerUpdateJob.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamerUpdateJob.java
index ef9ef2d..4628a11 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamerUpdateJob.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamerUpdateJob.java
@@ -24,6 +24,7 @@ import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.processors.cache.CacheObject;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.IgniteCacheProxy;
+import org.apache.ignite.internal.processors.security.SecurityUtils;
 import org.apache.ignite.internal.util.lang.GridPlainCallable;
 import org.apache.ignite.internal.util.typedef.C1;
 import org.apache.ignite.internal.util.typedef.F;
@@ -127,6 +128,8 @@ class DataStreamerUpdateJob implements GridPlainCallable<Object> {
                     checkSecurityPermission(SecurityPermission.CACHE_REMOVE);
             }
 
+            StreamReceiver receiver = SecurityUtils.sandboxedProxy(ctx, StreamReceiver.class, rcvr);
+
             if (unwrapEntries()) {
                 Collection<Map.Entry> col0 = F.viewReadOnly(col, new C1<DataStreamerEntry, Map.Entry>() {
                     @Override public Map.Entry apply(DataStreamerEntry e) {
@@ -134,10 +137,10 @@ class DataStreamerUpdateJob implements GridPlainCallable<Object> {
                     }
                 });
 
-                rcvr.receive(cache, col0);
+                receiver.receive(cache, col0);
             }
             else
-                rcvr.receive(cache, col);
+                receiver.receive(cache, col);
 
             return null;
         }
@@ -163,9 +166,7 @@ class DataStreamerUpdateJob implements GridPlainCallable<Object> {
      */
     private void checkSecurityPermission(SecurityPermission perm)
         throws org.apache.ignite.plugin.security.SecurityException {
-        if (!ctx.security().enabled())
-            return;
-
-        ctx.security().authorize(cacheName, perm);
+        if (ctx.security().enabled())
+            ctx.security().authorize(cacheName, perm);
     }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/job/GridJobWorker.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/job/GridJobWorker.java
index 90e54e5..baf75ae 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/job/GridJobWorker.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/job/GridJobWorker.java
@@ -53,6 +53,7 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.GridReservabl
 import org.apache.ignite.internal.processors.query.GridQueryProcessor;
 import org.apache.ignite.internal.processors.security.OperationSecurityContext;
 import org.apache.ignite.internal.processors.security.SecurityContext;
+import org.apache.ignite.internal.processors.security.SecurityUtils;
 import org.apache.ignite.internal.processors.service.GridServiceNotFoundException;
 import org.apache.ignite.internal.processors.task.GridInternal;
 import org.apache.ignite.internal.processors.timeout.GridTimeoutObject;
@@ -460,6 +461,8 @@ public class GridJobWorker extends GridWorker implements GridTimeoutObject {
 
             if (!internal && ctx.event().isRecordable(EVT_JOB_QUEUED))
                 recordEvent(EVT_JOB_QUEUED, "Job got queued for computation.");
+
+            job = SecurityUtils.sandboxedProxy(ctx, ComputeJob.class, job);
         }
         catch (IgniteCheckedException e) {
             U.error(log, "Failed to initialize job [jobId=" + ses.getJobId() + ", ses=" + ses + ']', e);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/resource/GridResourceProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/resource/GridResourceProcessor.java
index 6d508fe..d148142 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/resource/GridResourceProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/resource/GridResourceProcessor.java
@@ -83,6 +83,18 @@ public class GridResourceProcessor extends GridProcessorAdapter {
     }
 
     /** {@inheritDoc} */
+    @Override public void onKernalStart(boolean active) throws IgniteCheckedException {
+        super.onKernalStart(active);
+
+        // The IgniteSecurity started for this moment,
+        // and we can make a decision on what instance of Ignite injector should be used.
+        if (ctx.security().sandbox().enabled()) {
+            injectorByAnnotation[GridResourceIoc.ResourceAnnotation.IGNITE_INSTANCE.ordinal()] =
+                new GridResourceProxiedIgniteInjector(ctx.grid());
+        }
+    }
+
+    /** {@inheritDoc} */
     @Override public void stop(boolean cancel) {
         ioc.undeployAll();
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/resource/GridResourceProxiedIgniteInjector.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/resource/GridResourceProxiedIgniteInjector.java
new file mode 100644
index 0000000..de9dc45
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/resource/GridResourceProxiedIgniteInjector.java
@@ -0,0 +1,88 @@
+/*
+ * 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.resource;
+
+import java.util.concurrent.Callable;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteDataStreamer;
+import org.apache.ignite.compute.ComputeJob;
+import org.apache.ignite.compute.ComputeTask;
+import org.apache.ignite.internal.managers.deployment.GridDeployment;
+import org.apache.ignite.lang.IgniteBiClosure;
+import org.apache.ignite.lang.IgniteBiPredicate;
+import org.apache.ignite.lang.IgniteCallable;
+import org.apache.ignite.lang.IgniteClosure;
+import org.apache.ignite.lang.IgnitePredicate;
+import org.apache.ignite.lang.IgniteRunnable;
+
+import static org.apache.ignite.internal.processors.security.sandbox.SandboxIgniteComponentProxy.proxy;
+
+/** Ignite instance injector. */
+public class GridResourceProxiedIgniteInjector extends GridResourceBasicInjector<Ignite> {
+    /** Array of classes that should get a proxied instance of Ignite. */
+    private static final Class[] PROXIED_CLASSES = new Class[]{
+        Runnable.class,
+        IgniteRunnable.class,
+        Callable.class,
+        IgniteCallable.class,
+        ComputeTask.class,
+        ComputeJob.class,
+        IgniteClosure.class,
+        IgniteBiClosure.class,
+        IgniteDataStreamer.class,
+        IgnitePredicate.class,
+        IgniteBiPredicate.class,
+    };
+
+    /**
+     * @param rsrc Resource.
+     */
+    public GridResourceProxiedIgniteInjector(Ignite rsrc) {
+        super(rsrc);
+    }
+
+    /** */
+    private Ignite ignite(Object target) {
+        return shouldUseProxy(target) ? proxy(Ignite.class, getResource()) : getResource();
+    }
+
+    /**
+     * @return True if {@code target} should get a proxy instance of Ignite.
+     */
+    private boolean shouldUseProxy(Object target){
+        for (Class cls : PROXIED_CLASSES) {
+            if (cls.isInstance(target))
+                return true;
+        }
+
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void inject(GridResourceField field, Object target, Class<?> depCls, GridDeployment dep)
+        throws IgniteCheckedException {
+        GridResourceUtils.inject(field.getField(), target, ignite(target));
+    }
+
+    /** {@inheritDoc} */
+    @Override public void inject(GridResourceMethod mtd, Object target, Class<?> depCls, GridDeployment dep)
+        throws IgniteCheckedException {
+        GridResourceUtils.inject(mtd.getMethod(), target, ignite(target));
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/security/GridSecurityProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/security/GridSecurityProcessor.java
index f8986c2..a80444b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/security/GridSecurityProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/security/GridSecurityProcessor.java
@@ -22,6 +22,7 @@ import java.util.UUID;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.internal.processors.GridProcessor;
+import org.apache.ignite.internal.processors.security.sandbox.IgniteSandbox;
 import org.apache.ignite.plugin.security.AuthenticationContext;
 import org.apache.ignite.plugin.security.SecurityCredentials;
 import org.apache.ignite.plugin.security.SecurityException;
@@ -99,4 +100,15 @@ public interface GridSecurityProcessor extends GridProcessor {
      */
     @Deprecated
     public boolean enabled();
+
+    /**
+     * If this method returns true and {@link SecurityManager} is installed,
+     * then the user-defined code will be run inside the Sandbox.
+     *
+     * @return True if sandbox is enabled.
+     * @see IgniteSandbox
+     */
+    public default boolean sandboxEnabled() {
+        return false;
+    }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/security/IgniteSecurity.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/security/IgniteSecurity.java
index ea90299..ee73441 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/security/IgniteSecurity.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/security/IgniteSecurity.java
@@ -21,6 +21,7 @@ import java.util.Collection;
 import java.util.UUID;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.processors.security.sandbox.IgniteSandbox;
 import org.apache.ignite.plugin.security.AuthenticationContext;
 import org.apache.ignite.plugin.security.SecurityCredentials;
 import org.apache.ignite.plugin.security.SecurityException;
@@ -40,11 +41,6 @@ import org.apache.ignite.plugin.security.SecuritySubject;
  * </ul>
  */
 public interface IgniteSecurity {
-    /** */
-    static final String MSG_SEC_PROC_CLS_IS_INVALID = "Local node's grid security processor class " +
-        "is not equal to remote node's grid security processor class " +
-        "[locNodeId=%s, rmtNodeId=%s, locCls=%s, rmtCls=%s]";
-
     /**
      * Creates {@link OperationSecurityContext}. All calls of methods {@link #authorize(String, SecurityPermission)} or {@link
      * #authorize(SecurityPermission)} will be processed into the context of passed {@link SecurityContext} until
@@ -121,6 +117,11 @@ public interface IgniteSecurity {
     }
 
     /**
+     * @return Instance of IgniteSandbox.
+     */
+    public IgniteSandbox sandbox();
+
+    /**
      * @return True if IgniteSecurity is a plugin implementation,
      * false if it's used a default NoOp implementation.
      */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/security/IgniteSecurityProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/security/IgniteSecurityProcessor.java
index 782b198..ca23498 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/security/IgniteSecurityProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/security/IgniteSecurityProcessor.java
@@ -26,6 +26,9 @@ import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.processors.GridProcessor;
+import org.apache.ignite.internal.processors.security.sandbox.AccessControllerSandbox;
+import org.apache.ignite.internal.processors.security.sandbox.IgniteSandbox;
+import org.apache.ignite.internal.processors.security.sandbox.NoOpSandbox;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgniteFuture;
@@ -42,6 +45,8 @@ import org.jetbrains.annotations.Nullable;
 
 import static org.apache.ignite.events.EventType.EVT_NODE_FAILED;
 import static org.apache.ignite.events.EventType.EVT_NODE_LEFT;
+import static org.apache.ignite.internal.processors.security.SecurityUtils.MSG_SEC_PROC_CLS_IS_INVALID;
+import static org.apache.ignite.internal.processors.security.SecurityUtils.hasSecurityManager;
 import static org.apache.ignite.internal.processors.security.SecurityUtils.nodeSecurityContext;
 
 /**
@@ -66,6 +71,9 @@ public class IgniteSecurityProcessor implements IgniteSecurity, GridProcessor {
     /** Map of security contexts. Key is the node's id. */
     private final Map<UUID, SecurityContext> secCtxs = new ConcurrentHashMap<>();
 
+    /** Instance of IgniteSandbox. */
+    private IgniteSandbox sandbox;
+
     /**
      * @param ctx Grid kernal context.
      * @param secPrc Security processor.
@@ -152,6 +160,11 @@ public class IgniteSecurityProcessor implements IgniteSecurity, GridProcessor {
     }
 
     /** {@inheritDoc} */
+    @Override public IgniteSandbox sandbox() {
+        return sandbox;
+    }
+
+    /** {@inheritDoc} */
     @Override public boolean enabled() {
         return true;
     }
@@ -161,6 +174,18 @@ public class IgniteSecurityProcessor implements IgniteSecurity, GridProcessor {
         ctx.addNodeAttribute(ATTR_GRID_SEC_PROC_CLASS, secPrc.getClass().getName());
 
         secPrc.start();
+
+        if (hasSecurityManager() && secPrc.sandboxEnabled())
+            sandbox = new AccessControllerSandbox(this);
+        else {
+            if (secPrc.sandboxEnabled()) {
+                ctx.log(getClass()).warning("GridSecurityProcessor#sandboxEnabled returns true, " +
+                    "but system SecurityManager is not defined, " +
+                    "that may be a cause of security lack when IgniteCompute or IgniteCache operations perform.");
+            }
+
+            sandbox = new NoOpSandbox();
+        }
     }
 
     /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/security/NoOpIgniteSecurityProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/security/NoOpIgniteSecurityProcessor.java
index 7a17dc4..a9cbf58 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/security/NoOpIgniteSecurityProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/security/NoOpIgniteSecurityProcessor.java
@@ -22,6 +22,8 @@ import java.util.UUID;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.processors.GridProcessorAdapter;
+import org.apache.ignite.internal.processors.security.sandbox.IgniteSandbox;
+import org.apache.ignite.internal.processors.security.sandbox.NoOpSandbox;
 import org.apache.ignite.plugin.security.AuthenticationContext;
 import org.apache.ignite.plugin.security.SecurityCredentials;
 import org.apache.ignite.plugin.security.SecurityException;
@@ -32,6 +34,7 @@ import org.apache.ignite.spi.discovery.DiscoveryDataBag;
 import org.jetbrains.annotations.Nullable;
 
 import static org.apache.ignite.internal.processors.security.IgniteSecurityProcessor.ATTR_GRID_SEC_PROC_CLASS;
+import static org.apache.ignite.internal.processors.security.SecurityUtils.MSG_SEC_PROC_CLS_IS_INVALID;
 
 /**
  * No operation IgniteSecurity.
@@ -40,6 +43,9 @@ public class NoOpIgniteSecurityProcessor extends GridProcessorAdapter implements
     /** No operation security context. */
     private final OperationSecurityContext opSecCtx = new OperationSecurityContext(this, null);
 
+    /** Instance of IgniteSandbox. */
+    private final IgniteSandbox sandbox = new NoOpSandbox();
+
     /**
      * @param ctx Grid kernal context.
      */
@@ -98,6 +104,11 @@ public class NoOpIgniteSecurityProcessor extends GridProcessorAdapter implements
     }
 
     /** {@inheritDoc} */
+    @Override public IgniteSandbox sandbox() {
+        return sandbox;
+    }
+
+    /** {@inheritDoc} */
     @Override public boolean enabled() {
         return false;
     }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/security/SecurityUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/security/SecurityUtils.java
index b9a5215..d2b1eaf 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/security/SecurityUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/security/SecurityUtils.java
@@ -17,14 +17,27 @@
 
 package org.apache.ignite.internal.processors.security;
 
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.security.AccessController;
+import java.security.AllPermission;
+import java.security.Permissions;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.Callable;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteSystemProperties;
 import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.GridInternalWrapper;
+import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.IgniteNodeAttributes;
+import org.apache.ignite.internal.processors.security.sandbox.IgniteSandbox;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.marshaller.Marshaller;
 import org.apache.ignite.plugin.security.SecurityException;
@@ -34,6 +47,11 @@ import org.apache.ignite.plugin.security.SecurityPermission;
  * Security utilities.
  */
 public class SecurityUtils {
+    /** */
+    public static final String MSG_SEC_PROC_CLS_IS_INVALID = "Local node's grid security processor class " +
+        "is not equal to remote node's grid security processor class " +
+        "[locNodeId=%s, rmtNodeId=%s, locCls=%s, rmtCls=%s]";
+
     /** Default serialization version. */
     private static final int DFLT_SERIALIZE_VERSION = isSecurityCompatibilityMode() ? 1 : 2;
 
@@ -44,6 +62,16 @@ public class SecurityUtils {
         }
     };
 
+    /** Permissions that contain {@code AllPermission}. */
+    public static final Permissions ALL_PERMISSIONS;
+
+    static {
+        ALL_PERMISSIONS = new Permissions();
+
+        ALL_PERMISSIONS.add(new AllPermission());
+        ALL_PERMISSIONS.setReadOnly();
+    }
+
     /**
      * Private constructor.
      */
@@ -113,4 +141,98 @@ public class SecurityUtils {
             throw new SecurityException("Failed to get security context.", e);
         }
     }
+
+    /**
+     * Computes a result in a privileged action.
+     *
+     * @param c Instance of SandboxCallable.
+     * @param <T> Type of result.
+     * @param <E> Type of Exception.
+     * @return Computed result.
+     * @throws E if unable to compute a result.
+     */
+    public static <T, E extends Exception> T doPrivileged(Callable<T> c) throws E {
+        try {
+            return AccessController.doPrivileged((PrivilegedExceptionAction<T>)c::call);
+        }
+        catch (PrivilegedActionException e) {
+            throw (E)e.getException();
+        }
+    }
+
+    /**
+     * @return True if SecurityManager is installed.
+     */
+    public static boolean hasSecurityManager() {
+        return System.getSecurityManager() != null;
+    }
+
+    /**
+     * @return True if class of {@code target} is a system type.
+     */
+    private static boolean isSystemType(GridKernalContext ctx, Object target) {
+        Class cls = target instanceof GridInternalWrapper
+            ? ((GridInternalWrapper)target).userObject().getClass()
+            : target.getClass();
+
+        return ctx.getClass().getClassLoader() == cls.getClassLoader()
+            && ctx.marshallerContext().isSystemType(cls.getName());
+    }
+
+    /**
+     * @return Proxy of {@code instance} if the sandbox is enabled and class of {@code instance} is not a system type
+     * otherwise {@code instance}.
+     */
+    public static <T> T sandboxedProxy(GridKernalContext ctx, final Class cls, final T instance) {
+        if (instance == null)
+            return null;
+
+        Objects.requireNonNull(ctx, "Parameter 'ctx' cannot be null.");
+        Objects.requireNonNull(cls, "Parameter 'cls' cannot be null.");
+
+        final IgniteSandbox sandbox = ctx.security().sandbox();
+
+        if (sandbox.enabled() && !isSystemType(ctx, instance)) {
+            return (T)Proxy.newProxyInstance(sandbox.getClass().getClassLoader(),
+                proxyClasses(cls, instance), new SandboxInvocationHandler(sandbox, instance));
+        }
+
+        return instance;
+    }
+
+    /** Array of proxy classes. */
+    private static <T> Class[] proxyClasses(Class cls, T instance) {
+        return instance instanceof GridInternalWrapper
+            ? new Class[] {cls, GridInternalWrapper.class}
+            : new Class[] {cls};
+    }
+
+    /** */
+    private static class SandboxInvocationHandler<T> implements InvocationHandler {
+        /** */
+        private final IgniteSandbox sandbox;
+
+        /** */
+        private final Object original;
+
+        /** */
+        public SandboxInvocationHandler(IgniteSandbox sandbox, Object original) {
+            this.sandbox = sandbox;
+            this.original = original;
+        }
+
+        /** {@inheritDoc} */
+        @Override public Object invoke(Object proxy, Method mtd, Object[] args) throws Throwable {
+            try {
+                if (proxy instanceof GridInternalWrapper &&
+                    GridInternalWrapper.class.getMethod(mtd.getName(), mtd.getParameterTypes()) != null)
+                    return mtd.invoke(original, args);
+            }
+            catch (NoSuchMethodException e) {
+                // Ignore.
+            }
+
+            return sandbox.execute(() -> (T)mtd.invoke(original, args));
+        }
+    }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/security/sandbox/AccessControllerSandbox.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/security/sandbox/AccessControllerSandbox.java
new file mode 100644
index 0000000..bb3c4cf
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/security/sandbox/AccessControllerSandbox.java
@@ -0,0 +1,74 @@
+/*
+ * 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.sandbox;
+
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.Objects;
+import java.util.concurrent.Callable;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.processors.security.IgniteSecurity;
+import org.apache.ignite.internal.processors.security.SecurityContext;
+import org.apache.ignite.plugin.security.SecurityException;
+
+import static org.apache.ignite.internal.processors.security.SecurityUtils.hasSecurityManager;
+
+/**
+ * Sandbox that based on AccessController.
+ */
+public class AccessControllerSandbox implements IgniteSandbox {
+    /** Instance of IgniteSecurity. */
+    private final IgniteSecurity security;
+
+    /** Constructor. */
+    public AccessControllerSandbox(IgniteSecurity security) {
+        this.security = security;
+    }
+
+    /** {@inheritDoc} */
+    @Override public <T> T execute(Callable<T> c) throws IgniteException {
+        Objects.requireNonNull(c);
+
+        if (!hasSecurityManager())
+            throw new SecurityException("SecurityManager was, but it disappeared!");
+
+        final SecurityContext secCtx = security.securityContext();
+
+        assert secCtx != null;
+
+        final AccessControlContext acc = AccessController.doPrivileged(
+            (PrivilegedAction<AccessControlContext>)() -> new AccessControlContext(AccessController.getContext(),
+                new IgniteDomainCombiner(secCtx.subject().sandboxPermissions()))
+        );
+
+        try {
+            return AccessController.doPrivileged((PrivilegedExceptionAction<T>)c::call, acc);
+        }
+        catch (PrivilegedActionException e) {
+            throw new IgniteException(e.getException());
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean enabled() {
+        return true;
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/security/sandbox/IgniteDomainCombiner.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/security/sandbox/IgniteDomainCombiner.java
new file mode 100644
index 0000000..b8cf4f1
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/security/sandbox/IgniteDomainCombiner.java
@@ -0,0 +1,52 @@
+/*
+ * 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.sandbox;
+
+import java.security.DomainCombiner;
+import java.security.PermissionCollection;
+import java.security.ProtectionDomain;
+
+/**
+ * A {@code IgniteDomainCombiner} updates ProtectionDomains with passed {@code Permissions}.
+ */
+public class IgniteDomainCombiner implements DomainCombiner {
+    /** */
+    private final ProtectionDomain pd;
+
+    /** */
+    public IgniteDomainCombiner(PermissionCollection perms) {
+        pd = new ProtectionDomain(null, perms);
+    }
+
+    /** {@inheritDoc} */
+    @Override public ProtectionDomain[] combine(ProtectionDomain[] currDomains, ProtectionDomain[] assignedDomains) {
+        if (currDomains == null || currDomains.length == 0)
+            return assignedDomains;
+
+        if (assignedDomains == null || assignedDomains.length == 0)
+            return new ProtectionDomain[] {pd};
+
+        ProtectionDomain[] res = new ProtectionDomain[assignedDomains.length + 1];
+
+        res[0] = pd;
+
+        System.arraycopy(assignedDomains, 0, res, 1, assignedDomains.length);
+
+        return res;
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/security/sandbox/IgniteSandbox.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/security/sandbox/IgniteSandbox.java
new file mode 100644
index 0000000..ab04d52
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/security/sandbox/IgniteSandbox.java
@@ -0,0 +1,46 @@
+/*
+ * 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.sandbox;
+
+import java.util.UUID;
+import java.util.concurrent.Callable;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.processors.security.IgniteSecurity;
+import org.apache.ignite.internal.processors.security.SecurityContext;
+import org.apache.ignite.plugin.security.SecuritySubject;
+
+/**
+ * IgniteSandbox executes a user-defined code with restrictions.
+ */
+public interface IgniteSandbox {
+    /**
+     * Executes {@code callable} with constraints defined by current {@code SecuritySubject}.
+     *
+     * @param call Callable to execute.
+     * @return Result of {@code callable}.
+     * @see IgniteSecurity#withContext(UUID)
+     * @see IgniteSecurity#withContext(SecurityContext)
+     * @see SecuritySubject#sandboxPermissions()
+     */
+    public <T> T execute(Callable<T> call) throws IgniteException;
+
+    /**
+     * @return True if the sandbox is enabled.
+     */
+    public boolean enabled();
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/security/sandbox/NoOpSandbox.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/security/sandbox/NoOpSandbox.java
new file mode 100644
index 0000000..15b85d6
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/security/sandbox/NoOpSandbox.java
@@ -0,0 +1,36 @@
+/*
+ * 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.sandbox;
+
+import java.util.concurrent.Callable;
+import org.apache.ignite.IgniteException;
+
+/**
+ * No operation Sandbox.
+ */
+public class NoOpSandbox implements IgniteSandbox {
+    /** {@inheritDoc} */
+    @Override public <T> T execute(Callable<T> call) throws IgniteException {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean enabled() {
+        return false;
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/security/sandbox/SandboxIgniteComponentProxy.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/security/sandbox/SandboxIgniteComponentProxy.java
new file mode 100644
index 0000000..c8863eb
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/security/sandbox/SandboxIgniteComponentProxy.java
@@ -0,0 +1,110 @@
+/*
+ * 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.sandbox;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Objects;
+import java.util.concurrent.ExecutorService;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteAtomicLong;
+import org.apache.ignite.IgniteAtomicReference;
+import org.apache.ignite.IgniteAtomicSequence;
+import org.apache.ignite.IgniteAtomicStamped;
+import org.apache.ignite.IgniteBinary;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteCompute;
+import org.apache.ignite.IgniteCountDownLatch;
+import org.apache.ignite.IgniteDataStreamer;
+import org.apache.ignite.IgniteLock;
+import org.apache.ignite.IgniteQueue;
+import org.apache.ignite.IgniteScheduler;
+import org.apache.ignite.IgniteSemaphore;
+import org.apache.ignite.IgniteSet;
+import org.apache.ignite.IgniteTransactions;
+import org.apache.ignite.cache.affinity.Affinity;
+import org.apache.ignite.internal.processors.security.SecurityUtils;
+
+/** Create instace of Ignite component proxy to use inside the Ignite Sandbox. */
+public final class SandboxIgniteComponentProxy {
+    /** The array of classes that should be proxied. */
+    private static final Class[] PROXIED_CLASSES = new Class[] {
+        Ignite.class,
+        IgniteCache.class,
+        IgniteCompute.class,
+        ExecutorService.class,
+        IgniteScheduler.class,
+        IgniteTransactions.class,
+        IgniteDataStreamer.class,
+        IgniteAtomicSequence.class,
+        IgniteAtomicLong.class,
+        IgniteAtomicReference.class,
+        IgniteAtomicStamped.class,
+        IgniteCountDownLatch.class,
+        IgniteSemaphore.class,
+        IgniteLock.class,
+        IgniteQueue.class,
+        IgniteSet.class,
+        IgniteBinary.class,
+        Affinity.class
+    };
+
+    /**
+     * @return The proxy of {@code instance} to use inside the Ignite Sandbox.
+     */
+    public static <T> T proxy(Class cls, T instance) {
+        Objects.requireNonNull(cls, "Parameter 'cls' cannot be null.");
+        Objects.requireNonNull(instance, "Parameter 'instance' cannot be null.");
+
+        return SecurityUtils.doPrivileged(
+            () -> (T)Proxy.newProxyInstance(cls.getClassLoader(), new Class[] {cls},
+                new SandboxIgniteComponentProxyHandler(instance))
+        );
+    }
+
+    /** */
+    private static class SandboxIgniteComponentProxyHandler implements InvocationHandler {
+        /** */
+        private final Object original;
+
+        /** */
+        public SandboxIgniteComponentProxyHandler(Object original) {
+            this.original = original;
+        }
+
+        /** {@inheritDoc} */
+        @Override public Object invoke(Object proxy, Method mtd, Object[] args) throws Throwable {
+            Object res = SecurityUtils.doPrivileged(() -> mtd.invoke(original, args));
+
+            Class cls = proxiedClass(res);
+
+            return cls != null ? proxy(cls, res) : res;
+        }
+
+        /** */
+        private Class proxiedClass(Object obj) {
+            for (Class cls : PROXIED_CLASSES) {
+                if (cls.isInstance(obj))
+                    return cls;
+            }
+
+            return null;
+        }
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/plugin/security/SecuritySubject.java b/modules/core/src/main/java/org/apache/ignite/plugin/security/SecuritySubject.java
index 66e3c7c..6b3e6f6 100644
--- a/modules/core/src/main/java/org/apache/ignite/plugin/security/SecuritySubject.java
+++ b/modules/core/src/main/java/org/apache/ignite/plugin/security/SecuritySubject.java
@@ -19,7 +19,10 @@ package org.apache.ignite.plugin.security;
 
 import java.io.Serializable;
 import java.net.InetSocketAddress;
+import java.security.PermissionCollection;
+import java.security.ProtectionDomain;
 import java.util.UUID;
+import org.apache.ignite.internal.processors.security.SecurityUtils;
 
 /**
  * Security subject representing authenticated node with a set of permissions.
@@ -59,4 +62,13 @@ public interface SecuritySubject extends Serializable {
      * @return Authorized permission set for the subject.
      */
     public SecurityPermissionSet permissions();
+
+    /**
+     * @return Permissions for SecurityManager checks.
+     */
+    public default PermissionCollection sandboxPermissions() {
+        ProtectionDomain pd = SecurityUtils.doPrivileged(() -> getClass().getProtectionDomain());
+
+        return pd != null ? pd.getPermissions() : SecurityUtils.ALL_PERMISSIONS;
+    }
 }
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/AbstractSecurityTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/AbstractSecurityTest.java
index 13688dc..5907952 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/AbstractSecurityTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/AbstractSecurityTest.java
@@ -17,14 +17,26 @@
 
 package org.apache.ignite.internal.processors.security;
 
+import java.security.Permissions;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import javax.cache.Cache;
+import javax.cache.configuration.Factory;
+import org.apache.ignite.cache.store.CacheStoreAdapter;
 import org.apache.ignite.configuration.DataRegionConfiguration;
 import org.apache.ignite.configuration.DataStorageConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.processors.security.impl.TestSecurityPluginProvider;
+import org.apache.ignite.internal.util.typedef.T2;
+import org.apache.ignite.lang.IgniteBiInClosure;
+import org.apache.ignite.lang.IgniteFuture;
 import org.apache.ignite.plugin.security.SecurityPermission;
 import org.apache.ignite.plugin.security.SecurityPermissionSet;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.jetbrains.annotations.NotNull;
 
 import static org.apache.ignite.plugin.security.SecurityPermissionSetBuilder.ALLOW_ALL;
 
@@ -79,7 +91,98 @@ public class AbstractSecurityTest extends GridCommonAbstractTest {
      * @param isClient Is client.
      */
     protected IgniteEx startGrid(String login, SecurityPermissionSet prmSet, boolean isClient) throws Exception {
-        return startGrid(getConfiguration(login, new TestSecurityPluginProvider(login, "", prmSet, globalAuth))
-                .setClientMode(isClient));
+        return startGrid(login, prmSet, null, isClient);
+    }
+
+    /** */
+    protected IgniteEx startGrid(String login, SecurityPermissionSet prmSet,
+        Permissions sandboxPerms, boolean isClient) throws Exception {
+        return startGrid(getConfiguration(login,
+            new TestSecurityPluginProvider(login, "", prmSet, sandboxPerms, globalAuth))
+            .setClientMode(isClient));
+    }
+
+    /** */
+    protected static class TestFutureAdapter<T> implements Future<T> {
+        /** */
+        private final IgniteFuture<T> igniteFut;
+
+        /** */
+        public TestFutureAdapter(IgniteFuture<T> igniteFut) {
+            this.igniteFut = igniteFut;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean cancel(boolean mayInterruptIfRunning) {
+            return igniteFut.cancel();
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean isCancelled() {
+            return igniteFut.isCancelled();
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean isDone() {
+            return igniteFut.isDone();
+        }
+
+        /** {@inheritDoc} */
+        @Override public T get() throws InterruptedException, ExecutionException {
+            return igniteFut.get();
+        }
+
+        /** {@inheritDoc} */
+        @Override public T get(long timeout,
+            @NotNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+            return igniteFut.get(timeout, unit);
+        }
+    }
+
+    /** */
+    protected static class TestStoreFactory implements Factory<TestCacheStore> {
+        /** */
+        private final T2<Object, Object> keyVal;
+
+        /** */
+        public TestStoreFactory(Object key, Object val) {
+            keyVal = new T2<>(key, val);
+        }
+
+        /** {@inheritDoc} */
+        @Override public TestCacheStore create() {
+            return new TestCacheStore(keyVal);
+        }
+    }
+
+    /** */
+    private static class TestCacheStore extends CacheStoreAdapter<Object, Object> {
+        /** */
+        private final T2<Object, Object> keyVal;
+
+        /** Constructor. */
+        public TestCacheStore(T2<Object, Object> keyVal) {
+            this.keyVal = keyVal;
+        }
+
+        /** {@inheritDoc} */
+        @Override public void loadCache(IgniteBiInClosure<Object, Object> clo, Object... args) {
+            clo.apply(keyVal.getKey(), keyVal.getValue());
+        }
+
+        /** {@inheritDoc} */
+        @Override public Object load(Object key) {
+            return key;
+        }
+
+        /** {@inheritDoc} */
+        @Override public void write(Cache.Entry<?, ?> entry) {
+            throw new UnsupportedOperationException();
+        }
+
+        /** {@inheritDoc} */
+        @Override public void delete(Object key) {
+            // No-op.
+        }
     }
 }
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/cache/closure/CacheLoadRemoteSecurityContextCheckTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/cache/closure/CacheLoadRemoteSecurityContextCheckTest.java
index 00b0374..fea44fd 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/cache/closure/CacheLoadRemoteSecurityContextCheckTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/cache/closure/CacheLoadRemoteSecurityContextCheckTest.java
@@ -20,14 +20,10 @@ package org.apache.ignite.internal.processors.security.cache.closure;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.UUID;
-import javax.cache.Cache;
-import javax.cache.configuration.Factory;
 import org.apache.ignite.cache.CacheMode;
-import org.apache.ignite.cache.store.CacheStoreAdapter;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.internal.processors.security.AbstractCacheOperationRemoteSecurityContextCheckTest;
 import org.apache.ignite.internal.util.typedef.G;
-import org.apache.ignite.lang.IgniteBiInClosure;
 import org.apache.ignite.lang.IgniteRunnable;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -70,11 +66,11 @@ public class CacheLoadRemoteSecurityContextCheckTest extends AbstractCacheOperat
             new CacheConfiguration<Integer, Integer>()
                 .setName(CACHE_NAME)
                 .setCacheMode(CacheMode.PARTITIONED)
-                .setCacheStoreFactory(new TestStoreFactory()),
+                .setCacheStoreFactory(new TestStoreFactory(1, 1)),
             new CacheConfiguration<Integer, Integer>()
                 .setName(TRANSITION_LOAD_CACHE)
                 .setCacheMode(CacheMode.PARTITIONED)
-                .setCacheStoreFactory(new TestStoreFactory())
+                .setCacheStoreFactory(new TestStoreFactory(1, 1))
         };
     }
 
@@ -111,39 +107,4 @@ public class CacheLoadRemoteSecurityContextCheckTest extends AbstractCacheOperat
     @Override protected Collection<UUID> nodesToCheck() {
         return Collections.singletonList(nodeId(SRV_CHECK));
     }
-
-    /**
-     * Test store factory.
-     */
-    private static class TestStoreFactory implements Factory<TestCacheStore> {
-        /** {@inheritDoc} */
-        @Override public TestCacheStore create() {
-            return new TestCacheStore();
-        }
-    }
-
-    /**
-     * Test cache store.
-     */
-    private static class TestCacheStore extends CacheStoreAdapter<Integer, Integer> {
-        /** {@inheritDoc} */
-        @Override public void loadCache(IgniteBiInClosure<Integer, Integer> clo, Object... args) {
-            clo.apply(1, 1);
-        }
-
-        /** {@inheritDoc} */
-        @Override public Integer load(Integer key) {
-            return key;
-        }
-
-        /** {@inheritDoc} */
-        @Override public void write(Cache.Entry<? extends Integer, ? extends Integer> entry) {
-            throw new UnsupportedOperationException();
-        }
-
-        /** {@inheritDoc} */
-        @Override public void delete(Object key) {
-            // No-op.
-        }
-    }
 }
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/compute/ComputePermissionCheckTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/compute/ComputePermissionCheckTest.java
index 1fa6943..df83295 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/compute/ComputePermissionCheckTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/compute/ComputePermissionCheckTest.java
@@ -21,10 +21,8 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.Function;
@@ -40,14 +38,12 @@ import org.apache.ignite.compute.ComputeTask;
 import org.apache.ignite.internal.processors.security.AbstractSecurityTest;
 import org.apache.ignite.lang.IgniteCallable;
 import org.apache.ignite.lang.IgniteClosure;
-import org.apache.ignite.lang.IgniteFuture;
 import org.apache.ignite.lang.IgniteRunnable;
 import org.apache.ignite.plugin.security.SecurityException;
 import org.apache.ignite.plugin.security.SecurityPermission;
 import org.apache.ignite.plugin.security.SecurityPermissionSet;
 import org.apache.ignite.plugin.security.SecurityPermissionSetBuilder;
 import org.apache.ignite.testframework.GridTestUtils.RunnableX;
-import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -169,11 +165,11 @@ public class ComputePermissionCheckTest extends AbstractSecurityTest {
     /** */
     private Stream<Supplier<Future>> asyncOperations(Ignite... nodes) {
         Function<Ignite, Stream<Supplier<Future>>> nodeOps = (node) -> Stream.of(
-            () -> new FutureAdapter<>(node.compute().executeAsync(TEST_COMPUTE_TASK, 0)),
-            () -> new FutureAdapter<>(node.compute().broadcastAsync(TEST_CALLABLE)),
-            () -> new FutureAdapter<>(node.compute().callAsync(TEST_CALLABLE)),
-            () -> new FutureAdapter<>(node.compute().runAsync(TEST_RUNNABLE)),
-            () -> new FutureAdapter<>(node.compute().applyAsync(TEST_CLOSURE, new Object())),
+            () -> new TestFutureAdapter<>(node.compute().executeAsync(TEST_COMPUTE_TASK, 0)),
+            () -> new TestFutureAdapter<>(node.compute().broadcastAsync(TEST_CALLABLE)),
+            () -> new TestFutureAdapter<>(node.compute().callAsync(TEST_CALLABLE)),
+            () -> new TestFutureAdapter<>(node.compute().runAsync(TEST_RUNNABLE)),
+            () -> new TestFutureAdapter<>(node.compute().applyAsync(TEST_CLOSURE, new Object())),
             () -> node.executorService().submit(TEST_CALLABLE)
         );
 
@@ -221,43 +217,6 @@ public class ComputePermissionCheckTest extends AbstractSecurityTest {
         }
     }
 
-    /** */
-    private static class FutureAdapter<T> implements Future<T> {
-        /** Ignite future. */
-        private final IgniteFuture<T> igniteFut;
-
-        /** */
-        public FutureAdapter(IgniteFuture<T> igniteFut) {
-            this.igniteFut = igniteFut;
-        }
-
-        /** {@inheritDoc} */
-        @Override public boolean cancel(boolean mayInterruptIfRunning) {
-            return igniteFut.cancel();
-        }
-
-        /** {@inheritDoc} */
-        @Override public boolean isCancelled() {
-            return igniteFut.isCancelled();
-        }
-
-        /** {@inheritDoc} */
-        @Override public boolean isDone() {
-            return igniteFut.isDone();
-        }
-
-        /** {@inheritDoc} */
-        @Override public T get() throws InterruptedException, ExecutionException {
-            return igniteFut.get();
-        }
-
-        /** {@inheritDoc} */
-        @Override public T get(long timeout,
-            @NotNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
-            return igniteFut.get(timeout, unit);
-        }
-    }
-
     /**
      * Abstract test compute task.
      */
@@ -270,7 +229,7 @@ public class ComputePermissionCheckTest extends AbstractSecurityTest {
             return Collections.singletonMap(
                 new ComputeJob() {
                     @Override public void cancel() {
-                        // no-op
+                        // No-op.
                     }
 
                     @Override public Object execute() {
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecurityData.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecurityData.java
index 66bc171..1c85db0 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecurityData.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecurityData.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.processors.security.impl;
 
+import java.security.Permissions;
 import org.apache.ignite.plugin.security.SecurityCredentials;
 import org.apache.ignite.plugin.security.SecurityPermissionSet;
 
@@ -33,6 +34,9 @@ public class TestSecurityData {
     /** Security permission set. */
     private SecurityPermissionSet prmSet;
 
+    /** */
+    private Permissions sandboxPerms;
+
     /**
      * Default constructor.
      */
@@ -45,10 +49,12 @@ public class TestSecurityData {
      * @param pwd Password.
      * @param prmSet Permissions.
      */
-    public TestSecurityData(String login, String pwd, SecurityPermissionSet prmSet) {
+    public TestSecurityData(String login, String pwd, SecurityPermissionSet prmSet,
+        Permissions sandboxPerms) {
         this.login = login;
         this.pwd = pwd;
         this.prmSet = prmSet;
+        this.sandboxPerms = sandboxPerms;
     }
 
     /**
@@ -56,7 +62,7 @@ public class TestSecurityData {
      * @param prmSet Permissions.
      */
     public TestSecurityData(String login, SecurityPermissionSet prmSet) {
-        this(login, "", prmSet);
+        this(login, "", prmSet, new Permissions());
     }
 
     /**
@@ -75,6 +81,18 @@ public class TestSecurityData {
         return this;
     }
 
+    /** */
+    public Permissions sandboxPermissions() {
+        return sandboxPerms;
+    }
+
+    /** */
+    public TestSecurityData sandboxPermissions(Permissions perms) {
+        sandboxPerms = perms;
+
+        return this;
+    }
+
     /**
      * Login.
      */
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecurityPluginProvider.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecurityPluginProvider.java
index 5ee18ff..278f18c 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecurityPluginProvider.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecurityPluginProvider.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.processors.security.impl;
 
+import java.security.Permissions;
 import java.util.Arrays;
 import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.processors.security.AbstractTestSecurityPluginProvider;
@@ -34,6 +35,9 @@ public class TestSecurityPluginProvider extends AbstractTestSecurityPluginProvid
     /** Permissions. */
     private final SecurityPermissionSet perms;
 
+    /** */
+    private final Permissions sandboxPerms;
+
     /** Global authentication. */
     private final boolean globalAuth;
 
@@ -41,11 +45,18 @@ public class TestSecurityPluginProvider extends AbstractTestSecurityPluginProvid
     private final TestSecurityData[] clientData;
 
     /** */
-    public TestSecurityPluginProvider(String login, String pwd, SecurityPermissionSet perms, boolean globalAuth,
-        TestSecurityData... clientData) {
+    public TestSecurityPluginProvider(String login, String pwd, SecurityPermissionSet perms,
+        boolean globalAuth, TestSecurityData... clientData) {
+        this(login, pwd, perms, null, globalAuth, clientData);
+    }
+
+    /** */
+    public TestSecurityPluginProvider(String login, String pwd, SecurityPermissionSet perms,
+        Permissions sandboxPerms, boolean globalAuth, TestSecurityData... clientData) {
         this.login = login;
         this.pwd = pwd;
         this.perms = perms;
+        this.sandboxPerms = sandboxPerms != null ? sandboxPerms : new Permissions();
         this.globalAuth = globalAuth;
         this.clientData = clientData.clone();
     }
@@ -53,7 +64,7 @@ public class TestSecurityPluginProvider extends AbstractTestSecurityPluginProvid
     /** {@inheritDoc} */
     @Override protected GridSecurityProcessor securityProcessor(GridKernalContext ctx) {
         return new TestSecurityProcessor(ctx,
-            new TestSecurityData(login, pwd, perms),
+            new TestSecurityData(login, pwd, perms, sandboxPerms),
             Arrays.asList(clientData),
             globalAuth);
     }
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecurityProcessor.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecurityProcessor.java
index b62f97b..294c5c6 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecurityProcessor.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecurityProcessor.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.internal.processors.security.impl;
 
 import java.net.InetSocketAddress;
+import java.security.Permissions;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -48,6 +49,9 @@ public class TestSecurityProcessor extends GridProcessorAdapter implements GridS
     /** Permissions. */
     public static final Map<SecurityCredentials, SecurityPermissionSet> PERMS = new ConcurrentHashMap<>();
 
+    /** Sandbox permissions. */
+    private static final Map<SecurityCredentials, Permissions> SANDBOX_PERMS = new ConcurrentHashMap<>();
+
     /** Node security data. */
     private final TestSecurityData nodeSecData;
 
@@ -83,6 +87,7 @@ public class TestSecurityProcessor extends GridProcessorAdapter implements GridS
                 .setAddr(new InetSocketAddress(F.first(node.addresses()), 0))
                 .setLogin(cred.getLogin())
                 .setPerms(PERMS.get(cred))
+                .sandboxPermissions(SANDBOX_PERMS.get(cred))
         );
     }
 
@@ -103,6 +108,7 @@ public class TestSecurityProcessor extends GridProcessorAdapter implements GridS
                 .setAddr(ctx.address())
                 .setLogin(ctx.credentials().getLogin())
                 .setPerms(PERMS.get(ctx.credentials()))
+                .sandboxPermissions(SANDBOX_PERMS.get(ctx.credentials()))
         );
     }
 
@@ -140,11 +146,14 @@ public class TestSecurityProcessor extends GridProcessorAdapter implements GridS
         super.start();
 
         PERMS.put(nodeSecData.credentials(), nodeSecData.getPermissions());
+        SANDBOX_PERMS.put(nodeSecData.credentials(), nodeSecData.sandboxPermissions());
 
         ctx.addNodeAttribute(IgniteNodeAttributes.ATTR_SECURITY_CREDENTIALS, nodeSecData.credentials());
 
-        for (TestSecurityData data : predefinedAuthData)
+        for (TestSecurityData data : predefinedAuthData) {
             PERMS.put(data.credentials(), data.getPermissions());
+            SANDBOX_PERMS.put(nodeSecData.credentials(), data.sandboxPermissions());
+        }
     }
 
     /** {@inheritDoc} */
@@ -152,8 +161,16 @@ public class TestSecurityProcessor extends GridProcessorAdapter implements GridS
         super.stop(cancel);
 
         PERMS.remove(nodeSecData.credentials());
+        SANDBOX_PERMS.remove(nodeSecData.credentials());
 
-        for (TestSecurityData data : predefinedAuthData)
+        for (TestSecurityData data : predefinedAuthData) {
             PERMS.remove(data.credentials());
+            SANDBOX_PERMS.remove(data.credentials());
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean sandboxEnabled() {
+        return true;
     }
 }
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecuritySubject.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecuritySubject.java
index 6fa47cf..c41026e 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecuritySubject.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecuritySubject.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.internal.processors.security.impl;
 
 import java.net.InetSocketAddress;
+import java.security.PermissionCollection;
 import java.util.UUID;
 import org.apache.ignite.plugin.security.SecurityPermissionSet;
 import org.apache.ignite.plugin.security.SecuritySubject;
@@ -42,6 +43,9 @@ public class TestSecuritySubject implements SecuritySubject {
     /** Permissions. */
     private SecurityPermissionSet perms;
 
+    /** Permissions for Sandbox checks. */
+    private PermissionCollection sandboxPerms;
+
     /**
      * Default constructor.
      */
@@ -136,6 +140,18 @@ public class TestSecuritySubject implements SecuritySubject {
     }
 
     /** {@inheritDoc} */
+    @Override public PermissionCollection sandboxPermissions() {
+        return sandboxPerms;
+    }
+
+    /** */
+    public TestSecuritySubject sandboxPermissions(PermissionCollection perms) {
+        sandboxPerms = perms;
+
+        return this;
+    }
+
+    /** {@inheritDoc} */
     @Override public String toString() {
         return "TestSecuritySubject{" +
             "id=" + id +
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/sandbox/AbstractSandboxTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/sandbox/AbstractSandboxTest.java
new file mode 100644
index 0000000..1bee9060
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/sandbox/AbstractSandboxTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.sandbox;
+
+import java.security.AllPermission;
+import java.security.CodeSource;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.util.PropertyPermission;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.internal.processors.security.AbstractSecurityTest;
+import org.apache.ignite.testframework.GridTestUtils;
+
+import static org.apache.ignite.plugin.security.SecurityPermissionSetBuilder.ALLOW_ALL;
+import static org.apache.ignite.testframework.GridTestUtils.assertThrowsWithCause;
+
+/** */
+public abstract class AbstractSandboxTest extends AbstractSecurityTest {
+    /** */
+    protected static final String TEST_CACHE = "test_cache";
+
+    /** */
+    protected static boolean setupSM;
+
+    /** Sever node name. */
+    protected static final String SRV = "srv";
+
+    /** Client node that can write to test property. */
+    protected static final String CLNT_ALLOWED_WRITE_PROP = "clnt_allowed";
+
+    /** Client node that cannot write to the test property. */
+    protected static final String CLNT_FORBIDDEN_WRITE_PROP = "clnt_forbidden";
+
+    /** Test property name. */
+    private static final String PROP_NAME = "test.sandbox.property";
+
+    /** Test property value. */
+    private static final String PROP_VALUE = "propertyValue";
+
+    /** */
+    protected static void controlAction(){
+        System.setProperty(PROP_NAME, PROP_VALUE);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        if (System.getSecurityManager() == null) {
+            Policy.setPolicy(new Policy() {
+                @Override public PermissionCollection getPermissions(CodeSource cs) {
+                    Permissions res = new Permissions();
+
+                    res.add(new AllPermission());
+
+                    return res;
+                }
+            });
+
+            System.setSecurityManager(new SecurityManager());
+
+            setupSM = true;
+        }
+
+        prepareCluster();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        super.afterTestsStopped();
+
+        if (setupSM) {
+            System.setSecurityManager(null);
+            Policy.setPolicy(null);
+        }
+    }
+
+    /** */
+    protected void prepareCluster() throws Exception {
+        Ignite srv = startGrid(SRV, ALLOW_ALL, false);
+
+        Permissions perms = new Permissions();
+
+        perms.add(new PropertyPermission(PROP_NAME, "write"));
+
+        startGrid(CLNT_ALLOWED_WRITE_PROP, ALLOW_ALL, perms, true);
+
+        startGrid(CLNT_FORBIDDEN_WRITE_PROP, ALLOW_ALL, true);
+
+        srv.cluster().active(true);
+    }
+
+    /**
+     * @param r Runnable that runs {@link AbstractSandboxTest#controlAction()}.
+     */
+    protected void runOperation(Runnable r) {
+        System.clearProperty(PROP_NAME);
+
+        r.run();
+
+        assertEquals(PROP_VALUE, System.getProperty(PROP_NAME));
+    }
+
+    /**
+     * @param r RunnableX that that runs {@link AbstractSandboxTest#controlAction()}.
+     */
+    protected void runForbiddenOperation(GridTestUtils.RunnableX r, Class<? extends Throwable> cls) {
+        System.clearProperty(PROP_NAME);
+
+        assertThrowsWithCause(r, cls);
+
+        assertNull(System.getProperty(PROP_NAME));
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/sandbox/CacheSandboxTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/sandbox/CacheSandboxTest.java
new file mode 100644
index 0000000..1663ecf
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/sandbox/CacheSandboxTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.sandbox;
+
+import java.security.AccessControlException;
+import java.util.stream.Stream;
+import javax.cache.processor.EntryProcessorResult;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteDataStreamer;
+import org.apache.ignite.cache.CacheEntryProcessor;
+import org.apache.ignite.cache.query.ScanQuery;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.lang.IgniteBiPredicate;
+import org.apache.ignite.testframework.GridTestUtils.RunnableX;
+import org.junit.Test;
+
+import static java.util.Collections.singleton;
+
+/**
+ * Checks that user-defined code for cache operations is executed inside the sandbox.
+ */
+public class CacheSandboxTest extends AbstractSandboxTest {
+    /** */
+    private static final CacheEntryProcessor<Object, Object, Object> TEST_PROC = (entry, o) -> {
+        controlAction();
+
+        return null;
+    };
+
+    /** */
+    private static final IgniteBiPredicate<String, String> TEST_PRED = (a, b) -> {
+        controlAction();
+
+        return true;
+    };
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        return super.getConfiguration(igniteInstanceName)
+            .setCacheConfiguration(
+                new CacheConfiguration<String, String>(TEST_CACHE)
+                    .setCacheStoreFactory(new TestStoreFactory("1", "val"))
+            );
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        super.beforeTestsStarted();
+
+        populateCache();
+    }
+
+    /** */
+    @Test
+    public void testEntryProcessor() {
+        entryProcessorOperations(grid(CLNT_ALLOWED_WRITE_PROP)).forEach(this::runOperation);
+        entryProcessorOperations(grid(CLNT_FORBIDDEN_WRITE_PROP))
+            .forEach(r -> runForbiddenOperation(r, AccessControlException.class));
+    }
+
+    /** */
+    @Test
+    public void testScanQuery() {
+        scanQueryOperations(grid(CLNT_ALLOWED_WRITE_PROP)).forEach(this::runOperation);
+        scanQueryOperations(grid(CLNT_FORBIDDEN_WRITE_PROP))
+            .forEach(r -> runForbiddenOperation(r, AccessControlException.class));
+    }
+
+    /** */
+    @Test
+    public void testLoadCache() {
+        runOperation(() -> grid(CLNT_ALLOWED_WRITE_PROP).<String, String>cache(TEST_CACHE).loadCache(TEST_PRED));
+        runForbiddenOperation(() -> grid(CLNT_FORBIDDEN_WRITE_PROP)
+            .<String, String>cache(TEST_CACHE).loadCache(TEST_PRED), AccessControlException.class);
+    }
+
+    /**
+     * @return EntryProcessor operations to test.
+     */
+    private Stream<RunnableX> entryProcessorOperations(Ignite node) {
+        EntryProcessorResult<Object> dflt = () -> null;
+
+        return Stream.of(
+            () -> node.cache(TEST_CACHE).invoke("key", TEST_PROC),
+            () -> node.cache(TEST_CACHE).invokeAll(singleton("key"), TEST_PROC)
+                .getOrDefault("key", dflt).get(),
+            () -> node.cache(TEST_CACHE).invokeAsync("key", TEST_PROC).get(),
+            () -> node.cache(TEST_CACHE).invokeAllAsync(singleton("key"), TEST_PROC).get()
+                .getOrDefault("key", dflt).get()
+        );
+    }
+
+    /**
+     * @return ScanQuery operations to test.
+     */
+    private Stream<RunnableX> scanQueryOperations(Ignite node) {
+        return Stream.of(
+            () -> node.cache(TEST_CACHE).query(new ScanQuery<>((o, o2) -> {
+                controlAction();
+
+                return false;
+            })).getAll(),
+            () -> node.cache(TEST_CACHE).query(new ScanQuery<>((k, v) -> true), e -> {
+                controlAction();
+
+                return null;
+            }).getAll()
+        );
+    }
+
+    /** */
+    private void populateCache() {
+        try (IgniteDataStreamer<String, Integer> cache = grid(SRV).dataStreamer(TEST_CACHE)) {
+            for (int i = 1; i <= 10; i++)
+                cache.addData(Integer.toString(i), i);
+        }
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/sandbox/ComputeSandboxTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/sandbox/ComputeSandboxTest.java
new file mode 100644
index 0000000..fe3f27b
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/sandbox/ComputeSandboxTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.sandbox;
+
+import java.security.AccessControlException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.compute.ComputeJob;
+import org.apache.ignite.compute.ComputeJobResult;
+import org.apache.ignite.compute.ComputeJobResultPolicy;
+import org.apache.ignite.compute.ComputeTask;
+import org.apache.ignite.lang.IgniteCallable;
+import org.apache.ignite.lang.IgniteClosure;
+import org.apache.ignite.lang.IgniteRunnable;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.jetbrains.annotations.Nullable;
+import org.junit.Test;
+
+import static java.util.Collections.singletonList;
+
+/**
+ * Checks that user-defined code for compute operations is executed inside the sandbox.
+ */
+public class ComputeSandboxTest extends AbstractSandboxTest {
+    /** */
+    private static final TestComputeTask COMPUTE_TASK = new TestComputeTask();
+
+    /** */
+    private static final IgniteCallable<Object> CALLABLE = () -> {
+        controlAction();
+
+        return null;
+    };
+
+    /** */
+    private static final IgniteClosure<Object, Object> CLOSURE = a -> {
+        controlAction();
+
+        return null;
+    };
+
+    /** */
+    private static final IgniteRunnable RUNNABLE = AbstractSandboxTest::controlAction;
+
+    /** */
+    @Test
+    public void testCompute(){
+        computeOperations(grid(CLNT_ALLOWED_WRITE_PROP)).forEach(this::runOperation);
+        computeOperations(grid(CLNT_FORBIDDEN_WRITE_PROP))
+            .forEach(op -> runForbiddenOperation(op, AccessControlException.class));
+    }
+
+    /** */
+    @Test
+    public void testExecutorService(){
+        executorServiceOperations(grid(CLNT_ALLOWED_WRITE_PROP)).forEach(this::runOperation);
+        executorServiceOperations(grid(CLNT_FORBIDDEN_WRITE_PROP))
+            .forEach(op -> runForbiddenOperation(op, IgniteException.class));
+    }
+
+    /**
+     * @return Stream of Compute operations to test.
+     */
+    private Stream<GridTestUtils.RunnableX> computeOperations(Ignite node) {
+        return Stream.of(
+            () -> node.compute().execute(COMPUTE_TASK, 0),
+            () -> node.compute().broadcast(CALLABLE),
+            () -> node.compute().call(CALLABLE),
+            () -> node.compute().run(RUNNABLE),
+            () -> node.compute().apply(CLOSURE, new Object()),
+
+            () -> new TestFutureAdapter<>(node.compute().executeAsync(COMPUTE_TASK, 0)).get(),
+            () -> new TestFutureAdapter<>(node.compute().broadcastAsync(CALLABLE)).get(),
+            () -> new TestFutureAdapter<>(node.compute().callAsync(CALLABLE)).get(),
+            () -> new TestFutureAdapter<>(node.compute().runAsync(RUNNABLE)).get(),
+            () -> new TestFutureAdapter<>(node.compute().applyAsync(CLOSURE, new Object())).get()
+        );
+    }
+
+    /**
+     * @return Stream of ExecutorService operations to test.
+     */
+    private Stream<GridTestUtils.RunnableX> executorServiceOperations(Ignite node) {
+        return Stream.of(
+            () -> node.executorService().invokeAll(singletonList(CALLABLE))
+                .stream().findFirst().orElseThrow(IgniteException::new).get(),
+            () -> node.executorService().invokeAny(singletonList(CALLABLE)),
+            () -> node.executorService().submit(CALLABLE).get()
+        );
+    }
+
+    /** */
+    static class TestComputeTask implements ComputeTask<Object, Object> {
+        /** {@inheritDoc} */
+        @Override public Map<? extends ComputeJob, ClusterNode> map(List<ClusterNode> subgrid,
+            Object arg) throws IgniteException {
+            return Collections.singletonMap(
+                new ComputeJob() {
+                    @Override public void cancel() {
+                        // No-op.
+                    }
+
+                    @Override public Object execute() {
+                        controlAction();
+
+                        return null;
+                    }
+                }, subgrid.stream().findFirst().orElseThrow(IllegalStateException::new)
+            );
+        }
+
+        /** {@inheritDoc} */
+        @Override public ComputeJobResultPolicy result(ComputeJobResult res,
+            List<ComputeJobResult> rcvd) throws IgniteException {
+            if (res.getException() != null)
+                throw res.getException();
+
+            return ComputeJobResultPolicy.REDUCE;
+        }
+
+        /** {@inheritDoc} */
+        @Override public @Nullable Object reduce(List<ComputeJobResult> results) throws IgniteException {
+            return null;
+        }
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/sandbox/DataStreamerSandboxTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/sandbox/DataStreamerSandboxTest.java
new file mode 100644
index 0000000..e842a16
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/sandbox/DataStreamerSandboxTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.sandbox;
+
+import java.security.AccessControlException;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteDataStreamer;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.junit.Test;
+
+/**
+ * Checks that user-defined code for data streamer is executed inside the sandbox.
+ */
+public class DataStreamerSandboxTest extends AbstractSandboxTest {
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        return super.getConfiguration(igniteInstanceName)
+            .setCacheConfiguration(new CacheConfiguration<Integer, Integer>(TEST_CACHE));
+    }
+
+    /** */
+    @Test
+    public void test() throws Exception {
+        runOperation(operation(grid(CLNT_ALLOWED_WRITE_PROP)));
+        runForbiddenOperation(operation(grid(CLNT_FORBIDDEN_WRITE_PROP)), AccessControlException.class);
+    }
+
+    /**
+     * @return Operation to test.
+     */
+    private GridTestUtils.RunnableX operation(Ignite node) {
+        return () -> {
+            try (IgniteDataStreamer<Integer, Integer> strm = node.dataStreamer(TEST_CACHE)) {
+                strm.receiver((cache, entries) -> controlAction());
+
+                strm.addData(1, 100);
+            }
+        };
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/sandbox/DoPrivilegedOnRemoteNodeTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/sandbox/DoPrivilegedOnRemoteNodeTest.java
new file mode 100644
index 0000000..a5f46ef
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/sandbox/DoPrivilegedOnRemoteNodeTest.java
@@ -0,0 +1,180 @@
+/*
+ * 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.sandbox;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Proxy;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.AccessControlException;
+import javax.tools.JavaCompiler;
+import javax.tools.ToolProvider;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCompute;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteCallable;
+import org.apache.ignite.lang.IgniteRunnable;
+import org.apache.ignite.resources.IgniteInstanceResource;
+import org.apache.ignite.spi.deployment.local.LocalDeploymentSpi;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.apache.ignite.plugin.security.SecurityPermissionSetBuilder.ALLOW_ALL;
+import static org.apache.ignite.testframework.GridTestUtils.assertThrowsWithCause;
+
+/**
+ * This test shows that an user-defined code with a privileged block cannot execute a secure-sensitive operation.
+ */
+public class DoPrivilegedOnRemoteNodeTest extends AbstractSandboxTest {
+    /** */
+    private static final String CALLABLE_DO_PRIVELEGED_SRC =
+        "import java.security.AccessController;\n" +
+            "import java.security.PrivilegedAction;\n" +
+            "import org.apache.ignite.lang.IgniteCallable;\n" +
+            "\n" +
+            "public class TestDoPrivilegedIgniteCallable implements IgniteCallable<String> {\n" +
+            "    public String call() throws Exception {\n" +
+            "        return AccessController.doPrivileged(\n" +
+            "            (PrivilegedAction<String>)() -> System.getProperty(\"user.home\")\n" +
+            "        );\n" +
+            "    }\n" +
+            "}";
+
+    /** */
+    private static final String CALLABLE_SECURITY_UTILS_SRC =
+        "import org.apache.ignite.internal.processors.security.SecurityUtils;\n" +
+            "import org.apache.ignite.lang.IgniteCallable;\n" +
+            "\n" +
+            "public class TestSecurityUtilsCallable implements IgniteCallable<String> {\n" +
+            "    @Override public String call() throws Exception {\n" +
+            "        return SecurityUtils.doPrivileged(() ->{\n" +
+            "            return System.getProperty(\"user.home\");\n" +
+            "        });\n" +
+            "    }\n" +
+            "}";
+
+    /** Client node name. */
+    private static final String CLNT_NODE = "clnt";
+
+    /** */
+    private Path srcTmpDir;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        return super.getConfiguration(igniteInstanceName)
+            .setDeploymentSpi(new LocalDeploymentSpi());
+    }
+
+    /** */
+    @Before
+    public void prepare() throws IOException {
+        srcTmpDir = Files.createTempDirectory(getClass().getSimpleName());
+    }
+
+    /** */
+    @After
+    public void cleanup() {
+        U.delete(srcTmpDir);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void prepareCluster() throws Exception {
+        Ignite srv = startGrid("srv", ALLOW_ALL,  false);
+
+        startGrid(CLNT_NODE, ALLOW_ALL, true);
+
+        srv.cluster().active(true);
+    }
+
+    /** */
+    @Test
+    public void testDoPrivilegedIgniteCallable() throws Exception {
+        checkCallable(clientCompute(), callable("TestDoPrivilegedIgniteCallable", CALLABLE_DO_PRIVELEGED_SRC));
+    }
+
+    /** */
+    @Test
+    public void testSecurityUtilsCallable() throws Exception {
+        checkCallable(clientCompute(), callable("TestSecurityUtilsCallable", CALLABLE_SECURITY_UTILS_SRC));
+    }
+
+    /** */
+    @Test
+    public void testIgniteProxy(){
+        runForbiddenOperation(() -> clientCompute().broadcast(new TestRunnable() {
+            @Override public void run() {
+                assertTrue(Proxy.isProxyClass(ignite.getClass()));
+
+                ignite.compute().broadcast(() -> System.getProperty("user.home"));
+            }
+        }), AccessControlException.class);
+    }
+
+    /** */
+    private IgniteCompute clientCompute(){
+        Ignite clnt = grid(CLNT_NODE);
+
+        return clnt.compute(clnt.cluster().forRemotes());
+    }
+
+    /** */
+    private void checkCallable(IgniteCompute compute, IgniteCallable<String> c) throws Exception {
+        assertEquals(System.getProperty("user.home"), c.call());
+
+        assertThrowsWithCause(() -> compute.broadcast(c), AccessControlException.class);
+    }
+
+    /** */
+    IgniteCallable<String> callable(String clsName, String src) {
+        try {
+            Files.createDirectories(srcTmpDir);
+
+            File srcFile = new File(srcTmpDir.toFile(), clsName + ".java");
+
+            Path srcFilePath = Files.write(srcFile.toPath(), src.getBytes(StandardCharsets.UTF_8));
+
+            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+
+            compiler.run(null, null, null, srcFilePath.toString());
+
+            assertTrue("Failed to remove source file.", srcFile.delete());
+
+            URLClassLoader clsLdr = new URLClassLoader(new URL[] {srcTmpDir.toUri().toURL()});
+
+            Class<?> cls = clsLdr.loadClass(clsName);
+
+            return (IgniteCallable<String>)cls.newInstance();
+        }
+        catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /** */
+    abstract static class TestRunnable implements IgniteRunnable{
+        /** */
+        @IgniteInstanceResource
+        protected Ignite ignite;
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/sandbox/IgniteOperationsInsideSandboxTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/sandbox/IgniteOperationsInsideSandboxTest.java
new file mode 100644
index 0000000..c7b00b9
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/sandbox/IgniteOperationsInsideSandboxTest.java
@@ -0,0 +1,234 @@
+/*
+ * 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.sandbox;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteCompute;
+import org.apache.ignite.IgniteDataStreamer;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.cache.CacheEntryProcessor;
+import org.apache.ignite.cache.query.ScanQuery;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.compute.ComputeJob;
+import org.apache.ignite.compute.ComputeJobResult;
+import org.apache.ignite.compute.ComputeJobResultPolicy;
+import org.apache.ignite.compute.ComputeTask;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.util.typedef.T2;
+import org.apache.ignite.lang.IgniteCallable;
+import org.apache.ignite.lang.IgniteClosure;
+import org.apache.ignite.lang.IgniteRunnable;
+import org.apache.ignite.resources.IgniteInstanceResource;
+import org.jetbrains.annotations.Nullable;
+import org.junit.Test;
+
+import static java.util.Collections.singleton;
+import static java.util.Collections.singletonList;
+import static java.util.Collections.singletonMap;
+import static org.apache.ignite.plugin.security.SecurityPermissionSetBuilder.ALLOW_ALL;
+
+/**
+ * A user-defined code inside the sandbox can use the public API of Ignite without additional
+ * sandbox permissions.
+ */
+public class IgniteOperationsInsideSandboxTest extends AbstractSandboxTest {
+    /** Test compute task. */
+    private static final ComputeTask<Object, Object> TEST_COMPUTE_TASK = new ComputeTask<Object, Object>() {
+        /** {@inheritDoc} */
+        @Override public Map<? extends ComputeJob, ClusterNode> map(List<ClusterNode> subgrid, Object arg) {
+            return Collections.singletonMap(
+                new ComputeJob() {
+                    @Override public void cancel() {
+                        // No-op.
+                    }
+
+                    @Override public Object execute() {
+                        return null;
+                    }
+                }, subgrid.stream().findFirst().orElseThrow(IllegalStateException::new)
+            );
+        }
+
+        /** {@inheritDoc} */
+        @Override public ComputeJobResultPolicy result(ComputeJobResult res, List<ComputeJobResult> rcvd) {
+            if (res.getException() != null)
+                throw res.getException();
+
+            return ComputeJobResultPolicy.REDUCE;
+        }
+
+        /** {@inheritDoc} */
+        @Override public @Nullable Integer reduce(List<ComputeJobResult> results) {
+            return null;
+        }
+    };
+
+    /** Test callable. */
+    private static final IgniteCallable<Object> TEST_CALLABLE = new IgniteCallable<Object>() {
+        @Override public Object call() {
+            return null;
+        }
+    };
+
+    /** Test runnable. */
+    private static final IgniteRunnable TEST_RUNNABLE = new IgniteRunnable() {
+        @Override public void run() {
+            //No-op.
+        }
+    };
+
+    /** Test closure. */
+    private static final IgniteClosure<Object, Object> TEST_CLOSURE = new IgniteClosure<Object, Object>() {
+        @Override public Object apply(Object o) {
+            return null;
+        }
+    };
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        return super.getConfiguration(igniteInstanceName)
+            .setCacheConfiguration(
+                new CacheConfiguration<String, String>(TEST_CACHE)
+                    .setCacheStoreFactory(new TestStoreFactory("1", "val"))
+            );
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void prepareCluster() throws Exception {
+        Ignite srv = startGrid(SRV, ALLOW_ALL, false);
+
+        startGrid("srv_2", ALLOW_ALL, false);
+
+        startGrid(CLNT_ALLOWED_WRITE_PROP, ALLOW_ALL, true);
+
+        srv.cluster().active(true);
+    }
+
+    /** */
+    @Test
+    public void testComputeOperations() {
+        compute().broadcast(
+            new TestRunnable() {
+                @Override public void run() {
+                    ignite.compute().execute(TEST_COMPUTE_TASK, 0);
+                    ignite.compute().broadcast(TEST_CALLABLE);
+                    ignite.compute().call(TEST_CALLABLE);
+                    ignite.compute().run(TEST_RUNNABLE);
+                    ignite.compute().apply(TEST_CLOSURE, new Object());
+                    ignite.compute().executeAsync(TEST_COMPUTE_TASK, 0).get();
+                    ignite.compute().broadcastAsync(TEST_CALLABLE).get();
+                    ignite.compute().callAsync(TEST_CALLABLE).get();
+                    ignite.compute().runAsync(TEST_RUNNABLE).get();
+                    ignite.compute().applyAsync(TEST_CLOSURE, new Object()).get();
+                    try {
+                        ignite.executorService().invokeAll(singletonList(TEST_CALLABLE));
+                        ignite.executorService().invokeAny(singletonList(TEST_CALLABLE));
+                        ignite.executorService().submit(TEST_CALLABLE).get();
+                    }
+                    catch (InterruptedException | ExecutionException e) {
+                        throw new IgniteException(e);
+                    }
+                }
+            }
+        );
+    }
+
+    /** */
+    @Test
+    public void testCacheOperations() {
+        compute().broadcast(
+            new TestRunnable() {
+                @Override public void run() {
+                    IgniteCache<String, String> cache = ignite.cache(TEST_CACHE);
+
+                    cache.put("key", "val");
+                    cache.putAll(singletonMap("key", "value"));
+                    cache.get("key");
+                    cache.getAll(Collections.singleton("key"));
+                    cache.containsKey("key");
+                    cache.remove("key");
+                    cache.removeAll(Collections.singleton("key"));
+                    cache.clear();
+                    cache.replace("key", "value");
+                    cache.putIfAbsent("key", "value");
+                    cache.getAndPut("key", "value");
+                    cache.getAndRemove("key");
+                    cache.getAndReplace("key", "value");
+
+                    cache.invoke("key", processor());
+                    cache.invokeAll(singleton("key"), processor());
+                    cache.invokeAsync("key", processor()).get();
+                    cache.invokeAllAsync(singleton("key"), processor()).get();
+
+                    cache.query(new ScanQuery<String, Integer>()).getAll();
+                }
+            }
+        );
+    }
+
+    /** */
+    @Test
+    public void testDataStreamerOperations() {
+        compute().broadcast(
+                new TestRunnable() {
+                    @Override public void run() {
+                        try (IgniteDataStreamer<String, String> s = ignite.dataStreamer(TEST_CACHE)) {
+                            s.addData("k", "val");
+                            s.addData(singletonMap("key", "val"));
+                            s.addData((Map.Entry<String, String>)entry());
+                            s.addData(singletonList(entry()));
+                        }
+                    }
+                });
+    }
+
+    /** */
+    private IgniteCompute compute(){
+        Ignite clnt = grid(CLNT_ALLOWED_WRITE_PROP);
+
+        return clnt.compute(clnt.cluster().forRemotes());
+    }
+
+    /** */
+    private CacheEntryProcessor<String, String, String> processor() {
+        return (entry, o) -> {
+            entry.setValue("Val");
+
+            return null;
+        };
+    }
+
+    /**
+     * @return Cache entry for test.
+     */
+    private T2<String, String> entry() {
+        return new T2<>("key", "val");
+    }
+
+    /** */
+    private abstract static class TestRunnable implements IgniteRunnable {
+        @IgniteInstanceResource
+        protected Ignite ignite;
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/sandbox/SecuritySubjectPermissionsTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/sandbox/SecuritySubjectPermissionsTest.java
new file mode 100644
index 0000000..92e4b66
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/sandbox/SecuritySubjectPermissionsTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.sandbox;
+
+import java.io.FilePermission;
+import java.io.SerializablePermission;
+import java.lang.management.ManagementPermission;
+import java.lang.reflect.ReflectPermission;
+import java.net.SocketPermission;
+import java.security.AccessControlException;
+import java.security.BasicPermission;
+import java.security.CodeSource;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.SecurityPermission;
+import java.util.PropertyPermission;
+import javax.management.MBeanPermission;
+import javax.management.MBeanServerPermission;
+import javax.management.MBeanTrustPermission;
+import javax.net.ssl.SSLPermission;
+import org.apache.ignite.Ignite;
+import org.junit.Test;
+
+import static org.apache.ignite.plugin.security.SecurityPermissionSetBuilder.ALLOW_ALL;
+import static org.apache.ignite.testframework.GridTestUtils.assertThrowsWithCause;
+
+/**
+ * The security subject cannot have access higher than Ignite himself.
+ */
+public class SecuritySubjectPermissionsTest extends AbstractSandboxTest {
+    /** */
+    @Test
+    public void test() throws Exception {
+        Ignite srv = startGrid("srv", ALLOW_ALL, false);
+
+        Permissions perms = new Permissions();
+        // Permission that Ignite and subject do have.
+        perms.add(new TestPermission("common"));
+        // Permission that Ignite does not have.
+        perms.add(new TestPermission("only_subject"));
+
+        Ignite clnt = startGrid("clnt", ALLOW_ALL, perms, true);
+
+        srv.cluster().active(true);
+
+        clnt.compute().broadcast(() -> securityManager().checkPermission(new TestPermission("common")));
+
+        assertThrowsWithCause(() -> clnt.compute().broadcast(
+            () -> {
+                securityManager().checkPermission(new TestPermission("only_subject"));
+
+                fail();
+            }),
+            AccessControlException.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        if (System.getSecurityManager() == null) {
+            Policy.setPolicy(new Policy() {
+                @Override public PermissionCollection getPermissions(CodeSource cs) {
+                    Permissions res = new Permissions();
+
+                    res.add(new RuntimePermission("*"));
+                    res.add(new MBeanServerPermission("*"));
+                    res.add(new MBeanPermission("*", "*"));
+                    res.add(new MBeanTrustPermission("*"));
+                    res.add(new ReflectPermission("*"));
+                    res.add(new SSLPermission("*"));
+                    res.add(new ManagementPermission("monitor"));
+                    res.add(new ManagementPermission("control"));
+                    res.add(new SerializablePermission("*"));
+                    res.add(new SecurityPermission("*"));
+                    res.add(new SocketPermission("*", "connect,accept,listen,resolve"));
+                    res.add(new FilePermission("<<ALL FILES>>", "read,write,delete,execute,readlink"));
+                    res.add(new PropertyPermission("*", "read,write"));
+
+                    res.add(new TestPermission("common"));
+
+                    return res;
+                }
+            });
+
+            System.setSecurityManager(new SecurityManager());
+
+            setupSM = true;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        super.afterTestsStopped();
+
+        if (setupSM) {
+            System.setSecurityManager(null);
+            Policy.setPolicy(null);
+        }
+    }
+
+    /** */
+    private SecurityManager securityManager(){
+        SecurityManager sm = System.getSecurityManager();
+
+        assertNotNull(sm);
+
+        return sm;
+    }
+
+    /** Permission for test. */
+    public static class TestPermission extends BasicPermission {
+        /** */
+        public TestPermission(String name) {
+            super(name);
+        }
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java
index 3ad1402..658172e 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java
@@ -140,8 +140,6 @@ import org.junit.runners.Suite;
 
     IgnitePlatformsTestSuite.class,
 
-    SecurityTestSuite.class,
-
     GridSelfTest.class,
     ClusterGroupHostsSelfTest.class,
     IgniteMessagingWithClientTest.class,
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 a4d1815..1d54e75 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
@@ -32,6 +32,12 @@ import org.apache.ignite.internal.processors.security.compute.closure.Distribute
 import org.apache.ignite.internal.processors.security.compute.closure.ExecutorServiceRemoteSecurityContextCheckTest;
 import org.apache.ignite.internal.processors.security.datastreamer.DataStreamerPermissionCheckTest;
 import org.apache.ignite.internal.processors.security.datastreamer.closure.DataStreamerRemoteSecurityContextCheckTest;
+import org.apache.ignite.internal.processors.security.sandbox.CacheSandboxTest;
+import org.apache.ignite.internal.processors.security.sandbox.ComputeSandboxTest;
+import org.apache.ignite.internal.processors.security.sandbox.DataStreamerSandboxTest;
+import org.apache.ignite.internal.processors.security.sandbox.DoPrivilegedOnRemoteNodeTest;
+import org.apache.ignite.internal.processors.security.sandbox.IgniteOperationsInsideSandboxTest;
+import org.apache.ignite.internal.processors.security.sandbox.SecuritySubjectPermissionsTest;
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
 
@@ -45,6 +51,7 @@ import org.junit.runners.Suite;
     ScanQueryPermissionCheckTest.class,
     EntryProcessorPermissionCheckTest.class,
     ComputePermissionCheckTest.class,
+    ThinClientPermissionCheckTest.class,
 
     DistributedClosureRemoteSecurityContextCheckTest.class,
     ComputeTaskRemoteSecurityContextCheckTest.class,
@@ -54,9 +61,15 @@ import org.junit.runners.Suite;
     EntryProcessorRemoteSecurityContextCheckTest.class,
     DataStreamerRemoteSecurityContextCheckTest.class,
     CacheLoadRemoteSecurityContextCheckTest.class,
-    ThinClientPermissionCheckTest.class,
 
     InvalidServerTest.class,
+
+    CacheSandboxTest.class,
+    DataStreamerSandboxTest.class,
+    ComputeSandboxTest.class,
+    DoPrivilegedOnRemoteNodeTest.class,
+    IgniteOperationsInsideSandboxTest.class,
+    SecuritySubjectPermissionsTest.class
 })
 public class SecurityTestSuite {
 }


Mime
View raw message