それマグで!

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

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

dateコマンドでiso/rfc日付時刻タイムゾーンをぱぱっとフォーマットして作る

date コマンド便利だけど、フォーマット指定がめんどくさい

takuya@:~$ date --date "2017-03-22 + 90days"  +"%Y-%m-%d %H:%M:%S"
2017-06-20 00:00:00

ああ、めんどくさいよ%Y-%m-%d 、記号が多くてめんどくさい。

よくつかう日付は、オプションが用意されている

$ date  -I
2017-03-22

よく使う日付のフォーマットもショートカットが用意されている。

$ date  +'%F'
2017-03-22

$ date  +'%T'
15:31:54

$ date  +'%F %T'
2017-03-22 15:30:49

時刻を入れるなら、私達が日常使いではコレで十分だが、UNIX時刻でいくならタイムゾーンがないとおかしくなるので、DBなどはタイムゾーンを入れたほうが無難。

-I は更に使える

さらによく使うタイムスタンプにもショートカットが用意されている

$ date -Iseconds
2017-03-21T14:42:20+0900

スペースは駄目だよ。date -I seconds は無効

または、次のようにしてもほぼ同じである。

date --rfc-3339=seconds
2017-03-21 14:43:47+09:00

rfc 3339 のフォーマットの例

rfc 3339 は私達が見ても結構わかりやすい。rfc 3339の場合はスペースをいれる。

ISOとRFCでオプションの渡し方が違うので注意

$date -R date
2017-03-22

$date -R seconds
2017-03-22 15:44:26+09:00

$date -R ns
2017-03-22 15:44:26.468367829+09:00

ロングオプションなら

$date --rfc-3339=date
2017-03-22

$date --rfc-3339=seconds
2017-03-22 15:38:04+09:00

$date --rfc-3339=ns
2017-03-22 15:38:04.553941046+09:00

-I オプションの例

$ date -Idate
2017-03-21

$ date -Ihours
2017-03-21T14+0900

$ date -Iseconds
2017-03-21T14:47:08+0900

$ date -Ins
2017-03-21T14:47:11,948607361+0900

ロングオプションなら次の通り

$ date --iso-8601=date
2017-03-22

$ date --iso-8601=hours
2017-03-22T15+0900

$ date --iso-8601=minutes
2017-03-22T15:36+0900

$ date --iso-8601=seconds
2017-03-22T15:36:17+0900

$ date --iso-8601=ns
2017-03-22T15:36:17,095634462+0900

ナノ秒を含めたタイムスタンプを作るなら

$date --rfc-3339=ns
2017-03-21 14:46:40.646812475+09:00
$ date --iso-8601=ns
2017-03-22T15:36:17,095634462+0900

などとすると便利。

その他のDateコマンドの使い方はこちら

date コマンドで「昨日」などの日付を計算するとき

date コマンドで相対日付で目的の日をパパっと計算する。 - それマグで!

date コマンドのオプションについて

dateコマンドを見なおしてみる。dateコマンドオプションは使える - それマグで!

利用したdateコマンドについて

dateコマンドはいくつか実装があるので、若干の差異が有ります。利用する前にコマンドがGPL/BSDで違いがある。

takuya@:~$ date --version
date (GNU coreutils) 8.23
Copyright (C) 2014 Free Software Foundation, Inc.
ライセンス GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

作者 David MacKenzie。
takuya@:~$

bashで複数行の文字列の代入と、ヒアドキュメントを使わずに変数に格納する方法

bash で複数行の文字列を変数に格納する方法

「改行が入らない」ではなく、「改行が見えない」のです。入らないと思って改行を調べてしまう事例が多いです。注意しましょう

その1:一番シンプルなのはそのまま

str="
ここは
サンプル
の複数行文字列
です。
"
echo "$str"

echo $str ではなく、 echo "$str" なのがちょっと味噌ですね

その2:ヒアドキュメント編

一般的なのは、ヒアドキュメントをつかって定義する方法。インターネットを探すとコッチが多いんだけど、なぜわざわざcatを呼び出す無駄なことをしているのか理解できない。dash/bash/zsh でもサンプル1で動くのに。

str=$(cat <<EOS
ここは
サンプル
の複数行文字列
です。
EOS
)
echo "$str"

なぜ "$str" とクオートが必要なのか?

