20200528のLinuxに関する記事は7件です。

Developing Cipher Algorithms(1/2)

https://www.kernel.org/doc/html/latest/crypto/devel-algos.html

Docs » Linux Kernel Crypto API » Developing Cipher Algorithms

Developing Cipher Algorithms

Registering And Unregistering Transformation

There are three distinct types of registration functions in the Crypto API. One is used to register a generic cryptographic transformation, while the other two are specific to HASH transformations and COMPRESSion. We will discuss the latter two in a separate chapter, here we will only look at the generic ones.

Crypt APIには、3つの異なる種類の登録関数があります。1つは、一般的な cryptographic transformationを登録するもので、残り二つは、HASH trasnfomationとCOMPRESSionです。最後の2つについては別の機会に議論するとして、ここでは本来の一般的な最初についてだけ見ていきます。

Before discussing the register functions, the data structure to be filled with each, struct crypto_alg, must be considered – see below for a description of this data structure.

登録関数について議論する前に、それぞれ埋めるべきデータ構造体、struct crypt_alg について考えなければなりません。このデータ構造の詳細は以下を確認してください。

The generic registration functions can be found in include/linux/crypto.h and their definition can be seen below. The former function registers a single transformation, while the latter works on an array of transformation descriptions. The latter is useful when registering transformations in bulk, for example when a driver implements multiple transformations.

一般的な登録関数は、include/linux/crypto.hで見つけることができ、それらの定義は以下で見ることができます。former functionはsingle transformationを登録します、後者はtransfomation descriptorsの配列を登録します。。後者は、transformationをまとめて登録するときに役に立ちます。例えば、ドライバーには複数のtransfomationが実装されている場合です。

int crypto_register_alg(struct crypto_alg *alg);
int crypto_register_algs(struct crypto_alg *algs, int count);

The counterparts to those functions are listed below.

これらの関数に対応するものは、以下になります。

void crypto_unregister_alg(struct crypto_alg *alg);
void crypto_unregister_algs(struct crypto_alg *algs, int count);

The registration functions return 0 on success, or a negative errno value on failure. crypto_register_algs() succeeds only if it successfully registered all the given algorithms; if it fails partway through, then any changes are rolled back.

登録関数は成功したら0を、失敗したら負のerrno valueを返します。crypto_register_algs()では、全ての与えられたアルゴリズムに対して、登録が成功した場合にのみ成功となります。途中で失敗した場合には、変更はすべてロールバックされます。

The unregistration functions always succeed, so they don’t have a return value. Don’t try to unregister algorithms that aren’t currently registered.

登録解除関数は常に成功し、戻り値はありません。現在登録していないアルゴリズムに対して、登録解除を試みないでください。

Single-Block Symmetric Ciphers [CIPHER]

Example of transformations: aes, serpent, …

This section describes the simplest of all transformation implementations, that being the CIPHER type used for symmetric ciphers. The CIPHER type is used for transformations which operate on exactly one block at a time and there are no dependencies between blocks at all.

このセクションでは、全てのtransfomation実装のなかでもっとも単純である、symmtric ciphersを用いたCIPHER typeについて記述します。CIPHER typeは、一度に唯一の1ブロックのみを操作し、ブロック間に依存性の無いときに使われます。

Registration specifics

The registration of [CIPHER] algorithm is specific in that struct crypto_alg field .cra_type is empty. The .cra_u.cipher has to be filled in with proper callbacks to implement this transformation.

[CIPHER] algorithmの登録は、構造体crypto_alg のfirld .cra_typeが空である特徴を持ちます。このtransofrmを実装するには、cra_u.cipherに適切なコールバックを入力しなければなりません。

See struct cipher_alg below.

Cipher Definition With struct cipher_alg

Struct cipher_alg defines a single block cipher.

構造体cipher_algは、single block cipherを定義します。

Here are schematics of how these functions are called when operated from other part of the kernel. Note that the .cia_setkey() call might happen before or after any of these schematics happen, but must not happen during any of these are in-flight.

以下は、kernelのほかの部分から処理されたときに、これらの関数がどのように呼び出されるのかを示す図である。cia_setkey()は、これらの処理が実行される前か後に呼び出され、決して実行中には呼ばれないことに注意してください。

KEY ---.    PLAINTEXT ---.
       v                 v
 .cia_setkey() -> .cia_encrypt()
                         |
                         '-----> CIPHERTEXT

Please note that a pattern where .cia_setkey() is called multiple times is also valid:

cia_setkey()が複数回呼ばれるパターンも有効であることを注意してください。

KEY1 --.    PLAINTEXT1 --.         KEY2 --.    PLAINTEXT2 --.
       v                 v                v                 v
 .cia_setkey() -> .cia_encrypt() -> .cia_setkey() -> .cia_encrypt()
                         |                                  |
                         '---> CIPHERTEXT1                  '---> CIPHERTEXT2

Multi-Block Ciphers

