ハード」カテゴリーアーカイブ

ラズパイを使ったPC電源スイッチ

手元にないPCの電源スイッチを押すのがめんどくさい。VPNで自宅ネットワークに接続して、デスクトップPCの電源を入れたい。というような解決として、Wake on LAN を試行錯誤していたが、マザーボードの設定やWindowsの電源管理とかWoLの信号出すアプリとか、意外と面倒なので、いっそ物理電源ボタンを押す装置を作った方が早い気がしたので作ってみた。

前提

  • Raspberry Pi WHを常時起動しておく。
  • M/Bにつながっている電源スイッチを分岐させる
  • ちょっとした電子工作とスクリプト
  • PCやスマホからRaspiにログインしてスクリプトを実行させる環境

RasPiの用意

その辺にあるRasPi WHを使う。消費電力は1W以下なので、おそらく1年間電源を入れっぱなしにしても100–200円くらいにしかならない。OSはRaspberry Pi Imagerを使ってRaspberryh Pi OS Lite(32 bit)をいれた。本当は有線LANの方がいいのだろうが、面倒なので無線。
最低限の設定をして、アップデートをしておく。

マザーボードの電源スイッチの分岐

PCの自作をしたことある人なら自明の、マザーボードにつなぐ電源スイッチのケーブルの手前に分岐用のケーブルを自作して挿入しておく。「ピンケーブル 分岐 電源スイッチ」とかでググると製品が出てくるけど、単なる電源スイッチの分岐なので、手元にあるピンケーブルで自作した。一応熱収縮チューブで保護だけはした。
ちなみに電源スイッチは200Ωぐらいで短絡させると反応するようだ。

電源を短絡させるための回路

