我将如何构建一款允许玩家在 Ruby 的资源限制下编写引擎的游戏?
How would I build a game that allowed players to write their engines with resource constraints in Ruby?
我想复制 Scribd 开发者挑战,但使用 ruby 中的 Gosu 框架构建它。我知道大部分是怎么做的,除了我不是 100% 确定如何做下面的事情。我想要一些关于解决此问题的最佳方法的想法。
其他人(学生)将能够将他们的 ruby 代码检查到存储库中,我希望最终 运行 所有不同的机器人相互竞争以确定获胜者。以下是我关于如何执行此操作的问题。
有时间限制和内存使用限制。您将如何执行此操作。本质上,我想我想做的是让游戏 class 有一个棋盘表示,然后调用每个引擎的 main
方法并将其传递到游戏棋盘中。然后该方法应该return一招。如果在时限内没有 return 一步,那么我们继续下一步。此外,应该有一个 ram 限制,这样它们就不能遍历所有可能性并将它们存储在内存中,并且基本上存储游戏中的所有状态。
具体来说,如何在 ruby 中生成一个我可以监视和终止的进程?
当然,时间和 RAM 是问题,但更大的问题是安全性。 运行在您的服务器上使用任意用户代码会引发攻击。如何防止用户上传 monkey-patches 您的应用程序代码以作弊,或从您的服务器发送垃圾邮件,或使用 FileUtils.rm_rf(__dir__)
或 while { fork }
破坏内容?
为了 运行 安全地使用用户代码,您必须 运行 它在沙盒中。但我会回到那个。
开始(并解决 time/RAM 问题)的最简单方法是...
运行 单独进程中的用户代码
要求用户的脚本必须定义具有特定名称的 class(或模块),例如Bot,它实现了您的 main
接口。编写一个包装器脚本,它将用户脚本的路径作为参数,并从 $stdin
读取板数据(作为编组数据,或序列化为 YAML 或 JSON)。然后脚本将 require
临时文件并将板数据传递给 Bot。最后,它将从 Bot
、marshal/serialized 中获取输出,并将其写入 $stdout
.
当用户上传脚本时,您的应用程序会将其写入临时文件和 运行 上述包装器脚本(例如 Open3),将其传递给 marshaled/serialized 板stdin 上的数据,然后读取 unmarshaling/deserializing 来自其 stdout/stderr.
的结果
这如何解决 time/RAM 问题?好吧,由于您只是通过调用 ruby
在单独的进程中 运行ning 您的包装脚本,您可以依靠 OS 的 process-management 功能,从而删除用户 monkey-patching 绕过这些限制的可能性。如果你 google 例如"limit process memory" 连同您的名字 OS 您会找到很多信息。例如,对于 Linux,此工具看起来很方便:timeout。使用这样的工具,您可能 运行 例如:
$ timeout -t 60 -m 10000 ruby /path/to/user/script.rb
安全
好的,那么安全性呢?这是一个难题,尤其是因为 Ruby 非常灵活,所以我不能只告诉你 "this is the solution."
您可以做的一件事是 运行 虚拟机中的所有用户代码,例如使用Docker。这将使防止用户代码访问您的(真实)文件系统或网络变得容易。 (在这种情况下,在 VM 上有一个简单的 Ruby 服务器 运行ning 可能是有意义的,它可以从您的应用程序接收脚本和板数据,运行 脚本,并响应结果,因为您的应用程序将无法在 VM 上直接调用 ruby
。)
不过,这仍然留下了很大的恶作剧空间。它减轻了 FileUtils.rm_rf
或 while { fork }
可能造成的损害,因为您可以启动一个新的 VM,但这仍然是一个不便。为了完全防止这些,您确实需要一个沙箱来可靠地防止用户访问可能被恶意使用的方法和模块。在 Ruby 中没有一种真正的方法可以做到这一点,唉,但是有一些工具和一些代码可以帮助您入门。谷歌搜索 "Ruby sandbox" 会出现很多。我发现有启发性的一个项目是 RubyFiddle, which is open source and so its code is available on GitHub。它会指向 jruby-sandbox,它使用 JRuby 进行沙盒处理,因为 Java 与 (MRI) Ruby 不同,确实有成熟的沙盒解决方案。
希望对您有所帮助。祝你好运!
我想复制 Scribd 开发者挑战,但使用 ruby 中的 Gosu 框架构建它。我知道大部分是怎么做的,除了我不是 100% 确定如何做下面的事情。我想要一些关于解决此问题的最佳方法的想法。
其他人(学生)将能够将他们的 ruby 代码检查到存储库中,我希望最终 运行 所有不同的机器人相互竞争以确定获胜者。以下是我关于如何执行此操作的问题。
有时间限制和内存使用限制。您将如何执行此操作。本质上,我想我想做的是让游戏 class 有一个棋盘表示,然后调用每个引擎的 main
方法并将其传递到游戏棋盘中。然后该方法应该return一招。如果在时限内没有 return 一步,那么我们继续下一步。此外,应该有一个 ram 限制,这样它们就不能遍历所有可能性并将它们存储在内存中,并且基本上存储游戏中的所有状态。
具体来说,如何在 ruby 中生成一个我可以监视和终止的进程?
当然,时间和 RAM 是问题,但更大的问题是安全性。 运行在您的服务器上使用任意用户代码会引发攻击。如何防止用户上传 monkey-patches 您的应用程序代码以作弊,或从您的服务器发送垃圾邮件,或使用 FileUtils.rm_rf(__dir__)
或 while { fork }
破坏内容?
为了 运行 安全地使用用户代码,您必须 运行 它在沙盒中。但我会回到那个。
开始(并解决 time/RAM 问题)的最简单方法是...
运行 单独进程中的用户代码
要求用户的脚本必须定义具有特定名称的 class(或模块),例如Bot,它实现了您的 main
接口。编写一个包装器脚本,它将用户脚本的路径作为参数,并从 $stdin
读取板数据(作为编组数据,或序列化为 YAML 或 JSON)。然后脚本将 require
临时文件并将板数据传递给 Bot。最后,它将从 Bot
、marshal/serialized 中获取输出,并将其写入 $stdout
.
当用户上传脚本时,您的应用程序会将其写入临时文件和 运行 上述包装器脚本(例如 Open3),将其传递给 marshaled/serialized 板stdin 上的数据,然后读取 unmarshaling/deserializing 来自其 stdout/stderr.
的结果这如何解决 time/RAM 问题?好吧,由于您只是通过调用 ruby
在单独的进程中 运行ning 您的包装脚本,您可以依靠 OS 的 process-management 功能,从而删除用户 monkey-patching 绕过这些限制的可能性。如果你 google 例如"limit process memory" 连同您的名字 OS 您会找到很多信息。例如,对于 Linux,此工具看起来很方便:timeout。使用这样的工具,您可能 运行 例如:
$ timeout -t 60 -m 10000 ruby /path/to/user/script.rb
安全
好的,那么安全性呢?这是一个难题,尤其是因为 Ruby 非常灵活,所以我不能只告诉你 "this is the solution."
您可以做的一件事是 运行 虚拟机中的所有用户代码,例如使用Docker。这将使防止用户代码访问您的(真实)文件系统或网络变得容易。 (在这种情况下,在 VM 上有一个简单的 Ruby 服务器 运行ning 可能是有意义的,它可以从您的应用程序接收脚本和板数据,运行 脚本,并响应结果,因为您的应用程序将无法在 VM 上直接调用 ruby
。)
不过,这仍然留下了很大的恶作剧空间。它减轻了 FileUtils.rm_rf
或 while { fork }
可能造成的损害,因为您可以启动一个新的 VM,但这仍然是一个不便。为了完全防止这些,您确实需要一个沙箱来可靠地防止用户访问可能被恶意使用的方法和模块。在 Ruby 中没有一种真正的方法可以做到这一点,唉,但是有一些工具和一些代码可以帮助您入门。谷歌搜索 "Ruby sandbox" 会出现很多。我发现有启发性的一个项目是 RubyFiddle, which is open source and so its code is available on GitHub。它会指向 jruby-sandbox,它使用 JRuby 进行沙盒处理,因为 Java 与 (MRI) Ruby 不同,确实有成熟的沙盒解决方案。
希望对您有所帮助。祝你好运!