クオートが必要なのは、改行を含めた文字列が「変数に格納」されていて、出力時に「分割」されるからとかんがえられる

次のように定義したとき

str="a
b
c

出力前に、先に変数展開がされます。

echo $str

echo a 
b 
c

となり、IFSで分割されて

echo a b c 

となってると考えられます。

これを確かめるために、IFSをオフにして実験してみます。

実験:IFS をオフにして "$str" を不要にした。

str="
ここは
サンプル
の複数行文字列
です。
"
IFS=''
echo $str

ifs でbash の区切り文字を変えることがポイント

実行結果

takuya@2017-03-21#7$ bash   sample.sh

ここは
サンプル
の複数行文字列
です。

此の実験結果から分かる通り、変数には改行付きで格納されてることがわかります。

bash はコマンドを1行読み込むと、変数展開をします。展開後文字列にIFSを適用し、実行する。そのため一般的に"$str" とダブルクオートで囲む解決策が頻用されると考えれます。

その3:ヒアストリング

コマンドにヒアストリングを渡すことが出来ます。

cat <<< "here string"

これを応用して、次のように書けます。

str=$(cat <<<"
ここは
サンプル
の複数行文字列
です。
")
echo "$str"

インデントもしたい

ソースコードの可読性向上にインデントを企図してるときは、ヒアドキュメントが有能です。

cat <<-EOF
    ここは
            サンプル
            の複数行文字列
            です。
    EOF

-EOF と、ダッシュをつけることで先頭のタブ文字を捨ててくれます。タブ文字だけですよ?expandtab しててスペースはに置換してたら動きません。

繰り返しになりますが、インデントに使えるのはTAB文字だけですよ?

bash 4 からの新機能 mapfileを使う場合

StringIO#lines 的なことで解決する場合

mapfile str <<EOF
ここは
サンプル
の複数行文字列
です。
EOF

echo "${str[@]}"

MapFileは、ファイル(この場合はstdinのfd)を複数行に分割し、配列に格納してくれます。これを使うともっと楽にできます。

注意 パイプはできない

次のパイプはエラーになります。

echo "aa
bbb" | cat 

パイプに書くときは一旦変数に入れます。

str="aaa
bbb
"
echo $str | cat 

参考資料

2021-02-16

ifs で検索されないので ifsを明示的に指定した

2022-04-18

パイプについて追記。

sendmail でコマンドからメールを送信する。

メール通知のテストに使う。

postfix や exim4 のメールサーバーの設定をしていて、テスト・メールを送信したいときに、手作業でsendmail コマンドを送るのは、ちょっと面倒なのと、どのメールが未到達で、どのメールが到達したか区別するのが大変なのでスクリプト書きました。

サンプルスクリプト

サンプルスクリプトでは、連番を入れて、どのメールか識別できるようにしてて、日付と時刻をいれてそちらでも識別できるようにしておいた。

send_mail.sample.sh

#!/usr/bin/env bash


function counter() {
  if [[ !  -f /tmp/count ]] ;  then
    touch /tmp/count
    echo 1 > /tmp/count
  fi
  num=$(tail -n 1 /tmp/count)
  echo $(( num+1 )) > /tmp/count
  echo $num
}



ifs=$IFS
IFS=''
text="\
To:takuya@example.com
Subject: Hello, From $(hostname)

Hi, can you see me ?

num:$(counter)


---
takuya
 $(date --rfc-3339=seconds)
"


echo  $text
echo -e $text | sendmail -i -t

2019-12-13

タイプミスを修正

sudoersの設定を分割する /etc/sudoers.d を使おう

/etc/sudoers.d で分割できる。

/etc/sudoers.d を使うと、必要なものを覚えやすいファイル名で書いておいてバージョン管理することが出来る。

編集方法

編集方法がめんどくさい。分割して管理できるが、各ファイルを編集するのにvisudoを使う必要があり、これがめんどくさい。

visudo -f /etc/sudoers.d/my_conf

ただしく読み込まれたか

もし直接パスを指定して、編集し、手作業で読み込んだら、一旦パースしてテストするしか無いかもね。

sudo vim  /etc/sudoers.d/my_conf
visudo -x - | jq  

読み込む仕組み

どのタイミングで読み込むのかは /etc/sudoers を見ればわかります。

/etc/sudoers

sudoersには次のように書いてあります。最終行の #includedir に注目。 なのですべての一番最後に読み込む様になっています。

#
# This file MUST be edited with the 'visudo' command as root.
#
# Please consider adding local content in /etc/sudoers.d/ instead of
# directly modifying this file.
#
# Cmnd alias specification

# User privilege specification
root    ALL=(ALL:ALL) ALL

# Allow members of group sudo to execute any command
%sudo   ALL=(ALL:ALL) ALL
takuya  ALL=(ALL) NOPASSWD: ALL

# See sudoers(5) for more information on "#include" directives:

#includedir /etc/sudoers.d

#includedir はコメントじゃないの?

コメントではありません。

#includedir /etc/sudoers.d  # ←これは正しい
includedir /etc/sudoers.d  # ←これは間違い

一見すると、コメントに見えて、ハッシュ(#)を消そうと思うかもしれませんが。これは明確にディレクティブですね。*1

sudoersのファイルを見ていると、コメントでは、 '# ' と空白文字を入れて区別しているようですね。

# comment 
#include

参考資料

https://wiki.debian.org/sudo

nmtui-connectで無線LAN接続して、NetworkManagerと仲良くなる

Pizeroで無線LANを接続する

Wi−Fiにぱぱっとつなぐには、アレコレとコマンドを弄るより network-manager を使うのも楽ちんだと思う。

sudo nmtui-connect

画面が出てくる

f:id:takuya_1st:20170318015150p:plain

ssh 経由でも安心

細かいコマンドや /etc/network を使わなくていいので楽ちんですね。

コマンドに慣れてない raspberry pi ユーザには良いですね。

POSIX の メッセージ・キューを作成する(C言語)

POSIXのメッセージ・キューをC言語で扱う

コンパイルには gcc -lrt を使う。

まずは送信するところから。

q_push.c

#include <mqueue.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(){
  int cnt;
  int ret;
  char *str;
  void *pt;
  mqd_t q;

  q = mq_open("/sample", O_WRONLY | O_CREAT );
  while(1){
    sprintf(str ,"%d", cnt++);
    printf("%s\n",str);

    pt = calloc(strlen(str) + 1,sizeof(char));
    strcpy( pt, str );
    ret = mq_send( q, pt , strlen(pt) , 0    );

    if ( ret > 0  ) {
      perror("push error");
      return;
    }
    sleep(1);
  }
}

受信する所。

#include <mqueue.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(){
  mqd_t q;
  struct mq_attr attr;
  void *buff;
  ssize_t n;
  char *str;

  q = mq_open("/sample", O_RDONLY | O_CREAT );

  while(1){
    mq_getattr( q ,&attr );
    buff = malloc(attr.mq_msgsize);
    //
    n = mq_receive( q, buff, attr.mq_msgsize,NULL);


    // printf("read: %d bytes \n",n);
    printf("%s\n", (char*)buff);
    usleep(1000*100);
    free(buff);
  }
}

