designetwork

ネットワークを軸としたIT技術メモ

Cisco ASAにSSH公開鍵認証でログインする

f:id:daichi703n:20190103195640p:plain

サーバ運用では一般的なSSH公開鍵認証Cisco ASAで実装する。これにより、よりセキュア、かつ効率的に運用が可能になる。

オフィシャル情報

Cisco ASAのSSH関連設定ガイドはこちら www.cisco.com

本記事ではSSH公開鍵認証に特化して設定方法を記載する。

環境情報

自宅用なので、定期的なOSバージョンアップはしておらず、やや古い...。SSH公開鍵認証を適用する際には事前に導入バージョンで検証を実施ください。(後述の通りバージョンによる差異があります)

ASA5505# sh ver

Cisco Adaptive Security Appliance Software Version 9.2(3)
Device Manager Version 7.2(1)

Compiled on Mon 15-Dec-14 04:10 by builders
System image file is "disk0:/asa923-k8.bin"
...

認証は機器ローカルのみで実施する。よりセキュアな環境ではRADIUSサーバによる認証を検討ください。( Cisco ASA:RADIUSを使ったASAへのログイン設定と注意点 - Cisco Community )

ASA5505# sh run | i aaa
aaa authentication ssh console LOCAL
aaa authentication telnet console LOCAL

通常のパスワード認証でログインできる状態からスタートする。

ASA5505# sh run | i password
enable password PASSWORD encrypted
username dev password PASSWORD encrypted privilege 15
---

[dev@h-cent-mng01 ~]$ ssh dev@asa5505
dev@asa5505's password: PASSWORD
Type help or '?' for a list of available commands.
ASA5505>

SSH公開鍵認証の設定をする

設定コマンドは以下の通り。(上記ドキュメントを参考に設定)

ASA5505# conf t
ASA5505(config)# username dev password PASSWORD privilege 15
ASA5505(config)# username dev attributes
ASA5505(config-username)# ssh authentication publickey <PUBLIC_KEY>

ここで入力するPUBLIC_KEYは、以下のようなSSH公開鍵の、

[dev@h-cent-mng01 ~]$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAAD... dev@d-cent-mng01.designet.local

スペースを含まないこの部分だけを入力する。

AAAAB3NzaC1yc2EAAAAD...

SSH秘密鍵でログインする

ローカルからは適切な秘密鍵を使用して接続するよう、.ssh/config等を設定する。Teraterm等の場合はSSH秘密鍵ファイルを選択する。

パスフレーズなしのSSHキーペアを設定している場合、以下のようにパスフレーズなしでログインできる。

[dev@h-cent-mng01 ~]$ ssh dev@asa5505
Type help or '?' for a list of available commands.
ASA5505>

(注意)パスワードなしユーザの作成・追加設定

検証したバージョンでは、以下のようにnopasswordとしてユーザを作成した場合、空のパスワードが設定されるため注意。

ASA5505(config)# username dev nopassword privilege 15
[dev@h-cent-doc01 ~]$ ssh asa5505
dev@asa5505's password: [何も入力せずEnter]
Type help or '?' for a list of available commands.
ASA5505>

検証バージョンではパスワードなしユーザは作成できない...

ASA5505(config)# username hatena ?

configure mode commands/options:
  attributes  Enter the attributes sub-command mode for the specified user
  nopassword  Indicates that this user has no password
  password    The password for this user

ASA5505(config)# username hatena attributes
ERROR: Invalid username

以下のバージョンでパスワード未定義が作成できるようになったようだ。(未検証)

SSH 公開キー認証の改善

9.6(2)

以前のリリースでは、ローカル ユーザ データベース( (aaa authentication ssh console LOCAL ))を使用して AAA SSH 認証を有効にしなくても、SSH 公開キー認証((ssh authentication ))を有効にすることができました。この設定は修正されたため、AAA SSH 認証を明示的に有効にする必要があります。ユーザが秘密キーの代わりにパスワードを使用できないよう、パスワード未定義のユーザ名を作成できるようになりました。

次のコマンドが変更されました。ssh authentication、username

(備考)Privilegeの動作

Privilegeの動作についてはこちらのやりとりがが参考になった。

serverfault.com

ログイン時からEnableモード(15)になるのではなく、(enable_15ユーザでなく)そのユーザでEnableモードに遷移できるということのようだ。Linuxで言うsudoできるようなものだ。

ASA5505> show curpriv
Username : dev
Current privilege level : 1
Current Mode/s : P_UNPR
ASA5505> en
Password: ENABLE_PASSWORD

ASA5505# show curpriv
Username : enable_15
Current privilege level : 15
Current Mode/s : P_PRIV

ASA5505# exit
ASA5505> login
Username: dev
Password: PASSWORD
ASA5505# sh curpriv
Username : dev
Current privilege level : 15
Current Mode/s : P_PRIV

まとめ - Cisco ASAにSSH公開鍵認証でログインする

Cisco ASAにSSH公開鍵認証を設定し、秘密鍵によるログインを実装した。検証用途で言うと、パスフレーズなしキーペアの使用により効率が向上する。より新しいOSバージョンを使用することで、パスワードログインを禁止し鍵認証を必須とすることができる。

TurbulenceでBOSH環境にカオスエンジニアリングを導入する

f:id:daichi703n:20181224210012p:plain

Cloud Foundry Advent Calender 2018 の24日目

先進的な企業・サービスで導入されているカオスエンジニアリングを実施してみる。Cloud FoundryやKubernetesなど、BOSHで構築された環境であれば、本記事の方式で比較的簡単に導入できる。

なお、本記事では、BOSH環境でのカオスエンジニアリングの導入・動作確認までを範囲としており、実サービスでの使用事例等は含みませんのでご了承ください。

前提環境

こちらで構築したBOSH環境で試験する。

TurbuleneをBOSHデプロイする

カオスエンジニアリングツールとしては、BOSH用に作られているTurbulenceを使用する。

github.com

TurbulenceのクライアントをUAAに登録する

事前準備としてBOSH DirectorのUAAにTurbulenceのクライアントを登録しておく。

$ vi uaac-login.sh
bosh int creds.yml --path /uaa_ssl/ca > uaa_ca_cert
uaac target https://$BOSH_ENVIRONMENT:8443 --ca-cert uaa_ca_cert
uaac token client get uaa_admin -s `bosh int creds.yml --path /uaa_admin_client_secret`

$ vi uaac-turbulence.sh
uaac client add turbulence \
  --name turbulence \
  --secret turbulence-secret \
  --authorized_grant_types client_credentials,refresh_token \
  --authorities  bosh.admin

$ ./uaac-login.sh
Target: https://192.168.1.222:8443
Context: uaa_admin, from client uaa_admin

Successfully fetched token via client credentials grant.
Target: https://192.168.1.222:8443
Context: uaa_admin, from client uaa_admin

$ ./uaac-turbulence.sh
  scope: uaa.none
  client_id: turbulence
  resource_ids: none
  authorized_grant_types: refresh_token client_credentials
  autoapprove:
  authorities: bosh.admin
  name: turbulence
  required_user_groups:
  lastmodified: 1545644889344
  id: turbulence

Turbulence APIサーバをデプロイする

