Backdoor Server経由でLocal Client PCとRemote Serverとを繋ぐための手引き


IPの認証の壁を超えるためのbackdoorを構築して、そのサーバー経由で色々とやりとりしたいので、その定石設定とコマンド解説。

# やりたいことのイメージ
```text
# 構成イメージ
Local PC ← | ssh / when running | →　Backdoor Svr ← | ssh/IP Filter | → Remote Svr

# backdoorの起動制御イメージ
python backdoor.py (start|stop)
```

# 知識・スキル要件
* viなどのCUI上でのテキストエディティング
* AWSのEC2の構築とAPI経由での操作
* CUIリテラシー
* Python仮想環境構築とコーディング

# 環境
* Local Client PC: MacOS
* Backdoor Sever: Ubuntu
* Cloud: EC2@aws

# Backdoor Serverセットアップ
`Backdoor Server`(踏み台サーバー)は、使う時に立ち上げ、不要な時に止めるスクリプトを準備する。まずは、EC2を立ち上げElastic IPを割り当てる。

## EC2セットアップ手順
1. AWSのWEB ConsoleへログインしEC2メニューへ
1. OSはUbuntuを選びInstanceを立ち上げ**pem**を取得し`BACKDOORSVR.pem`で保存
1. 立ち上げたEC2のタグ`Name`に`BACKDOORSVR`など**ユニーク値**を設定
1. セキュリティグループのInboudにSSH(Port 22)を割り当てる
1. Elastic IPを取得し立ち上げたEC2に割り当て、固定のIPを取得
1. 念のため`AMI`を作成しておく

### pemを保存
```bash
$ mkdir ~/.pem
$ cd pemの保存先/
$ mv BACDOORSVR.pem ~/.pem/BACKDOORSVR.pem
$ chmod 0600 ~/.pem/BACKDOORSVR.pem

```

## AWS APIへの接続

