それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

マルチコアのCPUを使い切って圧縮を速くする

gzip の限界 = CPU 1コア

マルチコア・マルチスレッドのCPUがあるのに、gziplzma(xz)や bzipといったメジャーな圧縮は、CPUを1コアで処理するんですね。

CPU使用率を見てみたら、CPU利用率は100%を超えないんですね。

HDD・SSDの書き込み速度に限界があるからそれでも良かったんだろうが。いまはメモリが一般的に64GBもある時代です。うちのマシンでもメモリが12GBもあるのに3GB程度の圧縮に、5分とか耐えられません。もうちょっと速くしたい。

cpu利用率が100%で頭打ちになる。gzip

gzipを使ってると、CPU利用率が100%で止まるんですよね。lzma などの他の圧縮でも同じ。

f:id:takuya_1st:20210413030211p:plain

gzip/ gunzip をマルチで処理する pigz / unpigz

Pigz のマニュアルには次のように書いてある。スレッドを使って並列処理をするっぽい。

Pigz  compresses using threads to make use of multiple processors and cores.  The in‐
       put is broken up into 128 KB chunks with each compressed in parallel.  The individual
       check  value  for  each chunk is also calculated in parallel.  The compressed data is
       written in order to the output, and a combined check value is calculated from the in‐
       dividual check values.

実際にやってみると

cpu の利用率が、グンと上がる。CPUが余ってるなら、圧縮でもCPUをフルに使ったほうが良さそう。

f:id:takuya_1st:20210413031647p:plain

インストール

gzip の場合

sudo apt install pigz

bzip の場合

sudo apt install pbzip2 

xzの場合

sudo apt install pixz

まとめてインストールできない感じなのが辛い。piXXだとか pbzip2 とか表記が揺れているのも辛い。

詳しくは、参考資料に上げたURLに記載されているコマンドを必要に応じてインストールする必要がある。

7z でも マルチスレッド対応らしいが、time 7z a -mmt=on sample.gz ./sample を見た感じだと、gzipなどはマルチコア・マルチスレッドを使わないようだ。

実行速度比較

使用したCPU

takuya@livaz:~$ 7z

7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,
64 bits,4 CPUs Intel(R) Pentium(R) CPU N4200 @ 1.10GHz (506C9),ASM,AES-NI)

使用したファイル

takuya@livaz:/var/lib/libvirt/images$ ll -h sample
-rw-r--r-- 1 takuya takuya 3.6G Apr 13 02:04 sample

gzip での速度

takuya@livaz:/var/lib/libvirt/images$ time gzip sample

real    4m10.198s
user    3m58.115s
sys     0m5.359s

pigz での速度

takuya@livaz:/var/lib/libvirt/images$ time pigz sample

real    1m38.232s
user    4m56.837s
sys     0m9.070s

比較

real で比較

コマンド real user
gzip 4m10.198s 3m58.115s
pigz 1m38.232s 4m56.837s

CPUを4スレッド使うことで、ちゃんと約4倍の時間を稼げていることがわかる。もちろん分散のオーバーヘッドが入るので、完璧に1/4にはならないのですが。

まとめ

CPUが余ってるなら マルチスレッドをCPUで使い切ったほうが絶対早い。

参考資料

OPNSense でAPIを使う。

opnsense でAPIを使う。

APIを使うには、キーが必要

APIのリクエストのエンドポイント(OPNSenseのアドレス)

APIのリクエストのパス(ドキュメントから)

キーの作成

APIの認証キーの作成は、管理画面から行える。

ユーザーをクリック、編集、APIキー作成を選んで作成。作成するとキーファイルがダウンロードされる。

ユーザを選んで

f:id:takuya_1st:20210412173639p:plain

キーを作成。

f:id:takuya_1st:20210412173703p:plain

ファイルがダウンロードされる。

f:id:takuya_1st:20210412173724p:plain

かんたんにリクエストを投げてみる。

作成されたファイルは、イコールで結ばれたキーバリューなのでそのままソースコードBashに貼り付けて使える。

key=XXX
secret=XXX

これを curl コマンドとして作成。

f:id:takuya_1st:20210412173818p:plain

あとはリクエストを投げるだけ。

