designetwork

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

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等の各種制約を回避するためには非常に有用と考えられる。

Redmineで期日超過のチケットを一覧表示時にマークアップする (View Customize Plugin使用)

f:id:daichi703n:20180430002513p:plain

Redmineでの課題管理において、チケット一覧表示時に期日超過チケットをマークアップ表示することで対応漏れを防ぐことができる。リマインドメールを活用すれば同等効果が期待できるが、一覧表示時にもマークアップしておくと状況が把握しやすい。

実装イメージ

期日超過チケットを太字・赤字で表示する。CSS調整すればマークアップする箇所の拡大・絞り込みも可能。

f:id:daichi703n:20180430001648p:plain

前提環境

CSSのカスタマイズには、こちらのRedmine view customize pluginを使用する。

github.com

期限超過チケットをマークアップするView Customize CSS

Path pattern: /projects/.*/issues

Type: StyleSheet

Code:

/* Markup Overdue Issues */

tr.overdue{
font-weight: bold;
color: red;
}

f:id:daichi703n:20180430001117p:plain

(参考) Redmineの期日超過要素名

www.redmine.org

まとめ - Redmineで期日超過のチケットを一覧表示時にマークアップする (View Customize Plugin使用)

RedmineCSSをカスタマイズすることで、一覧表示時に期日超過チケットをマークアップすることができる。

Proxy環境での設定誤りでGem, Bundleインストールが失敗するケース

f:id:daichi703n:20180428182155p:plain

Proxy環境でのパッケージインストールは環境変数などでProxyサーバの指定が必要となる。Gem, Bundlerにおいても同様、http_proxy環境変数の設定が必要となる。

環境構築の中で、誤設定によりProxyが期待通りに動作しないケースがあったため、設定内容による動作の違いを確認した。

検証環境

今回の検証では2018/4/28時点のlatestのRuby Dockerイメージを使用した。

root@8271fa172878:/test# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 9 (stretch)"
NAME="Debian GNU/Linux"
VERSION_ID="9"
VERSION="9 (stretch)"
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"

root@8271fa172878:/test# ruby -v
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]

root@8271fa172878:/test# gem -v
2.7.6

root@8271fa172878:/test# bundle -v
Bundler version 1.16.1

root@8271fa172878:/test# cat ./Gemfile
source 'https://rubygems.org'
gem 'haml'

(失敗) Proxy未設定

Proxy環境で、インターネット向けのDNSによる名前解決ができないケースでは、Proxyの環境変数が未設定の場合は、当然ながら以下のようにGemインストール、Bundleインストールに失敗する。

root@8271fa172878:/test# env | grep proxy

root@8271fa172878:/test# gem install haml
ERROR:  Could not find a valid gem 'haml' (>= 0), here is why:
          Unable to download data from https://rubygems.org/ - no such name (https://rubygems.org/specs.4.8.gz)

root@8271fa172878:/test# bundle install
Fetching source index from https://rubygems.org/

Retrying fetcher due to error (2/4): Bundler::HTTPError Could not fetch specs from https://rubygems.org/
Retrying fetcher due to error (3/4): Bundler::HTTPError Could not fetch specs from https://rubygems.org/
Retrying fetcher due to error (4/4): Bundler::HTTPError Could not fetch specs from https://rubygems.org/
Could not fetch specs from https://rubygems.org/

(成功) http_proxy環境変数設定

http_proxy環境変数が適切に設定されている場合は、以下のように成功する。

root@8271fa172878:/test# export http_proxy=http://192.168.1.76:8080

root@8271fa172878:/test# env | grep proxy
http_proxy=http://192.168.1.76:8080

root@8271fa172878:/test# bundle install
Fetching gem metadata from https://rubygems.org/....
Using bundler 1.16.1
Using temple 0.8.0
Using tilt 2.0.8
Fetching haml 5.0.4
Installing haml 5.0.4
Bundle complete! 1 Gemfile dependency, 4 gems now installed.
Bundled gems are installed into `/usr/local/bundle`

(失敗) no_proxyの誤設定

Proxy環境でもイントラは直接アクセスする場合などはno_proxy環境変数により、ドメインIPアドレス指定でProxy経由しないようにすることが多い。

今回失敗したケースでは、対象ドメインの追加を想定してexport no_proxy=$no_proxy,localhost,127.0.0.1として再帰的にno_proxyを設定した。

root@8271fa172878:/test# gem uninstall haml

