豆腐食べたい

プログラミングとかその他もろもろとかの技術の摂取録。あと豆腐食べたい。

ECCS2012に頑張ってssh接続する

以下、自分の大学のサークルの部誌に書いた記事を転載です。webで広く公開した方が現在の阿鼻叫喚状態を少し和らげることができるようなので。。今の惨状がECCS2013では改善されることを願います・・・

------

ECCS2012へssh接続

 こんばんは、そうでない人はこんにちは、nullpo_headです。今までTSGに対して一般記事という貢献をしたことがなかったので、今回クリスマスコンパ号にてさせていただこうという運びに自分で勝手になっております。昨年度などはこの号の部誌には一般記事はなかったようで、急がなくても別に他の号で書けばいいじゃないかというお叱りを皆様から受ける気がしますが、今回のネタの賞味期限がだいぶ近いのでこの号に投稿することにしました。
 この記事では今年度iMacの導入とともにECCSから消えてしまったssh接続(今年度からですよね?)を、ECCS2012でもなんとかかんとか繋ぐ方法をご紹介いたします。方々からの阿鼻叫喚に応えECCS2013では恐らくsshが復活するとは思いますが、今回の方法を使えば(残り数ヶ月もない)ECCS2012でもあのくっそ重たいリモートアクセスからおさらばできるはずです。

今回の方法の概要

  • 学生にwebスペースを提供するべく東大がやってる「東京大学情報発信実験」では、webサーバへのssh接続が提供されててそれは学内のLAN内にあるよ
  • ECCSの各々の端末ではファイアウォールが一切働いてなくて、それはリモートアクセス用の端末でも同じだよ
  • だから非rootでsshdを立ち上げられればsshはwebサーバ経由でECCSまで到達できるよ

以上3点がまぁ今回の話の肝でして、あぁそうか!と納得された方はもう多分この記事を読み続ける必要はありません。以下では以上3点についての詳細をずらずらと説明させていただきます。



・「東京大学情報発信実験」へのssh接続

 僕が言っている情報発信実験とはこれ
ユーザ情報発信用WWWサーバによる情報発信実験
http://www.ecc.u-tokyo.ac.jp/guide/web/web1.html
のことです。大学がよくやってるアレです。仕様には書いてありませんが実はphpも動いたりして広告なしの無料webスペースとして実はそれなりに優秀だったりします。自分もALESSの実験でちょっと使わせていただいています。ここのアカウントを取得すると、学生ID@user.ecc.u-tokyo.ac.jpへのssh接続が可能になります。夏学期の情報の授業で僕たちは半強制的に取得させられました。この時点で東大の学内LANにあるサーバへのssh接続を獲得したことになるので既に割ともう便利になるのではないでしょうか。学内からしか見れないサイトとか論文とか見れるのでは。

・ECCSのiMacの端末名を調べて、アクセス

 ECCS内の各iMacにはファイアウォール等が2012年現在かかっておりません。(これ、色々と実験できてネットワークの勉強となるので非っ常にありがたいです。)ですから、あるiMacでサーバを立ち上げれば、その他の端末から問題なく接続することができます。アクセスするためにはiMacの端末名が必要になりますが、これを知る方法は簡単で、シェルを起動したときに

ci190021m:~ (学生ID)$

のように表示されている”ci[0-9]+m”が端末名となります。この端末でwebサーバを8080ポートで起動したとすれば、http://ci190021m.ecc.u-tokyo.ac.jp:8080/ でアクセスできることになります。

以上2点から、あのクッソ鈍いリモートアクセスをしぶしぶ立ち上げ、ぶーぶー言いながらもその端末で一度sshサーバを立ち上げてしまえば、情報発信実験のwebサーバにsshをつないで、さらにそこからsshでciホニャラmへと二段sshすれば、外部のPCからECCS端末へとアクセスできることがお分かりになったかと思います。

 最後の障壁はsshのサーバプログラム、sshdを管理者権限のない僕らのユーザで立ち上げることとなります。Unix系OSに慣れ親しんでいる方はそんなの基本だから説明するようなことではないだろうって感じの蔑みの目をお向けになるかもしれませんが、僕の場合はそうではなかったので説明します。。

