それマグで!

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

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

bashで文字列の中身の変数名の値を取り出す(可変変数・間接参照)

bash で配列に入れた文字列を変数として解釈する

変数の文字列を、変数として解釈する。

echo $name=${!name}

たとえば変数調べるときに変数名を列挙して中身を全部見るとか。

#!/usr/bin/env bash 
for i in ip mask subnet subnet interface router ; do
  echo $i=${!i}
done

可変変数とかvariable variable とか一般呼称はあるのですが、あまり普及してないですね。

bashのマニュアルを見ると、indirect access なので間接参照とかの邦訳になります。

文字列を変数名として解釈するとか、文字列から変数を取り出すとか。変数名を使って別の変数を取り出すとか、人によって表現にゆらぎが出るので困りますね。英語でも variable variables のように表現する人も多く、表現にゆらぎが出るので困りますね。

関連資料

takuya-1st.hatenablog.jp

参考資料

OpenWrt で softether のVPN接続する、udhcpdによるIP取得とルーティングする

openWrt で softetherVPNをルーティングする

openwrt に vpn 拠点間接続をやらせてみる

リモート側のネットワークの設定やマスカレードなどは済んでいる状態。OpenWRTからリモートへ接続する

Host-->openwrt--<SoftEtherVPN>--VPN Server--(office network)--10.193.3.40

細かいことは抜いておいて、相手先のネットワークのサーバーにアクセスできればいい。

openwrt に softether のクライアントを入れる

opkg install softethervpn-client

vpnclientvpncmd が導入される。

インストールするとClientが起動している

localhostSoftEther のクライアントが起動している。起動を確認しておく。

root@OpenWrt:~# ps auxf | grep softethervpn
root      2116  0.0  0.0   6644  1600 ?        S<s  03:48   0:00 /usr/libexec/softethervpn/vpnclient execsvc
root      2119  0.9  0.1  23000 16528 ?        S<l  03:48   0:02  \_ /usr/libexec/softethervpn/vpnclient execsvc

SoftEtherの設定 vpncmd

このあたりはmacOS/Ubuntu で接続するときと同じ

vpncmd /CLIENT localhost /CMD NicCreate
vpncmd /CLIENT localhost /CMD AccountList
vpncmd /CLIENT localhost /CMD AccountCreate
vpncmd /CLIENT localhost /CMD AccountPasswordSet
vpncmd /CLIENT localhost /CMD AccountConnect ocn
vpncmd /CLIENT localhost /CMD AccountList
コマンド 設定値
NicCreate soeth0
AccountCreate /name:ocn /nic:soeth0 /hub=Vpn /remote=vpn.my-office.jp:443
AccountPasswordSet パスワード

上記のとおりにコマンドをいれてもいいが、毎回毎回訊かれるのが面倒。

コマンドの引数を使ってコピペ可能にする。

## 共通化
alias vpncmd='vpncmd /CLIENT localhost /CMD ' # コマンド省略
conn=ocn-tak # 接続名
## 接続作成
vpncmd NicCreate soeth0
vpncmd AccountCreate $conn /SERVER:vpn.ocn.ac.tld:443 /USERNAME:takuya1234 /HUB:USER_HUB /NICNAME:vpn_soeth0
vpncmd /CLIENT localhost /CMD AccountPasswordSet $conn  /PASSWORD:mypassword /TYPE:radius

## 接続と確認
vpncmd /CLIENT localhost /CMD AccountConnect  $conn
vpncmd /CLIENT localhost /CMD AccountList 
vpncmd /CLIENT localhost /CMD AccountStatusGet $conn
## 切断と確認
vpncmd /CLIENT localhost /CMD AccountDisConnect  $conn
vpncmd /CLIENT localhost /CMD AccountList 
vpncmd /CLIENT localhost /CMD AccountStatusGet $conn

接続すると、Nicの名前に vpn_プレフィックスされたインターフェイスが現れる。

今回は、Nic の名前を soeth0 としたので、 vpn_soeth0 が出現する。

dhcpでアドレスを取得

openwrt の場合、udhcp を使えばいいので、それ用のスクリプトを用意する。

アドレス取得時に、最低限のルーティングを行うのだが

最初は動作確認のために、ルーティングは書かずにアドレスだけもらっておく。

ルーティングは動作確認後に後で追記する。

etc/config/custom/softether/dhcp.sh(ルーティングなし)

#!/usr/bin/env bash

setup_interface() {
  echo "udhcpc: ifconfig $interface $ip netmask ${subnet:-255.255.255.0} broadcast ${broadcast:-+}"
  ifconfig $interface $ip netmask ${subnet:-255.255.255.0} broadcast ${broadcast:-+}
    # info
  echo router=${router}
  
}
case "$1" in
  deconfig)
    ifconfig "$interface" 0.0.0.0
    ;;
  renew)
    setup_interface update
    ;;
  bound)
    setup_interface ifup
    ;;
esac

udhcp でアドレス取得

これで、IPアドレスが取得される。

udhcpc -i vpn_soeth0 -s /etc/config/custom/softether/dhcp.sh

ルーティング

仮想Hub経由してルータに接続する。

ping 10.35.225.254

ルータに接続できたら、GWとして、ルーティングを入れてやる。

ip route add 10.193.3.0/24 via 10.35.225.254 dev vpn_soeth0

ルーティングテーブル追加の確認。pingで接続を確認する。

ping 10.193.3.40

これで、OpenWRTから、リモート側のPCまで到達できた。

VPN接続を共有する(マスカレード)

OpenWRTにルータらしく、NATとマスカレードをして、ローカル側にVPN接続を共有させる。

要は、マスカレードとフォワーディングを入れてやる。

VPN_IF=vpn_soeth0
iptables -I FORWARD -o $VPN_IF -j ACCEPT
iptables -t nat -A POSTROUTING -o $VPN_IF -j MASQUERADE

