らくがきちょう

なんとなく ~所属組織/団体とは無関係であり、個人の見解です~

CentOS8 で管理用/検証用インターフェイスを Namespace で分離する

CentOS8 でネットワークインターフェイスが複数存在する場合、利用方法は幾つか考えられると思います。

  1. そのまま扱う (全インターフェイス同じルーティングテーブルに所属させる)
  2. インターフェイスごとに VLAN を分ける (8021q モジュールが必要)
  3. インターフェイスごとに VRF を分ける (カーネル 4.4 以上が必要)
  4. インターフェイスごとに Network Namespace を分ける (対応版カーネルと iproute2 パッケージが必要)

今回は 4 番目の Network Namespace を使った設定方法をメモしておきます。 尚、このメモは Network Namespaceを使ってLinuxのルーティングテーブルを分離させる を参考にさせて頂いている為、出来れば (このメモよりも) 元記事をご覧になることをお勧めします。

ゴール

最終的には以下の構成を作ります。 Linux には CentOS8 を使いました。

f:id:sig9:20200509132755p:plain

Linux にはふたつのインターフェイスが存在しますが、以下のように Network Space を分離します。

インターフェイス Network Namespace 設定方法 想定利用イメージ
ens192 default nmcli で設定 管理用
ens224 0 起動スクリプト内で iproute2 を使って設定 検証用

尚、Linux 上の接続名は以下である前提で設定を進めます (デバイス名と接続名を同じにしてあります)。

# nmcli connection show
NAME    UUID                                  TYPE      DEVICE 
ens224  775414a8-1b43-4b4f-8f06-d89254f4b75c  ethernet  ens224
ens192  d3a6ca7a-f8cd-4855-a60d-24fa62debce3  ethernet  ens192

Network Namespace の設定には iproute2ip netns コマンドを利用しますが、設定しただけでは OS を再起動すると未設定の状態に戻ってしまいます。 その為、systemd にサービスを登録し、起動時に意図したネットワーク設定が行われるようにします。 サービス名は nsctrl としました。

Step.1

管理用に利用する ens192 は任意にアドレス設定やゲートウェイDNS の設定を行います。

nmcli connection modify ens192 \
  ipv4.addresses 10.0.0.1/24 \
  ipv4.gateway 10.0.0.254 \
  ipv4.dns "1.1.1.1 1.0.0.1" \
  ipv4.method manual

Step.2

OS 再起動時もネットワーク設定がされるように、後の手順で systemd へサービスを登録します。 そのサービスから呼び出される想定の設定用スクリプトを用意します。 今回は以下の内容で /usr/local/sbin/nsctrl というファイルを新規作成しました。 このファイルの内容は「設定したいネットワークのパラメータ」に合わせて修正します。

尚、Network Namespace を操作する ip netns コマンドは「元々存在しているデフォルトの Network Namespace」を明示的に指定することが出来ません。 この操作を可能にする為、ln -s /proc/1/ns/net /var/run/netns/default というシンボリックリンクを作成することで ip netns コマンドから default という名前でデフォルトの Network Namespace を参照出来るように設定します。

#!/bin/bash

SSHD_CMD=`which sshd`
SSHD_CONF="/etc/ssh/sshd_config"

DEFAULT_NS="default"
DEFAULT_IF="ens192"

NS0_NS="0"
NS0_IF="ens224"
NS0_ADDR="192.168.0.1"
NS0_MASK="255.255.255.0"
NS0_GW="192.168.0.254"
NS0_SSHD_PID="/var/run/sshd-$NS0_NS-$NS0_IF.pid"

case $1 in
    "start")
        echo "Create namespace settings."
        # default
        mkdir -p /var/run/netns/
        ln -s /proc/1/ns/net /var/run/netns/$DEFAULT_NS
        # nw1
        ip netns add $NS0_NS
        ip link set $NS0_IF netns $NS0_NS up
        ip netns exec $NS0_NS ip address add $NS0_ADDR/$NS0_MASK dev $NS0_IF
        ip netns exec $NS0_NS ip route add 0.0.0.0/0 via $NS0_GW dev $NS0_IF
        ip netns exec $NS0_NS $SSHD_CMD -f $SSHD_CONF -o "PidFile $NS0_SSHD_PID"
        ;;
    "stop")
        echo "Delete namespace settings."
        ip netns delete $NS0_NS
        kill -9 `cat $NS0_SSHD_PID`
        rm $NS0_SSHD_PID
        rm /var/run/netns/default
        ;;
    "status")
        echo "-----[ip netns list]"
        ip netns list
        echo "-----[ip address show]"
        echo "(default)"
        ip address show
        echo "($NS0_NS)"
        ip netns exec $NS0_NS ip address show
        echo "-----[ip route show]"
        echo "(default)"
        ip route show
        echo "($NS0_NS)"
        ip netns exec $NS0_NS ip route show
        echo "-----[ip neigh show]"
        echo "(default)"
        ip neigh show
        echo "($NS0_NS)"
        ip netns exec $NS0_NS ip neigh show