最初はGPIOだけでON・OFFしようかと思ったけど、先人がいろいろ考えてくれていて、参考にさせてもらった。( http://purose.net/fanout/index.php?Raspberry%20Pi%20で%20PC%20の電源ボタンを押す )
参考のサイトではトランジスタに2SC945を使っていたけど、手元にあったのは2SC1815なので、代用とする。

参考サイトではGPIO17(PIN11)とGND(PIN9)以外に、RasPiそのものの電源をPC内部からもらっているが、PC側の電源を触るのは面倒なのとちょっとこわいので、今回は普通にUSB電源を使った。

RasPiの電源ON・OFFのために、PIN5とPIN6にタクトスイッチもつけておいた。(図では省略)

先ほどのマザボの分岐ケーブルにGNDとコレクタのM/B PWR SWのケーブルを接続しておく。
これで、GPIO17をHighにすることでPWR SWとGNDで電流が流れてPCの物理スイッチを押したと同じことになる。

RasPiにPC用電源ON・OFFのスクリプトを書く

$ cat push.sh 
#!/bin/bash

/usr/bin/raspi-gpio set 17 op
/usr/bin/raspi-gpio set 17 pu
/usr/bin/raspi-gpio set 17 dl
/usr/bin/raspi-gpio set 17 dh
sleep 0.5
/usr/bin/raspi-gpio set 17 dl

昔はgpioというコマンドがあったのだが、非推奨になったらしい。代わりにraspi-gpioというコマンドがあるので、そちらを使う。

sleepで押す時間を調整。0.5秒は手で推した時間を想定しているが、ここを長くすれば長押しができる。
0.5秒で電源が入るのを確認できた。ちなみにPCの電源が入っているときの電源ボタンの挙動はWindows側で決められるので、同じスクリプトで起動中からスリープに入るとかもできた。

RasPiの電源ON・OFF用の設定

PIN5とPIN6を短絡させるとRasPiの電源を入れられるらしい。知らなかった。
ここにスイッチをつけておいたが、同時にシャットダウンボタンも兼ねることにする。

dtoverlay=gpio-shutdown,gpio_pin=3,debounce=3000

/boot/config に上記の設置をしておくと、3000msecの長押しでシャットダウンに入る。便利だ。

iPhone用スクリプト

ここまでで、raspiにsshでログインしてpush.shを起動すればPCの電源をONできる。でも、微妙に面倒くさい。スマホでポチッとPC電源を入れたい。

iPhoneのスクリプトの機能を使ってSSHログインしてスクリプトを実行させる。
既存の鍵を登録させるのは難しそうなので、iPhoneで作った公開鍵をraspi側の.ssh/authorized_keysに登録することにした。
これで、どこでもPCの電源が入れられるようになった。

当然だが、ルーティングできる範囲内でしか動作しない。地球の裏側からでも電源を入れたい場合はVPNの環境が必要。

お手軽VPNのWireGuard

WireGuardの概要

WireGuard(https://www.wireguard.com/)はオープンソースのVPNです。まず最初にLinuxのカーネル向けに、そして、MacOSやWindows,iOS,Andoroidなど、多くのOSがサポートされています。Linuxではカーネルレベルで動作しており、WireGuardはアプリケーションではなく、インターフェースとして認識され、eth0やwlan0と同じく、wg0と言った新しいインターフェースを追加してVPNを扱うことが出来ます。したがって、ifconfigやip,iptablesといったコマンドがそのまま使えます。

以下に公式ページを参考にしつつ特徴を書いておきます。

ネットワーク

ネットワーク的には上記の通りインターフェースを追加するので、レイヤー3上に暗号化トンネルを作ることになります。使用するプロトコルはUDPで好きなポート番号を選ぶことが出来ます。トンネルの中を通すことが出来るのはIPプロトコルでv4もv6もどちらも行けます。

認証鍵

認証は2種類のキーを使います。サーバ側が持つプライベートキーとパブリックキーのキーペアーと、クライアント側のプライベートキーとパブリックキーのペアーです。このあたりはSSHの鍵の考え方とよく似ています。VPNでよくある、シェアードキーの設定も出来ます。

暗号化技術

暗号化やハッシュは最新の物が使われています。Curve25519, ChaCha20, Poly1305, BLAKE2, SipHash24, HKDFが使われていますが、難しすぎてよく分かりませんので、飛ばします。それぞれ、鍵交換、暗号化、認証、ハッシュ、ハッシュテーブル鍵、鍵導出だと思います。

攻撃の最小化

ソースコードや設定ファイルの行数が少ないのも特徴です。行数を減らすことによって、脆弱性やミスの入り込む余地が減り、レビューも簡単になります。VPNの設定はネットワーク機器でもアプリでも複雑になりがちですが、非常に簡単で分かりやすくなっています。

ハイパフォーマンス

カーネルの一部となっているため、高速に動作します。OpenVPN等と比較してもスループットの割にCPUの使用率が少なくなっています。

インストール

今回はうちで余っていたRasPi3にインストールしてみます。もともと自宅内DNSとして使ってましたが、OSの入れ替えなどでDNSを別RasPiにした際の、あまりRasPiです。

$ cat /proc/device-tree/model
Raspberry Pi 3 Model B Rev 1.2
$ cat /etc/os-release
PRETTY_NAME="Raspbian GNU/Linux 10 (buster)"
NAME="Raspbian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=raspbian
ID_LIKE=debian
HOME_URL="http://www.raspbian.org/"
SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"

Raspberry pi OSはbusterまでアップグレードしてあります。

WireGuardはDebian系は、busterの次のバージョンであるbullseyeからの対応になりますが、パッケージのソースにbackportsを指定するとbusterでも使えるようになります。/etc/apt/source.listに追加をしておきます。2行目の記述です。

$ cat /etc/apt/sources.list
deb http://ftp.jaist.ac.jp/raspbian/ buster main contrib non-free rpi
deb http://deb.debian.org/debian buster-backports main

ソースを追加すると鍵がないと言われたので、gpgコマンドでキーサーバから取得しました。(普通は必要ないと思われる)
gpg –keyserver キーサーバ –recv-keys 鍵、gpg -a –expprt 鍵 |sudo apt-key add -、といったコマンドですね。

あとは普通にaptでインストールです。カーネルヘッダーが同時にインストールされなかったので、個別に入れました。

$ sudo apt-get update
$ sudo apt install wireguard
$ sudo aptitude install raspberrypi-kernel-headers

インストール時に終わっているかも知れませんが、今回インストールしたWireGuardをカーネルモジュールとして取り込みます。modprobeコマンドですね。確認はlsmodで見ることができます。

$ sudo modprobe wireguard

サーバの設定

インストールされるとwgコマンドとwg-quickコマンドが使えるようになります。wgがWireGuardの鍵を作ったり、インターフェースやピアの設定をするコマンドになります。wg-quickは設定ファイルに基づいてインターフェースのアップ、ダウンなどが出来るようになります。

鍵の作成

WireGuardはサーバ、クライアントではなくsite-to-siteのVPNが出来るのですが、便宜上おうちの中にあるRasPiをサーバ、外で使うPCやスマートフォンをクライアントとして扱います。鍵はどこでも作れるのですが、今回はサーバの中で作るということにします。

鍵ペアの作成はプライベートキーを作ってからパブリックキーを作る流れになります。プライベートキーは他の人に知られてはいけないので、パーミッションを600にしておく必要があるのですが、デフォルトでファイルを作ると644のパーミッションになってしまいwgコマンドに拒否されるので、umaskで作られるファイルのパーミッションを600になるようにしておきます。先にtouchコマンドでファイルを作っておいてファイルのパーミッションを600にしておけば怒られないし、umaskを変える必要はありません。

$ umask 077
$ wg genkey > privatekey

umaskを元に戻す場合は、umask 033です。

プライベートキーからパブリックキーを作成します。

$ wg pubkey < privatekey > publickey

同様にクライアントの鍵も作ります。

$ wg genkey > cl-privatekey
$ wg pubkey < cl-privatekey > cl-publickey

サーバ側のインターフェースとピアの設定

この先、ipコマンドやwgコマンドを駆使してインターフェースの作成やピアの設定など出来るのですが、最終的に設定ファイルを作って自動起動するので、最初から設定ファイルを作ることにします。サーバ側の設定ファイルは/etc/wireguard/wg0.confとします。wg0はWireGuard用に作る新規のインターフェース名です。

[Interface]
ListenPort = 51820
Address = 192.168.1.1/24, fdfd:abcd:0001::1/64
PrivateKey = (サーバのプライベートキー)

[Peer]
PublicKey = (クライアントのパブリックキー)
AllowedIPs = 192.168.1.100/32, fd00:abcd:0001::100/128

[Interface]は自ホストの設定です。
ListenPortは文字通り受付用のポート番号です。ファイヤウォールなり、ポートフォワーディングなりで、サーバのListenポートを開けておく必要があります。tcpではなくudpなので注意です。
Addressはインターフェースに振るアドレスで、IPv4とIPv6の両方が使えます。適当なプライベートアドレスとユニークローカルアドレスをつけます。IPv4であれば、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16のプライベートアドレスの範囲から使います。サブネットも同時にしてしておきます。家庭用だったら/24で十分かと思います。IPv6であれば、fd00::/8のユニークローカルIPv6ユニキャストアドレス(ULA)から使います。サブネットは/64が適当かと思います。
PrivateKeyは先ほど作ったサーバのプライベートキー(privatekey)です。直接書き込む場合は設定ファイルのパーミッションを600にします。

[Peer]はクライアントの設定です。鍵ペア毎に作ります。複数クライアントがある場合は[Peer]が沢山並びます。
PublicKeyはクライアントのパブリックキー(cl-publickey)です。いわゆるユーザIDは存在せず、PublickKeyがその代わりとなります。
AllowedIPsはサーバの設定ファイルで書く場合は、受信可能なクライアントのアドレスとなります(クライアントの設定では意味が異なります)。今回の場合はサブネットがv4とv6でそれぞれ/32,/128となっていますので、1台のみの接続しか出来ません。アドレスを変えての鍵の使い回しによる同時接続が出来ません。

サーバ内部のネットワークの設定

上記の設定だけだとクライアントはWireGuardをインストールしたホストのみしか通信が出来ません。実際の運用では、サーバを踏み台にしてLAN内の他のホストと通信したり、インターネットと通信したいのが普通かと思います。そのため、wg0とeth0の間で通信が出来るようにし、追加のルーティングの設定をしなくて言いように、NAPT(IPマスカレード)の設定を追加します。

まずカーネルパラメータを変更して、パケットフォワードが出来るように/etc/sysctl.confに以下の記述を追加します。(コメントアウトします)

# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1
# Uncomment the next line to enable packet forwarding for IPv6
net.ipv6.conf.all.forwarding=1

sysctl.confの一部抜粋をしています。sysctl -p コマンドで有効化します。(再起動してもフォワーディングされます)

次にwg-quickの機能を使ってインターフェースがアップした際にiptablesをつかってクライアントがNAPTされるようにします。設定ファイル(wg0.conf)の[Interface]セクションのPostUP,PostDownにiptablesの記述を行います。(抜粋)

PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -A FORWARD -o wg0 -j ACCEPT; ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -D FORWARD -o wg0 -j ACCEPT; ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

PostUpにインターフェースがアップした際の動作を書きます。iptablesでwg0へ入ってきたパケットを許可してフォワーディング、そして、eth0へ出て行く場合はNAPTによるアドレス変換を行います。
同様にIPv6の設定もip6tablesで行います。ipv6はipv4と同じ設定に加えて、wg0へ向かうパケットの許可(ip6tables -A FORWARD -o wg0 -j ACCEPT)も必要でした。
PostDownはインターフェースがダウンした際の動作で、iptablesによる設定を消す動作をしています。

当然ですが、もともとiptablesの設定があった場合はインターフェースやWireGuardのListenPortの追加設定は必要です。

サーバ側の設定ファイル

[Interface]
ListenPort = 51820
Address = 192.168.1.1/24, fdfd:abcd:0001::1/64
PrivateKey = (サーバのプライベートキー)
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -A FORWARD -o wg0 -j ACCEPT; ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -D FORWARD -o wg0 -j ACCEPT; ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
PublicKey = (クライアントのパブリックキー)
AllowedIPs = 192.168.1.100/32, fd00:abcd:0001::100/128

WireGuardの自動起動

手動でWireGuardを起動したり終了したりするには以下のコマンドを使います。

$ sudo wg-quick up wg0
$ sudo wg-quick down wg0

これを行うと、wg0のいた-フェースの作成、アドレスの設定、ルーティング、リゾルバの設定、PostUpなどのコマンド実行などをやってくれます。

wg0のインターフェースが作成されれば、ifconfigやipなどのコマンドで見ることができます。

$ ifconfig wg0
wg0: flags=209<UP,POINTOPOINT,RUNNING,NOARP>  mtu 1420
        inet 192.168.1.1  netmask 255.255.255.0  destination 192.168.1.1
        inet6 fd00:abcd:1::1  prefixlen 64  scopeid 0x0<global>
        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 1000  (UNSPEC)
        RX packets 178459  bytes 143465460 (136.8 MiB)
        RX errors 23  dropped 0  overruns 0  frame 23
        TX packets 175183  bytes 173639084 (165.5 MiB)
        TX errors 0  dropped 87 overruns 0  carrier 0  collisions 0

$ ip address show dev wg0
4: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    link/none
    inet 192.168.1.1/24 scope global wg0
       valid_lft forever preferred_lft forever
    inet6 fd00:abcd:1::1/64 scope global
       valid_lft forever preferred_lft forever
$ ip route
default via 192.168.0.1 dev eth0 src 192.168.0.87 metric 202
192.168.1.0/24 dev wg0 proto kernel scope link src 192.168.1.1
192.168.0.0/24 dev eth0 proto dhcp scope link src 192.168.0.87 metric 202
$ ip -6 route
::1 dev lo proto kernel metric 256 pref medium
2001:db8:abcd:abcd::/64 dev eth0 proto ra metric 202 pref medium
fd00:abcd::/64 dev eth0 metric 202 pref medium
fd00:abcd:1::/64 dev wg0 proto kernel metric 256 pref medium
fe80::/64 dev eth0 proto kernel metric 256 pref medium
default via fe80::aaaa:bbbb:cccc:dddd dev eth0 proto ra metric 202 pref medium

2001:db8:abcd:abcd::/64はプロバイダから割り当てられたアドレス。192.168.0.0/24とfd00:abcd::/64がLAN内のアドレスです。これらはeth0に割り当てられています。

systemctlが使えるならば systemctl start wg-quick@wg0 が使えます。最後にインターフェース名をつけます。自動起動は同様にsystemctl enable wg-quick@wg0です。

クライアントの設定

クライアントの設定はクライアント側で個別に設定しても良いのですが、今回はサーバ側で設定ファイルを作り、配布する想定とします。設定ファイル名はcl.confとします。WireGuardにはユーザIDという概念はないので、クライアントの鍵がユーザの代わりとなります。

クライアントのインストール

クライアントも入れるアプリケーションは同じです。設定が変わるだけです。OS毎のインストール方法は、https://www.wireguard.com/install/ にあります。

鍵の作成(再掲)

クライアントの鍵を作ります。サーバの設定で既に作っているはずです。

$ wg genkey > cl-privatekey
$ wg pubkey < cl-privatekey > cl-publickey

クライアントの設定ファイルの作成

[Interface]
PrivateKey = (クライアントのプライベートキー)
Address = 192.168.1.100/24, fdfd:abcd:1::100/64
DNS = 192.168.0.53, fdfd:abcd::53

[Peer]
PublicKey = (サーバのパブリックキー)
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = vpn.example.jp:51820
#Endpoint = [2001:db8:abcd:abcd:aaaa:bbbb:cccc:dddd]:51820

[Interface]は自クライアントの設定です。
PrivateKeyは先ほど作ったクライアントのプライベートキー(cl-privatekey)です。
Addressはインターフェースに振るアドレスで、IPv4とIPv6の両方が使えます。サーバに合わせたプライベートアドレスとULAをつけます。今回クライアントのホストアドレスは100としています。
DNSは名前解決に使うサーバです。wg-quickで自動的にサーバのリゾルバが選ばれるはずですが、明示的に示したい場合や、リゾルバの設定が上手くいかない場合は直接記述も可能です。

[Peer]はサーバの設定です。サーバと対になっています。
PublicKeyはサーバのパブリックキー(publickey)です。
AllowedIPsはクライアントの設定ファイルで書く場合は、VPNのトンネルに転送するアドレス範囲となります(サーバの設定では受信するアドレスでした)。今回の場合はサブネットがv4とv6でそれぞれ/0となっているので全てのパケットはVPNのトンネルに転送することになります。トンネルの先のアドレスだけにすれば、それ以外の通信先(例えばインターネット)はVPNを通らずに直接出て行きます。
EndPointはVPNサーバの外部の(インターネットに直接接続可能な)アドレスとポート番号です。IPv6の場合はアドレスを[]で囲む必要があります。FQDNも可です。

クライアントへの設定

クライアントへの設定方法はOS毎に様々なので割愛しますが、スマートフォンではQRコードで設定ファイルを読み込む方法が用意されています。Linuxでは以下のコマンドでQRコードが作れます。

$ qrencode -t ansiutf8 < cl.conf

qrencodeはaptで入手可能です。

テストなど

ピアが作られないとき

設定が間違っているのだと思います。wg show コマンドで今の設定が見られます。wgコマンドだけでもピアの状態が確認できます。鍵の設定に間違いが無いか見直しましょう。

インターフェースにpingが通らない 

アドレス間違いの可能性が高いです。ip a コマンドなどでアドレスを確認しましょう。

LAN内と通信できない

フォワーディングの設定が出来ていない可能性があります。tracerouteも確認に使えます。ネットマスクがおかしい場合もあり得ます。ルーティングテーブルも見てみましょう。

VPN越しにインターネットに接続できない

NAPTの設定に問題があるかもしません。iptables -t nat -nvL等で確認。ルータやホストのiptablesなどのファイヤウォールなどの設定も確認します。

出来たこと

ラズパイでもVPNを作ることが出来ました。スループットはモバイルでしか試していないので、上限が50Mbpsほどですが、CPU使用率は50%ぐらいです。完全個人用になってしまいますが、ラズパイでも実用になりそうです。勿論まともなサーバでやれば大規模にも使えそうです。

普段使っているモバイルキャリアはOCNなのですが、未だにIPv6が降ってきません。このWireGuardを使えばIPv4でトンネルを作って、中にIPv6を通せるので、外出先からも自宅経由でIPv6環境が使えるようになりました。IPv6は test-ipv6.com 辺りで確認すると良いと思います。

また、自宅のDNSが使えるのも良いです。広告サイトやマルウェア配布サイトなどをまとめて0.0.0.0送りにしているので、モバイルでも快適な環境が手に入りそうです。

Raspberry Pi + GPS + NTP

使い古されたネタだとは思うが、意外と情報は少なく躓いたところも多かったので記録に残しておきます。

GPS受信機

秋月電気で売っているGPS受信機キット 1PPS出力付き 「みちびき」3機受信対応を使いました。GPSモジュールに太陽誘電のGYSFDMAXBを使い、受信機にはMedia Tek のMT3339が使われているようです。この受信機でGPSのL1電波を受信して、NMEAのフォーマットでシリアル信号にして出してくれます。

電池ホルダーとピンヘッダだけはんだ付けしました。ピンヘッダーは5V,GND,RXD,TXD,1PPSの順に並んでいます。RXD,TXDをシリアルに、1PPSは3次元測位をすると1秒毎にパルスが発信されるので、Rasberry Piの適当なGPIOにつなぎます。

電池はGPSをコールドスタートさせると時間がかかるので、衛星のデータ(アルマナックとエフェメリス)を保持しておくためのものです。なくても動きます。

Raspberry Piとの接続

Raspberry Pi のGPIOは2,4,6,8,10番ピンがうまい具合に、5V,GND,UART_TXD0,UART_RXD0,GPIO18と並んでいます。今回のGPSモジュールを順番につなげて、GPIO18を1PPSの入力に使えば、なんの工夫もなくフラットケーブルでちょうど繋げれます。

Raspberry Pi の準備

Raspberry Piのシリアルポート

Raspberry Pi (3とzero WなどのBluetoothがあるRasPi)のシリアルポート用に使う半導体は、PL011とmini UARTという回路で実装されています。Raspbian OSでは、シリアルポート(ttyS0)はデフォルトでオフになっていて使えないが内部的にはminiUARTにつながっています。一方PL011はBluetooth用に使っています。

/dev/serial0 (primary UART) -> /dev/ttyS0 -> mini UART (通常は有効化されていない)
/dev/serial1 (secondary UART) -> /dev/ttyAMA0 -> PL011 -> bluetooth

ちなみにAMAとはarmのAMBAというアーキテクチャの略でAdvanced Microcontroller Bus Architectureのことらしい。この実装がPL011ということ。

mini UARTはGPUとコアクロックを共有しています。GPUのクロックは動的に変化するため、mini UARTをシリアルポートに使うとボーレートが変化してしまい、これでは外のデバイスからデータを取得するには不都合があります。一方PL011はGPUのクロックの影響を受けません。前述のとおり、PL011はBluetoothで使用中のため、次の3つのいずれかの方法を取る必要があります。
a) Bluetoothの運用を止めPL011をシリアルポート用に使う。
b) Bluetoothはmini UARTを使い、PL011をシリアルポート用に使う。
c) Bluetoothはそのままにして、mini UARTのクロックを固定して使う。
最後のc)の方法はmini UARTをシリアルポートとして使うには制限が多いので今後の事を考えて使わないことにします。取れる方法はa)かb)の二択になります。

