Chienomi

Linux zsh/デュプレクサ/ssh設定の勘所

zsh

ここのところ大作をいくつも書いているため、忙しさも手伝ってなかなかアップロードできない。 この話もLinuxを使えるようセットアップする話をまとめようと思っていたのだが、それはあまりに時間がかかってしまうので、かいつまんで解説しようと思う。

世の中にはLinuxをインストールだけ繰り返すという人もいるのだが、だいたいそういう人はセットアップをしない。

1台だけのコンピュータを使う人、というのも、実はLinux手練(というよりも達人)の中でも結構多いのだが、1台だけ使っているうちにはその中で順次セットアップを煮詰めていけば良いのだが、Linuxは複数台使ってこそその真価を発揮するし、そうなると素早く適切にセットアップすることが求められる。 特にConoHaでインスタンスを立てたり閉じたりする場合にはなかなか重要だ。

ここでは頻繁にセットアップを行うがまだ手順を確立していない人、あるいはセットアップ自体確立していない人のために私のコツをご紹介しよう。

なお、ここでは前提としてManjaro Linuxあるいは(サーバーにおいては)Arch Linuxをセットアップする前提にあり、その中で極力少ない労力でセットアップしようとしている。 基本的な方針は他のディストリビューションでも応用できるはずだが、労力の多寡に関してはこの限りではない。

また、シェルはZshを使っていることを前提としている。 Bashを使っている人はZshに乗り換えてしまえば良いと思うが、Fishを使っている人に関しては私はわからないので留意していただきたい。

初期セットアップに関して

とりあえずアップデートして再起動

# pacman -Syu
# reboot

vimがないと設定に困るのでvimを用意する。 viも別パッケージであるのだが、むしろ不便なのでシンボリックリンクにする。 vim-pluginsが入っていればだいたい良いので、一緒にいれておく。これでvimで苦痛を感じることなく使うことができるようになる。 (デスクトップではvimではなくgvimを入れる。そうするとXセレクションを扱えるようになる)

# pacman -S vim vim-plugins
# (cd /bin; ln -s vim vi)

設定ファイルをViなんかで書けるか! Emacsにしろ! という人はEmacsも入れる。 私はEmacsを使わないのであまり詳しくない。 こちらもデスクトップではemacsパッケージを入れる。

# pacman -S emacs-nox

ここで一般ユーザーを作っておく。もちろん、Manjaroでは必要のない作業。 Manjaroでは一般的なデスクトップユーザーに追加すべきグループが多いので留意する必要がある。

# useradd -m -U -c "First User" -s /bin/zsh -G wheel,storage,sys,network luser
# passwd luser

wheelグループにsudoを許すようにする。 $wheelの行のコメントアウトを外す。NOPASSWDになっている行ではないほうが良いだろう。

# visudo

Zshをログインシェルにしたが、まだ設定していないのでbashで入る。

# sudo -u luser bash -l

AURを扱うのであればとりあえずyayを入れるのがお勧め。 Archでも2パッケージで済むからだ。それにタイプ数も少ない

$ sudo pacman -S go
$ git clone 'https://aur.archlinux.org/yay.git
$ cd yay
$ makepkg
$ sudo pacman -U yay*.pkg.tar.xz

あとはほしければTrizenでも入れておけば良い。 yaourtとpacaurは「安全ではないソフトウェア」になりつつあるらしいので、とりあえずお勧めはしない。

$ yay -S trizen

w3mとlvがあればとりあえず文書を読むのは楽になる。 コンソール作業する場合は必須

$ yay -S w3m lv

ターミナルマルチプレクサを入れる。Powerlevel9kはGNU Screenで位置がバグる問題があるので、ここでtmuxを入れておく。 一応、screenも入れておく。

$ yay -S screen tmux

Moshは便利だと思うけれど、私はUDPを透過するファイアウォール設定をしたくないため、ここではMoshの話はしない。

Zshとターミナルマルチプレクサ

とりあえず

私が新インスタンスに対して最初にすることは、Zshを導入しセットアップすることである。

ZshからFishに変えた、あるいはZshが使えなくてBashがいいという人の多くはZshのセットアップができていない。 ディストリビューションに良いZshの設定が含まれていることは稀だし、Zshの設定は非常に多くて難しい。 マニュアルを読んで設定を作り上げてこそなのだが、それをしない人が圧倒的に多い。 (似たようなことはVimにも言えるが、Vimの場合はそれが気になるケースは少ないかもしれない)

