2019.11.06に追記しました。
モチベーション
自宅でのプログラミング環境を整備するためにCentOS7にGitLabとMattermostをインストールした自宅サーバーを運用中(このセットアップは別途記事を準備中。いろいろあってめちゃくちゃ長くなってしまっている...)
GitLabからのいろいろな通知はMattermostで見れるようになっているが、それ以外にも例えばClamAVでウイルス検知したときや適当なログなどをMattermostのプライベートチャネルに送れたら管理が楽になりそう。Gmailは大量のスパムに埋もれるし。pythonにslackwebというslack等々にメッセージを送れるライブラリがあるようなので、そのインストールが最終目標。
とはいえ、そもそもpythonについてはサーバー内で特に弄っていなかったため、CentOS 7デフォルトの2.7ではなく3系統が使えるように環境を構築する。もとの環境を汚さないように適当な仮想環境を作るというのでググってみると、pyenvとpipenvによる仮想環境が良さそうだったので当面それを試す。
$と#の補足
コンソールについては$ならばローカルユーザー、#はrootユーザー(sudo su状態)とする。
事前準備
まずはいつも通りsudo yum update
で一旦環境を最新化する。今回はGitLabが12.4.0から12.4.1に上がっており、少々アップデートに時間がかかった。
pyenvのインストール
まずはpyenvに必要なパッケージをインストール。
# yum install gcc bzip2 bzip2-devel openssl openssl-devel readline readline-devel sqlite-devel tk-devel git
次にpyenvをgithubからクローンする。全ユーザーで利用可能なように/usr/local/pyenvに配置。
# git clone git://github.com/pyenv/pyenv.git /usr/local/pyenv
# cd /usr/local/pyenv
# mkdir {versions,shims} <- ,後にスペースを入れてはいけない
また、pyenvアップデート用のパッケージもダウンロートしておく。
# cd plugins/
# git clone git://github.com/pyenv/pyenv-update
pyenv用の環境変数を指定し、パスを通す。
# echo 'export PYENV_ROOT="/usr/local/pyenv"' >> /etc/profile.d/pyenv.sh
# echo 'export PATH="${PYENV_ROOT}/shims:${PYENV_ROOT}/bin:${PATH}"' >> /etc/profile.d/pyenv.sh
また、sudo時にPATHを引き継げるように
# visudo
の上、secure_path
のある行を下記のように編集する。
#Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin
Defaults env_keep += "PATH"
Defaults env_keep += "PYENV_ROOT"
これで再ログインか、$ source /etc/profile.d/pyenv.sh
するとpathが通るのでpyenvが使えるようになる。
$pyenv --version
pyenv 1.2.14-11-gb5f69fe
$pyenv install --list
~ 利用可能なpythonのバージョンがずらずら出る ~
(pathを通すところまではいいんだけどvisudoの中身の編集がどうしてこうなるのかはきちんと理解できていない。/sbinとかのところ、コメントアウトしていいの...?)
pipenvのインストール
pip自体が入っていなかったので、そこからインストール。
$curl https://bootstrap.pypa.io/get-pip.py | sudo python
$pip --version
pip 19.3.1 from /usr/lib/python2.7/site-packages/pip (python 2.7)
次にお待ちかねpipenvのインストール。
$sudo pip install pipenv
$pipenv --version
pipenv, version 2018.11.26
仮想環境自体をフォルダの中に作る #2019.11.06追記
上記の状態でpipenvは使えていて次節のpipenv --python 3.7.5
等でフォルダごと、ないしプロジェクトごとの仮想環境が作れる状態にある。
ただし、この仮想環境の実体は~/.local/share/virtualenvs/
の中に出来上がってしまう。サーバーにログインした際にMattermost上へユーザーの誰がログインしたかをgetpass.getuser()
で取得して通知したかったのだが、実体が各ユーザーのホームディレクトリにあるせいで、slackweb
を読み込めずエラーを吐いてしまった。pythonのバージョンは他ユーザーでも3.7.5になっていたので、pyenvの環境は読み込んだものの、pipenvの環境が読み込めていなかったようだ。
回避するには環境変数として$PIPENV_VENV_IN_PROJECT
を定義するとよい。これによって仮想環境の実体が各プロジェクトディレクトリの中の.venv
ディレクトリに格納されるようになる。
すべてのログインユーザーで各プロジェクトの.venv
を使ってほしいので、下記を/etc/bashrc
の最後に追記しておく。
# To enable local virtual enviroment for pipenv
export PIPENV_VENV_IN_PROJECT=1
これで単一のディレクトリの中身だけで使いたいプログラムから環境まで、すべてのユーザーから共通して扱えるようになった。
pyenvとpipenvによるpython仮想環境構築
当初目標のslackwebの動作確認用のプロジェクトフォルダを作成する。
$mkdir my_program
$mkdir my_program/slackweb
$cd my_program/slackweb
3.7系最新板のpython 3.7.5を環境に指定する。ただし、3.7系以降の利用にはlibffi-develが必要なようなので、3.7をいれる前にインストールしておく。(これをやらずに後で行ったり来たりしてしまった)
$sudo yum install libffi-devel
$sudo pyenv install 3.7.5
これでpyenv経由で3.7.5が入ったので、my_program/slackwebフォルダでこれを利用できるように仮想環境を作る。
$pipenv --python 3.7.5
この状態でsystemのpythonと、このフォルダにおけるpipenvのpythonのバージョンが違うことを確認する。
$python --version
Python 2.7.5
$pipenv run python --version
Python 3.7.5
pipenv run
を頭につけておくとその環境下でコマンドを利用したことになる。ちょっとした確認ではそれでいいが、仮想環境下に入りその後のコマンドはすべて仮想環境でのpythonで実施したい場合その後のコマンドはすべて仮想環境でのpythonで実施したい場合、pipenv shell
で環境に入る。出るときはexit
。
pyenvを先にやらずにいきなりpipenvからやっても、pyenvと連携してpythonのインストールからやってくれるようだが、その場合sudoでやらないといけない。そうすると作った仮想環境がroot配下になってしまいそうなので(?)、今回は分離して実行。
pyenvのインストールをローカルユーザーのみにして自分だけにパスを通せばsudoがいらなくなる(のか?)
最後にslackwebをインストール。
$pipenv install slackweb
通常のシェルでpip list
とするとsystemのpipでインストールされたあれやこれやが表示されるが、仮想環境下だと
[name@localhost slackweb]$ pipenv shell
(slackweb) [name@localhost slackweb]$ pip list
Package Version
---------- -------
pip 19.3.1
setuptools 41.6.0
slackweb 1.0.5
wheel 0.33.6
このように自分の作った仮想環境の中身が確認できる。
slackwebが動くかどうかのテストは次の通り。
(slackweb) [name@localhost slackweb]$python
Python 3.7.5 (default, Nov 3 2019, 00:13:05)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import slackweb
>>> slack = slackweb.Slack(url="自分のMattermostの内向きウェブフックアドレス")
>>> slack.notify(text="This is a test from inside of the server.")
MattermostのTown SquareにThis is a test from inside of the server.と表示されたので目標達成。これで今後はいろいろな通知がMattermostに出せて便利。いろいろはかどりそう。
Mattermostへログイン通知をさせる #2019.11.06追記
実際にサーバーログイン時にMattermostへ通知するプログラムを用意した。
import slackweb
import getpass
slack = slackweb.Slack(url="自分のMattermostの内向きウェブフックアドレス")
attachments = []
attachment = {"title": "Login notification",
"pretext": "This is a notification from _サーバーのFQDN_",
"text": "**" + getpass.getuser() + "** logged in.",
"mrkdwn_in": ["text", "pretext"]}
attachments.append(attachment)
slack.notify(channel="#通知先チャネル名",
username="CentOS 7",
attachments=attachments)
ちなみにgetpass
モジュールはpythonのデフォルトライブラリであって、pipenvで入れようとする必要などない(というか出来なくてエラーに悩むだけ。時間を無駄にしてしまった...)。もしうっかりインストールしてしまったらPipfileに変な記録が残ってしまうので、pipenv uninstall getpass
, pipenv clean
で余計な情報を消しておく。
あとはこれを実行すればログイン通知ができる。pipenv run python login_notification.py
をするだけだが、ログイン時に一度だけ自動で通知されてほしい。そこでこれもまた全ユーザーでそうなるように、/etc/bashrc
の最後に追記して対処する。
# Notify the login to mattermost
cd /home/username/my_program/slackweb/ <- usernameは上の環境やプログラムを作ったユーザー
pipenv run python login_notification.py
cd ~ <- 後で何をするかわからないので、ログインユーザーのホームに戻っておく
これでssh
でログインしたり、sudo su
するたびに各ユーザーやrootなどでのログインがされたという通知がMattermostに送られるようになった。Mattermostはスマホへの通知の連携なども簡単にできるので、これで自分の知らない間に怪しい人物が乗っ取りを行ったとしても即座に気づくことが出来る。
あくまで心の安寧のための保険なので、アクセスのための秘密鍵が漏洩しないようにすることがもっと大事だけれど、人間安心できるというのはとても大事だと思う。
(そういう意味ではこの記事をアップする際にウェブフックとかユーザー名とかうっかり晒してないか心配で全然安心できてない)
参考サイト
CentOSにPyenvをインストールしてPython3の開発環境を構築する
[Python] pyenv と pipenv による python 仮想環境を構築する(CentOS 7 ほぼ初期状態から )