共有を確認する

192.168.1.100 から 10.193.3.40へ接続を確認する

takuya@work $ ssh 192.168.1.100 
takuya@192.168.1.100 $ ping 10.193.3.40

パケットが応答されたので、接続できた。

設定を永続化する。

最後に、udhcp の起動スクリプトに、これらのルーティングを書き加える。

etc/config/custom/softether/dhcp.sh(ルーティング追加)

#!/usr/bin/env bash

setup_interface() {
  echo "udhcpc: ifconfig $interface $ip netmask ${subnet:-255.255.255.0} broadcast ${broadcast:-+}"
  ifconfig $interface $ip netmask ${subnet:-255.255.255.0} broadcast ${broadcast:-+}
    ### 追加
  # routing
  ip route add 10.193.3.0/24 via ${router} dev ${interface}
  # masquerade
  iptables -I FORWARD -o ${interface} -j ACCEPT
  iptables -t nat -A POSTROUTING -o ${interface} -j MASQUERADE
}

case "$1" in
  deconfig)
    ifconfig "$interface" 0.0.0.0
    ;;
  renew)
    setup_interface update
    ;;
  bound)
    setup_interface ifup
    ;;
esac

動作チェックする。

## iptables を初期化して
service firewall restart
## ip を解放してルーティング・テーブルをリセット
ifconfig vpn_soeth0 0.0.0.0
## softether を切断
vpncmd /CLIENT localhost /CMD AccountDisConnect ocn

## softethre を接続
vpncmd /CLIENT localhost /CMD AccountConnect ocn
## dhcp 取得とルーティング設定
udhcpc -i vpn_soeth0 -s /etc/config/custom/softether/dhcp.sh

接続を切断して、再接続し、dhcp を再取得、udhcが指定スクリプトを動かす。

udhcp が取得後に、指定スクリプトを実行して、ipアドレス設定とルーティングを設定コマンドを流してくれる。

この一連の流れをコマンド化

/etc/config/custom/softether/start-my-softether-connection.sh

vpncmd /CLIENT localhost /CMD AccountDisConnect ocn
vpncmd /CLIENT localhost /CMD AccountConnect ocn
udhcpc -i vpn_soeth0 -s /etc/config/custom/softether/dhcp.sh

udhcpc の接続をrenew/releaseする

udhcpc は常駐させるモードと常駐させないモードが有る。

udhcpc -S -C -i $VPN_IF -s $UDHCP_SCRIPT        # プロセスが常駐する
udhcpc -q -S -C -i $VPN_IF -s $UDHCP_SCRIPT    # 実行したら終わり。プロセスは常駐しない。

プロセスを常駐している場合、シグナルで、renew/releaseが可能になる。

## dhcp を renew する
 pkill -USR1 -f "udhcpc -S -C -i $VPN_IF -s $UDHCP_SCRIPT"
## dhcp を release する
 pkill -USR2 -f "udhcpc -S -C -i $VPN_IF -s $UDHCP_SCRIPT"

それぞれ、終了後にスクリプト実行して、ルーティングなどの処理を追加することができる。

割当解放(リリース)した場合は /bin/sh $UDHCP_SCRIPT deconfig が呼ばれ、割当更新(renew)した場合は、/bin/sh $UDHCP_SCRIPT renew がそれぞれの引数(deconfig , renew, bound )をつけて実行される。

この引数を使ってdhcp割当・解放・更新のスクリプトを書くことができる。

スクリプト

# router, interface ip の変数はudhcpcから渡される
function cleanup_interface(){
  ## ルーティング解放・IP割当解除などの処理
 nft del table my_vpn
  ip route del ${DEST_NET} via ${router} dev ${interface} 
  ifconfig $interface 0.0.0.0
}
function setup_interface (){
  ## IP割当処理、ルーティング作成などの処理
  ifconfig $interface $ip netmask ${subnet:-255.255.255.0} broadcast ${broadcast:-+}
  ip route add ${DEST_NET} via ${router} dev ${interface} 
  nft add table my_vpn
}

case "$1" in

  deconfig)
    cleanup_interface
    ;;
  renew)
    setup_interface update
    ;;
  bound)
    setup_interface ifup
    ;;

esac

常駐している場合にシグナルを受け取って片付けのdeconf 処理を書くことができる。

pppoe 接続時にSoftEtherVPN接続を起動する

ppooe 接続時にスクリプトを動かすには、hotplug.d を使えばいいので、hotplug で実行するスクリプトを作る。

PPPoEが起動したら、間髪入れず、SoftEther を接続しに行くようにする。

/etc/hotplug.d/iface/99-softether

#!/usr/bin/env bash
## PPPoE接続後に、 スクリプトを実行する
## iface の変化後に、TARGET_INTERFACEで検出して任意のフックが可能
## main
function main(){
  TARGET_INTERFACE=pppoe-XXX
  TARGET_DEVICE=eth1
  ###
  [ "$ACTION" = "ifup" -a "$INTERFACE" = "$TARGET_INTERFACE"  ] && {
    # log sample
    logger "iface $TARGET_INTERFACE / $TARGET_DEVICE up detected, do hotplug actions."
    sleep 10;
        /etc/config/custom/softether/start-my-softether-connection.sh   
    }
}
main

最後の最後に、再接続チェック

ネットワークを再起動して、pppoe接続後にsoftether がちゃんと接続されて、ルーティングとマスカレードされることを確認する。

root#openWrt $ service network restart

注意点

実験用openWrt ルータはx86/ext4 で動作しているので、パッケージを容量気にせずに入れている。

bash / iproute2 / iptables-full など割と大きめのパッケージを入れて、そのへんのubuntu とおなじような動作しているので、容量がシビアなwrt だとこの方法だとうまくイカないかもしれない。

