ラベル Linux の投稿を表示しています。 すべての投稿を表示
ラベル Linux の投稿を表示しています。 すべての投稿を表示

2024年5月4日土曜日

ubuntu 22.04.4 with GPU passthrough on ESXi 8

画像生成AIで遊んでみたくなったので下記パーツでPC作成、ESXi 8を導入。
  • ASRock B550M Pro4
  • AMD Ryzen 7 5700X
  • Palit NED407S019K9-1043D (GeForce RTX 4070 SUPER Dual 12GB)
内蔵NICがRealtek RTL8111Hチップだったので、NICを増設してESXiセットアップは完了。
久々でvmklinux driverが利用できなくなったのを忘れてて時間を無駄にしました。
#vmware買収で無償版が無くなりホームラボ利用が衰退しそうで寂しい限りです。
#VMUG Advantageは大丈夫だろうか。

さて本題、ESXiでGPUパススルー設定後、ubuntu server 22.04.4 LTSのVM作成しましたが、NVIDIAのドライバインストールでハマりました。
結論、nvidia-driver-nnnでうまくいかない場合は、nvidia-driver-nnn-openを試してみよう。

環境
$ uname -r
5.15.0-105-generic
$ grep -i version /etc/os-release 
VERSION_ID="22.04"
VERSION="22.04.4 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
$
オープンソースドライバーNouveauの確認。
$ lsmod |grep nouveau
nouveau              2306048  0
mxm_wmi                16384  1 nouveau
i2c_algo_bit           16384  1 nouveau
drm_ttm_helper         16384  1 nouveau
ttm                    86016  3 vmwgfx,drm_ttm_helper,nouveau
drm_kms_helper        311296  2 vmwgfx,nouveau
video                  65536  1 nouveau
wmi                    32768  2 mxm_wmi,nouveau
drm                   622592  7 vmwgfx,drm_kms_helper,drm_ttm_helper,ttm,nouvea
$
Nouveau無効化設定とkernel initramfs再構築。
$ cat << EOF | sudo tee /etc/modprobe.d/blacklist-nouveau.conf
blacklist nouveau
options nouveau modeset=0
EOF
$
$ sudo update-initramfs -u
リブートしNouveauドライバーがロードされていないことを確認。
$ lsmod |grep nouveau
$
ドライバーの検索。
$ ubuntu-drivers devices
ERROR:root:aplay command not found
== /sys/devices/pci0000:02/0000:02:05.0 ==
modalias : pci:v000010DEd00002783sv00001569sd0000F302bc03sc00i00
vendor   : NVIDIA Corporation
driver   : nvidia-driver-550-open - distro non-free
driver   : nvidia-driver-550 - distro non-free recommended
driver   : nvidia-driver-550-server-open - distro non-free
driver   : nvidia-driver-550-server - distro non-free
driver   : xserver-xorg-video-nouveau - distro free builtin

== /sys/devices/pci0000:00/0000:00:0f.0 ==
modalias : pci:v000015ADd00000405sv000015ADsd00000405bc03sc00i00
vendor   : VMware
model    : SVGA II Adapter
manual_install: True
driver   : open-vm-tools-desktop - distro free

$ 
recommendedなパッケージをインストールする。
$ sudo apt install nvidia-driver-550
リブート後に確認。
$ lsmod |grep -i nvidia
nvidia_uvm           4648960  0
nvidia_drm             94208  0
nvidia_modeset       1343488  1 nvidia_drm
nvidia              54018048  2 nvidia_uvm,nvidia_modeset
drm_kms_helper        311296  2 vmwgfx,nvidia_drm
drm                   622592  7 vmwgfx,drm_kms_helper,nvidia,nvidia_drm,ttm
$
$ nvidia-smi
No devices were found
$
$ lspci |grep -i nvidia
02:05.0 VGA compatible controller: NVIDIA Corporation AD104 [GeForce RTX 4070 SUPER] (rev a1)
02:05.1 Audio device: NVIDIA Corporation Device 22bc (rev a1)
$
上手くいっていない。
dmesgを見ると下記のメッセージがあり、初期化に失敗しています。
[    3.337870] nvidia-modeset: Loading NVIDIA Kernel Mode Setting Driver for UNIX platforms  550.67  Tue Mar 12 23:29:25 UTC 2024
[    3.339965] [drm] [nvidia-drm] [GPU ID 0x00000205] Loading driver
[    3.346790] ACPI Warning: \_SB.PC08.S6F0._DSM: Argument #4 type mismatch - Found [Buffer], ACPI requires [Package] (20210730/nsarguments-61)
[    5.381856] NVRM: GPU 0000:02:05.0: RmInitAdapter failed! (0x26:0x56:1598)
[    5.382198] NVRM: GPU 0000:02:05.0: rm_init_adapter failed, device minor number 0
[    5.385633] [drm:nv_drm_load [nvidia_drm]] *ERROR* [nvidia-drm] [GPU ID 0x00000205] Failed to allocate NvKmsKapiDevice
[    5.386063] [drm:nv_drm_register_drm_device [nvidia_drm]] *ERROR* [nvidia-drm] [GPU ID 0x00000205] Failed to register device
apt list |grep nvidia-driverで以前のバージョンを確認し、545,535を試してみたがどれも同じ結果となりました。
パッケージ削除は下記を実行すれば良い。
$ sudo apt-get --purge remove nvidia-*
$ sudo apt-get --purge remove libnvidia-*
調べてみると参考URL※1が、同じエラーのようでopen driverを利用すれば良いとのこと。
$ sudo apt install nvidia-driver-550-open
リブート後確認。
$ lsmod |grep -i nvi
nvidia_uvm           4657152  0
nvidia_drm             94208  0
nvidia_modeset       1495040  1 nvidia_drm
nvidia               8577024  2 nvidia_uvm,nvidia_modeset
drm_kms_helper        311296  2 vmwgfx,nvidia_drm
drm                   622592  7 vmwgfx,drm_kms_helper,nvidia,nvidia_drm,ttm
$ nvidia-smi
Fri May  3 21:39:07 2024
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.67                 Driver Version: 550.67         CUDA Version: 12.4     |
|-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA GeForce RTX 4070 ...    Off |   00000000:02:05.0 Off |                  N/A |
| 30%   41C    P8             15W /  220W |       2MiB /  12282MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI        PID   Type   Process name                              GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|  No running processes found                                                             |
+-----------------------------------------------------------------------------------------+
$
成功。
Ubuntu Server Docs ※2をみると、-server版の方が用途に合っていたかもしれません。

参考URL)

2022年10月30日日曜日

ASRock C236M WS障害


マザーボードの(BIOS/UEFI)バッテリーが弱るとHDDアクセスが不安定になる場合があることを知りました。
電源入れっぱなしのPCでマザーボードのバッテリーが原因で障害になるとは思いませんした。

サーバ運用しているASRock C236M WSにリモートログインが出来なくなり無反応な状態に。
普段外しているディスプレイ、キーボードを接続するも無反応なので、強制電源断で再起動すると取り敢えずログインできるようになりました。