実は話は実に簡単で

$ sudo pacman -S grml-zsh-config zsh-completions

これで再ログインすれば立派に使えるZshが出来上がっている。

grmlの設定は非常に練られていて、多くの場合これで十分だろう。 (場合によっては調整がいるかもしれない)

なお、Archのgrml-zsh-configはskelが含まれているのだが、Manjaroは含まれていない。 skelのほうは便利ツールがコメントアウトされているものなので、別になくても構わない。

なお、Zshの設定ファイルに日本語を使うとサーバーではトラブルのもとになるので注意してほしい。

オプション

だが、もう少し練ることにしよう。 まず、grmlの設定ではAUTO_CONTINUEが有効ではないので、間違ってフォアグラウンドで起動してdisownしたあとSIGCONTをわざわざ送る必要がある(実際はこのケースではbgしてからdisownするほうが良い)。

setopt AUTO_CONTINUE

重複する部分が多いが、重要なオプションは設定しておこう。

setopt NO_GLOB_DOTS
setopt NO_NUMERIC_GLOB_SORT
setopt HUP
setopt PRINT_EIGHT_BIT
setopt PROMPT_CR
setopt SHARE_HISTORY
setopt APPEND_HISTORY
setopt EXTEND_HISTORY
setopt HIST_ALLOW_CLOBBER
setopt NO_CLOBBER

# 以下はZshの機能に慣れている人向け。そうでない人はNO_をつけるか、unsetopt
setopt EXTENDED_GLOB
setopt EQUALS
setopt BRACE_CCL
setopt BARE_GLOB_QUAL

# 次はセットするのか、アンセットするのか好みが分かれるので試してから
setopt AUTO_PARAM_SLASH
setopt AUTO_REMOVE_SLASH

私は次も設定しているが、多分いらない。(RC_EXPAND_PARAMはgrmlではオフかも)

setopt RC_EXPAND_PARAM
setopt NOTIFY
setopt MENU_COMPLETE
setopt LIST_TYPES
setopt REC_EXACT

シンタックスハイライト

プラグインの中では便利なもの。 ただし、いくつかのオプションが制限される。

% sudo pacman -S zsh-syntax-highlighting
if [[ -f /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh ]]
then
    source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
fi

履歴の機能を拡張

grmlの履歴機能はhistory-beginning-{for,back}ward-endを採用している。 これは、「コマンド部分は途中ならその位置、コマンド部分が入力されていればその部分を維持し、カーソル位置を末尾としてヒストリをたどる」というものだ。

だが、個人的にはオプションも含めてカーソル位置まで維持してくれるほうが好きだ。 全面的に書き換えるのではなく、PgUp/PgDown時はカーソル位置を維持してヒストリをたどるようにする。

本来は記述が足りていないが、この内容はgrmlを前提としている。

autoload -U history-search
bindkey "\e[5~" history-beginning-search-backward
bindkey "\e[6~" history-beginning-search-forward

履歴を残す量も設定しておこう。 HISTSIZEは検索で辿れる量、SAVEHISTはファイルに残す量だ。

HISTFILE=$HOME/.zhistory
HISTSIZE=10000
SAVEHIST=5000000

viモード

私はviモード使いなので、設定しておく。

setopt vi
KEYTIMEOUT=1

bindkey -M viins "\e[5~" history-beginning-search-backward
bindkey -M viins "\e[6~" history-beginning-search-forward
bindkey -M vicmd "\e[5~" history-beginning-search-backward
bindkey -M vicmd "\e[6~" history-beginning-search-forward

なお、逆にEmacsキーバインドで使いたい人で、EDITORvivimnvimにしている人はちゃんと設定する必要がある。

setopt emacs

ターミナルマルチプレクサ

SSHの場合は切れないとか、セッション増やしたいとかだいたい起きるので、使用させることにする。

if [[ ( "$TERM" != "dumb" ) && ( "$TERM" == xterm* ) && -n "$SSH_TTY" ]]
then
  _DEFAULT_SHELLMODE=9k tmux
  #_DEFAULT_SHELLMODE=9k screen -xR
  if [[ "$TERM" != screen* ]]
  then
    exit
  fi
fi

なお、これでtmuxの設定で

set -g default-terminal "xterm-256color"

とかすると地獄をみるので絶対にしてはいけない。 必ず

set -g default-terminal "screen.xterm-256color"

または

