それマグで!

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

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

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

curl でSMTP経由のメール送信

curlsmtp 経由でメールを送信する。

curl -v --url 'smtp://192.168.1.1:2525' \
  --upload-file sample-mail.txt \
  --mail-from takuya@example.com \
  --mail-rcpt takuya@example.com

curl で送信できるわこれ。

smtpTELNETで接続してメール送信テストしなくて済む。 ポート解放をしておけば、あとあと困らないでメール送信ができますね。

curl って万能ですね。

ELHO ホスト名を指定する場合

Postfixをポートフォワーディングで使っていて、 Helo command rejected: need fully-qualified hostname となる場合

curl -v --url 'smtp://192.168.1.1:2525/smtp.example.com' \
  --mail-from takuya@example.com \

このように、スラッシュのあとに、EHLOのホスト名を追記すればいい。

curl で smtps で gmail 経由する場合

メールのログインとパスワードを追記したら SMTP-AUTH を経由してSMTPSでメールが送信できる。

curl --url "smtps://smtp.gmail.com:465" \
\ 
 --ssl-reqd --mail-from "sender@gmail.com"  \
 --user "sender@gmail.com:senderGmailPassword" \
\
 --mail-rcpt "example@gmail.com"\
 --upload-file /var/scripts/mail.txt \

GMAILのメール送信もcurl で1行でできるので、google の 「 less scure app ( 安全性の低いアプリ)」が1ヶ月未使用ならオフにされちゃうので定期的にログインしておくのに良さそう

postfix 相手にSMTPS (SSL / starttls ) で、オレオレ証明書

Postfixオレオレ証明書を使っていて、Submissionポート 587 を通して接続する場合

startls は内部がSMTPなので、smtp:// を使う。smtps ではない。ただし、starttls でSSLを使うので --ssl をつける。さらに、証明書がオレオレ証明書なので -k / --insecure をつける。

curl -v --url 'smtp://10.185.93.22:587/mailcow.example.com' --ssl -k

さらに、それが、SMTP Auth を経由する場合

curl -v --url 'smtp://10.185.93.22:587/mailcow.example.com'  --user "takuya@example.com:PASSWORD" --ssl 

なんかオプションが増えて大変だけど、ちゃんと送信できる。

TO / FROM を正確に記述する

curl は to(recpt) / from をSMTPプロトコルにつかうので、メールの本文のFROM/TOは書き換えは行われない。当然であるが。

メールのボディを指定して、FROM/TOとSubjectを正確に記載すると普通のメールとして送信が可能。SMTPの動作チェックであればなくても構わないので上記まででは省略してた。

echo 'To: takuya@example.com
From: takuya@mydomain.tld
Subject: this is a test from 465

this is a test
using smtps
' \
| curl -v --ssl --url 'smtps://mailcow.mydomain.tld:465' \
   --mail-from service@mydomain.tld \
   --mail-rcpt takuya@example.com \
   --user "account@mydomain.tld:XXX_PASS_XXX" \
   -T -

ここでは、--mail-from と、ボディの From: と、--user は、敢えてすべて異なるようにしている。SMTPではこれらを一致させる必要はなく、実際にこれで送れてしまうのだ。

一般のメールだと、拒否されるかもしれないが、smtp replay に直接投げ込みできると送信が可能なのである。

そして、From:が受信者のメーラーで表示されるはずだ。 ---mail-fromsmtp プロトコルだし、--userSMTP Auth用である。ややこしいですがそういうものなんでしょうね。

curl はほとんどの環境で使える。

curlWindows に添付された ( C:/Windows/System32/curl.exe ) ので、すべての環境で使えるコマンドになった。

curl未インストールは、 alpine や debian 軽量版など、特殊状況に限られるようになった。

curl だけでメールは送れるし、コマンド呼び出しで使えるのである。

メールプログラムを書く、ライブラリをインストールするなど、車輪の再発明するくらいならcurl でいいのである。

