カテゴリー: ソフトウェア

  • PostgreSQL 9.6 で “Peer authentication failed”

    PostgreSQL 9.6 で “Peer authentication failed”

    既に出尽くした感はありますが、昔からの PC ユーザほどハマりやすいのではないかと思うので記録します。

    データベースへ接続する際の認証手段は「ユーザ」と「パスワード」であると思い込みがちですが、最近の RDBMS では OS 側のユーザで認証するのがデフォルトになっています。

    例えば Linux で foo というユーザでログインしていたとします。ユーザを指定せずに mysql や psql を起動すると、同じ foo というユーザでデータベースに接続します。これはこれで簡潔でわかりやすい仕組みなのですが、落とし穴があって Linux 側と RDBMS が同じユーザでないといけない制限が掛かっていたりします。

    どういうことかというと、例えば PostgreSQL 9.6 を使っている環境で、ユーザ foo さんが psql をユーザ postgres で使いたいとき、次のようにユーザを指定して接続しても認証で弾かれます。パスワードが正しくても弾かれるので、ハマりやすいポイントです。

    foo@host:~$ psql -U postgres
    psql: FATAL: Peer authentication failed for user "postgres"

    postgres で psql に入るには sudo su – postgres などとして、Linux 側でもユーザ postgres である必要があります。

    foo@host:~$ sudo su - postgres
    postgres@host:~$ psql
    psql (11.1 (Debian 11.1-1.pgdg90+1), server 9.6.10)
    Type "help" for help.

    postgres=#

    この認証の設定は /etc/postgresql/9.6/main/pg_hba.conf にあります。hba は Host Based Authentication の略のようです。

    pg_hba.conf

    pg_hba.conf を開くと、説明のコメントがほとんどを占めていますが、必要なのは次の部分です。

    ...(略)...
    
    # Database administrative login by Unix domain socket
    local   all             postgres                                peer
    
    ...(略)...
    
    local   all             all                                     peer
    
    ...(略)...
    
    host    all             all             127.0.0.1/32            md5
    
    ...(略)...

    ここで “peer” となっているのが peer authentication です。これをパスワード認証にするには password または md5 とします。password は平文通信されるので、ハッシュ化される md5 を選びましょう。

    各設定はそれぞれ次のような意味です。

    local   all             postgres                                peer

    ローカルシステム上のユーザが、ユーザ名 postgres を使った peer 認証で全てのデータベース(all)に接続できます。peer 認証なので、実質的にローカルシステムの postgres というユーザが、全てのデータベースに接続できるのと同じです。

    local   all             all                                     peer

    ローカルシステム上のユーザは誰でも(2 つ目の all) peer 認証で全てのデータベース(1 つ目の all) に接続できます。

    host    all             all             127.0.0.1/32            md5

    127.0.0.1(localhost) からの接続はパスワード認証(md5)されて、全てのユーザ(2 つ目の all) が全てのデータベース(1 つ目の all) に接続できます。

    “peer” を “md5″ に書換えて、ロールにパスワードを設定すれば完了です。”sudo system reload postgresql” などとして、設定を反映させるのを忘れないように。

    ユーザ(ロール)のパスワード設定

    postgres 等の権限があるユーザで psql に入り、ALTER ROLE または ALTER USER を使います。

    postgres=# ALTER ROLE role_name ENCRYPTED PASSWORD 'your_password';

    そのパスワード認証、必要ですか?

    ここまで書いておいて今更ですが、peer 認証の仕組み自体は、分離している PostgreSQL と OS の認証をできるだけ簡素にする、とても良い仕組みです。MariaDB でも同じ手法を用いているため、これからは(あるいはもう既に)この手法が主流になっていくのでしょう。

    これが問題になるのは外部、あるいはプログラムからのアクセスでしょう。それについては、デフォルトで 127.0.0.1(localhost) からのアクセスはパスワード認証(md5)に設定されているため、ロールにパスワードを設定するだけで事足ります。

    今回は Python から psycopg2 で接続するのが最終目的だったため、ロールにパスワードを設定するだけで要件は満たしました。慣れた手法を使いたくなりますが、それによって時代の潮流から外れるのは、後々になって自分の首を絞めることになることはわかっているので、できる限り避けたいと思っています。

    参考

  • Debian 9 Stretch で Docker のインストール

    Debian 9 Stretch で Docker のインストール

    Debian 9 Stretch に Docker をインストールして動作確認を行うまで。

    $ sudo apt update
    $ sudo apt-get install apt-transport-https ca-certificates curl gnupg2 software-properties-common
    $ curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -

    /etc/apt/sources.list に次の一行を追加する。

    deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable

    もう一度 apt update して更新を行ったあとにインストールを行います。

    $ sudo apt update
    $ sudo apt install docker-ce

    動作確認に hello-world の image を run してみます。

    $ sudo docker run hello-world
    Unable to find image 'hello-world:latest' locally
    latest: Pulling from library/hello-world
    d1725b59e92d: Pull complete
    Digest: sha256:0add3ace90ecb4adbf7777e9aacf18357296e799f81cabc9fde470971e499788
    Status: Downloaded newer image for hello-world:latest
    Hello from Docker!
    This message shows that your installation appears to be working correctly.
    To generate this message, Docker took the following steps:
    The Docker client contacted the Docker daemon.
    The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
    The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
    The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.
    To try something more ambitious, you can run an Ubuntu container with:
    $ docker run -it ubuntu bash
    Share images, automate workflows, and more with a free Docker ID:
    https://hub.docker.com/
    For more examples and ideas, visit:
    https://docs.docker.com/get-started/

    ローカルのイメージを表示

    $ sudo docker images
    REPOSITORY TAG IMAGE ID CREATED SIZE
    hello-world latest 4ab4c602aa5e 2 months ago 1.84kB

    動作中のコンテナを表示

    $ sudo docker ps
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

    全てのコンテナを表示

    $ sudo docker ps -a
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    4d4807dccc3c hello-world "/hello" 8 minutes ago Exited (0) 8 minutes ago friendly_chandrasekhar

    コンテナの削除

    CONTAINER ID を指定します。

    $ sudo docker rm 4d4807dccc3c
    4d4807dccc3c

    ローカルのイメージを削除

    イメージの名前か IMAGE ID を指定します。

    $ sudo docker rmi hello-world
    $ sudo docker rmi 4ab4c602aa5e

    参考

  • Debian 9 Stretch で locale の変更

    Debian 9 Stretch で locale の変更

    $ localectl
    System Locale: n/a
    VC Keymap: n/a
    X11 Layout: us
    X11 Model: pc105
    $ localectl list-locales
    C.UTF-8
    en_US.utf8
    $ sudo localectl set-locale LANG=en_US.UTF-8
    $ localectl
    System Locale: LANG=en_US.UTF-8
    VC Keymap: n/a
    X11 Layout: us
    X11 Model: pc105
  • Python WSGI で毎回 encode(‘utf-8’) するのが面倒

    Python WSGI で毎回 encode(‘utf-8’) するのが面倒

    Python の WSGI プログラムで出力をするたび

    yield '<h1>テスト</h1>'.encode('utf-8')
    yield '<p>あいうえお</p>'.encode('utf-8')

    と encode(‘utf-8’) を付けていましたが、どう考えてもこれは手間だし無駄な気がします。Flask を使えばいいと言われたらお終いなので、何か手はないかと考えてみました。

    Python にはデコレータ(decorator) という仕組があって、任意の関数の前後に好きな処理を追加することができます。関数も第一級オブジェクトである Python の特性をうまく利用した素敵機能ですね。

    yield を扱った decorator の例があまり見つからなくてやや苦戦しましたが、次のような decorator で実際に動作することを確認しました。

    def encode_utf8(func):
    	def wrapper(*args, **kwargs):
    		for r in func(*args, **kwargs):
    			yield r.encode('utf-8')
    	return wrapper

    前回の WSGI でフォームを扱うプログラムに適用させてみたのが次のコードです。

    # coding: utf-8
    
    from urllib.parse import parse_qsl
    
    def encode_utf8(func):
    	def wrapper(*args, **kwargs):
    		for r in func(*args, **kwargs):
    			yield r.encode('utf-8')
    	return wrapper
    
    @encode_utf8
    def application(env, res):
    	res('200 OK', [('Content-type', 'text/html; charset=utf-8')])
    
    	# Form
    	yield '<form method="get" accept-charset="UTF-8"><p><input type="text" name="get" value=""><input type="submit" value="GET"></form>'
    	yield '<form method="post" accept-charset="UTF-8"><p><input type="text" name="post" value=""><input type="submit" value="POST"></form>'
    
    	# Request method
    	method = env.get('REQUEST_METHOD')
    	yield '<p>Method: {}</p>'.format(method)
    
    	# Get query string
    	if method == 'POST':
    		wsgi_input = env['wsgi.input']
    		query = wsgi_input.read(int(env.get('CONTENT_LENGTH', 0))).decode('utf-8')
    	else:
    		query = env.get('QUERY_STRING', '')
    	yield '<p>Query: {}</p>'.format(query)
    
    	# Output form fields
    	form = parse_qsl(query)
    	for k, v in form:
    		yield '<p>{} = {}</p>'.format(k, v)

    行数は変らないのでぱっと見の変化少ないですが、書き忘れて Internal Server Error をもらう率は少し減りますね。

  • Python WSGI でフォームのデータを取得

    Python WSGI でフォームのデータを取得

    TL; DR

    POST

    query = env['wsgi.input'].read(int(env.get('CONTENT_LENGTH', 0))).decode('utf-8')

    GET

    query = env.get('QUERY_STRING', '')

    parse and split

    # dictionary
    form = urllib.parse.parse_qs(query)
    # list
    form = urllib.parse.parse_qsl(query)
    (さらに…)
  • Apache2 MPM-ITK 環境下の CGI で sudo するときの注意点

    Apache2 MPM-ITK 環境下の CGI で sudo するときの注意点

    TL; DR

    /etc/apache2/conf-available/security.conf に次を追加します。root の UID: 0, GID: 0 を範囲に含めています。

    <IfModule mpm_itk_module>
        LimitUIDRange 0 6000
        LimitGIDRange 0 6000
    </IfModule>

    (さらに…)

  • sudo でファイルへのテキスト書き込み

    sudo でファイルへのテキスト書き込み

    TL; DR

    パイプで sudo tee しましょう。

    $ cat <<EOS | sudo tee /root/script.txt
    foo
    bar
    hoge
    EOS

    (さらに…)