git submodule add https://github.com/cppforlife/turbulence-release

Releaseファイルはここから取得する。
https://bosh.io/releases/github.com/cppforlife/turbulence-release?all=1

ops-files/turbulence-options.yml

- type: replace
  path: /releases/name=turbulence
  value:
    name: "turbulence"
    version: "0.10.0"
    url: "https://bosh.io/d/github.com/cppforlife/turbulence-release?v=0.10.0"
    sha1: "259344312796e23500b2836a15140f8f09ad99ee"

BOSH DirectorのCA Certが必要となるためファイルに書き出しておく。

bosh int ./creds.yml --path /director_ssl/ca > director_ca_cert

deploy-turbulence.sh

bosh deploy -d turbulence turbulence-release/manifests/example.yml \
  -o ops-files/turbulence-options.yml \
  -v turbulence_api_ip=10.244.0.101 \
  -v director_ip=$BOSH_ENVIRONMENT \
  --var-file director_ssl.ca=director_ca_cert \
  -v director_client=turbulence \
  -l turbulence_secret.yml

デプロイが完了したら、CredHubから認証情報を取得してWebGUIにアクセス確認する。

$ vi credhub-login.sh
bosh int ./creds.yml --path /credhub_ca/ca > credhub_ca_cert
credhub login -s $BOSH_ENVIRONMENT:8844 \
  --ca-cert credhub_ca_cert \
  --ca-cert uaa_ca_cert \
  --client-name credhub-admin \
  --client-secret `bosh int ./creds.yml --path /credhub_admin_client_secret`

$ ./credhub-login.sh
Setting the target url: https://192.168.1.222:8844
Login Successful

$ credhub get -n /bosh-lite/turbulence/turbulence_api_password
id: b954b174-f705-4b2b-96aa-89cf324122a3
name: /bosh-lite/turbulence/turbulence_api_password
type: password
value: x8LTqZBRzBFTlOtF9llfYg9bPIQ6Es
version_created_at: "2018-12-24T10:00:56Z"

https://<Turbulence IP>:8080Webブラウザでアクセスする。turbulence/xxx(credhub getの結果)でログインする。

f:id:daichi703n:20181224191616p:plain

まだIncidentを登録していないので何も表示されない。

f:id:daichi703n:20181224191629p:plain

Turbulence Agentを各VMにインストールする

TurbulenceはAgentを介してAPIサーバと各VMで処理をする。

BOSH VMへのAgentインストールは、共通的に適用できるようにRuntime Configを使用するとよい。

Director Runtime Config - Cloud Foundry BOSH

今回はサンプルとしてnginxのみに適用するが、include句の指定次第で環境全体にも適用できる。

$ vi ./turbulence-runtime.yml
---
releases:
- name: "turbulence"
  version: "0.10.0"
  url: "https://bosh.io/d/github.com/cppforlife/turbulence-release?v=0.10.0"
  sha1: "259344312796e23500b2836a15140f8f09ad99ee"

addons:
- name: turbulence_agent
  include:
    jobs:
    - name: nginx
      release: nginx
  jobs:
  - name: turbulence_agent
    release: turbulence
    consumes:
      api: {from: api, deployment: turbulence}
    properties:
      debug: false
$ vi ./deploy-turbulence-runtime.sh
bosh update-runtime-config turbulence-runtime.yml \
  --name=turbulence_agent \
  --no-redact
$ ./deploy-turbulence-runtime.sh
Using environment '192.168.1.222' as client 'admin'

+ releases:
+ - name: turbulence
+   sha1: 259344312796e23500b2836a15140f8f09ad99ee
+   url: https://bosh.io/d/github.com/cppforlife/turbulence-release?v=0.10.0
+   version: 0.10.0

+ addons:
+ - include:
+   - name: nginx
+     release: nginx
+   jobs:
+   - consumes:
+       api:
+         deployment: turbulence
+         from: api
+     name: turbulence_agent
+     properties:
+       debug: false
+     release: turbulence
+   name: turbulence_agent

Release 'turbulence/0.10.0' already exists.

Continue? [yN]: y

Succeeded

Runtime Configを設定したら、既存のDeploymentを再デプロイする。

$ ./deploy-nginx.sh
Using environment '192.168.1.222' as client 'admin'

Using deployment 'nginx'

  releases:
+ - name: turbulence
+   sha1: 259344312796e23500b2836a15140f8f09ad99ee
+   url: https://bosh.io/d/github.com/cppforlife/turbulence-release?v=0.10.0
+   version: 0.10.0

+ addons:
+ - include:
+     jobs:
+     - name: nginx
+       release: nginx
+   jobs:
+   - consumes:
+       api:
+         deployment: turbulence
+         from: api
+     name: turbulence_agent
+     properties:
+       debug: "<redacted>"
+     release: turbulence
+   name: turbulence_agent

Continue? [yN]: y

Task 31

Task 31 | 10:32:26 | Preparing deployment: Preparing deployment (00:00:02)
Task 31 | 10:32:29 | Preparing package compilation: Finding packages to compile (00:00:00)
Task 31 | 10:32:29 | Compiling packages: stress/6b00034151fd5be78893a537bd38818ad2a36bef (00:00:19)
Task 31 | 10:32:49 | Updating instance nginx: nginx/811fb685-b437-4994-b355-b36d7a58313d (0) (canary) (00:00:26)
Task 31 | 10:33:15 | Updating instance nginx: nginx/4de7506b-7a80-407e-9154-91dc98388385 (2) (00:00:26)
Task 31 | 10:33:41 | Updating instance nginx: nginx/d0684cd6-93f6-4fc0-b03c-d5380fdd87d2 (1) (00:00:25)

Task 31 Started  Mon Dec 24 10:32:26 UTC 2018
Task 31 Finished Mon Dec 24 10:34:06 UTC 2018
Task 31 Duration 00:01:40
Task 31 done

Succeeded

Processが追加された。

$ bosh -d nginx instances --ps
Using environment '192.168.1.222' as client 'admin'

Task 32. Done

Deployment 'nginx'

Instance                                    Process           Process State  AZ  IPs
nginx/4de7506b-7a80-407e-9154-91dc98388385  -                 running        z1  10.244.0.3
~                                           nginx             running        -   -
~                                           turbulence_agent  running        -   -
nginx/811fb685-b437-4994-b355-b36d7a58313d  -                 running        z1  10.244.0.2
~                                           nginx             running        -   -
~                                           turbulence_agent  running        -   -
nginx/d0684cd6-93f6-4fc0-b03c-d5380fdd87d2  -                 running        z1  10.244.0.4
~                                           nginx             running        -   -
~                                           turbulence_agent  running        -   -

3 instances

Succeeded

Taskを登録する

$ cat turbulence-release/docs/kill-scheduled.sh > turbulence-tasks/scheduled-kill-nginx.sh

nginxを2分毎に1〜2台殺してもらう。(※Limitの動作が期待通りにならない気がする...)

$ cat turbulence-tasks/scheduled-kill-nginx.sh
#!/bin/bash

body='
{
    "Schedule": "@every 2m",

    "Incident": {
        "Tasks": [{
            "Type": "Kill"
        }],

        "Selector": {
            "Deployment": {
                "Name": "nginx"
            },
            "Group": {
                "Name": "nginx"
            },
            "ID": {
                "Limit": "1-2"
            }
        }
    }
}
'

