advanced-ssh-configを使って.ssh/configをとてもスッキリさせる方法

[advanced-ssh-config](https://github.com/moul/advanced-ssh-config)はYAMLで.ssh/configの設定を書いてごちゃごちゃになりがちな設定をキレイに書くことができるGo言語で書かれたツールです🖥

![carbon.png](https://qiita-image-store.s3.amazonaws.com/0/7565/0ab02a77-a454-f3cd-ea2d-a52efe6cc229.png)


## 主機能

* 正規表現
* gateways: 透過的なSSH接続チェイン
* includes: ファイルのインクルード
* local command execution: `RemoteCommand`の逆
* templates: ホストと同じですが、テンプレートホストには直接接続することはできない。継承に最適。
* inheritance: ホストをホストホストまたはテンプレートから継承させる
* variable expansion: 環境変数を展開できる
* smart proxycommand: 素のtcp接続（可能であれば、デフォルトのフォールバックとしてnetcatとsocatを使用する）
* rate limit: ホスト単位またはグローバルなレート制限
* JSON output: JSONでの出力
* Graphviz: ホスト情報をgraphvizで出力する

## インストール

### Goでのインストール

```
go get -u github.com/moul/advanced-ssh-config/cmd/assh
```

### macOS環境

```
brew install assh
```

### その他の方法

https://github.com/moul/advanced-ssh-config/releases からバイナリを取得

### sshコマンドをaliasでasshを使うようにする

必須ではないが自動的に`.ssh/config`を生成できるようにするために必要。

```
alias ssh="assh wrapper ssh"
```

注意：sshは高度なパターンを理解していません。この制限を回避するため、asshは既知のホストのリストを保持し、`.ssh/config`をそれらの拡張された既知のホストすべてで再生成します。

wrapperがなければ、`.ssh/config`は初めて新しいホストに接続するときに古くなる危険性があり、コマンドを再度起動する必要があります。wrapperでは、sshは更新された`.ssh/config`ファイルを常に使います。

## 設定例

`~/.ssh/assh.yaml` を新規に作成してそこに設定を書きます。

```yaml:~/.ssh/assh.yaml
hosts:
  jump.k8s.aws:
    HostName: bastion.ap-northeast-1.elb.amazonaws.com
    User: admin
    IdentityFile: ~/.ssh/jump_k8s.pem

  node01.k8s:
    Hostname: 172.10.10.10
    inherits: node.k8s

  node02.k8s:
    Hostname: 172.10.10.11
    inherits: node.k8s

  node03.k8s:
    Hostname: 172.10.10.12
    inherits: node.k8s

templates:
  node.k8s:
    User: admin
    Gateways: jump.k8s.aws
```

## .ssh/config を生成する

*このステップで、 `.ssh/config` が上書きされます。必ず既存の `.ssh/config` はバックアップしましょう!*

```
assh config build > ~/.ssh/config
```

## 複雑な設定の例

```yaml
hosts:

  homer:
    # ssh homer ->  ssh 1.2.3.4 -p 2222 -u robert
    Hostname: 1.2.3.4
    User: robert
    Port: 2222

  bart:
    # ssh bart ->   ssh 5.6.7.8 -u bart           <- direct access
    #            or ssh 5.6.7.8/homer -u bart     <- using homer as a gateway
    Hostname: 5.6.7.8
    User: bart
    Gateways:
    - direct                   # tries a direct access first
    - homer                    # fallback on homer gateway

  maggie:
    # ssh maggie ->   ssh 5.6.7.8 -u maggie       <- direct access
    #              or ssh 5.6.7.8/homer -u maggie   <- using homer as a gateway
    User: maggie
    Inherits: bart             # inherits rules from "bart"

  bart-access:
    # ssh bart-access ->  ssh home.simpson.springfield.us -u bart
    Inherits:
    - bart-template
    - simpson-template

  lisa-access:
    # ssh lisa-access ->  ssh home.simpson.springfield.us -u lisa
    Inherits:
    - lisa-template
    - simpson-template

  marvin:
    # ssh marvin    -> ssh marvin    -p 23
    # ssh sad-robot -> ssh sad-robot -p 23
    # ssh bighead   -> ssh bighead   -p 23
    # aliases inherit everything from marvin, except hostname
    Port: 23
    Aliases:
    - sad-robot
    - bighead

  dolphin:
    # ssh dolphin   -> ssh dolphin -p 24
    # ssh ecco      -> ssh dolphin -p 24
    # same as above, but with fixed hostname
    Port: 24
    Hostname: dolphin
    Aliases: ecco
    RateLimit: 10M # 10Mbytes/second rate limiting

  schooltemplate:
    User: student
    IdentityFile: ~/.ssh/school-rsa
    ForwardX11: yes

  schoolgw:
    # ssh school ->   ssh gw.school.com -l student -o ForwardX11=no -i ~/.ssh/school-rsa
    Hostname: gw.school.com
    ForwardX11: no
    Inherits: schooltemplate

  "expanded-host[0-7]*":
    # ssh somehost2042 ->       ssh somehost2042.some.zone
    Hostname: "%h.some.zone"

  vm-*.school.com:
    # ssh vm-42.school.com ->   ssh vm-42.school.com/gw.school.com -l student -o ForwardX11=yes -i ~/.ssh/school-rsa
    Gateways: schoolgw
    Inherits: schooltemplate
    # do not automatically create `ControlPath` -> may result in error
    ControlMasterMkdir: true

  "*.shortcut1":
    ResolveCommand: /bin/sh -c "echo %h | sed s/.shortcut1/.my-long-domain-name.com/"

  "*.shortcut2":
    ResolveCommand: /bin/sh -c "echo $(echo %h | sed s/.shortcut2//).my-other-long-domain-name.com"

  "*.scw":
    # ssh toto.scw -> 1. dynamically resolves the IP address
    #                 2. ssh {resolved ip address} -u root -p 22 -o UserKnownHostsFile=null -o StrictHostKeyChecking=no
    # requires github.com/scaleway/scaleway-cli
    ResolveCommand: /bin/sh -c "scw inspect -f {{.PublicAddress.IP}} server:$(echo %h | sed s/.scw//)"
    User: root
    Port: 22
    UserKnownHostsFile: /dev/null
    StrictHostKeyChecking: no

  my-env-host:
    User: user-$USER
    Hostname: ${HOSTNAME}${HOSTNAME_SUFFIX}

templates:
  # Templates are similar to Hosts; you can inherit from them
  # but you cannot ssh to a template
  bart-template:
    User: bart
  lisa-template:
    User: lisa
  simpson-template:
    Host: home.simpson.springfield.us

defaults:
  # Defaults are applied to each hosts
  ControlMaster: auto
  ControlPath: ~/tmp/.ssh/cm/%h-%p-%r.sock
  ControlPersist: yes
  Port: 22
  User: bob
  Hooks:
    # Automatically backup ~/.ssh/config
    BeforeConfigWrite:
      - 'exec set -x; cp {{.SSHConfigPath}} {{.SSHConfigPath}}.bkp'

    AfterConfigWrite:
      # Concat another `ssh_config` file with the one just generated by `assh`
      - 'exec cat ~/.ssh/my-heroku-generated-config >> {{.SSHConfigPath}}'

      # Alert me with a Desktop notification
      - notify "{{.SSHConfigPath}} has been rewritten"

    OnConnect:
      # Log internal information to a file
      - 'exec echo {{.}} | jq . >> ~/.ssh/last_connected_host.txt'

      # Alert me with a Desktop notification
      - notify New SSH connection to {{.Host.Prototype}} at {{.Stats.ConnectedAt}}

      # Write the host prototype to the terminal stderr
      - write New SSH connection to {{.Host.Prototype}}

    OnDisconnect:
      # write on terminal and in a Desktop notification some statistics about the finished connection
      - "write  SSH connection to {{.Host.HostName}} closed, {{.Stats.WrittenBytes }} bytes written in {{.Stats.ConnectionDuration}} ({{.Stats.AverageSpeed}}bps)"
      - "notify SSH connection to {{.Host.HostName}} closed, {{.Stats.WrittenBytes }} bytes written in {{.Stats.ConnectionDuration}} ({{.Stats.AverageSpeed}}bps)"

includes:
- ~/.ssh/assh.d/*.yml
- /etc/assh.yml
- $ENV_VAR/blah-blah-*/*.yml

ASSHBinaryPath: ~/bin/assh  # optionally set the path of assh
```


## asshコマンドのオプション

```
NAME:
   assh - advanced ssh config

USAGE:
   assh [global options] command [command options] [arguments...]

VERSION:
2.7.0 (HEAD)

AUTHOR(S):
   Manfred Touron <https://github.com/moul/advanced-ssh-config>

COMMANDS:
   ping          Send packets to the SSH server and display statistics
   info          Display system-wide information
   config        Manage ssh and assh configuration
   sockets       Manage control sockets
   help, h       Shows a list of commands or help for one command

GLOBAL OPTIONS:
  --config value, -c value       Location of config file (default: "~/.ssh/assh.yml") [$ASSH_CONFIG]
  --debug, -D                    Enable debug mode [$ASSH_DEBUG]
  --verbose, -V                  Enable verbose mode
  --help, -h                     show help
  --version, -v                  print the version
```

## 詳細なドキュメント

[GitHub moul/advanced-ssh-config](https://github.com/moul/advanced-ssh-config)