コンパイル

$ gcc q_push.c -lrt -o q_push
$ gcc q_pop.c -lrt -o q_pop

python とやり取りしてみる

前に作った、Pythonposix_ipc を使ってキューを送信して受信してみる。

コンパイルしたバイナリで送信

takuya@:mq$ gcc q_push.c -lrt -o q_push
takuya@:mq$ ./q_push
1
2
3
4
5
6
7
8

python で受信

takuya@:mq$ python q_pop.py
113
114
115
116
117
118
119

異なる言語でデータ交換

JSON でデータのやり取りも良いんだけど、ファイルロックを考慮したり、名前付きパイプでもいいんだけど、複数ワーカーをぱぱっと動かせる感じなのは、とてもいいよね。いちいちSQLを持ち出してキュー構造を作るのは不便だし。

ワーカープログラムが死んでもキューは残ってるし。POSIXのファイルベースなAPIはホントわかりやすい。

参考資料

POSIX の メッセージ・キューを確認する。コマンドで。

POSIX message queue を確認する。

Linux なら /dev に出現する。

takuya@:~$ ls /dev/mqueue/

キューがあるとき

/sample という名前でキューを作ったのでキューを確認することが出来る。

takuya@:mq$ ls /dev/mqueue/
sample

キューの状態をサクッと確認する。

cat すれば中身が見られる。