set -g default-terminal "screen-256color"

とすること。

なお、私はGNU Screen使いなので、tmuxのキーバインドはscreen互換にしてある。

プロンプトとテーマモード

私は普段はgrmlプロンプトテーマを使っているが、SSHではPowerlevel9kを使っている。 これは、見やすく、わかりやすいため。普段から使わないのはプロンプトが戻るのがちょっと遅いからだ。

% sudo pacman -S zsh-theme-powerlevel9k awesome-terminal-fonts powerline-fonts

私はテーマ読み込みにこんなことをしている。

prm() {
  if [[ -e "$HOME/.zshextend.d/prompt/$1" ]]
  then
    source "$HOME/.zshextend.d/prompt/$1"
  else
    print -P "%F{red}NO PROMPT PROFILE $1%f"
  fi
}

if [[ -n $_DEFAULT_SHELLMODE ]]
then
  prm $_DEFAULT_SHELLMODE
fi

前述の$_DEFAULT_SHELLMODEはこのためのものだ。 だが、常にPowerlevel9kで良いのなら、別にロード部分を直接書いてもいい。

prompt off
POWERLEVEL9K_MODE=awesome-fontconfig
source /usr/share/zsh-theme-powerlevel9k/powerlevel9k.zsh-theme

grmlと競合してしまうので、prompt offすること。

私の設定はこんな感じ。

zle-keymap-select() {
  zle reset-prompt
  zle -R
}
zle -N zle-keymap-select
POWERLEVEL9K_SHORTEN_STRATEGY=truncate_from_right
POWERLEVEL9K_SHORTEN_DIR_LENGTH=5
POWERLEVEL9K_SHORTEN_DELIMITER=".."

# Colorize
POWERLEVEL9K_OS_ICON_BACKGROUND="white"
POWERLEVEL9K_OS_ICON_FOREGROUND="blue"
POWERLEVEL9K_USER_DEFAULT_BACKGROUND=white
POWERLEVEL9K_USER_DEFAULT_FOREGROUND=111
POWERLEVEL9K_USER_ROOT_BACKGROUND=red
POWERLEVEL9K_USER_ROOT_FOREGROUND=black
POWERLEVEL9K_USER_SUDO_BACKGROUND=051
POWERLEVEL9K_USER_SUDO_FOREGROUND=052
POWERLEVEL9K_USER_ICON="\uF415"
POWERLEVEL9K_ROOT_ICON="#"
POWERLEVEL9K_SUDO_ICON="\uF09C"
POWERLEVEL9K_VI_MODE_NORMAL_FOREGROUND=051
POWERLEVEL9K_VI_MODE_INSERT_FOREGROUND=168
POWERLEVEL9K_VCS_CLEAN_BACKGROUND=147
POWERLEVEL9K_VCS_UNTRACKED_BACKGROUND=174
POWERLEVEL9K_VCS_MODIFIED_BACKGROUND=139
POWERLEVEL9K_VCS_CLEAN_FOREGROUND=black
POWERLEVEL9K_VCS_UNTRACKED_FOREGROUND=black
POWERLEVEL9K_VCS_MODIFIED_FOREGROUND=black
POWERLEVEL9K_HOST_REMOTE_FOREGROUND=069
POWERLEVEL9K_HOST_REMOTE_BACKGROUND=153

POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(os_icon vi_mode battery user host dir newline)
POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=(status background_jobs command_execution_time vcs)

このあたりは好みなのだが、実はPowerlevel9kの公式マニュアルにはあるが動かないものというのが結構合ったりする。 典型的には$POWERLEVEL9K_SHORTEN_STRATEGYは多くがうまく動かない。

ポイントになるのが、プロンプトにcontextを使わずuser hostと分けた上でホストのREMOTEのみ色を設定していること。

基本的には「ローカルの場合はどのマシンでも同じ色でいいが、リモートの場合はどのマシンか判別できたほうがよい」と思う。 別にこれ以外のプロンプトテーマでもホスト名は出しているのだが、現在作業中のホストを勘違いしてやっちまった、ということはしょっちゅうある。

まずホスト名に一貫性があってわかりやすい名前をつけることが大切だ。

% sudo hostnamectl set-hostname thinkpad-x1

私の場合は花の名前をつけることにしている。 実はこれは1993年の出来事に由来しており、運用は1998年から、と私としてはとても歴史がある。