root@8271fa172878:/test# export no_proxy=$no_proxy,localhost,127.0.0.1

root@8271fa172878:/test# env | grep proxy
http_proxy=http://192.168.1.76:8080
no_proxy=,localhost,127.0.0.1

root@8271fa172878:/test# bundle install
Fetching gem metadata from https://rubygems.org/....
Using bundler 1.16.1
Using temple 0.8.0
Using tilt 2.0.8
Fetching haml 5.0.4

Retrying download gem from https://rubygems.org/ due to error (2/4): Gem::RemoteFetcher::UnknownHostError no such name (https://rubygems.org/gems/haml-5.0.4.gem)
Retrying download gem from https://rubygems.org/ due to error (3/4): Gem::RemoteFetcher::UnknownHostError no such name (https://rubygems.org/gems/haml-5.0.4.gem)
Retrying download gem from https://rubygems.org/ due to error (4/4): Gem::RemoteFetcher::UnknownHostError no such name (https://rubygems.org/gems/haml-5.0.4.gem)
Gem::RemoteFetcher::UnknownHostError: no such name (https://rubygems.org/gems/haml-5.0.4.gem)
An error occurred while installing haml (5.0.4), and Bundler cannot continue.
Make sure that `gem install haml -v '5.0.4'` succeeds before bundling.

In Gemfile:
  haml

root@8271fa172878:/test# gem install haml
ERROR:  Could not find a valid gem 'haml' (>= 0), here is why:
          Unable to download data from https://rubygems.org/ - no such name (https://rubygems.org/specs.4.8.gz)

Bundlerによる依存関係の確認は成功しているが、Gemパッケージの取得時にGem::RemoteFetcher::UnknownHostError: no such nameのエラーで失敗している。個別にGemインストールを試行しても同様に名前解決のエラーで失敗する。

厄介なのは、BundlerはProxy設定を読み込んで動作できているのに、GemでProxyが動作しないこと。

no_proxy構文エラーによりProxyがセットされない

上記の原因はこちらでもやり取りされている通り、no_proxyの構文エラーによるもので、その場合はGem Proxyがセットされず、直接アクセスになりGemインストールに失敗する。

github.com

(NG) no_proxy=,localhost,127.0.0.1
(OK) no_proxy=localhost,127.0.0.1

(成功) no_proxyを適切に設定

no_proxyを適切に設定することで、以下の通り期待通りにGemインストールが可能となる。

root@8271fa172878:/test# export no_proxy=localhost,127.0.0.1

root@8271fa172878:/test# env | grep proxy
http_proxy=http://192.168.1.76:8080
no_proxy=localhost,127.0.0.1

root@8271fa172878:/test# bundle install
Fetching gem metadata from https://rubygems.org/....
Using bundler 1.16.1
Using temple 0.8.0
Using tilt 2.0.8
Fetching haml 5.0.4
Installing haml 5.0.4
Bundle complete! 1 Gemfile dependency, 4 gems now installed.
Bundled gems are installed into `/usr/local/bundle`

まとめ - Proxy環境での設定誤りでGem, Bundleインストールが失敗するケース

no_proxyの誤設定によりProxy環境でのGemインストールに失敗するケースが確認できた。Proxy環境変数は適切に設定することが重要だと改めて実感した。

Proxy環境のDocker RedmineでGemインストールするための設定

f:id:daichi703n:20180421011243p:plain

(2018/4/28 Update 別原因だったため訂正)

Docker Redmineへのプラグイン追加の際、Gemパッケージのインストールが必要となる場合には、docker-compose.ymlでProxy環境変数を設定する。

.gemrcの配備が必要となる。

発生した問題と調査内容、対応方法を示す。

前提環境

Redmine Docker Image: sameersbn/docker-redmine

github.com

こちらのdocker-compose.ymlで各パラメータを設定している。

docker-redmine/docker-compose.yml at master · sameersbn/docker-redmine · GitHub

環境変数でのProxy設定

コンテナへの環境変数設定はdocker-compose.ymlで定義している。docker-compose.ymlファイルに認証情報を含むプロキシ情報は記載したくないので、ホストOSの環境変数を引き継ぐようにしている。

version: '2'

services:
  redmine:
    image: sameersbn/redmine:3.4.4-2
    container_name: redmine
    depends_on:
    - postgresql
    restart: always
    environment:
    - TZ=Asia/Tokyo
    - HTTP_PROXY=${HTTP_PROXY}
    - http_proxy=${HTTP_PROXY}
    - HTTPS_PROXY=${HTTPS_PROXY}
    - https_proxy=${HTTPS_PROXY}
    - NO_PROXY=${NO_PROXY}
    - no_proxy=${NO_PROXY}
    <snip>

問題動作

このログの通り、Gemでのインストールに失敗している。UnknownHostErrorのログより、Proxyに通信していないことが原因となっている。ホストOSでのtcpdumpからも、rubygems.orgの名前解決(DNS)要求が発生していた。(本来はDNSなしでProxy通信)

$docker-compose up
Starting postgres-redmine ... 
Starting postgres-redmine ... doneRecreating redmine        ... 
Recreating redmine        ... doneAttaching to postgres-redmine, redmine
postgres-redmine | Initializing datadir...
redmine       | Initializing logdir...
redmine       | Initializing datadir...
redmine       | Symlinking dotfiles...
redmine       | Installing configuration templates...
redmine       | Configuring redmine...
postgres-redmine | Initializing certdir...
postgres-redmine | Initializing logdir...
postgres-redmine | Initializing rundir...
postgres-redmine | Setting resolv.conf ACLs...
postgres-redmine | Creating database user: redmine
postgres-redmine | Creating database: redmine_production...
postgres-redmine | ? Granting access to redmine user...
postgres-redmine | Starting PostgreSQL 9.6...
postgres-redmine | LOG:  database system was shut down at 2018-04-19 12:22:57 UTC
postgres-redmine | LOG:  MultiXact member wraparound protections are now enabled
postgres-redmine | LOG:  database system is ready to accept connections
postgres-redmine | LOG:  autovacuum launcher started
redmine       | Configuring redmine::database...
redmine       | Configuring redmine::unicorn...
redmine       | Configuring redmine::secret_token...
redmine       | Generating a session token...
redmine       | Note:
redmine       |   All old sessions will become invalid.
redmine       |   Please specify the REDMINE_SECRET_TOKEN parameter for persistence.
redmine       |   **SHOULD** be defined if you have a load-balancing Redmine cluster.
redmine       | Configuring redmine::max_concurrent_ajax_uploads...
redmine       | Configuring redmine::sudo_mode...
redmine       | Configuring redmine::autologin_cookie...
redmine       | Configuring redmine::email_delivery...
redmine       | Configuring redmine::backups...
redmine       | Configuring redmine::backups::schedule...
redmine       | Configuring nginx...
redmine       | Configuring nginx::redmine...
redmine       | Installing plugins...
redmine       | Installing gems required by plugins...
redmine       | Gem::RemoteFetcher::UnknownHostError: no such name
redmine       | (https://rubygems.org/gems/rubyzip-1.2.1.gem)
redmine       | An error occurred while installing rubyzip (1.2.1), and Bundler cannot continue.
redmine       | Make sure that `gem install rubyzip -v '1.2.1'` succeeds before bundling.
redmine       | 
redmine       | In Gemfile:
redmine       |   write_xlsx was resolved to 0.85.3, which depends on
redmine       |     zip-zip was resolved to 0.3, which depends on
redmine       |       rubyzip
redmine exited with code 5

gemは基本的に環境変数http_proxy, https_proxyを設定していれば問題ないはずだが、私の環境では期待動作とならなかった。

.gemrcファイルをRedmineコンテナに配備する

BundleとGemインストールでProxyの設定方法が異なるという情報を見た気がするので、設定方法を変更する。上記(とtcpdumpでの切り分け)より、パッケージ情報は取得できているためBundlerは問題なく、Gem installの際に期待するProxy動作となっていないと特定できる。

そのため、GemのProxy設定の一つである.gemrcをコンテナに配備した。

no_proxyの設定が誤っていた

原因はこちらにまとめた通り、no_proxyの設定誤りだった。

designetwork.hatenablog.com

(NG) no_proxy=,localhost,127.0.0.1
(OK) no_proxy=localhost,127.0.0.1

問題解消

上記no_proxyの修正により、プラグインで必要となるGemパッケージのインストールでも正常にProxy通信が可能となった。

まとめ - Proxy環境のDocker RedmineでGemインストールするための設定

Proxy環境において、no_proxyを設定する場合に、記載が誤っているとGemパッケージのダウンロードに失敗する。

no_proxyを適切に設定することで、Redmineプラグインで追加で必要となるGemパッケージについてもProxy経由でRubygemsからダウンロードすることができるようになった。