Example of transformations: cbc(aes), chacha20, …

This section describes the multi-block cipher transformation implementations. The multi-block ciphers are used for transformations which operate on scatterlists of data supplied to the transformation functions. They output the result into a scatterlist of data as well.

このセクションでは、multi-block cipher transformの実装について記述します。multi-block cipherは、transfomation functionに提供されるデータのscatterlistを操作するtransformに使用されています。また結果をデータのscatterlistの中に出力します。

Registration Specifics

The registration of multi-block cipher algorithms is one of the most standard procedures throughout the crypto API.

multi-block cipher algorimの登録は、crypto APIを通過する最も基本的なやり方の1つである。

Note, if a cipher implementation requires a proper alignment of data, the caller should use the functions of crypto_skcipher_alignmask() to identify a memory alignment mask. The kernel crypto API is able to process requests that are unaligned. This implies, however, additional overhead as the kernel crypto API needs to perform the realignment of the data which may imply moving of data.

cipher実装は、dataの適切なアライメントを期待していることに注意してください。呼び出し元は、crypto_skcipher_alignmask()の関数を使って、メモリアライメントマスクを扱わねばなりません。kernel crypyo APIは、alignmentがあっていない要求を実行することができます。ただし、カーネル暗号APIがデータの再調整を実行する必要があるため、データの移動を意味する可能性があるため、これは追加のオーバーヘッドを意味します。

Cipher Definition With struct skcipher_alg

Struct skcipher_alg defines a multi-block cipher, or more generally, a length-preserving symmetric cipher algorithm.

構造体 skcipher_algでは、multi-block cipher、より一般的に言えば、長さ維持する対象cipher algorithmを定義します。

Scatterlist handling

Some drivers will want to use the Generic ScatterWalk in case the hardware needs to be fed separate chunks of the scatterlist which contains the plaintext and will contain the ciphertext. Please refer to the ScatterWalk interface offered by the Linux kernel scatter / gather list implementation.

ハードウェアに平文と暗号文を含むscatterlistの個別のチャンクを供給する必要がある場合に備えて、一部のドライバはGeneric ScatterWalkを使用することを望みます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Linux基礎11 -テキスト処理-

はじめに

Linuxではアプリケーションのデータやシステムの設定項目などの多くがテキストとして表現されています。そのためこれらのテキストを扱うことがLinuxでの主な作業になります。今回はテキスト処理を効率よく行うために、便利なコマンドを紹介します。

wcコマンド

wcコマンドは入力ファイルの行数・単語数・バイト数を数えるコマンドです。

wcコマンド
$ wc /etc/passwd
=> 21 31 973 /etc/passwd    #オプションを指定しない場合、左から行数(21)、単語数(31)、バイト数(973)を示す

-l-w-cオプションを指定するとそれぞれ行数単語数バイト数だけを表示します。
スクリーンショット 2020-05-28 10.58.38.png
中でも-lオプションは利用頻度が高いです。例えば、ルートディレクトリ直下にあるファイルやディレクトリの数を調べられます。
スクリーンショット 2020-05-28 11.01.16.png
なお、今回紹介するコマンドは、ファイル名を指定しない場合、標準入力をから読み込むことを念頭に入れておいてください。
標準入力についてはLinux基礎10 -標準入出力とパイプライン-を参照してください。

フィルタを使う際に標準入力から読み込むことを明示的に指定したい場合には-(ハイフン)を入力ファイル名として利用します。

標準入力を明示的に指定する
$ ls / | wc -l -    
=> 20 - 

sortコマンド

sortコマンドは行単位でテキストをソートするコマンドです。ソートとは、一定の規則に基づいて行を並べ替えることです。

ASCIIコード順にソート

sortコマンドは、デフォルトではACSIIコード順に行を並べ替えます。ASCIIコードとは、コンピュータ上で文字を表現するためのコード体系の1つです。コンピュータの内部では、アルファベットや、数字、記号などの半角英数字には、1つ1つの番号が振られており、この番号のことをASCIIコード呼びます。歴史的な理由から、ASCIIコードは、大文字の方が番号が小さく、小文字の方が割り当てられているため、sortコマンド用いた場合は、大文字が先に並べられることに注意しましょう。

では、例として次の内容のファイルを並べ替えてみましょう。
スクリーンショット 2020-05-28 11.13.32.png
このファイルをsortコマンドでソートすると、次のようにアルファベット順に並べ替えられます。
スクリーンショット 2020-05-28 11.16.36.png
sortコマンド行の先頭から文字を比較していきますが、行の途中の特定の項目を対象としてソートすることもできます。次の例をみていきましょう。
スクリーンショット 2020-05-28 11.18.27.png
ps xコマンドの結果は、PIDの順に表示されます。出力結果は、それぞれの項目がスペース区切りとなっており、この各項目をフィールドと呼びます。上の例でいうと、第一フィールドがPID、第二フィールドがTTYですね。
ここでCOMMANDフィールドを対象として、行を並べ替えたいとしましょう。ソートするフィールドは-kオプションを指定して、フィールド番号を引数に指定します。今回の場合は-k 5を指定します。
スクリーンショット 2020-05-28 11.26.33.png
COMMANDのフィールドを対象にASCIIコード順にソートすることができました。

