sudoを使用してもSSH経由でファイルにデータを書き込むと、権限エラーが発生します。

sudoを使用してもSSH経由でファイルにデータを書き込むと、権限エラーが発生します。

自動化スクリプトを作成しています。その一環として、cronジョブを追加したいと思います。以下は失敗したスクリプトの一部です。

BACKUP_USER=backupbot
SCRIPT_NAME=backup-script.sh

scp -i ./ssh-key ./$SCRIPT_NAME user@server:/tmp
ssh -i ./ssh-key user@server "
    sudo mv /tmp/$SCRIPT_NAME /home/$BACKUP_USER/bin/ &&
    sudo chown $BACKUP_USER /home/$BACKUP_USER/bin/$SCRIPT_NAME &&
    sudo chmod 100 /home/$BACKUP_USER/bin/$SCRIPT_NAME &&
    sudo sed -i 's/THE_URL/'${1}'/' /home/$BACKUP_USER/bin/$SCRIPT_NAME &&
    sudo echo '*/1 * * * *' $BACKUP_USER /home/$BACKUP_USER/bin/$SCRIPT_NAME > /etc/cron.d/discourse-backup"

問題のコマンドは次のとおりです。

sudo echo '*/1 * * * *' $BACKUP_USER /home/$BACKUP_USER/bin/$SCRIPT_NAME > /etc/cron.d/discourse-backup

私は得る:

bash:行5:/etc/cron.d/discourse-backup:権限が拒否されました。

この瞬間まですべてがきちんと働いていました。最後のコマンドに何の問題がありますか?私の考えでは、引用符に問題があるようです。一重引用符と二重引用符をさまざまに組み合わせてみましたが、最終的に同じ(またはより悪い)結果を得ました。

答え1

次のコマンドを実行すると、

sudo echo some text > file

リダイレクトは、実行前に通常のユーザーとしてシェルで実行されますsudo

コメントに答えて編集:

シェルはこれを他のコマンドと比較して特定のコマンドと見なすことはsudoできません。sudo

シェルの動作は次のようになります。

/bin/echo some text > file

シェルは、上記のコマンドラインのいずれかを解析するときにリダイレクトを探します。したがって、まずファイルを開き、次にプログラムがfork実行するプロセス、dupファイル記述子、およびstdoutプログラムを開きますexec。その後、リダイレクトが既にある状態で実行するか、/bin/echosudostdout

ユースケースでリダイレクトのために一般ユーザーとしてファイルを開くと失敗します。

次のことを試してください

echo '*/1 * * * *' $BACKUP_USER /home/$BACKUP_USER/bin/$SCRIPT_NAME | sudo tee /etc/cron.d/discourse-backup >/dev/null

この場合、ファイルはコマンドライン引数であり、実行され、次にsudoファイルroot名引数を渡すことによってtee高い権限で実行されます。これにより、teeファイルを書き込むために開くことができます。

2番目の編集:この回答は、sudo他の考えられる問題ではなく、リダイレクトに関連する問題を解決することに焦点を当てています。ユーザーが述べたようにカスコメントでは、変数は個別にまたは完全な文字列として引用する必要があります。

echo "*/1 * * * * $BACKUP_USER /home/$BACKUP_USER/bin/$SCRIPT_NAME"  | sudo ...

質問のユースケースでは、参照は2つの理由でそれほど重要ではないかもしれません。パラメーターはのみ使用され、echo出力は有効なcrontab行でなければなりません。それにもかかわらず、これは変数に複数の「問題のある」文字が存在することを許可しません。しかし、通常、正しい引用を常に推奨します。

このコマンドは長い引用符で囲まれた文字列の一部になるため、引用符をエスケープできます。

ssh -i ./ssh-key user@server "
    ...
    echo \"*/1 * * * * $BACKUP_USER /home/$BACKUP_USER/bin/$SCRIPT_NAME\" | sudo ... "

答え2

@Bodoが説明したように、問題はsshを介して文字列をリモートホストに送信する前に、ローカルシェルがメタ文字[>"を解釈することです。

"tee"コマンドを使用する別の方法は、バックスラッシュ "\"[">"または他のメタシーケンス(たとえば、">>"、"$1"、"*"などのメタ文字)を使用して問題になります。コマンドを単にエスケープするだけです。 ..] だからあなたは次のようなものがあります:

sudo echo '*/1 * * * *' $BACKUP_USER /home/$BACKUP_USER/bin/$SCRIPT_NAME \> /etc/cron.d/discourse-backup

また、echoステートメントの「*」文字が問題を引き起こさない理由は、ローカルで発生する可能性のあるシェル拡張を抑制するためにハード(一重引用符)ペアで囲まれていることを指摘したいと思います。

また、この例では、$BACKUP_USER および $SCRIPT 環境がリモート・システムではなくローカルで定義されることに注意してください。リモートシステムで定義されている環境を使用するには、「$」記号をエスケープする必要があります。

単純な経験則:文字列が引用符なしで、または滑らかな(二重)引用符で囲まれている場合は、メタシーケンスをエスケープする必要があります。二重引用符で囲まれている場合は、通常エスケープする必要はありません。

これはすべて、ターゲットファイルに書き込む権限があると仮定します。それ以外の場合は、@Boboが説明したように "tee"コマンドを使用するか、権限のある場所にファイルを作成してその場所に移動する("mv")ことです。

関連情報