2012-06-11 18:33:05 +02:00
/* *
2016-01-25 10:16:14 +01:00
* Copyright ( C ) 2009 - 2016 Typesafe Inc . < http : //www.typesafe.com>
2012-06-11 18:33:05 +02:00
*/
2012-06-18 13:57:42 +02:00
package akka.remote
2012-06-11 18:33:05 +02:00
2012-06-25 16:29:08 +02:00
import language.postfixOps
2012-06-11 18:33:05 +02:00
import akka.testkit._
import akka.actor._
import com.typesafe.config._
2012-07-04 15:25:30 +02:00
import scala.concurrent.Future
2012-09-19 17:32:54 +02:00
import scala.reflect.classTag
2012-06-11 18:33:05 +02:00
import akka.pattern.ask
import java.io.File
2012-06-18 14:55:49 +02:00
import java.security. { NoSuchAlgorithmException , SecureRandom , PrivilegedAction , AccessController }
2012-06-18 17:57:09 +02:00
import javax.net.ssl.SSLException
2012-06-29 13:33:20 +02:00
import akka.util.Timeout
2012-06-29 16:40:36 +02:00
import scala.concurrent.Await
2012-09-21 14:50:06 +02:00
import scala.concurrent.duration._
2012-06-19 22:44:01 +02:00
import akka.event. { Logging , NoLogging , LoggingAdapter }
2013-01-17 12:17:19 +01:00
import akka.remote.transport.netty. { SSLSettings , NettySSLSupport }
2014-08-29 13:39:30 +02:00
import Configuration. { CipherConfig , getCipherConfig }
import org.uncommons.maths.random.RandomDotOrgSeedGenerator
import scala.util.control.NonFatal
2012-06-11 18:33:05 +02:00
object Configuration {
// set this in your JAVA_OPTS to see all ssl debug info: "-Djavax.net.debug=ssl,keymanager"
// The certificate will expire in 2109
2012-06-18 18:47:35 +02:00
private val trustStore = getClass . getClassLoader . getResource ( "truststore" ) . getPath
private val keyStore = getClass . getClassLoader . getResource ( "keystore" ) . getPath
2012-06-11 18:33:05 +02:00
private val conf = "" "
akka {
actor . provider = "akka.remote.RemoteActorRefProvider"
2012-06-18 19:31:36 +02:00
test {
2012-06-18 20:07:58 +02:00
single - expect - default = 10 s
filter - leeway = 10 s
default - timeout = 10 s
2012-06-18 19:31:36 +02:00
}
2013-01-17 12:17:19 +01:00
2013-01-17 16:19:31 +01:00
remote . enabled - transports = [ " akka . remote . netty . ssl " ]
2013-01-17 12:17:19 +01:00
2013-01-17 16:19:31 +01:00
remote . netty . ssl {
2012-06-11 18:33:05 +02:00
hostname = localhost
2012-06-19 22:44:01 +02:00
port = % d
2013-02-11 13:33:21 +01:00
security {
2012-06-11 18:33:05 +02:00
trust - store = "%s"
key - store = "%s"
2013-01-17 12:17:19 +01:00
key - store - password = "changeme"
2013-04-11 13:14:48 +02:00
key - password = "changeme"
2013-01-17 12:17:19 +01:00
trust - store - password = "changeme"
2015-12-18 11:47:19 +01:00
protocol = "TLSv1.2"
2012-06-11 18:33:05 +02:00
random - number - generator = "%s"
2012-06-19 10:36:09 +02:00
enabled - algorithms = [ % s ]
2012-06-11 18:33:05 +02:00
}
}
}
2013-01-17 12:17:19 +01:00
"" "
2012-06-11 18:33:05 +02:00
2014-03-07 13:20:01 +01:00
final case class CipherConfig ( runTest : Boolean , config : Config , cipher : String , localPort : Int , remotePort : Int )
2012-06-19 22:44:01 +02:00
def getCipherConfig ( cipher : String , enabled : String * ) : CipherConfig = {
val localPort , remotePort = { val s = new java . net . ServerSocket ( 0 ) ; try s . getLocalPort finally s . close ( ) }
try {
//if (true) throw new IllegalArgumentException("Ticket1978*Spec isn't enabled")
2012-06-19 21:19:19 +02:00
2012-06-19 22:44:01 +02:00
val config = ConfigFactory . parseString ( conf . format ( localPort , trustStore , keyStore , cipher , enabled . mkString ( ", " ) ) )
2013-02-11 13:33:21 +01:00
val fullConfig = config . withFallback ( AkkaSpec . testConf ) . withFallback ( ConfigFactory . load ) . getConfig ( "akka.remote.netty.ssl.security" )
2013-01-17 12:17:19 +01:00
val settings = new SSLSettings ( fullConfig )
2012-06-18 17:57:09 +02:00
2013-02-08 16:20:40 +01:00
val rng = NettySSLSupport . initializeCustomSecureRandom ( settings . SSLRandomNumberGenerator , NoLogging )
2012-06-18 17:57:09 +02:00
2012-06-19 22:44:01 +02:00
rng . nextInt ( ) // Has to work
2013-01-17 12:17:19 +01:00
settings . SSLRandomNumberGenerator foreach {
2012-09-12 11:18:42 +02:00
sRng ⇒ rng . getAlgorithm == sRng || ( throw new NoSuchAlgorithmException ( sRng ) )
}
2012-06-18 17:57:09 +02:00
2013-01-17 12:17:19 +01:00
val engine = NettySSLSupport . initializeClientSSL ( settings , NoLogging ) . getEngine
2015-10-30 14:59:36 +01:00
val gotAllSupported = enabled . toSet diff engine . getSupportedCipherSuites . toSet
val gotAllEnabled = enabled . toSet diff engine . getEnabledCipherSuites . toSet
2012-06-19 22:44:01 +02:00
gotAllSupported . isEmpty || ( throw new IllegalArgumentException ( "Cipher Suite not supported: " + gotAllSupported ) )
gotAllEnabled . isEmpty || ( throw new IllegalArgumentException ( "Cipher Suite not enabled: " + gotAllEnabled ) )
2013-01-17 12:17:19 +01:00
engine . getSupportedProtocols . contains ( settings . SSLProtocol . get ) ||
( throw new IllegalArgumentException ( "Protocol not supported: " + settings . SSLProtocol . get ) )
2012-06-18 17:57:09 +02:00
2012-06-19 22:44:01 +02:00
CipherConfig ( true , config , cipher , localPort , remotePort )
} catch {
case ( _ : IllegalArgumentException ) | ( _ : NoSuchAlgorithmException ) ⇒ CipherConfig ( false , AkkaSpec . testConf , cipher , localPort , remotePort ) // Cannot match against the message since the message might be localized :S
}
2012-06-18 14:55:49 +02:00
}
2012-06-11 18:33:05 +02:00
}
@org . junit . runner . RunWith ( classOf [ org . scalatest . junit . JUnitRunner ] )
2012-06-15 18:05:02 +02:00
class Ticket1978SHA1PRNGSpec extends Ticket1978CommunicationSpec ( getCipherConfig ( "SHA1PRNG" , "TLS_RSA_WITH_AES_128_CBC_SHA" ) )
2012-06-11 18:33:05 +02:00
@org . junit . runner . RunWith ( classOf [ org . scalatest . junit . JUnitRunner ] )
2012-06-20 19:06:12 +02:00
class Ticket1978AES128CounterSecureRNGSpec extends Ticket1978CommunicationSpec ( getCipherConfig ( "AES128CounterSecureRNG" , "TLS_RSA_WITH_AES_128_CBC_SHA" , "TLS_RSA_WITH_AES_256_CBC_SHA" ) )
2012-06-11 18:33:05 +02:00
2012-07-02 13:13:45 +02:00
@org . junit . runner . RunWith ( classOf [ org . scalatest . junit . JUnitRunner ] )
class Ticket1978AES256CounterSecureRNGSpec extends Ticket1978CommunicationSpec ( getCipherConfig ( "AES256CounterSecureRNG" , "TLS_RSA_WITH_AES_128_CBC_SHA" , "TLS_RSA_WITH_AES_256_CBC_SHA" ) )
2012-06-11 18:33:05 +02:00
/* *
2015-05-15 16:53:24 +02:00
* Both of the `Inet` variants require access to the Internet to access random . org .
2012-06-11 18:33:05 +02:00
*/
2012-06-19 21:19:19 +02:00
@org . junit . runner . RunWith ( classOf [ org . scalatest . junit . JUnitRunner ] )
2012-06-20 19:06:12 +02:00
class Ticket1978AES128CounterInetRNGSpec extends Ticket1978CommunicationSpec ( getCipherConfig ( "AES128CounterInetRNG" , "TLS_RSA_WITH_AES_128_CBC_SHA" ) )
2014-08-29 13:39:30 +02:00
with InetRNGSpec
2012-06-11 18:33:05 +02:00
/* *
2015-05-15 16:53:24 +02:00
* Both of the `Inet` variants require access to the Internet to access random . org .
2012-06-11 18:33:05 +02:00
*/
@org . junit . runner . RunWith ( classOf [ org . scalatest . junit . JUnitRunner ] )
2012-06-20 19:06:12 +02:00
class Ticket1978AES256CounterInetRNGSpec extends Ticket1978CommunicationSpec ( getCipherConfig ( "AES256CounterInetRNG" , "TLS_RSA_WITH_AES_256_CBC_SHA" ) )
2014-08-29 13:39:30 +02:00
with InetRNGSpec
trait InetRNGSpec { this : Ticket1978CommunicationSpec ⇒
override def preCondition = try {
( new RandomDotOrgSeedGenerator ) . generateSeed ( 128 )
true
} catch {
case NonFatal ( e ) ⇒
log . warning ( "random.org not available: {}" , e . getMessage ( ) )
false
}
override implicit val timeout : Timeout = Timeout ( 90. seconds )
}
2012-06-11 18:33:05 +02:00
2012-06-15 18:31:28 +02:00
@org . junit . runner . RunWith ( classOf [ org . scalatest . junit . JUnitRunner ] )
class Ticket1978DefaultRNGSecureSpec extends Ticket1978CommunicationSpec ( getCipherConfig ( "" , "TLS_RSA_WITH_AES_128_CBC_SHA" ) )
2012-06-11 18:33:05 +02:00
@org . junit . runner . RunWith ( classOf [ org . scalatest . junit . JUnitRunner ] )
2012-06-19 22:44:01 +02:00
class Ticket1978CrappyRSAWithMD5OnlyHereToMakeSureThingsWorkSpec extends Ticket1978CommunicationSpec ( getCipherConfig ( "" , "SSL_RSA_WITH_NULL_MD5" ) )
2012-06-11 18:33:05 +02:00
2012-06-19 22:44:01 +02:00
@org . junit . runner . RunWith ( classOf [ org . scalatest . junit . JUnitRunner ] )
class Ticket1978NonExistingRNGSecureSpec extends Ticket1978CommunicationSpec ( CipherConfig ( false , AkkaSpec . testConf , "NonExistingRNG" , 12345 , 12346 ) )
abstract class Ticket1978CommunicationSpec ( val cipherConfig : CipherConfig ) extends AkkaSpec ( cipherConfig . config ) with ImplicitSender {
2012-06-18 17:57:09 +02:00
2014-08-29 13:39:30 +02:00
implicit val timeout : Timeout = Timeout ( 10. seconds )
2012-06-11 18:33:05 +02:00
2012-06-19 22:44:01 +02:00
lazy val other : ActorSystem = ActorSystem (
"remote-sys" ,
2013-01-17 16:19:31 +01:00
ConfigFactory . parseString ( "akka.remote.netty.ssl.port = " + cipherConfig . remotePort ) . withFallback ( system . settings . config ) )
2012-06-11 18:33:05 +02:00
2013-01-03 17:17:12 +01:00
override def afterTermination ( ) {
2012-06-19 22:44:01 +02:00
if ( cipherConfig . runTest ) {
2013-05-02 17:12:36 +02:00
shutdown ( other )
2012-06-19 22:44:01 +02:00
}
2012-06-11 18:33:05 +02:00
}
2014-08-29 13:39:30 +02:00
def preCondition : Boolean = true
2012-06-20 14:43:11 +02:00
( "-" ) must {
2014-08-29 13:39:30 +02:00
if ( cipherConfig . runTest && preCondition ) {
2014-01-16 15:16:35 +01:00
val ignoreMe = other . actorOf ( Props ( new Actor { def receive = { case ( "ping" , x ) ⇒ sender ( ) ! ( ( ( "pong" , x ) , sender ( ) ) ) } } ) , "echo" )
2012-11-21 14:18:24 +01:00
val otherAddress = other . asInstanceOf [ ExtendedActorSystem ] . provider . asInstanceOf [ RemoteActorRefProvider ] . transport . defaultAddress
2012-06-18 14:02:08 +02:00
2014-08-29 13:39:30 +02:00
"support tell" in within ( timeout . duration ) {
2013-03-26 18:17:50 +01:00
val here = {
system . actorSelection ( otherAddress . toString + "/user/echo" ) ! Identify ( None )
expectMsgType [ ActorIdentity ] . ref . get
}
2012-06-20 14:43:11 +02:00
2012-06-20 15:19:24 +02:00
for ( i ← 1 to 1000 ) here ! ( ( "ping" , i ) )
2014-08-29 13:39:30 +02:00
for ( i ← 1 to 1000 ) expectMsgPF ( ) { case ( ( "pong" , i ) , `testActor` ) ⇒ true }
2012-06-11 18:33:05 +02:00
}
2014-08-29 13:39:30 +02:00
"support ask" in within ( timeout . duration ) {
2012-07-21 18:30:14 +02:00
import system.dispatcher
2013-03-26 18:17:50 +01:00
val here = {
system . actorSelection ( otherAddress . toString + "/user/echo" ) ! Identify ( None )
expectMsgType [ ActorIdentity ] . ref . get
}
2012-06-20 14:43:11 +02:00
2012-09-19 17:32:54 +02:00
val f = for ( i ← 1 to 1000 ) yield here ? ( ( "ping" , i ) ) mapTo classTag [ ( ( String , Int ) , ActorRef ) ]
2015-01-16 11:09:59 +01:00
Await . result ( Future . sequence ( f ) , remaining ) . map ( _ . _1 . _1 ) . toSet should === ( Set ( "pong" ) )
2012-06-11 18:33:05 +02:00
}
2012-06-15 15:04:54 +02:00
} else {
2014-01-17 15:52:20 +01:00
"not be run when the cipher is not supported by the platform this test is currently being executed on" in {
pending
2012-06-15 15:04:54 +02:00
}
2012-06-11 18:33:05 +02:00
}
}
2012-06-18 13:57:42 +02:00
}