数値順にソート(-n)

-nオプションは、文字列を数値とみなして並べ替えるためのオプションです。例として次のようなテキストファイルを考えてみましょう。
スクリーンショット 2020-05-28 11.43.45.png
このファイルを、オプションを指定せずにsortコマンドを実行すると次のような結果になります。
スクリーンショット 2020-05-28 11.45.02.png
これは、sortコマンドが各行を文字列として評価しているためです。各行の値を数値として評価するために-nオプションを指定してあげると次のように変化します。
スクリーンショット 2020-05-28 11.46.41.png

逆順にソート(-r)

sortコマンド-rは逆順にソートするためのオプションです。デフォルトでは昇順にソートされますが、-rをつけると降順でソートしてくれます。
スクリーンショット 2020-05-28 11.49.28.png
応用として、数値データが記録されたファイルなどを値の大きい順に並べ替えることができます。例としてlsコマンドの結果をファイルサイズの大きい順に並べ替えてみましょう。headコマンドを組みわせて、上位5つを取得するようにしています。
スクリーンショット 2020-05-28 11.54.05.png

uniqコマンド

uniqコマンド連続した同じ内容の行を省くコマンドです。次の例をみていきましょう。
スクリーンショット 2020-05-28 12.01.58.png
file3では、HokkaidoやIwateが連続して重複していましたが、uniqコマンドを実行することで、重複を省くことができました。注意点としては、uniqコマンドは、同じ内容の行が連続している場合にのみ重複を取り除くことができるということです。
次の例をみていきましょう。
スクリーンショット 2020-05-28 12.05.11.png
連続して存在していたAomoriの重複は省かれましたが、離れて存在していたHokkaidoは残っています。全体から重複行を取り除きたい場合には、一旦sortコマンドで並び替えてからuniqコマンドを実行することで、可能になります。
スクリーンショット 2020-05-28 12.07.56.png

重複行を数える

uniqコマンドには様々なオプションがありますが、その中でもよく使われる-cオプションを紹介します。-cオプションは重複している行数を数えて表示するためのオプションです。例えば対象のファイルに各データがそれぞれ何件あるかを調べたりする時に使います。先ほどのfile4でに使ってみましょう。
スクリーンショット 2020-05-28 12.49.38.png

cutコマンド

cutコマンドは入力行の一部分を切り出して、出力するコマンドです。

cutコマンド
$ cut -d <区切り文字> -f <フィールド番号> [<ファイル名>] #区切り文字を指定しない場合はtabが区切り文字とみなされる
$ cut -d , -f 3 file.csv #入力例 区切り文字を(,)とみなして、3番目のフィールドだけが表示される

例として、/etc/passwdファイルを対象にします。
スクリーンショット 2020-05-28 18.49.08.png
/etc/passwdファイルは「:」で区切られて、それぞれのフィールドが形成されています。それぞれのフィールドは次の表のような意味を持っています。

フィールド番号 意味
1 ユーザ名
2 パスワード(本物ではなくダミー)
3 ユーザID
4 グループID
5 コメント(本名など)
6 ホームディレクトリ
7 ログインシェル

今回はここからログインシェルだけを出力します。
スクリーンショット 2020-05-28 18.54.07.png

trコマンド

trコマンドは文字を置き換えるコマンドです。

trコマンド
$ tr <置換前の文字> <置換後の文字>

$ tr : ,    #(:)を(,)に置換する例

今回は先ほどの/etc/passwdファイルを対象に「:」を「,」に置き換えてみましょう。
スクリーンショット 2020-05-28 18.58.17.png
複数の文字を置換することもできます。a->A、b->B、c->Cに変更してみましょう。
スクリーンショット 2020-05-28 19.01.05.png
注意すべきは、abcという文字列を対象としているのではなく、abcという単独の文字を置き換えているということです。

置換文字に-(ハイフン)を利用することで、文字範囲を指定することができます。例えばa-zと書くと、abcdef...xyzと同じ意味になります。
スクリーンショット 2020-05-28 19.04.40.png
おさらいですが、trコマンドは1文字単位の文字置換ですので注意しましょう。文字列の置換を行いたい場合は、sedコマンドawkコマンドを利用します。これは後の記事で後述します。

ファイル指定の注意

trコマンドは、純粋なフィルタとして設計されており、ファイルから直接読み込むことはできません
スクリーンショット 2020-05-28 19.09.34.png
テキストファイルにtrコマンド指定したい場合はcatしてパイプをわたすか、もしくは入力リダイレクトを使用してください。

テキストファイルへのtrコマンドの使用
$ cat /etc/passwd | tr : , #パイプを使う場合
$ tr : , < /etc/passwd   #入力リダイレクトを使う場合