2023-08-29 追記

接続コマンドと作成コマンドをダイアログではなくコマンド引数で与えるようにメモを更新。

2023-08-30 追記

udhcpc の使い方が足りなかったので調査して追記。

参考資料

mac で softetherVPN への接続(クライアント)

macsoftether への接続

できなくはないけど、設定がめんどくさい。

いくつか手順が必要。

公式マニュアルは、L2TP over IPSec なのですが、IPSec 上に L2TPを通して、その上にSSHを通してってもう考えたくないので、カプセル化は少ないほうが管理上も速度面でも嬉しいので、直接つなぎます。*1

tap の準備

brew install tuntap
sudo reboot 

私は上記コマンドだけでインストール完了し、Kextを有効にするために再起動をした。

ただ、私の環境はcsrutils がオフになっているので、一般的に動くかどうかはわからない。

kext を使うので、macOSのCSPが使える必要があるかもしれない。 SIP ( sytem itegrity protection )をdisableで使う。

csrutil status
csrutil disable

sip spctl 関連資料

softether の準備

softethermac版はコマンドクライアントから持ってきます。

ダウンロード

ダウンロードサイトから、macOS版をダウンロードする。

https://www.softether-download.com/en.aspx?product=softether

 curl -LJO https://github.com/SoftEtherVPN/SoftEtherVPN_Stable/releases/download/v4.38-9760-rtm/softether-vpnclient-v4.38-9760-rtm-2021.08.17-macos-x64-64bit.tar.gz

f:id:takuya_1st:20211103200748p:plain

ビルド

tar zxvf softether-vpnclient*
cd vpnclient
make 

softether client の起動

クライアントは常駐するソフトである。そういう設計らしい。まぁもともとWindowsのタスクトレイに居座るタイプだし、MacOSのメニューバーに居座ることをしてないので、自分で起動するってことかな。

まず起動します。

cd vpnclient
sudo ./vpnclient start

使用終わったら stop

sudo ./vpnclient stop

stop するの忘れがち。

softether の接続設定を作る

vpnclient に接続します。

  • 2 client の管理
  • localhost 指定で自ホストのClientと対話
  • VPNClientでコマンドで操作
takuya@vpnclient$ sudo ./vpncmd
vpncmd コマンド - SoftEther VPN コマンドライン管理ユーティリティ
SoftEther VPN コマンドライン管理ユーティリティ (vpncmd コマンド)
Version 4.38 Build 9760   (Japanese)
Compiled 2021/08/17 22:32:49 by buildsan at crosswin
Copyright (c) SoftEther VPN Project. All Rights Reserved.

vpncmd プログラムを使って以下のことができます。

1. VPN Server または VPN Bridge の管理
2. VPN Client の管理
3. VPN Tools コマンドの使用 (証明書作成や通信速度測定)

1 - 3 を選択: 2

接続先の VPN Client が動作しているコンピュータの IP アドレスまたはホスト名を指定してください。
何も入力せずに Enter を押すと、localhost (このコンピュータ) に接続します。
接続先のホスト名または IP アドレス: localhost

VPN Client "localhost" に接続しました。

VPN Client>

仮想NICを作成する

tap に接続されるSoftetherNICを作成する。

VPN Client>NicCreate
NicCreate コマンド - 新規仮想 LAN カードの作成
仮想 LAN カードの名前: se0

コマンドは正常に終了しました。

接続設定

コンソール画面から、接続設定を作るのは面倒なので、WindowsUbuntuから動作中の設定をインポートするほうが速いでしょう。

VPN Client>AccountImport
AccountImport コマンド - 接続設定のインポート
インポート元ファイル名: /path/to/file

とくに、仮想Hubの名前はServer側と密接に関わってるはずです。 適当に決められないはず。

他の動作済み設定をインポートするのが確実だと思います。

接続

VPN Client>AccountConnect
AccountConnect コマンド - 接続設定を使用して VPN Server へ接続を開始
接続設定の名前: ocn

IPアドレスの割当。

tapデバイスsoftether が使うので、tap デバイスIPアドレスを割り当てる。

Softetherの場合は、接続先がDHCPしていてて自動的に割り当てがほとんどだと思う。だからDHCPから割当もらえば十分。

sudo ipconfig set tap0 DHCP

ルーティングの追加。

接続ができたら、ルーティングを追加する。

sudo ip route add 10.100.10.0/24 via 10.9.100.254 dev tap0

私は、maciproute2相当のエイリアスを提供してくれるパッケージを入れてるので、ip route add で済ませた。

疎通の確認

route を確認してー

ip route get 10.100.10.100

ping を打ち込んでー

ping 10.100.10.100

ssh で接続してみる。

ssh -vvv 10.100.10.0

切断。

接続が終われば切断する。

VPN Client>AccountDisconnect
AccountDisconnect コマンド - 接続中の接続設定の切断
接続設定の名前: ocn

設定の確認

VPN Client>AccountGet
AccountGet コマンド - 接続設定の設定の取得
接続設定の名前: ocn

項目                                 |値
-------------------------------------+-----------------------------
接続設定名                           |ocn
接続先 VPN Server のホスト名         |vpn.example.ac.jp
接続先 VPN Server のポート番号       |443
接続先 VPN Server の仮想 HUB 名      |Vpn
経由するプロキシサーバーの種類       |直接 TCP/IP 接続
サーバー証明書の検証                 |無効
接続に使用するデバイス名             |seth0
認証の種類                           |RADIUS または NT ドメイン認証
ユーザー名                           |USER@EXAPLE
VPN 通信に使用する TCP コネクション数|1
各 TCP コネクションの確立間隔        |1
各 TCP コネクションの寿命            |無制限
半二重モードの使用                   |無効
SSL による暗号化                     |有効
データ圧縮                           |無効
ブリッジ / ルータモードで接続        |無効
モニタリングモードで接続             |無効
ルーティングテーブルを調整しない     |有効
QoS 制御機能を使用しない             |無効
コマンドは正常に終了しました。