Raspbianでは/dev/serial0はLinuxのコンソールに割り当てらていて、これを別の目的で使うには/dev/serial0とコンソールを切り離さなければなりません。

Rassoberry Pi の設定

コンソールとシリアルの切断

Linuxのコンソールがシリアルに割り当てられているので、これを切り離します。(実際にはttyサービスが動いていないので、シリアル経由でログインはできない設定になっている。)切り離す方法として、1)raspi-configコマンドでやる方法と、2)マニュアルで/boot/cmdline.txtを編集する方法法があります。これをやることによって、シリアルでのコンソール接続が不能になりますが、通常はsshなどでログインしていると思うのでなんの問題もないと思います。

1)raspi-configコマンドを管理者権限で立ち上げて、Interfacing Options、Serialと進みNoを選択する。

2)/boot/cmdline.txtを編集する場合は適当なエディタで開いて、console=serial0,115200を探し出してこれを削除する。他の記述はそのまま残しておく。

どちらの方法でも有効化するには再起動が必要となります。次に先に述べたGPSをシリアルにつなぐいずれかの方法を実行します。今回はb)を選びました。

a) Bluetoothを停止する方法

/boot/configを変更する必要があります。dtoverlay=pi3-disable-btを追加します。
再起動後これによりBluetoothが停止されます。一方、GPIOの6,8番ピンが自動的に/dev/serial0 -> /dev/ttyAMA0 として再割り当てされます。その後にモデムの初期化とUARTとの通信の無効化のためにsudo systemctl disable hciuartをします。