messagesログを見ると下記メッセージの記録後、同じメッセージが定期的に記録され
kernel: ata11.00: exception Emask 0x10 SAct 0xc0 SErr 0x4000000 action 0xe frozen
kernel: ata11.00: irq_stat 0x80000040, connection status changed
kernel: ata11: SError: { DevExch }
kernel: ata11.00: failed command: WRITE FPDMA QUEUED
kernel: ata11.00: cmd 61/08:30:68:d2:b4/00:00:06:00:00/40 tag 6 ncq 4096 out#012         res 50/00:00:00:00:00/00:00:00:00:00/a0 Emask 0x10 (ATA bus error)
kernel: ata11.00: status: { DRDY }
kernel: ata11.00: failed command: WRITE FPDMA QUEUED
kernel: ata11.00: cmd 61/08:38:80:2f:13/00:00:61:00:00/40 tag 7 ncq 4096 out#012         res 50/00:00:00:00:00/00:00:00:00:00/a0 Emask 0x10 (ATA bus error)
kernel: ata11.00: status: { DRDY }
kernel: ata11: hard resetting link
kernel: ata11: SATA link up 6.0 Gbps (SStatus 133 SControl 300)
kernel: ata11.00: configured for UDMA/133
kernel: ata11: EH complete
1時間40分後に下記メッセージの記録を最後にストール状態になったようです。
kernel: ata11.00: exception Emask 0x10 SAct 0x0 SErr 0x4000000 action 0xe frozen
kernel: ata11.00: irq_stat 0x80000040, connection status changed
kernel: ata11: SError: { DevExch }
kernel: ata11.00: failed command: FLUSH CACHE EXT
kernel: ata11.00: cmd ea/00:00:00:00:00/00:00:00:00:00/a0 tag 5#012         res 50/00:00:00:00:00/00:00:00:00:00/a0 Emask 0x10 (ATA bus error)
kernel: ata11.00: status: { DRDY }
kernel: ata11: hard resetting link
kernel: ata11: SATA link up 6.0 Gbps (SStatus 133 SControl 320)
kernel: ata11.00: configured for UDMA/133
kernel: ata11.00: retrying FLUSH 0xea Emask 0x10
kernel: ata11.00: device reported invalid CHS sector 0
kernel: ata11: EH complete
エラーが出ていたata11(起動HDD)のSMART情報を確認するとエラー記録もなく、ディスクとしては問題なさそうな様子。

SATAケーブルがダメになったかな?などと考えながらその他ログなどを確認していると、I/Oエラーのメッセージが表示されシェルでコマンドを実行できなくなり、電源断再起動後にBIOS画面も出ない状態に。
なんどか試すもBIOS画面が表示されたり、されなかったりでOSブートまで辿り着かず。
これはマザーボードのバッテリー交換あるあるかなと思い、バッテリー交換するとGRUB画面まで進み一先ず復旧しました。

不安定な状態で確認作業等をしていたため、ファイルシステムの不整合も起きたようで、/sysrootがマウントできずに起動失敗するようになっていました。

ブート時にシェルモードになり、dmesgで下記メッセージが表示されたので、lvscanでrootボリュームを調べ、xfs_repairを実行したところ失敗。
起動ディスクだから失敗したのかなと、あまり考えずUSBブートイメージを作成してxfs_repairを実行し直しました。
最終的にxfs_repairの-Lオプションを付けて実行したので、USBブートする必要なかったかもしれません。
  :
systemd[1]: Mounting /sysroot...
kernel: SGI XFS with ACLs, security attributes, no debug enabled
kernel: XFS (dm-0): Mounting V5 Filesystem
kernel: XFS (dm-0): Corruption warning: Metadata has LSN (720:28720) ahead or current LSN (720:28592). Please unmount and run xfs_repair (>= v4.3) to resolve.
kernel: XFS (dm-0): log mount/recovery failed: error -22
kernel: XFS (dm-0): log mount failed
mount[526]: mount: wrong fs type, bad option, bad superblock on /dev/mapper/ct-root
mount[526]: missing codepage or helper program, or other error
mount[526]: In some cases useful info is found in syslog - try
mount[526]: dmesg | tail or so.
systemd[1]: sysroot.mount mount process exited, code=exited status=32
systemd[1]: Failed to mount /sysroot.
systemd[1]: Dependency faild for Initrd Root File System.
systemd[1]: Dependency failed for Reload Configuration from the Real Root.
  :
しばらく様子見が必要ですが、マザーボードのバッテリー交換だけで今のところ復旧した感じです。
まさか、起動時だけでなく起動している状態でもマザーボードのバッテリー状態が影響するとは思いませんでした。

ちなみに外したバッテリーの電圧は、無負荷状態では3Vありました。

2021年1月23日土曜日

raidz2のST4000DM004交換

昨年12月にHDDを一台交換したraidz2(ST4000DM004:7台+WD40EZRZ:1台構成)ですが、また一台のHDDでCurrent_Pending_Sector, Offline_Uncorrectableが発生しました。
数日様子見して数値の増加はありませんでしたが、二桁だったので交換することに。
今回も前回と同じくWD40EZRZと入れ替えました。

稼働23,353時間で交換となりました。
今のところST4000DM004の8台中4台が3年未満で故障となっています。
ST3000DM001よりも故障が早いかな?と言う気がします。

2020年12月28日月曜日

raidz2のST4000DM004をWD40EZRZに交換

今年6月にHDD二台が故障したraidz2(ST4000DM004の8台構成)ですが、scrubを実行したところread failとなるHDDが二台出てしまいました。
うち一台は一時的なものだったようですが、他方は下記の感じでCurrent_Pending_Sector, Offline_Uncorrectableが3桁台に…。 8台中3台が3年未満で故障とは。
# smartctl -A /dev/sdf
smartctl 7.0 2018-12-30 r4883 [x86_64-linux-3.10.0-1160.6.1.el7.x86_64] (local build)
Copyright (C) 2002-18, Bruce Allen, Christian Franke, www.smartmontools.org

=== START OF READ SMART DATA SECTION ===
SMART Attributes Data Structure revision number: 10
Vendor Specific SMART Attributes with Thresholds:
ID# ATTRIBUTE_NAME          FLAG     VALUE WORST THRESH TYPE      UPDATED  WHEN_FAILED RAW_VALUE
  1 Raw_Read_Error_Rate     0x000f   059   055   006    Pre-fail  Always       -       10386248
  3 Spin_Up_Time            0x0003   096   096   000    Pre-fail  Always       -       0
  4 Start_Stop_Count        0x0032   100   100   020    Old_age   Always       -       96
  5 Reallocated_Sector_Ct   0x0033   100   100   010    Pre-fail  Always       -       0
  7 Seek_Error_Rate         0x000f   087   060   045    Pre-fail  Always       -       513462226
  9 Power_On_Hours          0x0032   075   075   000    Old_age   Always       -       22634 (210 239 0)
 10 Spin_Retry_Count        0x0013   100   100   097    Pre-fail  Always       -       0
 12 Power_Cycle_Count       0x0032   100   100   020    Old_age   Always       -       96
183 Runtime_Bad_Block       0x0032   099   099   000    Old_age   Always       -       1
184 End-to-End_Error        0x0032   100   100   099    Old_age   Always       -       0
187 Reported_Uncorrect      0x0032   088   088   000    Old_age   Always       -       12
188 Command_Timeout         0x0032   100   099   000    Old_age   Always       -       2 3 3
189 High_Fly_Writes         0x003a   100   100   000    Old_age   Always       -       0
190 Airflow_Temperature_Cel 0x0022   068   045   040    Old_age   Always       -       32 (Min/Max 27/45)
191 G-Sense_Error_Rate      0x0032   100   100   000    Old_age   Always       -       0
192 Power-Off_Retract_Count 0x0032   100   100   000    Old_age   Always       -       935
193 Load_Cycle_Count        0x0032   099   099   000    Old_age   Always       -       2628
194 Temperature_Celsius     0x0022   032   055   000    Old_age   Always       -       32 (0 20 0 0 0)
195 Hardware_ECC_Recovered  0x001a   070   064   000    Old_age   Always       -       10386248
197 Current_Pending_Sector  0x0012   098   098   000    Old_age   Always       -       720
198 Offline_Uncorrectable   0x0010   098   098   000    Old_age   Offline      -       720
199 UDMA_CRC_Error_Count    0x003e   200   200   000    Old_age   Always       -       0
240 Head_Flying_Hours       0x0000   100   253   000    Old_age   Offline      -       21826h+07m+03.803s
241 Total_LBAs_Written      0x0000   100   253   000    Old_age   Offline      -       14648100184
242 Total_LBAs_Read         0x0000   100   253   000    Old_age   Offline      -       28880173058
longtestでもエラーとなり、smartdもsyslogにメッセージを出すようになったので交換しました。
Reallocated_Sector_Ctが0のままなんですが、こう言うものなんでしょうか。
SMARTの情報ってイマイチ見方が分からないんですよね。

