Mazn.net

やってみて 調べてみて 苦労しなけりゃ 箱は動かじ

IT技術

WSL2にDockerをインストール

Ubuntu同梱のDocker.io をインストールすると、service コマンドで起動できないので、Dockerコミュニティ版を使用します。root ユーザで以下コマンド叩きます。

# curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable"
# apt install -y docker-ce
# service docker start

動作確認

# docker info

WSL2を使って本物のLinux環境をWindowsで使う

Windowsには、WSL (Windows Subsystem for Linux)というLinux互換機能が随分前から実装されていましたが、これはLinuxの全機能が使えるわけではなく様々な制限がありました。そのため、低レイヤーにアクセスするDockerのようなコンテナ機能や各種ネットワーク制御系アプリケーション等が動作せず使い勝手の悪いものでした。しかし、WSL2はHyper-Vを使った仮想マシン上でLinuxの本物のカーネルが動くため、これら制約がなくなりましたので早速使ってみます。

管理者としてコマンドプロンプトを開き、WSL2を有効化します。

> dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart

展開イメージのサービスと管理ツール
バージョン: 10.0.19041.572
イメージのバージョン: 10.0.19042.630
機能を有効にしています
[==========================100.0%==========================]
操作は正常に完了しました。

仮想マシンを有効化します。

> dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

展開イメージのサービスと管理ツール
バージョン: 10.0.19041.572
イメージのバージョン: 10.0.19042.630
機能を有効にしています
[==========================100.0%==========================]
操作は正常に完了しました。

一旦Windowsを再起動します。

再起動完了したら、コントロールパネルから「プログラムと機能」を開き、「Windowsの機能の有効化または無効化」の画面で、「Linux用Windowsサブシステム」と「仮想マシンプラットフォーム」にチェックを入れます。

PowerShellを管理者として開きWSL2をデフォルトに設定します。

> wsl --set-default-version 2
WSL 2 との主な違いについては、https://aka.ms/wsl2 を参照してください

Microsoft Storeを開き、Linuxをインストールします。今回は Ubuntu 20.04 をインストール(入手)しました。ダウンロードサイズは444.5MBでした。

ダウンロードが終わると「起動」というボタンが表示されるので起動します。

初めての起動時は、このようにInstalling ・・という表示が出力されます。その後、ユーザ名やパスワードを聞かれるので適宜設定してください。

UbuntuがWSL2上で動いているかどうかは、PowerShell上で以下のコマンドを使って確認できます。VERSIONが2ならOKです。

> wsl --list --verbose
NAME STATE VERSION
Ubuntu-20.04 Running 2

あとは、Ubuntuの世界の話なので、sudo 使って適宜アップデートなりパッケージインストールをしましょう。

curlで取得したスクリプトに引数

インストール用シェルスクリプトをcurlでダウンロードし、そのままシェルに渡して実行することが増えてきました。

例えば、HELM をインストールする場合、以下のコマンドでインストールする方法が紹介されています。

$ curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash

上記スクリプトを実行すれば、最新版のHELMがインストールされますが、実は --version という引数を与えることで、バージョンを指定することができます。これを、上記のようにcurlでダウンロードして直接bashで実行したい場合、bash の -s オプションを使えばできます。

試しに、curl でダウンロードしたと想定して、「echo $1 $2」を実行するスクリプトに、123 と 456 を引数で与えると、このように引数の内容が表示されます。

$ echo 'echo $1 $2' | bash -s 123 456
123 456

なお、引数が "--version" のように "-" があると、bash の引数と理解されてしまうので、その場合は、"--" を -s の後ろにつけましょう。

$ echo 'echo $1 $2 $3' | bash -s -- --version 123
--version 123

Dockerネットワークで割り当てるIP範囲を絞る

docker network createで新しいコンテナ用のブリッジネットワークを作成する場合、コンテナに割り当てるIPの範囲を絞ることができます。

例えば、以下のように --subnet で 172.16.0.0/16 を指定し、--ip-range で 172.16.0.0/24 を指定すると、コンテナには172.16.0.1~255の範囲で割り当てられます。

# docker network create --subnet 172.16.0.0/16 --ip-range 172.16.0.0/24 --attachable -o "com.docker.network.driver.mtu=1450" my-nw

git pushでフリーズする

git clone や git pull はできるのに、git push するとフリーズしてしまうのは、環境変数が悪さしてる可能性があります。ASKPASS関連の環境変数が定義されている場合、Linuxなら以下のようにunsetしてあげると、うまくpushできるようになるかもしれません。お試しあれ。

# unset SSH_ASKPASS
# unset GIT_ASKPASS

sshログインしても何も操作をさせないぜ!

トンネリングを目的にsshを使う場合、ログイン後の操作を制限したい場合があります。これは、sshサーバ側のユーザのauthorized_keysに「command」という設定を書くことで実現できます。
※ authorized_keysを使うのでsshキーによるログインが前提の方法です。

具体的には、サーバ側の~/.ssh/authorized_keys に以下のように書かれているとします。

ssh-rsa AAAABYMfIAJF(ssh公開キー中略)0aAAn root@localhost

この行に以下のようにcommandという設定を先頭に書き足します。

command="echo 'locked'; tail -f /dev/null" ssh-rsa AAAABYMfIAJF (公開キー中略) 0aAAn root@localhost

