Inquiry / Request for feedback [Service: a sandboxed JVM on a public server]

Quite recently, I got myself this dedicated server. As I was too busy at the time, and saw CommanderKeith’s game, which desperately needed some reliable public server, I offered him a tiny share of mine. This was a great experience, as it turned out to be rather tricky to get things running, which would have been much harder without access to a public server. Anyway, I think there might be a demand for such a service, that delivers a minimal share of a server, for a minimal fee - hopefully just to cover the costs of my dedicated server.

I had this idea to get some feedback from the community, before I would put a lot of effort into building the website. So here it is:

Service: a sandboxed JVM on a public server

[quote]To support multiplayer in your games, you probably want a public server to connect all players to eachother. The costs and maintainance of a dedicated server, or a VirtualPrivateServer is often a big turnoff. Normally you’d only use minimal resources, to run your matching-service, chatroom or simple gameserver.

What you might need is a service that allows you to use minimal resources on a public server. For a minimal fee of 5 EUR / month, you can run your own Java application, hosting 2 serversockets, within a sandbox, on a dedicated server.

To ensure everybody gets their fair share of performance, the following rules apply:

Available CPU resources (DELL Dual XEON 3.2 GHz):

  • 66% average usage in 3 seconds
  • 20% average usage in 10 seconds
  • 5% average usage in 60 seconds
  • 2% average usage in 3600 seconds

Available bandwidth:

  • 5000kB/s average throughput in 3 seconds
  • 1500kB/s average throughput in 10 seconds
  • 500kB/s average throughput in 60 seconds
  • 100kB/s average throughput in 3600 seconds
  • 250MB traffic per day

Available resources:

  • max 48MB Java-heap, 4MB direct-memory
  • two serversockets for incoming TCP connections (port 1024+)
  • 250MB harddisk space (read/write access in your own user-directory)
  • the STDOUT and STDERR as streams visible by a webbrowser, and as downloadable files after the process is terminated.

Runtime restrictions:

  • once you exceed the available resources, your process will be restarted. You will receive a notification email, describing what happened.
  • not possible to connect to hosts with a Socket other than port 80 (further, only incoming sockets allowed) to prevent spammers/hackers from abusing this service.
    [/quote]
    For 5 EUR a month, I think it’s affordable for just about anyone aspiring to get into networked games (or other services).

What are your thoughts? Would you use this service?

Sounds like a pretty great service, I use a dedicated box myself atm - but having somewhere seperate to run a fixed and stable build would be great.

These would probably kill my interest tho. 10MB in particular, but the memory probably also.

Kev

Thanks for your quick response!

Hm, well, the harddisk-space could probably be bumped up to 250MB, as that’s not really the problem. But as you’re not able to do much with it (no filesystem access) I thought 10MB was pretty much enough. I’ll look into how I can both have file-access support, and keep it secure… my knowledge of the SecurityManager is pretty rusty.

The memory-limitation is kinda there to stay - I haven’t got much room to spare - otherwise I’d have to double or triple my serverhosting-costs.

If there is enough demand, it wouldn’t be a problem though.

Maybe another account for 10 EUR / month for 75MB heap, 15 EUR / month for 100MB heap.

Any more expensive, and you can almost afford that VPS yourself :slight_smile:

Riven’s dedicated server has been sensational for testing my game, so I’d recommend this to anyone. The latency is extremely low - even from where I am in Sydney it takes only 175 milliseconds to send a message to the Amsterdam dedicated server.

It’s quite a thrill to have your game running on the internet for everyone to join in and play at once.

A big thumbs up from me :slight_smile:

Thanks for the feedback Keith :slight_smile:

Kevin, how much heap would not kill your interest?

64 I guess I’d see as the minimum, I’ve hit there before. I spose I’m assuming I’d want to grow further than my games have in the past, but then maybe once the game gets going this wouldn’t be the place for it?

Is the intention that this is for fledgling games, or for fully developed masterpieces eventually?

Kev

It’s mainly meant to lower the bar for those that are interested in deploying a networked game, but get frustrated by all hurdles to get there. So that’s pretty much ‘fledging’ IMHO. I simply don’t want to be held responsible for anyones business or losses due to downtime/bugs. I want to help out indies, and it would be great to breakeven with the hostingbills.

Once games grow, I think they will either need to make a a tiny profit to pay for a cheap VPS, or just optimize their gameserver. I can’t get everybody the specs of a decent server, as there are only 2 CPUs, and little spare RAM on this box, and everybody has to share these. For these low fees, an aspiring indie might want to make some compromises.

For 35 EUR/month you can rent some cheap-ass VPS with a decent amount of RAM, so raising the fees anywhere near that, wouldn’t make much sense right?

I guess I could simply have less accounts then, with a larger heap (64M).

When more than 3 indies use it, I can get another server with 1024MB RAM.

