NanoPi R4S + OpenWrtでv6プラス固定IPを使ってみる

NanoPi R4S

Gigabit LAN x 2 で映像出力なしというミニルータに特化したようなデバイスで4G RAMモデルと1G RAMモデルがあり、今回は4Gでケース付きを購入した。

ストレージはSBCらしくmicro SDカードで、無線LANはついていない。

CPUはRockchipのarmv8でRK3399というものらしい。

OpenWrt

市販のルータで動作できるほど軽量なLinux distributionで、市販のルータの真の力を解放するらしい。

openwrt.org

しかし残念ながら、NanoPi R4S向けにはstable releaseされておらず開発版のsnapshotを使う必要がある。 追記 2022/9/6 22.03がリリースされ、NanoPi R4S向けのイメージがstable releaseとして出ました。

初期設定

OS書き込み

downloads.openwrt.org から openwrt-22.03.0-rockchip-armv8-friendlyarm_nanopi-r4s-squashfs-sysupgrade.img.gz をダウンロードし、適当なライターでmicroSDに書き込む。

microSDをNanoPiに挿し電源をつけてLANポートとPCを有線で繋ぐと、DHCPで192.168.1.0/24のアドレスが降ってくる。

$ ssh root@192.168.1.1

sshで繋ぐとパスワードなしにログインできるので、すかさず

root@OpenWrt:~# passwd

パスワードの設定をする。

一旦exitでPCに戻りssh-keyの設定をする(OpenWrtで使われているSSHサーバであるdropbearはyubikeyで使ってるecdsaに対応していないため、新たにed25519で鍵を作る必要があり、ここで結構ハマった…)。

ssh-copy-idを使うとdropbearに合わせた設定をしてくれるので便利(~/.ssh/authorized_keyではなく/etc/dropbear/authorized_keysに追記する必要がある)。

$ cd ~/.ssh
$ ssh-keygen -t ed25519
$ ssh-copy-id -i id_ed25519 root@192.168.1.1

これで

$ ssh root@192.168.1.1

でパスワードなしでログインできるようになった。

ネットワークの初期設定

OpenWrtでは各種設定は/etc/config/以下に保存されておりuciコマンドで操作することができる。

例えばネットワークに関する設定は/etc/config/networkにあり、初期状態ではこのようになってる

root@OpenWrt:~# cat /etc/config/network

config interface 'loopback'
    option device 'lo'
    option proto 'static'
    option ipaddr '127.0.0.1'
    option netmask '255.0.0.0'

config globals 'globals'
    option ula_prefix 'fd56:d548:f6c2::/48'

config device
    option name 'br-lan'
    option type 'bridge'
    list ports 'eth1'

config device
    option name 'eth1'
    option macaddr 'ab:cd:ef:gh:ij:kl'

config interface 'lan'
    option device 'br-lan'
    option proto 'static'
    option ipaddr '192.168.1.1'
    option netmask '255.255.255.0'
    option ip6assign '60'

config device
    option name 'eth0'
    option macaddr '12:34:56:78:90:ab'

config interface 'wan'
    option device 'eth0'
    option proto 'dhcp'

config interface 'wan6'
    option device 'eth0'
    option proto 'dhcpv6'

このファイルをuciコマンド経由で見ると

root@OpenWrt:~# uci show network
network.loopback=interface
network.loopback.device='lo'
network.loopback.proto='static'
network.loopback.ipaddr='127.0.0.1'
network.loopback.netmask='255.0.0.0'
network.globals=globals
network.globals.ula_prefix='fd56:d548:f6c2::/48'
network.@device[0]=device
network.@device[0].name='br-lan'
network.@device[0].type='bridge'
network.@device[0].ports='eth1'
network.@device[1]=device
network.@device[1].name='eth1'
network.@device[1].macaddr='ab:cd:ef:gh:ij:kl'
network.lan=interface
network.lan.device='br-lan'
network.lan.proto='static'
network.lan.ipaddr='192.168.1.1'
network.lan.netmask='255.255.255.0'
network.lan.ip6assign='60'
network.@device[2]=device
network.@device[2].name='eth0'
network.@device[2].macaddr='12:34:56:78:90:ab'
network.wan=interface
network.wan.device='eth0'
network.wan.proto='dhcp'
network.wan6=interface
network.wan6.device='eth0'
network.wan6.proto='dhcpv6'

このようになる。

OpenWrtはデフォルトでLANデバイスとWLANデバイスをブリッジしているため、br-lanというデバイスが定義されており、それがlanインターフェイスとなっている。 しかし、NanoPi R4SにはWLANデバイスはないためこのブリッジデバイスを削除しておく。

ファイルを直接編集してもいいしuciコマンドで設定してもいいが、今回はuciコマンドで設定を行う。

ついでにlanにあるipv6の設定も消しておく。

root@OpenWrt:~# uci delete network.@device[0]
root@OpenWrt:~# uci set network.lan.device=eth1
root@OpenWrt:~# uci delete network.lan.ip6assign='60'
root@OpenWrt:~# uci show network
network.loopback=interface
network.loopback.device='lo'
network.loopback.proto='static'
network.loopback.ipaddr='127.0.0.1'
network.loopback.netmask='255.0.0.0'
network.globals=globals
network.globals.ula_prefix='fd56:d548:f6c2::/48
network.@device[0]=device
network.@device[0].name='eth1'
network.@device[0].macaddr='ab:cd:ef:gh:ij:kl'
network.lan=interface
network.lan.device='eth1'
network.lan.proto='static'
network.lan.ipaddr='192.168.1.1'
network.lan.netmask='255.255.255.0'
network.@device[1]=device
network.@device[1].name='eth0'
network.@device[1].macaddr='12:34:56:78:90:ab'
network.wan=interface
network.wan.device='eth0'
network.wan.proto='dhcp'
network.wan6=interface
network.wan6.device='eth0'
network.wan6.proto='dhcpv6

commitを行うことにより設定ファイルが更新される。

root@OpenWrt:~# uci commit network
root@OpenWrt:~# cat /etc/config/network

config interface 'loopback'
    option device 'lo'
    option proto 'static'
    option ipaddr '127.0.0.1'
    option netmask '255.0.0.0'

config globals 'globals'
    option ula_prefix 'fd56:d548:f6c2::/48'

config device
    option name 'eth1'
    option macaddr 'ab:cd:ef:gh:ij:kl'

config interface 'lan'
    option device 'eth1'
    option proto 'static'
    option ipaddr '192.168.1.1'
    option netmask '255.255.255.0'

config device
    option name 'eth0'
    option macaddr '12:34:56:78:90:ab'

config interface 'wan'
    option device 'eth0'
    option proto 'dhcp'

config interface 'wan6'
    option device 'eth0'
    option proto 'dhcpv6'

パッケージの更新・追加

ここでNanoPiのWANポートに既存インターネット(DHCPでアドレスが割り当てられる)を接続する。

# 初回は証明書が切れてたりするためチェックをせずに行う
root@OpenWrt:~# opkg update --no-check-certificate

# Webインターフェイス
root@OpenWrt:~# opkg install luci-ssl

IPIP6パッケージの作成

v6プラス固定IPで使用するIPIP6トンネルに関するパッケージは公式にはないため、 github.com こちらを使用させていただいた。

カスタムビルドするのはかなり手間なのでパッケージを手作りすることにした。

# ipip6パッケージ作成

$ mkdir ipip6
$ cd ipip6

## control.tar.gzの作成

$ mkdir control
$ cd control

$ cat << EOS > ./control
Package: ipip6
Version: 0.1
Depends: libc, kmod-ip6-tunnel, resolveip
Source: feeds/base/package/network/ipv6/ipip6
SourceName: ipip6
License: GPL-2.0
Section: net
SourceDateEpoch: 1641284700
Maintainer: kenjiuno <ku@digitaldolphins.jp>
Architecture: all
Installed-Size: 2716
Description: Provides support for ipip6 (RFC2473) in /etc/config/network.
EOS