サンプルで投げたAPIリクエストは、 ファームウェアの更新状況が完了しているか見るもので、マニュアルはここ ( https://docs.opnsense.org/development/api/core/firmware.html ) にある。

設定をAPIでバックアップする

たとえば、設定をAPI経由でバックアップするのは、システムのファームウェアから、os-api-backup のパッケージを追加する。 すると、API経由でXMLがダウンロードできるようになる。

パッケージインストール

f:id:takuya_1st:20210412174507p:plain

リクエス

key=XXXX
secret=XXXX
host=192.168.1.1
curl -k -u $key:$secret https://${host}/api/backup/backup/download -o config.$(date -I).xml

これで設定のバックアップは自動化できる。pfsense/OPNsenseは良く出来てるよなぁ。

参考資料

Firmware — OPNsense documentation

https://forum.opnsense.org/index.php?topic=18218.0

SPFからGoogleのSMTPのIPアドレスをぱぱっと調べる。

Google GMAILSMTPIPアドレス範囲

以前、Google workspaceで のGmailに存在しないアドレスはローカルのSMTPサーバーで処理するように、メールのルーティングルールを書いたわけですが。

ルールをかいたので、ローカルのSMTPサーバーへは、Gmailからメールが転送されてくるわけです。

しかし、ローカルの「ポート25」を全部開放すると大変なことになるわけです。もちろんPostfixのフィルタやスパムフィルタで処理もしているのですが、やっぱり スパムはとんでもない量が飛んでくるために、Postfixが詰まってしまうし、スパムフィルタでCPU使用率も跳ね上がるわけです。

そこで、IPアドレス範囲を絞ってGmailSMTPリレー以外をドロップするようにFWに記述し、port 25 のアクセスをSPFベースで絞ったほうがマシであろうと思われるわけです。

Google IP address ranges for outbound mail servers - Google Workspace Admin Help

SFP を見てみる。

gmailSPFのTXTレコードは次のようになっている。

$ dig txt gmail.com +short | grep spf
"v=spf1 redirect=_spf.google.com"

gmailspf 一覧は _spf.google.com に書かれているとわかる。

SFPレコードからinclude を取り出す。

では、_spf.google.com を調べてみる。

dig txt  _spf.google.com +short | $(which grep) -Po '(?<=include:)[^\s]+'

SFPレコードを見てみると

$ dig txt  _spf.google.com +short
"v=spf1 include:_netblocks.google.com include:_netblocks2.google.com include:_netblocks3.google.com ~all"

これをgrep でマッチすることで、

dig txt  _spf.google.com +short | $(which grep) -Po '(?<=include:)[^\s]+'
_netblocks.google.com
_netblocks2.google.com
_netblocks3.google.com

ここで、include なので、3つのドメイン名のTXTレコードを合わせたものが、GmailSPFだとわかる。

spf レコードから IPアドレスを取り出す。

それでは、 _netblocks.google.com をさらに見てみる。

dig txt   _netblocks.google.com +short | $(which grep) -Po '(?<=ip4:)[0-9\./]+'

これを実際に実行してみると、次のようになる。

dig txt   _netblocks.google.com +short | $(which grep) -Po '(?<=ip4:)[0-9\./]+'
35.190.247.0/24
64.233.160.0/19
66.102.0.0/20
66.249.80.0/20
72.14.192.0/18
74.125.0.0/16
108.177.8.0/21
173.194.0.0/16
209.85.128.0/17
216.58.192.0/19
216.239.32.0/19

これで、IPアドレスが得られることがわかる。

gmailSPFのTXTレコードについて。

ここまでで、GmailSPFレコードは、次のような階層を用いて管理されているとわかる。

redirect → include  → ipv4:[address/mask address/mask address/mask]

まとめてIPアドレスを取り出す。

for i in $( dig txt  _spf.google.com +short | \grep -Po '(?<=include:)[^\s]+' ); do 
  dig $i +short  txt  | \grep -Po '(?<=ip4:)[0-9\./]+'
done

これを実行することでSPFレコードに記載されたIPアドレスが得られることがわかる。

for i in $( dig txt  _spf.google.com +short | \grep -Po '(?<=include:)[^\s]+' ); do 
 dig $i +short  txt  | \grep -Po '(?<=ip4:)[0-9\./]+';
done | sort  -n | uniq
35.190.247.0/24
35.191.0.0/16
64.233.160.0/19
66.102.0.0/20
66.249.80.0/20
72.14.192.0/18
74.125.0.0/16
108.177.8.0/21
108.177.96.0/19
130.211.0.0/22
172.217.0.0/19
172.217.128.0/19
172.217.160.0/20
172.217.192.0/19
172.217.32.0/20
172.253.112.0/20
172.253.56.0/21
173.194.0.0/16
209.85.128.0/17
216.239.32.0/19
216.58.192.0/19

SPFレコードからIPアドレス範囲を得ることで、送信元のIPアドレスを見ることができる、そしてそのIPアドレスをから 送られてきたメールのみに絞ることができるが、FWのフィルタリングにも使えそうであるとわかる。

今回使った grep

上記の例で使ったGREPのコマンドは、GNU grep拡張正規表現を用いて、マッチした場所を取り出すオプションと、先読みを使っている。そのために随分とシンプルになって嬉しい。

参考資料

Google IP address ranges for outbound mail servers - Google Workspace Admin Help

cloudflare 以外からのアクセスを拒否する/ipsetとiptablesで

linux のFWで特定のアクセス元だけを許可する。

たとえば、cloudflare からのアクセスを許可して、通常のアクセスは全部拒否する。

アクセス元IPで接続を限定する。 クラウドVPSにデータを置くときには、アクセス制限をキッチリやらないから、情報漏洩が起きたときに被害甚大で「何万人」のデータが閲覧可能だったみたいな報道になっちゃうわけで。

cloudflareのIPリストはこれ

2021-04-06 現在では、次の場所に公開されています。https://www.cloudflare.com/ips/

173.245.48.0/20
103.21.244.0/22
103.22.200.0/22
103.31.4.0/22
141.101.64.0/18
108.162.192.0/18
190.93.240.0/20
188.114.96.0/20
197.234.240.0/22
198.41.128.0/17
162.158.0.0/15
104.16.0.0/12
172.64.0.0/13
131.0.72.0/22

ipset でリストを作る。

ipset create cloudflare
curl -s https://www.cloudflare.com/ips-v4 | xargs -n 1   echo sudo ipset add cloudflare @

アクセス元IPで制限。

cloudflare以外のアクセスを全部拒否する。

送信元のIPアドレスが指定のポートに来たときにチェックして、特定のIPアドレス以外を全部削除(DROP)する設定を次のように iptablesに入れてやる。

IF=eth0
dport=443
list=cloudflare
iptables -A INPUT -i $IF -p tcp -m comment --comment "takuya: deny ${dport} no cloudflare " \
   -m conntrack  \
   --ctstate NEW -m tcp --dport $dport -m set ! --match-set $list src -j DROP

消したいときは -Aを-Dに変える。

IF=eth0
dport=443
list=cloudflare
iptables -D INPUT -i $IF -p tcp -m comment --comment "takuya: deny ${dport} no cloudflare " \
   -m conntrack  \
   --ctstate NEW -m tcp --dport $dport -m set ! --match-set $list src -j DROP

iptablesについて。

上記の書き方だと次のとおりになる。

削除(DROP)ではなく拒否・拒絶(REJECT)でもいい。

-A は先頭にルールを追記するので即座にルールが最優先でマッチングされる。

-m comment --comment 'コメント' は、コメントを突っ込むモジュール。iptables -Lしたときにコメントが表示されるので便利。

-m tcptcp接続に限定している。http2 は UDPも使うので tcp はなくてもいいかもしれない。

-dport は宛先ポート

-m conntrack --ctstate NEWTCP新規接続を指定している。(SYN+SYN/ACK+SYN)の SYNを指定する。

-m set は ipset を使う設定。マッチしたときに適用される。 ! -m set は 否定でマッチしないとき(set以外)に適用される。

-set name は ipset のルール名を指定する。

参考資料

Allowing Cloudflare IP addresses – Cloudflare Help Center

chgrp で Operation not permittedになる場合の対処

事象

Linuxでファイルの所属グループを変更したいが、Operation not permitted になって権限が足りずにできない。

chgrp www-data virtualhosts/prod/test.com
chgrp: changing group of 'virtualhosts/prod/test.com': Operation not permitted

対応1:sudo をつける

sudo すればできるわけですが、sudo に頼るのも良くないですよね。

対応2:実行ユーザーが所属するグループを確認する。

chgrp を実行ユーザーが所属するグループにだけ、ファイルの所属グループを変更できます。

ユーザーが所属するグループ

たとえば、takuyaが以下の場合、www-dataに変更することはできない。

$ id takuya
uid=1000(takuya) gid=1000(takuya) groups=1000(takuya),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev)

上記の状態で chgrp www-dataするとパーミッションエラーになる。

ユーザーtakuyaが、www-dataに所属していれば、www-dataへ変更することができる。

www-dataグループにtakuyaが所属する

usermod -aG www-data takuya
logout

usermodは一度ログアウトしないと反映されない。

パーミッションエラーにならない。

グループに所属していればエラーにならない。

chgrp www-data virtualhosts/prod/test.com
## エラーなく実行完了

たまに、新規インストールしたLinuxでユーザーのグループ設定をわすれて、パニクることがあるのでまとめておいた。

lxc storage を btrfs のサブボリュームから作成する

lxc storage を btrfs のサブボリュームから

参考資料を見ながら、lxc の storage をbtrfs のサブボリュームで作ることにした。

全体の流れ

  • btrfs のストレージを用意する
  • ストレージを接続する
  • btrfs でフォーマットする。
  • btrfs をマウントする
  • btrfs でサブボリュームを作る
  • サブボリュームをlxc のストレージ保存場所に mount --bindする。
  • lxc にストレージとして認識させる。

できれば lxd init の実行前にやっておくと無難。

btrfs のストレージを作ってサブボリュームを作る

btrfs で初期化する。

sudo mkfs.btrfs -f /dev/sda1

btrfs をマウントする

sudo btrfs check /dev/sda1
sudo mkdir /mnt/8ce1230a
sudo mount /dev/sda1 /mnt/8ce1230a

btrfs のサブボリュームを作る(サブボリュームにはマウントが必須)

sudo btrfs subvolume create /mnt/8ce1230a/LXC-pool0

btrfsのボリュームとサブボリュームを snap/lxd にマウント ( bind )

takuya@ubuntu:/etc/systemd/system$ sudo btrfs subvolume list /mnt/8ce1230a
ID 256 gen 12 top level 5 path LXC
ID 257 gen 84 top level 5 path LXC-pool0

コマンドからサブボリュームを作成しマウントできた。あとは設定を書いて起動時に行うようにする。

マウント設定を書く。

マウント設定は、fstab でもいいのだけど、lxd が起動する直前のタイミングでマウントをしたいので、今回はsystemdに書いた。

$ cat <<'EOF' | sudo tee /etc/systemd/system/mnt-8ce1230a.mount
[Unit]
Description=External USB Ssd Storage
Before=snap.lxd.daemon.service

[Mount]
What=/dev/disk/by-uuid/8ce1230a-xxxx-xxxx-xxxx-xxxxxxx
Where=/mnt/8ce1230a
Type=btrfs