対話型の省略

対話型のCLIは便利だけど、なれてくると面倒でしかないので。

次のように、コマンドに引数を与えると、一回で実行できるショートカットとして動作する。

sudo ./vpncmd /CLIENT localhost /CMD AccountList

もともとがWindowsのCMD.exe っぽいコマンドなので、「引数」にハイフンをつけずスラッシュ(/)で指定するのがちょっとわかりにくい。macOSでもスラッシュで指定ですね。

接続切断 の省略

接続切断はぱぱっと呼び出したい。

接続は、次のようにする

sudo ./vpncmd /CLIENT localhost /CMD AccountConnect ocn

切断は、次のようにする。

sudo ./vpncmd /CLIENT localhost /CMD AccountDisconnect ocn

切断したら、クライアントを終了する

ほんとこれよく忘れるので注意です。

sudo ./vpnclient stop

参考資料

https://qiita.com/ask/items/9ff1529d228ec093aa07

*1: っていうか、ネットワーク管理の業者がこの仕様を理解できないので、macOSからL2TPできないんです。設定してもらえないんです。

OpenWrtでヘアピンNAT(NATループバック)

ヘアピンNAT(ループバック)

OpenWrtでときどき、接続できないポートがあって、不思議だなと思ってたらNATがループバックしてなかった。

設定→ネットワーク

ネットワーク→ファイアウォール→ポートフォワーディングの順にたどって該当のport forwarding についての、詳細(Advance)を見るとチェックボックスがある。このチェックボックスのチェックをするだけでオッケ。(よく忘れる)

f:id:takuya_1st:20211101223129p:plain

ヘアピンNATをiptablesで実現するとしたら

ヘアピンNAT( loopback NAT/ redirect NAT) を実現するとしたら。

PPPoEのデバイスに、DNATとSNATを噛ましてあげれば動く。

iptables -t nat -D PREROUTING -i $LAN_DEV -s $LAN_NETWORK_ADDR/24 -d $PPPOE_IPADDR -p tcp -m tcp --dport 443 -j DNAT --to-destination $TARGET

ルーターの場合SNATはマスカレードで入っていることが多いのでそのままで通る。 マスカレードしていいなら記述は不要。

LANをMASQURADE(NAPT)してないなら、SNATを書いてルーターからのパケットとしてあげる。などで対応が可能。

iptables -t nat -A POSTROUTING -o $LAN_DEV -s $LAN_NET_ADDR/24 -d $TARGET -p tcp -m tcp --dport 443 -j SNAT --to-source $ROUTER_IP

Before

たとえば、wireguard VPN経由( 172.16.4.0/24 ) のパケットがNATループバックをしてない。

root@OpenWrt:/etc/config# iptables -t nat -L  | grep 443
DNAT       tcp  --  192.168.10.0/24       ""ISP v4***  tcp dpt:https /* !fw3: atom-https (reflection) */ to:192.168.10.24:443
DNAT       udp  --  192.168.10.0/24       ""ISP v4***  udp dpt:https /* !fw3: atom-https (reflection) */ to:192.168.10.24:443
DNAT       tcp  --  172.16.4.1           ""ISP v4***  tcp dpt:https /* !fw3: atom-https (reflection) */ to:192.168.10.24:443
DNAT       udp  --  172.16.4.1           ""ISP v4***  udp dpt:https /* !fw3: atom-https (reflection) */ to:192.168.10.24:443
DNAT       tcp  --  anywhere             anywhere             tcp dpt:https /* !fw3: atom-https */ to:192.168.10.24:443
DNAT       udp  --  anywhere             anywhere             udp dpt:https /* !fw3: atom-https */ to:192.168.10.24:443

After

VPN経由( 172.16.4.0/24 )のパケットも443 でループバックするようになった。

root@OpenWrt:~# iptables -t nat -L  | grep 443
DNAT       tcp  --  192.168.10.0/24       ""ISP v4***  tcp dpt:https /* !fw3: atom-https (reflection) */ to:192.168.10.24:443
DNAT       udp  --  192.168.10.0/24       ""ISP v4***  udp dpt:https /* !fw3: atom-https (reflection) */ to:192.168.10.24:443
DNAT       tcp  --  172.16.4.0/24        ""ISP v4***  tcp dpt:https /* !fw3: atom-https (reflection) */ to:192.168.10.24:443
DNAT       udp  --  172.16.4.0/24        ""ISP v4***  udp dpt:https /* !fw3: atom-https (reflection) */ to:192.168.10.24:443
DNAT       tcp  --  172.16.4.1           ""ISP v4***  tcp dpt:https /* !fw3: atom-https (reflection) */ to:192.168.10.24:443
DNAT       udp  --  172.16.4.1           ""ISP v4***  udp dpt:https /* !fw3: atom-https (reflection) */ to:192.168.10.24:443
DNAT       tcp  --  anywhere             anywhere             tcp dpt:https /* !fw3: atom-https */ to:192.168.10.24:443
DNAT       udp  --  anywhere             anywhere             udp dpt:https /* !fw3: atom-https */ to:192.168.10.24:443

ループバックの設定

ループバックの設定は、config でも行える。

コマンドからSMTPでメールを送るテストを手軽にやる。

メールサーバーの疎通テストを簡単にやりたい

メールサーバーに接続して送信するのを定期的にチェックしています。

昭和平成のレトロ家庭用の機器(FAX)などがメールに対応しててもSMTPSに非対応なので、Raspiでプロキシしていますが、疎通テストや認証テストが面倒なので、自動的にチェックしたい

crontab にかけるくらい単純にしたい。

curlSMTPを送信するのが非常に楽

