diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8a3ef26d48..06b8dc6cbf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -411,7 +411,7 @@ In order to force the `validatePullRequest` task to build the entire project, re changes one can use the special `PLS BUILD ALL` command (typed in a comment on GitHub, on the pull request), which will cause the validator to test all projects. -Note, that `OK TO TEST` will only be picked up when the user asking for it is considered an admin. Public (!) members of the [akka organization](https://github.com/orgs/akka/people) are automatically considered admins and users manually declared admin in the Jenkins job (currently no one is explicitly listed). `PLS BUILD` and `PLS BUILD ALL` can be issued by everyone that is an admin or everyone who was whitelisted in the Jenkins Job (whitelisting != declaring someone an admin). +Note, that `OK TO TEST` will only be picked up when the user asking for it is considered an admin. Public (!) members of the [akka organization](https://github.com/orgs/akka/people) are automatically considered admins and users manually declared admin in the Jenkins job (currently no one is explicitly listed). `PLS BUILD` and `PLS BUILD ALL` can be issued by everyone that is an admin or everyone who was given permission in the Jenkins Job. ## Source style diff --git a/akka-actor/src/main/resources/reference.conf b/akka-actor/src/main/resources/reference.conf index 76f4eceef8..32d2d10c00 100644 --- a/akka-actor/src/main/resources/reference.conf +++ b/akka-actor/src/main/resources/reference.conf @@ -793,11 +793,7 @@ akka { } serialization.protobuf { - - # Additional classes that are allowed even if they are not defined in `serialization-bindings`. - # It can be exact class name or name of super class or interfaces (one level). - # This is useful when a class is not used for serialization any more and therefore removed - # from `serialization-bindings`, but should still be possible to deserialize. + # deprecated, use `allowed-classes` instead whitelist-class = [ "com.google.protobuf.GeneratedMessage", "com.google.protobuf.GeneratedMessageV3", @@ -805,6 +801,13 @@ akka { "akka.protobuf.GeneratedMessage", "akka.protobufv3.internal.GeneratedMessageV3" ] + + # Additional classes that are allowed even if they are not defined in `serialization-bindings`. + # It can be exact class name or name of super class or interfaces (one level). + # This is useful when a class is not used for serialization any more and therefore removed + # from `serialization-bindings`, but should still be possible to deserialize. + allowed-classes = ${akka.serialization.protobuf.whitelist-class} + } # Used to set the behavior of the scheduler. diff --git a/akka-cluster/src/main/scala/akka/cluster/ClusterRemoteWatcher.scala b/akka-cluster/src/main/scala/akka/cluster/ClusterRemoteWatcher.scala index fc7a7770ca..3f177ada87 100644 --- a/akka-cluster/src/main/scala/akka/cluster/ClusterRemoteWatcher.scala +++ b/akka-cluster/src/main/scala/akka/cluster/ClusterRemoteWatcher.scala @@ -71,7 +71,7 @@ private[cluster] class ClusterRemoteWatcher( override val log = Logging(context.system, ActorWithLogClass(this, ClusterLogClass.ClusterCore)) // allowed to watch even though address not in cluster membership, i.e. remote watch - private val watchPathWhitelist = Set("/system/sharding/") + private val watchPathAllowList = Set("/system/sharding/") private var pendingDelayedQuarantine: Set[UniqueAddress] = Set.empty @@ -177,7 +177,7 @@ private[cluster] class ClusterRemoteWatcher( private def isWatchOutsideClusterAllowed(watchee: InternalActorRef): Boolean = { context.system.name == watchee.path.address.system && { val pathPrefix = watchee.path.elements.take(2).mkString("/", "/", "/") - watchPathWhitelist.contains(pathPrefix) + watchPathAllowList.contains(pathPrefix) } } diff --git a/akka-docs/src/main/paradox/additional/rolling-updates.md b/akka-docs/src/main/paradox/additional/rolling-updates.md index ee52862ac4..225cc47428 100644 --- a/akka-docs/src/main/paradox/additional/rolling-updates.md +++ b/akka-docs/src/main/paradox/additional/rolling-updates.md @@ -105,14 +105,14 @@ The procedure for changing from Java serialization to Jackson would look like: described in @ref:[Serialization with Jackson](../serialization-jackson.md). * Test the system with the new serialization in a new test cluster (no rolling update). * Remove the binding for the marker interface in `akka.actor.serialization-bindings`, so that Jackson is not used for serialization (toBinary) yet. - * Configure `akka.serialization.jackson.whitelist-class-prefix=["com.myapp"]` + * Configure `akka.serialization.jackson.allowed-class-prefix=["com.myapp"]` * This is needed for Jackson deserialization when the `serialization-bindings` isn't defined. * Replace `com.myapp` with the name of the root package of your application to trust all classes. * Roll out the change. * Java serialization is still used, but this version is prepared for next roll out. 1. Rolling update to enable serialization with Jackson. * Add the binding to the marker interface in `akka.actor.serialization-bindings` to the Jackson serializer. - * Remove `akka.serialization.jackson.whitelist-class-prefix`. + * Remove `akka.serialization.jackson.allowed-class-prefix`. * Roll out the change. * Old nodes will still send messages with Java serialization, and that can still be deserialized by new nodes. * New nodes will send messages with Jackson serialization, and old node can deserialize those because they were diff --git a/akka-docs/src/main/paradox/remoting.md b/akka-docs/src/main/paradox/remoting.md index 4e3597c17a..b4603c86b3 100644 --- a/akka-docs/src/main/paradox/remoting.md +++ b/akka-docs/src/main/paradox/remoting.md @@ -271,22 +271,22 @@ Scala Java : @@snip [RemoteDeploymentDocTest.java](/akka-docs/src/test/java/jdocs/remoting/RemoteDeploymentDocTest.java) { #deploy } -### Remote deployment whitelist +### Remote deployment allow list -As remote deployment can potentially be abused by both users and even attackers a whitelist feature +As remote deployment can potentially be abused by both users and even attackers an allow list feature is available to guard the ActorSystem from deploying unexpected actors. Please note that remote deployment is *not* remote code loading, the Actors class to be deployed onto a remote system needs to be present on that remote system. This still however may pose a security risk, and one may want to restrict remote deployment to -only a specific set of known actors by enabling the whitelist feature. +only a specific set of known actors by enabling the allow list feature. -To enable remote deployment whitelisting set the `akka.remote.deployment.enable-whitelist` value to `on`. +To enable remote deployment allow list set the `akka.remote.deployment.enable-allow-list` value to `on`. The list of allowed classes has to be configured on the "remote" system, in other words on the system onto which others will be attempting to remote deploy Actors. That system, locally, knows best which Actors it should or should not allow others to remote deploy onto it. The full settings section may for example look like this: -@@snip [RemoteDeploymentWhitelistSpec.scala](/akka-remote/src/test/scala/akka/remote/classic/RemoteDeploymentWhitelistSpec.scala) { #whitelist-config } +@@snip [RemoteDeploymentAllowListSpec.scala](/akka-remote/src/test/scala/akka/remote/classic/RemoteDeploymentAllowListSpec.scala) { #allow-list-config } -Actor classes not included in the whitelist will not be allowed to be remote deployed onto this system. +Actor classes not included in the allow list will not be allowed to be remote deployed onto this system. ## Lifecycle and Failure Recovery Model diff --git a/akka-docs/src/main/paradox/security/index.md b/akka-docs/src/main/paradox/security/index.md index 2ab53762ae..fb05687f49 100644 --- a/akka-docs/src/main/paradox/security/index.md +++ b/akka-docs/src/main/paradox/security/index.md @@ -19,7 +19,7 @@ to ensure that a fix can be provided without delay. ## Security Related Documentation * @ref:[Java Serialization](../serialization.md#java-serialization) - * @ref:[Remote deployment whitelist](../remoting.md#remote-deployment-whitelist) + * @ref:[Remote deployment allow list](../remoting.md#remote-deployment-allow-list) * @ref:[Remote Security](../remoting-artery.md#remote-security) ## Fixed Security Vulnerabilities diff --git a/akka-docs/src/main/paradox/serialization-jackson.md b/akka-docs/src/main/paradox/serialization-jackson.md index 775344ab92..d6a51d19cc 100644 --- a/akka-docs/src/main/paradox/serialization-jackson.md +++ b/akka-docs/src/main/paradox/serialization-jackson.md @@ -69,7 +69,7 @@ such as: * `java.io.Serializable` * `java.util.Comparable`. -The blacklist of possible serialization gadget classes defined by Jackson databind are checked +The deny list of possible serialization gadget classes defined by Jackson databind are checked and disallowed for deserialization. @@@ warning @@ -350,12 +350,12 @@ That type of migration must be configured with the old class name as key. The ac ### Remove from serialization-bindings When a class is not used for serialization any more it can be removed from `serialization-bindings` but to still -allow deserialization it must then be listed in the `whitelist-class-prefix` configuration. This is useful for example +allow deserialization it must then be listed in the `allowed-class-prefix` configuration. This is useful for example during rolling update with serialization changes, or when reading old stored data. It can also be used when changing from Jackson serializer to another serializer (e.g. Protobuf) and thereby changing the serialization binding, but it should still be possible to deserialize old data with Jackson. -@@snip [config](/akka-serialization-jackson/src/test/scala/doc/akka/serialization/jackson/SerializationDocSpec.scala) { #whitelist-class-prefix } +@@snip [config](/akka-serialization-jackson/src/test/scala/doc/akka/serialization/jackson/SerializationDocSpec.scala) { #allowed-class-prefix } It's a list of class names or prefixes of class names. diff --git a/akka-docs/src/main/paradox/stream/operators/Source-or-Flow/statefulMapConcat.md b/akka-docs/src/main/paradox/stream/operators/Source-or-Flow/statefulMapConcat.md index 66c02212fb..60642fc069 100644 --- a/akka-docs/src/main/paradox/stream/operators/Source-or-Flow/statefulMapConcat.md +++ b/akka-docs/src/main/paradox/stream/operators/Source-or-Flow/statefulMapConcat.md @@ -28,13 +28,13 @@ Java : @@snip [StatefulMapConcat.java](/akka-docs/src/test/java/jdocs/stream/operators/flow/StatefulMapConcat.java) { #zip-with-index } In this sample we let the value of the elements have an effect on the following elements, if an element starts -with `blacklist:word` we add it to a black list and filter out any subsequent entries of `word`: +with `deny:word` we add it to a deny list and filter out any subsequent entries of `word`: Scala -: @@snip [StatefulMapConcat.scala](/akka-docs/src/test/scala/docs/stream/operators/flow/StatefulMapConcat.scala) { #blacklist } +: @@snip [StatefulMapConcat.scala](/akka-docs/src/test/scala/docs/stream/operators/flow/StatefulMapConcat.scala) { #denylist } Java -: @@snip [StatefulMapConcat.java](/akka-docs/src/test/java/jdocs/stream/operators/flow/StatefulMapConcat.java) { #blacklist } +: @@snip [StatefulMapConcat.java](/akka-docs/src/test/java/jdocs/stream/operators/flow/StatefulMapConcat.java) { #denylist } For cases where there is a need to emit elements based on the state when the stream ends, it is possible to add an extra element signalling the end of the stream before the `statefulMapConcat` operator. diff --git a/akka-docs/src/test/java/jdocs/stream/operators/flow/StatefulMapConcat.java b/akka-docs/src/test/java/jdocs/stream/operators/flow/StatefulMapConcat.java index ba501d2c94..541bf10ca2 100644 --- a/akka-docs/src/test/java/jdocs/stream/operators/flow/StatefulMapConcat.java +++ b/akka-docs/src/test/java/jdocs/stream/operators/flow/StatefulMapConcat.java @@ -43,40 +43,39 @@ public class StatefulMapConcat { // #zip-with-index } - static void blacklist() { - // #blacklist - Source fruitsAndBlacklistCommands = + static void denylist() { + // #denylist + Source fruitsAndDenyCommands = Source.from( - Arrays.asList( - "banana", "pear", "orange", "blacklist:banana", "banana", "pear", "banana")); + Arrays.asList("banana", "pear", "orange", "deny:banana", "banana", "pear", "banana")); - Flow blacklistingFlow = + Flow denyFilterFlow = Flow.of(String.class) .statefulMapConcat( () -> { - Set blacklist = new HashSet<>(); + Set denyList = new HashSet<>(); return (element) -> { - if (element.startsWith("blacklist:")) { - blacklist.add(element.substring(10)); + if (element.startsWith("deny:")) { + denyList.add(element.substring(10)); return Collections - .emptyList(); // no element downstream when adding a blacklisted keyword - } else if (blacklist.contains(element)) { + .emptyList(); // no element downstream when adding a deny listed keyword + } else if (denyList.contains(element)) { return Collections - .emptyList(); // no element downstream if element is blacklisted + .emptyList(); // no element downstream if element is deny listed } else { return Collections.singletonList(element); } }; }); - fruitsAndBlacklistCommands.via(blacklistingFlow).runForeach(System.out::println, system); + fruitsAndDenyCommands.via(denyFilterFlow).runForeach(System.out::println, system); // prints // banana // pear // orange // pear - // #blacklist + // #denylist } static void reactOnEnd() { diff --git a/akka-docs/src/test/scala/docs/stream/operators/flow/StatefulMapConcat.scala b/akka-docs/src/test/scala/docs/stream/operators/flow/StatefulMapConcat.scala index b58f4e33e9..3570a4b0b0 100644 --- a/akka-docs/src/test/scala/docs/stream/operators/flow/StatefulMapConcat.scala +++ b/akka-docs/src/test/scala/docs/stream/operators/flow/StatefulMapConcat.scala @@ -34,33 +34,33 @@ class StatefulMapConcat { // #zip-with-index } - def blacklist(): Unit = { - // #blacklist - val fruitsAndBlacklistCommands = Source( - "banana" :: "pear" :: "orange" :: "blacklist:banana" :: "banana" :: "pear" :: "banana" :: Nil) + def denylist(): Unit = { + // #denylist + val fruitsAndDeniedCommands = Source( + "banana" :: "pear" :: "orange" :: "deny:banana" :: "banana" :: "pear" :: "banana" :: Nil) - val blacklistingFlow = Flow[String].statefulMapConcat { () => - var blacklist = Set.empty[String] + val denyFilterFlow = Flow[String].statefulMapConcat { () => + var denyList = Set.empty[String] { element => - if (element.startsWith("blacklist:")) { - blacklist += element.drop(10) - Nil // no element downstream when adding a blacklisted keyword - } else if (blacklist(element)) { - Nil // no element downstream if element is blacklisted + if (element.startsWith("deny:")) { + denyList += element.drop(10) + Nil // no element downstream when adding a deny listed keyword + } else if (denyList(element)) { + Nil // no element downstream if element is deny listed } else { element :: Nil } } } - fruitsAndBlacklistCommands.via(blacklistingFlow).runForeach(println) + fruitsAndDeniedCommands.via(denyFilterFlow).runForeach(println) // prints // banana // pear // orange // pear - // #blacklist + // #denylist } def reactOnEnd(): Unit = { diff --git a/akka-remote/src/main/mima-filters/2.6.6.backwards.excludes/terminology-cleanup.excludes b/akka-remote/src/main/mima-filters/2.6.6.backwards.excludes/terminology-cleanup.excludes new file mode 100644 index 0000000000..a39667b891 --- /dev/null +++ b/akka-remote/src/main/mima-filters/2.6.6.backwards.excludes/terminology-cleanup.excludes @@ -0,0 +1,2 @@ +# Internal class rename +ProblemFilters.exclude[MissingClassProblem]("akka.remote.NotWhitelistedClassRemoteDeploymentAttemptException") \ No newline at end of file diff --git a/akka-remote/src/main/resources/reference.conf b/akka-remote/src/main/resources/reference.conf index ce17d6147f..76de8dc1a5 100644 --- a/akka-remote/src/main/resources/reference.conf +++ b/akka-remote/src/main/resources/reference.conf @@ -234,10 +234,18 @@ akka { # remote deployment configuration section deployment { - # If true, will only allow specific classes to be instanciated on this system via remote deployment + # deprecated, use `enable-allow-list` enable-whitelist = off + # If true, will only allow specific classes listed in `allowed-actor-classes` to be instanciated on this + # system via remote deployment + enable-allow-list = ${akka.remote.deployment.enable-whitelist} + + + # deprecated, use `allowed-actor-classes` whitelist = [] + + allowed-actor-classes = ${akka.remote.deployment.whitelist} } ### Default dispatcher for the remoting subsystem @@ -336,7 +344,7 @@ akka { untrusted-mode = off # When 'untrusted-mode=on' inbound actor selections are by default discarded. - # Actors with paths defined in this white list are granted permission to receive actor + # Actors with paths defined in this list are granted permission to receive actor # selections messages. # E.g. trusted-selection-paths = ["/user/receptionist", "/user/namingService"] trusted-selection-paths = [] @@ -807,7 +815,7 @@ akka { untrusted-mode = off # When 'untrusted-mode=on' inbound actor selections are by default discarded. - # Actors with paths defined in this white list are granted permission to receive actor + # Actors with paths defined in this list are granted permission to receive actor # selections messages. # E.g. trusted-selection-paths = ["/user/receptionist", "/user/namingService"] trusted-selection-paths = [] diff --git a/akka-remote/src/main/scala/akka/remote/RemoteDaemon.scala b/akka-remote/src/main/scala/akka/remote/RemoteDaemon.scala index 0ec0664ce9..acc694bd61 100644 --- a/akka-remote/src/main/scala/akka/remote/RemoteDaemon.scala +++ b/akka-remote/src/main/scala/akka/remote/RemoteDaemon.scala @@ -73,10 +73,11 @@ private[akka] class RemoteSystemDaemon( private val parent2children = new ConcurrentHashMap[ActorRef, Set[ActorRef]] - private val whitelistEnabled = system.settings.config.getBoolean("akka.remote.deployment.enable-whitelist") - private val remoteDeploymentWhitelist: immutable.Set[String] = { + private val allowListEnabled = system.settings.config.getBoolean("akka.remote.deployment.enable-allow-list") + private val remoteDeploymentAllowList: immutable.Set[String] = { import akka.util.ccompat.JavaConverters._ - if (whitelistEnabled) system.settings.config.getStringList("akka.remote.deployment.whitelist").asScala.toSet + if (allowListEnabled) + system.settings.config.getStringList("akka.remote.deployment.allowed-actor-classes").asScala.toSet else Set.empty } @@ -164,13 +165,13 @@ private[akka] class RemoteSystemDaemon( case DaemonMsgCreate(_, _, path, _) if untrustedMode => log.debug("does not accept deployments (untrusted) for [{}]", path) // TODO add security marker? - case DaemonMsgCreate(props, deploy, path, supervisor) if whitelistEnabled => + case DaemonMsgCreate(props, deploy, path, supervisor) if allowListEnabled => val name = props.clazz.getCanonicalName - if (remoteDeploymentWhitelist.contains(name)) + if (remoteDeploymentAllowList.contains(name)) doCreateActor(message, props, deploy, path, supervisor) else { val ex = - new NotWhitelistedClassRemoteDeploymentAttemptException(props.actorClass(), remoteDeploymentWhitelist) + new NotAllowedClassRemoteDeploymentAttemptException(props.actorClass(), remoteDeploymentAllowList) log.error( LogMarker.Security, ex, @@ -273,8 +274,8 @@ private[akka] class RemoteSystemDaemon( } /** INTERNAL API */ -final class NotWhitelistedClassRemoteDeploymentAttemptException(illegal: Class[_], whitelist: immutable.Set[String]) +final class NotAllowedClassRemoteDeploymentAttemptException(illegal: Class[_], allowedClassNames: immutable.Set[String]) extends RuntimeException( - s"Attempted to deploy not whitelisted Actor class: " + + s"Attempted to deploy Actor class: " + s"[$illegal], " + - s"whitelisted classes: [${whitelist.mkString(", ")}]") + s"which is not allowed, allowed classes: [${allowedClassNames.mkString(", ")}]") diff --git a/akka-remote/src/main/scala/akka/remote/serialization/ProtobufSerializer.scala b/akka-remote/src/main/scala/akka/remote/serialization/ProtobufSerializer.scala index 1cfe7317ae..c92847e061 100644 --- a/akka-remote/src/main/scala/akka/remote/serialization/ProtobufSerializer.scala +++ b/akka-remote/src/main/scala/akka/remote/serialization/ProtobufSerializer.scala @@ -47,9 +47,9 @@ class ProtobufSerializer(val system: ExtendedActorSystem) extends BaseSerializer private val parsingMethodBindingRef = new AtomicReference[Map[Class[_], Method]](Map.empty) private val toByteArrayMethodBindingRef = new AtomicReference[Map[Class[_], Method]](Map.empty) - private val whitelistClassNames: Set[String] = { + private val allowedClassNames: Set[String] = { import akka.util.ccompat.JavaConverters._ - system.settings.config.getStringList("akka.serialization.protobuf.whitelist-class").asScala.toSet + system.settings.config.getStringList("akka.serialization.protobuf.allowed-classes").asScala.toSet } // This must lazy otherwise it will deadlock the ActorSystem creation @@ -110,18 +110,18 @@ class ProtobufSerializer(val system: ExtendedActorSystem) extends BaseSerializer } private def checkAllowedClass(clazz: Class[_]): Unit = { - if (!isInWhitelist(clazz)) { + if (!isInAllowList(clazz)) { val warnMsg = s"Can't deserialize object of type [${clazz.getName}] in [${getClass.getName}]. " + - "Only classes that are whitelisted are allowed for security reasons. " + - "Configure whitelist with akka.actor.serialization-bindings or " + - "akka.serialization.protobuf.whitelist-class" + "Only classes that are on the allow list are allowed for security reasons. " + + "Configure allowed classes with akka.actor.serialization-bindings or " + + "akka.serialization.protobuf.allowed-classes" log.warning(LogMarker.Security, warnMsg) throw new IllegalArgumentException(warnMsg) } } /** - * Using the `serialization-bindings` as source for the whitelist. + * Using the `serialization-bindings` as source for the allowed classes. * Note that the intended usage of serialization-bindings is for lookup of * serializer when serializing (`toBinary`). For deserialization (`fromBinary`) the serializer-id is * used for selecting serializer. @@ -130,13 +130,13 @@ class ProtobufSerializer(val system: ExtendedActorSystem) extends BaseSerializer * * If an old class is removed from `serialization-bindings` when it's not used for serialization * but still used for deserialization (e.g. rolling update with serialization changes) it can - * be allowed by specifying in `akka.protobuf.whitelist-class`. + * be allowed by specifying in `akka.protobuf.allowed-classes`. * * That is also possible when changing a binding from a ProtobufSerializer to another serializer (e.g. Jackson) * and still bind with the same class (interface). */ - private def isInWhitelist(clazz: Class[_]): Boolean = { - isBoundToProtobufSerializer(clazz) || isInWhitelistClassName(clazz) + private def isInAllowList(clazz: Class[_]): Boolean = { + isBoundToProtobufSerializer(clazz) || isInAllowListClassName(clazz) } private def isBoundToProtobufSerializer(clazz: Class[_]): Boolean = { @@ -148,9 +148,9 @@ class ProtobufSerializer(val system: ExtendedActorSystem) extends BaseSerializer } } - private def isInWhitelistClassName(clazz: Class[_]): Boolean = { - whitelistClassNames(clazz.getName) || - whitelistClassNames(clazz.getSuperclass.getName) || - clazz.getInterfaces.exists(c => whitelistClassNames(c.getName)) + private def isInAllowListClassName(clazz: Class[_]): Boolean = { + allowedClassNames(clazz.getName) || + allowedClassNames(clazz.getSuperclass.getName) || + clazz.getInterfaces.exists(c => allowedClassNames(c.getName)) } } diff --git a/akka-remote/src/test/scala/akka/remote/artery/UntrustedSpec.scala b/akka-remote/src/test/scala/akka/remote/artery/UntrustedSpec.scala index fad12b330a..3d974ae780 100644 --- a/akka-remote/src/test/scala/akka/remote/artery/UntrustedSpec.scala +++ b/akka-remote/src/test/scala/akka/remote/artery/UntrustedSpec.scala @@ -94,7 +94,7 @@ class UntrustedSpec extends ArteryMultiNodeSpec(UntrustedSpec.config) with Impli "UntrustedMode" must { - "allow actor selection to configured white list" in { + "allow actor selection to configured allow list" in { val sel = client.actorSelection(RootActorPath(address) / receptionist.path.elements) sel ! "hello" expectMsg("hello") @@ -152,7 +152,7 @@ class UntrustedSpec extends ArteryMultiNodeSpec(UntrustedSpec.config) with Impli expectNoMessage(1.second) } - "discard actor selection to child of matching white list" in { + "discard actor selection to child of matching allow list" in { val sel = client.actorSelection(RootActorPath(address) / receptionist.path.elements / "child1") sel ! "hello" expectNoMessage(1.second) diff --git a/akka-remote/src/test/scala/akka/remote/classic/RemoteDeploymentWhitelistSpec.scala b/akka-remote/src/test/scala/akka/remote/classic/RemoteDeploymentAllowListSpec.scala similarity index 74% rename from akka-remote/src/test/scala/akka/remote/classic/RemoteDeploymentWhitelistSpec.scala rename to akka-remote/src/test/scala/akka/remote/classic/RemoteDeploymentAllowListSpec.scala index 9fdfd384f2..14669a0cbf 100644 --- a/akka-remote/src/test/scala/akka/remote/classic/RemoteDeploymentWhitelistSpec.scala +++ b/akka-remote/src/test/scala/akka/remote/classic/RemoteDeploymentAllowListSpec.scala @@ -5,19 +5,18 @@ package akka.remote.classic import scala.concurrent.duration._ - import com.github.ghik.silencer.silent import com.typesafe.config._ - import akka.actor._ import akka.remote.EndpointException +import akka.remote.NotAllowedClassRemoteDeploymentAttemptException import akka.remote.transport._ import akka.testkit._ // relies on test transport -object RemoteDeploymentWhitelistSpec { +object RemoteDeploymentAllowListSpec { - class EchoWhitelisted extends Actor { + class EchoAllowed extends Actor { var target: ActorRef = context.system.deadLetters def receive = { @@ -35,7 +34,7 @@ object RemoteDeploymentWhitelistSpec { } } - class EchoNotWhitelisted extends Actor { + class EchoNotAllowed extends Actor { var target: ActorRef = context.system.deadLetters def receive = { @@ -57,6 +56,7 @@ object RemoteDeploymentWhitelistSpec { akka { actor.provider = remote + remote { use-unsafe-remote-features-outside-cluster = on classic.enabled-transports = [ @@ -79,7 +79,7 @@ object RemoteDeploymentWhitelistSpec { transport-class = "akka.remote.transport.TestTransport" applied-adapters = [] registry-key = aX33k0jWKg - local-address = "test://RemoteDeploymentWhitelistSpec@localhost:12345" + local-address = "test://RemoteDeploymentAllowListSpec@localhost:12345" maximum-payload-bytes = 32000 bytes scheme-identifier = test } @@ -105,31 +105,30 @@ object RemoteDeploymentWhitelistSpec { } @silent("deprecated") -class RemoteDeploymentWhitelistSpec - extends AkkaSpec(RemoteDeploymentWhitelistSpec.cfg) +class RemoteDeploymentAllowListSpec + extends AkkaSpec(RemoteDeploymentAllowListSpec.cfg) with ImplicitSender with DefaultTimeout { - import RemoteDeploymentWhitelistSpec._ + import RemoteDeploymentAllowListSpec._ val conf = ConfigFactory.parseString(""" - akka.loglevel = DEBUG akka.remote.test { local-address = "test://remote-sys@localhost:12346" maximum-payload-bytes = 48000 bytes } - //#whitelist-config + //#allow-list-config akka.remote.deployment { - enable-whitelist = on + enable-allow-list = on - whitelist = [ + allowed-actor-classes = [ "NOT_ON_CLASSPATH", # verify we don't throw if a class not on classpath is listed here - "akka.remote.classic.RemoteDeploymentWhitelistSpec.EchoWhitelisted" + "akka.remote.classic.RemoteDeploymentAllowListSpec.EchoAllowed" ] } - //#whitelist-config + //#allow-list-config """).withFallback(system.settings.config).resolve() val remoteSystem = ActorSystem("remote-sys", conf) @@ -147,10 +146,10 @@ class RemoteDeploymentWhitelistSpec AssociationRegistry.clear() } - "RemoteDeployment Whitelist" must { + "RemoteDeployment Allow List" must { - "allow deploying Echo actor (included in whitelist)" in { - val r = system.actorOf(Props[EchoWhitelisted](), "blub") + "allow deploying Echo actor (included in allow list)" in { + val r = system.actorOf(Props[EchoAllowed](), "blub") r.path.toString should ===( s"akka.test://remote-sys@localhost:12346/remote/akka.test/${getClass.getSimpleName}@localhost:12345/user/blub") r ! 42 @@ -165,13 +164,19 @@ class RemoteDeploymentWhitelistSpec expectMsg("postStop") } - "not deploy actor not listed in whitelist" in { - val r = system.actorOf(Props[EchoNotWhitelisted](), "danger-mouse") - r.path.toString should ===( - s"akka.test://remote-sys@localhost:12346/remote/akka.test/${getClass.getSimpleName}@localhost:12345/user/danger-mouse") - r ! 42 - expectNoMessage(1.second) - system.stop(r) + "not deploy actor not listed in allow list" in { + EventFilter + .warning(start = "received dead letter", occurrences = 1) + .intercept { + EventFilter[NotAllowedClassRemoteDeploymentAttemptException](occurrences = 1).intercept { + val r = system.actorOf(Props[EchoNotAllowed](), "danger-mouse") + r.path.toString should ===( + s"akka.test://remote-sys@localhost:12346/remote/akka.test/${getClass.getSimpleName}@localhost:12345/user/danger-mouse") + r ! 42 + expectNoMessage(1.second) + system.stop(r) + }(remoteSystem) + }(remoteSystem) } } } diff --git a/akka-remote/src/test/scala/akka/remote/classic/UntrustedSpec.scala b/akka-remote/src/test/scala/akka/remote/classic/UntrustedSpec.scala index 9bb86dfd56..147554cd13 100644 --- a/akka-remote/src/test/scala/akka/remote/classic/UntrustedSpec.scala +++ b/akka-remote/src/test/scala/akka/remote/classic/UntrustedSpec.scala @@ -123,7 +123,7 @@ akka.actor.serialization-bindings { "UntrustedMode" must { - "allow actor selection to configured white list" in { + "allow actor selection to configured allow list" in { val sel = client.actorSelection(RootActorPath(address) / receptionist.path.elements) sel ! "hello" expectMsg("hello") @@ -181,7 +181,7 @@ akka.actor.serialization-bindings { expectNoMessage(1.second) } - "discard actor selection to child of matching white list" in { + "discard actor selection to child of matching allow list" in { val sel = client.actorSelection(RootActorPath(address) / receptionist.path.elements / "child1") sel ! "hello" expectNoMessage(1.second) diff --git a/akka-remote/src/test/scala/akka/remote/serialization/ProtobufSerializerSpec.scala b/akka-remote/src/test/scala/akka/remote/serialization/ProtobufSerializerSpec.scala index af85760222..1d9904f0c8 100644 --- a/akka-remote/src/test/scala/akka/remote/serialization/ProtobufSerializerSpec.scala +++ b/akka-remote/src/test/scala/akka/remote/serialization/ProtobufSerializerSpec.scala @@ -45,7 +45,7 @@ object MaliciousMessage { } class ProtobufSerializerSpec extends AkkaSpec(s""" - akka.serialization.protobuf.whitelist-class = [ + akka.serialization.protobuf.allowed-classes = [ "com.google.protobuf.GeneratedMessage", "com.google.protobuf.GeneratedMessageV3", "scalapb.GeneratedMessageCompanion", @@ -87,7 +87,7 @@ class ProtobufSerializerSpec extends AkkaSpec(s""" protobufV3Message should ===(deserialized) } - "disallow deserialization of classes that are not in bindings and not in configured whitelist-class" in { + "disallow deserialization of classes that are not in bindings and not in configured allowed classes" in { val originalSerializer = ser.serializerFor(classOf[MyMessage]) intercept[IllegalArgumentException] { @@ -95,7 +95,7 @@ class ProtobufSerializerSpec extends AkkaSpec(s""" } } - "allow deserialization of classes in configured whitelist-class" in { + "allow deserialization of classes in configured allowed classes" in { val originalSerializer = ser.serializerFor(classOf[MyMessage]) val deserialized = @@ -103,7 +103,7 @@ class ProtobufSerializerSpec extends AkkaSpec(s""" deserialized.getClass should ===(classOf[AnotherMessage]) } - "allow deserialization of interfaces in configured whitelist-class" in { + "allow deserialization of interfaces in configured allowed classes" in { val originalSerializer = ser.serializerFor(classOf[MyMessage]) val deserialized = @@ -111,7 +111,7 @@ class ProtobufSerializerSpec extends AkkaSpec(s""" deserialized.getClass should ===(classOf[AnotherMessage2]) } - "allow deserialization of super classes in configured whitelist-class" in { + "allow deserialization of super classes in configured allowed classes" in { val originalSerializer = ser.serializerFor(classOf[MyMessage]) val deserialized = diff --git a/akka-serialization-jackson/src/main/mima-filters/2.6.6.backwards.excludes/terminology-cleanup.excludes b/akka-serialization-jackson/src/main/mima-filters/2.6.6.backwards.excludes/terminology-cleanup.excludes new file mode 100644 index 0000000000..ae343eab5e --- /dev/null +++ b/akka-serialization-jackson/src/main/mima-filters/2.6.6.backwards.excludes/terminology-cleanup.excludes @@ -0,0 +1,2 @@ +# Internal class rename +ProblemFilters.exclude[MissingClassProblem]("akka.serialization.jackson.JacksonSerializer$GadgetClassBlacklist") \ No newline at end of file diff --git a/akka-serialization-jackson/src/main/resources/reference.conf b/akka-serialization-jackson/src/main/resources/reference.conf index 04df3d9279..d06d1fce3d 100644 --- a/akka-serialization-jackson/src/main/resources/reference.conf +++ b/akka-serialization-jackson/src/main/resources/reference.conf @@ -127,10 +127,14 @@ akka.serialization.jackson { # } json-write-features {} + # Deprecated, use `allowed-class-prefix` instead + whitelist-class-prefix = [] + # Additional classes that are allowed even if they are not defined in `serialization-bindings`. # This is useful when a class is not used for serialization any more and therefore removed # from `serialization-bindings`, but should still be possible to deserialize. - whitelist-class-prefix = [] + allowed-class-prefix = ${akka.serialization.jackson.whitelist-class-prefix} + # settings for compression of the payload compression { diff --git a/akka-serialization-jackson/src/main/scala/akka/serialization/jackson/JacksonSerializer.scala b/akka-serialization-jackson/src/main/scala/akka/serialization/jackson/JacksonSerializer.scala index 001adf0106..c3f5084205 100644 --- a/akka-serialization-jackson/src/main/scala/akka/serialization/jackson/JacksonSerializer.scala +++ b/akka-serialization-jackson/src/main/scala/akka/serialization/jackson/JacksonSerializer.scala @@ -30,11 +30,11 @@ import akka.util.OptionVal @InternalApi private[akka] object JacksonSerializer { /** - * Using the blacklist from Jackson databind of class names that shouldn't be allowed. + * Using the deny list from Jackson databind of class names that shouldn't be allowed. * Not nice to depend on implementation details of Jackson, but good to use the same * list to automatically have the list updated when new classes are added in Jackson. */ - class GadgetClassBlacklist extends SubTypeValidator { + class GadgetClassDenyList extends SubTypeValidator { private def defaultNoDeserClassNames: java.util.Set[String] = SubTypeValidator.DEFAULT_NO_DESER_CLASS_NAMES // it's has protected visibility @@ -204,10 +204,10 @@ import akka.util.OptionVal k -> transformer } } - private val blacklist: GadgetClassBlacklist = new GadgetClassBlacklist - private val whitelistClassPrefix = { + private val denyList: GadgetClassDenyList = new GadgetClassDenyList + private val allowedClassPrefix = { import akka.util.ccompat.JavaConverters._ - conf.getStringList("whitelist-class-prefix").asScala.toVector + conf.getStringList("allowed-class-prefix").asScala.toVector } private val typeInManifest: Boolean = conf.getBoolean("type-in-manifest") // Calculated eagerly so as to fail fast @@ -397,32 +397,32 @@ import akka.util.OptionVal className.length > 0 && className.charAt(className.length - 1) == '$' private def checkAllowedClassName(className: String): Unit = { - if (!blacklist.isAllowedClassName(className)) { + if (!denyList.isAllowedClassName(className)) { val warnMsg = s"Can't serialize/deserialize object of type [$className] in [${getClass.getName}]. " + - s"Blacklisted for security reasons." + s"Disallowed (on deny list) for security reasons." log.warning(LogMarker.Security, warnMsg) throw new IllegalArgumentException(warnMsg) } } private def checkAllowedClass(clazz: Class[_]): Unit = { - if (!blacklist.isAllowedClass(clazz)) { + if (!denyList.isAllowedClass(clazz)) { val warnMsg = s"Can't serialize/deserialize object of type [${clazz.getName}] in [${getClass.getName}]. " + - s"Blacklisted for security reasons." + s"Not allowed for security reasons." log.warning(LogMarker.Security, warnMsg) throw new IllegalArgumentException(warnMsg) - } else if (!isInWhitelist(clazz)) { + } else if (!isInAllowList(clazz)) { val warnMsg = s"Can't serialize/deserialize object of type [${clazz.getName}] in [${getClass.getName}]. " + - "Only classes that are whitelisted are allowed for security reasons. " + - "Configure whitelist with akka.actor.serialization-bindings or " + - "akka.serialization.jackson.whitelist-class-prefix." + "Only classes that are listed as allowed are allowed for security reasons. " + + "Configure allowed classes with akka.actor.serialization-bindings or " + + "akka.serialization.jackson.allowed-class-prefix." log.warning(LogMarker.Security, warnMsg) throw new IllegalArgumentException(warnMsg) } } /** - * Using the `serialization-bindings` as source for the whitelist. + * Using the `serialization-bindings` as source for the allowed classes. * Note that the intended usage of serialization-bindings is for lookup of * serializer when serializing (`toBinary`). For deserialization (`fromBinary`) the serializer-id is * used for selecting serializer. @@ -431,13 +431,13 @@ import akka.util.OptionVal * * If an old class is removed from `serialization-bindings` when it's not used for serialization * but still used for deserialization (e.g. rolling update with serialization changes) it can - * be allowed by specifying in `whitelist-class-prefix`. + * be allowed by specifying in `allowed-class-prefix`. * * That is also possible when changing a binding from a JacksonSerializer to another serializer (e.g. protobuf) * and still bind with the same class (interface). */ - private def isInWhitelist(clazz: Class[_]): Boolean = { - isBoundToJacksonSerializer(clazz) || isInWhitelistClassPrefix(clazz.getName) + private def isInAllowList(clazz: Class[_]): Boolean = { + isBoundToJacksonSerializer(clazz) || hasAllowedClassPrefix(clazz.getName) } private def isBoundToJacksonSerializer(clazz: Class[_]): Boolean = { @@ -452,8 +452,8 @@ import akka.util.OptionVal } } - private def isInWhitelistClassPrefix(className: String): Boolean = - whitelistClassPrefix.exists(className.startsWith) + private def hasAllowedClassPrefix(className: String): Boolean = + allowedClassPrefix.exists(className.startsWith) /** * Check that serialization-bindings are not configured with open-ended interfaces, diff --git a/akka-serialization-jackson/src/test/scala/akka/serialization/jackson/JacksonSerializerSpec.scala b/akka-serialization-jackson/src/test/scala/akka/serialization/jackson/JacksonSerializerSpec.scala index a75ce1f4b5..4fd4354f9e 100644 --- a/akka-serialization-jackson/src/test/scala/akka/serialization/jackson/JacksonSerializerSpec.scala +++ b/akka-serialization-jackson/src/test/scala/akka/serialization/jackson/JacksonSerializerSpec.scala @@ -471,7 +471,7 @@ class JacksonJsonSerializerSpec extends JacksonSerializerSpec("jackson-json") { } } - "allow deserialization of classes in configured whitelist-class-prefix" in { + "allow deserialization of classes in configured allowed-class-prefix" in { val json = """{"name":"abc"}""" val old = SimpleCommand("abc") @@ -638,7 +638,7 @@ abstract class JacksonSerializerSpec(serializerName: String) "akka.serialization.jackson.JavaTestMessages$$TestMessage" = $serializerName } } - akka.serialization.jackson.whitelist-class-prefix = ["akka.serialization.jackson.ScalaTestMessages$$OldCommand"] + akka.serialization.jackson.allowed-class-prefix = ["akka.serialization.jackson.ScalaTestMessages$$OldCommand"] """))) with AnyWordSpecLike with Matchers @@ -900,17 +900,17 @@ abstract class JacksonSerializerSpec(serializerName: String) event2.field2 should ===(17) } - "not allow serialization of blacklisted class" in { + "not allow serialization of deny listed class" in { val serializer = serializerFor(SimpleCommand("ok")) val fileHandler = new FileHandler(s"target/tmp-${this.getClass.getName}") try { intercept[IllegalArgumentException] { serializer.manifest(fileHandler) - }.getMessage.toLowerCase should include("blacklist") + }.getMessage.toLowerCase should include("deny list") } finally fileHandler.close() } - "not allow deserialization of blacklisted class" in { + "not allow deserialization of deny list class" in { withTransportInformation() { () => val msg = SimpleCommand("ok") val serializer = serializerFor(msg) @@ -918,18 +918,18 @@ abstract class JacksonSerializerSpec(serializerName: String) intercept[IllegalArgumentException] { // maliciously changing manifest serializer.fromBinary(blob, classOf[FileHandler].getName) - }.getMessage.toLowerCase should include("blacklist") + }.getMessage.toLowerCase should include("deny list") } } - "not allow serialization of class that is not in serialization-bindings (whitelist)" in { + "not allow serialization of class that is not in serialization-bindings (allowed-class-prefix)" in { val serializer = serializerFor(SimpleCommand("ok")) intercept[IllegalArgumentException] { serializer.manifest(Status.Success("bad")) - }.getMessage.toLowerCase should include("whitelist") + }.getMessage.toLowerCase should include("allowed-class-prefix") } - "not allow deserialization of class that is not in serialization-bindings (whitelist)" in { + "not allow deserialization of class that is not in serialization-bindings (allowed-class-prefix)" in { withTransportInformation() { () => val msg = SimpleCommand("ok") val serializer = serializerFor(msg) @@ -937,7 +937,7 @@ abstract class JacksonSerializerSpec(serializerName: String) intercept[IllegalArgumentException] { // maliciously changing manifest serializer.fromBinary(blob, classOf[Status.Success].getName) - }.getMessage.toLowerCase should include("whitelist") + }.getMessage.toLowerCase should include("allowed-class-prefix") } } diff --git a/akka-serialization-jackson/src/test/scala/doc/akka/serialization/jackson/SerializationDocSpec.scala b/akka-serialization-jackson/src/test/scala/doc/akka/serialization/jackson/SerializationDocSpec.scala index 7658172725..29d19f1101 100644 --- a/akka-serialization-jackson/src/test/scala/doc/akka/serialization/jackson/SerializationDocSpec.scala +++ b/akka-serialization-jackson/src/test/scala/doc/akka/serialization/jackson/SerializationDocSpec.scala @@ -150,11 +150,11 @@ object SerializationDocSpec { #//#date-time """ - val configWhitelist = """ - #//#whitelist-class-prefix - akka.serialization.jackson.whitelist-class-prefix = + val configAllowList = """ + #//#allowed-class-prefix + akka.serialization.jackson.allowed-class-prefix = ["com.myservice.event.OrderAdded", "com.myservice.command"] - #//#whitelist-class-prefix + #//#allowed-class-prefix """ }