2022-03-15 追記

EHLOについて追記。

SMTP + start TLS について追記。

2022-03-16 追記

メールボディ指定、FROM / mail-fromについて追記。

参考資料

https://stackoverflow.com/questions/10523147/what-are-the-curl-smtp-command-line-arguments-to-support-gmail-tls-starttls-v

16 Command Examples to Send Email From The Linux Command Line

Sending email - Everything curl

Postfixが ssh ログインして、メールをリレー配送する設定を入れる。

動機: ssh 経由でsendmail を使えると気づいたので応用してみた。

Dockerや仮想マシンを起動でメール設定が億劫だし、postfixのsmartリレーを設定が面倒。Gmail送信するとgoogle アカウントの規制が多い。何度もパスワードを保存設定をするのが面倒だった。sendmailssh 経由で使えることに気づいたとき、メール送信テストで、メールサーバーをいくつ起動しようが、ssh ログインでどっかのPostfixに集約したらいいじゃないかと気づいた。

ローカル postfixsshさせ、SSHで別サーバーからメール送信させたい。

得られるメリット

メール送信は格段に便利になる。

1つのPostfixSSH経由で使い回す。

VPS として使ってるサーバーはpotfix導入済みですね。この vps をポート開放やリレー設定をせずに使いたい。テストサーバーのPostfixのメール送信は、SSH経由でVPSPostfixを利用すればいい。 VPSはポート25から手軽にメールを送信できるのだから、新規でインストールするPostfixVPSSSH sendmail でメールを送信すればいいじゃないか。

root@vps:~# apt list postfix* --installed
Listing... Done
postfix/focal-updates,now 3.4.13-0ubuntu1 amd64 [installed]

Dccker のインスタンスを増やしたり、仮想マシンを増やしているとメール設定と配送テストが億劫になる。億劫だからといって全く設定しないと使えないサービスもある。なので、HostAはすべてのメールを HostB 経由でで送ればいいわけである。 しかし、メールのリレーだとかPostfixのあれこれは覚えたくないのである。そこで、もっとかんたんな方法がないか模索したわけである。

設定と覚えることがシンプル

postfixのことは殆ど知らなくていいのである。ssh接続先に sendmailが動く環境さえあればいい。

パスワードを平文保存しなくていい。

postfix にパスワードを平文保存するための、dockerfileの設定をどうするかと、などを考えなくていい。不要になれば 公開鍵のアクセス許可を抹消すればいい。

ポート開放などに悩まなくていい。

メール送信のサブミッションポートだとかOP25Bとか考えなくていい。22番へ接続してsendmailを起動、メールを配送するだけなのだから。

設定の概要

テストをするにあたり、次のような構成を考えた。

HostA ( postfix) ---ssh----> HostB (postfix  sendmail)-smtp--> gmail smtp など