さて、前回交換時は多重障害になってしまいましたがresilverに数日かかったので、今回はCMRのものを試そうとWD40EZRZに変えてみました。
たまたまセール中にあたりST4000DM004より安く買えたのでお財布にも優しくラッキーでした。

で、resilverにかかった時間ですが、17時間弱で完了しました。
私の用途ではST4000DM004でも普段使っている時は全く問題ないんですけど、resilverなど長時間書き込みが発生すると影響が出てしまうような感じです。
大容量で安いCMRのHDDって無いんですよね。
# zpool status
  pool: tank
 state: ONLINE
  scan: resilvered 3.27T in 0 days 16:47:29 with 0 errors on Sun Dec 27 03:04:23 2020
config:

        NAME                                                STATE     READ WRITE CKSUM
        tank                                                ONLINE       0     0     0
          raidz2-0                                          ONLINE       0     0     0
            ata-ST4000DM004-2CV104_aaaaaaaa-part1           ONLINE       0     0     0
            ata-ST4000DM004-2CV104_bbbbbbbb-part1           ONLINE       0     0     0
            ata-ST4000DM004-2CV104_cccccccc-part1           ONLINE       0     0     0
            ata-ST4000DM004-2CV104_dddddddd-part1           ONLINE       0     0     0
            ata-ST4000DM004-2CV104_eeeeeeee-part1           ONLINE       0     0     0
            ata-WDC_WD40EZRZ-22GXCB0_WD-ffffffffffff-part1  ONLINE       0     0     0
            ata-ST4000DM004-2CV104_gggggggg-part1           ONLINE       0     0     0
            ata-ST4000DM004-2CV104_hhhhhhhh-part1           ONLINE       0     0     0

errors: No known data errors

2020年12月5日土曜日

送信者アドレスで接続先MTAを変更してSMTPSとSMTP認証を使用する

いくつかのISPのメールアドレスを利用しているのですが、受信はISPの転送設定を利用してVPSのMTAに集め、送信はISPのメールサーバを利用せずにVPSのMTAを経由させて直接配送していました。

MUAでFromアドレス毎にISPのメールサーバを指定するのが面倒なので、このように利用していたのですがDMARCも普及してきているようなので正しくISPのメールサーバを経由させるように設定変更することにしました。

まあ、なりすましメールと同じなので仕方ないですが、SPFの認証結果は気にはなっていたけど面倒なので長年放置していたと言う感じです。
ちなみに独自ドメインについては一応SPF設定はしてありますが、こちらのアドレスでメールを送信することはほとんどありません。

前置きが長くなりましたが、環境と条件は下記になります。
  • CentOS 8.2
  • postfix 3.3.1
  • Envelope FromがISPのメールアドレスの場合は、ISPのメールサーバを経由する。
  • 上記以外のメールアドレスの場合は、直接配送する。
  • ISPのメールサーバは、SMTP over SSLで認証(SMTP Auth)ありとする。
まずは、/etc/postfix/main.cfに下記の設定を追加します。
sender_dependent_default_transport_maps = hash:/etc/postfix/sender_dependent_transport
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/password_maps
smtp_sasl_security_options = noanonymous
クライアントサイドのSMTP Auth有効化と、送信者アドレスによるtranspot mapの定義を行っています。
次にsmtp_sasl_password_mapsに指定した/etc/postfix/password_mapsを下記の形式で記述します。
[ISPメールサーバ]:ポート  アカウント名:パスワード
ファイルの作成後、postmapコマンドでハッシュファイルを作成します。
password_maps, password_maps.dbはプレーンなパスワード情報を含むため、rootのみ参照可能に設定します。
例えば、下記のようになります。
# cd /etc/postfix
# cat password_maps
[mail.example1.com]:465   foo@example1.com:password1
[mail.example2.com]:465   var@example2.com:password2
#
# postmap hash:password_maps
# chmod og-rxw password_maps*
sender_dependent_default_transport_mapsに設定した/etc/postfix/sender_dependent_transportは下記の形式で記述します。
メールアドレス  relay-smtps:[ISPメールサーバ]:ポート
こちらもファイルの作成後、postmapコマンドでハッシュファイルを作成します。
例えば、下記のようになります。
# cat sender_dependent_transport
foo@example1.com  relay-smtps:[mail.example1.com]:465
var@example2.com  relay-smtps:[mail.example2.com]:465
#
# postmap hash:sender_dependent_transport
relay-smtpsサービスを指定しているのがポイントになります。
次に/etc/postfix/master.cfに下記のようにrelay-smtpsサービスを追加します。
relay-smtps unix  -     -       n       -       -       smtp
        -o smtp_tls_security_level=encrypt
        -o smtp_tls_wrappermode=yes
設定後、postfixを再起動すれば完了です。
これで通常は適宜STARTTLSを使用し直接配送、設定したメールアドレスの場合はSMTPS(SMTP over SSL)を使用し各ISP経由で配送するようになります。

最後にsyslogに下記のメッセージを出し認証に失敗する場合は、SMTP Authの認証メカニズムが不足しているのでSASLのパッケージを追加します。
warning: SASL authentication failure: No worthy mechs found
恐らくcyrus-sasl-md5, cyrus-sasl-plainパッケージをインストールすれば解決するはずです。
認証アルゴリズムは、CRAM-MD5, DIGEST-MD5, PLAINがあれば問題ないかと思います。

参考)
Postfix Configuration Parameters: sender_dependent_default_transport_maps
Postfix TLS Support: Sending only mail for a specific destination via SMTPS

2020年8月22日土曜日

fail2ban on CentOS8

先日CentOS8化したメール中継サーバですが、firewalldとipsetをCentOS7の この記事 と同じようにpublic, domesticゾーンを利用するように設定していました。

sshとsmtp-submissionは国内IPのみ許可するように設定したのでそれほどでもないのですが、smtpはIP制限することもできず鬱陶しいアクセスが多く辟易してきたので対処することにしました。

CentOS6の時は、iptablesでルールを書いていたりしましたが今回はfail2banを利用します。
firewall-cmdとipsetを使用する場合は、以下のようになります。
# dnf install fail2ban

# cat << EOF > /etc/fail2ban/jail.local
[DEFAULT]
banaction = firewallcmd-ipset[actiontype=<multiport>]
banaction_allports = firewallcmd-ipset[actiontype=<allports>]

bantime = 604800
findtime = 86400
maxretry = 3

[sshd]
enabled = true

[postfix]
enabled = true
mode = aggressive
maxretry = 2
EOF
#

# cat << EOF > /etc/fail2ban/action.d/firewallcmd-common.local
[Init]
blocktype = DROP
EOF
#
これで、sshdとpostfixの不正アクセス対策が動作します。
動作状況は下記のようにfail2ban-clientコマンドで確認できます。
# fail2ban-client status
Status
|- Number of jail:	2
`- Jail list:	postfix, sshd
#
# fail2ban-client status postfix
Status for the jail: postfix
|- Filter
|  |- Currently failed:	5
|  |- Total failed:	90
|  `- Journal matches:	_SYSTEMD_UNIT=postfix.service
`- Actions
   |- Currently banned:	18
   |- Total banned:	31
   `- Banned IP list:	x.x.x.x y.y.y.y .......