文字の削除

trコマンドを使用すると文字を削除することができます。

trコマンドによる文字の削除
$ tr -d <削除文字列>

今回は改行文字(\n)を削除してみましょう。
スクリーンショット 2020-05-28 19.18.34.png

tailコマンド

tailコマンドはファイルの末尾を表示するコマンドです。オプションを指定しない場合は、次のようにファイルの末尾10行が表示されます。
スクリーンショット 2020-05-28 19.21.06.png
tailコマンドで表示する行数は、-nオプションで指定します。

ファイルへの追記を監視する

ログの出力やデータ収集などで、常時追記されていくファイルを表示する際に、追記されるたびにその内容を表示してファイルを監視したい場合があります。そのような場合は-fオプションを指定します。

tail_-fでファイルを監視
$ tail -f <ファイル名>

これにより、ファイルの内容が書き換えられると、リアルタイムに表示されます。下の例ではターミナルを2つ起動して、片方でtail -fを実行しながら、もう1つのターミナルでファイルへ書き込みを行っています。
スクリーンショット 2020-05-28 19.27.50.png

diffコマンド

diffコマンドは2つのファイルの差分を表示するコマンドです。このコマンドはプログラムのソースコードの編集前と編集後を比較するのによく使用されます。

diffコマンド
$ diff [オプション] <比較元ファイル> <比較先ファイル>

ここでは例として、file1をコピーしてfile1cpを作成し、末尾に都道府県を追加しました。
スクリーンショット 2020-05-28 19.36.37.png
diffコマンドで変更前後を比較してみましょう。
スクリーンショット 2020-05-28 19.38.35.png
7a8,11の部分は変更コマンドと呼ばれる部分で、以下の表の通りの意味を持ちます。

記号 内容
<範囲>a<範囲> Addのa。1つ目のファイルの範囲1の後に、2つ目のファイルの範囲の内容が追加された
<範囲>c<範囲> Changeのc。1つ目のファイルの範囲1の箇所が、2つ目のファイルの範囲2の内容に変更された
<範囲>d<範囲> Deleteのd。1つ目のファイルの範囲1の箇所が削除された

今回は、7a8,11なので、変更前のファイルの7行目の後に変更後のファイルの8-11行目の内容が追加されたということですね。
なお、追加した内容は、変更コマンドの下に記述されますが、ここでの><はそれぞれ次の意味を持ちます。

記号 内容
< 1つ目のファイルにだけ存在する行
> 2つ目のファイルにだけ存在する行

今回は、"Tochigi"から"Saitama"が編集後のファイルに追加されていますよという意味ですね。

ユニファイド出力形式

diffコマンドの差分表示には、ざまざまな形式が、用意されています。デフォルトでは先ほどの><で表現されていますが、よく使われる形式として、ユニファイド形式(Unified format)があります。ユニファイド形式は、diff -uで出力できます。先ほどの都道府県の変更例をユニファイド形式で出力してみましょう。
スクリーンショット 2020-05-28 19.51.20.png
ユニファイド形式の特徴は以下の通りです。

  • 最初の2行に2つのファイルのファイル名と更新日時が表示されます
  • 3行目からが差分で、追加した行には+、削除された行には-がつきます
  • 差分は変更箇所だけでなく、その前後何行かも合わせて表示されます
  • 差分表示の冒頭、@@から始まる行は、その差分が元ファイルの何行目に対応しているのかを示しています。これは「@@ -<1つ目のファイルの変更開始行>,<変更行数> +<2つ目のファイルの変更開始行>,<変更行数>」という形式で表されます。<変更行数>は変更のあった行だけでなく、その前後も含みます

差分の使い方とパッチ

あるファイルの変更を他人に伝える時に、変更後のファイル全体を送るのではなく、diffコマンドで出力した分だけを共有することもできます。このとき、受信者は元ファイルと差分ファイルから、変更後のファイルを復元できます
修正内容を共有するこのようなファイルのことを、パッチと呼びます。diffコマンドで出力した差分はpatchコマンドでパッチを当てる(修正を適応する)ことができます。パッチを適応する際、元ファイルが変更されていて、行番号がずれていると、エラーとなることがあります。しかし、ユニファイド形式なら前後の内容も含まれているため、行が多少ずれていても、パッチとして適応できます。
ユニファイド形式はGitの差分表示にも使われていたりするので、目にする機会が多いでしょう。

参考資料

新しいLinuxの教科書

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

mvコマンド 一つ上の階層に移動

すぐに忘れてしまうので、おぼえがき

一つ上の階層の別のディレクトリ。ここではtestというディレクトリ 。

$mv [ファイル名] ../test
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

接続制限されたWebサーバでのサイト更新