### 作業環境準備
* [Pythonの仮想環境の構築](https://qiita.com/kiyotaman/items/93689429a2c7652ed601)を準備し`boto3`をインストールする
```bash
kiyota@local:~ $ source ./venv/bin/activate
(venv) kiyota@local:~ $ pip install boto3
```

### AWS APIの認証設定
* EC2が操作できる`aws_access_key_id`と`aws_secret_access_key`を取得し`~/.aws/credentials`へ追加
* **[BACKDOORSVR]** は、EC2のインスタンスのタグ名などにすると分かりやすい
* **region** は、利用中のEC2のAWSのリージョンを指定

```bash
cat <<EOF | tee -a ~/.aws/credentials
[BACKDOORSVR]
region = ap-northeast-1
aws_access_key_id = ********************
aws_secret_access_key = ********************
EOF
```

### EC2インスタンスのStart/Stopスクリプト

**使い方**
```
python backdoor.py (start|stop)
```
**backdoor.pyスクリプト**
```backdoor.py
import boto3
import sys


PROFILE_NAME = "BACKDOORSVR"  # APIの認証設定のプロファイル名を指定
TAG_VALUE = "BACKDOORSVR"  # タグNameの設定値を指定

def manage_ec2_instances_by_tag(operation):
    session = boto3.Session(profile_name=PROFILE_NAME)
    ec2 = session.client('ec2')

    filters = [{
        'Name': 'tag:Name',
        'Values': [TAG_VALUE]
    }]

    instances = ec2.describe_instances(Filters=filters)
    instance_ids = [instance['InstanceId'] for reservation in instances['Reservations'] for instance in reservation['Instances']]

    if not instance_ids:
        print("No instances found.")
        return

    if operation == 'stop':
        ec2.stop_instances(InstanceIds=instance_ids)

    elif operation == 'start':
        ec2.start_instances(InstanceIds=instance_ids)

    else:
        print("Invalid operation. Use 'stop' or 'start'")
        return


if __name__ == '__main__':
    if len(sys.argv) != 2:
        print("Usage: python backdoor.py <operation>")
    else:
        manage_ec2_instances_by_tag(sys.argv[1])
```

# セキュリティ設定
## プライベートキーをssh-addで登録
:::note warn
**必ず行う**
```
$ ssh-add ~/.ssh/BACKDOORSVR.pem
$ ssh-add -l  # 登録されているキーを確認
2048 SHA256:************************* ~/.pem/BACKDOORSVR.pem (RSA)
```
:::
* 公開鍵を準備する
## 公開キーをサーバーへの登録
:::note warn
**必ず行う**
### Backdoor Serverに鍵を転送
```
# Backdoor Server の ~/.ssh/authorized_keys に登録される
ssh-copy-id -i ~/.ssh/id_rsa.pub ubuntu@ec2-PUBLIC-IP.ap-northeast-1.compute.amazonaws.com
```
### Remove Serverに鍵を登録
```
# 公開書きをコピーして
(venv) kiyota@local:~ $ cat ~/.ssh/id_rsa.pub |pbcopy
(venv) kiyota@local:~ $ ssh ubuntu@ec2-PUBLIC-IP.ap-northeast-1.compute.amazonaws.com
kiyota@dev:~ $ ssh ユーザー名@リモートサーバー
kiyota@リモートサーバー: ~ $  # pbcopyした鍵を "~/.ssh/authorized_keys2" 追加
:::


# 各種シェルサンプル

## ダウンロード
リモートサーバーからファイルを `bash download.sh filepath/file.txt` でダウンロード

```download.sh
#!/bin/bash

PUBLIC_IP=000-000-000-000  # AWS EC2を参照
REMOTE_BASEDIR=/REMOTE_SERVER/HOME/DIR
FILEPATH=$1
DIRPATH=`dirname ${FILEPATH}`

# ディレクトリなければ作成
if [ ! -d ${DIRPATH} ]; then
  mkdir -p ${DIRPATH}
fi

echo "FILEPATH: ${FILEPATH}"
scp -o ProxyJump=ubuntu@ec2-${PUBLIC_IP}.ap-northeast-1.compute.amazonaws.com リモートユーザー@リモートサーバー:${REMOTE_BASEDIR}/${FILEPATH} ${FILEPATH}
```

## 転送（アップロード）
Local ClientからRemote Serverへ`bash upload.sh filepath/file.txt` でファイルの転送
```upload.sh
#!/bin/bash

PUBLIC_IP=000-000-000-000  # AWS EC2を参照
REMOTE_BASEDIR=/home/ark-dev/ark-site
FILEPATH=$1
echo "FILEPATH: ${FILEPATH}"
scp -o ProxyJump=ubuntu@ec2-${PUBLIC_IP}.ap-northeast-1.compute.amazonaws.com ${FILEPATH} リモートユーザー@リモートサーバー:${REMOTE_BASEDIR}/${FILEPATH}
```

## リモートサーバーのファイル一覧を取得
リモートコマンド `bash remote_cmd.sh` を使ってファイル一覧を取得

```remote_cmd.sh
#!/bin/bash

PUBLIC_IP=000-000-000-000  # AWS EC2を参照
REMOTE_BASEDIR=/home/ark-dev/ark-site
DIRPATH=$1
echo "DIRPATH: ${DIRPATH}"
ssh -o ProxyJump=ubuntu@ec2-${PUBLIC_IP}.ap-northeast-1.compute.amazonaws.com ark-リモートユーザー@リモートサーバー "ls -al ${REMOTE_BASEDIR}/${DIRPATH}"
```

# vimとの連動
Local Client PC上で、Vimを使って特定の作業ディレクトリ内でファイルを編集し保存した時、そのファイルをRemove Serverへ転送する設定

```vimscript
autocmd BufWritePost /作業ディレクトリ/* let fullpath = expand("%:p") | let relpath = substitute(fullpath, "/作業ディレクトリ/", "", "") | execute '!echo "File changed. Running scp." && /アップロードスクリプトがあるディレクトリ/upload.sh '.relpath.' && echo "Upload complete"'
```

# まとめ
個別にスクリプトを準備しエディターと連動させるなどすると結構色々とできるから便利。vscodeとか他にも優秀なエディターがあるが、サーバーサイドでCUIコンソール上でのエンジニアリングが必要なことが多い場合、やはりCUI作業とコーディングを並行してできると便利。レガシーなスタイルかもしれないが、個人的にはこの方法は結構好き。