#
nftablesやipsetの定義は下記のように、iptables、ipsetコマンドで確認できます。
# iptables -nL
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0            multiport dports 22 match-set f2b-sshd src
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0            multiport dports 25,465,587 match-set f2b-postfix src

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
#
# ipset --list --name
f2b-postfix
DOMESTICv4
DOMESTICv6
f2b-sshd
#
# ipset --list f2b-postfix
Name: f2b-postfix
Type: hash:ip
Revision: 4
Header: family inet hashsize 1024 maxelem 65536 timeout 600
Size in memory: 1944
References: 1
Number of entries: 18
Members:
x.x.x.x timeout 342227
y.y.y.y timeout 214654
~
#
f2b-xxxがfail2banで作成されたipsetです。
ipsetのDOMESTICv4は、firewalldのdomesticゾーンに紐づけています。
本サーバでは、IPv6のアドレスをアサインしていないのでDOMESTICv6は未使用です。

上記設定で暫く運用していたのですが、smtpポートアクセスで遮断できないものが多かったのでpostfix用フィルターを下記のように改造してみました。
# cd /etc/fail2ban/filter.d
# diff -uw postfix.conf.original  postfix.conf
--- postfix.conf.original	2020-04-16 21:41:47.000000000 +0900
+++ postfix.conf	2020-08-10 21:45:56.232418891 +0900
@@ -44,9 +44,13 @@
 mdre-extra = %(mdre-auth)s
             %(mdre-normal)s
 
-mdpr-aggressive = (?:%(mdpr-auth)s|%(mdpr-normal)s|%(mdpr-ddos)s)
+mdpr-ddos2 = disconnect from
+mdre-ddos2 = ^unknown\[<HOST>\].+commands=\d/\d
+
+mdpr-aggressive = (?:%(mdpr-auth)s|%(mdpr-normal)s|%(mdpr-ddos)s|%(mdpr-ddos2)s)
 mdre-aggressive = %(mdre-auth2)s
                   %(mdre-normal)s
+                  %(mdre-ddos2)s
 
 mdpr-errors = too many errors after \S+
 mdre-errors = ^from [^[]*\[<HOST>\]%(_port)s$
逆引きが出来ず、かつ必要なコマンドを発行していない接続は大体検知出来ています。
漏れはありますが9割以上遮断できているので一先ず良しとしています。

sshdのアクセス制限については、TCP Wrapperが利用できなくなり不便になりました。
ipsetとhosts.allowを併用して許可IPを絞り込んでいたのですが、良い代替方式が見つかりません。

2020年8月9日日曜日

postfixのメール中継設定

メール中継サーバで利用しているCentOS6のEOLが迫ってきたので、CentOS8に入れ替えました。 その際にpostfixのメール中継サーバ設定で少しはまったのでメモしておきます。

結論から言うと「パラメータのデフォルト値が変わっているのでマニュアルを確認しよう」です。

CentOS8ではpostfix 3.3.1がインストールされますが、compatibility_levelの設定値によりパラメータのデフォルト値が変わります。
postconfの出力からcompatibility_levelを抜き出すと、下記になります。
# postconf | grep compatibility_level
append_dot_mydomain = ${{$compatibility_level} < {1} ? {yes} : {no}}
compatibility_level = 2
mynetworks_style = ${{$compatibility_level} < {2} ? {subnet} : {host}}
relay_domains = ${{$compatibility_level} < {2} ? {$mydestination} : {}}
smtpd_relay_restrictions = ${{$compatibility_level} < {1} ? {} : {permit_mynetworks, permit_sasl_authenticated, defer_unauth_destination}}
smtputf8_enable = ${{$compatibility_level} < {1} ? {no} : {yes}}
#
デフォルトのmain.cfでは、compatibility_levelが2に設定されています。
このためrelay_domainsのデフォルトが空となり、リレーが許可されません。
リレーを許可するには、明示的にrelay_domainsを設定する必要があります。
また、これらパラメータを参照しているパラメータも影響を受けるので注意が必要です。

今回のリプレースでは旧サーバの設定ファイルをコピーせず、デフォルトの設定ファイルから書き直しました。 main.cf内のコメントを読みながら設定していて「The default relay_domains value is $mydestination.」と記載があったため、旧バージョンと同じと考えてしまい、リレーできない原因が分からず暫くはまりました。

マニュアルにはバッチリ記載がありましたので、横着せずマニュアルを確認しようと言うことかと思います。 ちなみにcompatibility_levelのデフォルトは0なので、旧サーバの設定をコピーした場合は挙動が変わることはなかったようです。

2020年6月12日金曜日

raidz2のST4000DM004交換

raidz2(ST4000DM004の8台構成)で運用中のHDDが一台、I/Oエラー頻発ということなく突然死という感じで故障しました。
他HDDのSMART情報から大体17740時間で壊れたようです。

予備と交換してresilver中にもう一台壊れました...。
たまたま二台とも故障時にPCの近くにいましたが、どちらも突然カッコン、カッコンと嫌な音を立てたあと鳴り止み御臨終です。
どちらもSMART情報も取れない状態。

まだ、1台目のresilverが完了していない状況ですが、zpool statusで進捗を確認するとDEGRADED状態のHDDが1台発生。
こちらはSMART情報が取れたので確認するとエラーが記録されていました。
何とか完了まで持ち堪えて欲しい。

初ST4000DM004ですが、2年で二台故障、一台エラー発生と耐久性が低いような感じがします。

2020/6/20追記)
一台目のresilver中に全HDDがDEGRADEDステータスになりヒヤッとしましたが、リブート後無事再開することができました。
一台目のresilver完了後に、二台目のHDDを入れ替えresilverを開始しましたが何方も6日程度時間がかかり、およそ12日で復旧となりました。

処理時速度は、resilver開始後に暫くして3桁MB/sになるも、直ぐに70MB/s程度になり徐々に減速し終了間際は52MB/s程度になり非常に低速です。
SMRが原因と思われるので、次に入れ替える時はCMRを選んでみようと思います。 4TBのHDD入れ替えで6日は、厳しいかなと思います。
ちなみに、プールの使用率は90%程度で空きが1.5TB程度ある状態、正常時の書き込みが激遅になっていないので、空き容量としては許容範囲かなと考えています。

Seagate BarraCudaはコスパが良くて気に入っていたのですが、SMRとzfsの相性はやはり悪そうです。
普段利用している時は特に不都合は感じないのですが、resilverの長時間書き込みではデメリットが顕著になるように感じました。

最近組んだPCのHDDもBarraCudaなのでSMRなんですよね、しかも6TBにしてしまいました。

2018年6月24日日曜日

firewalld and ipset on CentOS7

CentOS 7でipsetを利用してみました。
firewalldが標準になり、ipsetはどうやって使うんだろうと思っていましたが、firewalldの設定でOKのようです。
先日設定したVPN接続を国内からのみ許可するように設定しました。

IPアドレスのリストは、http://nami.jp/ipv4bycc/で配布されているものを利用させてもらうのが簡単かと思います。
私は、IPv6のリストも欲しかったので、RIRが提供してるリストを加工して利用することにしました。
  • ftp://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest
  • ftp://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-extended-latest
  • ftp://ftp.apnic.net/pub/stats/apnic/delegated-apnic-extended-latest
  • ftp://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-extended-latest
  • ftp://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-extended-latest
設定するにあたり、IPアドレスのリストをCIDRの形式でx.x.x.x/nやx::/nの形式で用意しておきます。

以下の環境で設定しています。
$ cat /etc/centos-release
CentOS Linux release 7.5.1804 (Core) 
$ uname -rv
3.10.0-862.3.2.el7.x86_64 #1 SMP Mon May 21 23:36:36 UTC 2018
$