SMTP over SSL の場合

curl --url 'smtps://smtp.gmail.com:465' \
  --ssl-reqd \
  --mail-from 'example@gmail.com' \
  --mail-rcpt 'edm-user@example.com.com' \
  --upload-file mail.txt  \
  --user 'developer@gmail.com:your-accout-password'

ローカル用の認証・SSLなしSMTPの場合

curl -v --url 'smtp://192.168.1.1:25255' \
  --mail-from from@example.com \
  --mail-rcpt  to@example.com \
  --form-string content=hello

curl でやるのが一番ラク

telnet で接続確認して、そのまま送ると自動化できない。

expect で telnet するのは流石に違う。

python でsmtplib などを使うのもいいが管理が煩雑だ。

ssmpt をインストールするのも面倒だ。

curl は比較的にどこにでも入ってる。

curl ならコマンドがどこにでもあるし、crontab に書くのもらくちんだった。

参考資料

https://blog.edmdesigner.com/send-email-from-linux-command-line/

うるさいHDDを黙らせる/ hdparm でHDD電源オフでスピン停止にする

HDD が睡眠を妨げる。

いまどきHDDなんかやめてSSDにしろと言われそうなのですが。SSD・HDDを1TBあたりの単価を考えると8倍近い。 そこそこ安価にデータをアーカイブするとしたらHDDの効率がいいわけです。

アーカイブなのでそんなに頻繁にアクセスしないので、電源を切っておけばいいのですが、取り外してしまうと使いたいときに使えないので死蔵することになり、データを置いておく意味すら希薄になる。

そこで、スピンダウンをして静かにしてもらえばいいのではないかと。

hdparm でスピンオフ時間を設定できる。

hdparm を使えば、スピンをオフ時間を設定できるらしい。

takuya@:~$ sudo hdparm -S 25  /dev/sd*

/dev/sda:
 setting standby to 25 (2 minutes + 5 seconds)

/dev/sdb:
 setting standby to 25 (2 minutes + 5 seconds)

/dev/sdc:
 setting standby to 25 (2 minutes + 5 seconds)

/dev/sdd:
 setting standby to 25 (2 minutes + 5 seconds)
 

即時オフ

今すぐオフもできる。恐ろしいので、やらないけど。

sudo hdparm -S 0 /dev/sda1

sudo vim /etc/hdparm.conf

設定の永続化

 command_line {
  hdparm -S 25 /dev/sda
  hdparm -S 25 /dev/sdb
  hdparm -S 25 /dev/sdc
  hdparm -S 25 /dev/sdd
}

注意点

HDDのスピンダウンとアップを頻繁に繰り返すと故障の原因になりやすい。 また、自動チェックツール(smartd)やデフラグでアップになる可能性が高い。

現在の状態を確認する。

hdparm -C で状態を確認できる。

takuya@:~$ sudo hdparm -C /dev/sd*

/dev/sda:
 drive state is:  active/idle

/dev/sdb:
 drive state is:  active/idle

/dev/sdc:
 drive state is:  active/idle

/dev/sdd:
 drive state is:  active/idle

/dev/sde:
 drive state is:  active/idle

/dev/sdf:
 drive state is:  active/idle

今すぐスリープ

今すぐスリープできる。

sudo hdparm -Y /dev/sd*

スリープになったの確認する

sudo hdparm -C /dev/sd*  # ちょっと時間がかかる。

smartmontools が起動させてしまうらしい。

しかし、HDDの電源をオフでも、smartd が起こしてしまうとのこと https://www.systemworks.co.jp/ex_spindown.php

たとえば、3日おきくらいにしておくとか。

takuya@borane:~$ cat /etc/default/smartmontools
# 2021-11-18 by takuya 3日おきくらい
smartd_opts="--interval=259200"

電源オフは茨の道。

どうしても停止させたいときは止められることはわかるが、あまり止めるべきでもないのかもしれない。

予備ディスクはスタンバイでもいいかも

接続してるだけの待機ディスクは、スタンバイにしてもいいと思う。

参考資料

tarに纏めながら、rclone の転送を同時にやる

tar しながら転送も同時にやる。

tar zcvf - <dir> | rclone rcat remote:path/to/file

rclone "rcat"

rcat を使えば、パイプで処理するみたいに投げられる。

https://rclone.org/commands/rclone_rcat/

echo "hello world" | rclone rcat remote:path/to/file
ffmpeg - | rclone rcat remote:path/to/file

使い所

バックアップをとったりするとファイル数が多くなりがちで、ダウンロードや同期にAPI呼び出しが多くなりタイムロスが多い。 また、google drive のように symbolic link に非対応なクラウド・ストレージに転送すると情報が欠損するためtar にまとめて転送したほうがなにかと使い勝手がいい。

同様に圧縮しながら転送

gzip や xz を使って圧縮しながら転送すれば、処理も楽である。

mysqldump と組み合わせると。

一時ファイルを作らずに直接rclone の先にバックアップ取れるから快適じゃないか。

たとえば、こんな感じでmysqldump | gzip | rclone をパイプに纏めてやる

sudo \
 mysqldump --single-transaction  \
 -h localhost -u takuya --password=XXXXX takuya-database  2>/dev/null \
 | gzip -  | \ 
 rclone gdrive:takuya-server/backup/mysqldump.sql.gz > /dev/null

gdriveなら30日間のファイル履歴が残るので、バックアップをローテーションして消さなくても良い。最強じゃないか。

rclone で圧縮しながら dd

ddも rclone で圧縮しながらできます。

sudo dd if=/dev/sdg bs=16M --progress | \
  gzip -9 -c  | \
 rclone --bwlimit=5M rcat gdrive:/backup.img.gz

ディスク容量がカツカツでも dd / ddrescue できるので嬉しい。

2021-11-26

rclone と mysqldump の組み合わせについて追記。