この状態でsshログインすると、上記commandに指定されたコマンドが実行されるため、

locked

という文字が表示された後、"tail -f /dev/null" が実行され続けます。つまりユーザは何もできません。
※ tail -f /dev/null は、永遠に /dev/null から情報を読み続けるだけの意味のないコマンドで、別のコマンドでも構いません。

Docker with CentOS 8

CentOS 8(RHEL 8)にはDockerが同梱されなくなり、長い間DockerコミュニティからもCentOS 8向けのDockerがリリースされていなかったので、CentOS 8上でDockerを使うのが難しかったのですが、2020/9 頃にリリースされていましたので、インストールしてみました。

公式ドキュメントにも書かれていますが、インストール方法は簡単です。(CentOS 7と同じです)

# yum install -y yum-utils
# yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# yum install docker-ce docker-ce-cli containerd.io

以上です。

インストールしただけではDockerは起動しないので、systemctl で起動しましょう。

# systemctl start docker

ssh接続が切れたときの対処方法を考える

sshの接続はサーバダウンやNW故障等、様々な理由で切断されます。端末の目の前にいるなら手動で再接続すれば良いのですが、いない場合はどんな方法で復旧できるかをまとめてみました。

autossh

まず1つ目は、autossh を使用する方法です。RHEL系ならepelのリポジトリを使ってyumやdnf, Debian系ならaptでインストールできます。使い方はsshとほぼ同じですが、-M というオプションが必要です。以下はsshserverというホストにuserというユーザ名でポート22に接続しています。-M オプションは接続監視のオプションです。0を指定すると監視オプションは無効化されます。
※ 無効化してもTCPのセッションが切れた場合は再接続してくれます

# autossh -M 0 -p 22 user@sshserver

-M に 9000 といったポート番号を指定すると、端末とsshserver間で定期的に指定したポートを使って実際に通信ができるか定期的にチェックしてくれます。これは、autosshの “-M” オプションの正体 という記事がわかりやすいです。上記記事によると端末側とサーバ側で9000ポート、さらに端末側で9001(9000+1)ポートが使われていない必要があるようです。

autossh -M 9000 -p 22 user@sshserver

なお、自動で再接続をするには、sshキーを登録しておいてパスワードなしでログインできるようにしておいたり、sshキー登録が無理なら、テキストファイルにサーバ名やID,パスワードを書いておいて再接続時に自動で入力するようなソフトもあるようです。

mosh

正確にはsshではないですが、mosh を使うと端末のIPが変わったり、NWが切断されてもセッションを保つことができます。サーバ側にmoshサーバ、クライアントにmoshクライントが必要ですし、プロトコルにUDPを使うので、制約のある環境では使いにくいです。

これもRHEL系ならEPELリポジトリを有効にしてyumやdnf、Debian系ならaptでインストールできます。

moshクライアントは、Windows上ならcygwin 向けのコマンドや、MobaXterm といったクライアントソフトを使えば接続できます。

その他

sshクライアントが切断されたら自動で再接続するようなスクリプトを書くこともできると思います。Linux系ならsystemd 等を使ってプロセス停止時に自動でsshコマンドを再起動する方法なども考えられます。puttyなら、この辺りの記事が参考になると思います。他にも、Bitviseといったsshクライアントはソフトウェアとして再接続機能が実装されているようです。

Thunderbird で表示される絵文字を無効化

いつ頃からか、メールソフトのThunderbirdでメールを開くと、: ) のような文字が、😊 という絵文字で表示されるようになりました。この表示を元に戻す方法の紹介です。今回調査に使用したThunerbirdのバージョンは、68.12.0 です。

Thunderbird のメニューバーから、ツール⇒オプションを選びます。

さらに表示というメニューを選択すると、「顔文字をアイコンで表示する」というチェックボックスがあるので、このチェックを外すと、😊 が : ) という表示に戻りました。

以上です。

k8s Nginx Ingress ControllerでWAF

セキュリティを考慮したKubernetesのNginx Ingress ControllerのIngress設定例です。
※ これが完璧な設定というわけではなく、あくまで一設定例です。

難しい設定はspecに書けないので、以下のようにannotationsに書きます。server_tokens でNginxのバージョン情報を隠してます。

さらにModSecurityを用いてWAFを構築しています。WAFのルールにはOWASPを使いつつ、自分のルールも書けるようにしています。

tlsやrulesの設定は環境に合わせて適宜修正してください。

---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: my-ingress
  namespace: myapp
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/configuration-snippet: |
    server_tokens off;
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/enable-modsecurity: "true"
    nginx.ingress.kubernetes.io/modsecurity-transaction-id: "$request_id"
    nginx.ingress.kubernetes.io/modsecurity-snippet: |
      SecRuleEngine On
      SecRequestBodyAccess On
      SecAuditEngine RelevantOnly
      SecAuditLogParts ABIJDEFHKZ
      SecAuditLog /var/log/modsec_audit.log
      SecDebugLog /var/log/modsec_audit_debug.log
      Include /etc/nginx/owasp-modsecurity-crs/nginx-modsecurity.conf
spec:
  tls:
  - hosts:
    - example.com
    secretName: example-com-tls-secret
  rules:
  - host: example.com
    http:
      paths:
        - path: /
          backend:
            serviceName: my-service
            servicePort: 80