ユーザー独自で導入されたWebサーバ(OSはRedhatLinux-Ver8)に、Wordpressを
導入し、Webサイトを構築したいとの話が有りました。
Webサーバ自体はセキュリティの観点から、サーバ上へのファイルのアップ、サーバ
サーバ内のファイル等の更新、設定変更等を行う場合、外部からの接続はSSH経由の
接続しか許可されていないとの事です。
この様なサーバ環境でWordpressを導入する場合、Wordpressの管理画面への接続は
どの様に行えば宜しいのでしょうか?
そもそも出来ないと云う事であれば、Wordpressは諦めていただき、ホームページ
作成ソフトを利用するよう、ユーザー様には伝えたいと思っております。

よろしくお願い致します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CentOS7 on VirtualBOXにおけるホストOSとの共有フォルダ設定方法

はじめに

はじめまして。「かびごん」といいます。
普段は、金融機関でビジネスを理解しようとしないデータサイエンティストをしています。
Qiita初記事として自分の奮闘記録を残したいと思います。

執筆の背景

UdemyでDockerを勉強を進める際に、VirtualBoxの共有フォルダ設定(ホストOS⇔ゲストOS)が必要になりそこでハマってしまいました。
先駆者の方々の記事はあるものの、どれもそのままでは解決できず、さまざまな情報のアンサンブル学習の末、設定が出来ましたので備忘録も含めこちらに記します。
参照された方の一助となれれば幸いです。

※Vagrantを使えば楽だよ!という事は承知の上、どうしても下記環境下での作業が求められていたため奮闘しておりました。

実行環境

(Host OS) Windows10 pro
(Guest OS:virtual)CentOS7.7 (1908) CLI
(Virtualization)VirtualBox 6.1.8-137981
試行時期:2020/05/28

手順

CentOS導入~起動までは省きます。

VirtualBoxでは、[設定]>[共有フォルダー]といったいかにも親切そうな設定項目があり、ここでホストOSのフォルダを指定してあげれば出来るんだろうな~と思ったのもつかの間。
VirutalBoxでは共有フォルダーの設定の前に、
「Guest Additions」というものを設定してあげなければいけません。
※これが厄介

下記で、一連の流れを説明します。

GetAdditions設定前夜(パッケージ更新)

suでroot権限に移った後
パッケージのアップデートと必要なパッケージのインストールを実施します。

yum -y update
yum -y install kernel-devel kernel-headers gcc gcc-c++
yum -y install bzip2

kernel-develkernel-headersから初めてしまうと、アップデートされていない既存kernelとの整合性がとれなくなってしまうので、kernelのアップデート(yum -y updateに含まれる)を先に実行する意味があります。

bzip2は後の工程で必要になるので、ここでインストールする事をお勧めします。

上記の工程が終了したら一度reboot再起動しましょう!

GuestAdditionsのインストール

(再起動後また、suコマンドでroot権限に移行しておく)

GuestAdditionsはisoイメージ形式で導入しますが、特段入手する必要なく、VirtualBoxに標準で搭載されています。
※搭載されているだけで、それを読み込む必要があります。

ISOイメージの挿入

VirtualBoxの[デバイス]>[Guest Additions CDイメージの挿入...]を選択し接続。
ss1.png
※上図赤枠部分

インストール

接続したディスクイメージをCentOS内mediaフォルダへマウントします。

mount -r /dev/cdrom /media

mediaフォルダへ移動し、
マウントの完了後、マウントイメージからインストールを実行します。
ここで行う、VBoxLinuxAdditions.runの際にbzip2パッケージが必要となります。

cd /media
sh ./VBoxLinuxAdditions.run

インストールが完了したら、イメージをアンマウントして再起動をする。

cd
umount /media
reboot

また、アンマウントはGUIからも実行できる。
ss2.png

共有フォルダの設定

再起動からの復帰後、[デバイス]>[共有フォルダー]>[共有フォルダー設定]から、
ホストOS(今回は、Windows)内の共有したいフォルダを設定します。
ss3.png

[共有フォルダー設定]を選択すると、下記ウィンドウへ遷移します。
ss4.png
「共有フォルダー」を選択し、右側の新規追加ボタンを押します。
ss5.png
任意のフォルダを選択します。(今回はdocker_fileというフォルダ名)
また、

  • 自動マウント(A)
  • 永続化する(M)

にチェックをし、[OK]を押して閉じる。

これで、ゲストOS(CentOS)内で/media/sf_{共有フォルダ名}今回の場合は、/media/sf_docker_fileとして共有される。
※もし、共有されていない場合は再起動をかけてみましょう。

共有フォルダのマウント

上記、/media/sf_{共有フォルダ名}以外でマウントしたい場合は、

su
cd media/
mount -t vboxsf sf_{共有フォルダ名} {マウント先のディレクトリ名}
reboot

で、ゲストOS内で別フォルダを共有フォルダとすることができます。

アクセス制限設定

上記までの作業で、rootユーザーが共有フォルダを利用可能になりますが、その他一般ユーザーに共有フォルダのアクセス権を与える場合は、下記のコマンドでユーザーを追加します。