まず、国内からのアクセスを扱うように新しいゾーンを作成します。
# firewall-cmd --permanent --new=zone=domestic
次に、IPv4、IPv6用のipsetを作成します。
# firewall-cmd --permanent --new-ipset=DOMESTICv4 --type=hash:net
# firewall-cmd --permanent --new-ipset=DOMESTICv6 --type=hash:net --family=inet6
リロードして確認すると次のようになります。
# firewall-cmd --reload
# firewall-cmd --get-ipsets
DOMESTICv4 DOMESTICv6
#
作成したipsetへのエントリ追加は、次のようにファイル指定で行うことができます。
# firewall-cmd --permanent --ipset=DOMESTICv4 --add-entries-from-file=./ipv4_list
# firewall-cmd --permanent --ipset=DOMESTICv6 --add-entries-from-file=./ipv6_list
追加したエントリは、次のように確認できます。
$ firewall-cmd --ipset=DOMESTICv4 --get-entries |head
1.0.16.0/20
1.0.64.0/18
1.1.64.0/18
1.5.0.0/16
1.21.0.0/16
1.33.0.0/16
1.66.0.0/15
1.72.0.0/13
1.112.0.0/14
14.0.8.0/22
ここまででipsetの設定ができましたので、firewalldのゾーンとipsetを関連づけます。
# firewall-cmd --permanent --zone=domestic --add-source=ipset:DOMESTICv4
# firewall-cmd --permanent --zone=domestic --add-source=ipset:DOMESTICv6
これで、DOMESTICv4, DOMESTICv6に定義したIPアドレスからのアクセスは、domesticゾーンが適用されるようになりました。

あとは、domesticゾーンに必要なサービスを追加して、デフォルトゾーン(ここではpublic)から不要なサービスを削除すればOKです。
# firewall-cmd --permanent --zone=domestic --add-service=ssh
# firewall-cmd --permanent --zone=domestic --add-service=ipsec
# firewall-cmd --permanent --zone=domestic --add-port=5555/tcp
#
# firewall-cmd --permanent --zone=public --remove-service=ssh
# firewall-cmd --permanent --zone=public --remove-service=ipsec
# firewall-cmd --permanent --zone=public --remove-port=5555/tcp
#
# firewall-cmd --reload
# firewall-cmd --info-zone=domestic
domestic (active)
  target: default
  icmp-block-inversion: no
  interfaces: 
  sources: ipset:DOMESTICv4 ipset:DOMESTICv6
  services: ssh ipsec
  ports: 5555/tcp
  protocols: 
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules: 

permanent指定なので、適宜リロードが必要です。

2018年6月8日金曜日

SoftEther on CentOS7

L2TP/IPsecを利用するため、SoftEtherをインストールします。
前準備として、次のポートを利用できるようにします。
  • 500/udp
  • 4500/udp
  • 5555/tcp
  • 443/tcp
/usr/lib/firewalld/services/ipsec.xmlをみると、500/udp, 4500/udpはipsecサービスを利用すれば良さそうです。 443はSoftEtherの管理ツール用に利用します。

次のように設定します。
# firewall-cmd --permanent --zone=public --add-service=ipsec
# firewall-cmd --permanent --zone=public --add-port=5555/tcp
# firewall-cmd --permanent --add-rich-rule="rule family=ipv4 source address=192.168.0.0/23 service name=https accept"
# firewall-cmd --reload
...
# firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: br0 enp4s0 enp0s31f6 enp5s0
  sources: 
  services: ssh dhcpv6-client ipsec
  ports: 5555/tcp
  protocols: 
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules: 
 rule family="ipv4" source address="192.168.0.0/23" service name="https" accept
...
あとは、公式サイトにある手順に従いSoftEtherをインストールすればOKです。
ただ、CentOS7では、initではなくsystemdを利用するようになっているため、起動用のunitファイルを作成する必要がありました。

公式サイトに記載されているinit用の起動スクリプトを参考に下記のunitファイルを作成しました。
# cat /etc/systemd/system/vpnserver.service
[Unit]
Description=SoftEther VPN Server
After=network.target

[Service]
Type=forking
ExecStart=/usr/local/vpnserver/vpnserver start
ExecStop=/usr/local/vpnserver/vpnserver stop
Restart=on-abort
RestartSec=3

[Install]
WantedBy=multi-user.target
次のように有効化して起動します。
# systemctl enable vpnserver
# systemctl start vpnserver
iPhoneで接続確認してみたところ、移行前と同じように利用できました。

Policy based routing on CentOS 7

v6プラスの導入以降、ネットワーク環境はIPoE接続とPPPoE接続を併用しています。
実際のアドレスとは違いますが、次のような構成になっています。
  • PPPoE BBルータ (192.168.0.1/23)
  • IPoE BBルータ (192.168.1.1/23)
192.168.0.0/24で利用していたものに、192.169.1.0を追加し、同セグメントにするためサブネットマスクを/23に変更した形になります。
IPoEの経路は速くて良いのですが、外部から接続できるようにNATも利用するためPPPoEの経路もなくせません。

ということで、新PCでは、デフォルトゲートウェイはIPoEのBBルータを利用し、NAT通信はPPPoEのルータを経由するようポリシーベースルーティングの設定をすることにしました。

ネットワック環境は次のようになります。
# nmcli con show
NAME                    UUID                                  TYPE      DEVICE
bridge-br0              4a45b8de-49cc-4c50-8b8d-eeeeeeeeeeee  bridge    br0
bridge-slave-enp0s31f6  417d8d8a-31d3-4f91-b64e-ffffffffffff  ethernet  enp0s31f6
enp4s0                  b91dc271-b52b-4363-a74d-bbbbbbbbbbbb  ethernet  enp4s0
enp5s0                  0584bdaf-5f78-44f1-a941-cccccccccccc  ethernet  enp5s0
....
# ip route
default via 192.168.1.1 dev enp5s0 proto static metric 102
192.168.0.0/23 dev enp5s0 proto kernel scope link src 192.168.0.100 metric 102
192.168.0.0/23 dev br0 proto kernel scope link src 192.168.0.101 metric 425
....
#
enp5s0に192.168.0.100/23、br0に192.168.0.101/23、デフォルトゲートウェイに192.168.1.1が設定してあります。
この環境で192.168.0.101で受けた接続は、192.168.0.1を経由するルートとなるように設定します。

まず、NetworkManagerでルーティングルールが扱えるように次のパッケージをインストールして有効化します。
# yum install NetworkManager-dispatcher-routing-rules
....
# systemctl enable NetworkManager-dispatcher
これで、/etc/sysconfig/network-scripts/rule-NAMEファイルが使えるようになったので設定ファイルを作成していきます。
まずは、ルーティングテーブルIDと名前を/etc/iproute2/rt_tablesに追加します。
# cat /etc/iproute2/rt_tables
#
# reserved values
#
255     local
254     main
253     default
0       unspec
#
# local
#
#1      inr.ruhep
100     pppoe-rt
末尾の100 pppoe-rtを追加しています。
次にbr0に割り当てている192.168.0.101からの通信が上記で設定したルーティングテーブルを参照してルーティングするように設定します。
/etc/sysconfig/network-scripts/route-bridge-br0を次のように作成します。
# cat /etc/sysconfig/network-scripts/route-bridge-br0
default via 192.168.0.1 dev br0 table pppoe-rt
192.168.0.0/23 dev br0 table pppoe-rt
ルーティングポリシーのルールを/etc/sysconfig/network-scripts/rule-bridge-br0に作成します。
# cat /etc/sysconfig/network-scripts/rule-bridge-br0
from 192.168.0.101/32 table pppoe-rt priority 100

念のため、リブート後に自動設定されることを確認します。
# ip route
default via 192.168.1.1 dev enp5s0 proto static metric 102
192.168.0.0/23 dev enp5s0 proto kernel scope link src 192.168.0.100 metric 102
192.168.0.0/23 dev br0 proto kernel scope link src 192.168.0.101 metric 425
.....
# ip rule show
0:      from all lookup local
100:    from 192.168.0.101 lookup pppoe-rt
32766:  from all lookup main
32767:  from all lookup default
#
# ip route show table pppoe-rt
default via 192.168.0.1 dev br0
192.168.0.0/23 dev br0 scope link