[Install]
WantedBy=multi-user.target
RequiredBy=snap.lxd.daemon.service

マウント設定を有効にしてマウントする

sudo systemctl enable mnt-8ce1230a.mount

systemd のマウント設定は、マウントするPATH名がファイル名になることが必須なので注意。

bind 設定も合わせて書く。

lxd がもともと使っている disks フォルダそのものをマウントしてしまったほうが楽だろうということで。

また、このbindのマウントの前にbind元になる/mnt/ のマウント後実行したい。ので順番を意識してunitsファイルを書く。

$ cat <<'EOF' | sudo tee /etc/systemd/system/var-snap-lxd-common-lxd-disks.mount
[Unit]
Description=External Storage for LXD
After=usb-ssd.mount
Before=snap.lxd.daemon.service

[Mount]
What=/mnt/8ce1230a/LXC
Where=/var/snap/lxd/common/lxd/disks
Options=bind

[Install]
WantedBy=multi-user.target

[Install]
WantedBy=multi-user.target
RequiredBy=snap.lxd.daemon.service
EOF

有効にしてマウント。

sudo systemctl enable var-snap-lxd-common-lxd-disks.mount

マウントできた

これで、2つのマウントが完成した。

/dev/sda1 on /mnt/8ce1230a type btrfs (rw,relatime,ssd,space_cache,subvolid=5,subvol=/)
/dev/sda1 on /var/snap/lxd/common/lxd/disks type btrfs (rw,relatime,ssd,space_cache,subvolid=256,subvol=/LXC)

lxd を初期化する。

lxd を初期化する

lxd init 

ここでサブボリュームで指定した lxd のパスを入れてあげればいい。すでに、初期化している場合でも再初期化すればいい。

ストレージ追加で使う場合

ストレージ追加で使う場合は、次の通り

 lxc storage create pool0 btrfs  source=/mnt/8ce1230a/LXC-pool0

サブボリュームをsourceを指定すればいい。

lxc storage create pool0 btrfs  source=/mnt/8ce1230a/LXC-pool0
lxc profile show default
lxc profile device set default root pool=pool0

初期化済みの場合、デフォルト・プロファイルの設定がlxd init で定義されたストレージを使うようになっているため、いったんプロファイルを別に作成してdefaultを削除して作り直してもよさそう。

ストレージ作成

lxc storage create pool0 btrfs  source=/mnt/8ce1230a/LXC-pool0
takuya@ubuntu:~$ lxc storage list
+---------+-------------+--------+--------------------------------------------+---------+
|  NAME   | DESCRIPTION | DRIVER |                   SOURCE                   | USED BY |
+---------+-------------+--------+--------------------------------------------+---------+
| default |             | btrfs  | /var/snap/lxd/common/lxd/disks/default.img | 1       |
+---------+-------------+--------+--------------------------------------------+---------+
| pool0   |             | btrfs  | /mnt/8ce1230a/LXC-pool0                    | 0       |
+---------+-------------+--------+--------------------------------------------+---------+

ストレージ切り替え

takuya@ubuntu:~$ lxc profile device set default root pool=pool0

結果

takuya@ubuntu:~$ lxc storage list
+---------+-------------+--------+--------------------------------------------+---------+
|  NAME   | DESCRIPTION | DRIVER |                   SOURCE                   | USED BY |
+---------+-------------+--------+--------------------------------------------+---------+
| default |             | btrfs  | /var/snap/lxd/common/lxd/disks/default.img | 0       |
+---------+-------------+--------+--------------------------------------------+---------+
| pool0   |             | btrfs  | /mnt/8ce1230a/LXC-pool0                    | 1       |
+---------+-------------+--------+--------------------------------------------+---------+

参考資料

https://gihyo.jp/admin/serial/01/ubuntu-recipe/0571

LXCの起動済みのコンテナにmacvlanのネットワークを足してホスト側ネットと通信する

LXCの起動済みのコンテナにmacvtap/macvlan を足す。

既存のコンテナ・インスタンスがあって、そこにmacvtap を追加する。

すでにあるコンテナは、次の通りのsample01を作ってある。これはlxdbr0を経由して外部と接続する。

takuya@ubuntu:~$ lxc start sample01
takuya@ubuntu:~$ lxc list
+----------+---------+---------------------+-----------+-----------+
|   NAME   |  STATE  |        IPV4         |    TYPE    | SNAPSHOTS |
+----------+---------+---------------------+-----------+-----------+
| sample01 | RUNNING | 10.135.9.111 (eth0) | CONTAINER | 0         |
+----------+---------+---------------------+-----------+-----------+

ネットワークを追加する

次のコマンドで起動中のコンテナ・インスタンスにネットワーク・インターフェイスを追加する。追加するNICはmacvlan でホスト側のネットワークに直接接続することに。

追加するコマンド。

lxc config device add sample01 mytap0 nic nictype=macvlan parent=eth0

lxc config device add で、インスタンスを指定しデバイス追加する。追加するのはnic です。

コマンドの実行書式

lxc config device add コンテナ名  設定名 nic nictype=タイプ parent=元になるやつ

タイプは、macvlan かbridged などいくつかある。だいたいはbridgedかな。

実行するとNIC接続される。

この追加したネットワークをdhcp 有効にして設定を書いておく。

cloud-init ファイルは永続化ファイルじゃないのでOS再起動で変更が消失云々と書かれているのですが、実験なのでとりあえず書く。

/etc/network/interfaces.d/50-cloud-init

# This file is generated from information provided by the datasource.  Changes
# to it will not persist across an instance reboot.  To disable cloud-init's
# network configuration capabilities, write a file
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
# network: {config: disabled}
auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp
auto eth1
iface eth1 inet dhcp

書いたらネットワークを再起動

/etc/init.d/networking restart

DHCPが降ってきた。

root@sample01:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
18: eth0@if19: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:45:89:a7 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.135.9.111/24 brd 10.135.9.255 scope global dynamic eth0
       valid_lft 3018sec preferred_lft 3018sec
    inet6 fd42:d79a:8b11:68a4:216:3eff:fe45:89a7/64 scope global dynamic mngtmpaddr
       valid_lft 3557sec preferred_lft 3557sec
20: eth1@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:cc:85:e2 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.11127/24 brd 192.168.11255 scope global dynamic eth1
       valid_lft 42621sec preferred_lft 42621sec

ついかしたNICはホスト側でも確認できる

takuya@ubuntu:~$ lxc list
+----------+---------+----------------------+-----------------------------------------------+-----------+-----------+
|   NAME   |  STATE  |         IPV4         |                     IPV6                      |   TYPE    | SNAPSHOTS |
+----------+---------+----------------------+-----------------------------------------------+-----------+-----------+
| sample01 | RUNNING | 192.168.11127 (eth1) | fd42:d79a:8b11:68a4:216:3eff:fe45:89a7 (eth0) | CONTAINER | 0         |
|          |         | 10.135.9.111 (eth0)  |                                               |           |           |
+----------+---------+----------------------+-----------------------------------------------+-----------+-----------+

ネットワークを再起動して接続したら、 ホスト側にveth が増えた。

ホスト側で ip a を叩くと veth が増えていた。

19: veth466e08c2@if18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master lxdbr0 state UP group default qlen 1000
    link/ether 5e:04:fc:b6:ee:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0

んんんん。vethかぁ。macvlan の空間に接続するveth なのかなぁ。とりあえずmacvtapではなさそう

仮想マシンに追加された、ホスト側との接続用IPを確認する。ホスト側のネットワークからDHCPでIPをもらってくる

takuya@ubuntu:~$ lxc exec sample01 ip addr show eth1
20: eth1@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:cc:85:e2 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.11127/24 brd 192.168.11255 scope global dynamic eth1
       valid_lft 43014sec preferred_lft 43014sec
    inet6 fe80::216:3eff:fecc:85e2/64 scope link
       valid_lft forever preferred_lft forever