takuya@:mq$ sudo cat  /dev/mqueue/sample
QSIZE:30         NOTIFY:0     SIGNO:0     NOTIFY_PID:0

キューを削除する

/dev/ から消せば、簡単にゴミを消せる。

参考資料

http://unix.stackexchange.com/questions/70837/linux-command-to-check-posix-message-queue

PythonでPOSIXのMessageQueue を使う。

POSIX の メッセージ・キューを使う。

プロセス間通信などと呼ばれる。POSIXのキューを使ってみる。

プロセス間通信 - Wikipedia

プロセスとプロセスのデータのやり取りにメッセージ・キューを使うことで幾分楽になる。マルチスレッド以前に複数プロセスでデータをやり取りすると複数分散して処理が楽になることもある。

動作例

youtu.be

ポイント

  • 送信側はドンドン送信できる。
  • 受信側は逐次読み込んで取り出せる。
  • データが空っぽなら、受信側はブロックされる。

受信側は、なにもしなくても受信不可ならブロックされるので楽ちん。感覚的には、空っぽのパイプと同じですね。

インストール

python で使うには posix_ipcを使う

pip install posix_ipc

Queueにメッセージを追加

#!/usr/bin/env python

import json
import posix_ipc

def main () :
  mq = posix_ipc.MessageQueue("/my_q01", posix_ipc.O_CREAT )
   #
  cnt = 0
  #
  while True:
    cnt = cnt + 1
    obj = { "counter": cnt }
    mq.send( json.dumps( obj ) )
    print("pushed: %d" % cnt )
    time.sleep(1)


if __name__ == "__main__" :
  main()

Queue からメッセージを取り出す

#!/usr/bin/env python

import json
import posix_ipc

def main () :
  mq = posix_ipc.MessageQueue("/my_q01", posix_ipc.O_CREAT )
  # 
  while True:
    data = mq.receive()
    print( data[0] )
    time.sleep(1)

if __name__ == "__main__" :
  main()

Queue を削除する

#!/usr/bin/env python
import posix_ipc

mq = posix_ipc.MessageQueue("/my_q01")
mq.unlink()

コマンドからも確認できます。

takuya-1st.hatenablog.jp

参考資料

Semanchuk.com - POSIX IPC for Python - Semaphores, Shared Memory and Message Queues

シェルで指定文字に文字列を分割して結合する。

文字列をシェルで、指定文字数に分割して結合するには

選択肢になるコマンドは、分割・結合でそれぞれ次の通り

  • 分割
  • 結合
    • paste
    • tr

などが使えることがわかった。

分割の例
fold -w2
grep -E -o '.{2}'

文字列を指定文字に分割する。fold コマンド

fold コマンドが便利。ruby であればsplit/grep などで出来る。

takuya@~$ echo $(openssl rand -hex 6) | fold -w1
0
3
3
9
5
2
f
0
7
3
1
e

fold -w コマンドは、指定バイトで切り出す。

takuya@Desktop$ echo $(openssl rand -hex 4) | fold -w4
d0ac
01f3

指定バイトなので日本語のようなユニコード文字列は3バイトで切り出す。

takuya@~$ echo ああああ | fold -w 3
あ
あ
あ
あ

grep でもなんとか出来る

 echo $(openssl rand -hex 4) | /bin/grep -i -E  -o  '.{2}'
7f
1c
10
ec

おお。grep 万能説!

さて、分割した文字列を結合するには

join かと思いきや、paste を使うと便利だった。

paste -sd ':'

paste で複数行を結合

takuya@Desktop$ cat - | paste -sd ","
a
b
c
a,b,c

使いみち

xdd/hexdump/od 何かと組み合わせると便利かも

たとえば、画像のdumpをちょっと見る。

takuya@Desktop$ xxd -p  -l 100 logo.png  | ~/.bin/grep -i -E  -o  '.{2}' | paste -sd ","
89,50,4e,47,0d,0a,1a,0a,00,00,00,0d,49,48,44,52,00,00,00,60,00,00,00,2d,08,03,00,00,00,66,80,9e,b8,00,00,00,19,74,45,58,74,53,6f,66,74,77,61,72,65,00,41,64,6f,62,65,20,49,6d,61,67,65,52,65,61,64,79,71,c9,65,3c,00,00,03,12,69,54,58,74,58,4d,4c,3a,63,6f,6d,2e,61,64,6f,62,65,2e,78,6d,70,00,00,00,00,00