Thanks for the feedback, and I’ll see whether there is a demand for it! :wink:

It’s strange that more people haven’t jumped on this. I think it’s because the experts here already have access to a server, and other people don’t understand what a server does.

Maybe I can try to explain?

So the type of server that Riven offers is great because you can run your program on it (ie use the CPU) instead of just storing files on it (like the free web pages you can get from www.freewebs.com etc). And having this dedicated server is better than using your own internet-connected computer since you don’t have to do port-forwarding (which is difficult and must be done for those connected to the net via a router, which is probably most people) and the dedicated server’s IP address never changes, unlike many people’s internet ip does when they re-connect to the net. Also, the dedicated server is never turned off, is probably faster than your machine and has faster upload/download limits and latencies.

Since non-experts are unlikely to know that they even need a server and won’t know how to set it up anyway, I suppose the demand for this kind of thing will be low, even though it’s so useful.

Maybe people on the forum who are paying for their own dedicated server (Riven, myself (free-riding off Riven for the moment), Kingaschi (with Aevum Obscrurum?)) should see if it’s worthwhile to pool resources?

I agree. This service would be a solution waiting for people to realize the problem. Most give up before they reach this point.

Anyway, enough of the theories, I’ve been working on this service for a few hours last week, and I pretty much figured out most of it. I’ve written a bunch of tools to monitor CPU usage and network-activity (unix commandline tools rock!). As always, I’m rather busy with work, but I think this might be up and running in the next month.

I’ll be interested in a slot assuming prices are reasonable :slight_smile: if only to offload the server from my current box. I’m looking seriously at Amazon EC2 hosting atm also tho.

Kev

Hm… I kinda disliked the CPU-limitation (and termination upon exceeding), which was obviously only going to get your app offline when things are about to get fun with a few players.

So quite accidently I stumbled upon this page that explained UNIX processes, and how they can be stopped (SIGSTOP) and continued (SIGCONT). With these I can start/stop the process in a loop, once it starts to exceed the restrictions - no termination of processes!

I immediately coded it in Java, with Runtime.exec(“kill -{CONT|STOP} {pid}”), but performance was horrendous (like 7% CPU usage when starting/stopping every 100ms). I kinda hoped it was the Java overhead, so I started to code it in C to write an commandline-utility that throttles the process at a specified PID.


	while(1)
	{
		kill(pid, SIGSTOP);
		msleep(stopms);
		kill(pid, SIGCONT);
		msleep(contms);
	}

Java: 7.0% cpu usage
Native: 0.1% CPU usage :slight_smile:

My first babysteps in C++ :wink: (apart from JNI with basic math on pointers)


#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <time.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>







void msleep(int x)
{
    struct timespec sleeptime;
    struct timespec remaining;
    int result;               
    remaining.tv_sec = x/1000;
    remaining.tv_nsec = (x%1000)*1000000;
    do {                                 
        sleeptime.tv_sec = remaining.tv_sec;
        sleeptime.tv_nsec = remaining.tv_nsec;
        result = nanosleep(&sleeptime,&remaining);
    } while(0>result && EINTR==errno);
}



int parseInt(char *arg)
{
   int num = 0;
   while(*arg)
      num = (num*10)+((*arg++)-'0');
   return num;
}



int main(int argc, char *argv[])
{
   if(argc != 4)
   {
      printf("Usage: {PID} {INTERVAL} {PERCENTAGE}\n");
      return 1;
   }

   int pid  = parseInt(argv[1]);
   int intr = parseInt(argv[2]);
   int p100 = parseInt(argv[3]);

   int work = intr * p100 / 100;
   int wait = intr - work;

   while(1)
   {
      if(kill(pid, SIGSTOP)!=0)break;
      msleep(wait);
      if(kill(pid, SIGCONT)!=0)break;
      msleep(work);
   }
}

That’s so much better than killing the process, nice. 8)

Let me know when it’s ready to try my game as a test.

I had way too much fun yesterday writing my sandbox implementation/securitymanager.

I can run multiple programs in a JVM now, each with their own stdout/stderr (!), completely isolated from eachother, each with their own SecurityManager, and a System.exit(0) that actually only shuts down their own Sandbox safely (w00t), which will also be terminated when all the alive threads of the Sandbox turn out to be daemons. As some of you might have figured out, I’m making heavy use of ThreadGroups (which even Sun called a failed experiment, yet I’m very grateful they exist!), and escaping out of a ThreadGroup would mean you’d escape the Sandbox. Now my question to you: are there ways to escape from a ThreadGroup? I tried Swing.invokeLater but luckily it requires a RuntimePermission with the name “createClassLoader” which can be intercepted in the SecurityManager (phew!). ;D

