Объединение R plumber и RAppArmor

#r #plumber #apparmor

#r #сантехник #снаряжение

Вопрос:

Я просто хочу объединить пакеты 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")
}
 

Этот фрагмент кода представляет собой упрощенную версию того, что произойдет позже. Пока кажется, что любой объект, указанный в функции, например data, не может быть передан в eval.secure, учитывая ограничения пользователя в r-user (стандартный профиль RAppArmor). Я даже пытался разрешить полный доступ к /** для r-пользователя, отредактировав профиль, но безуспешно. Открытие plumber на localhost и использование curl с curl --data "func=function(x){return(nrow(x))}" "http://localhost:8000/eval_res" для получения результата eval.secure() приводит к пустому ответу {} . Тот же код работает без eval.secure() и корректно возвращает [100] результат. Может ли кто-нибудь помочь мне, чтобы я лучше понял проблему или даже исправил код?

Мой текущий обходной путь — сохранить все в формате csv перед eval.secure(), а затем прочитать его в eval.secure, предоставив доступ к этой папке в профиле r-user, но это определенно не убедительное решение.

Для тех из вас, кто запутался в функции: на более позднем этапе опция func будет содержать некоторый код, который будет проанализирован и оценен, но для этого небольшого примера я подумал, что это только добавит ненужной сложности.

Заранее спасибо!

Ответ №1:

 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 ожидает найти ваши переменные в its 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")
}
 

Комментарии:

1. Это было именно то, что мне было нужно. Большое вам спасибо!