例えば、MACアドレスっぽいものを作る

takuya@~$ echo $(openssl rand -hex 6) | fold -w2 | paste -sd ':'
7e:25:cb:0b:70:ef

参考資料

http://stackoverflow.com/questions/15553493/how-to-print-only-the-hex-values-from-hexdump-without-line-numbers-or-ascii-tabl

http://stackoverflow.com/questions/2764051/how-to-join-multiple-lines-of-file-names-into-one-with-custom-delimiter

http://stackoverflow.com/questions/7578930/bash-split-string-into-character-array

Debian/ubuntu のapt自動アップデートのUnattendedUpgradesを設定する

Debian の apt 更新がめんどくさいのでなんとかする。

昔は cron-apt でやってたけど、今時はどうするのかなーと思って調べてたらUnattendedUpgradesがあったのでコレを設定することに。

Debian Wikiに従ってインストール

sudo apt install unattended-upgrades apt-listchanges

apt-listchanges は無くても良さそう。apt-listchangesはメールで更新の通知をするツール。

アップデートを有効にする

sudo dpkg-reconfigure -plow unattended-upgrades

アップグレードを有効にする。

sudo -e /etc/apt/apt.conf.d/20auto-upgrades

定期的にアップグレードとパッケージリストの更新を有効にする。

APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";

自動更新の確認

sudo unattended-upgrades --dry-run

dry-run で自動更新の設定と動作をチェックできる。

更新するパッケージの指定と除外はここで設定できる

takuya@orangepizero:~$ cat  /etc/apt/apt.conf.d/50unattended-upgrades | head
// Unattended-Upgrade::Origins-Pattern controls which packages are
// upgraded.
//
// Lines below have the format format is "keyword=value,...".  A
// package will be upgraded only if the values in its metadata match
// all the supplied keywords in a line.  (In other words, omitted
// keywords are wild cards.) The keywords originate from the Release
// file, but several aliases are accepted.  The accepted keywords are:
//   a,archive,suite (eg, "stable")
//   c,component     (eg, "main", "crontrib", "non-free")

設定が不安なら dpkg-recongfigure でも出来る

sudo dpkg-reconfigure -plow unattended-upgrades

no を選ぶと、/etc などのファイル更新でどっちを優先するか決められる。

設定ファイル更新されるの嫌だから、ローカル優先しておいた。

もし更新するパッケージを指定するなら

以下のファイルにアレコレ書いて、更新するパッケージを指定する。 デフォルトで大丈夫だと思うのですが。更新したら動かなくなる野良ビルドドライバとか入ってたら linux-header とかカーネルの更新は避けたいところだったりする。

sudo -e /etc/apt/apt.conf.d/50unattended-upgrades

たとえば、gitlab-ce の パッケージを指定した例。

gitlab-ce をapt に行を追加していて、ominibus パッケージとして使っている場合