su
gpasswd --add {ユーザ名} vboxsf

関連記事

各記事、大変参考にさせて頂き、一部引用させて頂いた部分があります。
もし、ご迷惑なようであればご指摘頂けますと幸いです。

参考サイト

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Wake on LAN用のPythonスクリプトを作成する(NAT超えWake on LAN[5])

はじめに

前回の続きです.
とりあえずやりたいことはこんなんです.

network_detail.png

今回は遂に「Wake on LAN用のPythonスクリプトを作成する」です.

DesktopPCの設定

前提:Ubuntu18.04です.
まずはWake on LANが実行できるように設定します.

固定IPの割り当て

sudo vi /etc/netplan/~~.yaml

以下を記入します,

/etc/netplan/~~.yaml
# Let NetworkManager manage all devices on this system
network:
  version: 2
  renderer: NetworkManager

  ethernets:
    eno1:
       dhcp4: no
       wakeonlan: true # enable wake on lan
       addresses: [192.168.1.**/24] # assign arbitrary address
       gateway4: 192.168.1.1  # your router(raspi)'s address
       nameservers:
         addresses: [8.8.8.8,8.8.4.4] # google's public dns

再起動します.

sudo netplan apply
sudo reboot

wake on lan が有効になっているか確認するためにethtoolをインストールします.

sudo apt-get install -y ethtool

Ethernetの名前をメモします.ついでにIPが固定されているか確認します.

$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eno1<<<<copy<<<<: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether **:**:**:**:**:** brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.**/24 brd 192.168.1.255 scope global noprefixroute eno1
       valid_lft forever preferred_lft forever
    inet6 fe80::2d8:61ff:fe56:242d/64 scope link
       valid_lft forever preferred_lft forever

Wake on LANが有効になっているか確認します.Wake-on: gになっていればOKです.

$ sudo ethtool eno1
~~
Supports Wake-on: pumbg
Wake-on: g # if it's d, your wake on lan setting may be wrong
~~

BIOSの設定

省略します.起動される側のPCのWOL設定(1/3):BIOS/UEFIの設定wo
参考にしてください.

確認

ラズパイでip aを確認してDesktopに繋いでいるEthernetにipが割り当てられていればOKです.(UP,LOWER_UPになっている必要があります.ありがとうtelcomM

$ ip a
4: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether **:**:**:**:**:** brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.1/24 brd 192.168.1.255 scope global noprefixroute eth1
       valid_lft forever preferred_lft forever
    inet6 fe80::83fa:6dee:9799:9a6e/64 scope link 
       valid_lft forever preferred_lft forever

確認(wakeonlanをインストールし起動できるか確認)

ラズパイにwakeonlanをインストールします.

sudo apt install wakeonlan
wakeonlan -i 192.168.1.255 -p 7 **:**:**:**:**:**

これで起動できればOKです.

mod_wsgi

まず,PythonスクリプトをApacheで動かすことができるmod_wsgiと諸々をインストールします.

sudo apt install python3-dev python3-pip apache2 libapache2-mod-wsgi-py3
sudo apt-get -y install python3 ipython3 python3-flask curl

Pythonスクリプト作成

とりあえず動くPythonスクリプトを作成します.今回はFlaskを用います.

cd ~
mkdir flask && cd flask
vi app.py
~/flask/app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
    return 'This is vpn server for wake on lan!\n\n'
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

とりあえず,mod_wsgiが動くか確認します.

$ python3 app.py
$ curl http://localhost:5000 # 別タブで
This is vpn server for wake on lan!

”This is vpn server for wake on lan!”と出ればOKです.

Apacheとmod_wsgiの設定

新たにflask用の設定ファイルを作成します.

sudo vi /etc/apache2/sites-available/flask.conf

今回はGCPから80ポートにリダイレクトされるようにしているので以下のように記入します.

/etc/apache2/sites-available/flask.conf
<VirtualHost *:80>
    ServerName kado.example.com
    WSGIDaemonProcess flask user={username} group={username} threads=5
    WSGIScriptAlias / /home/{username}/flask/adapter.wsgi
    <Directory /home/{username}/flask/>
        WSGIProcessGroup flask
        WSGIApplicationGroup %{GLOBAL}
        WSGIScriptReloading On
        Require all granted
    </Directory>
</VirtualHost>

flask.confを有効化します.

sudo a2dissite 000-default.conf
sudo a2ensite flask.conf
sudo service apache2 restart

adapter.wsgiファイルを作成します.

vi ~/flask/adapter.wsgi
~/flask/adapter.wsgi
import sys
if sys.version_info[0]<3:       # require python3
    raise Exception("Python3 required! Current (wrong) version: '%s'" % sys.version_info)
sys.path.insert(0, '/home/kadorpi/flask/')
from app import app as application # <- app means app of app.py

Apacheを再起動します.

sudo service apache2 restart

動いているか確認します.

$ curl localhost
This is vpn server for wake on lan!

これで,PythonスクリプトがApache上で動くようになりました.

Line bot用のWake on LANスクリプト作成

ここまできたら後少しです.LineからGCPにHTTPリクエストを送り,受け取ったリクエストをラズパイの80ポートにリダイレクトします.そして,ラズパイで受け取ったリクエストをPythonで処理します.

まず,Line Developerにアカウントを登録します.
次にChannelを作成します.

1.png

2.png

3.png

Channel Secretを発行し,メモします.

4.png

Channel Access Tokenを発行し,メモします.

5.png

>Response settingsからAuto-replyなどを適宜設定します.

6.png

>Message APIからWebhookURLを入力します.今回はhttps://{domain name}/wake-on-lanとします.

7.png

これでLINE Botの設定は終わりです.

mod_wsgiの再設定

環境変数として,先ほどメモしたChannel SecretとChannel Access Tokenを設定します.

vi ~/flask/adapter.wsgi

以下を追記します.

~/flask/adapter.wsgi
import os
os.environ['LINE_CHANNEL_SECRET'] = 'fuga'
os.environ['LINE_CHANNEL_ACCESS_TOKEN'] = 'hogehoge'

Pythonスクリプト

必要なモジュールをインストールします.

pip3 install line-bot-sdk flask

pythonコードは以下のようにします.

import os, sys
from flask import Flask, request, abort
app = Flask(__name__)

from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import (
    TextMessage, TextSendMessage, MessageEvent
)

# get channel_secret and channel_access_token from your environment variable
channel_secret = os.getenv('LINE_CHANNEL_SECRET', None)
channel_access_token = os.getenv('LINE_CHANNEL_ACCESS_TOKEN', None)
if channel_secret is None:
    print('Specify LINE_CHANNEL_SECRET as environment variable.')
    sys.exit(1)
if channel_access_token is None:
    print('Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.')
    sys.exit(1)

line_bot_api = LineBotApi(channel_access_token)
handler = WebhookHandler(channel_secret)

@app.route('/')
def show_web():
    return 'This is vpn server for wake on lan!\n\n'

@app.route('/wake-on-lan', methods=['POST'])
def wake_on_lan():
    # get X-Line-Signature header value
    signature = request.headers['X-Line-Signature']

    # get request body as text
    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)

    # handle webhook body
    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        abort(400)

    return 'ok'