自分のルールを書きたい場合は、Include /etc/nginx/owasp-modsecurity-crs/nginx-modsecurity.conf の前後辺りに書くと良いでしょう。例えば、myconf.php へのアクセスを防ぎたいなら以下のような設定になると思います。

      Include /etc/nginx/owasp-modsecurity-crs/nginx-modsecurity.conf
      SecRule REQUEST_URI "myconf.php" "id:500001,phase:2,deny,log,status:405"

IPアドレス計算に使えるLinuxコマンド ipcount ipcalc sipcalc

以前、コマンドラインでIP計算に使えるコマンドとして、perl-Net-IPというパッケージに入ってる ipcountを紹介しました。これ以外にも、ipcalc や sipcalc というIP計算に使えるコマンドがあるので紹介します。
※ 環境にコマンドが入ってない場合は、apt や dnf, yum 等のコマンドで簡単にインストールできると思います。

まず最初に、ipcount, ipcalc, sipcalc の使い分けですが、ざっくり書くとこのような感じです。

  • ipcount
    • IPの範囲 (最初のIPと最後のIP) に関する計算ができます。範囲からホストアドレスやプレフィックスを計算したり、その範囲のIPの数などが簡単に分かります。
  • ipcalc
    • 与えられたIPの情報を表示できます。また、IP の表記が間違ってないかのチェックや、IPからホスト名の逆引きなどもできます。
  • sipcalc
    • ipcalcの高機能版と言われてますが、どちからというとipcalcよりIPの詳細情報を表示するコマンドというイメージで、IPの表記を2, 10, 16進数に変換することができます。ipcalcと互換性はなく、ipcalcでできるホスト名逆引きなどはできません。

ipcount

ネットワークアドレスとプレフィックスを指定すると、IPの範囲が分かります。

# 192.168.0/24 192.168.0.0 - 192.168.0.255 [256]ipcount 192.168.0.0/24
192.168.0/24 192.168.0.0 - 192.168.0.255 [256]

IPの範囲を以下のようにハイフンを挟んで指定すると、逆にネットワークアドレスやプレフィックスが分かります。

# ipcount 192.168.0.0 - 192.168.0.255
192.168.0/24 192.168.0.0 - 192.168.0.255 [256]

一つのサブネットで表現できない場合、複数のネットワークに分けて表示されます。

# ipcount 192.168.0.200 - 192.168.0.255
192.168.0.200/29 192.168.0.200 - 192.168.0.207 [8]
192.168.0.208/28 192.168.0.208 - 192.168.0.223 [16]
192.168.0.224/27 192.168.0.224 - 192.168.0.255 [32]
192.168.0.200/29,/28,/27 192.168.0.200 - 192.168.0.255 [56]

ちなみに、プレフィックスの代わりにサブネットマスク(255.255.255.0)は指定できないようです。サブネットマスクへの読み替えには、iptab コマンドを使いましょう。

# iptab
+----------------------------------------------+
| addrs   bits   pref   class  mask            |
+----------------------------------------------+
|     1      0    /32          255.255.255.255 |
|     2      1    /31          255.255.255.254 |
|     4      2    /30          255.255.255.252 |
|     8      3    /29          255.255.255.248 |
|    16      4    /28          255.255.255.240 |
|    32      5    /27          255.255.255.224 |
|    64      6    /26          255.255.255.192 |
|   128      7    /25          255.255.255.128 |
|   256      8    /24      1C  255.255.255.0   |
|   512      9    /23      2C  255.255.254.0   |
|    1K     10    /22      4C  255.255.252.0   |
|    2K     11    /21      8C  255.255.248.0   |
|    4K     12    /20     16C  255.255.240.0   |
|    8K     13    /19     32C  255.255.224.0   |
|   16K     14    /18     64C  255.255.192.0   |
|   32K     15    /17    128C  255.255.128.0   |
|   64K     16    /16      1B  255.255.0.0     |
|  128K     17    /15      2B  255.254.0.0     |
|  256K     18    /14      4B  255.252.0.0     |
|  512K     19    /13      8B  255.248.0.0     |
|    1M     20    /12     16B  255.240.0.0     |
|    2M     21    /11     32B  255.224.0.0     |
|    4M     22    /10     64B  255.192.0.0     |
|    8M     23     /9    128B  255.128.0.0     |
|   16M     24     /8      1A  255.0.0.0       |
|   32M     25     /7      2A  254.0.0.0       |
|   64M     26     /6      4A  252.0.0.0       |
|  128M     27     /5      8A  248.0.0.0       |
|  256M     28     /4     16A  240.0.0.0       |
|  512M     29     /3     32A  224.0.0.0       |
| 1024M     30     /2     64A  192.0.0.0       |
| 2048M     31     /1    128A  128.0.0.0       |
| 4096M     32     /0    256A  0.0.0.0         |
+----------------------------------------------+

ipcalc , sipcalc

ipcalcでIPを指定すると、そのIPのクラスとプライベートアドレスなのかどうかを表示します。--all-info オプションを付けると、さらにIPを保有している国や、大まかな位置情報なども表示されます。