$ cat << EOS > ./postinst
#!/bin/sh
[ "${IPKG_NO_SCRIPT}" = "1" ] && exit 0
[ -s ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
. ${IPKG_INSTROOT}/lib/functions.sh
default_postinst $0 $@
EOS

$ cat << EOS > ./postinst-pkg
[ -n "${IPKG_INSTROOT}" ] || { rm -f /tmp/luci-indexcache
    rm -rf /tmp/luci-modulecache/
    killall -HUP rpcd 2>/dev/null
    exit 0
}
EOS

$ cat << EOS > ./prerm
#!/bin/sh
[ -s ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
. ${IPKG_INSTROOT}/lib/functions.sh
default_prerm $0 $@
EOS

$ chmod +x postinst postinst-pkg prerm

$ tar --numeric-owner --group=0 --owner=0 -czf ../control.tar.gz ./

$ cd ..

## data.tar.gzの作成

$ mkdir data
$ cd data

$ mkdir -p ./lib/netifd/proto
$ mkdir -p ./www/luci-static/resources/protocol

$ wget -O ./lib/netifd/proto/ipip6.sh https://github.com/HiraokaHyperTools/openwrt-ipip6/raw/main/net/ipv6/ipip6/files/ipip6.sh
$ wget -O ./www/luci-static/resources/protocol/ipip6.js https://github.com/HiraokaHyperTools/openwrt-ipip6/raw/main/net/ipv6/ipip6/files/ipip6.js

$ chmod +x ./lib/netifd/proto/ipip6.sh

$ tar --numeric-owner --group=0 --owner=0 -czf ../data.tar.gz ./

$ cd ..

## debian-binaryの作成

$ cat << EOS > ./debian-binary
2.0
EOS

## ipip6_0.1_all.ipkの作成

$ tar --numeric-owner --group=0 --owner=0 -czf ./ipip6_0.1_all.tar.gz ./debian-binary ./data.tar.gz ./control.tar.gz
$ mv ipip6_0.1_all.tar.gz ../ipip6_0.1_all.ipk

出来上がった、ipip6_0.1_all.ipkをNanoPiに持っていき、インストールする。

root@OpenWrt:~# opkg install ./ipip6_0.1_all.ipk

ここでNanoPiを再起動し、WANに挿していたケーブルをONU直結へと変える。

IPv6パススルーの設定

Webインターフェイス(https://192.168.1.1)を開き、Network->InterfaceにいきWAN6をEditする。

DHCP Serverのタブに行き、Set up DHCP Serverを押す。

IPv6 Settingsのタブに行き、Designated masterにチェックを入れ、全てrelay modeにする。

同様にLANも設定する。

今度はDHCPv6 Serviceのみserver modeにし、それ以外はrelay modeにする。

Announced IPv6 DNS serversにPublic DNSサーバのIPv6アドレスを入れる。

  • 2001:4860:4860::8888 # Google
  • 2606:4700:4700::1111 # CloudFlare

NDP-Proxy slaveにはチェックを入れない

次にAdvanced Settingsのタブに行きDHCP伝えるDNSサーバの設定をする 。

DHCP-Optionsのところに 6,8.8.8.8,1.1.1.1と入れる。

atmarkit.itmedia.co.jp

6がDNSの設定でそれ以降にコンマ区切りで、アドレスを入れる。

これでLAN側につなげたPCからIPv6アクセスが可能となる。

WAN6のインターフェイスIDの固定化

固定IPサービスのIPIP6トンネルを張るにはローカル(自身)のIPv6アドレスのインターフェイスIDが指定されたものでないといけないので上書きする。

root@OpenWrt:~# uci set network.wan6.ifaceid='::[ISPから通知されたインターフェイスID]'
root@OpenWrt:~# uci commit network

IPv4 over IPv6の設定

Add new interface...を押し、ProtocolIPv4 over IPv6 (ipip6)を選択する。

Tunnel remote IPv6 addressにv6プラスのBRのアドレスを入れる。 Your global IPv4 addressにはISPから通知された固定IPv4アドレスを入れる。 Local IPv6 addressは空にする(スクリーンショットでは例示用アドレスを入れてある)。

Firewall Settingsのタブでwanゾーンに適応する。

これで、IPv4でのアクセスが可能となる。

スナップショットのアップグレード

アップグレードを行うと設定は保持されるが、パッケージは初期状態になるので再度インストールする。

root@OpenWrt:~# wget https://downloads.openwrt.org/releases/22.03.0/targets/rockchip/armv8/openwrt-22.03.0-rockchip-armv8-friendlyarm_nanopi-r4s-squashfs-sysupgrade.img.gz
root@OpenWrt:~# sysupgrade -v ./openwrt-22.03.0-rockchip-armv8-friendlyarm_nanopi-r4s-squashfs-sysupgrade.img.gz

# 再起動がかかり更新される
root@OpenWrt:~# opkg update
root@OpenWrt:~# wget https://static.akashisn.info/packages/openwrt/ipip6_0.1_all.ipk
root@OpenWrt:~# opkg install ./ipip6_0.1_all.ipk
root@OpenWrt:~# opkg install luci-ssl luci-i18n-base-ja luci-i18n-firewall-ja luci-i18n-opkg-ja

# 以下お好みで
root@OpenWrt:~# opkg install map ds-lite iptables-mod-ipopt
root@OpenWrt:~# opkg install ca-certificates
root@OpenWrt:~# opkg install qrencode wireguard-tools luci-proto-wireguard luci-app-wireguard luci-i18n-wireguard-ja
root@OpenWrt:~# opkg install openvpn-openssl kmod-ovpn-dco openvpn-easy-rsa luci-app-openvpn luci-i18n-openvpn-ja
root@OpenWrt:~# opkg install ddns-scripts-cloudflare luci-app-ddns luci-i18n-ddns-ja
root@OpenWrt:~# opkg install acme acme-dnsapi luci-app-acme luci-i18n-acme-ja

root@OpenWrt:~# reboot

速度測定

2スレッドしか使われていないようだが、

十分に速度が出ている。

Ubuntu 20.04でIPoE + v6プラス固定IPルータをつくる

akashisn.hatenablog.com

Edgerouterではなぜかhwnatを有効化するとipipトンネルがおかしくなるみたいでhardware offloadを無効化していたのだが、やはりCPU性能がボトルネックになっていたのでノートパソコン(XPS13)でルータを作ってみた。

前回の記事での「RA Prefixの取得」を行って、

$ sudo apt install radvdump

を取得しているものとする。

環境

Ubuntu 20.04 Desktop XPS13-9360

  • eth0: ONUに直結
  • eth1: LAN側 (10.10.0.1/24)

Ubuntu Serverではないため、Network Managerが入っているがこれは以下で設定するnetplanと競合してしまったりするためアンインストールしておく。

$ sudo apt purge network-manager

(ここで再起動しておいたほうがいいかも)

Netplanを使用したIPアドレスとIPIPトンネリング

eth0に取得したIPv6 Prefix (240b:xxxx:xxxx:xxxx) とISPから通知されたインターフェイスID (yyyy:yyyy:yyyy:yyyy) を連結させたものをセットする。 IPv6デフォルトゲートウェイを隣ルータのリンクローカルアドレス (fe80::xxxx:xxxx:xxxx:xxxx) とする。

IPIPトンネルにはISPから通知された固定IP情報に記載されている[固定IPアドレス]と[BRアドレス]を使用する。 IPv4はこのトンネルを使用するようにする。

/etc/netplan/01-netcfg.yamlを以下の内容で作成。

(このディレクトリにある他の設定ファイルは拡張子を変えるとかして無効化しておく)

network:
  version: 2
  ethernets:
    eth0:
      accept-ra: false
      dhcp4: false
      dhcp6: false
      addresses:
        - 240b:xxxx:xxxx:xxxx:yyyy:yyyy:yyyy:yyyy/64
      routes:
        - to: ::/0
          via: fe80::xxxx:xxxx:xxxx:xxxx
    eth1:
      accept-ra: false
      addresses:
        - 10.10.0.1/24
        - 240b:xxxx:xxxx:xxxx::1/64
      nameservers:
        addresses:
          - 8.8.8.8
          - 2001:4860:4860::8888
  tunnels:
    ip6tnl1:
      mode: ipip6
      addresses:
        - [固定IPアドレス]
      remote: [BRアドレス]
      local: 240b:xxxx:xxxx:xxxx:yyyy:yyyy:yyyy:yyyy
      routes:
        - to: 0.0.0.0/0
          scope: link

Forwardingの設定

IPパケットの転送を有効化する。

/etc/sysctl.confを編集。以下のコメントを外す。

# Uncomment the next line to enable packet forwarding for IPv4
#net.ipv4.ip_forward=1
       ↓
net.ipv4.ip_forward=1
# Uncomment the next line to enable packet forwarding for IPv6
#  Enabling this option disables Stateless Address Autoconfiguration
#  based on Router Advertisements for this host
#net.ipv6.conf.all.forwarding=1
       ↓
net.ipv6.conf.all.forwarding=1
$ sudo sysctl -p 

ufwの設定

/etc/default/ufwを編集。DEFAULT_FORWARD_POLICYACCEPTにする。

# Set the default forward policy to ACCEPT, DROP or REJECT.  Please note that
# if you change this you will most likely want to adjust your rules
DEFAULT_FORWARD_POLICY="DROP"
       ↓
DEFAULT_FORWARD_POLICY="ACCEPT"

/etc/ufw/before.rulesを編集。最初の部分に以下を追加。

#
# rules.before
#
# Rules that should be run before the ufw command line added rules. Custom
# rules should be added to one of these chains:
#   ufw-before-input
#   ufw-before-output
#   ufw-before-forward
#

# ここから
# Masquarade
*nat
:POSTROUTING ACCEPT [0:0]
:PREROUTING ACCEPT [0:0]
-A POSTROUTING -s 10.10.0.0/24 -j MASQUERADE
COMMIT
# don't delete the 'COMMIT' line or these rules won't be processed
# ここまで追加

/etc/ufw/after.rules/etc/ufw/after6.rulesを同じように編集。最初の部分に以下を追加。

#
# rules.input-after
#
# Rules that should be run after the ufw command line added rules. Custom
# rules should be added to one of these chains:
#   ufw-after-input
#   ufw-after-output
#   ufw-after-forward
#

# ここから
*mangle
:POSTROUTING ACCEPT [0:0]
:PREROUTING ACCEPT [0:0]
-A FORWARD -o ip6tnl1 -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1412
COMMIT
# don't delete the 'COMMIT' line or these rules won't be processed
# ここまで追加

ufwを有効化する

$ sudo ufw enable

LANからLocalへのアクセスを許可する

$ sudo ufw allow from 10.10.0.0/24

DHCPサーバ・RAの設定

IPv4アドレスはDHCPサーバで配布して、IPv6アドレスはNDProxy+RAによる告知を行う。

isc-dhcp-serverの設定

$ sudo apt install isc-dhcp-server

/etc/default/isc-dhcp-serverを編集。DHCPv4サーバがlistenするインターフェイスを指定。

# On what interfaces should the DHCP server (dhcpd) serve DHCP requests?
#   Separate multiple interfaces with spaces, e.g. "eth0 eth1".
INTERFACESv4=""
INTERFACESv6=""
       ↓
INTERFACESv4="eth1"
#INTERFACESv6=""

/lib/systemd/system/isc-dhcp-server.serviceを編集。PIDファイルのパスを/var/run/dhcpd.pidへ変更。

    exec dhcpd -user dhcpd -group dhcpd -f -4 -pf /run/dhcp-server/dhcpd.pid -cf $CONFIG_FILE $INTERFACESv4'
       ↓
    exec dhcpd -user dhcpd -group dhcpd -f -4 -pf /var/run/dhcpd.pid -cf $CONFIG_FILE $INTERFACESv4'
$ sudo systemctl daemon-reload

最初からある設定ファイルの名前を変えておく。

$ sudo mv /etc/dhcp/dhcpd.conf /etc/dhcp/dhcpd.conf.sample

/etc/dhcp/dhcpd.confを以下の内容で作成。

# default
default-lease-time 600;
max-lease-time 7200;
ddns-update-style none;

authoritative;

subnet 10.10.0.0 netmask 255.255.255.0 {
  range 10.10.0.30 10.10.0.199;
  option routers 10.10.0.1;
  option domain-name-servers 1.1.1.1, 8.8.8.8;
  default-lease-time 3600;
  max-lease-time 7200;
}

radvdの設定

/etc/radvd.confを以下の内容で作成。

interface eth1   
{   
    AdvCurHopLimit 64;
    AdvLinkMTU 1500;
    AdvManagedFlag off;
    AdvOtherConfigFlag off;
    AdvReachableTime 0;
    AdvRetransTimer 0;
    AdvSendAdvert on;
    MaxRtrAdvInterval 600;
    prefix 240b:xxxx:xxxx:xxxx::/64 
    {
        AdvAutonomous on;
        AdvOnLink on;
        AdvValidLifetime 2592000;
    };
    RDNSS 2606:4700:4700::1111 2001:4860:4860::8888
    {
    };
};    
$ sudo apt install radvd

ndppdの設定

/etc/ndppd.confを以下の内容で作成。

proxy eth0 {
  router no
  timeout 500
  autowire yes
  keepalive yes
  retries 3
  ttl 30000
  240b:xxxx:xxxx:xxxx::/64 {
    iface eth1
  }
}
$ sudo apt install ndppd
$ sudo systemctl enable ndppd.service

ここで再起動する

速度計測

十分に速くなった!

Edgerouter Xでv6プラス固定IPを使う

目次

環境

脱PPPoEをするためにIPv4 over IPv6を使おうと思うのだが、一般に広く使われているv6プラス(MAP-E)や、transix(DS-Lite)などは、一つのIPv4アドレスを複数の契約者で共有するため利用できるポートが限られてしまう。 *1

そこで、固定IPオプションをつけることにより一つのIPv4アドレスを占有することができ任意のポートを使用できる。

www.jpne.co.jp

www.mfeed.ad.jp

これらは全てNGN網から固定IP終端装置に対してIPIPトンネルを張る。

v6プラス固定IP、transix固定IPのどちらかを選ぶことになるのだが、どうやら transix固定IPでは制限がかけられているらしいのでv6プラス固定IPを使用する。

qiita.com

しかし、v6プラス固定IPサービスを提供している事業者は数少ない。

www.jpne.co.jp

今回引っ越した新居には光コンセントがあったので小型ONUを簡単に出してくれるらしいフレッツコラボのenひかりを契約した。

前提

今回は光電話を契約しなかったため、/64プレフィックスがRAにより配布されているため、EdgerouterでNDproxyを導入する必要がある。

説明に用いている表記は、

# (configureモード)
$ (通常モード)

となっていて、configureモードから通常モードに移行した際はcommit; save; exitをしている。

RA Prefixの取得

回線の開通が済んだら、IPv6のPrefixを取得する。 ここで必要になるのは、

の二つ。

$ sudo radvdump
#
# radvd configuration generated by radvdump 2.16
# based on Router Advertisement from fe80::xxxx:xxxx:xxxx:xxxx
# received by interface eth0
#

interface eth0
{
        AdvSendAdvert on;
        # Note: {Min,Max}RtrAdvInterval cannot be obtained with radvdump
        AdvManagedFlag off;
        AdvOtherConfigFlag on;
        AdvReachableTime 300000;
        AdvRetransTimer 10000;
        AdvCurHopLimit 64;
        AdvDefaultLifetime 1800;
        AdvHomeAgentFlag off;
        AdvDefaultPreference medium;
        AdvSourceLLAddress on;

        prefix 240b:xxxx:xxxx:xxxx::/64
        {
                AdvValidLifetime 2592000;
                AdvPreferredLifetime 604800;
                AdvOnLink on;
                AdvAutonomous on;
                AdvRouterAddr off;
        }; # End of prefix definition

}; # End of interface definition

内部LAN側のIPv6アドレスの定義

enひかりから通知された固定IP情報に記載されているインターフェイスIDをyyyy:yyyy:yyyy:yyyyとすると、内部LANのアドレスは240b:xxxx:xxxx:xxxx:yyyy:yyyy:yyyy:yyyy/64となる。

# set interfaces switch switch0 address '240b:xxxx:xxxx:xxxx:yyyy:yyyy:yyyy:yyyy/64'
# set interfaces switch switch0 description Local
# set interfaces switch switch0 ipv6 dup-addr-detect-transmits 1
# set interfaces switch switch0 ipv6 router-advert cur-hop-limit 64
# set interfaces switch switch0 ipv6 router-advert link-mtu 1500
# set interfaces switch switch0 ipv6 router-advert managed-flag false
# set interfaces switch switch0 ipv6 router-advert max-interval 600
# set interfaces switch switch0 ipv6 router-advert other-config-flag true
# set interfaces switch switch0 ipv6 router-advert prefix '240b:xxxx:xxxx:xxxx::/64' autonomous-flag true
# set interfaces switch switch0 ipv6 router-advert prefix '240b:xxxx:xxxx:xxxx::/64' on-link-flag true
# set interfaces switch switch0 ipv6 router-advert prefix '240b:xxxx:xxxx:xxxx::/64' valid-lifetime 2592000
# set interfaces switch switch0 ipv6 router-advert reachable-time 0
# set interfaces switch switch0 ipv6 router-advert retrans-timer 0
# set interfaces switch switch0 ipv6 router-advert send-advert true

IPv6デフォルトゲートウェイを対岸のルータとする。

set protocols static route6 '::/0' next-hop 'fe80::xxxx:xxxx:xxxx:xxxx' interface eth0

IPIPトンネルを張る

enひかりから通知された固定IP情報に記載されている[固定IPアドレス]と[BRアドレス]を使用する。

# set interfaces ipv6-tunnel v6tun0 address [固定IPアドレス]
# set interfaces ipv6-tunnel v6tun0 description v6plus-static
# set interfaces ipv6-tunnel v6tun0 encapsulation ipip6
# set interfaces ipv6-tunnel v6tun0 firewall in name WAN_IN
# set interfaces ipv6-tunnel v6tun0 firewall local name WAN_LOCAL
# set interfaces ipv6-tunnel v6tun0 local-ip '240b:xxxx:xxxx:xxxx:yyyy:yyyy:yyyy:yyyy'
# set interfaces ipv6-tunnel v6tun0 mtu 1452
# set interfaces ipv6-tunnel v6tun0 multicast disable
# set interfaces ipv6-tunnel v6tun0 remote-ip '[BRアドレス]'
# set interfaces ipv6-tunnel v6tun0 ttl 64

NAT・ルーティングの設定

set service nat rule 5010 outbound-interface v6tun0
set service nat rule 5010 type masquerade
set protocols static interface-route 0.0.0.0/0 next-hop-interface v6tun0

mss-clampの設定

set firewall options mss-clamp interface-type all
set firewall options mss-clamp mss 1412

NDProxyによるIPv6環境

以下を参考にndppdを導入する

akashisn.hatenablog.com

速度測定

EdgerouterのCPU性能がボトルネックになってるよう...

次はx86_64なPCでVyOSを使ってやってみたいな

*1:自宅鯖(80/443)や一部のゲームで使用できない場合があるそう

最近のLaTeX事情

以前各環境でTeX Liveをインストールする記事を書いたのですが、最近はもっぱらDockerを使ってどの環境でも同じように\LaTeXを書いてるのでそれの紹介をします。

akashisn.hatenablog.com

akashisn.hatenablog.com

akashisn.hatenablog.com

VS Code + Remote Containers

VS CodeのRemote Development機能を使って\LaTeXの環境とおなじみのLaTeX WorkshopをContainerの中に閉じ込めていつでも同じ環境で作業することが出来ます。

marketplace.visualstudio.com

marketplace.visualstudio.com

Remote Developmentのインストール

Remote Developmentは、

  • Remote SSH
  • Remote Containers
  • Remote WSL (Windowsの場合)

の3つがセットになってるのでContainer機能だけでいい場合はRemote - Containersをインストールします。

marketplace.visualstudio.com

いつものように拡張機能からインストールします。

使い方

準備

VS Code\LaTeXのプロジェクトルートとなるフォルダを開きます。

ルートディレクトリに .devcontainer と言うフォルダを作ってその中に devcontainer.jsondocker-compose.yml と言うファイルを作ります。

そしてそれぞれ以下の内容を貼り付けます。

Dockerで使うイメージを今回は

arkark様のlatexmk-docker

github.com

を少しカスタマイズした

github.com

を使ってますが、\LaTeXの環境があれば何でもいいです。

devcontainer.json

  • name: このDevContainerの名前
  • dockerComposeFile: DevContainerの元となるイメージを指定するためのComposeファイル
  • extensions: Containerの中にインストールするVS Codeプラグイン
  • service: このDevContainerの中で動かすComposeサービスの名前
  • settings: Containerの中で有効なVS Codeの設定
    • latex-workshop.*: LaTeX Workshopに関する設定
  • workspaceFolder: WorkDirの設定(Dockerイメージと合わせる)
  • shutdownAction: DevContainerを停止するときにする動作

次に、 latexmkの設定をする。 フォルダルートに .latexmkrc と言うファイルを以下の内容で作成します。

これで準備完了

Containerの中に入る

左下の緑のところを押して

Reopen in Containerを選ぶ

すると、DockerのイメージをダウンロードしてContainerを実行してくれます。

左下の緑のところが "Dev Container:" となっていればContaeinrを開いています。

ビルド

TeXファイルを開いて左の端にある \TeXを押して "Build LaTex project" を押すとデフォルトの uplatex によるビルドが始まります。

そして、その下にある "View LaTeX PDF" を押すと、タブが別れてPDFのプレビューが表示されます。

ビルドされたPDFは、 out フォルダ以下に生成されます。

また、 lualatex でビルドしたいときは、

Ctrl + Shift + P を押して "LaTeX Workshop: Build with recipe" を選択して、

"latexmk with lualatex" を選択すると lualatex によるビルドが始まります。

一回ビルドを行うと保存を行うたびに再度ビルドがかかってプレビューも更新されるので便利

Edgerouter XでIPv6 IPoE + IPv4 over IPv6 (transix) と IPv4 PPPoEを併用する

目次

環境

大学の学生宿舎にて有料で提供されているインターネット接続サービスであるDoCanvasが、IPv6 IPoE と IPv4 over IPv6 に対応するようになるとのニュースがあったためEdgerouter Xで設定を行う。

また、オプションでグローバルIPアドレスサービスにも加入しているため、PPPoEのユーザ情報が手元にあり、今まで使っていた。

しかし、DoCanvasのPPPoEは通信速度が遅いのはまぁ許せるのですが、時々PPPoEのセッションが切れしばらく再接続されないみたいな状況が続いていた。 そのときにIPv6サービスの提供の話があり、これはやってみるしかないとなった。

また、自宅鯖を外部に公開するためにPPPoEは残しておいてPBR(Policy-Based Routing)により使い分ける。

前提

共用部で光終端装置とルータがあり、グローバルIPアドレスサービスを契約していない人でも各部屋までは来ているLANポートに指せば使えるようになっているため、もちろんDoCanvasには光電話のサービスはない。 そのため、共用部のルータから /64 のIPv6プレフィクスがRAにより配布されている。

また、配布されたIPv6プレフィックスを調べてみるとInternet Multifeedの物だったので transix のサービスが使えると思われる。

また、EdgerouterではRAをLAN側にも通知するためのNDProxy機能が標準では搭載されていないため、自前でビルドする必要がある。

説明に用いている表記は、

# (configureモード)
$ (通常モード)

となっていて、configureモードから通常モードに移行した際はcommit; save; exitをしている物とする。

PPPoEとDSLite(transix)によるIPv4環境

PPPoEの設定

まずは普通にPPPoEの設定をする

# set interfaces ethernet eth0 pppoe 0 default-route none
# set interfaces ethernet eth0 pppoe 0 description PPPoE
# set interfaces ethernet eth0 pppoe 0 mtu 1452
# set interfaces ethernet eth0 pppoe 0 name-server auto
# set interfaces ethernet eth0 pppoe 0 password ${PPPoE_PASS}
# set interfaces ethernet eth0 pppoe 0 user-id ${PPPoE_USER}

ここで重要なのがdefault-route noneとすること こうしないとPPPoEによる通信が優先されてしまう

NATの設定もする

# set service nat rule 5010 description 'masquerade for WAN(PPPoE)'
# set service nat rule 5010 outbound-interface pppoe0
# set service nat rule 5010 type masquerade

IPv6のプレフィックスを調べる

プレフィックスを調べるためautoconfによりアドレスの自動設定を行う。

# set interfaces ethernet eth0 ipv6 address autoconf
# set interfaces ethernet eth0 ipv6 dup-addr-detect-transmits 1
$ ip -6 address show dev eth0
4: eth0@itf0: <BROADCAST,MULTICAST,ALLMULTI,UP,LOWER_UP> mtu 1500 state UP qlen 1000
    inet6 24XX:XXXX:XXXX:XXXX:xxxx:xxxx:xxxx:xxxx/64 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::aaaa:bbbb:cccc/64 scope link
       valid_lft forever preferred_lft forever

eth0にリンクローカルアドレス(fe80::%)ではなく24XXから始まるグローバルアドレスが割り当てられたら、アドレスの上位64bit(24XX:XXXX:XXXX:XXXX)をメモしておく。

$ ip -6 neigh show dev eth0
fe80::feed lladdr 00:60:b9:aa:bb:cc router REACHABLE

また、対岸のルータのリンクローカルアドレス(fe80::feed)もメモしておく。

IPv6アドレスを静的設定する

EdgeOSではipip6トンネルを作成する際のローカルアドレスは手動設定であるため、先ほど割り当てられたプレフィックスから好きなtokenを設定する。

また、このアドレスを設定するのはLAN側のインターフェイスであるswitch0であることに注意する。

今回はIPv4アドレスに合わせて、::100:1というtokenを使用する。

# delete interfaces ethernet eth0 ipv6
# set interfaces switch switch0 address 172.16.100.1/24
# set interfaces switch switch0 address '24XX:XXXX:XXXX:XXXX::100:1/64'

IPv6のdefault routeを設定する

このままではswitch0は外部と繋がっていなく、通信できないためeth0を経由して先ほど調べた対岸のルータのリンクローカルアドレスをネクストホップとして設定する。

# set protocols static route6 '::/0' next-hop 'fe80::feed' interface eth0

DSLiteの設定

# set interfaces ipv6-tunnel v6tun0 description DSLite
# set interfaces ipv6-tunnel v6tun0 encapsulation ipip6
# set interfaces ipv6-tunnel v6tun0 local-ip '24XX:XXXX:XXXX:XXXX::100:1'
# set interfaces ipv6-tunnel v6tun0 mtu 1452
# set interfaces ipv6-tunnel v6tun0 multicast disable
# set interfaces ipv6-tunnel v6tun0 remote-ip '2404:8e01::feed:100'
# set interfaces ipv6-tunnel v6tun0 ttl 64

local-ipには先ほど設定したswitch0の静的アドレスを設定する。

remote-ipにはtransixのAFTRのアドレスを設定する。

  • NTT西日本エリア

    • 2404:8e01::feed:100
    • 2404:8e01::feed:101
  • NTT東日本エリア

    • 2404:8e00::feed:100
    • 2404:8e00::feed:101

ここで、v6tun0に対するNATの設定を行うと通信が不安定になるため行わない

ルーティングテーブルの設定

PBR(Policy-Based Routing)により、送信元IPアドレスによりどちらを経由して通信を行うかを設定するために、ルーティングテーブルを設定する。

# set protocols static table 1 interface-route 0.0.0.0/0 next-hop-interface pppoe0
# set protocols static table 2 interface-route 0.0.0.0/0 next-hop-interface v6tun0

Policy-Based Routingの設定

Policy-Based Routingはfirewallのmodifyで設定する。

IPアドレスが172.16.100.1-172.16.100.10の範囲をサーバセグメントとして、PPPoEによる通信を行い、 それ以外の172.16.100.11-172.16.100.254ではDSLiteによる通信を行うように設定する。

# set firewall modify LAN_PBR rule 10 action modify
# set firewall modify LAN_PBR rule 10 description 'LAN to WAN(PPPoE)'
# set firewall modify LAN_PBR rule 10 destination address '!172.16.100.0/24'
# set firewall modify LAN_PBR rule 10 modify table 1
# set firewall modify LAN_PBR rule 10 source address 172.16.100.1-172.16.100.10

# set firewall modify LAN_PBR rule 20 action modify
# set firewall modify LAN_PBR rule 20 description 'LAN to WAN(DSLite)'
# set firewall modify LAN_PBR rule 20 destination address '!172.16.100.0/24'
# set firewall modify LAN_PBR rule 20 modify table 2
# set firewall modify LAN_PBR rule 20 source address 172.16.100.11-172.16.100.254
# set interfaces switch switch0 firewall in modify LAN_PBR

switch0インターフェイスにこのルールを適応する。

これでIPアドレスにより、DSLiteとPPPoEによる通信を併用することが出来る。

NDProxyによるIPv6環境

LANにRAを配布する

# set interfaces switch switch0 ipv6 dup-addr-detect-transmits 1
# set interfaces switch switch0 ipv6 router-advert cur-hop-limit 64
# set interfaces switch switch0 ipv6 router-advert link-mtu 1500
# set interfaces switch switch0 ipv6 router-advert managed-flag false
# set interfaces switch switch0 ipv6 router-advert max-interval 600
# set interfaces switch switch0 ipv6 router-advert other-config-flag true
# set interfaces switch switch0 ipv6 router-advert prefix '::/64' autonomous-flag true
# set interfaces switch switch0 ipv6 router-advert prefix '::/64' on-link-flag true
# set interfaces switch switch0 ipv6 router-advert prefix '::/64' valid-lifetime 2592000
# set interfaces switch switch0 ipv6 router-advert reachable-time 0
# set interfaces switch switch0 ipv6 router-advert retrans-timer 0
# set interfaces switch switch0 ipv6 router-advert send-advert true

これにより、クライアントにIPv6アドレスが割り当てられる

NDProxyのクロスコンパイル

github.com

この実装を用いる

ローカルのコンピュータでDockerを用いてクロスコンパイルを行う

EdgeRouter Xはmipselで、EdgeRouter Lite 3はmipsであるため、それぞれの環境にあったコンパイラをインストールする

FROM debian:stretch

RUN apt-get update -y
RUN apt-get install -y g++-mipsel-linux-gnu g++-mips-linux-gnu make file

CMD ["/bin/bash"]
$ sudo docker buld -t mips .

ndppdをcloneする

$ git clone https://github.com/DanielAdolfsson/ndppd.git
$ cd ndppd

以下のように mipsel.Makefileを作成する

CXX      = /usr/bin/mipsel-linux-gnu-g++
CXXFLAGS = -O2 -s
LDFLAGS  = -s

ビルドする

(Local)$ sudo docker run --rm -it -v `pwd`:/ndppd mips
(docker)#  cd /ndppd
(docker)#  make -f mipsel.Makefile -f Makefile
(docker)#  file ndppd
ndppd: ELF 32-bit LSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /lib/ld.so.1, BuildID[sha1]=a2a060155ad0e5b70791ea5c06526695126abba7, for GNU/Linux 3.2.0, stripped
(docker)#  exit
(Local)$ scp ndppd er-x:

カレントディレクトリにndppdが生成されるのでscp等でEdgerouterに持って行く

NDProxyの設定

先ほど持ってきたndppd/config/user-data/ndppdに移動させる

$ sudo mv ndppd /config/user-data/ndppd

/config以下に配置することでファームウェアの更新の際も残る

それぞれ以下のようにファイルを作成する。

/config/user-data/ndppd.conf

proxy eth0 {
  router no
  timeout 500
  autowire yes
  keepalive yes
  retries 3
  ttl 30000
  rule ::/0 {
    iface switch0
  }
}

proxy switch0 {
  router yes
  timeout 500
  autowire yes
  keepalive yes
  retries 3
  ttl 30000
  rule ::/0 {
    auto
  }
}

/config/user-data/ndppd.service

[Unit]
Description=ndp_proxy_daemon
Wants=network-online.target
After=network-online.target

[Service]
ExecStart=/usr/sbin/ndppd -d -p /run/ndppd.pid
Type=forking
PIDFile=/run/ndppd.pid

[Install]
WantedBy=multi-user.target

/config/user-data/setup_ndppd.sh

#!/bin/sh

ln -s /config/user-data/ndppd /usr/sbin/ndppd
ln -s /config/user-data/ndppd.conf /etc/ndppd.conf
ln -s /config/user-data/ndppd.service /etc/systemd/system/ndppd.service

systemctl daemon-reload
systemctl start ndppd.service
systemctl enable ndppd.service

セットアップを行う。

$ sudo /bin/sh /config/user-data/setup_ndppd.sh

/config/user-data/setup_ndppd.shはファームウェアの更新などによって消えてしまった際に実行することでセットアップを行うことが出来る。

再起動を行い、クライアントからもIPv6によるアクセスが出来ることを確認する。

$ reboot

f:id:akashisn:20210329193109p:plain

f:id:akashisn:20210329193113p:plain

この通りかなりの改善をすることが出来た。

ファイアウォールの設定

このままでは危ないのでファイアウォールの設定行う

set firewall ipv6-name WANv6_IN default-action drop
set firewall ipv6-name WANv6_IN description 'WANv6 to LAN'
set firewall ipv6-name WANv6_IN enable-default-log
set firewall ipv6-name WANv6_IN rule 10 action accept
set firewall ipv6-name WANv6_IN rule 10 description 'Allow established/related'
set firewall ipv6-name WANv6_IN rule 10 state established enable
set firewall ipv6-name WANv6_IN rule 10 state related enable
set firewall ipv6-name WANv6_IN rule 20 action accept
set firewall ipv6-name WANv6_IN rule 20 description 'Allow IPv6 ICMP'
set firewall ipv6-name WANv6_IN rule 20 protocol ipv6-icmp
set firewall ipv6-name WANv6_IN rule 30 action drop
set firewall ipv6-name WANv6_IN rule 30 description 'Drop invalid state'
set firewall ipv6-name WANv6_IN rule 30 state invalid enable
set firewall ipv6-name WANv6_LOCAL default-action drop
set firewall ipv6-name WANv6_LOCAL description 'WANv6 to Router'
set firewall ipv6-name WANv6_LOCAL enable-default-log
set firewall ipv6-name WANv6_LOCAL rule 10 action accept
set firewall ipv6-name WANv6_LOCAL rule 10 description 'Allow established/related'
set firewall ipv6-name WANv6_LOCAL rule 10 state established enable
set firewall ipv6-name WANv6_LOCAL rule 10 state related enable
set firewall ipv6-name WANv6_LOCAL rule 20 action accept
set firewall ipv6-name WANv6_LOCAL rule 20 description 'Allow IPv6 ICMP'
set firewall ipv6-name WANv6_LOCAL rule 20 protocol ipv6-icmp
set firewall ipv6-name WANv6_LOCAL rule 30 action accept
set firewall ipv6-name WANv6_LOCAL rule 30 description 'Allow DHCPv6'
set firewall ipv6-name WANv6_LOCAL rule 30 destination port 546
set firewall ipv6-name WANv6_LOCAL rule 30 protocol udp
set firewall ipv6-name WANv6_LOCAL rule 40 action accept
set firewall ipv6-name WANv6_LOCAL rule 40 description 'Allow DSLite'
set firewall ipv6-name WANv6_LOCAL rule 40 protocol ipip
set firewall ipv6-name WANv6_LOCAL rule 50 action drop
set firewall ipv6-name WANv6_LOCAL rule 50 description 'Drop invalid state'
set firewall ipv6-name WANv6_LOCAL rule 50 state invalid enable

set interfaces ethernet eth0 firewall in ipv6-name WANv6_IN
set interfaces ethernet eth0 firewall local ipv6-name WANv6_LOCAL

set firewall name DSLite_IN default-action drop
set firewall name DSLite_IN description 'WAN(DSLite) to LAN'
set firewall name DSLite_IN rule 10 action accept
set firewall name DSLite_IN rule 10 description 'Allow established/related'
set firewall name DSLite_IN rule 10 state established enable
set firewall name DSLite_IN rule 10 state related enable
set firewall name DSLite_IN rule 20 action drop
set firewall name DSLite_IN rule 20 description 'Drop invalid state'
set firewall name DSLite_IN rule 20 state invalid enable
set firewall name DSLite_LOCAL default-action drop
set firewall name DSLite_LOCAL description 'WAN(DSLite) to Router'
set firewall name DSLite_LOCAL rule 10 action accept
set firewall name DSLite_LOCAL rule 10 description 'Allow established/related'
set firewall name DSLite_LOCAL rule 10 state established enable
set firewall name DSLite_LOCAL rule 10 state related enable
set firewall name DSLite_LOCAL rule 20 action drop
set firewall name DSLite_LOCAL rule 20 description 'Drop invalid state'
set firewall name DSLite_LOCAL rule 20 state invalid enable

set interfaces ipv6-tunnel v6tun0 firewall in name DSLite_IN
set interfaces ipv6-tunnel v6tun0 firewall local name DSLite_LOCAL

set firewall name PPPoE_IN default-action drop
set firewall name PPPoE_IN description 'WAN(PPPoE) to LAN'
set firewall name PPPoE_IN rule 10 action accept
set firewall name PPPoE_IN rule 10 description 'Allow established/related'
set firewall name PPPoE_IN rule 10 state established enable
set firewall name PPPoE_IN rule 10 state related enable
set firewall name PPPoE_IN rule 20 action drop
set firewall name PPPoE_IN rule 20 description 'Drop invalid state'
set firewall name PPPoE_IN rule 20 state invalid enable
set firewall name PPPoE_LOCAL default-action drop
set firewall name PPPoE_LOCAL description 'WAN(PPPoE) to Router'
set firewall name PPPoE_LOCAL rule 10 action accept
set firewall name PPPoE_LOCAL rule 10 description 'Allow established/related'
set firewall name PPPoE_LOCAL rule 10 state established enable
set firewall name PPPoE_LOCAL rule 10 state related enable
set firewall name PPPoE_LOCAL rule 20 action accept
set firewall name PPPoE_LOCAL rule 20 description 'Allow ping'
set firewall name PPPoE_LOCAL rule 20 destination group address-group ADDRv4_pppoe0
set firewall name PPPoE_LOCAL rule 20 log disable
set firewall name PPPoE_LOCAL rule 20 protocol icmp
set firewall name PPPoE_LOCAL rule 30 action drop
set firewall name PPPoE_LOCAL rule 30 description 'Drop invalid state'
set firewall name PPPoE_LOCAL rule 30 state invalid enable

set interfaces ethernet eth0 pppoe 0 firewall in name PPPoE_IN
set interfaces ethernet eth0 pppoe 0 firewall local name PPPoE_LOCAL

set firewall options mss-clamp interface-type pppoe
set firewall options mss-clamp mss 1412

WindowsでYubikey5を使ってみる

前提条件

GPGキーを作成し、Yubikeyに移動させた状態とする。

まだの場合は以下を参照してください。 akashisn.hatenablog.com akashisn.hatenablog.com

環境

  • Windows 10 Pro (20H2 - 19042.685)

Gpg4winのインストール

www.gpg4win.org

https://files.gpg4win.org/ から最新版をダウンロードする。

KleopatraGpgEX にチェックを入れてインストールする。

gpg4win install option

text.baldanders.info

  • Kleopatra は GnuPGGUI ラッパーで OpenPGP 鍵および X.509 電子証明書の管理を行う。
  • GpgEX は Windows Explorer拡張機能で, Explorer 上からファイルの暗号化や復号を行うことができる。 個人的に GpgEX はかなり使い勝手がいいのでお勧めである。

公開鍵のインポート

公開鍵のエクスポート でエクスポートした公開鍵をインポートする。

> gpg --import .\gpg-BF8A708B020A19BB-2020-12-09.txt
gpg: 鍵BF8A708B020A19BB: 公開鍵"<名前> <メールアドレス>"をインポートしました
gpg:           処理数の合計: 1
gpg:             インポート: 1

インポートした公開鍵を究極的に信用する(自分の物のため)。

trust -> 5 -> y と入力する。

> gpg --edit-key BF8A708B020A19BB
gpg (GnuPG) 2.2.25; Copyright (C) 2020 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.


pub  nistp521/BF8A708B020A19BB
     作成: 2020-12-09  有効期限: 無期限      利用法: C
     信用: 不明の        有効性: 不明の
sub  nistp521/983982062E1BB33D
     作成: 2020-12-09  有効期限: 無期限      利用法: S
sub  nistp521/008734F36B087C1C
     作成: 2020-12-09  有効期限: 無期限      利用法: E
sub  nistp521/26BAE38657860920
     作成: 2020-12-09  有効期限: 無期限      利用法: A
[  不明  ] (1). <名前> <メールアドレス>

gpg> trust
pub  nistp521/BF8A708B020A19BB
     作成: 2020-12-09  有効期限: 無期限      利用法: C
     信用: 不明の        有効性: 不明の
sub  nistp521/983982062E1BB33D
     作成: 2020-12-09  有効期限: 無期限      利用法: S
sub  nistp521/008734F36B087C1C
     作成: 2020-12-09  有効期限: 無期限      利用法: E
sub  nistp521/26BAE38657860920
     作成: 2020-12-09  有効期限: 無期限      利用法: A
[  不明  ] (1). <名前> <メールアドレス>

他のユーザの鍵を正しく検証するために、このユーザの信用度を決めてください
(パスポートを見せてもらったり、他から得たフィンガープリントを検査したり、などなど)

  1 = 知らない、または何とも言えない
  2 = 信用し ない
  3 = まぁまぁ信用する
  4 = 充分に信用する
  5 = 究極的に信用する
  m = メーン・メニューに戻る

あなたの決定は? 5
本当にこの鍵を究極的に信用しますか? (y/N) y

pub  nistp521/BF8A708B020A19BB
     作成: 2020-12-09  有効期限: 無期限      利用法: C
     信用: 究極          有効性: 不明の
sub  nistp521/983982062E1BB33D
     作成: 2020-12-09  有効期限: 無期限      利用法: S
sub  nistp521/008734F36B087C1C
     作成: 2020-12-09  有効期限: 無期限      利用法: E
sub  nistp521/26BAE38657860920
     作成: 2020-12-09  有効期限: 無期限      利用法: A
[  不明  ] (1). <名前> <メールアドレス>
プログラムを再起動するまで、表示された鍵の有効性は正しくないかもしれない、
ということを念頭においてください。

gpg> q

SSHキーとして使う

GPGの認証SubキーをSSHキーの代わりに使うことができる。 そのため、今まで複数の鍵を管理していたのをYubikeyにある取り出せない秘密鍵に集約することが出来るため利便性も向上しセキュリティ的にも良い感じになる。

scdaemonの設定

Windows10には既に仮想スマートカードリーダ(Windows Helloなど)がインストールされているため、scdaemonがちゃんとYubikeyを使用するようにする必要がある。

Yubikeyがささった状態で以下でデバイスを取得することが出来るので、 デバイス名を%APPDATA%\gnupg\scdaemon.conf に設定してあげる。

> Get-PnpDevice -Class SoftwareDevice | Where-Object {$_.FriendlyName -like "*YubiKey*"} | Select-Object -ExpandProperty FriendlyName
Yubico YubiKey OTP+FIDO+CCID 0
reader-port Yubico YubiKey OTP+FIDO+CCID 0

gpg-agentの設定

gpg-agentはssh-agentとしても動作することが出来るので、 %APPDATA%\gnupg\gpg-agent.confに設定する。

enable-ssh-support
enable-putty-support

設定を適応するためにagentを再起動する

> gpg-connect-agent killagent /bye
OK closing connection

> gpg-connect-agent /bye
gpg-connect-agent: gpg-agentが実行されていません - 'C:\Program Files (x86)\GnuPG\bin\gpg-agent.exe'を開始します
gpg-connect-agent: agent の起動のため、5秒待ちます...
gpg-connect-agent: agent への接続が確立しました。

認識しているかを確認する。

> gpg --card-status
Reader ...........: Yubico YubiKey OTP FIDO CCID 0
Application ID ...: Dxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 
Application type .: OpenPGP
Version ..........: 3.4
Manufacturer .....: Yubico
Serial number ....: xxxxxxxx
Name of cardholder: <名><姓>
Language prefs ...: ja
Salutation .......:
URL of public key :
Login data .......: <Email等>
Signature PIN ....: 強制なし
Key attributes ...: nistp521 nistp521 nistp521
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 9
KDF setting ......: off
Signature key ....: xxxx xxxx xxxx xxxx xxxx xxxx xxxx
      created ....: 2020-12-09 14:35:35
Encryption key....: xxxx xxxx xxxx xxxx xxxx xxxx xxxx
      created ....: 2020-12-09 14:36:18
Authentication key: xxxx xxxx xxxx xxxx xxxx xxxx xxxx
      created ....: 2020-12-09 14:37:21
General key info..: sub  nistp521/983982062E1BB33D 2020-12-09 <名前> <メールアドレス>
sec#  nistp521/BF8A708B020A19BB 作成: 2020-12-09 有効期限: 無期限
ssb>  nistp521/983982062E1BB33D 作成: 2020-12-09 有効期限: 無期限
                                 カード番号: 0006 xxxxxxxxx
ssb>  nistp521/008734F36B087C1C 作成: 2020-12-09 有効期限: 無期限
                                 カード番号: 0006 xxxxxxxxx
ssb>  nistp521/26BAE38657860920 作成: 2020-12-09 有効期限: 無期限
                                 カード番号: 0006 xxxxxxxxx

SSH形式の公開鍵に変換する

> gpg --export-ssh-key BF8A708B020A19BB > ~/.ssh/gpg-BF8A708B020A19BB.pub

作成した公開鍵は普通のSSHの公開鍵と同じように扱う。 つまり、接続先の~/.ssh/authorized_keysに追記する。

agent socketのproxyを行う

Windows 10にネイティブにあるOpenSSHではGnuPGが出力するsocketをうまく扱えないようなので、 github.com

socketファイルをWindowsの名前付きパイプにプロキシしてくれるプログラムを使用する。

github.com

最新版の wsl2-gpg-agent.exe を適当なところ(今回は C:\tools\utils\wsl2-gpg-agent\wsl2-gpg-agent.exe においた) にコピーする。

> wsl2-gpg-agent.exe --winssh ssh-pageant

と実行するとpageant\\.\pipe\ssh-pageantにプロキシした状態でシステムトレイに常駐するようになる。

この状態で、SSH_AUTH_SOCK=\\.\pipe\ssh-pageantという環境変数をセットすることによりsshクライアントはプロキシされたgpg-agentを参照するようになる

接続テストをするために既存の ~/.ssh/configでの設定をコメントアウトしておく。

> ssh git@github.com
PTY allocation request failed on channel 0
Hi AkashiSN! You've successfully authenticated, but GitHub does not provide shell access.
Connection to github.com closed.

github.comにsshをすると、このようにPINを聞かれてちゃんと接続できることが確認できる。

設定の永続化

自動起動の設定

$UserProperty = $(Get-ItemProperty 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders')
$WsShell = New-Object -ComObject WScript.Shell
$Shortcut = $WsShell.CreateShortcut($UserProperty.Startup+"\wsl2-gpg-agent.exe.lnk")
$Shortcut.TargetPath = "C:\tools\utils\wsl2-gpg-agent\wsl2-gpg-agent.exe"
$Shortcut.Arguments = "--winssh ssh-pageant"
$Shortcut.WorkingDirectory = "C:\tools\utils\wsl2-gpg-agent"
$Shortcut.IconLocation = "C:\tools\utils\wsl2-gpg-agent\wsl2-gpg-agent.exe"
$Shortcut.Save()

環境変数の設定

環境変数も設定しておく

SSH_AUTH_SOCK=\\.\pipe\ssh-pageant

SSHの設定

~/.ssh/config で何か設定するときは、

Host github.com
    HostName        github.com
    IdentityFile    ~/.ssh/gpg-BF8A708B020A19BB.pub
    IdentitiesOnly  yes

このように、IdentityFile に先ほど作成した公開鍵を設定すると良い。

Git for Windowsの設定

Git for Windowsにはsshコマンドが付属しているため、WindowsネイティブのSSHを使うように設定しないとうまく動かない。

> git config --global core.sshCommand 'C:/Windows/System32/OpenSSH/ssh.exe'

GitHubでのコミット署名

gpg-BF8A708B020A19BB-2020-12-09.txt の公開鍵を https://github.com/settings/keys に設定する。

Gitの設定を行う。

> git config --global user.signingkey BF8A708B020A19BB
> git config --global gpg.program 'C:/Program Files (x86)/gnupg/bin/gpg.exe'

デフォルトで署名するには以下を実行する。

> git config --global commit.gpgsign true

手動でコミットに署名をするときは -S オプションを加える。

$ git commit -S -m your commit message

このときOpenPGPのPINを求められる。

ちゃんと署名できていればこんな感じにVerifyマークがつく

WSL2で使う

WSL2ではUSBシリアルをサポートしていないため直接Yubikeyに接続することは出来ない。

そこで、Windowsの名前付きパイプをUnixソケットへプロキシしてAgent転送を行うことでWSL2でもYubikeyを使うことが出来る。

先ほどの wsl2-gpg-agent.exe を使う。

公開鍵のインポート

Windowsで行ったのと同様に公開鍵をインポートして、信用しておく。

WSL2側の設定

socat, lsof のインストールをする

$ sudo apt update
$ sudo apt install socat iproute2

.zshrcに以下を追加してログイン時に起動するようにする

wsl2_ssh_pageant_bin="/mnt/c/tools/wsl2-ssh-pageant/wsl2-ssh-pageant.exe"
if test -x "$wsl2_ssh_pageant_bin"; then

  export SSH_AUTH_SOCK="$HOME/.gnupg/S.gpg-agent.ssh"
  if ! ss -a | grep -q "$SSH_AUTH_SOCK"; then
    rm -f "$SSH_AUTH_SOCK"
    (setsid nohup socat UNIX-LISTEN:"$SSH_AUTH_SOCK,fork" EXEC:"$wsl2_ssh_pageant_bin --ssh" >/dev/null 2>&1 &)
  fi

  export GPG_AGENT_SOCK="$HOME/.gnupg/S.gpg-agent"
  if ! ss -a | grep -q "$GPG_AGENT_SOCK"; then
    rm -rf "$GPG_AGENT_SOCK"
    (setsid nohup socat UNIX-LISTEN:"$GPG_AGENT_SOCK,fork" EXEC:"$wsl2_ssh_pageant_bin --gpg S.gpg-agent" >/dev/null 2>&1 &)
  fi

  export GPG_AGENT_EXTRA_SOCK="$HOME/.gnupg/S.gpg-agent.extra"
  if ! ss -a | grep -q "$GPG_AGENT_EXTRA_SOCK"; then
    rm -rf "$GPG_AGENT_EXTRA_SOCK"
    (setsid nohup socat UNIX-LISTEN:"$GPG_AGENT_EXTRA_SOCK,fork" EXEC:"$wsl2_ssh_pageant_bin --gpg S.gpg-agent.extra" >/dev/null 2>&1 &)
  fi

else
  echo >&2 "WARNING: $wsl2_ssh_pageant_bin is not executable."
fi
unset wsl2_ssh_pageant_bin

再度ログインしてYubikeyが認識しているかを確認する。

$  gpg --card-status
Reader ...........: Yubico YubiKey OTP FIDO CCID 0
Application ID ...: Dxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 
Application type .: OpenPGP
Version ..........: 3.4
Manufacturer .....: Yubico
Serial number ....: xxxxxxxx
Name of cardholder: <名><姓>
Language prefs ...: ja
Salutation .......:
URL of public key :
Login data .......: <Email等>
Signature PIN ....: 強制なし
Key attributes ...: nistp521 nistp521 nistp521
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 9
KDF setting ......: off
Signature key ....: xxxx xxxx xxxx xxxx xxxx xxxx xxxx
      created ....: 2020-12-09 14:35:35
Encryption key....: xxxx xxxx xxxx xxxx xxxx xxxx xxxx
      created ....: 2020-12-09 14:36:18
Authentication key: xxxx xxxx xxxx xxxx xxxx xxxx xxxx
      created ....: 2020-12-09 14:37:21
General key info..: sub  nistp521/983982062E1BB33D 2020-12-09 <名前> <メールアドレス>
sec#  nistp521/BF8A708B020A19BB 作成: 2020-12-09 有効期限: 無期限
ssb>  nistp521/983982062E1BB33D 作成: 2020-12-09 有効期限: 無期限
                                 カード番号: 0006 xxxxxxxxx
ssb>  nistp521/008734F36B087C1C 作成: 2020-12-09 有効期限: 無期限
                                 カード番号: 0006 xxxxxxxxx
ssb>  nistp521/26BAE38657860920 作成: 2020-12-09 有効期限: 無期限
                                 カード番号: 0006 xxxxxxxxx

これでWSL2でも使用することができたので後は普通のLinuxと同様にコミット署名とかを設定する。

トラブルシューティング

WSL2でいきなり動かなくなったら、

> wsl --shutdown

でwslの再起動を行えばたいてい治る

参考