・非rootでsshdを立ち上げる

 sshdはECCSへもインストールされています。しかしそれを起動しようとするには2つの障壁があります。一つ目は0~1023番のポートはroot権限がないと開けないこと、2つ目は/etc/hosts.allowとdenyにて、大学の管理者しかsshdにアクセスできないよう設定されていることです。一つ目はもちろん適当なでかい番号のポートでsshdを起動すればおkです。二つ目はどうしようもありません。ということで二つ目をどうにかできるようsshdを自分でコンパイルし直します。が、sshdの話をするのが本筋ではないのでここではそれは後ろの方の小さい文字にまわします。sshdをとりあえず適切なオプションでビルドするシェルスクリプトを書いたので

$ /home07/1298296540/Public/sshdinstall.sh

で~/sshdにsshdがインストールされます。あとは適当に公開鍵や秘密の鍵の設定をする人はしてください。これですべての準備は終わりました。

○では接続しましょう

リモートアクセスでECCSにつないでください。そしてシェルを起動して、今自分が使っている端末の端末名を調べましょう。ここではcm10003mとかそんな感じだったとします(実際にどんな名前になったかはちょっとうろ覚えです)
あとは

$ /home*/(自分の学生ID)/sshd/sbin/sshd –d –p23232

とすれば、その端末でsshdが23232番ポートで起動します。(※sshdは絶対パスで起動する必要があるのです)
ではwebサーバを経由してここまでつなげましょう。ECCSへssh接続したい自分のPCから

$ ssh (自分の学生ID)@user.ecc.u-toyko.ac.jp

で一旦情報発信実験のサーバにつなぎます。で、その中のシェルから

$ ssh cm10003m.ecc.u-tokyo.ac.jp –p 23232

でリモートECCS上で起動しているsshdへ更に接続しましょう!これで軽快・快適なCUI環境が手に入ります。TCPプロクシとか使えば、自分のPCのブラウザから学内向けのリソースへもアクセスできますね。論文とか。
(なお追記として、自分はパスワードによるログインができなかったので、公開鍵と秘密鍵を設定してログインする方法にしました。そうするとログインできました。sshdの設定の問題なのかもしれませんが、自分は詳しくないのでなんとも・・・)

以上でどうしてもECCS2012を外部から使う必要がある!!!しかしリモートアクセスじゃ重すぎて何もできねぇよ!!!!というときにsshでハッピーな生活を送れるようになるはずです。では、メリークリスマス!!!



---

以下、sshdについて

先にお話しした2番目の障壁を越える方法についてです
TSGerの皆様にお話しすると、釈迦に説法って感じになってしまうような場合も多々あるとは思いますし、しかも自分はよく理解している訳ではないということでかなり恐縮なんですが、/etc/hosts.allow, denyで設定されている設定はTCP wrapperに関する設定です。これを使えばいろいろなアプリケーションのアクセス管理を一元的にできるようになります。sshdも、ビルドする際に通常 –with-tcp-wrappersオプションをつけて、このアクセス制御の恩恵を受けられるようにするわけです。が、今回の場合はこの/etcホニャララがrootユーザでしか設定できないために却って邪魔となっていましたので、このオプション無しにコンパイルし直せばいいのだということになります。そして、非rootでもきちんとmakeができるよう、prefixオプションをつけて自分のホームディレクトリにインストールするわけですね。ですから最終的に単に $./configure -prefix=ホニャララ&& make && make install みたいな感じにすれば、目的通りのsshdが得られることになります

PHPでWrenchによるWebSocketを使ってチャットデモを作る

タイトルの通りですけど、PHPでWebSocketを使ったチャットデモを作りました。この記事ではそのために自分が取った生まれたての小鹿のようにたどたどしい行動の記録を残そうというわけであります。