@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    message = event.message.text

    replies = []
    if re.compile("\s*(check)\s*", re.IGNORECASE).search(message):
        result = confirm()
        replies += [TextSendMessage(result)]

    elif re.compile("\s*(kick)\s*", re.IGNORECASE).search(message):
        if confirm() == 'Awake':
            replies += [TextSendMessage('Already awake')]
        else:
            result = kick()
            replies += [TextSendMessage(result)]

    buttons_template = ButtonsTemplate(
        title='usage', text='Tap below buttons', actions=[
            MessageAction(label=m, text=m) for m in message_list
        ])

    template_message = TemplateSendMessage(alt_text='Usage', template=buttons_template)
    replies += [template_message]

    line_bot_api.reply_message(event.reply_token, replies)


def confirm():
    hostname = "192.168.1.**"
    response = os.system("ping -c 1 " + hostname)
    # and then check the response...
    if response == 0:
        pingstatus = "Awake"
    else:
        pingstatus = "Sleeping"

    return pingstatus

def kick():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    s.sendto(b'\xFF' * 6 + b'\x**\x**\x**\x**\x**\x**' * 16, ('192.168.1.255', 7))
    s.close()

    return 'kicked'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

少し説明を入れると,まず以下でLINE用のChannel SecretとChannel Access Tokenを読み込みます.

# get channel_secret and channel_access_token from your environment variable
channel_secret = os.getenv('LINE_CHANNEL_SECRET', None)
channel_access_token = os.getenv('LINE_CHANNEL_ACCESS_TOKEN', None)
if channel_secret is None:
    print('Specify LINE_CHANNEL_SECRET as environment variable.')
    sys.exit(1)
if channel_access_token is None:
    print('Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.')
    sys.exit(1)

line_bot_api = LineBotApi(channel_access_token)
handler = WebhookHandler(channel_secret)

以下は,Web表示用に一応加えました.

@app.route('/')
def show_web():
    return 'This is vpn server for wake on lan!\n\n'

ここに上記で設定したWebhookのリクエスト処理を書きます.

@app.route('/wake-on-lan', methods=['POST'])
def wake_on_lan():
    # get X-Line-Signature header value
    signature = request.headers['X-Line-Signature']

    # get request body as text
    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)

    # handle webhook body
    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        abort(400)

    return 'ok'

そして,最後にメッセージで"kick"が来たら,MagicPacketの送信,"check"が来たらPingで起動できているかを確認する処理をここで書いています.