MacOSの濁点・半濁点を解決する。

macOS で作られたファイルの濁点半濁点問題。

ファイル名がUTF-8といえども、mac は濁点を合成文字として扱うので、Linuxで読むと面倒が起きる。

sudo convmv -f utf-8 -t utf-8 -r --notest --nfc ./*

ディレクトリ内部を再帰的に処理される。 find と組み合わせなくていいのが便利。

逆に言えば、内部全部が対象なので、うっかりしないように注意。

rsync 時にまとめてやる

rsync -a --iconv=utf-8-mac,utf-8 

rsync 時にまとめてやっておくと便利だと思われる。

ユニコード正規化

ユニコードで、macOSの日本語はNFDの亜種が使われている。

たとえば、macで作った<パ>は2文字で構成されている。

"パ".each_codepoint{|e| p e.to_s(16) }.map
"30cf"
"309a"

文字コードから文字のコードポイントを取得すると、2文字で構成されていることがわかる。

Windowsで作った「パ」は1文字で構成されている。

"パ".each_codepoint{|e| p e.to_s(16) }
"30d1"

この文字の違いは、ブラウザなど対応したソフトウェアで表示自体は同じだし、ユニコードとして特に問題がないので間違いではない。

けど、非対応なソフトウェアで表示すると、表示が乱れる。カーソル位置がおかしくなる

特に問題がでてくるのがWindowsのターミナルである。本当に不便なのです。 個人的な意見を言えば、日本語の取り扱いは、日本政府による規制と標準化が求められると思います。

apt でバージョン指定でアップグレード・インストール/apt list -a ですべてのバージョンを確認

/## apt install でバージョン指定でインストール

やりかた

sudo apt install パッケージ名=バージョン名

sudo apt install gitlab-ce=13.12.12-ce.0

apt install は指定バージョンまでアップグレードできます。

apt コマンドは アップグレードもインストールも同じだし当たり前なんだけど。

sudo apt install --upgrade gitlab-ce=13.12.12-ce.0
sudo apt upgrade gitlab-ce=13.12.12-ce.0

バージョン指定ができますね。

バージョン一覧を取得する

インストール対象となるバージョンを探すには、まずバージョン一覧を取得して選択する必要があります。

sudo apt list --upgradable -a gitlab-ce

または apt-cacheから

sudo apt-cache showpkg gitlab-ce

アップグレード時のコツ

単純にアップグレードしようとして、sudo apt upgrade アップグレードコマンドを打ち込んだとき

unpack でエラーになったりする。

dpkg: アーカイブ /var/cache/apt/archives/gitlab-ce_14.3.3-ce.0_amd64.deb の処理中にエラーが発生しました (--unpack):

そういうときに、一気にバージョンをあげようとせずに、一つずつバージョンを上げて試していいくことで解決する。

例に上げた、gitlab のバージョンアップは、postgresql 11 が必須になっていて sudo gitlab-ctl pg-upgrade をやれと、12→13 のときに出てきた、 12→14だと表示はもうなかった。

このように、最新バージョンに一気に挙げずに、バージョンを少しずつアップグレードしていくことで問題点が洗い出される事例は多い。

バージョン指定はgitlab-ce でよく使う。

gitlab-ceは、一気にバージョンを上げると、エラーになる。なので、ちょっとずつ上げていく

バックアップとって、バージョンを指定して、バージョンアップする。

sudo gitlab-backup create
sudo apt install --upgrade gitlab-ce=12.10.14-ce.0

gitlabについては、upgrade時にバックアップは自動取得されること「も」あるが、油断しちゃだめ。

ssh 秘密鍵のパスフレーズを変更(解除)する

ssh秘密鍵のパスワード変更

パスフレーズを変更する。(パーミッション変更が必要)

chmod 600  ~/my.ssh.id_rsa
ssh-keygen -p -f  ~/my.ssh.id_rsa

sshパスフレーズを解除する

パスフレーズを解除するときは、Enterを押す。空のパスワードを設定する。

Enter old passphrase:
Enter new passphrase (empty for no passphrase):
Enter same passphrase again:

以前の投稿

秘密鍵のパスフレーズを解除してパスフレーズなしの秘密鍵を作る。 - それマグで!

ookla のスピードテストが安定しないうえにサーバー指定できなくなった

定期的に speedtest で速度測定してる

もうかれこれ2年位毎日スピードテストしてはログをためてたんだけど、サーバー指定ができなくなった。

いままでは、speedtest-cli でサーバー指定ができたのだけど、2021/08/29から最寄りのサーバーしか接続できなくなった。

f:id:takuya_1st:20211012032800p:plain

測定しすぎと怒られそうではあるが。。。ユーチューブが普及してインターネットが遅くなるピーク時間帯の速度変化を観察することで、混雑時間帯を避けて、apt update / upgrade やdocker の更新など個人で使うにはとんでもない量のパケットを流し続ける負い目がどうしてもあったので、速度測定の結果をダウンロードやアップロードの負荷分散に利用していた。どの時間帯が混んでいるか、PPPoEを複数ISPで接続して測定したんですが、統計的にデータを取るためには測定結果ブレ幅が大きいので、サーバー指定してたわけです。しかしできなくなった。

オフィシャルのクライアントなら指定できる

f:id:takuya_1st:20211012033521p:plain

speedtest-cli ではなく、オフィシャルのspeedtestコマンドを使えばいまでもID指定は可能なのですが。いつ終わるかわからないし、有志のCPUパワーなのでずっと気が引けてたわけで。これを期に乗り換えようと思ったわけです。

代替のスピードテスト

そこで、Cloudflareのスピードテストがサービス開始していたことを思い出して、そのクライアントを探したら・・・あった

私は断続的に測定して結果の変化を知りたいので、最高速度が知りたいわけでもなく。継続的に安定した測定結果を求めていた。

たぶんもっといい方法はある。VPS借りてHTTPの転送速度をCurlで測るとかしとけばだいぶ正確に出るとは思うけど。管理するサーバーが増えるのは困るし。どこかの画像をずっとダウンロードし続けるのも気が引ける。

cloudflareのスピードテスト用のcli

speed-cloudflare-cli - npm

takuya@~$ npm i -g speed-cloudflare-cli

added 1 package, and audited 2 packages in 3s

found 0 vulnerabilities
takuya@~$ speed-cloudflare-cli
 Server location: Tokyo (NRT)
         Your IP: 27.230.xxxxx (JP)
         Latency: 48.63 ms
          Jitter: 2.80 ms
     100kB speed: 24.94 Mbps
       1MB speed: 61.60 Mbps
      10MB speed: 189.32 Mbps
      25MB speed: 213.41 Mbps
     100MB speed: 274.18 Mbps
  Download speed: 207.27 Mbps
    Upload speed: 29.66 Mbps
takuya@~$

いけそう

grep の後方参照の代替案 5つ

grep では 後方参照ができないので、代替案を考えることになる。

方法1基本的な方法 -o

マッチした箇所だけを取り出す -o を使って、マッチした箇所だけを取り出す。

cat out.txt  | \grep -Po  'Abc.*Xyz'

方法2 先読み+あとよみ

先読み・後ろ読みにマッチした箇所だけを取り出す -o を組み合わせる。

cat out.txt  | \grep -Po  '(?<=Abc).*(?=Xyz)'

方法3 perlruby などワンライナーを使う

もはや、grep ではないが、代替案としては行ける

cat out.txt  | ruby -pe 'sub(/^Abc(.*)Xyz/, "\\1")'

方法4 sed で置換する

もはや、grep ではない。が代替案としては行けるし、sedは手軽に使える。

cat out.txt | sed  -nr 's/Abc(.+)Xyz/\1/p' 

ただし、sedでは、非マッチ箇所が残る。 sedはマッチした箇所を置換するだけなので、マッチしない箇所は出力される。

そこで、-nをつけて、マッチした箇所だけを表示するようにする。

方法5 bashの機能を使う。

[[ takuya =~ taku(.+) ]]
echo ${BASH_REMATCH[@]}

bash に正規表現のマッチングが追加されている。マッチした箇所をBASH_REMATCH変数で取り出せるので、後方参照の代わりになる。

後方参照を使いたいような場面はシェルスクリプトを作ってるときだろうから bash正規表現の各種機能で事足りることがほとんどである。

感想

grep正規表現と後方参照の代替は、「後方参照」を「置換」と考えて、sedで行うのがベターだと思われる。

grep でマッチした箇所を取り出す -o などと併せて覚えおけば、ちょっとしたフィルタ処理やデータを整形するときに使えるのではないかと思う。

後方参照=置換と考えるのが大事。マッチした箇所を取り出す。だと限界がある。

2023-05-23

bash rematchを追加

github actionでテストを自動実行する例

github のActionでテストを実行する例をつくりました。

https://github.com/takuya/github-action-sample-testing-code

テストの設定

github action を実行するには、レポジトリにディレクトリとYAMLを作成します。

## レポジトリの作成
mkdir my-repo
cd my-repo
touch README.md
git init 
git add README.md
git commit -m initial
git remote add origin git@github.com/xxxx/xxxx 
## actions の設定ファイルを設置
mkdir -p .github/workflows/
touch .github/workflows/actions.yml
## push で起動する
git add .github
git push origin master

テスト実行する

アクションの記述例はシンプルにしてあります。

name: build and test

on:
  push:
    branches:
      - master
  
jobs:
  build_and_test:
    runs-on: ubuntu-latest

    steps:
      - name: checkout
        uses: actions/checkout@v2
      - name: composer install
        shell: bash
        run : |
          composer install  -q --no-ansi --no-interaction --no-scripts --no-progress
          composer dump-autoload
      - name : run
        shell : bash
        run : |
          vendor/bin/phpunit
          

github アクションの限界

action は主に ubuntu-latest のgithub が用意したインスタンスが利用されます。

node や npm/yarn や webdriver などほしいと思うものは一通りインストール済みなので、無理せず既存のインスタンスを活用するのがいいかともう。

自分のdocker イメージを利用するのは少し手間なので、docker イメージを利用するなら circleCIのほうが楽かもしれない。

実行結果バッジ

実行結果バッジは次のようなURLで画像を取得できる。

https://github.com/takuya/cronjob-alternative/workflows/test%20src/badge.svg

URLの構造は次の通り。

https://github.com/takuya/{$レポジトリ名}/workflows/${ジョブ名}/badge.svg

ジョブ名のところには、yaml の最上位で指定した name が入る。

次のようなYAMLの場合は build and testになる。スペースは%20 になる。URLエンコードされる。

name: build and test

on:
  push:
    branches:
      - master
  

github actionsは動作がメチャクチャ速いので活用できる範囲で使っていきたい。

参考資料

https://flutter.takuchalle.dev/posts/how-to-test-on-github-actions/

cronをWEB管理するソフトを作りました。

cron の代替を作りました。

github.com

f:id:takuya_1st:20210917182720p:plain

経緯

cron(自動実行プログラム)の管理が煩雑でした。

増え続けるcrontab。ここ数年管理が崩壊していました。systemd.timer や google app scripts なども増えるし、ラムダ系で実行してたりいろいろな所で、タイマー(カレンダー)による実行が溢れかえる。手軽に作れる一方でエラーログを確認したりがめんどくさいので放置してて実行してるけど動いてないスクリプトとかもあり全容把握が困難でした。

散らばる cron / systemd.timer

crontab による管理は、3つの箇所に別れます。

システム(/etc/cron.d/) root ( /var/spool/cron/crontabs/root ) そして個人ユーザー ( /var/spool/cron/crontabs/$USER ) の3つに別れます。この時点でいい加減に管理していると大変。

最近は、systemd.timer が増えてきました。 systemd.timer は systemd.service とペアになっているので、ファイルが複数に分かれます。さらに、ユーザー空間の systemd ( systemd --user )もあります。ログ管理や任意実行は平易化したが、ファイル管理は煩雑になりました。

ここに、lxd/docker/vbox などの仮想環境のcronが加わると管理は不可能になります。

散らばる通知・ログ

大変なのはログと通知の管理です。スケジューラーがコケたときに通知くるけどcron などは > /dev/null とか > /var/log/my-job.log みたいにログファイルに書くしかなくて困る。エラー時の実行ログと正常ログを見たいのに、実行ログと実行時間から推察してログをたどるのが面倒だった。

crontab 書くのめんどくさい

ちょっとした分岐が書きたい

crontab で実行しようとすると、スクリプトいったん実行可能なファイルにして書き出す必要があって、条件分岐をかくのが大変だし、実行ファイルを一箇所に集めて管理とか大変だ。crontab 見ても何が実行されるのかパット見わからないことが多くて困る。

一時停止が大変

ジョブがエラーになってるときなど、一時停止したいときコメントアウトで対応してるとコメントアウトだらけになって、何がなんだかわからなくなった。

crontab の実行予定がわからない。

大量にあるとスクロールしながらひと目見て、cron 書式から次回実行はいつなんだというのがわかりにくい。ソートしたい。

ssh経由でリモート実行したい

cron を一箇所に集めて、sshでリモート実行ちゃえばある程度解決する。SSHで実行するとなると環境変数ssh-agentを書いて環境変数をうまく扱う必要がある。するとスクリプトファイルを使って実行するがcrontabファイルで完結しなくなる。

cron 限界じゃね?

というわけで、cronは5~10件が限界じゃね?別の何かが必要じゃないかと思いました。

webで管理したい。

webでcronの実行予定とスクリプト本文を編集できたらいいじゃん。

web管理機能を探したけど

いいものが見つからなかった。ジョブスケジューラは高機能すぎる気がするしjenkinsとかはCI/CDツールだし。どうも用途に合わないので、laravelでcron書式をつかったスケジュール実行ができるのでそれを応用して作りました。

ソフトウェア仕組み

root で スケジュール実行用のワーカーを常駐させて、毎分単位で実行するべき処理を探しては実行する。

このソフトウェアの限界

毎分単位で処理を探して、プロセスをforkしてジョブを実行するので、1000件くらいが上限だと思われる。

このソフトの特長

WEBから編集ができます。

一時停止状況がわかるように

コメントアウトの代わりに一時停止状況がわかるように

f:id:takuya_1st:20211004210305p:plain

次回実行の予定がわかるように

cron表記を日本語表記にして次回実行がわかるようにしておいた。

f:id:takuya_1st:20210917182425p:plain

実行ごとに結果がわかるように

実行ごとにSTDOUT/STDERRを保存するように。また実行時間も測定し、スケジュール全体の見直しに活用できるようにしました。

f:id:takuya_1st:20210917182531p:plain

スクリプト本文の直接記述

crontab が1コマンド(1行)で記述しなくちゃいけなくて改行を入れてるとおかしくなるのでとても不便なので、

改行やコメントを書けるようにして、直接記述できるようにしして、実行シェルをbash以外から選べるようにしておいた。現在はbash/phpを選べます。sh/rubyは行けそうなのでそのうち対応しようと思います。pythonはちょっと難しいかも。

f:id:takuya_1st:20211004210436p:plain

インストールと取得はgithub

Github に公開しておきます。

github.com

とりあえず試す用のdockerも用意しました。

docker pull ghcr.io/takuya/cronjob-alternative:latest
docker run --rm -p 5000:80 ghcr.io/takuya/cronjob-alternative:latest

cronといってますが、cron表記を使うだけで、crontabの編集を一切やりません。実行するワーカーはcronとは別にじぶんで起動して使います。

multipass のubuntu で dockerd を動かして外部から接続できるようにする。

multipass に docker を入れる。

multipass を windowsでインストールすると、HyperVの仮想マシンで起動します。

choco install multipass 

windowshyper-V で起動したubuntu に docker を入れる。

sudo snap install docker 

mutipass な ubuntu の docker(dockerd) が外部接続を受けるようにする。

dockerd の起動設定を変える。

[Service]
# ExecStart=/usr/bin/snap run docker.dockerd  
ExecStart=/usr/bin/snap run docker.dockerd  -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock

書き換えコマンド。

sudo vim /etc/systemd/system/snap.docker.dockerd.service
# または
# sudo systemctl edit snap.docker.dockerd.service

リロード

sudo systemctl daemon-reload
sudo systemctl status snap.docker.dockerd.service

外部(Windows/Mac)からmultipass のdocker に接続する。

multipass の仮想マシンtcp://172.17.231.118:2375 でlisten しているので、そこへ接続する。

DOCKER_HOST=tcp://172.17.231.118:2375 /usr/bin/docker run --rm hello-world

docker コマンドをインストールする。

dockerd を除いた、docker コマンドだけをインストールするには

wsl v1 のDeibanの場合。

curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo   "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
## 
sudo apt install docker-ce-cli

windows内に複数の仮想環境が乱立するが、TCP経由でdockerd を使うと選択肢が多く、比較的インストールがスムーズに終わることがわかる。

Windows内部の構造を単純に考えると次のようになる。

windows 内部
   - multipass 
       - ubuntu 
   - wsl
     - v1 
       - debian 

docker-desktop でお手軽なんだけど、docker-desktop 版をいれるとwslv2 になるのでめんどくさいんですよ。