echo $body | curl -vvv -k -X POST https://turbulence:x8LTqZBRzBFTlOtF9llfYg9bPIQ6Es@10.244.0.101:8080/api/v1/scheduled_incidents -H 'Accept: application/json' -d @-

echo
$ ./turbulence-tasks/scheduled-kill-nginx.sh

所定の時間が経過すると、Taskが実行される。

f:id:daichi703n:20181224202043p:plain

各Incidentを開くと、どのインスタンスに対して処理を実行したかが確認できる。

f:id:daichi703n:20181224202548p:plain

ちなみに、原因は調査していないが、以下のように00sを付けた書き方だとうまく動かなかった。

body='
{
    "Schedule": "@every 2m 00s",

サービス継続確認

最低限のcurlでのアクセス確認をする。DNSラウンドロビンでアクセスしているが、OSの機能で障害VMを避けてアクセスできている。

20:02:31〜20:03:48あたりで.4がダウンしていることが分かる。BOSH Releaseに問題がなければ、障害VMはBOSHのHealth Monitorにより自動的に復旧される。

$ while true; do sleep 5; date "+%H:%M:%S"|tr '\n ' ' '; curl http://nginx.bosh.local -m 1; done
20:01:51     10.244.0.2
20:01:56     10.244.0.3
20:02:01     10.244.0.4
20:02:06     10.244.0.2
20:02:11     10.244.0.3
20:02:16     10.244.0.4
20:02:21     10.244.0.2
20:02:26     10.244.0.3
20:02:31     10.244.0.2
20:02:37     10.244.0.2
20:02:42     10.244.0.3
20:02:47     10.244.0.2
20:02:52     10.244.0.2
20:02:57     10.244.0.3
20:03:02     10.244.0.2
20:03:08     10.244.0.2
20:03:13     10.244.0.3
20:03:18     10.244.0.2
20:03:23     10.244.0.2
20:03:28     10.244.0.3
20:03:33     10.244.0.2
20:03:38     10.244.0.2
20:03:43     10.244.0.3
20:03:48     10.244.0.4
20:03:53     10.244.0.2
20:03:58     10.244.0.3
20:04:03     10.244.0.4
...

設定可能Incident

設定可能なIncidentはこちらに記載されている通り。

turbulence-release/api.md at master · cppforlife/turbulence-release · GitHub

Killbosh delete-vm VMCIDで、実際のVM障害とは異なったりするので、要確認。

運用スクリプト

Indicentを取得する。

$ cat turbulence-tasks/get_incidents.sh
curl -k https://turbulence:x8LTqZBRzBFTlOtF9llfYg9bPIQ6Es@10.244.0.101:8080/api/v1/scheduled_incidents

IDを引数としてDeleteする。

$ cat turbulence-tasks/delete-incidents.sh
#!/bin/sh

if [ $# -ne 1 ]; then
  exit 1
fi

curl -k -XDELETE https://turbulence:x8LTqZBRzBFTlOtF9llfYg9bPIQ6Es@10.244.0.101:8080/api/v1/scheduled_incidents/$1

まとめ - TurbulenceでBOSH環境にカオスエンジニアリングを導入する

Turbulenceにより、BOSH環境でランダムにインスタンスに障害を発生させるカオスエンジニアリングの導入を検証した。

本番リリース前の障害試験として実施することにより、想定外の障害動作を摘出することが期待できる。さらには本番環境に適用し、自動回復機能の常時試験も可能となる。

BOSH-Lite in VirtualBoxでブリッジアダプターを使用しリモート接続する

f:id:daichi703n:20181224013042p:plain

Cloud Foundry Advent Calender 2018 の23日目

BOSH-LiteをVirtualBoxでローカル環境に構築する手順は各所で説明されている。その場合、BOSHオフィシャルにも記載されている通りホストOSから各種操作をすることが基本となっている。ただ、メインのPCでBOSH-Liteを稼働させると、リソース消費が大きく他の作業への影響があったり、PCを停止させた場合に再度BOSHの起動作業が必要となったり面倒である。また、Cloud Foundryなどを構築しても環境を共有できず、アプリを公開できない。

そのため、リモートサーバ上で、VirtualBoxのネットワーク構成を変更してBOSH-Lite Directorにリモート接続できる環境を構築した。

ハードウェア

物理サーバ:DELL PowerEdge T110 II, XeonE3 1220, RAM 24GB
ホストOS:CentOS7.5(vSphere VM) , 1vCPU(4コア), RAM 8GB
PC:MacBookAir 2018, RAM 8GB

前提環境

基本的な流れと前提環境はオフィシャルに記載の通り。

Quick Start - Cloud Foundry BOSH

VirtualBox - Cloud Foundry BOSH

VirtualBoxのネットワークをブリッジに変更する

オフィシャルの手順だと、次のようにホストオンリーアダプタ(vboxnet0)+NATネットワーク(NatNetwork)の構成でBOSH Directorを構築する。

f:id:daichi703n:20181224130430p:plain

ここで問題となるのは、リモート環境からホストオンリーアダプターの先のVMへの接続をどうするか。後述のNG例に記載した通り、ポートフォワーディング・ルーティングのいずれの方法でもこの問題を解消できない。

そこで、ネットワーク構成を変更し、VirtualBoxブリッジアダプターの接続方式により、ホストOSのNICを共有し外部NWに直接接続する構成とする。

f:id:daichi703n:20181224130608p:plain

オフィシャルの対応状況

上記ブリッジ接続だが、2018/12月現在オフィシャルではサポートされていない。次のBranchがマージされれば使用可能となるのだが、放置されているように見える...

github.com

ドキュメントを用意した人もいる。

github.com

しかしマージされておらず、当然Releaseファイルも更新されていない...

cloneしてReleaseを作成する

既にbridgedのブランチで機能追加はされているので、単純にチェックアウトしてbosh create releaseする。

git clone > git checkout -b bridged remote/origin/bridged > blob更新 > config/final.yml,config/private.yml調整 > bosh create release --final ...

BOSH Releaseの作成方法はこちらの通り。
Creating a Release - Cloud Foundry BOSH

BOSH-Lite Directorをデプロイする

作成したReleaseファイルを使用してBOSH-Lite Directorをデプロイする。BOSH-Lite DirectorのデプロイはVirtualBoxが稼働しているホストOSで実施する。

Operations fileでCPIを置き換え、ブリッジアダプターを使用するようにする。

※下記URLはそのまま使用可能ですが、今後公開停止するかもしれませんのでご了承ください。

ops-files/virtualbox-bridged-network.yml

- type: remove
  path: /releases/name=bosh-virtualbox-cpi

- type: replace
  path: /releases/-
  value:
    name: bosh-virtualbox-cpi
    version: "0.0.13"
    url: https://s3-ap-northeast-1.amazonaws.com/d703n-bosh-virtualbox-blobs/bosh-virtualbox-cpi-release-0.0.13-bridged.tgz
    sha1: beaf2ff627663f7df6a8730c96caf6fd7c0c57bd

- type: replace
  path: /networks/name=default/subnets/0/cloud_properties?
  value:
    type: bridged
    name: ((network_device))

- type: replace
  path: /networks/name=default/subnets/0/dns
  value: [((dns_servers))]

network_device, IPアドレス環境に合わせて要変更。

deploy-bosh.sh

bosh create-env ~/workspace/bosh-deployment/bosh.yml \
  --state ./state.json \
  -o ~/workspace/bosh-deployment/virtualbox/cpi.yml \
  -o ~/workspace/bosh-deployment/bosh-lite.yml \
  -o ~/workspace/bosh-deployment/uaa.yml \
  -o ~/workspace/bosh-deployment/credhub.yml \
  -o ~/workspace/bosh-deployment/jumpbox-user.yml \
  --vars-store ./creds.yml \
  -o ops-files/virtualbox-bridged-network.yml \
  -v director_name=bosh-lite \
  -v network_device=ens192 \
  -v internal_ip=192.168.1.222 \
  -v internal_gw=192.168.1.5 \
  -v internal_cidr=192.168.1.0/24 \
  -v dns_servers=192.168.1.77

デプロイする。

$ ./deploy-bosh.sh
Deployment manifest: '/home/dev/workspace/bosh-deployment/bosh.yml'
Deployment state: './state.json'

Started validating
  Downloading release 'bosh'... 

Started installing CPI
  Compiling package 'golang-1.8-linux/c97f9a00c26b34a3f59ca15b0f5a079d7f7e27c334cc8100248143c5dc0d4c0a'... 

Starting registry... Finished (00:00:00)
Uploading stemcell 'bosh-vsphere-esxi-ubuntu-xenial-go_agent/170.9'... Finished (00:00:24)

Started deploying
  Creating VM for instance 'bosh/0' from stemcell 'sc-261d9c63-8431-41e5-6396-0068ab24cfdf'... 

Finished deploying (00:10:06)

Stopping registry... Finished (00:00:00)
Cleaning up rendered CPI jobs... Finished (00:00:00)

Succeeded

リモートからBOSH Directorに接続する

ここからリモート作業が可能となる。ログインに必要となるcreds.ymlを手元にコピーし、次のように環境変数として読み込むようにする。

#BOSH-Lite
export BOSH_ENVIRONMENT=192.168.1.222
export BOSH_CA_CERT=`bosh int ~/project/bosh-deployment/creds.yml --path /director_ssl/ca`
export BOSH_CLIENT=admin
export BOSH_CLIENT_SECRET=`bosh int ~/project/bosh-deployment/creds.yml --path /admin_password`

環境変数を読み込んだ上でbosh vmsなどのコマンドの応答があれば接続成功。

$ bosh vms
Using environment '192.168.1.222' as client 'admin'

Succeeded

Nginx BOSH Releaseをデプロイする

環境が構築できたので、BOSH Releaseをデプロイしてみる。シンプルなNginxをデプロイする。(https://github.com/cloudfoundry-community/nginx-release)

準備としてStemcellとCloud Configをアップロードし、Nginx BOSH Releaseをダウンロードする。

bosh upload-stemcell https://bosh.io/d/stemcells/bosh-warden-boshlite-ubuntu-trusty-go_agent

bosh update-cloud-config ~/project/bosh-deployment/warden/cloud-config.yml

git submodule add https://github.com/cloudfoundry-community/nginx-release

環境調整のためのOps-fileを作成する。

ops-files/nginx-options.yml

- type: replace
  path: /releases/name=nginx/url?
  value: https://github.com/cloudfoundry-community/nginx-release/releases/download/1.13.12/nginx-release-1.13.12.tgz

- type: replace
  path: /releases/name=nginx/sha1?
  value: c5d767f43a68b89d9603bccac2a60b6a5dc29836

- type: remove
  path: /instance_groups/name=nginx/networks/name=default/static_ips

デプロイコマンド

deploy-nginx.sh

bosh deploy -d nginx nginx-release/manifests/nginx-lite.yml \
  -o ops-files/nginx-options.yml

デプロイする。

$ ./deploy-nginx.sh
Using environment '192.168.1.222' as client 'admin'

Using deployment 'nginx'

Task 2

Task 2 | 17:03:23 | Downloading remote release: Downloading remote release (00:00:04)
Task 2 | ...
Task 2 done
+ azs:
+ - name: z1
...

Continue? [yN]: y


Task 3

Task 3 | 17:03:47 | Preparing deployment: Preparing deployment (00:00:01)
Task 3 | 17:03:48 | Preparing package compilation: Finding packages to compile (00:00:00)
Task 3 | 17:03:48 | Compiling packages: nginx/23c0391f6bb6630cf68ba02c99f93eabdd65839d (00:02:27)
Task 3 | 17:06:17 | Creating missing vms: nginx/bd0e9a7f-6aeb-45e0-b5cf-8647c55bd7c7 (0) (00:00:13)
Task 3 | 17:06:31 | Updating instance nginx: nginx/bd0e9a7f-6aeb-45e0-b5cf-8647c55bd7c7 (0) (canary) (00:00:24)

Task 3 Started  Sun Dec 23 17:03:47 UTC 2018
Task 3 Finished Sun Dec 23 17:06:55 UTC 2018
Task 3 Duration 00:03:08
Task 3 done

Succeeded

$ bosh vms
Using environment '192.168.1.222' as client 'admin'

Task 4. Done

Deployment 'nginx'

Instance                                    Process State  AZ  IPs         VM CID                                VM Type  Active
nginx/bd0e9a7f-6aeb-45e0-b5cf-8647c55bd7c7  running        z1  10.244.0.2  b290ee7a-fa28-4232-500c-54422c6f3eab  default  true

1 vms

Succeeded

アクセス確認する

リモート環境からBOSH環境へアクセスするためにルーティングを追加する必要がある。Macの場合以下の通りルートを追加する。(再起動するとリセットされるため注意)

複数人で共有する場合はデフォルトゲートウェイとなっているルータにスタティックルートを追加しておくと良い。

sudo route add -net 10.244.0.0/16 192.168.1.222
$ curl http://10.244.0.2
    <html><head><title>BOSH on IPv6</title>
    </head><body>
    <h2>Welcome to BOSH's nginx Release</h2>
    <h2>
    My hostname/IP: <b>10.244.0.2</b><br />
    Your IP: <b>192.168.1.126</b>
    </h2>
    </body></html>

f:id:daichi703n:20181224021516p:plain

問題なくアクセスできた。

まとめ - BOSH-Lite in VirtualBoxでブリッジアダプターを使用しリモート接続する

bosh-virtualbox-cpi-releaseを拡張し、VirtualBox Bridged Adaptorを使用することにより、リモートからBOSH-Lite環境にアクセスできるようにした。BOSH-Liteをリモートサーバ上で稼働させることにより、作業用PCの停止やリソースなどの影響なく、BOSH Releaseの開発から動作確認までを実施できる。

また、検証用途に限るが、そのままチームへの公開環境として使用することもできる。


NG方法

以下の方法を試してみたが、期待動作とならなかった。

VirtualBoxポートフォワーディング

ホストOS:192.168.1.83
ゲストOS(BOSH Director):192.168.50.6
ポートフォワーディング:192.168.1.83:25555 -> 192.168.50.6:25555

VirtualBoxでポートフォワーディングをするだけでは、以下の通り証明書のエラーで接続できない。

$ bosh vms
Fetching info:
  Performing request GET 'https://192.168.1.83:25555/info':
    Performing GET request:
      Retry: Get https://192.168.1.83:25555/info: x509: certificate is valid for 192.168.50.6, not 192.168.1.83

Exit code 1

なお、証明書検証を無効にすれば良いと考えても、残念ながらBOSH CLIv2では証明書の検証が必須となっている。

stackoverflow.com

ホストOSでルーティング

手元のPCでBOSH Directorが所属するサブネット(ホストオンリーアダプター)宛のスタティックルートを設定する。

$ sudo route add -net 192.168.50.6/32 192.168.1.83
Password:
add net 192.168.50.6: gateway 192.168.1.83

追加された。

$ netstat -nr
Routing tables

Internet:
Destination        Gateway            Flags        Refs      Use   Netif Expire
default            192.168.1.5        UGSc          165        0     en0
127                127.0.0.1          UCS             0        0     lo0
127.0.0.1          127.0.0.1          UH              1     1750     lo0
...
192.168.1.83       x:x:x:xx:xx:xx     UHLWIi          2      899     en0   1074
...
192.168.50.6/32    192.168.1.83       UGSc            0        0     en0
...
255.255.255.255/32 link#9             UCS             0        0     en0      !

BOSH Directorにもルート追加して戻りパケットがNatNetworkとならないようにする。

$ bosh int creds.yml --path /jumpbox_ssh/private_key > jumpbox.key
$ chmod 600 jumpbox.key
$ ssh jumpbox@192.168.50.6 -i jumpbox.key
bosh/0:~# ip r
default via 10.0.2.1 dev eth0
10.0.2.0/24 dev eth0  proto kernel  scope link  src 10.0.2.4
192.168.50.0/24 dev eth1  proto kernel  scope link  src 192.168.50.6

bosh/0:~# route add -net 192.168.1.0/24 dev eth1 gw 192.168.50.1

bosh/0:~# ip r
default via 10.0.2.1 dev eth0
10.0.2.0/24 dev eth0  proto kernel  scope link  src 10.0.2.4
192.168.1.0/24 via 192.168.50.1 dev eth1
192.168.50.0/24 dev eth1  proto kernel  scope link  src 192.168.50.6

上記設定の場合、Pingは通るが、TCPの通信ができない。こちらで議論されている通り、ホストオンリーアダプターではルーティングがされない仕様になっているらしい。(Pingが通るのは不思議だが...)

superuser.com

BOSHゲストはブリッジ接続不可

BOSH-LiteではWarden VMでゲストOSを作成し、ゲストのネットワークはVBoxのブリッジアダプターまで延伸できない。

Cloud Configでのネットワークの設定が誤っていると、VMの通信ができずデプロイエラーになる。

Task 6

Task 6 | 18:30:53 | Preparing deployment: Preparing deployment (00:00:01)
Task 6 | 18:30:54 | Preparing package compilation: Finding packages to compile (00:00:00)
Task 6 | 18:30:54 | Compiling packages: nginx/23c0391f6bb6630cf68ba02c99f93eabdd65839d (00:10:48)
                   L Error: Timed out pinging to 38b8f90b-395b-40d0-8610-ca3de241a0bd after 600 seconds
Task 6 | 18:41:42 | Error: Timed out pinging to 38b8f90b-395b-40d0-8610-ca3de241a0bd after 600 seconds

Task 6 Started  Fri Dec 21 18:30:53 UTC 2018
Task 6 Finished Fri Dec 21 18:41:42 UTC 2018
Task 6 Duration 00:10:49
Task 6 error

Updating deployment:
  Expected task '6' to succeed but state is 'error'

Exit code 1

Easy Gantt with Gitmike で休日をグレーアウトする

f:id:daichi703n:20180506175702p:plain

Redmineガントチャートツールとして有用なEasy Ganttだが、テーマGitmikeと組み合わせるとデフォルトでは休日がグレーアウトされない。

f:id:daichi703n:20180506172740p:plain

なお、デフォルトのテーマの場合はこの問題が発生しない。

f:id:daichi703n:20180506173038p:plain

キレイなテーマであるGitmikeはそのまま使用し、休日を考慮してスケジュール・ガントチャートを管理したいため、休日をグレーアウトできるようにCSSをカスタマイズする。

Easy Ganttの導入はこちら

www.easyredmine.com

休日がグレーアウトされない原因

Easy Gantt with Gitmikeで休日がグレーアウトされないのは、Easy Ganttでの休日のレンダリング方法とGitmikeの色設定の組み合わせの問題。

ガントチャートの休日部分は、Javascriptによりスケジュールエリアに箱を書くようになっているのだが、その時の色指定が背景色 * 透明度xx%となっている。Gitmikeではこの背景色として白(255,255,255)が指定されているため、グレーアウトがされなくなっている。

CSSをカスタマイズして休日をグレーアウトする

上記問題の対応として、CSSをカスタマイズする。

CSSのカスタマイズは、直接ソースコードを編集するのではなく、View Customizeプラグインで上書きする。

github.com

CSS適用するIDはJavaScriptにより生成されており変動するため、正規表現で指定する。

Path pattern: /projects/.*/easy_gantt

Type: StyleSheet

Code:

/* Customize gantt css */

[id^="Svgjs"] {
  fill: rgba(0,0,0,0.05);
}

f:id:daichi703n:20180506174256p:plain

これによりEasy Gantt with Gitmikeでも休日をグレーアウトできる。(色は自由に指定可能)

f:id:daichi703n:20180506174151p:plain

まとめ - Easy Gantt with Gitmike で休日をグレーアウトする

View CustomizeプラグインCSSを編集することにより、GitmikeでEasy Ganttのガントチャートで休日がグレーアウトされない問題に対応した。キレイなテーマはそのままに、Easyガントチャートも活用しプロジェクト管理ができるようになった。

Docker-ComposeでMACVLAN (802.1Q VLAN Tag) ネットワークを作成する

f:id:daichi703n:20180502182756p:plain

こちらの記事でDockerコンテナを外部ネットワークと802.1Q VLAN Tag接続した。

designetwork.hatenablog.com

今回はDocker-Composeファイルで定義して運用しやすくする。さらに作り込めば、ネットワークテスト自動化が実現できると考えている。

参考情報

Docker-Composeファイルの書き方はオフィシャル参照。バージョンによりファイルフォーマット(Key名)が変わっていたり使えないので注意が必要。

docs.docker.com

こちらでMACVLANを使用する例がある。

Docker Compose File for MacVLAN Network Driver ( Single Node) · GitHub

Docker-ComposeでVLAN Tagネットワークを作成する

構築するネットワークはこちらの通り。(前回記事と同様)

f:id:daichi703n:20180502125139p:plain

docker-compose.ymlはこのようになる。Alpineを使用し、tail -f /dev/nullとしてコンテナを単純に起動させる。

* versionの指定が異なるとエラーが発生する場合があります。別バージョン使用時はDocker Docsを参照し、書き換えてください。

$ cat ./docker-compose.yml
version: '2.1'

services:
  vlan20:
    image: alpine
    container_name: container-vlan20
    command: ['tail', '-f', '/dev/null']
    networks:
      vlan20:
        ipv4_address: 192.168.20.201
    environment:
      VLAN: 20
  vlan30:
    image: alpine
    container_name: container-vlan30
    command: ['tail', '-f', '/dev/null']
    networks:
      vlan30:
        ipv4_address: 192.168.30.201
    environment:
      VLAN: 30

networks:
  vlan20:
    name: vlan20
    driver: macvlan
    driver_opts:
      parent: ens192.20
    ipam:
      config:
        - subnet: 192.168.20.0/24
          gateway: 192.168.20.1
  vlan30:
    name: vlan30
    driver: macvlan
    driver_opts:
      parent: ens192.30
    ipam:
      config:
        - subnet: 192.168.30.0/24
          gateway: 192.168.30.1

上記docker-compose.ymlファイルを使用してネットワーク・コンテナを作成・起動する。

$ docker-compose up -d
Creating network "vlan20" with driver "macvlan"
Creating network "vlan30" with driver "macvlan"
Creating container-vlan20 ... done
Creating container-vlan30 ... done

Dockerネットワーク・コンテナ状態確認

作成されたDockerネットワークを確認する。MACVLANドライバーを使用して802.1Q VLANタグネットワークとして作成されている。

$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
61b2b0b3706c        bridge              bridge              local
e14ebf55d626        host                host                local
112dc8a40c20        none                null                local
e44db3a06622        vlan20              macvlan             local
44e22d10ce22        vlan30              macvlan             local

$ docker network inspect vlan20
[
    {
        "Name": "vlan20",
        "Id": "e44db3a06622aa890d0af9851782e398ad938c552b3aad3636b364f9fd45186c",
        "Created": "2018-05-02T05:10:57.097052723-04:00",
        "Scope": "local",
        "Driver": "macvlan",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "192.168.20.0/24",
                    "Gateway": "192.168.20.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": true,
        "Containers": {
            "894e66f1395a1771737a0c9dbb1812781a8f2d17ec60d27b57b46c12e776de10": {
                "Name": "container-vlan20",
                "EndpointID": "a424fe4eb6bff57c8aa04a116acffcc41bcc9f0c3f8edd3afa782f63484935d6",
                "MacAddress": "02:42:c0:a8:14:c9",
                "IPv4Address": "192.168.20.201/24",
                "IPv6Address": ""
            }
        },
        "Options": {
            "parent": "ens192.20"
        },
        "Labels": {
            "com.docker.compose.network": "vlan20",
            "com.docker.compose.project": "vlan-tag"
        }
    }
]

$ docker network inspect vlan30
[
    {
        "Name": "vlan30",
        "Id": "44e22d10ce2269ed8204244430dcea4249b931d2be00b459db4bd731cf5cdca3",
        "Created": "2018-05-02T05:10:57.123608227-04:00",
        "Scope": "local",
        "Driver": "macvlan",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "192.168.30.0/24",
                    "Gateway": "192.168.30.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": true,
        "Containers": {
            "bea9276ab6df772e5af985bc7e761765d562caf3d682850f5669e516c02de374": {
                "Name": "container-vlan30",
                "EndpointID": "e6f134af82f057c0ed7259ec8bc9932e23e594f9211663563b776736e884537b",
                "MacAddress": "02:42:c0:a8:1e:c9",
                "IPv4Address": "192.168.30.201/24",
                "IPv6Address": ""
            }
        },
        "Options": {
            "parent": "ens192.30"
        },
        "Labels": {
            "com.docker.compose.network": "vlan30",
            "com.docker.compose.project": "vlan-tag"
        }
    }
]

$ docker ps
CONTAINER ID        IMAGE               COMMAND               CREATED             STATUS              PORTS               NAMES
894e66f1395a        alpine              "tail -f /dev/null"   26 seconds ago      Up 26 seconds                           container-vlan20
bea9276ab6df        alpine              "tail -f /dev/null"   26 seconds ago      Up 26 seconds                           container-vlan30

コンテナ同士の疎通・経路確認

起動したコンテナで相互にPing・Tracerouteにより疎通・経路確認する。

$ docker exec -it container-vlan20 /bin/sh
/ # ip a
84: eth0@if82: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UNKNOWN
    link/ether 02:42:c0:a8:14:c9 brd ff:ff:ff:ff:ff:ff
    inet 192.168.20.201/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:c0ff:fea8:14c9/64 scope link
       valid_lft forever preferred_lft forever

/ # ping 192.168.20.1
PING 192.168.20.1 (192.168.20.1): 56 data bytes
64 bytes from 192.168.20.1: seq=0 ttl=64 time=1.164 ms
64 bytes from 192.168.20.1: seq=1 ttl=64 time=0.332 ms

/ # ping 192.168.30.201
PING 192.168.30.201 (192.168.30.201): 56 data bytes
64 bytes from 192.168.30.201: seq=0 ttl=63 time=1.160 ms
64 bytes from 192.168.30.201: seq=1 ttl=63 time=0.317 ms

/ # traceroute 192.168.30.201
traceroute to 192.168.30.201 (192.168.30.201), 30 hops max, 46 byte packets
 1  192.168.20.1 (192.168.20.1)  0.366 ms  0.141 ms  0.452 ms
 2  192.168.30.201 (192.168.30.201)  0.200 ms  0.283 ms  0.268 ms

/ # exit

$ docker exec -it container-vlan30 /bin/sh
/ # ip a
85: eth0@if83: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UNKNOWN
    link/ether 02:42:c0:a8:1e:c9 brd ff:ff:ff:ff:ff:ff
    inet 192.168.30.201/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:c0ff:fea8:1ec9/64 scope link
       valid_lft forever preferred_lft forever

/ # ping 192.168.30.1
PING 192.168.30.1 (192.168.30.1): 56 data bytes
64 bytes from 192.168.30.1: seq=0 ttl=64 time=0.298 ms
64 bytes from 192.168.30.1: seq=1 ttl=64 time=0.300 ms

/ # ping 192.168.20.201
PING 192.168.20.201 (192.168.20.201): 56 data bytes
64 bytes from 192.168.20.201: seq=0 ttl=63 time=0.511 ms
64 bytes from 192.168.20.201: seq=1 ttl=63 time=0.405 ms

/ # traceroute 192.168.20.201
traceroute to 192.168.20.201 (192.168.20.201), 30 hops max, 46 byte packets
 1  192.168.30.1 (192.168.30.1)  0.217 ms  0.215 ms  0.197 ms
 2  192.168.20.201 (192.168.20.201)  0.306 ms  0.458 ms  0.443 ms
/ # exit

SUCCEEDED!! 期待通り相互に疎通確認・経路確認ができている。Tracerouteの結果からわかる通り、ホストNICはVLANタグを透過して、外部ルータでVLAN間ルーティングされている。

まとめ - Docker-ComposeでMACVLAN (802.1Q VLAN Tag) ネットワークを作成する

Docker-ComposeでMACVLAN (802.1Q VLAN Tag) ネットワークを作成し、それぞれのネットワーク(VLAN/セグメント)にコンテナを配置し、相互に疎通・経路確認を実施した。

docker-compose.ymlの生成方法、起動コンテナ用シェルスクリプトを工夫すれば、ネットワークテスト自動化が可能になると考えている。

Dockerコンテナを外部ネットワークとVLAN Tag接続する

f:id:daichi703n:20180502133729p:plain

ネットワークの疎通確認・経路確認などのテストにDockerコンテナを活用する方式を検討している。テストの際に大量のPCを用意するのは非効率なため、軽量・スピーディなDockerコンテナを活用していきたい。

イメージしている構成は以下の通り。802.1Q VLANタグでホストNICを分離して、各VLAN(セグメント)にDockerコンテナをぶら下げて、相互にPing・Tracerouteにより疎通確認・経路確認ができるようにする。

f:id:daichi703n:20180502125139p:plain

最終的には、以下のような一連の作業をタスク化し、ネットワークテスト自動化環境を構築したい。

  • VLAN Tagネットワーク生成
  • コンテナ起動
  • テスト実行
  • 結果評価

参考にした情報

要素技術として以下の情報を参考にさせていただきました。ありがとうございます。

前提環境

vSphereのスタンドアロン環境で、NICIntel ProなどVLANトランク対応していれば問題なし。標準vSwitchのポートグループでVLANトランクを選択して全VLANを802.1Qタグ付きで通している。また、無差別モード(Promiscuous)を有効にしている。

ゲストOS

CentOSでDockerをインストールして使用する。Dockerが動けば問題なし。Docker構築手順は割愛。

  • CentOS Linux release 7.4.1708 (Core)
  • Docker version 1.13.1, build 774336d/1.13.1
  • docker-compose version 1.21.0, build 5920eb0

環境構築

冒頭の図のような環境を構築する。

Dockerネットワーク作成

MACVLANドライバを指定してDockerネットワークを作成する。parentのens192部分は環境に合わせる。

$ docker network create -d macvlan \
    --subnet=192.168.20.0/24 \
    --gateway=192.168.20.1 \
    -o parent=ens192.20 vlan20

$ docker network create -d macvlan \
    --subnet=192.168.30.0/24 \
    --gateway=192.168.30.1 \
    -o parent=ens192.30 vlan30

Dockerネットワークが作成された。

$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
61b2b0b3706c        bridge              bridge              local
e14ebf55d626        host                host                local
112dc8a40c20        none                null                local
cacbb0f62e7e        vlan20              macvlan             local
554515e7aa1a        vlan30              macvlan             local

$ docker network inspect vlan20
[
    {
        "Name": "vlan20",
        "Id": "cacbb0f62e7e971c34f9a556e153c367d81ca23eb7d22a2fce8ce35920f031d9",
        "Created": "2018-05-01T23:33:01.063894797-04:00",
        "Scope": "local",
        "Driver": "macvlan",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "192.168.20.0/24",
                    "Gateway": "192.168.20.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Containers": {},
        "Options": {
            "parent": "ens192.20"
        },
        "Labels": {}
    }
]

$ docker network inspect vlan30
[
    {
        "Name": "vlan30",
        "Id": "554515e7aa1ad11d7df178ce3b6244f087cecf7ca19bb3792915f4f9f7f73de9",
        "Created": "2018-05-01T23:33:03.007612901-04:00",
        "Scope": "local",
        "Driver": "macvlan",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "192.168.30.0/24",
                    "Gateway": "192.168.30.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Containers": {},
        "Options": {
            "parent": "ens192.30"
        },
        "Labels": {}
    }
]

DockerホストのCentOSのネットワークは以下のような状態になっている。(抜粋)

$ ip a
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:0c:29:3a:4c:62 brd ff:ff:ff:ff:ff:ff
4: ens192.10@ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP qlen 1000
    link/ether 00:0c:29:3a:4c:62 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.80/24 brd 192.168.1.255 scope global ens192.10
       valid_lft forever preferred_lft forever
5: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN
    link/ether 02:42:a6:4a:5b:fe brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:a6ff:fe4a:5bfe/64 scope link
       valid_lft forever preferred_lft forever
19: ens192.20@ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
    link/ether 00:0c:29:3a:4c:62 brd ff:ff:ff:ff:ff:ff
20: ens192.30@ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
    link/ether 00:0c:29:3a:4c:62 brd ff:ff:ff:ff:ff:ff

Dockerコンテナ作成

作成したネットワーク、IPアドレスを指定してコンテナを作成する。Ping・Tracerouteができればよいので、Alpineを使用する。WARNINGが出るが今回は無視する。

$ docker run -it -d --rm \
    --net=vlan20 \
    --ip=192.168.20.201 \
    --name container-vlan20 \
    alpine /bin/sh

WARNING: IPv4 forwarding is disabled. Networking will not work.
4b5dd6ab28bdaa49b8a64221177b942a3754ae058854081318b14f6e79b9e038

$ docker run -it -d --rm \
    --net=vlan30 \
    --ip=192.168.30.201 \
    --name container-vlan30 \
    alpine /bin/sh

WARNING: IPv4 forwarding is disabled. Networking will not work.
c68efffc09fffb01f44054293bb71213db3f7fb7c72f85300771a5aa11cabb64

コンテナが作成された。

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
c68efffc09ff        alpine              "/bin/sh"           3 seconds ago       Up 2 seconds                            container-vlan30
4b5dd6ab28bd        alpine              "/bin/sh"           43 seconds ago      Up 43 seconds                           container-vlan20

動作確認

それぞれのコンテナから疎通確認する。

$ docker exec -it container-vlan20 /bin/sh

/ # ip a
23: eth0@if19: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UNKNOWN
    link/ether 02:42:c0:a8:14:c9 brd ff:ff:ff:ff:ff:ff
    inet 192.168.20.201/24 scope global eth0
       valid_lft forever preferred_lft forever

/ # ping 192.168.20.1
PING 192.168.20.1 (192.168.20.1): 56 data bytes
64 bytes from 192.168.20.1: seq=0 ttl=64 time=1.201 ms
64 bytes from 192.168.20.1: seq=1 ttl=64 time=0.295 ms

/ # ping 192.168.30.201
PING 192.168.30.201 (192.168.30.201): 56 data bytes
64 bytes from 192.168.30.201: seq=0 ttl=63 time=23.294 ms
64 bytes from 192.168.30.201: seq=1 ttl=63 time=0.521 ms

/ # traceroute 192.168.30.201
traceroute to 192.168.30.201 (192.168.30.201), 30 hops max, 46 byte packets
 1  192.168.20.1 (192.168.20.1)  0.343 ms  0.157 ms  0.251 ms
 2  192.168.30.201 (192.168.30.201)  0.323 ms  0.294 ms  0.238 ms

逆側からも疎通・経路確認。

$ docker exec -it container-vlan30 /bin/sh

/ # ip a
24: eth0@if20: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UNKNOWN
    link/ether 02:42:c0:a8:1e:c9 brd ff:ff:ff:ff:ff:ff
    inet 192.168.30.201/24 scope global eth0
       valid_lft forever preferred_lft forever

/ # ping 192.168.30.1
PING 192.168.30.1 (192.168.30.1): 56 data bytes
64 bytes from 192.168.30.1: seq=0 ttl=64 time=0.281 ms
64 bytes from 192.168.30.1: seq=1 ttl=64 time=0.293 ms

/ # ping 192.168.20.201
PING 192.168.20.201 (192.168.20.201): 56 data bytes
64 bytes from 192.168.20.201: seq=0 ttl=63 time=0.392 ms
64 bytes from 192.168.20.201: seq=1 ttl=63 time=0.444 ms

/ # traceroute 192.168.20.201
traceroute to 192.168.20.201 (192.168.20.201), 30 hops max, 46 byte packets
 1  192.168.30.1 (192.168.30.1)  0.240 ms  0.176 ms  0.303 ms
 2  192.168.20.201 (192.168.20.201)  0.463 ms  0.216 ms  0.306 ms
/ #

SUCCEEDED!! 期待通り相互に疎通確認・経路確認ができている。Tracerouteの結果からわかる通り、ホストNICはVLANタグを透過して、外部ルータでVLAN間ルーティングされている。

まとめ - Dockerコンテナを外部ネットワークとVLAN Tag接続する

DockerのMACVLANネットワークドライバをすることで、Dockerコンテナを外部ネットワークと802.1Q VLAN Tag接続することができた。Dockerコンテナを活用することで、ネットワークのテストのために大量のPCを用意する必要がなくなる。

(追記) Docker-Composeはこちら

本記事と同内容をDocker-Composeで実装しました。

designetwork.hatenablog.com

SSH Piper w/DockerでSSH R-Proxyを構築する

f:id:daichi703n:20180430124255p:plain

FirewallなどによりサーバへのSSHアクセスが制限されている環境において、踏み台サーバではなく、SSH R-Proxyサーバ (Dockerコンテナ) によりバックエンドのサーバへのSSH Proxyアクセスを実装する。

HTTP Proxy経由でSSH接続をする方法もあるが、今回はHTTP Proxyを使用せず、シンプルなSSH R-Proxyを使用する。

SSH Piper

今回、SSH ProxyとしてこちらのSSH Piperを使用する。

github.com

READMEより
ここでは各個人用サーバ(コンテナ)へのユーザ名ベースでのSSH振り分けを想定している。

+---------+                      +------------------+          +-----------------+
|         |                      |                  |          |                 |
|   Bob   +----ssh -l bob----+   |   SSH Piper   +------------->   Bob' machine  |
|         |                  |   |               |  |          |                 |
+---------+                  |   |               |  |          +-----------------+
                             +---> pipe-by-name--+  |                             
+---------+                  |   |               |  |          +-----------------+
|         |                  |   |               |  |          |                 |
|  Alice  +----ssh -l alice--+   |               +------------->  Alice' machine |
|         |                      |                  |          |                 |
+---------+                      +------------------+          +-----------------+


 Downstream                         SSH Piper                       Upstream               

期待するのは、HTTPリバースプロキシのような動作だが、SSHではHTTPのようにhostヘッダでのルーティングはできないので、ログインユーザ名でのリバースプロキシ的な動作になる。

DockerでSSH Piperを構築する

SSH PiperはDocker Imageとして提供されている。これによりGo言語の実行環境セットアップなどをスキップできる。

ディレクトリ・ファイル構成

再利用性を考慮してdocker-composeを使用する。ディレクトリ、ファイル構成は以下の通り。

$ sudo tree -pug
.
├── [-rw-r--r-- root     root    ]  README.md
├── [drwxr-xr-x root     root    ]  config
│   └── [drwxr-xr-x root     root    ]  sshpiper
│       ├── [drwx------ root     root    ]  asa
│       │   └── [-rw------- root     root    ]  sshpiper_upstream
│       └── [drwx------ root     root    ]  centos7
│           └── [-rw------- root     root    ]  sshpiper_upstream
└── [-rw-r--r-- root     root    ]  docker-compose.yml

docker-compose.ymlはこのようになる。

$ cat ./docker-compose.yml
version: '2'

#https://github.com/tg123/sshpiper
services:

  external:
    image: farmer1992/sshpiperd
    container_name: sshpiper
    ports:
      - "2222:2222"
    volumes:
      - /etc/ssh/ssh_host_rsa_key:/etc/ssh/ssh_host_rsa_key:ro
      - ./config/sshpiper:/var/sshpiper

詳細表示している通り、ConfigのPermissionに気をつける必要がある。Configファイルは700/600とする。

$ sudo chmod 700 config/sshpiper/asa
$ sudo chmod 600 ./config/sshpiper/asa/sshpiper_upstream
$ sudo ls -la ./config/sshpiper/asa/
合計 4
drwx------. 2 root root 31  4月 30 10:57 .
drwxr-xr-x. 4 root root 28  4月 30 10:56 ..
-rw-------. 1 root root 36  4月 30 10:57 sshpiper_upstream

sshpiper_upstream の記載はこの通り。

$ sudo cat ./config/sshpiper/asa/sshpiper_upstream
# Cisco ASA5505
dev@192.168.1.5:22

SSH Proxy動作確認

SSH Piper の Dockerコンテナを起動して動作確認する。

$ docker-compose up

<Another Terminal>
$ ssh 192.168.1.76 -p 2222 -l asa
asa@192.168.1.76's password:
Type help or '?' for a list of available commands.
ASA5505>

$ ssh 192.168.1.76 -p 2222 -l centos7
centos7@192.168.1.76's password:
[dev@CentOS7-01 ~]$

上記はLinuxからのログだが、TeraTerm等のターミナルからも同様にSSHアクセス可能。パスワード入力プロンプトではSSH Piperのものが表示されるが、接続に成功すると、アップストリームのサーバ(NW機器)に接続できている。

SSH Piperのアクセスログは以下の通り。ユーザ名とアップストリーム(バックエンド)のサーバがマッピングされている。

sshpiper    | 2018/04/30 02:03:09 sshpiperd started
sshpiper    | 2018/04/30 02:03:10 connection accepted: 192.168.1.111:60508
sshpiper    | 2018/04/30 02:03:10 mapping user [asa] to [dev@192.168.1.5:22]

Permissionが不適切だと使用不可

sshpiper_upstreamファイルのPermissionは.ssh同様に制限されている必要がある。Permissionが不適切だと以下のログのようにperm is too openのエラーでSSH接続に失敗する。

$ sudo docker-compose up
Creating network "sshpiper_default" with the default driver
Creating sshpiper ...
Creating sshpiper ... done
Attaching to sshpiper
sshpiper    | sshpiperd by Boshi Lian<farmer1992@gmail.com>
sshpiper    | https://github.com/tg123/sshpiper
sshpiper    |
<snip>
sshpiper    | 2018/04/30 01:59:23 sshpiperd started
sshpiper    | 2018/04/30 01:59:25 connection accepted: 192.168.1.111:60436
sshpiper    | 2018/04/30 01:59:25 connection from 192.168.1.111:60436 establishing failed reason: /var/sshpiper/asa/sshpiper_upstream's perm is too open

TODO

まとめ - SSH Piper w/DockerでSSH Proxyを構築する

SSH PiperによりSSH Proxyを構築できた。引き続きアクセス制御関連の検証が必要だが、シンプルにSSH Proxyとして使用することができるため、Firewall等の各種制約を回避するためには非常に有用と考えられる。