I want to run a command on Linux in a way that it cannot create or open any files to write. It should still be able to read files as normal (so an empty chroot is not an option), and still be able to write to files already open (especially stdout).Bonus points if writing files to certain directories (i.e. the current directory) is still possible.Im looking for a solution that is process-local, i.e. does not involve configuring things like AppArmor or SELinux for the whole system, nor root privileges. It may involve installing their kernel modules, though.I was looking at capabilities and these would have been nice and easy, if there were a capability for creating files. ulimit is another approach that would be convenient, if it covered this use case.
How to prevent a process from writing files
linux;security;selinux;apparmor;capabilities
It seems that the right tool for this job is fseccomp Based on sync-ignoringf code by Bastian Blank, I came up with this relatively small file that causes all its children to not be able to open a file for writing:/* * Copyright (C) 2013 Joachim Breitner <mail@joachim-breitner.de> * * Based on code Copyright (C) 2013 Bastian Blank <waldi@debian.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this *    list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, *    this list of conditions and the following disclaimer in the documentation *    and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */#define _GNU_SOURCE 1#include <errno.h>#include <fcntl.h>#include <seccomp.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#define filter_rule_add(action, syscall, count, ...) \  if (seccomp_rule_add(filter, action, syscall, count, ##__VA_ARGS__)) abort();static int filter_init(void){  scmp_filter_ctx filter;  if (!(filter = seccomp_init(SCMP_ACT_ALLOW))) abort();  if (seccomp_attr_set(filter, SCMP_FLTATR_CTL_NNP, 1)) abort();  filter_rule_add(SCMP_ACT_ERRNO(EACCES), SCMP_SYS(open), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, O_WRONLY, O_WRONLY));  filter_rule_add(SCMP_ACT_ERRNO(EACCES), SCMP_SYS(open), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, O_RDWR, O_RDWR));  return seccomp_load(filter);}int main(__attribute__((unused)) int argc, char *argv[]){  if (argc <= 1)  {    fprintf(stderr, usage: %s COMMAND [ARG]...\n, argv[0]);    return 2;  }  if (filter_init())  {    fprintf(stderr, %s: can't initialize seccomp filter\n, argv[0]);    return 1;  }  execvp(argv[1], &argv[1]);  if (errno == ENOENT)  {    fprintf(stderr, %s: command not found: %s\n, argv[0], argv[1]);    return 127;  }  fprintf(stderr, %s: failed to execute: %s: %s\n, argv[0], argv[1], strerror(errno));  return 1;}Here you can see that it is still possible to read files:[jojo@kirk:1] Wed, der 06.03.2013 um 12:58 Uhr Keep Smiling :-)> ls testls: cannot access test: No such file or directory> echo foo > testbash: test: Permission denied> ls testls: cannot access test: No such file or directory> touch testtouch: cannot touch 'test': Permission denied> head -n 1 no-writes.c # reading still works/*It does not prevent deleting files, or moving them, or other file operations besides opening, but that could be added.A tool that enables this without having to write C code is syscall_limiter.