ssh 自動運転用制限シェルってないのかな
ssh の話題がプチ盛り上がりしてたのにのっかるわけじゃないんですが,自動運転用に実行できるコマンドを制限できるシェルってないものですかね。
候補として考えられるのは
- rssh
- ibsh
- ユーザのやれることを色々制限できるシェル
- 今回やりたいことのわりには巨大すぎ(っぽくてあんまり調べてない)
- authorized_keys の command オプション
- 参考 1: ssh scp sftp の正しい自動実行方法
- 参考 2: ぴろ日記 - expect
- 実行したいコマンドごとに鍵ペアを生成しなきゃいけないのでめんどい
何を考えているかというと,
- 自動運転してやりたいことなんて,scp とか rsync とかなにかしらのコマンド一発(DB のバックアップ)とか,固定化されてることが多い
- 引数とかもある程度固定化されてることが多い
- scp とか rsync とかまんべんなく使用できるようにすると scp -S とかでハマりがち
- さりとてコマンドごとに鍵ペア用意するのはめんどくないすか
なので,
- 設定ファイルで指定したコマンドだけ実行できる
- 失敗するとエラーを吐く(設定ファイルに反映しやすいように)
というシンプルな仕様のシェルがほしいのです。
あくまで,コンセプトコードですが,
#!/usr/bin/perl # rrss - Restricted Remote Shell for SSH use strict; use warnings; use NetAddr::IP::Lite qw( :aton ); use Sys::Syslog qw( :standard :macros ); use YAML (); my ($me) = ( $0 =~ m'([^/]+)$'o ); my $user = $ENV{USER}; my $remote = q{}; if (defined $ENV{SSH_CLIENT} && $ENV{SSH_CLIENT} =~ m'^(\S+)'o) { $remote = $1; } openlog $me, 'pid', LOG_AUTHPRIV; eval { my $config = eval { YAML::LoadFile(sprintf '%s/.rrss/config', $ENV{HOME}); }; die 'configuration file not found' if ! $config; die 'only support remote execution' if ! $remote; die 'interactive mode is not supported' if @ARGV != 2 || $ARGV[0] ne '-c'; my $command = $ARGV[1]; my $remote_ip = NetAddr::IP::Lite->new($remote); foreach my $conf (@$config) { next if exists $conf->{host} && ! NetAddr::IP::Lite->new($conf->{host})->contains($remote_ip); if ($conf->{command} eq $command) { syslog LOG_INFO, '[%s@%s] execute: %s', $user, $remote, $command; exec $command or die "failed: $!"; } } die "permission denied: $command"; }; if ($@) { my $error = $@; # trim error location $error =~ s{ \s+ at \s+ .+? \s+ line \s+ \S+ \s* \z }{}xmso; $remote ||= 'localhost'; syslog LOG_CRIT, '[%s@%s] error: %s', $user, $remote, $error; print $error, "\n"; } closelog; exit 1;
設定ファイル ~/.rrss/config*1 は以下のような感じで。
--- - command: rsync --server -vlogDtprz . target_dir - command: scp -r -t dest_dir host: 192.168.0.0/24 - command: scp -f /etc/passwd host: 192.168.0.1
別段 YAML じゃなくてもいいんですが。あと正規表現を使うことも考えましたが,ゆるさのメリットと危うさのデメリットだとデメリットがやや勝つかな,と思い,あえて eq で。
新しいコマンドを設定に追加したかったら,とりあえず実行すると LOG_AUTHPRIV なログが吐かれるのでそれを見て追加する,と(すれば rsync のコマンドラインも与えやすい)。
こんな感じで C で書かれたシェルが欲しい!ひょっとして私が知らないだけで既にあるんですか?それとも bash とかでそういう設定子があるとか?それともそれともそもそもこういう発想には穴があるとか?
おまけ:ssh でのリモートコマンド実行はどのように呼ばれるか
たとえば,
$ ssh -l foo foreign ls -l /tmp
は,リモート側で,
${SHELL} -c "ls -l /tmp"
みたく実行されるに等しいです。
scp, sftp, rsync はデフォルトで ssh をサブプロセスとして利用します。そのときのコマンドライン(第2引数)はどのようになるかを以下書きます。
local | remote | 備考 |
---|---|---|
scp -r foo user@foreign:bar |
scp -r -t bar |
-t (to mode) |
scp user@foreign:foo bar |
scp -f foo |
-f (from mode) |
scp user@foreign:foo user@foreign:bar |
scp foo user@foreign:bar |
|
sftp user@foreign |
/usr/libexec/openssh/sftp-server |
※1 |
rsync -avz foo user@foreign:bar |
rsync --server -vlogDtprz . bar |
※2 |
rsync -avz user@foreign:foo bar |
rsync --server --sender -vlogDtprz . foo |
※3 |
scp にしても rsync にしても,ローカルのパスなんて知ったことか,という態度でリモートにコマンドが渡されます。つまり,リモート側ではそれを元にアクセス制限をかけることはできない,ということです。
他,注釈
- ※1
- 当たり前といえば当たり前ですが,sftp で制限をかけるのは chroot 等使わないと難しいです。
- ※2
-v
オプションが先頭にきて,-a
オプションが展開されます。--server
オプションがつきます。- ※3
--sender
オプションが追加され,<ローカル> <リモート> という引数順が変わらないことに注目。
2007/10/02 追記
authorized_keys の command がちょっと嫌な理由を思い出しました。
リモートからどのようなコマンドを与えても command で指定したコマンドしか実行されません。なので,
$ scp -r contents_1 user@foreign:target_1
に対応する
scp -r -t target_1
を command として authorized_keys に書いておくとします。
で,そのことを忘れて,
$ scp -r contents_2 user@foreign:target_2
とすると,contents_2 の内容が target_1 に上書きされてしまいます。
というのを実は昨日やってしまったんですね。アヒャ。
*1:$HOME においてますけど,/etc/rrss/$USER とかに置くようにしたほうが安全で管理も楽かも