@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    message = event.message.text

    replies = []
    if re.compile("\s*(check)\s*", re.IGNORECASE).search(message):
        result = confirm()
        replies += [TextSendMessage(result)]

    elif re.compile("\s*(kick)\s*", re.IGNORECASE).search(message):
        if confirm() == 'Awake':
            replies += [TextSendMessage('Already awake')]
        else:
            result = kick()
            replies += [TextSendMessage(result)]

    buttons_template = ButtonsTemplate(
        title='usage', text='Tap below buttons', actions=[
            MessageAction(label=m, text=m) for m in message_list
        ])

    template_message = TemplateSendMessage(alt_text='Usage', template=buttons_template)
    replies += [template_message]

    line_bot_api.reply_message(event.reply_token, replies)


def confirm():
    hostname = "192.168.1.**"
    response = os.system("ping -c 1 " + hostname)
    # and then check the response...
    if response == 0:
        pingstatus = "Awake"
    else:
        pingstatus = "Sleeping"

    return pingstatus

def kick():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    s.sendto(b'\xFF' * 6 + b'\x**\x**\x**\x**\x**\x**' * 16, ('192.168.1.255', 7))
    s.close()

    return 'kicked'

Apacheを再起動します.

sudo service apache2 restart

おわり

後半,疲れてだれてしまいました...
さらに備忘録的に書いているのでわかりづらいかもしれません...
とりあえず全部書けたので良かったです。参考になれば幸いです.

動画

参考

How To Use Apache HTTP Server As Reverse-Proxy Using mod_proxy Extension

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ラズパイをルーター化する(NAT超えWake on LAN[4])

はじめに

前回の続きです.
とりあえずやりたいことはこんなんです.

network_detail.png

今回は「ラズパイをルーター化する」です.

有線LANアダプタを買う

ラズパイには,LANポートは1つしかないので,有線LANアダプタを書います.こんなやつ

新しいEthernetに固定IPアドレスを割り当てる

ip aコマンド(sudo ifconfig)で確認すると新たなEthernet Ineterfaceが追加されているはずです.今回はeth1という名前でした.

$ sudo ifconfig # ip a の方がいいっぽいです
eth1: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        ether 04:ab:18:3b:af:e2  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

固定IPを割り当てます.今回は,下図のようにラズパイをルーター※として,新たなネットワーク(192.168.1.0/24)を作りたいので,

network

sudo vi /etc/dhcpcd.conf

以下を追記します.

/etc/dhcpcd.conf
# External ethernet
interface eth1
static ip_address=192.168.1.1/24 # you can assign arbitrary ip address and subnet mask. Note that client must designate this address as gateway
static routers=**.**.**.** # you can assign arbitrary ip too. if this server's network is from another router, set routers address may be better.(you will not need to set dns masquerade)
static domain_name_servers=8.8.8.8 # see above line's comment

※ルーター化と言っていましたが,実はIPマスカレードするだけでした.
↑の**.**.**.**にルーターのアドレス,dnsアドレスにGoogleのPublicアドレスを入れています.(もはやルーターではないかもしれませんね...詳しくないのでわかりません笑)
※ラズパイに完全にルーター機能を付与する場合は**.**.**.**にも任意のアドレスを割り当て,dnsアドレスはコメントアウトすべき?.そして,参考にあるようにDNSマスカレードとDHCPサーバーの設定を行うといいのですかね...

確認

ラズパイに繋いだデスクトップPCの電源を入れて,ipを確認すると,eth1にIPが割り当てられます.
※デスクトップPCはまだインターネットに繋がりません.

sudo ifconfig # or ip a
# eth1: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
#         inet 192.168.1.1  netmask 255.255.255.0  broadcast 192.168.1.255
#         ether **:**:**:**:**:**  txqueuelen 1000  (Ethernet)
#         RX packets 0  bytes 0 (0.0 B)
#         RX errors 0  dropped 0  overruns 0  frame 0
#         TX packets 0  bytes 0 (0.0 B)
#         TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
ping 192.168.1.**

IPマスカレードの設定

IPマスカレードの設定をします.
1行目:プライベートIPアドレス空間から-oへの転送許可.
2行目:SNAT.
3行目:DNAT.

sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
sudo iptables -A FORWARD -i eth0 -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT # SNAT
sudo iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT # DNAT

このままだと一時的で,再起動すると情報が消えてしまうので,

mkdir ~/iptable && cd ~/iptable
sudo iptables-save > iptables.dat
sudo vi /etc/rc.local 
/etc/rc.local
# iptables
iptables-restore < /home/{user name}/iptable/iptables.dat

これで,デスクトップPCがインターネットに繋がるはずです.

参考

Ubuntu 16.04 でルータつくる
Ubuntu 18.04LTS で NAT ルータを構築
Linuxマシンをルータにする方法
手元のUbuntuにUSB-LANアダプタを追加してルータ化する
Ubuntu PCをルータ代わりにして、新しくLANを構築してみる
How to Setup a Raspberry Pi DNS Server
Raspberry PiにDnsmasqをインストールする

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む