先にやったことをまとめときますと、PHP製のWebSokcetサーバーであるWrenchを用いて、ユーザー名とチャットルーム名を入力してチャットができるタイプのチャットアプリケーションを作りました。

そして先に断っときますと、自分は以前、というか前の記事でメール送信フォームを作ったのがほぼ初のweb開発体験&PHP体験ですので、これはつまり2度目のPHP体験です。ですから、きちんとPHPを触ってきた方には一体何書いとんじゃこいつって感じの死ぬ前の牡鹿のように弱々しい行動をとっていたりするかもしれませんが、そのときはコメント欄で指摘してくださるか、そっと心の中でだけ死ねやハゲとかつぶやいてくだされば幸いです。

では以下やったことです。

1.Wrenchの導入 〜 デモを動かす

1.1-Wrenchについて

まず、PHPでWebSocektを実現するためにWrench (https://github.com/varspool/Wrench) を用いました。これはvarspool氏が「DO WHAT THE FUCK YOU WANT TO」パブリックライセンスに基づいて公開していらっしゃるプロダクトです。PHPでWebSocketをいじった方々のブログを見るとlemmingzshadow氏のphp-websocekt (https://github.com/lemmingzshadow/php-websocket) を紹介していらっしゃる方々が多いようですが、このphp-websocketはwrenchからforkされたもののようでしたので、事情はよく把握できていないのですがこちらのものを使ってみることにしました。(あと被forkとstarの数も多かったので・・・)

ちなみにphpでwebsocketを使えるようにするためのものとしてはこの他にもmazhack氏のphp-websocket (https://github.com/mazhack/php-websocket) と nicokaiser氏の php-websocket (https://github.com/nicokaiser/php-websocket) がありまして、それぞれ有名なようです。そしてWrenchのREADMEのAuthors節では

Authors
The original maintainer and author was @nicokaiser. Plentiful improvements were contributed by @lemmingzshadow and @mazhack. Parts of the Socket class were written by Moritz Wutz. The server is licensed under the WTFPL, a free software compatible license.

となっていまして、複雑に入り組んでいて正直どれを使うのが一番メジャーなのかわかりません。詳しい方がいたら教えていただきたいですね。。
また、WrenchはREADMEにも書いてあるとおりRFC 6455に対応していますので、各最新版のブラウザならだいたい対応していくはずです。WebSocketのバージョンについて詳しくは
WebSocket - Wikipedia
などでどうぞ

1.2-ダウンロード

まずは、WebSocektが動く様を同梱されているデモで確かめます
https://github.com/varspool/Wrench/ からDownloadを経由してZipをダウンロードするかもしくはgit cloneでもするとWrenchのデータが入手できると思います。すると中にディレクトリdoc, examples, libがあります。まずはapacheなりなんなりのサーバー公開ディレクトリに「Wrench」ディレクトリでも作り、そのなかにそのlibとexamplesをコピーします。*1

1.3-動くように改変

次にexamplesの中のserver.phpを環境に最適化しましょう。Server.phpの以下の部分("?php"ははてなのシンタックスハイライトを有効にするために書いたもので、以下の部分は先頭にはあるわけではないです)

<?php
$server = new \Wrench\Server('ws://localhost:8000/', array(
    'allowed_origins'            => array(
        'mysite.localhost'
    ),

の"mysite.localhost"を自分のサーバーに適したものに変更してください。私は単にlocalhostに変更しました。8000ポートがすでに使われている場合はこれも変更してもいいのですが、その場合クライアント側のjavascriptにもポートの変更が必要になります。

さて、普通なら改変はこんなものですむかな〜とか思うわけですけど、現実は厳しい、これからそれなりに改変が必要となります。初期状態じゃechoデモは全然動きません。なんでこんなことになったんだろう。

やることは ・coffeescriptを動くようにする ・jquery,json2.jsを設置する ・いくつかの不整合を修正する
の3点です。素直に他ブログで紹介されてた通りのWebSocketライブラリ導入すれば良かったとかは生まれたての小鹿なので考えつきませんでした。

1.3.1-coffeescriptを動くようにする

まずexamplesの中のcoffeescriptディレクトリを開いた中にあるindex.htmlをエディタで開きますと

<script src="lib/coffeescript/jsmaker.php?f=client.coffee"></script>

という部分があり、引数で渡したcoffeescriptをjavascriptに変換してくれることになっているようですが、社会は厳しい、そんなPHPスクリプトは同梱されてません。そこですでにcoffeescriptの開発環境を導入済みの方はそのようなPHPスクリプトを用意するか、coffeeをコンパイルしてjsにしたものを直接設置しましょう。(私はこれでやりましたがcoffeeの導入で色々あって苦労しました・・・)
coffeeを導入していない方で、デモを動かせればいいから導入もしなくていいやって方は

CoffeeScriptはインストールしなくてもブラウザ上で実行できるよという話 - ariyasacca

さんを参考にしてcoffeescriptをブラウザで直接動かせるようにしましょう。具体的には上記の部分を

<script type="text/coffeescript" src="coffee/client.coffee"></script>
<script type="text/javascript" src="http://jashkenas.github.com/coffee-script/extras/coffee-script.js"></script> 

に修正すれば万事OKです。(2012-10-1現在)

1.3.2-jquery,json2.jsを設置する

次にindex.htmlではjqueryとjson2.jsを要求していますが、やはりデモには同梱されていません。

http://jquery.com/
および
https://github.com/douglascrockford/JSON-js
からそれぞれ入手して、jsディレクトリを作ってそこに配置しましょう。

1.3.3-不整合を修正する

ラストです。これをすませばecho demoが動くようになります。
と言ってもやることは簡単、coffeeディレクトリの中にあるclient.coffeeを開いて

    serverUrl = 'ws://127.0.0.1:8000/demo

となっているところを

    serverUrl = 'ws://127.0.0.1:8000/echo

に変更しましょう。
必要な修正は以上です。

1.4-echoデモを動かす

では早速echoデモを動かしましょう
まずはexamplesディレクトリでターミナルを開いたら

    $php server.php

としてWebSocektサーバーを開始してください。

    info: Server Initialized

と表示されれば成功です。
これが表示されたならwebブラウザで http://localhost/Wrench/examples/coffeescript/index.html にアクセスしましょう(URLを環境に合わせて適宜変更してください)
下の画像のようにconnectedとでれば成功です。適当にメッセージを送ってみましょう

f:id:tofu_head:20121001185323p:plain

これで無事にPHPでWrenchによるWebSocketが動きました

2.チャットアプリケーションを作る

では同梱のShinyなデモの話は終わり、うって変わって怪しいチャットデモを作る話です。同時に自作アプリケーションを作るレクチャーにもなるかもしれません。ならないかもしれません。

2.1-自作アプリケーションをWrenchサーバーに登録する

まずは先ほどのデモの段階で作成したexamplesを複製し、"chatdemo"にでもリネームしてください。ここにこのデモをベースとしてチャットデモを作ります。

次にexamplesを抜け、lib/Wrench/Applicationを見てください。Application.phpとEchoApplication.phpがあるはずです。すべてのWrenchサーバー用のアプリケーションは、このうちのひとつApplication.phpを継承する単一のクラスで表現されます。ではこのEchoApplication.phpを複製し、ChatApplication.phpにでもリネームしてください。そして中身をエディタで開き、EchoApplicationクラスをChatApplicationクラスに書き換えましょう。これで自作アプリケーションをWrenchに登録する準備ができました。

次に先ほど作ったchatdemoディレクトリを作り、server.phpを開きます。コメント部分を無視すると下記のようになっていますね

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

ini_set('display_errors', 1);
error_reporting(E_ALL);

require(__DIR__ . '/../lib/SplClassLoader.php');

$classLoader = new SplClassLoader('Wrench', __DIR__ . '/../lib');
$classLoader->register();

$server = new \Wrench\Server('ws://localhost:8000/', array(
    'allowed_origins'            => array(
        'localhost'
    ),
));

$server->registerApplication('echo', new \Wrench\Application\EchoApplication());
$server->run();

これの

<?php
$server->registerApplication('echo', new \Wrench\Application\EchoApplication());

<?php
$server->registerApplication('chat', new \Wrench\Application\ChatApplication());

に書き換えましょう。このregisterApplicationメソッドこそが、Wrenchにアプリケーションを登録するためのメソッドです。
登録できたかどうかブラウザからアクセスして確かめましょう。chatdemoの配下のclient.coffeeを開き、

serverUrl = 'ws://127.0.0.1:8000/chat'

へと書き換えたなら、 echoデモの時と同じようにchatdemoのサーバーを起動し、http://localhost/Wrench/chatdemo/coffeescript/index.htmlへとアクセスしましょう。EchoApplicationがきちんと動いていたら、アプリケーションの登録が成功したということです。

ところで自分は、このregisterApplicationに渡している\Wrench\Application\ホニャララと言う識別子に面食らった情報弱者なのですが、これはphpにおける名前空間の表し方のようです。ちなみに先頭の\はグローバル名前空間を表します。名前空間の詳細についてはマニュアルを参照するのが一番いい気がします。またこれもちなみになんですが、マニュアルを読めばわかるようにphp名前空間はディレクトリ構造に対応する必要はありません。これは、spl_autoload_register関数で、名前空間で修飾されたクラス名を渡すとそのクラスをrequireするような関数を登録することで、名前空間の解決を行うからです。phpでは名前空間からどのようにクラスをロードするかの仕組みがユーザーに丸なげされてるわけですね。でもまぁ結局はディレクトリに対応した名前空間の仕組みが慣れ親しまれているからか、Wrenchのデモでは、ディレクトリ&ファイル名と名前空間を対応付けるSplClassLoaderクラスを用いて名前空間を解決しています。

2.1-アプリケーションを作り変える

すべての基本はApplicationクラスを継承したこのChatApplication.phpです。
何ができるかはドキュメントとApplication.phpを覗いてみましょう・・・と言いたいところですが、どちらもあまり整備されていないようですのでコアのConnection.phpを見たほうがいいようです。現在

onConnect, onDisconnect, onData, onBinaryData

メソッドを実装できるようです。何が起こるかはメソッド名から推測できるでしょう。
こうしてWebSocketを使った自作のアプリケーションができるというわけです。

2.2-チャットデモを作る

さて、では早速ChatApplication.phpを書き換えましょう。といってもチャットの作り方講座なんてするわけではないので、タイトル通りの作ったものを垂れ流します。

まずはChatApplication.phpは下のようになりました

<?php

namespace Wrench\Application;

use Wrench\Application\Application;
use Wrench\Application\ChatApplication\ChatRoom;
use Wrench\Application\ChatApplication\UserNotFoundException;
use Wrench\Application\ChatApplication\ConnectionAlreadyEstablishedException;

class ChatApplication extends Application
{
    private $clienId2room = array(); //clientとチャットルームのインスタンスのテーブル
    private $roomName2room = array(); //チャットルームの名前とその名前のチャットルームのインスタンスのテーブル
    
    private function sendError($errorMessage, $client) {
        $client->send(json_encode(array("type" => "error", "message" => $errorMessage)));
    }        
    
    // $connection: connection型    
    public function onDisconnect($connection) {
        if (isset($this->clienId2room[$connection->getId()])) {
            $this->clienId2room[$connection->getId()]->logoutUser($connection);
        }
    }

    // $client: connection型
    public function onData($json, $client)
    {
        $data = json_decode($json, true);
        if ($data === NULL || !(isset($data['type'])) || (!isset($this->clienId2room[$client->getId()]) && $data['type'] !== "participate")){
            $this->sendError("不正なデータを受信しました:無意味なメッセージ", $client);
            return;
        }

        try {
            switch ($data['type']) {
            case "participate":
                
                if (!isset($data['roomId']) || !isset($data['userId'])) {
                    $this->sendError("ログインに必要な情報がたりません", $client);
                    return;
                }
                $data['roomId'] = trim($data['roomId']);
                $data['userId'] = trim($data['userId']);
                if (!isset($this->roomName2room[$data['roomId']])) {
                    $this->roomName2room[$data['roomId']] = new ChatRoom();
                }
                $this->clienId2room[$client->getId()] = $this->roomName2room[$data['roomId']];
                $this->clienId2room[$client->getId()]->loginUser($client, $data['userId']);
                    
                break;
            case "message":
                if (!isset($data['body'])) {
                    $this->sendError("不正なデータを受信しました:内容のないチャット送信", $client);
                    return;
                }
                $this->clienId2room[$client->getId()]->sendMessage($client, $data['body']);
                break;
            case "logout":
                $this->clienId2room[$client->getId()]->logoutUser($client);
                unset($this->clienId2room[$client->getId()]);
                break;
                
                
            default:
                sendError("不正なデータを受信しました:無効な種類のメッセージ", $client);
                return;
            }

        } catch (UserNotFoundException $e) {
            $this->sendError($e->getMessage(), $client);
            return;
        } catch (ConnectionAlreadyEstablishedException $e) {
            $this->sendError($e->getMessage(), $client);
            return;
        }




    }
}

次にこの中でロードされているChatRoom.phpはこんな感じです

<?php 

namespace Wrench\Application\ChatApplication;

class ChatRoom {

    private $userTable = array();

    public function loginUser($client, $username) {
        if (isset($this->userTable[$client->getId()])) {
            throw new ConnectionAlreadyEstablishedException();
        }

        $this->userTable[$client->getId()]['username'] = $username;
        $this->userTable[$client->getId()]['client'] = $client;

        $loginNotification = json_encode(array("type" => "login notify", "username" => $username));
        $usernames = array();
        foreach ($this->userTable as $user) {
            array_push($usernames, ($user['username']));
            if ($user['client'] !== $client) {
                $user['client']->send($loginNotification);
            }
        }
        $client->send(json_encode(array("type" => "login accept", "usernames" => $usernames)));
        return true;

    }

    public function logoutUser($client) {
        if (!isset($this->userTable[$client->getId()])) {
            throw new UserNotFoundException();
        }
        $logoutNotification = json_encode(array("type" => "logout notify", "username" => $this->userTable[$client->getId()]['username']));
        unset($this->userTable[$client->getId()]);
        foreach ($this->userTable as $user) {
            $user['client']->send($logoutNotification);
        }
        return true;
    }

    public function sendMessage($client, $message) {
        if (!isset($this->userTable[$client->getId()])) {
            throw new UserNotFoundException();
        }
        $data = json_encode(array("type" => "message", "username" => $this->userTable[$client->getId()]['username'], "body" => $message));
        foreach($this->userTable as $user) {
            $user['client']->send($data);
        }
        return true;
    }
}

ソースをこれ以上いちいち書き上げるわけにもいかないのでgithubに公開しておきました。あとindex.html, client.coffeeも改変され、2,3個の例外クラスが追加されています。

https://github.com/nullpo-head/Wrench-ChatApplicationDemo

ダウンロードして公開フォルダにでも設置してくれれば動くようになるはずです。

2.3-注意点

チャットデモを作っていて気づいたことや学んだことはphp初心者erなので星の数の星の数乗ほどあるわけですが、その中で一つ書き留めていた方がいいかなということがあります。それは、FatalErrorがプログラム中で一回でも出れば、Wrench自体が停止してサーバーが落ちてしまうことです。
apache上でPHPを動かしているときは、ユーザーがアクセスしてきてエラーが出たとしてもそのユーザーのページがきちんと表示されないくらいですみますが、Wrenchだとサーバー自体が落っこちて、以後再起動させるまでアクセスできなくなります。適切にエラーハンドリングをしなければいけませんね。ちなみに私のチャットデモではもちろんそんなことはできていませんからご安心ください。

2.4-動画

チャットデモはもちろんgithubからダウンロードすれば動かせるわけですけど、そんなのめんどくさいって方のためと自分の自己満足のために動作動画を上げておきます。ブラウザでしっかりプッシュがされている様がわかるのではないでしょうか。




*1:WebSocketサーバ本体であるlibは公開ディレクトリに本来は置くべきではありませんし、examplesの中のserver.phpも同様ですが、便宜のために一つにまとめておくよう説明しています。もちろんこのままwebに公開したりしないでくださいね

Android標準ブラウザでjpgが表示されない(原因はCMYKでした)

先日いろいろなことが重なりに重なって、知人のwebサイトを作ることとなりました。

画像やらなんやらは知人の知人のデザイナーさんが担当してくださることになったので、素材に関してはめちゃくちゃに綺麗になりました。(作りは別ですよ、もちろん。私が担当ですからね!)で、ちゃかちゃかっと色々作って、PCの各ブラウザでレイアウト崩れがないことも確認してひとまず完成。

と思ったんですが、そこで知人から「なんかケータイからだと画像が一切表示されないんだけど」との連絡が・・・。

しかし普段使ってる自分のケータイ(HTC EVO3D, Opera mobile)からは何の問題もなく表示されていたので困惑していろいろ試してみると、androidの標準ブラウザでは確かに画像が真っ白になる現象を確認できました。(ちなみにfirefoxでもきちんと表示されてました)ググッてもそれらしき情報がなくて困ってたんですが、以下のページを発見。

IE8 jpegが見れない

http://onlineconsultant.jp/pukiwiki/?IE8%20Jpeg%E3%81%8C%E8%A6%8B%E3%82%8C%E3%81%AA%E3%81%84

 

もしかしてと思って画像を確認してみると、問題の画像もプロファイルがCMYKとなっていました。ためしにRGBに適当に直したものを上げてみると見事に表示。どうやらこれが原因だったようです。。

Androidの標準ブラウザ、少なくとも2.3以下のバージョンでは、同様の問題があると思われます。似たような事態があれば、皆様もご注意くださいませ

Ubuntuでログイン不可のユーザーを作る/システムユーザーを作る

Apachemysqlなんかをインストールしたときにできる、セキュリティのために一つのアプリケーションが占有して使うようになるあのアカウントを手動で作ってみようとしてみた。このようなアカウントはシステムユーザーと呼ばれることが多いらしく、以下のようにして作成する

 

#adduser --system username

 

これで本来の目的は達せられる

が。

この方法のことを知らなかった当初はまず一般ユーザーを作成してから、それをログイン不可にする方向で考えていた。というか、一般ユーザーとシステムユーザーの間に、特別な区別があると思ってなかった。(無知)[1]

まぁつまり

 

#useradd[2] -M -s /bin/false username

 

を実行したわけである。(※ちなみにdebian系ではuseraddとadduserという別の2つのコマンドがある。違いなんかはmanなりgoogleなりで確認してくださいませ)

で、実はこれでもCUIシェル上では期待通りの動作をする。だがubuntuを起動したときにでるGUIログイン画面のユーザー一覧では、このとき作成したユーザーがきちんとリストアップされてしまう。(もちろんログインはできない) しかしもし、この画面にユーザーが出てこないように設定を変更することができるなら、一般ユーザーを作る方法でも同じ目的が達成できるだろう。むしろログイン不可のユーザーを一般ユーザーとして作りたいという場面が出てきたらこうするしかないんじゃないか。

ubuntu11.04以降のunityが乗っている環境では

/etc/lightdm/users.conf

を編集して

hidden-users=nobody nobody4 noaccess

となっている行(デフォルトではこうなってた)に、目的のユーザー名を追加すればいい。

が。(2回目)

どうやらubuntu11.10(筆者の環境)と12.04の一部の環境には、この設定をうまく反映できないバグがあるらしく、(https://bugs.launchpad.net/ubuntu/+source/lightdm/+bug/857651) 2012年8月13日現在、この設定をいじってもやはりログイン画面にはログイン不可のユーザーの名前が表示されてしまう。あらら

ということでこのようなユーザーを作りたければ、とりあえず今現在はシステムユーザーとして作成しましょうというとこでしょうかね。

 


[1] -Mオプション:ubuntuの日本語manには載っていないが、ホームディレクトリを使わないようにするオプション。間違った構文のuseraddを叩けば出てくるオプション一覧にはきちんと載っている。(例えば$useradd -hogeとかしてみたら出てくる)

-sオプション:ログインしたときに起動するシェルを指定する。ここに/bin/falseまたは/bin/nologinを指定することでログインできないユーザーを作れる

[2] debian系のディストリビューションではuidが100〜1000のユーザーをシステムユーザーとして扱うらしい

 

 

Linuxでユーザーをグループに追加する

結論から言って

 

gpasswd -a username groupname

または

usermod -a -G groupname usernamae

 

を使うべき。両者とも現在ユーザーが入っているグループに加え、新しいグループにもユーザーを追加する。

時々紹介されてる

usermod -G groupname username

なんかは、もとから入っていたグループを全削除して上書きしてしまうのでただ追加したいときは避けましょうと。-aオプションが大事です。

 

Apache2がroot権限で動いてる?

今年5月にubuntuをインストールして以来linux使い見習いとして生きてきたのだが、まだサーバー系のものは何も使ったことがなかった。んで、先日大学関係でPCにapacheやらmysqlやらをインストールすると少し幸せになれる場面があったので、いい機会だと思ってaptでインストールしてみた。

 

が、一つ気になることが発生。psで確かめるとapacheのプロセスの一つがroot権限で動いてる

 

$ ps aux | grep apache2
root      1253  0.0  0.1  36012  7788 ?        Ss   Aug11   0:00 /usr/sbin/apache2 -k start
www-data  1268  0.0  0.0  36036  3932 ?        S    Aug11   0:00 /usr/sbin/apache2 -k start
www-data  1269  0.0  0.0  36036  3932 ?        S    Aug11   0:00 /usr/sbin/apache2 -k start
www-data  1270  0.0  0.0  36036  3932 ?        S    Aug11   0:00 /usr/sbin/apache2 -k start
www-data  1271  0.0  0.0  36036  3932 ?        S    Aug11   0:00 /usr/sbin/apache2 -k start
www-data  1272  0.0  0.0  36036  3932 ?        S    Aug11   0:00 /usr/sbin/apache2 -k start

 

で、色々とググッてapacheの設定を見てみるも、実行ユーザーの設定はちゃんとwww-dataになってる・・・

 

で、更に調べた結果、次の情報を発見

 

Apacheのプロセス管理

 

これによれば、linuxでは0〜1230番のwell-knownポートはroot権限がないと開くことができないらしい。なのでその範囲のポートをapacheが使用する際には、そのポートの待受のためのプロセスをひとつ生成して待機し、実際の処理はより低い権限を持つプロセスに投げるようだ。納得。

 

 

ブログでもはじめようかなーなんて

昔偉い人は言いました、アウトプットはインプット並に大事であると。

ま、そんなわけで、世界中の素晴らしい文献や偉大なる方々がくださったキラキラと輝く素晴らしいインプット達を、ほんとにこれから吐き出されたのかよと疑いたくなるような小汚いアウトプットへと変換していく作業を、このブログで始めようと思います。

当分はこの小汚いアウトプットたちをとりあえず目を当てられる程度のレベルまで引き上げられるよう精進するのが目標。

では、よろしくお願いします。