esac

スクリプトを用意したら実行権限を付与しておきます。

chmod u+x /usr/local/sbin/nsctrl

Step.3

systemd にサービス登録する為のファイルを作成します。 以下の内容で /etc/systemd/system/nsctrl.service というファイルを新規作成します。

[Unit]
Description = Control network namespace.
After=networking.service.target

[Service]
ExecStart=/bin/bash /usr/local/sbin/nsctrl start
ExecStop=/bin/bash /usr/local/sbin/nsctrl stop
Type=Simple
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

作成したファイルを systemd に反映する為、systemctl daemon-reload を実行しておきます。

systemctl daemon-reload

これで systemd へサービスが登録されました。

# systemctl list-unit-files | grep nsctrl
nsctrl.service                                                         disabled

Step.4

登録したサービスが OS 起動時に自動実行されるようにしておきます。

systemctl enable nsctrl

Step.5

「意図した通りに動作するか?」を確認する為、OS ごと再起動します。

reboot

Step.6

再起動が完了したら OS へログインし直し、下記のコマンドなどを使って意図した設定がされていることを確認します。

ip netns list

ip netns exec default ip address show
ip netns exec default ip route show

ip netns exec 0 ip address show
ip netns exec 0 ip route show

ip netns list を実行すると定義された Network Namespace の一覧を表示することが出来ます。 下記では default0 の、ふたつの Network Namespace が定義されていることを確認出来ます。

# ip netns list
0 (id: 0)
default

sshddefault0 の各々で動作しています。

# ls -l /var/run/sshd*
-rw-r--r-- 1 root root 4 May  9 12:55 /var/run/sshd-0-ens224.pid
-rw-r--r-- 1 root root 4 May  9 12:55 /var/run/sshd.pid

コマンドを個々に実行していくのが面倒な場合は今回、作成したスクリプトnsctrl status のように実行することで一度に状態確認することも出来ます。 以下は nsctrl status を実行した場合の出力例です。

# nsctrl status
-----[ip netns list]
0 (id: 0)
default
-----[ip address show]
(default)
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
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:50:56:be:73:c1 brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.1/24 brd 10.0.0.255 scope global noprefixroute ens192
       valid_lft forever preferred_lft forever
    inet6 fe80::d9aa:bd1d:2f0:808b/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
(0)
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
3: ens224: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:50:56:be:d2:06 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.1/24 scope global ens224
       valid_lft forever preferred_lft forever
    inet6 fe80::250:56ff:febe:d206/64 scope link
       valid_lft forever preferred_lft forever
-----[ip route show]
(default)
default via 10.0.0.254 dev ens192 proto static metric 100
10.0.0.0/24 dev ens192 proto kernel scope link src 10.0.0.1 metric 100
(0)
default via 192.168.0.254 dev ens224
192.168.0.0/24 dev ens224 proto kernel scope link src 192.168.0.1
-----[ip neigh show]
(default)
10.0.0.254 dev ens192 lladdr 00:0a:b8:d0:6f:c1 REACHABLE
(0)
192.168.0.254 dev ens224  FAILED

Network Namespace を削除する

Network Namespace を削除したい場合は以下のように実行します。

systemctl stop nsctrl

これで Network Namespace は削除されました。

# ip netns list
#

プロンプトに Network Namespace 名を表示する

bash のプロンプトに Network Namespace 名を表示するには以下の内容で /etc/profile.d/prompt.sh を新規作成します。

NETNS=`ip netns identify $BASHPID`
if [ `id -u` = 0 ]; then
    if [ $NETNS ]; then
        PS1="[\[\033[1;31m\]\u@\h\[\033[00m\] \W]($NETNS)\\$ "
    else
        PS1="[\[\033[1;31m\]\u@\h\[\033[00m\] \W]\\$ "
    fi
else
    if [ $NETNS ]; then
        PS1="[\[\033[1;36m\]\u@\h\[\033[00m\] \W]($NETNS)\\$ "
    else
        PS1="[\[\033[1;36m\]\u@\h\[\033[00m\] \W]\\$ "
    fi
fi

これでシェルにログインするとプロンプトが以下のように表示されます。

[root@localhost ~](default)#

参考