ssh 関連の設定がちゃんとできるなら使えるはずだ。

  • HostA ( postfix をインストールしたが、自身は外部へ送信しない。
  • HostB ( postfix をインストール・設定済サーバ、smart-replay で外部へ送信する)

HostA Postfixは メール送信を ssh HostB sendmail -i で丸投げ。HostB Postfixは smartリレーで外部のSMTP(Google/Gmail)などへ丸投げ。こうすることとにする。

ssh を使ってメールを送信

HostBのsendmail は、実はSSH経由で呼び出すことができる。

hostBから HostAの sendmail を起動してメール送信

cat sample-mail.txt | ssh HostB sendmail -i -t 

ローカルの場合。

自ホストにpostfixが導入され設定済みであれば、ローカルのpostfix を経由してsendmail でメール送信が可能。

次のように、ローカルのUNIXソケットを使って sendmailコマンドを起動したら終了である。

cat sample-mail.txt | sendmail -i -t 

この、sendmail コマンドは、ssh 経由でも起動することができるので、とてもかんたんに sendmail を使うことができる。

ぶっちゃけ、php だとか、wordpressだとか、python だとかを開発環境で動かす際のメール配送はこれでほとんどが解決する。送信メールコマンドを /usr/sbin/sendmail から ssh hostB /usr/sbin/sendmail に設定を変えれば済むのだから。

ただ、仮想マシンやコンテナだとそうもいかないので、postfixを設定しssh 経由で丸投げすることにする。

postfixssh 経由で sendmail へ転送する。

postfix は メールの配送をいろいろなやりかたで転送することができる。 古えのメーリスだとか、ローカル配送だとか、LAN内で配送とか、部署ごとに配送とかできるわけである。

その一つが postfixのpipe である。すでにpipeがあるんだから、postfix が、 sshコマンドで外部sendmail を呼び出せば設定はとてつもなく簡単になるはずである。

HostA の Postfixssh hostB sendmail を実行させメール配送させる

HostA ( postfix) ---ssh----> HostB (postfix  sendmail)-smtp--> gmail smtp など
  • HostA ( postfix をインストールしたが、自身は外部へ送信しない。
  • HostB ( postfix をインストールしサーバ、smart-replay で外部へ送信する)

これをやる方法が transport service 定義である。

master.cf

postfix のmaster.cf に次の行を追記する。

ssh-relay unix    -       n       n       -       -     pipe
  user=takuya argv=/usr/bin/ssh HostB /usr/sbin/sendmail -i $recipient

main.cf

main.cf 設定に、次の1行を加え、postfix の転送をすべて ssh-relay 経由にする。default_transport を記述すればオッケ

default_transport = ssh-relay

反映

あとは postfix をリロードするわけである。

$ systemctl restart postfix

テスト

cat sample-mail.txt | sendmail -i -t 

これで、メールはPostfixから、ssh のコマンドパイプ(pipe)へ転送され、HostB側で送信処理される。

設定の記述量も極小であり、SSHのポートと公開鍵認証だけを意識すれば解決である。ウルトラシンプルである。

公開鍵の設定。

postfix のmaster.cf に追記した箇所を見てみる。

ssh-relay unix    -       n       n       -       -     pipe
  user=takuya argv=/usr/bin/ssh HostB /usr/sbin/sendmail -i $recipient

ここでは、どのようにメールを配送するのか書いてある。 unix ソケットで受け付けて pipeながす。

pipe の先に ssh コマンドを起動する。起動するコマンドは ssh コマンドで sshがHostBでリモートsendmail を起動する。

とくに、難しいことはない。

ただし、公開鍵認証でコマンドが実行できる必要がある。

hostA に takuya ユーザーを作成し、hostB へ鍵を登録する。

root@hostB:/root $ adduser takuya
root@hostB:/root $ su takuya
takuya@hostB:~ $ ssh-keyen -t rsa 
takuya@hostB:~ $ ssh-copy-id hostA

公開鍵の登録をすれば、ssh 経由で sendmail が呼び出せることを確認しておく

takuya@hostB:~ $ cat - | ssh hostA /usr/sbin/sendmail -i -t 
To:takuya@example.com
From:takuya@example.com
Subject: relay test


hello from HostA via ssh HostB

メールが無事に配送されることを確認する。あとは hostA の postfix にローカルからの sendmail コマンドでSSH経由させるだけである。 楽である。

この設定を作ったときに躓いたポイント。

ユーザーの秘密鍵パーミッション。と master.cf へ記述する user=$USER のユーザー

ssh を実行するユーザーは root/postfix とかだとうまく行かなかった。

  • postfix ユーザーで ssh を起動しようとしたら status=deferred (unknown mail transport error) になるし
  • mail ユーザーで ssh 起動しようとしたら、No such file or directory. Permission denied, please try again._ Permission denied, please try again になるし
  • root の場合は、postfix が privilegedはだめとかいうし。

というわけで一般ユーザーで起動するのが無難だった。

秘密鍵を指定してみる。

ssh コマンドを指定なのだから、秘密鍵を引数でファイル指定すればいいはずである。 ssh -i 秘密鍵PATHで使えるのでそれを設定すれば動くはずである。

ssh-relay unix    -       n       n       -       -     pipe
  user=mail argv=/usr/bin/ssh -i  秘密鍵パス  takuya@HostB /usr/sbin/sendmail -i $recipient

hostA で次のコマンドを実行して、HostBからメール送信ができれば、postfixも同じ設定を使えるはずである。

mail ユーザ が sendmail をリモート実行できることを確認する。

root@hostA:~ $ cat sample-mail.txt | sudo -u mail ssh -i /etc/mail/ssh-sendmail-key -l takuya hostB  /usr/sbin/sendmail -i takuya@example.com

送信できれば、mail ユーザーを使う設定を記入する

postfix/master.cf に設定を追記する

今回は、設定名を変えて追記した。

ssh-relay2 unix    -       n       n       -       -     pipe
  user=mail argv=/usr/bin/ssh -i /etc/mail/ssh-sendmail-key -l takuya 192.168.2.5 /usr/sbin/sendmail -i $recipient

postfix/main.cf に設定を追記する

default_transport = ssh-relay2

これで、固定ユーザーに依存せずにメール起動ができるのである。便利ですね。

公開鍵の機能を限定しておく。

特定の公開鍵では、機能を限定することが可能なので、 authorized_keys を使って機能を限定しておけば安心である。接続元を限定したり、起動可能なコマンドを限定しておけばいい。

実際にやってみると楽だった

たとえば、SSHが可能でsendmail が利用可能なレンタルサーバーやVPS経由でメールを送信すれば、 OP25B や submission ポートに悩まされずに済む。

また、VPS側で postfixをリッスンせず、SSHのみ済むのでファイアウォール設定がとてもシンプルに片付くのである。

また、仮想マシンを増やすたびに smart-replay ホストを設定しパスワード保存する必要もないし、 smart-relayを設定するたびに、Google SMTPサーバーや google account secutiy で警告をもらわずに済むのである。

ssh で トンネル貼る必要もないし、socks5しなくても済むし、VPNも要らないのである。SSHなのでちゃんと暗号化されているのである。

さくらVPSやheteml レンタルサーバーで ssh sendmail をしてしまえば、25番ポートからfromをアレしたメールが送り放題になってしまうのである。postfixSSH pipe経由とは、実に、けしからんメール配送解決策である。

参考資料

Using sendmail over SSH - Super User

sendmail コマンドを ssh 経由で呼び出せることに気づいた。

sendmail でメールを送ろうとして苦労してた。

webの問い合わせフォームだとか、phpsendmail をサーバでは使えるが、ローカルでは smart-relay な postfix を設定して試していた。

実にめんどくさいことだった。或る日、コマンドを眺めていて、ふと気づいた。sendmail って ssh経由で呼び出せるんじゃね。

ローカルのpostfix を経由してsendmail を使う場合、次のように、ローカルのUNIXソケットを使って sendmailコマンドを起動したら終了である。

cat sample-mail.txt |sendmail -i -t 

これ、ssh経由でコマンド呼び出せば、メール配信完了するんじゃないか。ためしたできた。いままでの 苦労は何だったんだ・・・

ssh を使ってメールを送信

HostBのsendmail は、じつは、SSH経由で呼び出すことができる。

cat sample-mail.txt | ssh HostB /usr/sbin/sendmail -i -t 

この、sendmail コマンドは、ssh 経由でも起動することができるので、とてもかんたんに sendmail を使うことができる。

ぶっちゃけ、php だとか、wordpressだとか、python だとかを開発環境で動かす際のメール配送はこれでほとんどが解決する。

レンタルサーバーやVPSSSH経由でsendmail コマンドを発行すればいいのだから。