b) Bluetoothをmini UARTで使う方法

もう一つの方法が、mini UARTを使ってBluetoothを動かす方法です。これも/boot/config.txtを編集して、dtoverlay=pi3-miniuart-btを追加します。更にクロックを固定するためにcore_freq=250の記述も追加しておきます。
再起動後miniUARTとPL011が入れ替わり、GPIOの6,8番ピンが/dev/serial0 -> /dev/ttyAMA0として再割り当てされます。
余談ですがc)のmini UARTをserial0として使うときは、GPIOのUARTを有効化(enable_uart=1)することでmini UARTのクロックは自動的に固定化されるため、core_freq=250の記述は必要ありません。

b)の操作によってシリアルとBluetoothの関係は以下のようになります。今回のGPSモジュールは上段のserial0につながることになります。

/dev/serial0 (primary UART) -> /dev/ttyAMA0 -> PL011 (GPIOのUART)
/dev/serial1 (secondary UART) -> /dev/ttyS0 -> mini UART -> bluetooth

PPS信号の受信

PPS信号の受信は前述したとおりGPIO18(ピン番号12)で入力する。後でわかったことですが、PPSのありなしで、NTPの安定度(offsetやJitter)にほとんど影響はありませんでした。

設定は/boot/configにdtoverlay=pps-gpio,gpiopin=18,assert_falling_edge=trueを追加するのと、/etc/modulesにpps-gpioを追加してppsのカーネルモジュールを読み込んでおきます。
assert_falling_edge=trueはアクティブLowで、つまりパルスは立ち下がりのときに発信されるので、その設定です。(参考

NTPの設定

関連ツールのインストール

シリアル経由でGPSのNMEAフォーマットもPPSの信号も取り出せるようになったので、このままNTPのソースに使ってもいいのですが、NTP以外でも使えるようにしたり(直接シリアルから読んでしまうとNTPがシリアルを専有してしまい、時刻以外の位置情報などが全く取れなくなってしまう。)、NMEAのフォーマットを読みやすい形に変換してもらうために、gpsd,gpsd-clientsのインストールを行います。

gpsdはシリアルに吐き出されるGPSからの信号をtcpの2947番ポートを通して使えるようにしてくれ、また、NTPにおいては127.127.28.uのシェアードメモリ経由で時刻の情報を提供してもらえるようになります。

gps-clientsはgpsdで配信してくれる情報をモニターしてくれるgpsmonコマンドなどが提供されます。

gpsdは/etc/default/gpsdに設定ファイルがあり、今回はGPSデータのソースとしてDEVICES="/dev/serial0 /dev/pps0"の設定を追加しました。また、GPSD_OPTIONS="-n"としてシェアードメモリを介してNTPを同期させるための設定を入れておきます(参考)。

gpsdはインストールしただけでは自動起動してくれないようなので、sudo systemctl enable gpsdなどで自動起動の設定をしておきます。ちなみにこれを忘れるとntpq -pなどで見てもいつまで経ってもGPSがdelay,offset,jitterが0.000のままで同期しません。gpsmonなどで確認するとgpsdが動いてしまうので、問題ないように見えてしまう。しばらくこれで悩みました。

     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
 SHM(2)          .GPS.            0 l    -   16    0    0.000    0.000   0.000
xPPS(0)          .PPS.            0 l   14   16  377    0.000   -2.582   0.459
 ntp.nict.jp     .POOL.          16 p    -   64    0    0.000    0.000   0.002
-ntp-a2.nict.go. .NICT.           1 u    9   64    3    9.904    0.021   1.391

シェアードメモリの様子はipcs -mコマンドで見ることができます。

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x4e545030 0          root       600        80         1                       
0x4e545031 32769      root       600        80         1                       
0x4e545032 65538      root       666        80         1                       
0x4e545033 98307      root       666        80         1                       
0x4e545034 131076     root       666        80         1                       
0x4e545035 163845     root       666        80         1                       
0x4e545036 196614     root       666        80         1                       
0x4e545037 229383     root       666        80         1 

0x4e545030から始まるメモリーがgpsdのシェアードメモリで、最後の一桁がユニット番号(u)で、ここでは7まで見えています。0,1はownerがrootでpermsが600なので、root権限がないと読めません。今回はユニット2が使えそうなのでこれを使うことになります。

NTPの設定とインストール

パッケージのNTPをインストールします。今回のバージョンはntpd 4.2.8p12@1.3728-o (1)です。

設定は/etc/ntp.confですが、追加で記述したのは以下になります。

server 127.127.28.2 minpoll 4 maxpoll 4  prefer
fudge 127.127.28.2 time1 0.000 refid GPS
server 127.127.22.0 minpoll 4 maxpoll 4
fudge 127.127.22.0 refid PPS

1行目と2行目がGPSの設定です。gpsdのシェアードメモリから読む設定で、ユニット番号は2です。このとき参照するアドレスが127.127.28.2になります。ntpのドキュメントが参考になります。gpsdは127.127.46.0でも時刻情報を出しているようですが、こちらは試していません。
minpollとmaxpollの値はgpsdの推奨のままです。この値だと2^4で16秒間隔で読みに行きます。
preferは必須のようです。
fudgeのtime1は手動でoffsetを設定するときの設定ですが、今回は0です。

3行目と4行目がPPSの設定です。/dev/pps0で読めるデータですが、ntpでは127.127.22.0がそれに当たるようです。gpsdでもserverのモードを指定すれば読めるようですが、今回は直接127.127.22.0で読みました。

あと、他のNTPの設定でもやるアクセスコントロールやインターネットのサーバ指定などはお好きにどうぞです。

結果確認

起動させたあとしばらくするとGPSが衛星を補足して、reachが377になる頃(3分ほど)経つと、GPSで時刻同期が実現します。ntpq -pコマンド確認します。

     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
*SHM(2)          .GPS.            0 l    -   16  377    0.000    0.004   0.002
oPPS(0)          .PPS.            0 l   13   16  377    0.000    0.002   0.002
 ntp.nict.jp     .POOL.          16 p    -   64    0    0.000    0.000   0.002
+ntp-a2.nict.go. .NICT.           1 u   28   64  377    6.401    0.987   2.171
+ntp-a3.nict.go. .NICT.           1 u   27   64  377    8.572    1.191   1.557
-ntp-b2.nict.go. .NICT.           1 u   29   64  377   10.725    2.783   1.308
-ntp-b3.nict.go. .NICT.           1 u   18   64  377   10.519    2.685   1.352
-ntp-a2.nict.go. .NICT.           1 u   22   64  377    8.623    2.046   1.910
-ntp-a3.nict.go. .NICT.           1 u   18   64  377   11.298    2.499   2.275

1行目のSHMがGPSの状態です。先頭に*がついているので、同期サーバになっています。2行目のPPSの先頭にoがついているので、参照されていることがわかります。

また、ntpq -c rvコマンドでも状態がわかります。

associd=0 status=0118 leap_none, sync_pps, 1 event, no_sys_peer,
version="ntpd 4.2.8p12@1.3728-o (1)", processor="armv6l",
system="Linux/4.19.57+", leap=00, stratum=1, precision=-19,
rootdelay=0.000, rootdisp=1.180, refid=PPS,
reftime=e0dfee76.435da06f  Mon, Jul ** 2019 17:12:38.263,
clock=e0dfee82.d4c99980  Mon, Jul ** 2019 17:12:50.831, peer=5249, tc=4,
mintc=3, offset=0.002763, frequency=-7.570, sys_jitter=0.001907,
clk_jitter=0.002, clk_wander=0.001, tai=37, leapsec=201701010000,
expire=201912280000

1行目でsync_ppsとなっているので、PPSで同期していることがわかります。インターネット経由のNTPだとここがsync_ntpとなります。

offsetもjitterもmsec表示なので一桁usec(micro second)の精度が出えているようです。インターネット経由はNICTのサーバを使用していますが、だいたい3桁のオーダで正確なようです。

真ん中のギザギザしているのがインターネットのNTP(ntp.mict.jp) を参照してきたときの、DelayとOffsetとJitterのグラフ。おおよそ数msecの値です。前後は、0のように見えますが、GPSを参照していたときのグラフで、数μsecです。ほとんど0に見えます。

Raspberry Pi B で監視カメラ

古いラズパイの有効活用

気がついたら家にラズパイがゴロゴロ転がっていたので、一番古いRaspberry Pi model Bを使って、ペット用の監視カメラを作ることにした。ラズパイのバージョン1で、CPUは遅いしメモリも512MBしかないので、実用になるかどうかが心配。

# cat /proc/cpuinfo
processor : 0
model name : ARMv6-compatible processor rev 7 (v6l)
BogoMIPS : 697.95
Features : half thumb fastmult vfp edsp java tls
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xb76
CPU revision : 7
Hardware : BCM2835
Revision : 000d
Serial : 0000000039ab319d

カメラはラズパイの基盤に直接取り付ける純正のカメラ

OSのインストールと設定

OSは2019-06-19に出たばかりのRaspbianのbuster。ダウンロードして、SDカードに書き込んで、基本的な設定を終了させる。最近は最低限の設定はansibleでノンストップで進めている。基本的な設定以外に必要な設定は、カメラの認識ぐらい。

$ sudo raspi-config

mjpg-streamerの設定

ラズパイの監視カメラとして定番っぽいmjpg-streamerを導入。

$ sudo aptitude install git cmake libjpeg8-dev gcc g++
$ git clone https://github.com/jacksonliam/mjpg-streamer
$ cd mjpg-streamer/mjpg-streamer-experimental/
$ make
$ sudo make install

起動用のシェルスクリプトをroot権限で動かすように作る。一般ユーザーだとカメラデバイスへの読み書きの権限を着ける必要がある。このラズパイは監視カメラ専用にするので、rootで起動させる。以下のスクリプトを用意。

!/bin/bash
LIB=/usr/local/lib/mjpg-streamer
WWW=/usr/local/share/mjpg-streamer/www

AUTH_USER=user
AUTH_PASS=password

/usr/local/bin/mjpg_streamer -o "$LIB/output_http.so -c $AUTH_USER:$AUTH_PASS -w $WWW/" -i "$LIB/input_raspicam.so -x 640 -y 480 -rot 180 -fps 15 -q 10 -ex verylong -ev 10"

/root/run.sh

mjpg-streamerはwebサーバも内蔵しているので、そのままそれを利用。設置位置の関係から-rotで画像を180度回転させている。-ftpでフレームレートを指定している。結果的にはこのレートでも問題なく稼働できた。

起動時に自動実行するために以下のファイルを用意。

[Unit]
Description=mjpg-streamer

[Install]
WantedBy=multi-user.target

[Service]
User=root
Group=root
ExecStart=/root/run.sh

/etc/systemd/system/mjpg-streamer.service

以下のコマンドで自動起動するようにしておく

$ sudo systemctl enable mjpg-streamer

webサーバはport 8080 で起動しているので、http://hogehoge:8080で見られる。
外から見られるように、他のサーバからリバースプロキシを通しておく。

Netatmoで屋内のCO2を監視する

Netatmoはフランス生まれの気象観測ガジェット。日本でも販売しており、手軽に温度、湿度、気圧、二酸化炭素濃度などが測れます。

二酸化炭素濃度は室内を測っているのだけれど、生活の中ですごく変化する様子が面白い。屋外の濃度はおおよそ400ppmなのだが、室内で窓を閉め切ると人間の呼吸だけであっという間に600〜800ppmほどになります。さらに、調理などでガスコンロを使うとあっという間に1000ppmを超え、冬場の石油ストーブなどは締めきった部屋で使うとみるみる2000,3000ppmと上がっていき見ているだけで息苦しくなる気がします。

通常、1000ppm以下が快適とされている二酸化炭素濃度ですが、Netatmoで計測できても気軽に知る方法がありません。スマホアプリがあるので、スマホのウィジェットやタブレットなどに表示させておけるのですが、それでも見にくい。というわけで、7セグメントLEDとraspberry piを使い常時表示させるガジェットを作りました。

続きを読む

Raspberry pi zero w のネットワーク設定

無線の設定

Raspberry pi zero w は無線のインターフェースしかないので、最初のセットアップはモニターとキーボドを繋いでやるのが無難。起動したらまずは無線の設定から始める。

raspi-configで無線のSSIDとPASSWORDは設定できるので、/etc/wpa_supplicant/wpa_supplicant.conを確認しておく。もし、うまく設定できなかったら手動で以下のコマンドでもできる。

sudo wpa_passphrase SSID PASSPHRASE | sudo tee -a /etc/wpa_supplicant/wpa_supplicant.conf

問題はパスフレーズ。’!’などの文字が入っているとコマンドが正常に認識されないので、SSIDやパスフレーズなどは’で囲っておく。

ファイルのパーミッションは念のために600にしておくのが良いだろう。(上のコマンドのままだとPSKの平文が残る。念のためにhistoryも消しておくのが良かろう)

固定アドレス

固定アドレスはかつては/etc/network/interfacesで設定していたが、最近は/etc/dhcpcd.confでやるのが推奨されているらしい。以下を追加しておく

interface wlan0
statuc ip_address=192.168.0.79/24
static routers=192.168.0.1
static domain_name_servers=192.168.0.75

無線はwlan0のインターフェース。

 

Raspberry piにmirakurunとchinachuをインストール

ハード

  • raspberry pi
  • PX-S1UD V2.0
  • ACR39-NTTCom(USBカードリーダー)

チューナーの設定

http://plex-net.co.jp/plex/px-s1ud/PX-S1UD_driver_Ver.1.0.1.zipをダウンロードして、ファームウェアをコピーする
$ sudo cp PX-S1UD_driver_Ver.1.0.1/x64/amd64/isdbt_rio.inp /lib/firmware
続きを読む

Raspberry pi インストールメモ

ハード

  • Raspberry pi 2 B+
  • micro SD 8GB
  • 無線LANアダプター(WDC-150SU2MBK)
  • Macbook (SDカード書き込み用)

Raspbianの書き込み

https://www.raspberrypi.org/downloads/raspbian/ からRASPBIAN STRETCH WITH DESKTOPをダウンロード。今回のダウンロードは2017-09-07-raspbian-stretch.zipというファイル。念のためにチェックサムを確認。

$ shasum -a 256 tmp/2017-09-07-raspbian-stretch.zip 
a64d742bc525b548f0435581fac5876b50a4e9ba1d1cd6433358b4ab6c7a770b tmp/2017-09-07-raspbian-stretch.zip

SDカードを挿入し以下のコマンドでデバイス名を取得(今回は /dev/disk2)

$ diskutil list
(略)
/dev/disk2 (internal, physical):
 #: TYPE NAME SIZE IDENTIFIER
 0: FDisk_partition_scheme *8.1 GB disk2
 1: Windows_FAT_32 boot 66.1 MB disk2s1
 2: Linux 8.0 GB disk2s

デバイスを確認したらddで書き込むためにアンマウント

$ diskutil unmount /dev/disk2s1

ddコマンドを使いイメージを書き込む

$ sudo dd bs=1m if=tmp/2017-09-07-raspbian-stretch.img of=/dev/disk2 conv=sync

最近のRaspbianはインストール直後はssh接続ができないようになっている。セットアップ時にsshは使いたいので、出来上がったbootフォルダ内に ‘ssh’ というファイルを作っておく。

$ touch boot/ssh

初期設定

ssh接続

sshで接続するのだが、DHCPで取得しているIPアドレスを調べる必要がある。ディスプレイとキーボドが繋がっていれば、ログインしてifconfigで調べればeth0(有線のイーサネットポート)のアドレスが分かるが、ディスプレイ無しで調べるのは以下の方法で。

$ ping 192.168.0.255
$ arp -a

一行目が、自分のネットワークのブロードキャストアドレス。二行目が、自分のPCのarpテーブルを表示する。arpで調べたアドレスの中に、MACアドレスがb8:27:ebで始まっているものがあるはず。これが、raspberry piのベンダーコードなので、これに割り当てているIPアドレスが今回DHCPで取得したアドレスとなる。今回は192.168.0.244だった。

 $ ssh pi@192.168.0.244

初期ユーザはpiでパスワードはraspberry。とりあえずこれでログインするが、初期設定が終わったらこのユーザは停止する。

raspi-configによる初期設定

$ sudo raspi-config

上記のコマンドによる、初期設定。ここで変更したのは、パスワード、ホストネーム、ローカリゼーション、アドバンスドオプションのSDカードの拡張ぐらい。終了すると自動的に再起動。

アップデート

/etc/apt/source.listに近場のリポジトリを登録

deb http://ftp.jaist.ac.jp/raspbian/ stretch main contrib non-free rpi
$ sudo aptitude update
$ sudo aptitude safe-upgrade

ネットワークの状況にもよるが、数分から数十分だと思う。

$ sudo rpi-update

ファームウェアの更新もしておく

$ sudo reboot

再度リブートしておく

ネットワークの設定

普段は無線LANの運用をするので、以下はその設定。Raspberry Pi 2には無線インターフェイスはないので、USBアダプタを接続。あっさりとwlan0として認識した(/sbin/ifconfigで確認)。固定IPアドレスを設定する。場所は/etc/network/interfacesとなる。今回は以下の行を追加。

auto lo
iface lo inet loopback

allow-hotplug eth0
iface eth0 inet dhcp

allow-hotplug wlan0
iface wlan0 inet static
    address 192.168.0.79
    netmask 255.255.255.0
    gateway 192.168.0.1
    dns-domain mirahouse.jp
    dns-nameservers 192.168.0.75
    wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf

無線はwlan0のインターフェース。eth0は有線インターフェースで、無線がつながらない時に使うことになるはずなので、dhcpで残しておく。

無線の最終行はセキュリティの設定となる。wpa_supplicant.confにSSIDやPSKの設定を入れておく。具体的には以下のコマンドで設定。

sudo wpa_passphrase SSID PASSPHRASE | sudo tee -a /etc/wpa_supplicant/wpa_supplicant.conf

問題はパスフレーズ。’!’などの文字が入っているとコマンドが正常に認識されないので、SSIDやパスフレーズなどは’で囲っておく。

ファイルのパーミッションは念のために600にしておくのが良いだろう。(上のコマンドのままだとPSKの平文が残る。念のためにhistoryも消しておくのが良かろう)

ユーザの作成とSSH設定

新たにユーザを作成する。group sudoにも追加して、sudoできるようにしておく。ついでにrootのパスワードも変更しておく。

$ sudo useradd -m -k /etc/skel -d /home/hoge -s /bin/bash hoge
$ sudo passwd hoge
$ sudo gpasswd -a hoge sudo
$ sudo passwd root

すでにある、sshの公開鍵をコピーしてsshのパスワード認証を使えないようにする。

(Raspberry pi)
$ sudo mkdir /home/hoge/.ssh
$ sudo chown hoge.hoge /home/hoge/.ssh
$ sudo chmod 700 /home/hoge/.ssh
(クライアント側)
$ scp .ssh/*.pub 192.168.0.79:~/.ssh/
(Raspberry pi ここから新しいユーザでログイン)
$ cd .ssh
$ cat id_edcsa.pub >>authorized_keys
$ cat id_rsa,pub >>authorized_keys

一旦ログアウトして、公開鍵認証でsshできることを確認する。問題ないことを確認したら、/etc/ssh/sshd_configに以下の行を追加。再起動かsshdの再起動を行う。

PasswordAuthentication no

piユーザからsudoのグループを外しておく

sudo gpasswd -d pi sudo

Yubikey (スマートカード)に入れた秘密鍵を使ってSSHにログインする

ラノベのタイトルみたいになってしまった

すったもんだしてYubikeyにSSHの秘密鍵を入れるところまで行ったのだが、これを利用して、ローカルのハードディスクに秘密鍵を置かずに、SSHのログインを使用という話。クライアントはWindows,Mac,Linuxを想定している。
いままで、公開鍵暗号でのSSH接続ができていたことが前提。

Mac,Linux

Yubicoのページ( https://developers.yubico.com/PIV/Guides/SSH_with_PIV_and_PKCS11.html )のStep 4 からほぼそのままでいけた。

まずはOpenSCのインストール。Mac OSX の場合はhomebrewでインストールするのがよさ気。

brew install opensc

これだけでいける。インストール先は/usr/local/opt/openscになる。環境によっては、/usr/local/Cellar/openscになることもある。インストールされたディレクトリは以下のコマンドでも確認できる。

brew --prefix opensc

OSXはメジャーバージョンアップの際に/usr/localにものがあるとしくじる前科があるので若干心配だ。Linuxの場合は、それぞれのパッケージ管理ソフトに従う。

次に参考したページにはssh-keygenで公開鍵を取り出して、リモートホストのauthorized_keysに追加せよとあるが、今まで同じ鍵でサーバ運用していた場合は関係なし。

必要なのはpkcs11のライブラリの場所を調べてそいつがスマートカードを読み取れるかどうか。ちなみに手近のLinuxクライアントはVMware Player のDebianしかないのだが、VMのゲストOSがスマートカードを認識できないので確認できない。(no slotsというエラーが出る)

あとはsshコマンドを叩くときにIオプションでpkcs11ライブラリの場所を指定するだけ。

ssh -I /usr/local/opt/opensc/lib/opensc-pkcs11.so remotehost

こうするとYubikeyの中の秘密鍵を使ってセッションを確立します。しかしこれを毎回打つのは面倒なので、~/.ssh/configに記述しておきます。私の場合はこんな感じ。

Host hoge
        Hostname          hoge.example.jp
        Port              12345
        PKCS11Provider    /usr/local/opt/opensc/lib/opensc-pkcs11.so

 

4行目に書いてあるPKCS11Providerが追加されたところ。

Windows

WindowsのSSHクライアントといえば、TeratermかPuttyが思い浮かぶ。そんな人に手っ取り早いのが、Puttyの中に入っているPageantを使う方法。認証エージェントですが、スマートカード対応版があります。Puttyシリーズをスマートカード対応にしているPutty-CACというプロジェクトがあってバイナリを公開しています。

SSHサーバに接続するだけなら、Putty本体とTeratermでも使えるPageantをダウンロードするだけで大丈夫です。

Puttyそのものの使い方は割愛しますが、接続先ホストの設定でConnection-SSH-Certificationとすすみ、Attempt Certification Authentication にチェックを入れて、Set CAPI certで証明書を選び、PIN入力が出てくれば接続可能です。

同じようにpageantにも証明書を指定しておき、Teratermで接続する際にSSH認証で、pageantを使う、でPIN入力が出てくれば接続可能です。

pageantはOS起動時に起動させれば良いと思います。

その他

それにしてもECDSAの鍵を使えないのが痛い。。。

Yubikey 4 にSSHの秘密鍵を格納する

 

Yubico社のYubikey4は単なるワンタイムパスワード生成器じゃなくて証明書を格納可能になった。SSHの秘密鍵をYubkikeyに入れて、sshでログインするときだけYubikeyをPCに挿せば、秘密鍵をあちこちのPCに保存しておく必要がなくなり安心である。Yubikey4は日本のAmazonでも販売されている(AA)

Yubikey自体に鍵生成機能があるので、そこで秘密鍵と公会議のペアを作っても良いのだが、あちこちのサーバの公開鍵を置き換えるのが面倒なのと、Yubikey内で生成した秘密鍵はセキュリティ上取り出すことができないので、既存の鍵を使いたい。当然鍵は取り出せないのが安全なのだが、万が一Yubikeyごと秘密鍵をなくしてしまったら二度とその鍵でログインできなくなるのが怖いのだ。ちなみに、既存の秘密鍵をYubkikeyに入れたものも取り出せないので、一通り設定が終わったら、あちこちのPCにある秘密鍵を消し去って、信頼できるところにだけ秘密鍵のコピーを持っておくつもり。なんなら秘密鍵を紙に印刷しておくのが一番安全かもしれない。

以上の理由で、既存の秘密鍵をYubikeyに入れるのだが、やり方が簡単ではなかったのでメモっておく。前提として、rsa 2048bitの秘密鍵 id_rsa を格納するとする。

 

鍵の変換

まずは既存の鍵の変換から。sshの鍵生成は普通ssh-keygenで行うと思うのだが、これで作られる秘密鍵と公開鍵はopensshの独自仕様のようなので、まずはPEM形式に変換する。PEMとかDERとかいうのは鍵のエンコーディング形式でPEMは鍵をBASE64でエンコーディングしてあり、DERはバイナリのようだ。opensshはBASE64なのだが、PEMとヘッダーとかが違うそうだ。

opensll rsa -in id_rsa -out ide_rsa.pem -outform pem

これでPEM形式に変換。

次に同じように公開鍵も作る

openssl rsa -in id_rsa.pem -pubout >id_rsa_pub.pem

 

Yubikey4のPINコード等の変更

次にyubico社からダウンロードしたyubico-piv-toolを使う。ただしサンプルのとおりにコマンドを打ってもエラーが出る(Windows)ので多少工夫が必要。まずは、yukbikeyをUSBに差し込んで、まずはPINコード、PUKコード(管理者用)、マネジメントキーを変更する。

まずマネジメントキーを登録する。

yubico-piv-tool -a set-mgm-key -n (16進数48文字)

すでにマネジメントキーが登録されている場合は、-kオプションのあとに既存のキーを書く。不思議なのは、-kオプションの後にスペースをおいてキーを指定してもうまく動かない。オプションの後のスペースはあってもなくてもいいのだが、kオプションだけはなぜかスペースがあるとうまく動かない。

yubico-piv-tool -a set-mgm-key -n (16進数48文字) -k(古いマネジメントキー)

次にPIN

yubico-piv-tool -a change-pin

デフォルトのPINは123456で、同じくデフォルトのPUKは12345678。

yubico-piv-tool -a change-puk

同じく変更する。

この辺のキーの変更はGUIツールのほうが使いやすい。(ただしGUIツールはなぜかPUKコードが変更できない。なんだか中途半端……)

あと、好みではあるが、リトライ回数を設定する。

yubico-piv-tool -a verify -P(PIN) -a pin-retries --pin-retries=15 --puk-retries=15 -k(mgm key)

秘密鍵の格納

PEM形式に変換した秘密鍵をyubikeyに格納する。yubico-piv-toolのマニュアル( https://www.yubico.com/wp-content/uploads/2016/05/Yubico_PIV_Tool_Command_Line_Guide_en.pdf )によるとyubikeyの証明書を保存する領域は9a,9c,9d,9eと4つあるようで、9aがPIV Authentication、9cがDigital Signature、9dがKey Management、9eが- Card Authenticationらしい。今回は9aに格納する。

yubico-piv-tool -a import-key -s 9a -i id_rsa.pem -K PEM -k(マネジメントキー)

公開鍵の自己署名証明書作成と格納

次に公開鍵を自己署名する。自分で作った秘密鍵の正当性をどのように担保するかについて、第三者にお墨付きをつけてもらう方法もあるが、今回の目的は他人に自分の秘密鍵の正当性を主張したいわけではないので、自分の秘密鍵の正当性は自分で担保する。これをwebでやるといわゆるオレオレ証明書になるやつだ。今回、yubkikeyはOSから見るとスマートカードとして見えるので、SSHの秘密鍵の対になる公開鍵を自己署名して、自己署名証明書を作り鍵ペアをyubikey内部においておく。まずは公開鍵の自己署名証明書から。

この操作は指定されたスロットに秘密鍵が入っていることが前提。秘密鍵を入れずにやってもエラーになる。

yubico-piv-tool -a verify-pin -a selfsign-certificate -s 9a -S "/CN=SSH/" -i id_rsa_pub.pem -o 9a-cert.pem --valid-days=(日数)

この操作にはPINコードが必要になる。Sオプションはx.509証明書の記述だ。最低限CN(Common Name)さえ入っておけば大丈夫だと思う。OSの証明書選択画面などで使われる名前だ。–valid-daysは証明書の有効期限で、指定しないとでおフォルトで365日となる。試しに1日で作ったのだが、2日経っても期限切れの証明書が使えた。本当に意味あるのか不安。

次に、この証明書をyubikeyに格納する。

yubico-piv-tool -a import-certificate -s 9a -i 9a-cert.pem -K PEM -k(マネジメントキー)

以上でyubikeyに対する操作は終了

yubico-piv-tool -a status

で証明書の状態を確認できる。

CHUIDの作成

普通は必要ないと思うが、実験などでハードウェアリセットをかけてしまうとCHUIDがなくなってしまう。ランダムでCHUIDを発行する必要がある。

yubico-piv-tool -a set-chuid -s 9a

その他

同じ手順で、ECDSA384bit鍵の登録を試みた。同じ鍵を複数鍵を登録することは不可能なので、スロット82に格納してみたいが、Windowsからだと証明書の一覧にECDSAの鍵が出てこない。スロット82だとダメなのかと思って、9aに入れてみたが、やっぱり出てこない。