目次
sshに関して
技術メモなどに色々バラバラに書いていたssh関係を一度まとめる。 詳細は色々な本などにあるだろうし、過去記事のまとめ程度に抑えておく。
ssh とは
sshは、Secure Shell から名前をとった、リモート端末との通信を行うためのプロトコル、コマンドのことをいう。
- 大本に、rsh(remote shell)がある。rshは、Remote Shellから名前をとったもの
- 他に、telnetやrloginなどもある。何れにしても暗号化などの保護はない。なお、telnetにSSL/TLSを組み合わせたtelnetsもあるが、ほぼ利用されていない。
- rshには、通信保護の機構はなく、通信内容はRPCを利用し、生データが流れていた
- sshでは、RPCを利用することをやめ、ssh daemonとssh client間で暗号化された通信路を用いてデータをやりとりする
sshはRFCにより仕様が規定および公開されている。(20190925時点で調べたもの。今ではもっとあるんだろうけど、面倒だから調べてない)
- RFC 4250 : The Secure Shell (SSH) Protocol Assigned Numbers
- RFC 4251 : The Secure Shell (SSH) Protocol Architecture
- RFC 4252 : The Secure Shell (SSH) Authentication Protocol
- RFC 4253 : The Secure Shell (SSH) Transport Layer Protocol
- RFC 4254 : The Secure Shell (SSH) Connection Protocol
- RFC 4255 : Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints
- RFC 4256 : Generic Message Exchange Authentication for the Secure Shell Protocol (SSH)
機能関係
以下、CEoR改を実装するために必要となりそうなsshの機能のみに限定してのメモ
対象とする実装は、 OpenSSH とする。これは、CEoRで管理をしたい対象がFreeBSD/OpenBSD/Linux系のOS環境であり、これらには標準的にOpenSSHが採用されているからである。
Option | Config Param name | default | descriptions |
---|---|---|---|
-A/-a | 認証Agentの転送を許可/禁止 | ||
-D | Debug logを引数で指定したファイルに書き出す(-v/-vv/-vvv出力) | ||
-e | EscapeChar | ~ | 文字 /^文字 指定文字/制御文字をエスケープ文字にする |
none: エスケープ文字を禁止する。(Binary Dataに対して透過になる) | |||
-F | ~/.ssh/config | 設定ファイルを指定する。 システムdefaultは無視される | |
-f | sshの認証後、BackGroundに移行する | ||
-J | 引数で指定されたマシンを踏み台にして対象ホストに接続する | ||
対象ホストの名前解決ができなければならない | |||
-L | LocalForward | Local forward | |
-l | User | login名 | |
-N | リモートコマンドを実行しない (-Mと併せて使う) | ||
-n | stdinを/dev/nullに切り替える。Passphraseなどを入力する必要がある場合 -f を使う |
||
-O | -Mで作ったMasterへの制御コマンドを送る。 check: Masterがあるかのチェック | ||
forward: port forwardを要求 / cancel: port forwardをキャンセル | |||
exit: Master Processを終了する / stop: これ以上の分岐を禁止する | |||
-o | 設定ファイルと同じ形式でのオプション指定 | ||
-M | ControlMaster | no | 単一のネットワーク接続において、複数セッションを共有するか |
-Q | 問い合わせ。cipher(対象暗号の種類)/cipher-auth(認証付き暗号化をサポートする対象暗号の種類) | ||
mac(メッセージ認証コードの種類)/kex(鍵交換アルゴリズム)/key-cert(証明書の鍵の形式) | |||
-q | quiet mode | ||
-R | RemoteForward | リモーロフォワード | |
-S | ControlPath | Master modeにおける制御用Socketファイルの指定 | |
-T/-t | RequestTTY | セッションに対してTTYを要求するか? no:要求しない/yes:要求する(-t)/forcce:強制的に割り当て(-tt) | |
-X/-x | ForwardX11 | no | X11接続を転送するか yes:許可/no:禁止 |
-y | logをsysぉg経由で飛ばす。defaultではstderrに出力される | ||
BatchMode | no | Password/Passphrase入力を求めるか? | |
CheckHostIP | yes | knwnhostsファイルのエントリーとの比較を行うか? | |
ConnectionAttmpts | 1 | 接続試行回数 | |
ConnectTimeout | TCP Connectionが成立するまでのTimeout時間の設定 | ||
ControlPath | Master modeにおける制御用Socketファイルの指定 | ||
ControlPersist | no: MasterはForegroundのまま、最初のClientが終了した直後に終了 | ||
yes / 0 : MasterはBackgroundに移行し、永続的に保持される | |||
数値(秒) / 時間形式 : idleな時間が指定値を越えると接続が終了 | |||
OpenSSH 5.6以降でのみ利用可能 | |||
ForwardAgent | no | 認証エージェントへの接続を転送するか | |
IdentityFile | 利用する秘密鍵 | ||
force: 常に要求 /auto: loginセッションの時だけ要求する |
ControlMaster
- Masterになる場合、
-M
もしくは-o ControlMaster=yes
を指定し、ControlPath=[SocketFilename]
を指定する。 - Masterの接続を流用する場合、
-o ControlMaster=no
を指定し、ControlPath=[SocketFilename]
を指定する。- ControlPathに指定されたSocket Fileがない場合には、通常の接続が行われる
-o ControlMaster=auto
を指定すると、「Master接続が利用できる場合にはそれを利用、利用できない場合には自動的に新しい接続を作成」する- ControlPathの引数にはEscape Sequenceが利用できる
%L
: Localのホスト名の最初の部分%l
: localのホスト名(hostnameコマンドの返り値)%h
: remoteのホスト名%n
: コマンドラインで指定された(remote)ホスト名%p
: remoteのport番号%r
: remoteのログイン名%u
: sshを実行したユーザーのログイン名%i
: sshを実行したユーザーのUID%C
:%l%h%p%r
ControlMasterに関するCommand line
ssh -M 127.0.0.1
- Masterとして動作し、RemoteのTTYを掴む。
- 制御用socketは(FreeBSDの場合)
/tmp
に置かれる - 制御用socketのファイル名を取得する方法がない
ssh -M -S [SocketFileName] 127.0.0.1
- Masterとして動作し、RemoteのTTYを掴む。
- 制御用socketは
-S
の引数に記載された[SocketFileName]
になる
ssh -N -f -M -S [SocketFileName] 127.0.0.1
- Masterとして動作し、Socketを作成した後sshはBackgroundに
- 制御用socketは
-S
の引数に記載された[SocketFileName]
になる
ssh -N -f -O exit -S [SocketFileName] 127.0.0.1
[SocketFileName]]
に紐づいたssh接続が終了する
ssh -N -f -M -S [SocketFileName] 127.0.0.1
を2回起動するControlMaster=auto
相当の挙動をする
shell scriptとssh
shell scriptにおいて、ファイルから1行ずつ読み込んで処理をするような場合、以下のようにかける。
while read line; do echo ${line} done < file
ところが、この手を使ってsshを実行すると、なぜか最初の行しか実行されない
while read line; do ssh xxx.xxx.xxx.xxx ${line} # XXX 期待通りに動かない done < file
原因はsshコマンド実行に伴う標準入力の切替と考えられる。 sshコマンドを実行すると、ローカルホストのstdinからの入力を終了し、sshで指定したリモートホストのstdinからの入力受付を開始する。 従って、ローカルホストのファイルの読込みを終了させた上でsshコマンドを実行し、再びreadコマンドを実行しようとしていると考えられる。もちろん、この時点で既にファイルがcloseされている為、whileが終了してしまう。
対策は、sshに-n
オプションをつけること。これよって、sshコマンドのstdin切り替えを禁止することが出来る。
この原因がちっともわからず、数時間を無駄にしてしまった。
while read line; do ssh -n xxx.xxx.xxx.xxx ${line} # これで/dev/nullがsshのstdinにつながる done < file
このほかに、for文で for line in `cat file`
で代用する手も考えられるが、これは、行にスペースがある場合、$lineに代入される値が1行まるごとではなく、スペースまでの部分になるので、ここにも明確な罠があると考えられる。これを回避するにはIFSを変えれば良いのだから、
IFS=$'\n' # 区切り文字を" "から"\n"に変える for line in `cat file`; do # while readの代わりにcatで読み込ませる ssh -n xxx.xxx.xxx.xxx ${line} done
あと、whileはちょっと特殊な制御文で、場合によってはwhile内で変数設定しているはずなのにloopをでてくると変数が空のようなことが起こる。 これは、Whileと他のコマンドを組み合わせた場合、組み合わせ方次第で処理がsubshellがわで処理されてしまう事が原因である。 shellでpipelineを用いて実行した処理は、subshellで処理される。while loopの内部でpipelineを利用すると、whileブロック全体がsubshellで処理されるため、whileブロックの内部と外部で変数の共有が出来ない。 このような場合、whileに対して出力をredirectしてやることで解決できる。
while read line; do OUT=`echo ${line} | sed 's/test/test2'` done < $1 echo ${OUT}
なお、/bin/shの引数に -v
や -x
をつけると、デバッグに大変に役立つ
sftpとscp関連
scpには様々な問題があるということで、scpはDeprecatedになっており、sftpを利用したファイル転送を利用すべきである。
というわけで、メモ
http://d.hatena.ne.jp/cakephper/20120918/1347935609 http://f99aq.hateblo.jp/entry/20090802/1249222491
要するに、/etc/ssh/sshd_configを修正し、対象Dirをroot:wheelにすること。
ちなみに、sshd_configに ForceCommand internal-sftp を追加しないと、passwdに記載されているshellを実行しようとするので注意。
sshと二要素認証
参考
要するに、BSDでは、上記patchを当てないと出来ないが、RSA AuthenticationとChallenge Response Authenticationを組み合わせて二要素認証にするという話。
/etc/ssh/sshd_config の変更点だけ書いておく。
ChallengeResponseAuthentication yes RSAAuthentication yes RequiredAuthentications2 publickey,keyboard-interactive
Password Authenticationを使う事は出来ないっぽいけど、まだ試してない。
sshdでPublickeyをauthorized_keys以外から持ってくる
ちょっと某所で sshd でのPublickey Authentication時のPublickeyを~/.ssh/authorized_keys以外から持ってくる設定について聞かれたので、ちょっと調べてみた。
問題
AuthorizedKeysCommand
に公開鍵を引っ張ってくるscriptを指定したんだけど、認証に失敗する- sshd_configに以下の記述
AuthorizedKeysCommand /tmp/pubkey.sh
AuthorizedKeysCommandUser hogehoge
- pubkey.shは以下の通り
- pubkey.sh
#!/bin/bash -e echo "ssh-rsa AAAA........."
- ssh -v の結果
Permission denied (publickey,gssapi-keyex,gssapi-with-mic).
解決
- sshdのlogを見ると、
error: Unsafe AuthorizedKeysCommand “/tmp/pubkey.sh”: bad ownership or modes for file /tmp/pubkey.sh
が/var/log/auth.log
に出力されていた - 色々調べてみると、以下が判明
- sshdは以下の条件のうち一つでも満たすと、上記エラーを吐く
- 他人に書き換えられる可能性のあるDirectoryに設置されている(/tmpとかはやばい)
- Ownerがrootではない
- Owner以外が書き換えることができる(g+wとかo+wだとまずい)
- AuthorizedKeysCommandUser に指定されたUser Accountで実行できない
- というわけで、以下のように修正
- Scriptを
/usr/local/bin/pubkey.sh
に設置 AuthorizedKeysCommand /usr/local/bin/pubkey.sh
としてsshd_configに設定chmod 750 /usr/local/bin/pubkey.sh
を実行chown root:nobody /usr/local/bin/pubkey.sh
を実行- sshdを再起動
結論
- この手法を使えば、LDAPでsshの公開鍵を引っ張ってくることができそうだ
- OpenSSHはSecurity的に相当細かくCheckしているので、ある程度はこの手法を利用しても安全性を確保できそうだ
- 意外と問題の原因が分かりにくいので、追いかけるのが大変
- 俺はエスパーじゃないので、何をどうしたいのか説明してくれ
sshの秘密鍵をOS-XのKeychain Accessに登録
OS-Xでman ssh-add
していたら、-KなるOptionを発見した。
そういえば、むかーしむかしに見たような気もする。
<node> -K When adding identities, each passphrase will also be stored in your
keychain. When removing identities with -d, each passphrase will be removed from your keychain.
</note>
つまり、ssh-add -K
しておけば、その後はいちいちパスフレーズを入力しなくてもよくなる。
OS-Xでsshdの待ち受けポートを変更する
MacOS-Xでリモートアクセスを許可すると、sshで接続できるようになる。
しかし、/etc/sshd_configをいじってもsshの待ち受けポートが変わらないので調べてみたら、/System/Library/LaunchDaemons/ssh.plist
を修正する必要があるということで、以下のように修正
<key>Sockets</key> <dict> <key>Listeners</key> <dict> <key>SockServiceName</key> <string>65535</string> <key>Bonjour</key> <array> <string>ssh</string> <string>sftp-ssh</string> </array> </dict> </dict>
このSockServiceNameの文字列sshを待ち受けたいport番号にかえればOK。なお、この例では65535にしているが、ポート番号は適当にどうぞ。
なお、当然、この変更を行った後で
% sudo launchctl unload /System/Library/LaunchDaemons/ssh.plist % sudo launchctl load /System/Library/LaunchDaemons/ssh.plist
を実行する事。そうしないと、変更が反映されないよ。ま、再起動でもいいけどね。
sshとcron
Backupを取得するに際して、Backupを元々のServerに置いておいたら、Disk破損の時に一気に死ぬ。 で、そんな事態は救われないので、BackupをRemoteに置いておくために、Backup Scriptまで作ったわけだが、Serverの内側でcronなどでBackupを取得するのは設定ミスとか管理の分散とかが起こってうれしくない。 というわけで、remoteからsshを利用してBackupを取得するようにscriptを作ったのだが、
Pseudo-terminal will not be allocated because stdin is not a terminal.
などといいうErrorが出た。で、これはptyの割り付けないremoteからのコマンドは実行できないということでsshが吐き出すErrorである。
しかし、shell scriptには-t
は記載されていて、ちゃんと動くはずだとどはまりをしたわけで…
で、困りに困って調べてみたら、出てました。man ssh
-t Force pseudo-tty allocation. This can be used to execute arbitrary screen-based pro- grams on a remote machine, which can be very useful, e.g. when implementing menu ser- vices. Multiple -t options force tty allocation, even if ssh has no local tty.
よく見たら、Multiple -t options force tty allocation, even if ssh has no local tty.
だそうな。
要するに、-t -t
としろと。
わかりにくいからメモにしておく。
教訓は、RTFM … orz.
古いNetwork機器に対するssh接続
うちの環境にはいまだに古いAlaxalAのL2 SwitchやApresiaのL3 Switchがあるのだが、これが古いssh protocolをしゃべる。 そのため、2023/05時点のmacOS(Ventura/13.3)だとsshで接続しようとした時にsshの暗号化パラメータ(鍵交換や暗号アルゴリズム)が対応していなくて接続できなくなる。
その解消のためには、~/.ssh/configに以下のような設定を入れておくこと。
# for AlaxalA AX2430S Host [IP Address] [alias name] ... HostKeyAlgorithms ssh-dss KexAlgorithms +diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1 # for Apressia Host [IP Address] [alias name] ... KexAlgorithms +diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1