The only disadvantage of these multiple isolated sandboxes is that they do share the heap :frowning: and are in the same process, so cannot be cpu-throttled individually with my own app. But i can use ThreadMXBeans to monitor the Sandbox anyway. A big advantage though is that there is only one JVM memory-footprint. I want to use these Sandboxes for evaluating the service, and maybe extremely low-end hosting, for an even lower price (to get people interested, and wanting to upgrade…?).

No idea about ThreadGroups sorry.

Sounds like you’ve just about made an multi virtual machone (MVM) which is meant to be pretty hard. JavaFX uses an MVM by the way…

Apparently there are problems with them though - if one program crashes it crashes the whole MVM/JVM, and you can’t kill a selected program, you can only kill the whole MVM/JVM.

Hm? Kinda weird :slight_smile: I can kill individual Sandboxes…

From outside the sandbox:
sandbox.terminate();

From inside the sandbox:
System.exit(0);

System.out.println(“hello world”); ends up on the stdout of the Sandbox.

Even a OutOfMemoryException will terminate the ‘offending’ Sandbox (although that might not be the sandbox that uses the most memory… :slight_smile:
Only a native crash can take everything down… but the SecurityManager is quite restrictive, so I doubt I’ll see a native crash.

Psst… const correctness.

And if you want to do things the C way then there’s atoi, or theres the (prefered, more robust) C++ way via stringstream (see from_string about halfway down).

I should have been specific, I meant like doing a ctrl-c or killing the program from the windows task manager.

What stuff are you limiting the security manager to? no reflection?

Pretty much everything is restricted that might cause harm (SandboxIsolationSecurityManager)

  • manipulating Threads and ThreadGroups
  • loading native libraries
  • changing securitymanager
  • writing into system properties
  • reflection (on inaccessible fields/methods)
  • management with mxbeans
  • AWT/Swing… this is serverside after all

Everything left is restricted by the SandboxSecurityManager which you are able to grant permissions.

My current setup code is:


      System.setSecurityManager(new SandboxIsolationSecurityManager());

      SandboxSecurityManager sandboxManager1 = new SandboxSecurityManager(Thread.NORM_PRIORITY);
      sandboxManager1.allowReadDir(new File(System.getProperty("java.home")));
      sandboxManager1.allowReadDir(new File("M:/java/workspace/Base/bin/"));
      sandboxManager1.allowReadDir(new File("M:/java/workspace/Components/bin/"));
      sandboxManager1.allowReadDir(new File("M:/sandbox"));
      sandboxManager1.allowWriteDir(new File("M:/sandbox"));
      sandboxManager1.allowServerSocket(3344);
      sandboxManager1.allowServerSocket(3355);
      sandboxManager1.allowSocket(new InetSocketAddress("127.0.0.1", 3344));

      SandboxSecurityManager sandboxManager2 = sandboxManager1; // me = lazy

      Runnable sandboxTask1 = createLauncher("my.jar:your.jar:their.jar", "my.mainclass", new String[]{"the args", "to pass"});
      Runnable sandboxTask2 = createLauncher("my.jar:your.jar:their.jar", "my.mainclass", new String[]{"the args", "to pass"});

      OutputStream out1 = new FileOutputStream("M:/whatever/sandbox1_stdout.txt");
      OutputStream err1 = new FileOutputStream("M:/whatever/sandbox1_stderr.txt");
      OutputStream out2 = new FileOutputStream("M:/whatever/sandbox2_stdout.txt");
      OutputStream err2 = new FileOutputStream("M:/whatever/sandbox2_stderr.txt");

      new Sandbox("FirstBox",  sandboxManager1, sandboxTask1, out1, err1);
      new Sandbox("SecondBox", sandboxManager2, sandboxTask2, out2, err2);

And this creates that Sandbox-Runnable:


   public static final Runnable createLauncher(String classpath, String mainclass, final String[] args)
   {
      // setup classpath classloader

      String[] cpStrings = Text.split(classpath, File.pathSeparatorChar);
      URL[] cpURLs = new URL[cpStrings.length];

      try
      {
         for (int i = 0; i < cpURLs.length; i++)
            cpURLs[i] = new File(cpStrings[i]).toURI().toURL();
      }
      catch (MalformedURLException exc)
      {
         throw new RuntimeException(exc);
      }

      // invoke: public static void main(String[] args) on mainclass

      try
      {
         URLClassLoader cl = new URLClassLoader(cpURLs);
         Class< ? >[] params = new Class[] { String[].class };
         final Method method = cl.loadClass(mainclass).getMethod("main", params);

         Runnable task = new Runnable()
         {
            @Override
            public void run()
            {
               try
               {
                  method.invoke(null, new Object[] { args });
               }
               catch (Exception exc)
               {
                  exc.printStackTrace();
               }
            }
         };

         return task;
      }
      catch (Exception exc)
      {
         throw new RuntimeException(exc);
      }
   }

Sounds pretty watertight! Unfortunately my game needs reflection though.

Since people will be taken on trust, do you think that the restrictions might get in the way?