以上

2018年6月3日日曜日

CentOS6からCentOS7へKVM環境移行

KVM環境をCentOS6からCentOS7に移行します。
作業内容は次のようになります。
  • ブリッジデバイスの準備
  • KVMゲストの移行
まずは、ネットワーク環境から。
# nmcli con show
NAME       UUID                                  TYPE      DEVICE
enp0s31f6  03b67f02-f8a7-4893-aa4e-aaaaaaaaaaaa  ethernet  enp0s31f6
enp4s0     b91dc271-b52b-4363-a74d-bbbbbbbbbbbb  ethernet  enp4s0
enp5s0     0584bdaf-5f78-44f1-a941-cccccccccccc  ethernet  enp5s0
virbr0     f0764441-0948-4d0d-830d-dddddddddddd  bridge    virbr0
#
enp0s31f6, enp4s0がLOMの1Gbps、enp5s0がXG-C100Cの10Gbpsです。
enp5s0をシステムのデフォルト、enp0s31f6をKVMの仮想ブリッジ用、enp4s0をSoftEtherのブリッジ用に使用 します。

ブリッジデバイスbr0を作成します。
# nmcli con add type bridge ifname br0
Connection 'bridge-br0' (4a45b8de-49cc-4c50-8b8d-eeeeeeeeeeee) successfully added.
#
# nmcli con show
NAME        UUID                                  TYPE      DEVICE
bridge-br0  4a45b8de-49cc-4c50-8b8d-eeeeeeeeeeee  bridge    br0
enp0s31f6   03b67f02-f8a7-4893-aa4e-aaaaaaaaaaaa  ethernet  enp0s31f6
enp4s0      b91dc271-b52b-4363-a74d-bbbbbbbbbbbb  ethernet  enp4s0
enp5s0      0584bdaf-5f78-44f1-a941-cccccccccccc  ethernet  enp5s0
virbr0      f0764441-0948-4d0d-830d-dddddddddddd  bridge    virbr0

次に、br0と物理NIC(enp0s31f6)の関連付けを行います。
# nmcli con add type bridge-slave ifname enp0s31f6 master bridge-br0
Connection 'bridge-slave-enp0s31f6' (417d8d8a-31d3-4f91-b64e-ffffffffffff) successfully added.
#
# nmcli con show
NAME                    UUID                                  TYPE      DEVICE
bridge-br0              4a45b8de-49cc-4c50-8b8d-eeeeeeeeeeee  bridge    br0
enp0s31f6               03b67f02-f8a7-4893-aa4e-aaaaaaaaaaaa  ethernet  enp0s31f6
enp4s0                  b91dc271-b52b-4363-a74d-bbbbbbbbbbbb  ethernet  enp4s0
enp5s0                  0584bdaf-5f78-44f1-a941-cccccccccccc  ethernet  enp5s0
virbr0                  f0764441-0948-4d0d-830d-dddddddddddd  bridge    virbr0
bridge-slave-enp0s31f6  417d8d8a-31d3-4f91-b64e-ffffffffffff  ethernet  --

ブリッジデバイスのスレーブとなったenp0s31f6を削除します。
# nmcli con delete enp0s31f6
Connection 'enp0s31f6' (03b67f02-f8a7-4893-aa4e-aaaaaaaaaaaa) successfully deleted.
#
# nmcli con show
NAME                    UUID                                  TYPE      DEVICE
bridge-br0              4a45b8de-49cc-4c50-8b8d-eeeeeeeeeeee  bridge    br0
bridge-slave-enp0s31f6  417d8d8a-31d3-4f91-b64e-ffffffffffff  ethernet  enp0s31f6
enp4s0                  b91dc271-b52b-4363-a74d-bbbbbbbbbbbb  ethernet  enp4s0
enp5s0                  0584bdaf-5f78-44f1-a941-cccccccccccc  ethernet  enp5s0
virbr0                  f0764441-0948-4d0d-830d-dddddddddddd  bridge    virbr0

ブリッジデバイスにIPアドレスを設定します。
# nmcli con mod bridge-br0 ipv4.method manual ipv4.addresses "192.168.0.101/23"
#
# ip route
default via 192.168.1.1 dev enp5s0 proto static metric 102
192.168.0.0/23 dev enp5s0 proto kernel scope link src 192.168.0.100 metric 102
192.168.0.0/23 dev br0 proto kernel scope link src 192.168.0.101 metric 425
192.168.122.0/24 dev virbr0 proto kernel scope link src 192.168.122.1

KVMゲストの移行
旧PCでKVMゲストをシャットダウンして、下記コマンドで設定ファイルをエクスポートします。
GUEST-NAMEは、virsh list -allなどで表示される名前に置き換えてください。
# virsh dumpxml GUEST-NAME > GUEST-NAME.xml

次に旧PCからエクスポートした設定ファイルと、ゲストのイメージファイルを新PCにコピーします。 標準では、/var/lib/libvirt/images 配下にイメージファイルがあります。
コピー後、新PCで次のコマンドを実行して設定をインポートします。
# virsh define GUEST-NAME.xml

あとは、virt-managerを起動してメモリサイズ等を微調整後、ゲストを起動すれば移行完了。
のはずでしたが、ゲスト起動時に下記メッセージが表示され起動しませんでした。
Error Starting domain: unsupported
configuration: host doesn't support invariant TSC

結局、/etc/libvirt/qemu/GUEST-NAME.xmlから次の記述を削除したら起動しました。
<feature policy='require' name='invtsc'/>

PT3関連インストール

旧PCからPT3を移設したのでドライバと録画ツールを設定しました。
OS情報は、次になります。
$ cat /etc/centos-release
CentOS Linux release 7.5.1804 (Core)
$ uname -mvr
3.10.0-862.3.2.el7.x86_64 #1 SMP Mon May 21 23:36:36 UTC 2018 x86_64

PT3ドライバのインストール
$ git clone https://github.com/m-tsudo/pt3.git
$ cd pt3
# make install

カードリーダ関連のインストール
# yum install pcsc-lite*
# systemctl enable pcscd
# systemctl start pcscd

録画ツールのインストール
まずは、libarib25のインストール。
$ hg clone http://hg.honeyplanet.jp/pt1 -r b25 b25
$ cd b25/arib25
$ make
# make install
logを見るとrecpt1は、最近の衛星放送再編の修正が入っているようです。
$ hg clone http://hg.honeyplanet.jp/pt1
$ cd pt1/recpt1
$ ./autogen.sh
$ ./configure --enable-b25
$ make
# make install
動作確認できたので、pt1_dev.hで定義されているデバイスファイル名をpt3用に編集して再度make installしました。
特に問題なく完了。

2018年6月2日土曜日

新PC

サーバ利用しているPCの利用期間がそろそろ5年になるので、下記構成で新PCを作りました。
  • ASRock C236M WS
  • Intel Xeon E3-1245v6
  • DDR4-2400 8GB x2
  • Seagate ST4000-DM004 x8
  • HGST HDT725032VLA360 x1
  • AREA SD-PE4SA3ES4L
  • ASUS XG-C100C
骨董品のHDT725032VLA360ですが、S.M.A.R.T.のPower_On_Hoursが151のものがあったので、システム領域用に使いました。 そのうち新しいHDDに変えよう。

CentOS7, ZFS on Linuxをインストールして数日動かしていましたが安定しているようなので、ぼちぼちメイン環境を旧PCから移行しようと思います。
とりあえず、作業予定は以下です。
  • PT3の移設
  • KVM環境の移行
  • SoftEther VPN移行