// Automatically upgrade packages from these (origin:archive) pairs
Unattended-Upgrade::Allowed-Origins {
    "${distro_id}:${distro_codename}-security";
    "${distro_id}:${distro_codename}-updates";
    // この行を追加
    "o=*packages.gitlab.com/gitlab/gitlab-ce,codename=${distro_codename}";

このようにすると apt に追加した gitlab のパッケージが自動更新の対象になる。

もし自身でapt に追加していたらこのようにする。通常は debian の updates / security を自動インストールしてくれる。

除外したい場合はブラックリストにパッケージ名を書く

アップデートの実行ログ

アップデートの実行ログは /var/log/unattended-upgrades/unattended-upgrades.log に書かれる。

また、dpkg のログが /var/log/dpkg.log に記載されるので、自動更新のヒストリ(履歴・実行記録)をたまに見てやるといい。

参考資料

2021-11-23

ログについて記述を追加

web.archive.org のURLを考える

web.archive.org のURLはどうなってるのかを見てみる

web.archive.org のインターネットアーカイブがとても便利。

著作権云々だとかで魚拓サービスはとても使いにくいので、アーカイブから取るのが良い。

基本的なURL

基本的はURLは日付+取得したいURLになってる

たとえばヤフオクの 20170225084900 のデータを見たい場合

http://web.archive.org/web/20170225084900/http://auctions.yahoo.co.jp/

日付の指定を省略して、どの日付のアーカイブがあるか調べる

http://web.archive.org/web/*/http://auctions.yahoo.co.jp/
http://web.archive.org/web/http://auctions.yahoo.co.jp/ # または

特定月の最新版を取りたい

日付を省略すれば、その月の最新版の付近が取れる。

http://web.archive.org/web/201303/http://auctions.yahoo.co.jp/

特定年の最新版を取りたい

月日を省略すれば、その年の最新版の付近が取れる。

http://web.archive.org/web/2013/http://auctions.yahoo.co.jp/

取り敢えずこの辺ください

存在しなくてもその前後の日付で、アーカイブが存在する一番近い日付にリダイレクトしてくれる。

## あるかないか知らないけど、20130315 のデータくださいってリクエスト
http://web.archive.org/web/20130315/http://auctions.yahoo.co.jp/

画像があるの?

画像も同じようにURLを指定すれば取れると思う。

http://web.archive.org/web/20040610050213/http://example.com/sample.jpg

ただし、ページに埋まってる画像は次のようなURLで取得されることになる。

## 20040610050213のhttp://example.comが存在する場合で、その中に埋まってる画像のURL
http://web.archive.org/web/20040610050213_im/http://example.com/sample.jpg

なんなら、FLVとかもあったりする。

InternetArchive は大切にしたいプロジェクト。

InternetArchiveのwayback machine はいざという時に貴重。

WebArchiveプロジェクトは、ストレージや転送量が大変なプロジェクトだとおもう。でもとても救われることが多い。

なので少しばかり応援してさせていただいた。

バックエンドへのssh のユーザー名で接続先を振り分ける(SSHプロキシ的な踏み台サーバーの作成

ssh でフロントとバックエンドへの接続を簡単にしたい。

ssh でIP浪費するのもめんどくさい話だし。

バックエンドへの接続をもう少し楽にできたら良いんだけど。

ユーザー名でバックエンドへの接続を切り分ける

発想としていくつかある。

フロント┬────── Backend 0
    ├────── Backend 1
    └────── Backend 2

フロント側にアレコレする

フロントにSSHログインする場合:踏み台経由の多段ログイン

  • authorized_keys をつかう
  • /etc/sshd/sshd_config を使う
  • LDAPなどを使う

sshd_config を使う場合

sshd_config の Match を使ってフォワードする。

Match user takuya
    ForceCommand ssh -t backend-host

authorized_keys を使う場合

authorized_keys に登録する公開鍵に次のように書く。

Command ssh -t backend-host ssh-rsa AAAAAAAAAAAA

LDAPを使う。

LDAPの認証エントリに sshd_config/authorized_keys と同等のこと記述したらいい。

2回の認証を省略したい。

そもそも認証状態を転送できないのか? LDAPを使えばワンちゃん。Backend側がフロント側のPAMに聞きに行けばいいんだけど、依存関係が増えてもうめんどくさい。

ポート転送をかける:転送分だけポートが必要

  • フロント:2201 を Backend01 22
  • フロント:2202 を Backend02 22

すぐに思いつくのはポートフォワード。でもVhostに振り分けたいって要望がある時点で、ポートを開けたりIPを触りたくない前提があるわけだから、此の解決策は手軽なゆえに、多くの人は規制により使えないことが多いと思う。

NFSでマウント:ファイルを編集するだけなら

  1. NFSでフロント側からBackendをつなぐ
  2. Backendにはログインしない。
  3. 主にファイル編集だけなら有用
  4. ChrootDirectory と組み合わせて戦える
  5. 同一ホスト内のLXCならなおさら有効?

NFS マウント/Chroot 

BackendがLXCで同一ホスト内なら、SSHログイン後の Chroot Directory や Home をアレコレすることでなんとかなる。

実はこれは意外とオススメ。NFSでマウントして触らせておくのはバックエンドサーバーのプロセスをそのまま触られないので結構バランスする。

Chroot設定。

Match Group sftpuser
  ChrootDirectory /var/backend/01

NFS マウント

sudo mount Backend01:/var/www /var/backend/01

もしくは、これらのマウントを sshfs で起動するスクリプトをログイン(bashrc/sshd_config/authorize_keys)に仕込めばいい。

リバースプロキシ

sshpiperd が使えるらしい

クライアント側で操作する。

クライアント側で -t オプションを付ける

クライアント側でバックエンドへの転送を、フロント側のSSHサーバーに指定をする。

ssh front-host -t ssh  backend-host

クライアント側のconfig で -W オプションをつける

.ssh/config

Host backend
  Hostname backend
  Port 22
  ProxyCommand ssh -W %h:%p frontend

ポート転送を掛ける

ポート転送をしてフロント側とバックエンドへの接続用に、フロント側にポートを開ける。

などで指定すればいけそう。

その他使える設定ファイル。

  • ForceCommand
  • ~/.ssh/environment
  •  ~/.ssh/rc
  • /etc/ssh/sshrc

その他の解決方法

今回はネットワークの3レイヤあたりは余り考慮せず7レイヤをメインで考慮したのでこれらの解決策は余り検討しなかった。

  • SSL で CONNECT でSocksプロキシを使う
  • stunnel を使う
  • openVPNを使う
  • ssh/tun-tap を使う。

SNI出来ないの?

OpenSSHの此のスレッドのこの辺り(Name based SSH proxy )で議論されてる。

I hope we do not introduce a cleartext SNI into the SSH protocol. This leaks far too much sensitive metadata for passive monitors. TLS has cleartext SNI, and it is quite difficult to figure out how to protect it from passive monitors

やろうと思えば出来るけど、SSHプロトコルにないもんを作るのはSSHプロトコルの定義からの見直しになりそうだし、そもそもSNIはクリアテキストになってんじゃないの?そんなの実装したくねーわって丁寧な言葉で書かれてる。たぶんコレが全て答え。そのため、どうしてもやりたいなら stunnel を使う必要がある。

参考資料

https://www.infiniteloop.co.jp/blog/2016/05/user-namebase-ssh-reverse-proxy/

http://hogem.hatenablog.com/entry/2015/06/08/233000

https://github.com/tg123/sshpiper

http://yudai.arielworks.com/memo/2010/12/15/225715

http://tsuchinoko.dmmlabs.com/?p=1387

Name based SSH proxy

php で終了しないプログラムを書く

phpホスティングなどのリソースを上手に扱う関係上、実行時間が限られてる。

なので、終了しない無限ループをキックしようと思うとこんな感じなる。

#!/usr/bin/env php
<?php

set_time_limit(0); # forever program!

どこで使うかというと、WEBから起動する場合に、apacheが起動させちゃうもので、ずっと動画配信したいものなど。

動画の音声・動画ストリーム情報をJSONで受け取るffprobe json オプション

事の発端:ファイル名変換をミスった

この動画なんだっけ?wmv?avi? mpegts ?

ファイル名の変換をミスった。拡張子がトンだ。もうしにたい。仕方ないのでコーデック情報見ながら各種データの拡張子を探ることに

動画のストリーム情報の解析がめんどくさい。

ffmpeg で動画の情報を見るには ffprobe を使う。

ffprobe -i movie.mov 

まぁこれだけでも見るだけなら 標準エラーにffmpeg と同じ情報が出てきてログ見たらわかるんだけど。もっと手軽にほしいところだけ見たいな的な。

ネット見てたら、ffmpeg/ffprobe のエラーストリームへログをgrep してたりしてたいへんだ。

JSON で取り出す

-print_format json

ffprobe には show_format があってJSONでストリーム情報を取り出せる。

ffprobe -i movie.mov -show_streams -print_format json

これで、ストリーム情報を取り出せる。

うるさいログを黙らせる。

サポートしてるフォーマットとかの情報がうるさいので黙らせる。-loglevel quietで指定する。

ffprobe -i movie.mov -loglevel quiet -show_streams -print_format json

-print_format json jq を組み合わせる。

ここまで来たら後は楽ちん。 jq と組合あせて戦う。

takuya@Desktop$ ffprobe  -loglevel quiet -show_streams -print_format json "$f" | jq '.streams[] | {"index":.index,"codec_type":.codec_type,"codec_name":.codec_name}  '  -r
{
  "index": 0,
  "codec_type": "audio",
  "codec_name": "aac"
}
{
  "index": 1,
  "codec_type": "video",
  "codec_name": "h264"
}
{
  "index": 2,
  "codec_type": "data",
  "codec_name": null
}
{
  "index": 3,
  "codec_type": "data",
  "codec_name": null
}
takuya@Desktop$

動画のコーデック情報を取り出す。

ffprobe  -loglevel quiet -show_streams -print_format json "$i" | jq .streams[0].codec_name  -r

最後にこれを関数にする。

function ffmpeg_show_video_codec()
    f=$1
    ffprobe  -loglevel quiet -show_streams -print_format json "$f" | jq .streams[0].codec_name  -r
}

コレで行けそうですね。

追記1 XML + XPATHの方が簡単かも

json と jq はどうしても煩雑なので、 XML+XPATH のほうが楽だった。

XML で出力する -print_format xml

XML で出力してXPATHの方が // の省略記法があるので圧倒的に楽

ffprobe /Users/takuya/.Desktops/2017-02-10/IMG_4046.MOV-1.mov -loglevel quiet -show_streams -print_format xml | xpath '//stream/@codec_name'

やっぱりJSONめんどくさいんだよ。読みやすいけど、コードが煩雑。廃れてほしい。*1

追記 XML / JSON 以外のフォーマット

などがある

追記 他に見れるもの

formats 以外に見れるものもある。

ffprobe の man を眺めているとアレコレあるみたい

       -show_data
       -show_data_hash algorithm
       -show_error
       -show_format
       -show_format_entry name
       -show_entries section_entries
       -show_packets
       -show_frames
       -show_streams
       -show_programs
       -show_chapters

時間無くて調べきれない。-show_programs 番組のprogram_id などが見れる TS のストリーム情報などはコッチ見た方がいいのかな。

追記

動画の拡張子を wmv / mp4 にわける。

#!/usr/bin/env bash
for i in ./movies/* ; do
  name=$(ffprobe  -loglevel quiet -show_streams -print_format json "$i" | jq .streams[0].codec_name  -r)
  echo $name $i
  case "$name" in
    wmv*)
      :
      echo mv "$i" "${i/.*/.wmv}"
      # mv "$i" "${i/.*/.wmv}"
      ;;
    h264|aac|mpeg4)
      :
      echo mv "$i" "${i/.*/mp4}"
      ;;
  esac

