结合 R plumber 和 RAppArmor
Combining R plumber and RAppArmor
我只是想以某种方式组合 plumber 和 RAppArmor 包,用户可以使用 plumber-API 将代码发送到服务器,然后在 RAppArmor 中的配置文件的帮助下安全地对其进行评估。我怎样才能让下面的代码工作:
#* Evaluate the result
#* @post /eval_res
function(func){
library("RAppArmor")
data <- cbind(rnorm(100),rnorm(100))
eval.secure(nrow(data),profile="r-user")
}
此代码块是稍后发生的事情的简化版本。到目前为止,似乎函数中指定的任何对象,例如鉴于 r-user(RAppArmor 的标准配置文件)中的用户限制,数据不能传递给 eval.secure。我什至尝试通过编辑配置文件允许 r-user 完全访问 /**,但没有成功。在本地主机上打开 plumber 并使用带有 curl --data "func=function(x){return(nrow(x))}" "http://localhost:8000/eval_res"
的 curl 来获取 eval.secure() 的结果导致空答案 {}
。相同的代码在没有 eval.secure() 和正确 returns [100]
的情况下也能正常工作。有人可以帮助我更好地理解问题甚至修复代码吗?
我目前的解决方法是在 eval.secure() 之前将所有内容保存到 csv,然后在 eval.secure 中读取它,授予对 r 用户配置文件中该文件夹的访问权限,但这绝对是不是一个令人信服的解决方案。
对于那些对函数感到困惑的人:在后面的步骤中,选项 func 将包含一些将被解析和评估的代码,但对于这个小例子,我认为它只会增加不必要的复杂性。
提前致谢!
library("RAppArmor")
#* Evaluate the result
#* @post /eval_res
function(func){
data <- cbind(rnorm(100),rnorm(100))
unix::eval_safe(nrow(data), profile="r-user")
}
所以
library("RAppArmor")
#* Evaluate the result
#* @post /eval_res
function(func){
data <- cbind(rnorm(100),rnorm(100))
unix::eval_safe(eval(parse(text=func)), profile="r-user")
}
eval.secure
只需将所有参数传递给unix::eval_safe
即可。 eval.secure
不起作用的原因是因为 eval_safe
期望在它的 parent.frame()
中找到你的变量,在 eval.secure
的情况下是一个空函数体。
eval_safe
使用 parent.frame()
function (expr, tmp = tempfile("fork"), std_out = stdout(), std_err = stderr(),
timeout = 0, priority = NULL, uid = NULL, gid = NULL, rlimits = NULL,
profile = NULL, device = pdf)
{
orig_expr <- substitute(expr)
out <- eval_fork(expr = tryCatch({
if (length(priority))
setpriority(priority)
if (length(rlimits))
set_rlimits(rlimits)
if (length(gid))
setgid(gid)
if (length(uid))
setuid(uid)
if (length(profile))
aa_change_profile(profile)
if (length(device))
options(device = device)
graphics.off()
options(menu.graphics = FALSE)
------> serialize(withVisible(eval(orig_expr, parent.frame())),
NULL)
}, error = function(e) {
old_class <- attr(e, "class")
structure(e, class = c(old_class, "eval_fork_error"))
}, finally = substitute(graphics.off())), tmp = tmp, timeout = timeout,
std_out = std_out, std_err = std_err)
if (inherits(out, "eval_fork_error"))
base::stop(out)
res <- unserialize(out)
if (res$visible)
res$value
else invisible(res$value)
}
# parent.frame() in eval_safe when using eval.secure
function (...)
{
# nothing here
unix::eval_safe(...)
}
# parent.frame() when using eval_safe directly
function(func){
data <- cbind(rnorm(100),rnorm(100))
# Your stuff is here
unix::eval_safe(nrow(data), profile="r-user")
}
我只是想以某种方式组合 plumber 和 RAppArmor 包,用户可以使用 plumber-API 将代码发送到服务器,然后在 RAppArmor 中的配置文件的帮助下安全地对其进行评估。我怎样才能让下面的代码工作:
#* Evaluate the result
#* @post /eval_res
function(func){
library("RAppArmor")
data <- cbind(rnorm(100),rnorm(100))
eval.secure(nrow(data),profile="r-user")
}
此代码块是稍后发生的事情的简化版本。到目前为止,似乎函数中指定的任何对象,例如鉴于 r-user(RAppArmor 的标准配置文件)中的用户限制,数据不能传递给 eval.secure。我什至尝试通过编辑配置文件允许 r-user 完全访问 /**,但没有成功。在本地主机上打开 plumber 并使用带有 curl --data "func=function(x){return(nrow(x))}" "http://localhost:8000/eval_res"
的 curl 来获取 eval.secure() 的结果导致空答案 {}
。相同的代码在没有 eval.secure() 和正确 returns [100]
的情况下也能正常工作。有人可以帮助我更好地理解问题甚至修复代码吗?
我目前的解决方法是在 eval.secure() 之前将所有内容保存到 csv,然后在 eval.secure 中读取它,授予对 r 用户配置文件中该文件夹的访问权限,但这绝对是不是一个令人信服的解决方案。
对于那些对函数感到困惑的人:在后面的步骤中,选项 func 将包含一些将被解析和评估的代码,但对于这个小例子,我认为它只会增加不必要的复杂性。
提前致谢!
library("RAppArmor")
#* Evaluate the result
#* @post /eval_res
function(func){
data <- cbind(rnorm(100),rnorm(100))
unix::eval_safe(nrow(data), profile="r-user")
}
所以
library("RAppArmor")
#* Evaluate the result
#* @post /eval_res
function(func){
data <- cbind(rnorm(100),rnorm(100))
unix::eval_safe(eval(parse(text=func)), profile="r-user")
}
eval.secure
只需将所有参数传递给unix::eval_safe
即可。 eval.secure
不起作用的原因是因为 eval_safe
期望在它的 parent.frame()
中找到你的变量,在 eval.secure
的情况下是一个空函数体。
eval_safe
使用 parent.frame()
function (expr, tmp = tempfile("fork"), std_out = stdout(), std_err = stderr(),
timeout = 0, priority = NULL, uid = NULL, gid = NULL, rlimits = NULL,
profile = NULL, device = pdf)
{
orig_expr <- substitute(expr)
out <- eval_fork(expr = tryCatch({
if (length(priority))
setpriority(priority)
if (length(rlimits))
set_rlimits(rlimits)
if (length(gid))
setgid(gid)
if (length(uid))
setuid(uid)
if (length(profile))
aa_change_profile(profile)
if (length(device))
options(device = device)
graphics.off()
options(menu.graphics = FALSE)
------> serialize(withVisible(eval(orig_expr, parent.frame())),
NULL)
}, error = function(e) {
old_class <- attr(e, "class")
structure(e, class = c(old_class, "eval_fork_error"))
}, finally = substitute(graphics.off())), tmp = tmp, timeout = timeout,
std_out = std_out, std_err = std_err)
if (inherits(out, "eval_fork_error"))
base::stop(out)
res <- unserialize(out)
if (res$visible)
res$value
else invisible(res$value)
}
# parent.frame() in eval_safe when using eval.secure
function (...)
{
# nothing here
unix::eval_safe(...)
}
# parent.frame() when using eval_safe directly
function(func){
data <- cbind(rnorm(100),rnorm(100))
# Your stuff is here
unix::eval_safe(nrow(data), profile="r-user")
}