実は、去年暮れにamazon.comでGS100MX−100NASとXG-C100C 2つを購入して年初に完成予定でした。 しかし、ケースの納期が遅れ届いたのが3月になり、さっそく組み立てるも不幸なことにマザーボードの初期不良、購入から時間が経っていたので修理対応となりました。

先日ようやく戻ってきて完成となったのですが、HDDが安くなってたり、XG-C100Cが日本で普通に買えるようになっていたりで微妙な気分です…。

2017年10月30日月曜日

raidz2のHDD交換

I/Oエラーが頻発するようになったためHDDを交換しました。
SMARTの情報によると33716時間で交換です。
ST3000DM001からST3000DM008になりました。

2017年8月7日月曜日

raidz2のディスク交換

HDD故障のため1本交換、どうやら約一月前にアクセス不能になっていたようです。

2017年2月4日土曜日

MicroServerのHDD故障

MicroServerのHDDが故障したので交換しました。
ZFSのresilverに時間かかりそう。

2016年11月21日月曜日

raidz2のディスク交換

I/Oエラーが頻発するようになったためHDDを交換しました。
SMARTの情報によると25390時間からエラー頻発のようでしたので、1057日22時間で故障ですね。

2015年6月9日火曜日

raidz2のHDD交換

raidz2で使用しているHDDが1台壊れました。
SMARTから情報が取れなくなっていましたが、大体24000時間超は動作していたようです。
同時期に使い始めたHDDが同じプールにあるので、そろそろ交換かな?と思いながら、zfs replaceしたらresilver中にもう1台も壊れました。

RAID障害時にありがちなリビルド中の二重故障、raidz2にしていて良かった。

HDDを2台交換して作業終了。
HDD1台あたり、resilverに約14時間(2.7TB)かかりました。

2014年8月12日火曜日

USB温度計

昨年末辺りからUSB接続の温度計でPC付近の室温を計測しています。
上海問屋で買ったものですが、PCsenserのTEMPerと同じ物のようです。

CentOS 6でpcsensor-1.0.0-multi.tgzを少し弄って使用しています。

MRTG用の出力オプションを使用して利用するつもりでしたが、私の環境では1回起動すると2回目以降正常動作しませんでした。
機器を抜き差しすると1回は正常動作するため、初期化か終了処理がうまくいっていないようなのですが、いろいろ試しても直せませんでした。

取りあえずループモード(-lオプション)では動作するので、次のようにHTTPで情報取得できるようにして使用しています。
$ curl http://localhost:9999/
30.12
30.12
10:37
pcsensor
$

pcsenser-1.0.0-multi との差分は下記になります。
Index: Makefile
===================================================================
--- Makefile    (.../vendor/pcsensor/1.0.0-multi)
+++ Makefile    (.../pcsensor/trunk)
@@ -3,7 +3,7 @@
 CFLAGS = -O2 -Wall

 pcsensor:      pcsensor.c
-       ${CC} -DUNIT_TEST -o $@ $^ -lusb
+       ${CC} -DUNIT_TEST -o $@ $^ -lusb -levent

 clean:
        rm -f pcsensor *.o
Index: pcsensor.c
===================================================================
--- pcsensor.c  (.../vendor/pcsensor/1.0.0-multi)
+++ pcsensor.c  (.../pcsensor/trunk)
@@ -34,6 +34,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 


 #define VERSION "1.0.0"
@@ -45,6 +47,8 @@
 #define INTERFACE2 0x01

 #define MAX_DEV 4
+#define PID_FILE "/var/run/pcsensor.pid"
+#define PORT_NUM 9999

 const static int reqIntLen=8;
 const static int reqBulkLen=8;
@@ -64,6 +68,7 @@
 static int formato=0;
 static int mrtg=0;
 static int calibration=0;
+static int use_daemon=0;

 static usb_dev_handle *handles[MAX_DEV];

@@ -78,24 +83,24 @@
 void usb_detach(usb_dev_handle *lvr_winusb, int iInterface) {
         int ret;

-       ret = usb_detach_kernel_driver_np(lvr_winusb, iInterface);
-       if(ret) {
-               if(errno == ENODATA) {
-                       if(debug) {
-                               printf("Device already detached\n");
-                       }
-               } else {
-                       if(debug) {
-                               printf("Detach failed: %s[%d]\n",
-                                      strerror(errno), errno);
-                               printf("Continuing anyway\n");
-                       }
-               }
-       } else {
-               if(debug) {
-                       printf("detach successful\n");
-               }
-       }
+        ret = usb_detach_kernel_driver_np(lvr_winusb, iInterface);
+        if(ret) {
+                if(errno == ENODATA) {
+                        if(debug) {
+                                printf("Device already detached\n");
+                        }
+                } else {
+                        if(debug) {
+                                printf("Detach failed: %s[%d]\n",
+                                       strerror(errno), errno);
+                                printf("Continuing anyway\n");
+                        }
+                }
+        } else {
+                if(debug) {
+                        printf("detach successful\n");
+                }
+        }
 }

 int setup_libusb_access() {
@@ -152,10 +157,10 @@

      struct usb_bus *bus;
         struct usb_device *dev;
-       int i;
+        int i;

-       memset(handles, 0, sizeof(handles));
-       i = 0;
+        memset(handles, 0, sizeof(handles));
+        i = 0;
         for (bus = usb_busses; bus; bus = bus->next) {
         for (dev = bus->devices; dev; dev = dev->next) {
                         if (dev->descriptor.idVendor == VENDOR_ID &&
@@ -170,8 +175,8 @@
                                         continue;
                                 }
                                 handles[i++] = handle;
-                               if (i == MAX_DEV)
-                                       break;
+                                if (i == MAX_DEV)
+                                        break;
                         }
                 }
         }
@@ -314,6 +319,81 @@

       (void) signal(SIGINT, SIG_DFL);
 }