done


覚えにくいので、bash alias や function にしてコマンドにした。

alias ffmpeg_info_json ='$ ffprobe -loglevel quiet -show_streams  -print_format json -i '

*1: 配列とオブジェクトがどちらも入るのがより煩雑さを加速してる感じがある。 XMLの子ノードと属性問題はあるけど、XPATHではあまり区別なく使いやすい。

qpdfでPDFにパスワードをつけて、編集不可にしたり、閲覧不可にする。

qpdf でパスワードをつける。

qpdf があればコピーを不可、編集不可、閲覧不可などのパスワードをつけることが出来る

使うオプション --encrypt

qpdf  --encrypt パスワード1 パスワード2 キー フラグ -- in.pdf out.pdf

最後にいれる -- はオプションフラグの識別のために必要

-- in.pdf out.pdf

パスワードを付ける例

USER_PASSWD='ユーザーパスワード'
OWNER_PASSWD='オーナパスワード'
qpdf  --encrypt $USER_PASSWD $OWNER_PASSWD 40 --print=n --modify=n --extract=n --annotate=n -- 2.pdf 3.pdf

Mac の場合アイコンも変わる。

f:id:takuya_1st:20170313190523p:plain:w120

プレビューでも開けなくなってる。

f:id:takuya_1st:20170313190526p:plain:w300

オーナパスワードだけ付ける場合

誰でも閲覧は出来るが、編集不可で印刷不可にする場合

ユーザーパスワードには「空文字」が許可されるので、空文字を入れることで、オーナパスワードだけをつけることが出来る。

OWNER_PASSWD='オーナパスワード'
qpdf  --encrypt '' $OWNER_PASSWD  40 --print=n --modify=n --extract=n --annotate=n -- 2.pdf 3.pdf

オーナパスワードで印刷不可にした場合

印刷するには、このパスワードダイアログ出てくる。

f:id:takuya_1st:20170313190243p:plain:w200

でもqpdf で閲覧状態のPDFをdecrypt して解除できちゃうんだけどね。知らない人にはちょっとプレッシャーをかけることが出来る。

参考資料

http://qpdf.sourceforge.net/files/qpdf-manual.html