このIPアドレスについて、疎通を確認する。

とうぜんだけど、ホスト・ゲスト間で通信はできない。macvlan をホスト側に作っておくと通信できる。

ping 192.168.11127 -I eth0
ping 192.168.11127 -I macvlan1

永続化設定

先程は、とりあえずで /etc/networkに書いたが、macvlan の設定に問題なければ、永続化の設定を書く。または デフォルト・プロファイルに記述する。

cloud-init を使って起動されたコンテナはcloud-init の流儀に従う。debian なら /var/lib/cloud/seed/nocloud-net/network-config 、 ubuntuなら netplanですかね。

/var/lib/cloud/seed/nocloud-net/network-config

version: 1
config:
  - type: physical
    name: eth0
    subnets:
      - type: dhcp
        control: auto
    name: eth1
    subnets:
      - type: dhcp
        control: auto

この他の追加方法

今回は、コンテナに直接デバイスmacvlan を追加した。

この他にも、コンテナ作成時のデフォルト設定(プロファイル)にデバイスを追加しておくと、コンテナ作成時に最初から追加された状態で初期化される。という方法を取ることもできる。

参考資料

https://gihyo.jp/admin/serial/01/ubuntu-recipe/0535?page=2

https://linux-svr.com/%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E4%BB%AE%E6%83%B3%E5%8C%96/59.php#default-bridge

https://lxd-ja.readthedocs.io/ja/latest/cloud-init/

macvtap でできた仮想マシンとホストと通信してみる。macvlan/macvtap

macvtap でできた仮想マシンとホストと通信してみる。

macvtap でできた仮想マシンとホストと通信してみる。

macvtap を使った場合、ホスト・ゲスト間の通信ができない。

しかたないので、ホストに別にNICをmacvlan で定義して、そこを経由して通信するとしてみる。

sudo ip link add dev macvlan1 link eth1 type macvlan mode bridge
sudo ip addr add 192.168.11.112/24 dev macvlan1
sudo ip link set macvlan1 up

macvtap に向けてパケットを送ってみる、応答がきて通信がができることがわかる。

ping 192.168.11.103 -I macvlan1

削除するには

sudo ip link set macvlan0 down
sudo ip addr del 192.168.11.112/24 dev macvlan0
sudo ip link del dev macvlan0 

ちゃんと macvlan 経由で出ていくことがわかる。

takuya@ubuntu:~$ ip route get 192.168.11.103
192.168.11.103 dev macvlan1 src 192.168.11.101 uid 1001
    cache
    

ちょっと理解が追いつかず怖いなと思った箇所が次の通り。

パケットはごちゃまぜ(?)になって eth0から出たはずのパケットは、内部でeth1 へ到達している。

ホストに [eth1,eth0] の2物理NICがあって、macvlan1@eth1として設定。ゲストでは macvtap はmacvtap@eth0 に割り当てたのだが、

ゲスト側のネットワークは次の通り

root@OpenWrt:/# ip a
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP qlen 1000
    link/ether 52:54:00:ab:ff:35 brd ff:ff:ff:ff:ff:ff
    inet 192.168.11.12/24 brd 192.168.11.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:feab:ff35/64 scope link
       valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP qlen 1000
    link/ether 52:54:00:3a:09:af brd ff:ff:ff:ff:ff:ff
    inet 192.168.11.107/24 brd 192.168.11.255 scope global eth1
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fe3a:9af/64 scope link
       valid_lft forever preferred_lft forever

ホスト側のネットワークは次の通り。

takuya@ubuntu:~$ ip a
### もともとの物理NIC
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether dc:a6:32:dd:23:c4 brd ff:ff:ff:ff:ff:ff
    inet 192.168.11.111/24 brd 192.168.11.255 scope global dynamic eth0
       valid_lft 42628sec preferred_lft 42628sec
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether e8:fc:af:c7:aa:21 brd ff:ff:ff:ff:ff:ff
### ここが macvlan
5: macvlan1@eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether aa:85:1b:0e:e4:59 brd ff:ff:ff:ff:ff:ff
    inet 192.168.11.101/24 brd 192.168.11.255 scope global dynamic macvlan1
       valid_lft 42623sec preferred_lft 42623sec
### ゲストの eth0
10: macvtap0@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 500
    link/ether 52:54:00:ab:ff:35 brd ff:ff:ff:ff:ff:ff
### ゲストの eth1
11: macvtap1@eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 500
    link/ether 52:54:00:3a:09:af brd ff:ff:ff:ff:ff:ff

[ホスト macvtap0@eth0 , ゲスト側eth0]、[ホスト macvtap1@eth1 , ゲスト側eth1]、がそれぞれペアとなる割当にしてある。

ここで、ホストの macvlan1@eth1 から ゲストの eth0 へ向かってping を投げると・・・到達するんですよね。ホスト外部には出ません。これがちょっと気持ち悪いと思った点。

macvlan はホスト内部で処理されてしまう。つまりホスト内部のvlan という扱いなのですかね。ちょっとモヤっとする。

ホスト・ゲストで通信ができた。

これで無事に macvtap と疎通する事ができるとわかる。

ただし、これを起動時に最初からやるのはしんどいのです。if-up / if-down スクリプトをこのご時世に書いて管理するのもちょっと。

そこでnetworkd が動いているなら、それを使うと、macvlan を起動時にと来ることができるはずです。

3つのファイルを作ります。

ぱぱっとファイルを作ります。

touch /etc/systemd/network/{00-eth1.network,00-macvlan1.netdev,00-macvlan1.network}

つくった。

