Linuxでコマンドを実行して、書き込むファイルを作成したり開いたりできないようにしたいのですが。それでも通常どおりファイルを読み取ることができ(空のchrootはオプションではありません)、すでに開いているファイル(特にstdout)に書き込むことができます。
特定のディレクトリ(つまり、現在のディレクトリ)へのファイルの書き込みがまだ可能である場合のボーナスポイント。
プロセスローカルなソリューションを探しています。つまり、システム全体のAppArmorやSELinuxなどの設定や、ルート権限を必要としません。ただし、カーネルモジュールのインストールが必要になる場合があります。
私は機能を検討していましたが、ファイルを作成する機能があれば、これは簡単で簡単だったでしょう。 ulimitは、このユースケースをカバーする場合に便利な別のアプローチです。
この仕事に適したツールはfseccomp
に基づいているようですsync-ignoring
fバスティアン・ブランクによるコード。私はこの比較的小さなファイルを思いついたので、そのすべての子がファイルを書き込み用に開くことができなくなりました。
/*
* Copyright (C) 2013 Joachim Breitner <[email protected]>
*
* Based on code Copyright (C) 2013 Bastian Blank <[email protected]>
*
* 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;
}
ここでは、ファイルを読み取ることがまだ可能であることがわかります。
[jojo@kirk:1] Wed, der 06.03.2013 um 12:58 Uhr Keep Smiling :-)
> ls test
ls: cannot access test: No such file or directory
> echo foo > test
bash: test: Permission denied
> ls test
ls: cannot access test: No such file or directory
> touch test
touch: cannot touch 'test': Permission denied
> head -n 1 no-writes.c # reading still works
/*
ファイルの削除、移動、または開く以外のその他のファイル操作を妨げるものではありませんが、追加することはできます。
Cコードを記述せずにこれを可能にするツールは syscall_limiter です。
空のchrootを作成してから、chroot内でメインファイルシステムを読み取り専用としてバインドマウントしますか?
読み取り専用のバインドマウントを作成するには、おそらく次のようになります。
mount --bind /foo/ /path/to/chroot/
mount -o remount,ro /path/to/chroot/
Jailに書き込みアクセスを許可したい他のディレクトリをバインドマウントすることもできます。特別なディレクトリ(/ dev /、/ proc /、/ sys /)をバインドマウントする必要がある場合は、そのままマウントするのは安全ではない可能性があるので注意してください。
open(…)
関数の代替を作成し、LD_PRELOADを使用してそれをロードすることを検討しますか?
最も簡単な解決策は、関連するファイルシステムを読み取り専用でマウントして新しいファイルシステム名前空間を作成し、制限しようとしているプログラムを実行するラッパープログラムでしょう。
これは、ReadOnlyDirectories=
を使用して特定のディレクトリをサービスの読み取り専用としてマークするときにsystemd
が行うことです。 util-linux
にはunshare
コマンドもあり、新しい名前空間を作成することができるため、次のようなことができます。
unshare -m <wrapper>
ここで、wrapper
は、実際のターゲットプログラムを開始する前に、必要に応じてファイルシステムを再マウントする必要があります。
唯一の問題は、新しい名前空間を作成するにはroot
である必要があるということです...
これをchrootで実行し、/tmp
などの特別なバージョンを内部にマウントできます。たぶん systemd が役に立ちます。特に systemd-nspawn(1) は、あなたが望んでいるように見えます。
仮想マシンを使用すると、ホストシステムに影響を与えることなく、スクリプトで任意の場所に書き込むことができます。また、目的のように見えるスクリプトが実際にどこに書き込みを試みているのかを調べることができます。
たとえば、次のコマンドでArch Linuxを簡単に起動できます。
kvm -boot d -m 512 -cdrom archlinux-*.iso
Rootとしていくつかの初期設定を行うことが、本当に最も簡単な方法です。具体的には、 読み取り専用バインドマウントへのchroot は、最も抵抗の少ないパスです。
mount --bind
の代わりに bindfs を使用して、rootでなくても読み取り専用ビューを作成できます。ただし、chrootなどの他のファイルへのアクセスを防ぐために、rootとして何かを行う必要があります。
別のアプローチは LD_PRELOAD
ファイルのオープンにフックし、書き込みを許可しないライブラリです。これには特別な権限は必要ありません。セキュリティの観点からは、これは回避できますが、任意のネイティブコードではなく、特定の機能のみを含める必要があるユースケースでは問題ありません。ただし、このための既存のライブラリは知りません。 LD_PRELOAD
を使用して、mount --bind
またはbindfs
で作成された読み取り専用ビューにプログラムを限定することもできます。繰り返しますが、私は既存のライブラリを知りません。
Debianとその派生物では、 schroot 環境をセットアップできます。 Schrootはsetuidルートであり、ルートとして構成する必要がありますが、許可されたユーザーであれば誰でも実行できます。
ルートからの協力を必要としない方法は、仮想マシンでプロセスを実行することです。 KVMまたはVirtualBox、または ser-mode Linux を設定できます。これは少し重いですが、追加のメモリ消費を意味しますが、速度には影響しません。生のシンボリック計算。
rootにならずにプロセスを「監獄」にする方法 は、インスピレーションを与える可能性があります。
プロセスがファイルを書き込まないようにする(ただし、ファイルを作成しない)ための1つの方法は、最初にulimit -f 0
を呼び出すことです。これは、ファイルに書き込もうとするとすぐにプロセスを中止しますが、空のファイルを作成することも可能です。