+
+void daemonize()
+{
+     pid_t pid;
+
+     pid = fork();
+     if (pid < 0) {
+        perror("failed fork()");
+        exit(1);
+     }
+
+     if (pid > 0) {
+        FILE *fp;
+        fp = fopen(PID_FILE, "w");
+        if (fp != NULL) {
+          fprintf(fp, "%d\n", pid);
+          fclose(fp);
+        }
+        exit(0);
+     }
+     seteuid(99);
+     setsid();
+}
+
+void http_handler(struct evhttp_request *req, void *arg)
+{
+        int i;
+        char msg[1024];
+
+        if (req->type != EVHTTP_REQ_GET) {
+                evhttp_send_error(req, HTTP_SERVUNAVAIL, "server unavail");
+                return;
+        }
+
+        struct evbuffer *ebuf;
+        ebuf = evbuffer_new();
+        if (ebuf == NULL) {
+                evhttp_send_error(req, HTTP_SERVUNAVAIL, "Cannot allocate buffer");
+                return;
+        }
+
+        for (i = 0; handles[i] != NULL && i < MAX_DEV; i++) {
+                float tempc;
+                struct tm *local;
+                time_t t;
+
+                memset(msg, 0, sizeof(msg));
+                control_transfer(handles[i], uTemperatura );
+                interrupt_read_temperatura(handles[i], &tempc);
+
+                t = time(NULL);
+                local = localtime(&t);
+
+                if (formato==2) {
+                        tempc = (9.0 / 5.0 * tempc + 32.0);
+                }
+                snprintf(msg, sizeof(msg), "%.2f\n%.2f\n%02d:%02d\npcsensor\n", tempc, tempc, local->tm_hour, local->tm_min);
+        }
+        char content_length[8];
+
+        snprintf(content_length, sizeof(content_length)-1, "%d", strlen(msg));
+
+        evhttp_add_header(req->output_headers, "Content-Type", "text/plain");
+        evhttp_add_header(req->output_headers, "Content-Length", content_length);
+        evbuffer_add(ebuf, msg, strlen(msg));
+        evhttp_send_reply(req, HTTP_OK, "", ebuf);
+        evbuffer_free(ebuf);
+}
+
+void signal_cb(int fd, short event, void *arg)
+{
+        struct event *signal = arg;
+        // printf("%s: got signal %d, ev=%0x\n", __func__, EVENT_SIGNAL(signal), event);
+        event_base_loopexit(signal->ev_base, 0);
+}

 int main( int argc, char **argv) {

@@ -323,7 +403,7 @@
      time_t t;

      memset(handles, 0, sizeof(handles));
-     while ((c = getopt (argc, argv, "mfcvhl::a:")) != -1)
+     while ((c = getopt (argc, argv, "mfcvhl::a:d")) != -1)
      switch (c)
        {
        case 'v':
@@ -359,19 +439,23 @@
          } else {
               break;
          }
+       case 'd':
+         use_daemon=1;
+         break;
        case '?':
        case 'h':
          printf("pcsensor version %s\n",VERSION);
-        printf("      Aviable options:\n");
-        printf("          -h help\n");
-        printf("          -v verbose\n");
-        printf("          -l[n] loop every 'n' seconds, default value is 5s\n");
-        printf("          -c output only in Celsius\n");
-        printf("          -f output only in Fahrenheit\n");
-        printf("          -a[n] increase or decrease temperature in 'n' degrees for device calibration\n");
-        printf("          -m output for mrtg integration\n");
+         printf("      Aviable options:\n");
+         printf("          -h help\n");
+         printf("          -v verbose\n");
+         printf("          -l[n] loop every 'n' seconds, default value is 5s\n");
+         printf("          -c output only in Celsius\n");
+         printf("          -f output only in Fahrenheit\n");
+         printf("          -a[n] increase or decrease temperature in 'n' degrees for device calibration\n");
+         printf("          -m output for mrtg integration\n");
+         printf("          -d daemon for mrtg integration\n");

-        exit(EXIT_FAILURE);
+         exit(EXIT_FAILURE);
        default:
          if (isprint (optopt))
            fprintf (stderr, "Unknown option `-%c'.\n", optopt);
@@ -394,67 +478,112 @@
      (void) signal(SIGINT, ex_program);

      for (i = 0; handles[i] != NULL && i < MAX_DEV; i++) {
-            ini_control_transfer(handles[i]);
+             ini_control_transfer(handles[i]);

-            control_transfer(handles[i], uTemperatura );
-            interrupt_read(handles[i]);
+             control_transfer(handles[i], uTemperatura );
+             interrupt_read(handles[i]);

-            control_transfer(handles[i], uIni1 );
-            interrupt_read(handles[i]);
+             control_transfer(handles[i], uIni1 );
+             interrupt_read(handles[i]);

-            control_transfer(handles[i], uIni2 );
-            interrupt_read(handles[i]);
-            interrupt_read(handles[i]);
-       }
+             control_transfer(handles[i], uIni2 );
+             interrupt_read(handles[i]);
+             interrupt_read(handles[i]);
+        }

-     do {
-       for (i = 0; handles[i] != NULL && i < MAX_DEV; i++) {
-           control_transfer(handles[i], uTemperatura );
-           interrupt_read_temperatura(handles[i], &tempc);
+     if (use_daemon) {
+        struct event_base *ebase;
+        struct evhttp *http;
+        struct event signal[2];

-           t = time(NULL);
-           local = localtime(&t);
+        daemonize();

-           if (mrtg) {
-              if (formato==2) {
-                  printf("%.2f\n", (9.0 / 5.0 * tempc + 32.0));
-                  printf("%.2f\n", (9.0 / 5.0 * tempc + 32.0));
-              } else {
-                  printf("%.2f\n", tempc);
-                  printf("%.2f\n", tempc);
-              }
-
-              printf("%02d:%02d\n",
-                          local->tm_hour,
-                          local->tm_min);
+        ebase = event_base_new();
+        if (ebase == NULL) {
+                perror("failed event_base_new()");
+                goto EXIT;
+        }
+        signal_set(&signal[0], SIGHUP, signal_cb, &signal[0]);
+        event_base_set(ebase, &signal[0]);
+        signal_add(&signal[0], 0);

-              printf("pcsensor\n");
-           } else {
-              printf("%04d/%02d/%02d %02d:%02d:%02d ",
-                          local->tm_year +1900,
-                          local->tm_mon + 1,
-                          local->tm_mday,
-                          local->tm_hour,
-                          local->tm_min,
-                          local->tm_sec);
+        signal_set(&signal[1], SIGTERM, signal_cb, &signal[1]);
+        event_base_set(ebase, &signal[1]);
+        signal_add(&signal[1], 0);

-              if (formato==2) {
-                  printf("Temperature %.2fF\n", (9.0 / 5.0 * tempc + 32.0));
-              } else if (formato==1) {
-                  printf("Temperature %.2fC\n", tempc);
-              } else {
-                  printf("Temperature %.2fF %.2fC\n", (9.0 / 5.0 * tempc + 32.0), tempc);
-              }
-           }
-
-           if (!bsalir)
-              sleep(seconds);
-       }
-     } while (!bsalir);
+        http = evhttp_new(ebase);
+        if (http == NULL) {
+                perror("failed evhttp_new()");
+                event_base_free(ebase);
+                goto EXIT;
+        }
+
+        if (evhttp_bind_socket(http, "127.0.0.1", PORT_NUM) < 0) {
+                perror("failed evhttp_bind_socket()");
+                evhttp_free(http);
+                event_base_free(ebase);
+                goto EXIT;
+        }
+
+        evhttp_set_gencb(http, http_handler, NULL);
+        event_base_dispatch(ebase);
+
+        evhttp_free(http);
+        event_base_free(ebase);
+        unlink(PID_FILE);
+     }
+     else {
+             do {
+                for (i = 0; handles[i] != NULL && i < MAX_DEV; i++) {
+                   control_transfer(handles[i], uTemperatura );
+                   interrupt_read_temperatura(handles[i], &tempc);
+
+                   t = time(NULL);
+                   local = localtime(&t);
+
+                   if (mrtg) {
+                      if (formato==2) {
+                          printf("%.2f\n", (9.0 / 5.0 * tempc + 32.0));
+                          printf("%.2f\n", (9.0 / 5.0 * tempc + 32.0));
+                      } else {
+                          printf("%.2f\n", tempc);
+                          printf("%.2f\n", tempc);
+                      }
+
+                      printf("%02d:%02d\n",
+                                  local->tm_hour,
+                                  local->tm_min);
+
+                      printf("pcsensor\n");
+                   } else {
+                      printf("%04d/%02d/%02d %02d:%02d:%02d ",
+                                  local->tm_year +1900,
+                                  local->tm_mon + 1,
+                                  local->tm_mday,
+                                  local->tm_hour,
+                                  local->tm_min,
+                                  local->tm_sec);
+
+                      if (formato==2) {
+                          printf("Temperature %.2fF\n", (9.0 / 5.0 * tempc + 32.0));
+                      } else if (formato==1) {
+                          printf("Temperature %.2fC\n", tempc);
+                      } else {
+                          printf("Temperature %.2fF %.2fC\n", (9.0 / 5.0 * tempc + 32.0), tempc);
+                      }
+                   }
+
+                   if (!bsalir)
+                      sleep(seconds);
+                }
+             } while (!bsalir);
+    }

+EXIT:
     for (i = 0; handles[i] != NULL && i < MAX_DEV; i++) {
      usb_release_interface(handles[i], INTERFACE1);
      usb_release_interface(handles[i], INTERFACE2);
+     usb_reset(handles[i]);

      usb_close(handles[i]);
     }
USB温度計を複数持っていないので試していませんが、恐らく複数機器ではうまく動かないかと…。
初めてlibeventを使ってみましたが、面白いですね。