takuya@ubuntu:~$ ll  /etc/systemd/network/*
-rw-r--r-- 1 root root 46  3月 24 22:25 /etc/systemd/network/00-eth1.network
-rw-r--r-- 1 root root 60  3月 24 22:22 /etc/systemd/network/00-macvlan1.netdev
-rw-r--r-- 1 root root 44  3月 24 22:18 /etc/systemd/network/00-macvlan1.network

eth1 に macvlan1 をbridge定義する 設定 macvlan1 をネットワークデバイスとする 設定 macvlan1 にIPアドレスを割り当てる 設定

ですかね

/etc/systemd/network/00-eth1.network

[Match]
Name=eth1

[Network]
MACVLAN=macvlan1

/etc/systemd/network/00-macvlan1.netdev

[NetDev]
Name=macvlan1
Kind=macvlan

[MACVLAN]
Mode=bridge

/etc/systemd/network/00-macvlan1.network

[Match]
Name=macvlan1

[Network]
DHCP=ipv4

最後の netplan を networkd 経由に

ubuntu の場合 netplan が動作しているので。

/etc/netplan/50-cloud-init.yaml

network:
    renderer: networkd

これで、 networkd を優先にする。

netplan generate で設定を作り出す。

sudo netplan --debung generate

追記

netplan を renderer を networkd に変更した場合、/run/systemd/network/10-netplan-eth0.networkが作成される。

sudo ls  /run/systemd/network/10-netplan-eth0.network

これよりも、networkd が作成するファイルが優先するために、 10-netplan-eth0.networkより、優先度の高いファイル名を設定する必要がある

もしかしてnetplan っていらない子なんじゃ。。。

再起動

sudo netplan --debug apply
sudo systemctl restart systemd-networkd.service

netplan を適用しつつ、networkd も設定してやる。

結果を確認

takuya@ubuntu:~$ sudo systemctl status systemd-networkd.service
● systemd-networkd.service - Network Service
     Loaded: loaded (/lib/systemd/system/systemd-networkd.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2021-03-24 22:48:36 JST; 7s ago
TriggeredBy: ● systemd-networkd.socket
       Docs: man:systemd-networkd.service(8)
   Main PID: 3526 (systemd-network)
     Status: "Processing requests..."
      Tasks: 1 (limit: 9257)
     CGroup: /system.slice/systemd-networkd.service
             └─3526 /lib/systemd/systemd-networkd

 3月 24 22:48:36 ubuntu systemd-networkd[3526]: macvlan1: Gained IPv6LL
 3月 24 22:48:36 ubuntu systemd-networkd[3526]: eth1: Gained IPv6LL
 3月 24 22:48:36 ubuntu systemd-networkd[3526]: eth0: Gained IPv6LL
 3月 24 22:48:36 ubuntu systemd-networkd[3526]: Enumeration completed
 3月 24 22:48:36 ubuntu systemd[1]: Started Network Service.
 3月 24 22:48:36 ubuntu systemd-networkd[3526]: macvlan1: IPv6 successfully enabled
 3月 24 22:48:36 ubuntu systemd-networkd[3526]: eth1: IPv6 successfully enabled
 3月 24 22:48:36 ubuntu systemd-networkd[3526]: eth0: IPv6 successfully enabled
 3月 24 22:48:36 ubuntu systemd-networkd[3526]: macvlan1: DHCPv4 address 192.168.11.101/24 via 192.168.11.1
 3月 24 22:48:36 ubuntu systemd-networkd[3526]: eth0: DHCPv4 address 192.168.11.111/24 via 192.168.11.1

これで、networkd でも macvlan が作成されるとわかる。

netplan は非対応

raspi 4 に ubuntu を入れているので、ubuntu だしnetplan で設定しようかと思ったが、netplan は macvlan を作れないらしい。

なので if-up/if-down を書くか、/etc/network/interfacesを書くか、networkd を書く必要がある。今回は networkd に書くことで対応した。

参考資料

https://tenforward.hatenablog.com/entry/20111221/1324466720

https://major.io/2015/10/26/systemd-networkd-and-macvlan-interfaces/

Today's HOGE(2019-10-04)

Raspberry Pi4 にKVM+qemu で仮想マシンの仮想環境を作る/raspi+kvm

Raspberry Pi4 にKVM+qemu仮想マシンの実行環境を作る

Raspberry Pi4 もメモリが8GBもあれば、仮想マシンを動かすのに十分な性能があると思うんですね。

仮想マシンを動かしたらいろいろ便利そうなので、仮想マシンを動かすことにした

SDカードにOSを準備

KVMが有効な仮想マシンを動かすために、通常のRaspbianではちょっと厳しい。

Raspbian はKVMのサポートが無効化された32bit版が配布されている。動くには動くだろうが、KVMのサポートは欲しい。

そこで、KVMサポートがされたOSをインストールする。

選択肢はこの通りになる。

  • Raspbian 64bit
  • Ubuntu for raspi arm64 server
  • Arch とか

raspbianの64bit版は、KVMが有効化された状態でコンパイルされているので、Raspbianなら64bit版を使う。

わたしは、初回インストールの手間を考えて、ubuntuにした。

ubuntu には、arm4でraspiようにビルドされたイメージが配布されているので楽そうだった。

ubuntuの場合は、次のイメージを探してきて、SDカードに書き込む

ubuntu-20.04.2-preinstalled-server-arm64+raspi.img

初回起動後

初回起動したら、そのままSDカード容量に合わせて、ext4がリサイズされる。

初回アップデートも初回起動時に行われる。

ssh など必要な初回インストール作業が少なくて済むのがubuntu-server版の魅力

いつもの通り、piユーザーの代わりになる、自分のユーザーを作り、piユーザーはログインを無効化しておく。

ubuntu版の場合は、piユーザーではなく id/pw=ubuntu/ubuntu がプリインされている。

ユーザー作成

sudo adduser takuya
sudo usermod -aG ubuntu takuya
sudo usermod -aG adm takuya
sudo usermod -aG dialout takuya
sudo usermod -aG cdrom takuya
sudo usermod -aG floppy takuya
sudo usermod -aG sudo takuya
sudo usermod -aG audio takuya
sudo usermod -aG dip takuya
sudo usermod -aG video takuya
sudo usermod -aG plugdev takuya
sudo usermod -aG netdev takuya
sudo usermod -aG lxd takuya

ssh 設定

PasswordAuthentication no
Match User ubuntu Address 192.168.100.0/24
  PasswordAuthentication yes

カーネル確認

ubuntu で raspi4 にインストールしたら、この表記になった。

takuya@ubuntu:~$ uname -a
Linux ubuntu 5.4.0-1030-raspi #33-Ubuntu SMP PREEMPT Wed Feb 24 11:20:11 UTC 2021 aarch64 aarch64 aarch64 GNU/Linux

apt 設定

インストールを最小限にしておく。

echo -e  "APT::Install-Suggests 0;\nAPT::Install-Recommends 0;" | sudo tee /etc/apt/apt.conf.d/00-no-install-recommends

apt ミラーサーバー設定(わからない

apt ミラー設定は、ミラーサーバーの設定がわからない・・・ちょっと特殊なバイナリ( arm64 )なのであとに回す

たとえばJAISTミラーには、ARM64がない。arm/arm64バイナリはミラー郡の提供に含まれないっぽい。

ちょっと特殊みたいなので、いつものUbuntuミラーサーバーは使えないっぽい。

gihyo の ubuntu weekly recipe (柴田充也氏)の記事によると。

ちなみにarm64やarmhfなどamd64以外のアーキテクチャーパッケージについては,ports.ubuntu.comで提供されています。こちについては「公式のミラーサーバー」は用意されていないため,Raspberry Piなどでは原則として「ports.ubuntu.com」を指定することになります。 https://gihyo.jp/admin/serial/01/ubuntu-recipe/0677?page=2

ということです。

apt の更新

sudo apt update && sudo apt upgrade -y

タイムゾーンの設定

sudo timedatectl set-timezone Asia/Tokyo

ロケール設定

sudo apt-get install language-pack-ja
sudo update-locale LANG=ja_JP.UTF-8
sudo locale-gen
sudo locale -a

または

sudo dpkg-reconfigure locales

GUIでやるなら後者が確実。

http://www.dreamedge.net/archives/111

LXD 最初から入ってる。つおい

ちなみに、ubuntuだと、Snapcraftの snapd がLXDで最初から導入されているので、qemu/libvirtを使わなくても仮想マシン(コンテナ)を手軽に作れる。

わざわざqemukvmを動かさなくても、殆どの場合LXC/lxd で十分な気もする。

takuya@ubuntu:~$ sudo snap list
Name    Version   Rev    Tracking       Publisher   Notes
core18  20210128  1990   latest/stable  canonical✓  base
lxd     4.0.4     19040  4.0/stable/…   canonical✓  -
snapd   2.49      11115  latest/stable  canonical✓  snapd

kvm qemulibvirt で仮想環境を作る

kvm が使えるかどうか、念のためにチェック

sudo apt install cpu-checker

KVM が使える!。raspberry Pi4 でもKVMが使えます。

takuya@ubuntu:~$ sudo kvm-ok
INFO: /dev/kvm exists
KVM acceleration can be used

kvm qemu 一式をインストールする

kvmqemulibvirt 経由で使うために、つぎをインストール。

sudo apt -y install qemu-kvm libvirt-daemon-system \
libvirt-daemon virtinst bridge-utils libguestfs-tools virt-top

uefi をインストール

qemu-efi がないと動かない。(aarch64用)

sudo apt install qemu-efi

virtio で ネットワークを扱う。

インストールしてないと、failed to find romfile "efi-virtio.rom" になり、ネットワークを扱えない仮想マシンになった。

sudo apt install ipxe-qemu

virbr0 を使う。

libvirtd が提供する virbr0 を使うために、dnsmasq が必要だった。インストールが必要だった。

sudo apt install dnsmasq

virt/kvm グループに参加

自分のユーザーで、system virshを使いたいのでグループに参加しておく。ユーザーセッションでもいいんだけど、qemu://system も使えたほうが楽だし。

sudo usermod -aG libvirt takuya
sudo usermod -aG libvirt-qemu takuya
sudo usermod -aG kvm takuya

qemu仮想マシンを起動(openwrt)

とりあえず、OpenWRTでも動かしてみようか。

openwrt には、arm64の仮想マシンで動かすための armvirt/64bit のバイナリが配布されていて、起動方法も公式Wikiに記載されていたのでこれを起動しようと思う。

openwrtの公式→ https://openwrt.org/docs/guide-user/virtualization/qemu#openwrt_in_qemu_aarch64

イメージをダウンロード → https://downloads.openwrt.org/releases/19.07.6/targets/armvirt/64/

ダウンロードしたら、ext4squashfs は qcow2 に変えておく qemu-img convert -f raw $in -O qcow2 $out

とりあえず、起動してみる。

KERNEL_IMAGE=/var/lib/libvirt/images/openwrt-19.07.7-armvirt-64-Image
INITRAMFS=/var/lib/libvirt/images/openwrt-19.07.7-armvirt-64-Image-initramfs
DISK=/var/lib/libvirt/images/openwrt-19.07.7-armvirt-64-root.squashfs.qcow2
sudo qemu-system-aarch64 --enable-kvm -m 1024 -smp 2 -cpu host -M virt -nographic \
  -kernel $INITRAMFS \
  -drive if=none,file=$DISK,id=hd0 \
  -device virtio-blk-device,drive=hd0 \

OpenWRTがちゃんと起動する。

qemuの起動方法をよく知らないので、マニュアルが助かった。

virt-install仮想マシンを作成

qemukvm 仮想マシン起動ができたので、同じように、virt-install から libvirt経由で使える仮想マシンとしてインストールしてみる。

ネットワークはとりあえず、br0 を作ってそれを使うことに。

br0を作成

sudo brctl addbr br0
sudo brctl addif br0 eth1
sudo ip link set br0 up

eth1 を使ってるのは SSH接続が切れないように。USB-LANをRaspiに挿してeth1として使ってる。SSH経由で作業するeth0 (メイン・オンボード)をbr0に入れるとSSH経由で完結しなくなる面倒が起きる。それを避けるために eth1を使った。

virt-install仮想マシン作成。(openwrt)

KERNEL_IMAGE=/var/lib/libvirt/images/openwrt-19.07.7-armvirt-64-Image
INITRAMFS=/var/lib/libvirt/images/openwrt-19.07.7-armvirt-64-Image-initramfs
DISK=/var/lib/libvirt/images/openwrt-19.07.7-armvirt-64-root.ext4.qcow2

sudo virt-install   \
  --connect qemu:///system \
  --name=wrt01  \
  --disk path=$DISK,format=qcow,device=disk,bus=virtio  \
  --ram=512 \
  --arch=aarch64 \
  --cpu host-passthrough \
  --virt-type kvm \
  --graphics none \
  --network bridge=br0,model=virtio \
  --boot kernel=$KERNEL_IMAGE,initrd=$INITRAMFS,kernel_args='root=fe00' \
  --import \
  --features acpi=off \
 

acpiをOFFにしないとエラーなる。仕方ないこととはいえ、ちょっとめんどくさい。

acpiが使えないので、virsh から shutdown が使えない。デメリットである。終了はdestroyをする必要がある。

ただ、macvtap などネットワークの管理がvirt-managerに任せられるのでメリットである。

ubuntu仮想マシンを起動。

qemuubuntu 仮想マシンを起動しよう。とおもったんだけど、LXCあるから別にいらないのでパス。

raspbian の仮想マシンを起動

Raspbianがちゃんと動かせたら、RaspiAP のプロジェクトRaspberry Pi - Wifi Routerのプロジェクトの成果物を仮想マシンで動かせるじゃん。

って思ったんだけど、よく考えたら、仮想マシンWifi渡せないしRaspApプロジェクト動かせないんだよね。。どうせWifiバイスをUSBパススルーは地獄なんだし。OpenWRTが仮想マシンで動いてたら、スマホから扱えるルーターにはなるわけだし。

とりあえずraspbian を このraspi ubuntu で起動しよう。とおもったんだけど、よく考えたら、raspiのarmのあれこれだとか吸収しても、ちょっと風変わりなDebianになるだけなので、労苦の割に使いにくいものにしかならない。Debian欲しいならLXDで素のDebian起動したらいいじゃないかと思ったのでパス。

adguard home

Raspiにadguard homeとか入れたら面白そうだと思ってて、仮想マシンでやろうと思ってたんだけど、UbuntuにしたのでLXD/Docker使えるので、これもVMじゃなくても良くなってしまった。

kvm+qemuな wrtから iperf3をやってみる。

仮想マシンで起動したwrt から

root@OpenWrt:~# iperf3 -c 192.168.100.1
(...
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  1.06 GBytes   912 Mbits/sec  352             sender
[  5]   0.00-10.05  sec  1.06 GBytes   905 Mbits/sec                  receiver
iperf Done.

ubuntuをインストールしたraspiから

takuya@ubuntu:~$ iperf3 -c 192.168.100.1
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  1.10 GBytes   941 Mbits/sec  119             sender
[  5]   0.00-10.01  sec  1.09 GBytes   937 Mbits/sec                  receiver

仮想マシン化による速度低下は気にならないレベルだった

まとめ raspi4で kvm + qemu 起動

8GBのメモリがあると快適に動く。

Ubuntuを使うと楽ちん。ubuntuのRaspi arm64 server版だと、最初からKVMが有効になってた。

らくにqemu と libvirtdで仮想マシンを扱えるのだけど、UbuntuだとSnapdとLXDがインストール済みで配布されているので。よくあるLinux仮想マシンを扱う場合ではdocker / lxd が使えるんでそんなにメリットはないかも。

WindowsのArmかOpenWrtくらいしか動かしてメリットあるものはないと思うんだ。仮想マシンでwrt動かせるから、Raspi4をルーターとして放置しつつ他の用途にも使えそうで熱い感じはある。自分で、arm64用になにかプログラミングするならとてもいいと思うんだけどね。

今回は、Ubuntuサーバー版をつかって仮想マシン作ったんだけど、GUIを扱うときにqemuでそこそこそ軽快に動かせるから管理は楽になるかもしれない。

純粋なサーバーとしてRaspiの4K2画面出せるグラフィック系の機能を殆ど使わないので、だいぶ勿体ない贅沢な使い方だとは思う。

参考

http://www.rcps.jp/doku.php?id=linux:kvm

https://www.kkaneko.jp/tools/container/ubuntu_qemu.html

https://kb.novaordis.com/index.php/Virt-install#Optional_Installation_Method_Option

https://dustymabe.com/2020/01/30/virt-install-boot-from-specific-kernel/initrd-just-for-install/

http://manpages.ubuntu.com/manpages/eoan/man1/virt-install.1.html

https://translatedcode.wordpress.com/2017/07/24/installing-debian-on-qemus-64-bit-arm-virt-board/

https://openwrt.org/docs/guide-user/virtualization/qemu#openwrt_in_qemu_aarch64

https://kitsunemimi.pw/notes/posts/running-windows-10-for-arm64-in-a-qemu-virtual-machine.html

gsuite のアカウント増やさずにメアドだけ増やして mailcow で管理する。

概要

GSuite(google workspace) 宛のメールを、独自のメールホストへ転送してGoogle アカウント管理下にないメールアドレスを使う。

前提条件

課金済み

重大な前提条件 Google Workspaceで課金済み。

課金しているGsuiteが必要です。歴史的経緯の無償版Gsuiteで使えません。

postfix などのメールサーバー

postfix単体が正しく設定されたもの。

デフォルトインストールでは、スパム処理や配送処理をしてないので、そのままでは内部転送が使い物にならないので devecot などIMAPを正しく設定しておいたもの( とりあえずmailcow いれとけばおっけ)

設定ステップ

設定は4ステップが必要

  1. Gmailの設定でホストを追加
  2. 転送設定(ルーティングを書く)
  3. 送信設定(返信・送信をGmail経由にする)
  4. postfix 側でメールボックスを追加

1.google admin でホストを追加

Google admin console にアクセスし、アプリからGmailを開いてホストを追加する。

f:id:takuya_1st:20210318190645p:plain

2. google admin → gmail → でルーティングを追加

ルーティングを追加する。

ルーティング設定の概要は次の通り

  • 対象経路
    • 外部から受信
    • Gmail内部から受信メール
  • 対象メアド
    • 受信者が一致
      • パターンに一致
      • 正規表現パターンに合致したもの
    • 存在しないメールアドレス
  • 転送先
    • 先程設定した mailcow サーバー
    • バウンスメールはOP25Bのために転送されないので無視する

      f:id:takuya_1st:20210318190929p:plain

      f:id:takuya_1st:20210318191000p:plain

      f:id:takuya_1st:20210318191023p:plain

3 送信ルート・返信ルートをGmail経由にする。

mailcow posfixから直接相手に送信せずに、GoogleSMTPを経由するよう設定する。

まず、GoogleSMTP経由を許可させる。 f:id:takuya_1st:20210318191147p:plain

つぎに、PostfixのTransport設定する

トランスポート設定で、smtp-relay.gmail.com:587 をスマートリレーで設定する。

このとき、SMTP認証が必要になるので、Google Workspace中の適当な アカウントをless secure apps(安全性の低いアプリ)に認定するか、アプリごとのパスワード生成(2FA時)をしてリレーを設定すればオッケ。

もし送信元のmailcowが固定IPアドレスであれば、SMTP認証は不要なのだが・・・

mailcow の場合

mailcow で転送設定するなら。次のステップで

  • 一般からSender Transportを追加
  • ドメインから 追加したSender Transportを指定

mailcow にログイン して 右上メニュ → ルーティング → Sender Transport

f:id:takuya_1st:20210318192212p:plain

右上メニュ→メールセットアップ→ドメイン→編集 f:id:takuya_1st:20210318192414p:plain

ドメインが追加されてない場合は追加しておく。

4.posftix でメールボックスを追加

postfix でちゃんと配送設定を書いていれば不要。

mailcow の場合。

mailcow を使うなら設定をする必要がある。

mailcow にログイン → 右上メニュー → mailsetup → 真ん中タブ → mailboxes の順にたどってメアドを追加。 f:id:takuya_1st:20210318192008p:plain

最後に

メールの送受信をチェックして終了。

GoogleSMTP経由で送信されるので、うっかりSPAM判定されるということはまずなさそう。

受信もGoogleSMTP経由から配送されるのでスパムフィルタもそこそこ動いてくれそう。

メールアドレスだけほしいために、Google Driveの容量とかGCPAPI利用可能なアカウントを発行するのは煩雑すぎるしこれでいいや。

google workspace がメールのルーティング機能を提供してくれているとか優しすぎです。Postfixの地獄のセットアップ修行をしなくてもmailcow 入れていたらとりあえず動くのも幸せすぎます。

ただ、gsuite と同じドメインで、独自メアド運用すると、一部のgoogle accounts 管理やログイン連携がちょっと面倒なことになりそうなんですよね。

参考資料

https://support.google.com/a/answer/60730?hl=ja&ref_topic=2921034

https://support.google.com/a/answer/2685650?hl=ja&ref_topic=2921034#zippy=%2Csmtp-%E3%83%AA%E3%83%AC%E3%83%BC-%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%97%E3%81%A6-gmail-%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E7%B5%8C%E7%94%B1%E3%81%A7%E3%83%A1%E3%83%BC%E3%83%AB%E3%82%92%E3%83%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%81%99%E3%82%8B%2C%E3%81%99%E3%81%B9%E3%81%A6%E3%81%AE%E3%83%A1%E3%83%BC%E3%83%AB%E3%82%92%E3%82%B2%E3%83%BC%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A4-%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E7%B5%8C%E7%94%B1%E3%81%A7%E9%80%81%E4%BF%A1%E3%81%99%E3%82%8B

https://support.google.com/a/answer/2956491#zippy=%2Cpostfix

xz/unxz コマンドをインストールする。

unxz コマンドが見つからない。

debian でいつものように xz をunxz展開しようとしたら。コマンドが見つからない

unxz ubuntu-20.04.2-preinstalled-server-arm64+raspi.img.xz
-bash: unxz: command not found

インストール

xzを使うパッケージは、xz-utilsになる。

次のコマンドがインストールされることがわかる。

sudo apt-file list xz-utils
xz-utils: /usr/bin/lzmainfo
xz-utils: /usr/bin/unxz
xz-utils: /usr/bin/xz
xz-utils: /usr/bin/xzcat
xz-utils: /usr/bin/xzcmp
xz-utils: /usr/bin/xzdiff
xz-utils: /usr/bin/xzegrep
xz-utils: /usr/bin/xzfgrep
xz-utils: /usr/bin/xzgrep
xz-utils: /usr/bin/xzless
xz-utils: /usr/bin/xzmore

インストール結果

 sudo apt install  xz-utils
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following packages were automatically installed and are no longer required:
  gyp libatomic1 libbrotli1 libjs-inherits libjs-is-typedarray libssl-dev libuv1-dev
Use 'sudo apt autoremove' to remove them.
The following NEW packages will be installed:
  xz-utils
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 183 kB of archives.
After this operation, 453 kB of additional disk space will be used.
Get:1 http://deb.debian.org/debian buster/main amd64 xz-utils amd64 5.2.4-1 [183 kB]
Fetched 183 kB in 0s (2,361 kB/s)
Selecting previously unselected package xz-utils.
(Reading database ... 30068 files and directories currently installed.)
Preparing to unpack .../xz-utils_5.2.4-1_amd64.deb ...
Unpacking xz-utils (5.2.4-1) ...
Setting up xz-utils (5.2.4-1) ...
update-alternatives: using /usr/bin/xz to provide /usr/bin/lzma (lzma) in auto mode

なんか lzma も増えた

mailcowをインストールしてubuntuでお手軽メールサーバー構築

mailcow でメールサーバー構築

メールの運用はめんどくさい。それ以前にインストールがめんどくさい。

スパム対策や、DKIMだとか、MXとMTAだとかあれこれ考える必要がある。考えるのをやめて、GoogleApps(Gsuite/Google workspace)だとかoffice365を使えばいいんだろう。しかし、すべてが解決するというわけでもない。メアドの追加だとか、ライセンス数を考慮したらしんどい(エイリアスで無料で足せるんだけど)、キャッチオールアドレスが欲しくなったり

期間限定なプロジェクトで、アルバイト雇用など一気に人が増えるとか。フェスティバルやイベントのために問い合わせフォームとメアドが必要だったりとか。たかだが数ヶ月のためにメアドとアカウントを大量にサービス側で追加するのもコスト的に負荷がかかる。そういうなにかメアドを管理できる方法を模索した。

mailcow パッケージがオールインワン

mailcow というパッケージをみつけた。 https://mailcow.email/

これをDockerで動かすとメールのあれこれが解決しそう。

WEBメールとカレンダーとアドレス帳機能があるSogo WEB を使える。

OpenMail プロジェクトも mailcow をベースに使っている。

https://openemail.io/

スパム対策

最初から、設定済みでソフトウェアが導入されているので、とても楽。 設定を増やすだけでいいのだから。必要なclamdとRspamdとか最初から入ってる。これが嬉しい。

インストール

インストールは、とてもかんたんだった。

docker と docker-composeが動く環境さえあればいい。

準備

ssh server-docker-host
cd /var/www/virtualhost/maicow.example.com
git clone https://github.com/mailcow/mailcow-dockerized
cd mailcow-dockerized
./generate_config.sh
vim  mailcow.conf

設定変更

設定変更は次の箇所だけにした

MAILCOW_HOSTNAME=mail.example.com
HTTP_PORT=11334
HTTPS_PORT=11335
SMTP_PORT=2525
TZ=Asia/Tokyo

MXレコード設定

dns 設定 で MXレコードとAレコード(今回はcname)を作った。

## 
mail.example.com cname gw.example.com
mail.example.com mx gw.example.com

リバプロ設定

HTTPSの443アクセスで、nginx があるので、それをdockerで起動したmailcow へ向け、リバプロする。

server {
  listen 443 http2 ssl;
  server_name mail.exammple.com;
  if ($scheme != "https") {
  return 301 https://$host$request_uri;
  }
  ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

  location ~ {
    proxy_set_header Host $host;
    ## 略
    proxy_pass http://docker-host.local:11334;
  }
  
}

起動と終了

docker-composeで解決。

cd $installed_path
docker-compose up -d
docker-compose restart

docker-compose でshutdown rm しても 設定が ./data に残るので、永続化も問題ない。ついてもportainer を入れていおいてもいいくらいだ。

OP25B をどうするのか

メール送信を行うとき、配送先のメールサーバーの25番に接続するが、一般のプロバイダ網内から外向けの25番は接続ができない。

通常個人ユーザーであれば、外向きにメール送信をする際のサブミッションポートを使えば事足りるが。メールサーバー間の配送は相手サーバー25番へ接続が不可欠である。

つまり Postfixから相手への配送は宛先ポート25番で転送するが、OP25Bでその接続(宛先ポート25)は使えない。ということで、代替手段が必要になる。

代替手段といっても、外部で25番が接続許可されている、制限されないIPアドレスのサーバーを確保するのが対策。わかりやすく言えばFromを書き換え許可されているSMTPサーバーが確保できるかが重要。

外部25へ転送方法さえ確保できれば、サーバーへの接続方法はなんでもいい。VPN接続でもよし、SSHポート転送でもよし、SSHのパイプ経由すら使える。一般的ならSubmissionポートへ接続だろう。

mailcow の管理画面からトランスポートを設定できる。

OP25Bへの対応が必要な場合、トランスポートを設定して、外部のリレーサーバーを指定する。mailcow のWEB画面から設定できるのが強い。

起動後の初期パスワード

初期パスワードは admin / moohoo でログインできるので変えておく。

SOGoが嬉しい

SOGoでWEBメールのUIがあるので、最低限WEBからメールを使うことができる。IMAPPOP3SMTP(submissuon)のポートや総当り対策を考えなくていいのは本当に嬉しい。

カレンダー

共有できるWEBカレンダーがあってちょっと驚く。

アドレス帳

メール運用に不可欠なアドレス帳もWEB機能があり、さらにドメイン内ユーザーでの共有アドレス帳も使える。いいじゃん

スパム対策

Rsamd があるのでこれを使っていく。フィルタの書き方や clamcv の導入に時間を取られないので嬉しい。

キューの一覧

postfix にたまったキューのチェックや削除も mailcow のWEB画面から行える。

メアドの追加

メールアドレスとメールボックスの追加は、mailcown のUIから行える。

そのさいにエイリアスや複数のドメインなども使える。

mailcow導入で考えることが減る。

とりあえず、MXレコードとポート転送さえしておけば、辛うじてつかえるメール環境が手に入る。がちでPostfixから設定したらノウハウがまとまるまで1年経っても終わらない仕事になる。docker-compose でWEBメールボックス一式が手に入るのは強い。

運用してくとスパムとの戦いユーザー数が多くなる長期間の運用などは、運用担当者を割当てノウハウを確保してくのが大変だろうけど、一通り行えることはわかる。

VPSのメモリと容量が余っていればとりあえず入れておけば、クラウド追加課金を考えずにメアドを増やせて便利だと思う。

XMPP や unbound は流石に要らないと思うけど・・

その他のWEBメール環境

PostfixDovecotSMTPIMAPさえ動かせてしまえば、あとは好きにできる。

nextcloud-mail / roundcube /Cypht のような、他のWEBメール環境も使える。

WEBメール環境を確保するには、IMAP環境さえあればなんとかなる。しかし、自前のIMAP構築のためにPostfixDovecot+pamAssassinのインストールがあまりに面倒すぎる。それで誰も試さないのがメール構築。 メール運用の最初ハードルがインストールと構築だと思う。それがこんなに簡単に行けるなら、脱GoogleGmailも不可能じゃないと思えてくる。

2023/10/17 追加

Solr が動かないので

https://docs.mailcow.email/manual-guides/Dovecot/u_e-dovecot-fts/#delete-mailbox-data

インデックスが入ってるフォルダを明示的に消した。

docker compose exec solr rm /opt/solr/server/solr/dovecot-fts/data/index
docker compose exec solr rmdir /opt/solr/server/solr/dovecot-fts/data/index

参考資料

nvim で vimrc が読み込まれなくなった

~/.vimrc が読み込まれなくなった

特定の環境で nvim を使っていると、 .vimrcが正しく解釈されなかったのでメモ。

mkdir -p ~/.config/nvim; ln -s ~/.vimrc ~/.config/nvim/init.vim

nvim は .config を標準に変えたんですね。

参考資料

https://stackoverflow.com/questions/3495124/not-reading-vimrc

ssh で ユーザー名 と ネットワークの複数条件のANDで設定を書く

sshd_config で条件を複数AND/ORで書きたい

ユーザーが takuya で、LocalIPからの、の両方にマッチしたときのみ、パスワードログインを許可する。

複数を記述は、そのまま列挙すればいい。

Match User takuya Address 192.168.0.0/16
  PasswordAuthentication yes

複数条件をORで列挙したいとき

たとえば、takuya or pi のユーザーに対して許可したい

Match User takuya,pi Address 192.168.0.0/16
  PasswordAuthentication yes

たとえば、複数のネットワークを指定したいとき

Match User takuya,pi Address 192.168.11.0/24,192.168.1.0/24
  PasswordAuthentication yes

などとと書けば、AND OR のように使うことができる。

not の 否定条件も併せて使うと便利。

拒否すると列挙が楽になると思う。

Match Group sudo User !pi Address 192.168.11.0/24,192.168.1.0/24
  PasswordAuthentication yes

ネットワークもアドレス範囲をガッツリかいて特定を拒否とかね。

Match User takuya,pi Address 192.168.11.0/24,!192.168.11.222
  PasswordAuthentication yes

ANDで絞り込めると便利。

複数条件をANDでかけると、条件記述がシンプルになって嬉しいです。

関連資料

参考資料

htaccess でサブディレクトリをトップディレクトリとして公開する。

htaccess をサブディレクトリをトップディレクトリとして公開する。

https://example.com として、 https://example.com/subdomain を公開したい。

RewriteEngine on
RewriteBase /
RewriteRule ^(.*)$  subdirectory/$1

現実で、こんなことする必要はないんだろうが。特定のディレクトリで作業内容をいったん公開しておいて、そのまま正式リリースしたいとか。

ほかの条件と組み合わせると格段に使える。

たとえば、公開日まではサブディレクトリへ転送するとか

# 2021年03月01日 09:00 まではティザーページ(サブディレクトリ)を表示
RewriteEngine on
RewriteBase /
RewriteCond %{TIME} <202103010900.*
RewriteRule ^(.*)$  subdirectory/$1

参考資料

.htaccess の書き方(リダイレクト編) - Qiita