さらにメリットとして花なので、色が連想できる。私は$POWERLEVEL9K_HOST_REMOTE_{FORE,BACK}GROUNDはその花の代表的な色をモチーフにした色使いにしている。 それぞれのマシンでイメージが離れた花の名前をつけているため、色被りも少ない。

簡単でわかりやすいのは、機種名とケースの色だろうか。VPSは難しい。

なお、

zle-keymap-select() {
  zle reset-prompt
  zle -R
}
zle -N zle-keymap-select

vi_modeを正しく表示するために必要な部分。

SSH

基本的なSSH

とりあえずroot鍵を登録してパスワード認証は閉じる。

まずはログインするほうでssh-keygen -f <file>によって生成した.pubのほうのファイルを ログインされるほうの~root/.ssh/authorized_keysにコピーする。

なお、.sshはパーミッション0600であること。

このroot鍵を登録するステップはサーバーに対するもので、直接ログインできるのであればスキップすべき作業である。

そして/etc/ssh/sshd_config (ssh_configではない!)を

PasswordAuthentication no

としておく。 この時点で一旦リロード

% sudo systemctl reload sshd

同じ要領でユーザー鍵を登録する。 鍵そのものも分けたほうがいい。

% ssh-keygen -f server-luser_rsa
% rsync -e "ssh -i server-root_rsa" server-luser_rsa.pub root@server:/home/luser/.ssh/authorized_keys
% ssh -i server-root_rsa root@server "chown luser:luser -R /home/luser

以降のために~/.ssh/configに設定しておく。

Host server-luser
  User luser
  Port 22
  HostName server.example.org
  IdentityFile ~/.ssh/server-luser_rsa

これでssh server-luserとして入れるようになる。

なお、HostNameだが、LANで同じセグメント内にいるのであればZeroconfによる.localを使えばいいだろう。 あるいは、各マシンを固定アドレスとして/etc/hostsに書いておくというのも手。 /etc/hosts上の名前は1マシン1つではなく、役割ごとに名前をつけておくと、その役割が他のマシンに移ったときにあまり苦労しなくて済む。dnsmasqで配るという方法もある。

SSHに関する話は以前にしたので、応用技としてはそのあたりを参考にしてくれると良い。

いざというときのための暗号化経路

なにかのときのため、ネットワークごとに1台は透過的にアクセスできると良いだろう。 これはいくつかの方法がある。

最も簡単なのは、Socksプロキシを使うことだ。

% ssh -TND 4711 -o ServerAliveInterval 10 -o ServerAliveCountMax 3 luser@server

これでlocalhost:4711をSOCKS5プロキシとして使用すればSSHサーバーを経由してアクセスすることが可能になる。 これは、ウェブブラウザやメールクライアントで有用である。これは公衆Wi-Fiからアクセスする程度の場合に有効だ。

もうひとつは、SSH経由の環境から利用できるようにしておくことだ。 w3mやMutt, Vim, Emacs, lvなど端末から利用できるアプリケーションを一通り揃えておけば、GUIは使えなくてもひととおりの作業が可能だろう。

本当にそのネットワークを通じてアクセスする必要が生じた場合はどうだろうか? 私はそんなサバイバルな経験を何度かしているが、普通の人はあまりない。 常時必要とするのであればVPNを用意しておけば良いのたでが、緊急避難的に使用できるようにしておくと何かと便利だ。

まずは双方にpppをインストールしておく。

% sudo pacman -S ppp

ログインするサーバーに対してはパスワードをかけたコマンド用rootキーを使うのが最も確実で安全。

command="/usr/sbin/pppd nodetach notty noauth",pty,no-X11-forwarding,no-port-forwarding,no-agent-forwarding ssh-rsa ...

さらに実際に使うときにログインした上でフォワーディングを許す。

# echo 1 > /proc/sys/net/ipv4/ip_forward
# iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

そしてつなぐ。

% sudo pppd updetach noauth silent nodeflate pty "sudo -u luser ssh server-ssh-ppp" ipparam vpn 192.168.32.1:192.168.32.2

192.168.64.0/24に対してアクセスできるようにしたい場合:

% sudo ip route add 192.168.64.0/24 via 192.168.32.1

すべての未知のホストにこの経路でアクセスしたい場合。サーバーのアドレスは10.0.8.1、現在のデフォルトゲートウェイは192.168.1.1だとすると:

% sudo ip route add 10.0.8.1 via 192.168.1.1
% sudo ip route replace default via 192.168.32.1 proto static metric 101