sipcalcの場合、IPを指定すると主にIPの別進数(10,16表記が表示される。--all オプションを付けると、2進数表記も表示されるほか、IPのクラス等の情報が表示される。

ipcalcでIPを指定

# ipcalc 8.8.8.8
Address: 8.8.8.8
Address space: Internet
Address class: Class A











ipcalc に--all-info オプションを付与

# ipcalc 8.8.8.8 --all-info
Address: 8.8.8.8
Reverse DNS: 8.8.8.8.in-addr.arpa.
Address space: Internet
Address class: Class A

Country code: US
Country: United States
Coordinates: 37.751000,-97.822000

sipcalcでIPを指定

# sipcalc 8.8.8.8
-[ipv4 : 8.8.8.8] - 0
[CIDR]
Host address - 8.8.8.8
Host address (decimal) - 134744072
Host address (hex) - 8080808
Network address - 8.8.8.8
Network mask - 255.255.255.255
Network mask (bits) - 32
Network mask (hex) - FFFFFFFF
Broadcast address - 8.8.8.8
Cisco wildcard - 0.0.0.0
Addresses in network - 1
Network range - 8.8.8.8 - 8.8.8.8


sipcalc--allオプションを付与

# sipcalc 8.8.8.8 --all
-[ipv4 : 8.8.8.8] - 0
[Classful]
Host address - 8.8.8.8
Host address (decimal) - 134744072
Host address (hex) - 8080808
Network address - 8.0.0.0
Network class - A
Network mask - 255.0.0.0
Network mask (hex) - FF000000
Broadcast address - 8.255.255.255
[CIDR]
Host address - 8.8.8.8
Host address (decimal) - 134744072
Host address (hex) - 8080808
Network address - 8.8.8.8
Network mask - 255.255.255.255
Network mask (bits) - 32
Network mask (hex) - FFFFFFFF
Broadcast address - 8.8.8.8
Cisco wildcard - 0.0.0.0
Addresses in network - 1
Network range - 8.8.8.8 - 8.8.8.8
[Classful bitmaps]
Network address - 00001000.00000000.00000000.00000000
Network mask - 11111111.00000000.00000000.00000000
[CIDR bitmaps]
Host address - 00001000.00001000.00001000.00001000
Network address - 00001000.00001000.00001000.00001000
Network mask - 11111111.11111111.11111111.11111111
Broadcast address - 00001000.00001000.00001000.00001000
Cisco wildcard - 00000000.00000000.00000000.00000000
Network range - 00001000.00001000.00001000.00001000 -
00001000.00001000.00001000.00001000
[Networks]
Network - 8.8.8.0 - 8.8.8.0
Network - 8.8.8.1 - 8.8.8.1
Network - 8.8.8.2 - 8.8.8.2
Network - 8.8.8.3 - 8.8.8.3
~省略~

プレフィックスを付けると以下の表示になります。青字がプレフィックスを付けない時との差分です。両コマンドとも、IPの範囲や数が表示されるようになります。

ipcalcでIPをプレフィックス付きで指定

# ipcalc 8.8.8.8/24
Address: 8.8.8.8
Network: 8.8.8.0/24
Netmask: 255.255.255.0 = 24
Broadcast: 8.8.8.255

Address space: Internet
Address class: Class A
HostMin: 8.8.8.1
HostMax: 8.8.8.254
Hosts/Net: 254




ipcalcはサブネットマスクを指定することもできます。

※ ipcalc 8.8.8.8 255.255.255.0
(表示は同じ)

sipcalcでIPをプレフィックス付きで指定

# sipcalc 8.8.8.8/24
-[ipv4 : 8.8.8.8/24] - 0
[CIDR]
Host address - 8.8.8.8
Host address (decimal) - 134744072
Host address (hex) - 8080808
Network address - 8.8.8.0
Network mask - 255.255.255.0
Network mask (bits) - 24
Network mask (hex) - FFFFFF00
Broadcast address - 8.8.8.255
Cisco wildcard - 0.0.0.255
Addresses in network - 256
Network range - 8.8.8.0 - 8.8.8.255
Usable range - 8.8.8.1 - 8.8.8.254

sipcalcもサブネットマスクを指定することもできます。

# sipcalc 8.8.8.8 255.255.255.0
(表示は同じ)

ipcalcはDNSへの問い合わせもできます。IPからホスト名(dig -x コマンド相当)を確認します。なお、sipcalc には同様の機能は無いようです。

ipcalcでホスト名を確認

# ipcalc -h 8.8.8.8
HOSTNAME=dns.google

他にも、ipcalcでは、IPの表記が間違ってないか確認したり、サブネットマスクからプレフィックスの値だけを表示したりすることもできます。

ipcalcでIP表記やプレフィックス付きのIPが有効な表記か確認

# ipcalc -c 8.8.8.8
# echo $?
0   ※ 有効なアドレスなのでコマンドの戻り値が0

# ipcalc -c 8.8.8.300
ipcalc: bad IPv4 address: 8.8.8.300
# echo $?
1   ※ 無効なアドレスなのでコマンドの戻り値が1

# ipcalc -c 8.8.8.8/24
# echo $?
0   ※ 有効なアドレスなのでコマンドの戻り値が0

# ipcalc -c 8.8.8.8/99
ipcalc: bad IPv4 prefix: 99
# echo $?
1   ※ 無効なアドレスなのでコマンドの戻り値が1

ipcalcでサブネットのプレフィックス値を確認

# ipcalc --prefix 8.8.8.8 255.255.255.0
PREFIX=24

Dockerコンテナイメージファイル(tar)からファイル抽出

コンテナのイメージファイル(tar)から、Dockerを使わずにファイルを抽出する方法の紹介です。

まずは、以下のpythonスクリプトを書きます。
※ 1行目の #!/usr/bin/python3 は自身のpython3のパスに適宜読み替えてください。

docker-image-extract.py

#!/usr/bin/python3

import tarfile
import json
import os
import sys

image_path = sys.argv[1]
extracted_path = sys.argv[2]

image = tarfile.open(image_path)
manifest = json.loads(image.extractfile('manifest.json').read())

for layer in manifest[0]['Layers']:
    print('Found layer: %s' % layer)
    layer_tar = tarfile.open(fileobj=image.extractfile(layer))

    for tarinfo in layer_tar:
        print('  ... %s' % tarinfo.name)
        if tarinfo.isdev():
            print('  --> skip device files')
            continue

        dest = os.path.join(extracted_path, tarinfo.name)
        if not tarinfo.isdir() and os.path.exists(dest):
            print('  --> remove old version of file')
            os.unlink(dest)

        layer_tar.extract(tarinfo, path=extracted_path)

busyboxのコンテナイメージファイル busybox.tar の中身を busybox-image-files ディレクトリ名配下に抽出します。以下のようにコンテナ内の全ファイルが抽出されます。

# ./docker-image-extract.py busybox.tar busybox-image-files
Found layer: df8698476c65c2ee7ca0e9dbc2b1c8b1c91bce555819a9aaab724ac64241ba67.tar.gz
… bin
… bin/[
… bin/[[
… bin/acpid
… bin/add-shell
… bin/addgroup
… bin/adduser
… bin/adjtimex
… bin/ar
… bin/arch
~省略~
… var
… var/spool
… var/spool/mail
… var/www

このような感じでファイルが抽出されました。

# ls -l busybox-image-files/
total 40
drwxr-xr-x 2 root root 12288 Sep 25 22:31 bin
drwxr-xr-x 2 root root 4096 Sep 9 03:09 dev
drwxr-xr-x 3 root root 4096 Sep 25 22:31 etc
drwxr-xr-x 2 nobody nobody 4096 Sep 9 03:09 home
drwx------ 2 root root 4096 Sep 9 03:09 root
drwxrwxrwt 2 root root 4096 Sep 9 03:09 tmp
drwxr-xr-x 3 root root 4096 Sep 25 22:31 usr
drwxr-xr-x 4 root root 4096 Sep 25 22:31 var

参考 : https://www.madebymikal.com/quick-hack-extracting-the-contents-of-a-docker-image-to-disk/

craneコマンドでDockerイメージをダウンロード・アップロード

以前、Dockerのコンテナイメージダウンロードに graboid というツールを紹介したのですが、 今回はアップロード (docker push相当) もできるgoogleが開発しているツール crane を紹介します。

インストール

craneはGO製のコマンドラインツールで、go-containerregistry というプロジェクトの一部です。craneコマンド以外にもgcraneやk8schainといったコマンドが提供されています。今回は、tar.gz ファイルダウンロード後、craneファイルだけを取り出して、/user/local/bin に配置します。
※ Windows版は提供されていないので、今回はLinux環境が前提です。

# curl -L https://github.com/google/go-containerregistry/releases/download/v0.1.3/go-containerregistry_Linux_x86_64.tar.gz -o /tmp/go-containerregistry.tar.gz
# tar zxvf /tmp/go-containerregistry.tar.gz -O crane > /usr/local/bin/crane
# chmod 755 /usr/local/bin/crane
# rm -f /tmp/go-containerregistry.tar.gz

コンテナイメージのダウンロード (pull)

ダウンロード(pull)は、コンテナイメージのURLと保存先ファイル名(tar)を指定するだけです。DockerHub上のbusyboxコンテナをダウンロードしてみます。DockerHubにログインしてないため、No matching credentials were found というワーニングがでますが、無視して構いません。

# crane pull busybox busybox.tar
2020/09/25 08:01:42 No matching credentials were found, falling back on anonymous
# ls
busybox.tar

コンテナイメージのアップロード (push)

アップロード(push)も、認証が特にかかっていないレジストリの場合は、以下のようにtarファイル名とアップロード先URL(ここではmyregistry:5000というサーバを想定)を指定します。pullと引数が逆なので注意してください。

# crane push busybox.tar myregistry:5000/busybox:latest

認証は cran auth login サーバURL -u ユーザ名 -p パスワード コマンドできます。なお、パスワードをコマンドラインで対話的に入力する機能はないようです。パスワードを直接指定してDockerHubにログインしてみます。

# crane auth login docker.io -u myusername -p mypassword

コマンドラインに直接パスワードを入力したくない場合、-pオプションの代わりに--password-stdinオプションを使って、標準入力からパスワードを受け取ることができます。pass.txt というテキストファイルにパスワードを書いて、以下のようにログインできます。

# cat pass.txt | crane auth login docker.io -u myusername --password-stdin

なお、ログインした場合 ~/.docker/config.json に認証情報が保存されます。つまり、Dockerがインストールされている環境で、docker login を実行している場合、新たに crane コマンドでログインする必要はありません。

ログイン後のpushは最初に紹介した方法と同じです。DockerHub だとpush先のパスにユーザ名が必要なので、以下のようなコマンドでpushできます。

# crane push busybox.tar myusername/busybox:latest

Dockerで日本語入力対応Linuxデスクトップ環境

Dockerを使うと様々なアプリを簡単にhttps://github.com/Rosyuku/ubuntu-rdp起動できますが、基本的にCUIでの操作やWeb経由でのアクセスがメインだと思います。しかし、Linuxデスクトップ環境をコンテナ使って構築できると便利だなと思い探してみました。

調査に使ったコンテナ実行環境は、Docker 19.03 on CentOS 7で、アクセスに使った環境は Chrome on Windows 10です。

docker-ubuntu-vnc-desktop ⇒ 日本語未対応

まずはじめに試したのが、docker-ubuntu-vnc-desktop というコンテナ。名前にはVNCと入ってますが、VNCで接続するものではなくブラウザから接続します。

Quick Startに書かれている通り、以下のコマンドで起動しました。-pでマッピングするポートは適宜読み替えてください。本記事では基本的に8080ポートを使います。

# docker run -p 8080:80 -v /dev/shm:/dev/shm dorowu/ubuntu-desktop-lxde-vnc

ブラウザから、http://ホストのIP:8080ポートにアクセスすると以下のようにデスクトップが表示されました。

デフォルトで、ターミナルやブラウザ(Firefox, Chrome)が入っていましたが、Chromeは私の環境ではなぜか起動しませんでした。

これを最初に起動して思ったのが、日本語入力環境がないのが不便で、コンテナ起動する度に日本語設定するのは面倒だなということです。ということで、以降では日本語に対応した Linuxデスクトップ環境を探してまとめてみました。

docker-xrdp ⇒ 画面真っ黒で表示されず

次に試したのが、docker-xrdp というコンテナです。Qiita で「Dockerで日本語入力可能なubuntu 20.04イメージを作りました (xrdp)」という記事で紹介されている通り、デフォルトで日本語入力可能を謳っています。xrdp と書かれているとおり、接続にはリモートデスクトップを使います。

Qiitaの記事によると、--shm-size オプション使わないとFirefoxがクラッシュするらしいので、これを付与して起動します。パスワードは指定しないと自動生成されるらしいですが、ここでは「hogehoge」を指定しました。

# docker run -p 8080:3389 --shm-size=1g -e PASSWD=hogehoge tukiyo3/xrdp:core

リモートデスクトップからサーバの8080ポートに接続すると、以下のxrdpのログイン画面が現れました。

デフォルトのユーザ名は、「focal」で、パスワードには起動時に指定したhogehogeを入力するとログインは成功しマウスカーソルが表示されます。画面上で右クリックすると以下のようなメニューが出てくるのですが、画面全体がなぜか真っ黒でタスクバーなどが表示されず、まともなデスクトップが表示されませんでした・・・

ubuntu-desktop-jp ⇒ 日本語入力できず

次が、「日本人向けのUbuntuデスクトップ環境」を謳っている ubuntu-desktop-jp です。こちらは最初に紹介したdocker-ubuntu-vnc-desktopのように、ブラウザ経由で使うコンテナのようです。

GitHubに書かれているコマンドで起動してみます。

# docker run --rm -p 8080:8080 uphy/ubuntu-desktop-jp:18.04

ブラウザから、8080ポートにアクセスすると以下の画面が表示されました。

デフォルトの日本語入力ONのキーは、「Super + Space」になっていますが、Windows環境で Windowsキー + Space を押すと、Windowsの日本語入力選択画面が表示されてしまい、Linuxデスクトップ側の日本語入力をONにすることができませんでした。よって、IBus Preferences の設定を開き、<Super>space を <Shift>space に変更してみました。

この状態で、ターミナルを開き Shift + Spaceを押すと、日本語入力切替画面が出てくるようになりましたが、なぜか日本語入力できません・・・

右上のアイコンから「ひらがな」を選んでもやっぱり無理。ちなみにブラウザも開きませんでした。

openbox-mozc-docker ⇒ 日本語入力できず

次に試したのが、openbox-mozc-docker です。日本語入力ができるシンプルなデスクトップ環境のようで、接続はVNCです。

ビルド済みのコンテナがないので、Dockerfileからビルドする必要があります。(数分要します)

# git clone https://github.com/hiroshi-nishiura/openbox-mozc-docker.git
# cd openbox-mozc-docker/
# docker build -t openbox-mozc-docker .

ビルドが完了したら、起動します。

# docker run --rm -it -p 8080:5900 openbox-mozc-docker xvnc

VNC Viewerを使って8080ポートに接続すると、背景が真っ黒な超シンプルな画面が表示されました。(画面下にタスクバーは表示されています)

デフォルトの日本語入力はCtrl + Spaceになっていますので、ブラウザを起動してCtrl + Spaceを押してみましたが、反応がありません。どうやら、Input Methodが設定されていないようです・・・。GitHubに書いてるようにEmacs以外については、日本語入力できるように設定されていないのかもしれません。

yama07/docker-ubuntu-lxde ⇒ 日本語入力可。シンプル。起動時エラーあり

次は、docker-ubuntu-lxde です。Ubuntu + LXDEをベースの日本語入力対応を謳ってます。接続にはリモートデスクトップを使います

まずは、GitHubのREADMEに何も書かれてないので、DockerHub のドキュメントを見ながら起動してみます。「rootユーザとして起動した場合は、日本語入力(mozc)が利用できません。」とあるので、-u には別ユーザIDとグループIDを指定しています。--privileged も付けておいた方がよいとのことなので、つけてます。

# docker run --privileged --rm -it -p 8080:3389  -u 1000:1000  -e USER=test -e PASSWD=hogehoge yama07/docker-ubuntu-lxde:ubuntu18.04_ja

リモートデスクトップで、8080ポートに接続すると、先程と同じくxrdpの見慣れたログイン画面が出てきます。

起動時に指定した、ユーザ test, パスワード hogehoge でログインすると、DockerHubのドキュメントみたいな背景ではなく、真っ黒なシンプルなLXDEの画面が出てきました。

しかし、ログイン時に以下のエラー画面が出て、コンテナのログにも以下のエラーが出力されました。

xrdp-sesman[42]: pam_systemd(xrdp-sesman:session): Failed to create session: Input/output error

デフォルトの日本語入力切替が Super + Space になっているのですが、ONにならなかったので、ubuntu-desktop-jp の時と同じように、IBus Preferencesの設定画面を開いて、Shift + Space に変更しました。

この状態で、ターミナルを起動して、Shift + Space を押すと、日本語切替の画面が以下のように出てきましたが、「あ」を選択しても日本語入力できません。

しかし、画面右下のアイコンから、入力モードを手動で「ひらがな」に変更すると、無事日本語入力ができました。

ちなみに、ブラウザ(Firefox)も起動できました。

rosyuku/ubuntu-rdp ⇒ 日本語入力可。シンプル

ubuntu-rdp は、リモートデスクトップとSSH接続ができる、日本語入力対応のコンテナと謳われています。

起動してみます。

# docker run --rm -it -p 8080:3389 -p 10022:22 --shm-size=256m rosyuku/ubuntu-rdp:0.1.2

また、xrdp のログイン画面が出てくるので、デフォルトのIDとパスワード my-ubuntu / my-Password でログインします。

画面は、ubuntu-desktop-jp と同じですが、デスクトップのアイコンは日本語化されていませんでした。

上と同じく、IBus Preferencesの設定画面を開いて、Shift + Space に変更します。

ターミナルを開いて、Shift + Space 入力をONにします。

yama07/docker-ubuntu-lxde 同様に、ONにしただけでは日本語が入力できなかったので、画面右上から「ひらがな」を選択すると、無事入力できました。

ちなみにブラウザ(Firefox)も起動でき、日本語入力できました。

docker-centos-xfce-ja ⇒ 日本語入力可。CentOSベース。

最後が、docker-centos-xfce-ja です。今まで紹介してきたコンテナは全てUbuntuベースでしたが、こちらはCentOSベースで、ブラウザ経由でもリモートデスクトップでもアクセスできるようです。さらに、SSHやブラウザ経由でターミナルにアクセスできたり、ファイル管理(アップロード・ダウンロードも可)もできたり、ブラウザ版VSCodeも使えたり、デフォルトでHTTPSで暗号化できたりと、かなり機能盛りだくさんです。

起動してみます。ポート8080がWebアクセス(https)用、22がssh用、3389がリモートデスクトップ用になっているようなので、それぞれを-pでマッピングしています。

# docker run -d -p 8080:8080 -p 10022:22 -p 8081:3389 -e PASSWORD=hogehoge --name centos-xfce-ja --shm-size=2g tmatsuo/centos-xfce-ja:1.2

※ 機能豊富な分イメージがでかいです。

まずは、ブラウザでアクセスしてみます。機能盛りだくさんのため、機能毎にアクセスするパスが決まっているようで、Linuxデスクトップにアクセスしたい場合、
    https://サーバIP:8080/desktop/
にアクセスします。オレオレ証明書のため証明書エラーが出ますが、エラーを無視して接続すると、ベーシック認証画面がでてきます。

ドキュメントによると、Nginxがフロント(リバースProxy)にいて、PAMを使って認証しているようです。上記ベーシック認証画面で、IDにroot, パスワードは起動時に指定したパスワード hogehoge を入力すると、以下の画面が表示されました。

日本語入力ONは、Shift + Spaceに設定されていると書かれているので、ターミナルを起動して、Shift + Spaceを押すとONにできました。

日本語入力もデフォルトの設定で問題なくできます。今までのコンテナ中で一番手軽です。

リモートデスクトップでも繋いでみます。起動時に指定した8081ポートに繋いでみると、先程ブラウザで起動したターミナルが見えます。ブラウザとリモートデスクトップで同じ画面が表示されるようです。

ついでに他の機能にもアクセスしてみました。ブラウザから
    https://サーバIP:8080/term/
にアクセスすると、ログインプロンプトが出てき、先程のIDとパスワードでログインできました。

ブラウザから
    https://サーバIP:8080/file/
にアクセスすると、以下のようにファイルブラウザが表示されました。操作端末との間でファイルやりとりするのも簡単そうです。

ブラウザから
    https://サーバIP:8080/code/
にアクセスすると、以下のようにVS Codeの画面が表示されました。ここまでお膳立てされていると、ちょっとした開発ならすぐにでも開始できそうです。

まとめ

いろいろなコンテナを起動してみましたが、日本語対応を謳っていても実際はうまく起動できなかったり日本語入力できなかったりするものが多い印象です。今回はあまり深くは触っていませんが、

シンプルなUbuntuベースのLinuxデスクトップ ⇒ rosyuku/ubuntu-rdp

高機能なCentOSベースのLinuxデスクトップ ⇒ docker-centos-xfce-ja

の二択かなと思いました。

legoコマンドでLet's Encrypt

Let's EncryptのTLS(https)証明書を取得するのにcertbotというツール(コマンド)がよく使われますが、今回は lego というツールを紹介します。

GO製ツールで実行ファイルはシングルバイナリファイルのため、インストールも簡単です。GitHubのリリースページから対象のOSやアーキテクチャのtar.gzファイルをダウンロードし、展開して出てくるlegoファイルを、Linuxなら/usr/local/bin/辺り(PATHが通ったディレクトリ)に放り込むだけです。

すでにWebサーバが構築されており、そのサーバにドメインが割り当てられている場合、以下のようなコマンドで証明書を取得できます。

# lego --accept-tos --http --http.webroot /var/www/html/ --domains example.com --path /etc/lego/ --email [email protected] run

/var/www/html が、Webサーバのドキュメントルートです。example.com がサーバのドメイン名で、[email protected] がメールアドレスです。取得した証明書は、--path で指定した/etc/legoに保存されます。各引数の値は、適宜環境にあわせて修正してください。

実行すると、上記で指定した/var/www/html ディレクトリに、.well-known というディレクトリが作成され、Let's Encrypt がドメインの所有者有無を確認するためのファイルが配置されます。

証明書保存先の/etc/lego/ には、certificates というディレクトリが作成され、ドメイン名が付与されたファイルが4つ作成されます。

# ls /etc/lego/certificates/
/etc/lego/certificates/example.com.crt
/etc/lego/certificates/example.com.json
/etc/lego/certificates/example.com.crt
/etc/lego/certificates/example.com.key

あとは、自身のWebサーバで上記証明書を指定すれば、Let's Encryptの証明書が使えるようになります。

Let's Encryptの証明書は3ヶ月で期限が切れるため、定期的に更新が必要になります。証明書の更新は、runの代わりにrenewを使います。

# lego --accept-tos --http --http.webroot /var/www/html --domains example.com --path /etc/lego/ --email [email protected] renew --days 30 --renew-hook /usr/local/bin/restart-server.sh

更新を頻繁に実行すると制限にひっかかってしまいますので、上記では--days 30を指定して、証明書の期限が30日を切った場合に更新処理が実行されるように設定しています。--renew-hook では、更新後に実行するスクリプトを指定します。ここでは/usr/local/bin/restart-server.sh(予め作成)を指定し、Webサーバを再起動しています。この処理を1ヶ月に1回ぐらいcron等で実行してあげれば、自動で証明書の更新も可能です。

コンテナを使ってWebサイトの脆弱性スキャン

とりあえず、サイトの脆弱性調査のためにフルスキャンしたい場合、Dockerがインストールされている環境で以下のコマンド叩けばできます。
※ 他人のサイトに向けて以下のスキャンを試すと、不正アクセスで訴えられる可能性があるため絶対に行わないでください。

# docker run -t owasp/zap2docker-stable zap-full-scan.py -t https://{スキャンしたいURL} 2>&1 | tee -a scanlog.txt

なお、フルスキャンはかなり時間がかかります。(数時間ぐらい?)
結果は tee を用いているので、画面に表示されつつ scanlog.txt にも保存されます。

Podmanでコンテナのログをjournaldへ転送

RHEL8 (CentOS8)からは、Dockerが同梱されておらず、代わりにPodmanが提供されています。PodmanはDockerのようにデーモンがおらず、機能もシンプルです。ここでは、Podmanで起動したコンテナのログの保存方法について紹介します。

podman run コマンドで起動したコンテナのログは、通常はpodmanの管理領域に保存され、コンテナを削除すると削除されてしまいます。これだと、実際の運用では古いログが参照できなくて困ってしまいます。これを避けるには、ログドライバーをコンテナ起動時に指定します。

ここでは、CentOS 8のPodmanを前提に書きますが、以下のように、--log-driver journald というオプションを付けると、コンテナのログがjournaldに出力されるようになります。

# podman run -d --log-driver journald nginx

CentOS 8では、journald に出力されたログは、デフォルトでrsyslogを経由して/var/log/messagesに保存されるため、これによりコンテナのログを永続化することができるようになります。Dockerだと、ログ設定をデフォルトの設定で変更できるようですが、Podmanではそのような設定ファイルがないようで、podman run 時に毎回付与する必要がありそうです。

なお、Dockerの場合、--name nginx のようなオプションを付けると、journaldのログのメタデータにCONTAINER_NAME=nginx といった情報が付与されるため、journalctl -o json コマンドで見るとどのコンテナのログか簡単に判別できますし、rsyslogのフィルタ設定によってコンテナ名でログファイルを分けることができるのですが、2020年9月現在のCentOS 8のpodman はこのメタデータが付与されませんでした。この不具合は、GitHubのこちらのIssueで修正されたようなので、そのうち正常に本メタデータも保存されるようになりそうです。

このブログについて
プライバシーポリシー・お問い合わせ等
購読する(RSS)
記事検索
アーカイブ
カテゴリー
  • ライブドアブログ