人間とウェブの未来

「ウェブの歴史は人類の歴史の繰り返し」という観点から色々勉強しています。

プロセスのオーナ情報をTCPオプションヘッダに書き込むに至った背景とアプローチの補足

hb.matsumoto-r.jp

上記のリンクの昨日書いた記事のスコープや前提、及び、ユースケースがわかりにくかったので、以下にそれらをもう少し詳細に書こうと思います。コメントやアドバイスをすでに頂いた方はありがとうございます。

まず、この手法にいたった課題について説明してきます。 これまでWebホスティングサービス(レンタルサーバ)のように、WordPressのようなWebアプリケーションを配置するための領域(一般ユーザで利用するテナント)を貸し出すようなプラットフォームサービスにおいて、低価格化を実現するために単一のサーバにどれだけ高集積にテナントを収容するかという検討がなされてきました。

そんな中、テナント単位でプロセスを用意したり、IPアドレスをはじめとした個別リソースの紐付けを極力行わずに、共有のデータベースミドルウェアを使い、できるだけリソースを共有するような方式、例えばApacheのVirtualHostなどが採用されてきました。これによって、メモリ32GBぐらいのサーバに数万から10万ホストぐらいまで収容できるようになりました。プライベートIPアドレスや静的な情報を各テナントに紐付けると、10万ホスト収容の場合はそれ相当の数の静的な設定が必要になり、例えばApacheのVirtualHostの場合だと、それだけで4GBぐらいのworkerプレセスになります。そうなると、clone()に1秒ぐらいかかったりと大幅にCGIなどの性能低下やリソース逼迫が生じることとなり、そこも動的な設定にするなどの提案がなされてきました。

一方、そのようにリソースを共有するような方式をとると、プロセスの権限をテナントへのリクエストに応じていかに適切かつ動的に分離しつつ、Webアプリケーションの実行性能を落とさないかという点が課題になります。そこで、スレッド単位で権限を分離する方式などいくつか提案されてきました。しかし、そのような状況でも、Webサーバが持つ脆弱性やシステムにおける権限分離の穴などによって、特定のテナントから別のテナントのファイルを読み込めたりする問題がこれまでに何度も繰り返されてきました。

例えば、数年前にあったのは、とあるテナントのWebアプリが脆弱性なのによってのっとられたとします。その上で、そのテナントからシンボリックリンクを読みたい別のテナントのスクリプトファイルに対して.htmlなどに張り替え、Webサーバからは単に静的ファイルとして表示させるようにし、Webサーバのシンボリックリンクチェックがシステムコールレベルでアトミックに行われないこととマルチプロセス(スレッド)であることを利用して、特定テナントから何度か試行することでファイルを覗き見るような所謂TOCTOUな手法によってファイルを覗き見されるようなことがありました。また、ホスティング環境で提供しているライブラリやApacheにロードしているモジュールの脆弱性などによって、意図せず同一OS上でread権限を持つ複数のファイルの閲覧ができる問題などがありました。そして、その漏れた情報がどこかのサイトで共有され、とあるタイミングで特定のテナントからデータを抜かれるといった事象もありました。現在ではそれらを解決するための権限分離手法を沢山提案・実装してきたので、随分と強固になってきたと思いますが、依然として日々様々なインシデントが生じています。

各テナントはサービス利用者に一般ユーサで利用できる領域を貸し出しており、そこで公開されていWebアプリケーションをプラットフォームサービス事業者としてメンテするわけにはいかず、各テナントには脆弱性があり、そこが踏み台にされる前提で、そうなってもセキュリティ上の問題が起きないようなプラットフォーム基盤を構築する必要があります。また、WordPressをはじめとした、スクリプトファイルにかかれているID/PASSを抜こうという攻撃が特に多く、踏み台となったテナントから各テナントのID/PASSの認証情報が仮に漏れたとしても、それぞれのテナントを乗っ取らない限りにおいてはその認証情報を使えないようにしたいという多層防御の仕組みを検討していました。

そこで要件をまとめると、

  • テナントが収容されるサーバがフロントにあり、テナントから利用する共有データベースが後ろのプライベートネットワークで繋がっているようなホスティングシステム構成において
  • 接続元のIPアドレスが共有され、各テナントのWebアプリケーションが利用するデータベースミドルウェアを共有し、OSの各種リソースを共有するような低価格で提供される高集積マルチテナント環境でも
  • 仮に各テナントの各種スクリプトが利用するデータベースのID/PASSのような認証情報がシステムの脆弱性によって漏れたとしても
  • 収容サーバの特定のプロセスからのデータベースアクセスでない限りID/PASS認証を通さず
  • 収容サーバの接続元プロセスがユーザランドで正しい接続元プロセスかのように詐称できない(自身を証明する識別子を扱わせない)
  • IPアドレスベースよりもより粒度の細かいプロセスベースでの接続元プロセスの検証方式

を考えた結果、昨日に書いた記事のアプローチに繋がりました。

また、実装についてもeBPFを検討していたのですが、TCPオプションヘッダをいじるようなeBPFの使い方が以下の論文ぐらいで、これをためそうとしたところ論文に記載されているフックポイントなどはまだ見当たらず、論文がその提案であるということろで止まっており、今の所公開されているLinuxでは実現ができなさそう?に思ったのでカーネルモジュールで実装しています。

もしここについても、eBPFでこんなふうにできるよ、というのがあればご教示いただけると幸いです。

新たにカーネルでTCPオプションヘッダに書き込んだ情報をTCPセッション確立時にユーザランドでどう取得すべきか

追記:2020-06-05

このエントリの背景が雑だったので以下に補足記事を書きました。先にこちらに目を通していただいた方が良いかもしれません。

https://hb.matsumoto-r.jp/entry/2020/06/05/110709


speakerdeck.com

今tcprivというソフトウェアを開発しているのだが、細かい内容については上記のスライドを見てもらうとして、やりたいことは、TCPセッションを確立するプロセスのオーナ情報を接続先のプロセスで透過的に検証するという処理である。

github.com

以下ではその実装の概要を紹介しつつ、今検討していることについてお話したい。

接続元プロセスは一般ユーザを想定しており、脆弱性などによって悪意のあるユーザにのっとられることもありうるし、とあるプロセスが利用する認証情報も漏れることがあることを想定している。 しかし、情報が漏れたとしても、適切なオーナからとそのオーナに紐付いた認証情報を利用しないと、リモート先のプロセスで認証を拒否出来るようなしくみを考えている。 いわゆる多要素認証のサーバ間通信版と考えてもよいのかもしれない。

例えば、とあるマルチテナントシステム間連携において、とあるプロセスが利用しているDBのID/PASS、あるいは、トークンが漏れた時に、それらの認証情報を使って、同システム内の別のオーナのプロセスがDBに接続を試みた場合、オーナが違うのでID/PASSやトークンが一致していても認証を通さない。 マルチテナント型のマネージドシステム内で、プロセスレベルで隔離はされているがOS上に共存しているプロセスや、同じIPアドレスが割り当てられているコンテナなどを接続元として想像すると良いかもしれない。 あとはレンタルサーバの例えばWordPress的なWebアプリケーションとか。 オーナや権限で分離されている沢山のWordPressにおいて、たとえはとあるWordPressのDB接続のためのID/PASSが漏れたとしても、そのID/PASSを使うべきプロセスオーナからのみしかそのID/PASSの認証を接続先のDBで認証しない、というようなケースである。

また、接続時にオーナの情報を詐称できないように、その一連のオーナに関する情報をカーネル側で透過的に実現している。 すなわち、認証情報がもれた当該プロセスからのみのアクセスや脆弱性で乗ったられたプロセスからのみしか、認証情報を利用して接続できなくなるので、被害範囲を局所化できる。

オーナ情報の付与を透過的にカーネルで実現するために、カーネル内のNetfilterのフックポイントを利用して、カーネルモジュールによりカーネルのTCPスタック前後でTCPオプションヘッダにオーナ情報を書き込む領域を定義し、書き込んでいる。

static int __init tcpriv_init(void)
{
  printk(KERN_INFO TCPRIV_INFO "open\n");
  printk(KERN_INFO TCPRIV_INFO "An Access Control Architecture Separating Privilege Transparently via TCP Connection "
                               "Based on Process Information\n");

  nfho_in.hook = hook_local_in_func;
  nfho_in.hooknum = NF_INET_LOCAL_IN;
  nfho_in.pf = PF_INET;
  nfho_in.priority = NF_IP_PRI_FIRST;

  nf_register_net_hook(&init_net, &nfho_in);

  nfho_out.hook = hook_local_out_func;
  nfho_out.hooknum = NF_INET_LOCAL_OUT;
  nfho_out.pf = PF_INET;
  nfho_out.priority = NF_IP_PRI_FIRST;

  nf_register_net_hook(&init_net, &nfho_out);

  return 0;
}

こんな感じで、ローカルに入ってきたパケットと出ていくパケットをフックしている。

出ていくフェーズ、すなわち、リモートサーバにTCPで接続しにいくような状況では、synパケット送出時にオプションのチェックとtcprivオプションのセット(tcpriv_tcp_syn_options)、さらに、定義した実験的TCPオプションフィールドにオーナ情報を書き込んでいる(tcpriv_tcp_options_write)。

static unsigned int hook_local_out_func(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
  struct iphdr *iphdr = ip_hdr(skb);
  struct tcphdr *tcphdr = tcp_hdr(skb);

  if (iphdr->version == 4) {
    if (iphdr->protocol == IPPROTO_TCP && tcphdr->syn) {
      struct tcp_out_options opts;
      struct sock *sk;
      struct tcp_md5sig_key *md5;

      printk(KERN_INFO TCPRIV_INFO "found local out TCP syn packet from %pI4.\n", &iphdr->saddr);

      sk = state->sk;
      memset(&opts, 0, sizeof(opts));
      tcpriv_tcp_syn_options(sk, skb, &opts, &md5);
      tcpriv_tcp_options_write((__be32 *)(tcphdr + 1), NULL, &opts);
    }
  }

  return NF_ACCEPT;
}

現在、IANAが規定しているTCPオプションヘッダは複数存在し,同時に,HOST_IDやLinuxカーネルバージョン5系で実装されているShared Memory communications over RMDA protocol(以降SMC-R)*1といった実験的なTCPオプションも存在する。 例えばTCP Fast Open Coolieは2014年に標準化され、IANAから正式なTCPオプションとしてkindナンバーを付与されている。 一方で、比較的新しいTCPオプションであるため、依然としてLinuxカーネルのバージョンによっては、実験的なkindナンバーを共有して他の実験的なTCPオプションと利用する実装になっている*2。 そのため、実装においては、TCP Fast Open Cookieが固有のkindナンバーを保つ場合と共有の実験的なkindナンバーを持つ場合を想定して実装を行っている。

また、Linuxカーネルバージョン5系においては、共有の実験的なkindナンバーにSMC-Rを利用しているため、IANAからSMC-Rに割り当てられているTCP Experimental Option Experiment Identifiers(以降TCP ExIDs)*3とは別のTCP ExIDsを暫定で割り振って区別する。

特にパケットのTCPヘッダオプションフィールドの解析を行う際に、既存のTCPオプションはそのままに、提案手法用の実験的オプションの共有kindナンバー、データレングス、TCP ExIDs、キー情報を書き込む。 実験的オプションの仕様を忘れていると、ExIDsの定義を忘れちゃうので注意。

書き込み処理は以下のような感じ。

static void tcpriv_options_write(__be32 *ptr, u16 *options)
{
  if (unlikely(OPTION_TCPRIV & *options)) {
    kuid_t uid = current_uid();
    kgid_t gid = current_gid();

    *ptr++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | (TCPOPT_EXP << 8) | (TCPOLEN_EXP_TCPRIV_BASE));
    *ptr++ = htonl(TCPOPT_TCPRIV_MAGIC);

    /* TODO; write tcpriv information: allocate 32bit (unsinged int) for owner/uid area */
    *ptr++ = htonl(uid.val);
    *ptr++ = htonl(gid.val);
  }
}

static void tcpriv_tcp_options_write(__be32 *ptr, struct tcp_sock *tp, struct tcp_out_options *opts)
{
  u16 options = opts->options; /* mungable copy */

  if (unlikely(OPTION_MD5 & options)) {
    *ptr++;
    ptr += 4;
  }

  if (unlikely(opts->mss)) {
    *ptr++;
  }

  if (likely(OPTION_TS & options)) {
    if (unlikely(OPTION_SACK_ADVERTISE & options)) {
      *ptr++;
      options &= ~OPTION_SACK_ADVERTISE;
    } else {
      *ptr++;
    }
    *ptr++;
    *ptr++;
  }

  if (unlikely(OPTION_SACK_ADVERTISE & options)) {
    *ptr++;
  }

  if (unlikely(OPTION_WSCALE & options)) {
    *ptr++;
  }

  if (unlikely(opts->num_sack_blocks)) {
    int this_sack;

    *ptr++;

    for (this_sack = 0; this_sack < opts->num_sack_blocks; ++this_sack) {
      *ptr++;
      *ptr++;
    }
  }

  if (unlikely(OPTION_FAST_OPEN_COOKIE & options)) {
    struct tcp_fastopen_cookie *foc = opts->fastopen_cookie;
    u8 *p = (u8 *)ptr;
    u32 len; /* Fast Open option length */

    if (foc->exp) {
      len = TCPOLEN_EXP_FASTOPEN_BASE + foc->len;
      p += TCPOLEN_EXP_FASTOPEN_BASE;
    } else {
      len = TCPOLEN_FASTOPEN_BASE + foc->len;
      *p++;
      *p++ = len;
    }

    ptr += (len + 3) >> 2;
  }

  tcpriv_options_write(ptr, &options);
}

続いて、入ってくるフェーズでは、syncパケット受信時にTCPオプションヘッダフィールドをparseし、オプションが定義されていれば当該フィールドからオーナ情報(uid32bit+gid32bit)を取得するようにしている(tcpriv_tcp_parse_options)。

static unsigned int hook_local_in_func(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
  struct iphdr *iphdr = ip_hdr(skb);
  struct tcphdr *tcphdr = tcp_hdr(skb);
  struct tcp_options_received tmp_opt;

  if (iphdr->version == 4) {
    if (iphdr->protocol == IPPROTO_TCP && tcphdr->syn) {
      printk(KERN_INFO TCPRIV_INFO "found local in TCP syn packet from %pI4.\n", &iphdr->saddr);

      /* parse tcp options and store tmp_opt buffer */
      memset(&tmp_opt, 0, sizeof(tmp_opt));
      tcpriv_tcp_clear_options(&tmp_opt);
      tcpriv_tcp_parse_options(&init_net, skb, &tmp_opt, 0, NULL);
    }
  }

  return NF_ACCEPT;
}

parseについては、カーネルのコードを存分に参考にしながら、ひとつずつオプションフィールドのフラグとレングスのチェックを行って、tcprivオプションがああればその情報を取得するようにしている。

/* TCP parse tcpriv option functions */
static void tcpriv_parse_options(const struct tcphdr *th, struct tcp_options_received *opt_rx, const unsigned char *ptr,
                                 int opsize)
{
  if (th->syn && !(opsize & 1) && opsize >= TCPOLEN_EXP_TCPRIV_BASE && get_unaligned_be32(ptr) == TCPOPT_TCPRIV_MAGIC) {
    /* TODO: check tcpriv information */
    u32 uid, gid;
    uid = get_unaligned_be32(ptr + 4);
    gid = get_unaligned_be32(ptr + 8);
    printk(KERN_INFO TCPRIV_INFO "found client process info: uid=%u gid=%u\n", uid, gid);
  }
}

/* ref: https://elixir.bootlin.com/linux/latest/source/net/ipv4/tcp_input.c#L3839 */
void tcpriv_tcp_parse_options(const struct net *net, const struct sk_buff *skb, struct tcp_options_received *opt_rx,
                              int estab, struct tcp_fastopen_cookie *foc)
{
  const unsigned char *ptr;
  const struct tcphdr *th = tcp_hdr(skb);
  int length = (th->doff * 4) - sizeof(struct tcphdr);

  ptr = (const unsigned char *)(th + 1);
  opt_rx->saw_tstamp = 0;

  while (length > 0) {
    int opcode = *ptr++;
    int opsize;

    switch (opcode) {
    case TCPOPT_EOL:
      return;
    case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */
      length--;
      continue;
    default:
      if (length < 2)
        return;
      opsize = *ptr++;
      if (opsize < 2) /* "silly options" */
        return;
      if (opsize > length)
        return; /* don't parse partial options */
      switch (opcode) {

      case TCPOPT_EXP:
        /* Fast Open or SMC option shares code 254 using a 16 bits magic number. */
        if (opsize >= TCPOLEN_EXP_FASTOPEN_BASE && get_unaligned_be16(ptr) == TCPOPT_FASTOPEN_MAGIC) {
          // do nothing
        } else if (th->syn && !(opsize & 1) && opsize >= TCPOLEN_EXP_SMC_BASE &&
                   get_unaligned_be16(ptr) == TCPOPT_SMC_MAGIC) {
          // do nothing
        } else {
          tcpriv_parse_options(th, opt_rx, ptr, opsize);
        }

        break;
      }
      ptr += opsize - 2;
      length -= opsize;
    }
  }
}

これでめでたしめでたし、リモート先のプロセス側で接続元プロセスのオーナ情報を取得できるわけであるが、さてここからユーザランドで動作するミドルウェア等でこのデータをどのように取得するかを考える必要がある。

現状では、以下の図にあるように、socket APIを使うあるいは同様のAPIを実装することによって、tcprivオプションが有効化どうかをチェックする関数を用意する。

f:id:matsumoto_r:20200604144054p:plain

それを用いて、ミドルウェア、あるいは、その前端においたミドルウェア対応のプロキシで、セッションを確立する際にその関数でtcprivオプションをチェックする。 その上で、tcprivオプションがenabledであれば/procファイルシステムの指定の場所に、tcpriv/ipaddress+src-portみたいなファイルを見ると、接続元のオーナ情報が取得できるようにしておき、それを行える関数を用意する。 そのためにも、tcprivを実装しているカーネルモジュールの中で、オーナ情報をparseした後に、その情報を/proc以下に書き込むようにしておく。 すると、TCPセッション確立時に接続元プロセスのオーナ情報を取得できるので、その段階、あるいはミドルウェアとしての認証を行う段階で、そのオーナ情報を従来のID/PASSやトークンを突き合わせることで認証行う。


とまあ、ここまでが頭の中で大体できそうだと考えている設計なのだが、もっとシンプルにオーナ情報をユーザランドでセッション確立時に取得できる方法はないかと最近考えている。 scoket APIを作ったり拡張したりすることはできるが、もう少し、うまく/procにtcprivに関する情報を配置するだけで、セッション確立時にあわよくば関数を使わなくてもreadとかだけで扱えるような設計はないか検討している。

もしアイデアがありましたら、ご教示いただけると幸いでございます。

*1:RFC-7609 IBM's Shared Memory Communications over RDMA (SMC-R) Protocol, https://tools.ietf.org/html/rfc7609

*2:RFC-6994 Shared Use of Experimental TCP Options, https://tools.ietf.org/html/rfc6994

*3:TCP Experimental Option Experiment Identifiers (TCP ExIDs), https://www.iana.org/assignments/tcp-parameters/tcp-parameters.xhtml#tcp-exids

論文のrejectという希望

ここ2、3年で目標としていた、IEEE Computer SocietyのFlagship Conferenceの一つとされているCOMPSAC 2020のメインシンポジウムに、ファーストオーサの論文がフルペーパーで採録されました。

送られてきたメールによると、今回のメインシンポジウムのフルペーパ採録は24%以下ということで、昨年の250本程度の投稿論文があることを考えると、全体のフルペーパ採録は60本程度になるかと予想できます。去年はCOMPSACのメインシンポジウムにショートペーパとしてファーストオーサの論文が1本採録、ラストオーサの論文がショートペーパで1本、併設ワークショップに1本という実績でした。

さらに、2年前は未だに当時のショックを覚えているぐらいに、メインシンポジウムでの査読でフルボッコの査読結果を頂き、なんとか気を持ち直してワークショップに通すことができた、ぐらいの実績でした。その当時の話は以下のエントリにも書いています。

hb.matsumoto-r.jp

hb.matsumoto-r.jp

そういう意味で、自分のやり方を2年前に見つめ直し、国際会議に自分の論文を通すための方法論について模索し、論文の書き方、更には国際会議に通すための英語の論文を模索したのが昨年でした。その模索についての去年の夏の段階での考えは以下のエントリにまとめています。

hb.matsumoto-r.jp

レベルの高い国際会議からのrejectの理由と考察

さて、ここからが今日のエントリの本題です。去年の夏の段階でCOMPSACのレベルの国際会議にショートペーパとして採録するための自信とそのスキルはついてきていたように思いますが、やはりメインシンポジウムのショートペーパとフルペーパというのは歴然とした差があり、ショートペーパではページ数が6ページに対して、フルペーパでは最大12ページと、その分量からも国際会議で扱われる論文としての重要度に大きな差があります。CORE Ranking Portalというサイトで、学術コミュニティが協力して国際会議の難易度をスコアリングしており、さくらインターネット研究所はそれがすべてというわけではありませんが、ある程度参考にして戦略を練っています。ここでのRankingは概ねそのカンファレンスのメインシンポジウムのフルペーパーを指していると理解しています。

去年は夏以降に、COMPSACのショートペーパに通せたのだからといって、カンファレンスのレベルやランキングを定義しているCORE Rankに基づいて、Aランクや、所謂トップカンファレンスと呼ばれるA*ランクのカンファレンスに論文を書いて投稿していました。しかし、全ての投稿論文はrejectされ、厳しい指摘とともに査読結果が返ってきては、直視することができずに頭を抱え、数日間あいだをあけてなんとか査読結果を読むということを繰り返していました。

査読結果からは主に、

  1. あっと驚く新規性がない
  2. 論文の構成がまずくて伝えたいことが伝わらない内容になっている
  3. 実験のベースラインが明確でない

といったことが書かれていました。ただ、査読の段階では文面からは意味がわかりますが、なんとなく腑に落ちない感覚でした。なので、自分が所属するさくらインターネット研究所にお願いして、rejectされた国際会議に参加させてもらって、一体acceptされている論文はどういう内容で、どういう研究発表がなされているのかを実際に見てみたいと思い、rejectされた国際会議にすべて参加してみました。

すると、全ての発表に共通していたこととして、

  • 最新技術が今どこにあるか?(The state of the art)
  • 自分の研究が活きるユースケースはどこか?(リアルシステムでの効果)
  • 自分の研究は最新技術と比べてどういう面でどういう成果が上げられているか?(The state of the artと比べてどこが優れていてどこがnoveltyなのか)
  • 評価の際にどのレベルをクリアできれば、有効であるといえるか?(妥当なベースラインの設定がなされているか、そのベースラインに対してどう優れているか)

というポイントをほぼ全ての講演で満たされている事に気づきました。これに気づいた後に査読結果を思い直すと、まさに、トップカンファレンスで皆が当たり前に共通して述べていることそのものが欠けていると、新規性が見えず、内容が理解できず、有効かどうか信頼足りうるかがわからない、という上述した査読結果の3つの指摘の話になると腑に落ちたのでした。

また、それが腑に落ちたときに、査読結果にはそれを改善するための指摘やわからないことの主張、実際にどのように誤読させてしまっているかの議論、などを読むことができるので、これはすなわち、論文のrejectは自分の研究や論文を明確に改善できるチャンスであり、希望なのだと思えるようになりました。

reject, reject, reject, reject ..... accept!

そこで、今年の2月に投稿したCOMPSAC2020ではそれらの査読結果で指摘された内容を、新規性の主張、前提の一致、最新技術の立ち位置、最新技術と提案手法の多面的な比較と考察、評価のベースラインの明確化を意識して書き直して提出しました。すると、先週COMPSACのプログラム委員から査読結果が返ってきて、

「This paper is well written. The readability of this paper is good.」

「The proposed method is novel and feasible.」

「Congratulations on your paper being accepted for the conference! 」

という、いまだかつてないポジティブなメッセージとともに、査読についても、「私はこう思うけど、どう思う?」とか「こうやると多分もっとよくなると思う」などと、今までの指導的な査読ではなく、あくまで同じ目線で査読メッセージを書いてくれているような査読結果の通知が届きました。

自分は、昨年はほとんどのプロポーザルでrejectをされてしまったのですが、そのrejectと査読結果、そして、実際にrejectされた国際会議に参加してその違いを知ることは、自分の研究を明確に成長させることのできる希望である、と思います。博士課程を修了後は、自分がファーストオーサで、かつ、企業の研究所で指導的立ち位置でラストオーサとして論文を書く中で、ようやく「自分の良いと思える論文」そして「国際会議などに論文を通す書き方」を模索した上で、それが、COMPSAC2020メインシンポジウムのフルペーパacceptという形で結果を得られたことが、まるで自分のここ数年の取り組みが認められたかのように思えて、感慨深い気持ちになりました。そんな気持ちを忘れないように、このエントリを書いています。

rejectと査読結果は希望となる

最後に、これから国際会議の論文にどんどん挑戦していこうとしている研究者の皆様に、rejectというのは希望であるということお伝え、あるいは、rejectで悩んでいる人と共有できると良いなと思っています。実際にrejectされると落ち込むのですが、rejectされるということは改善の余地が明確にあるということを客観視できる機会であるといえます。研究していると、それを得るチャンスというのは意外となかったりします。

なので、何度rejectを受けたとしても、その査読結果をどうにか研究や論文に少しずつでも反映していけば、その研究は常に前進し続けることができます。前に進めるという希望は、きっと自分の研究を次の領域へと押し上げてくれるのではないかと思います。何度rejectされても、一歩ずつ進んで最終的にacceptされてしまえば、落ち込んだことも忘れられますし、きっと良い思い出になります。

僕もまだまだこれからrejectを繰り返しながら、研究者として前に進み続けたいと思います。rejectされる限り、研究し続ける希望があり続けるのだという気持ちで更にやっていきますし、rejectされて落ち込んで途方にくれている人がもしいたら、そんな人にこのエントリが届くと幸いです。

クライアントプロセスのオーナ情報によるTCPを介した透過的な権限分離

研究アイデアや構想の公開

まつもとりースタイルとして、研究開発をしつつあいであがまとまってきたら公開しながらやっていくスタイルをとっていますので、国際会議などの延期に伴い、新しい研究をやり始めているのでそれをアイデアや構想ベースで公開します。

また、僕の研究のやり方として、まずはこのように考えて研究を組み立てているんだという紹介でもあります。是非ご笑覧下さい。

本研究のアイデア紹介

単一のOS環境に複数のテナントを配置するようなマルチテナント環境において、一般的に各テナント間での権限分離はプロセスのオーナやパーミッション情報を利用します。 一方で、Webホスティングサービスをはじめ、Webサービスにおいてもコンテナによって処理を担当するプロセスの権限分離が普及している状況において、データ処理に関しては、複数の異なるオーナのプロセスがデータベースのようなミドルウェアをネットワークを介して通信し共有することで実現されるケースがあります。

そのようなシステム構成においては、単一のOS内でのプロセス間は権限分離されていても、ネットワークを介した分散システムと捉えたときには、OS側の権限分離とは独立してユーザとパスワードによってデータベースを始めとしたミドルウェアの認証を行うことになります。 すなわち、アプリケーションやシステムの脆弱性によって、特定のプロセスが他のオーナのプロセスのユーザとパスワードを取得できた場合、容易に通信先ミドルウェアの情報にアクセスできることになります。 そこで、Linuxのプロセスのオーナ情報をTCPを介したミドルウェアの認証に付与し、特定のオーナからのみミドルウェアの認証を可能とする透過的なTCPを介した権限分離手法を考えています。

以下、研究の組み立てに使った言語化なので、デスマス調ではなくなります。

ユースケース・キラーアプリ

コンテナを使った分散システム環境

コンテナの普及によって、ステートレスな処理はコンテナで分離するようになってきているが、それによってよりデータの接続先が共有されるようにもなっていくかもしれない。その際に、コンテナのシステム周りで脆弱性があり、他のコンテナの処理のread権限があった場合に、データベースアクセス等の認証情報を得られると、ネットワークリーチアビリティがあるケースでは簡単にデータベースにアクセスして情報を抜くことができる。このようなケースでも、コンテナはプロセスであるのでオーナ情報等を利用して、接続先でオーナの情報を認証に追加しておけば、しかるべきコンテナからしかアクセスができないようになる。

コンテナの普及によって、処理の権限分離は進んでいるが、依然としてデータの権限分離はネットワークレベルでのセグメントを分ける程度の分離になっているので、提案手法を使うことで同じセグメントであってもプロセスの権限分離の情報をTCPの通信先でも有効であるように権限分離できるようになる。

大規模Webホスティング

Webホスティングサービスなどのマルチテナント環境において、オーナでテナントの権限分離を行っている状況で、とあるテナントが別テナントのファイルを何らかの脆弱性で読み取れた場合、DBは共有であることが多いため、簡単に別テナントのデータを抜き取れてしまう。そのような状況で、プロセスのオーナ情報もDB側で認証に使うことができれば、別テナントのデータを抜き取れなくなる。また、クライアントプロセス側でそのオーナ情報をコントロールしてDBにアクセスできると意味がないので、ユーザランドでは操作できないようにカーネル側で透過的にオーナ情報を伝達するようにする。

Webサービス

とあるアプリケーションが脆弱性により任意のコマンドを叩けるようになったという状況で、例えば、とあるスクリプトがデータのアップロード処理であり任意のコマンドを叩ける脆弱性を持っており、他のスクリプトにデータベース連携の処理がかかれているようなケースを想定した場合において、アップロードスクリプトがデータベース連携スクリプト等のread権限を保持することによって、他のアプリケーションが扱うDBなどのパスワードが書かれたプログラムや設定を読み取ることにより、同一OSに稼働しているアプリケーションの情報を抜かれる情報がある。この状況においても、アップロード処理とデータベース連携処理のオーナを分けておきさえすれば、データベースなどの通信先ミドルウェアが、通信元クライアントのオーナ情報を認証に利用することにより、認証を突破できなくすることができる。

これにより、そもそも、データベースアクセスするアプリケーションのオーナを分けるということ自体が、DBやネットワーク経由でのアクセスにおいてはあまりメリットがないため、あまり気にせず設計されているが、この研究によって、同一OS上で稼働している複数のアプリケーションもオーナを分けることによってセキュリティのリスクを限定的にすることが可能になる。

DBの認証

キラーアプリ的には上述したとおり、マルチテナント環境や複数のアプリケーションからTCP接続でDB認証する際に、オーナ単位でさらに細かく認証を行えるようにするようなDBのプラグインが上げられる。このような、TCPを介したミドルウェアの認証にクライアントプロセスのオーナ情報を活用する系の拡張が主なキラーアプリになりうるだろうと思える。

設計

プロセスからTCPを介して接続する際に、TCP/IPヘッダのどこかにプロセスのオーナ情報を埋め込むカーネルモジュールやTCPスタックを作る。つまり、基本戦略として、ユーザランド側からはそのオーナ情報を変更することは不可能になるようにカーネル側で制御する。

その上で、TCPを介した接続先のプロセスでは、埋め込まれたオーナ情報を取得できるようにする。これもユーザランドで改変できると意味がないので、基本的にはカーネル側で構造体を持ちたい。が、まずの最小スコープとしては、クライアント側からの認証について、オーナを使って認証できれば良いとすると、サーバ側ではライブラリなどでオーナ情報を取得できるようにし、そのライブラリを使ってミドルウェアで認証を行うプラグインやモジュールなどを開発するというのでも良さそうだ。

@pyama86 さんのSTNSによるユーザ名の名前解決などを活用する*1ことにより、オーナ情報をうまく統一的に扱う方法があるかも?

実装

ユーザランドで操作可能にしては、アプリケーションの脆弱性を踏まれる、すなわち、最悪の場合はそのプロセスをのっとれるわけなので、プロセスのオーナ情報は透過的にユーザランドでコントロール不可能にしながらカーネル側で制御する必要がある。

そこで、ひとまずの最小スコープとしてDBなどにアクセスするクライアント側のプロセスが起動しているカーネルのカーネルモジュールから、TCP/IPスタックにおいてヘッダ情報のどこかに、プロセスのオーナに関するtask_struct構造体の情報を埋め込む。その上で、接続先のDBのようなミドルウェアが起動しているOS上でTCP/IPヘッダを読み取るライブラリによって、そのオーナ情報を受取る。これはユーザランドでも一旦良い(ミドルウェア側の脆弱性は一旦別問題とする)。そして、DBをはじめとしたミドルウェアのプラグインやモジュールで、そのライブラリを利用してオーナ情報を取得し、認証に利用できるような実装にすることで、データベースだけでなく、各種TCP通信による認証系の共有ミドルウェアに対して、接続元クライアントのID/PASSに加えた独自性の高いオーナ情報を利用することにより、セキュアなシステムが組める。

問題としては、オーナ情報のシステム全体としての一意性をどうするかなどがあるが、比較的小規模システムにおいては、オーナは個別にかぶらないように設定することもあるので、利用できることが多そうではある。

実験

ひとまず強引にでもカーネルモジュール or テスト的にTCPスタックをユーザランドに組んで*2*3、そこからヘッダにオーナ情報を埋め込みつつ通信して、それを接続先から取り出すような実装を行って試す。

それをミドルウェアで認証に使ってたしかに制限できていることも確認や、それを利用することによるスループットやオーバーヘッドの差を調べるとか。

さくらインターネットに入社して1年が経ちました

本当にあった言う間に、2018年11月にさくらインターネットに入社してからちょうど1年が経ちました。

hb.matsumoto-r.jp

随分と活動が海外に広がり、異様に優秀な研究所のメンバーに刺激を受けて、毎日がとても楽しいですし、これからさらに楽しくなっていく話ばかりです。自分の研究のキューも沢山ありますが地道に頑張ります。

この1年は、さくらインターネット研究所がチームとして機能するように研究開発の取り組み方などにアプローチしてきました。チーム設計が落ち着いてきてから特に意識していることは、自分ができることは共有したりレビューして伝えつつ、世界に目を向けて自分ができていないことを先回りして挑戦・調査することです。そんなことを継続してやっています。

とういうこうことで、入社後印象的だったことをまとめてみようと思います。

さくらインターネット研究所におけるフォロワーシップ

さくらインターネット研究所においてびっくりしたことは、まず鷲北所長のフォロワーシップがとにかくすごいことです。やりたいことは何でもやらしてくれます。多分僕がやりたいといってだめだったことは一つもないんじゃないでしょうか。 これもできそうでなかなかできないことなのですよね。

それに加えて会社の理解があるからこそ、我々は自由に研究したり発言したり社内外で活動できたりしています。 研究という言語化の難しい取り組みに対して会社から理解してくれていることも考えると、まさにこれも研究所に対する会社からのフォロワーシップでもあります。 そして、最近では優秀な人が研究所に沢山集まってきていますが、まさにこれは所長自ら率先したフォロワーシップが良いチームを作っているからであり、それが外からも見えるようになってきていることに他ならないでしょう。

さくらインターネット研究所最近やばいよねという話に対する回答の一つは、所長がとにかくリーダーシップを発揮しないといけない、というわけではなく、実はリーダーだからこそフォロワーシップを発揮することがすごく周りに効いてくるという話であると思えます。 研究所内ではそういう個々の関係性の中でフォロワーシップが見事に機能しており、皆がそれに影響を受けて個々にフォロワーシップが自然にできているわけなので、もう少しマクロにチームや組織として会社を見た時に、これからは研究所自体がチームとして組織に対するフォロワーシップを率先してやっていくことが、うまくチーム間や組織全体のポジティブな文化を醸成していくことになるでしょう。 なのでそこをしっかり引き続きやっていきたいです。

そのためにも、研究に対する理解を求めるばかりじゃなくて、研究所でやっていることをもっと透明化して会社や周りに伝えながらポジティブな影響力を与えていきたいです。 研究所自らが積極的にフォロワーシップすることが、さらに研究所に対する周りからのフォロワーシップを強め、その共感と賛同は自信にも繋がり、さらなる研究の質を上げていくように思えるのでありました。

余白の重要性

さくらインターネットではとにかく働きやすさを改善し、余白を設けて心身ともに休めるようにし、適切な時間の仕事を行えるようにすることで、精神的にも肯定的になりリード&フォローが可能となって、組織として持続的に成長し続けられるような人事施策を戦略的に行なっています。これは本当に素晴らしいと思います。もちろん有給休暇やその他記念日休暇なども使用期間からすでに20日以上あります。

一時的に数値面に課題が出て、もっとみんな働けるとか、無駄を省いて効率化だ生産性だ、みたいなことを通常やりがちですが、さくらインターネットではそういうことは後々別の場所にヒビが入り破綻することを理屈としても経験的にも理解していて、だからこそ自信を持ってこういう施策に取り組めています。

目の前の傾きに気を取られて局所最適に陥ると、傾きは緩やかでも持続的に成長すべき状況で、フォースエコノミーのような単一の側面だけで善し悪しを測ってしまってそこから派生する悪影響を認識できない状況になってしまい、それを取り返すのにまた膨大な時間とコストがかかってしまいます。常に多面的に評価を行い、結果として持続できる健康な組織を作り始めていて素晴らしいです。

さくらインターネットでは、楽しくやろう、余白を持とう、しっかり休もう、やりたいことをやろうなどを強調し、それが精神を充実させ、リード&フォローを促し、少しずつ前へと進み続けられる組織になってきているように思えます。短期の強引な成長よりも、長期での緩やかな持続的成長が重要なのですね。

そして、このように後からだと理解できるのですが、それをずっと前からコンセプトして以前から何度も提唱してきている田中さんは本当に過去や歴史から物事を学び、そこから自分の新しい発想を取り入れながら先を見据えた発言をされていて、その理屈が自分の中で筋が通った時に、あーーーそういうことですね!とっなって、本当にすごいなと感心します。

あなたはやりたいのですか?

さくらインターネット研究所の所長にやりたいことを相談した時には必ず「で、まつもとさんはやりたいの?」「やりたいです」「じゃあやりましょう」という会話になるので、本当に楽しく仕事させてもらっていますね。「さくららしさがあれば良いし、やりたいことにさくららしさが移動するかも?」ともおっしゃってくれます。

結局のところ、あなた個人が本当にやりたいかどうか、ひいては社員のみんなが本当に幸せにやれているか、その結果として株主や事業を通じて社会に貢献できるような状態に持っていける組織構造を作れているかを重要視しており、これは本当にさくららしい考え方だと思えました。

まとめ

ということで、入社してとても特徴的であったことをまとめてみました。こういう話を会社として建前だけでなく、組織として進められているというのは本当にすごいなと思います。 意外とできそうでできないことですよね。

そして実際のところ、僕がホスティングサービスに興味を持ち出した2004年あたりから、田中さんと鷲北さんというと当時自分が最も尊敬するエンジニアであったのですが、そういう人たちと今一緒に仕事をできていたり、割と自由に話せたりできる環境は本当に楽しいですね。良い意味で、尖っていて憧れていたエンジニア像として、今もそのままでいてくれるので、とても刺激があって会話も楽しいです。

こういう状態で仕事が続けられるように、少しでもこういう良いことが外にも伝わるように、そして、同じ環境で切磋琢磨できるより優秀な人と働けるように、こういう内容の記事をこれからも書いていきたいなと思います。

冒頭で述べたとおり、さくらインターネット研究所はさくらの斥候部隊という立ち位置ですが、自分は研究所の中の斥候として研究所の未来を切り開いていきたいなと思います。2年目も引き続き世界に目を向けながら、焦らず自分の力を世界水準で発揮できるようにし、それをまた研究所ひいてはさくらにフィードバックします。

入社時に田中さんや鷲北さんからの期待として「世界でさくらの新しい顔になれるように活躍して欲しい」と言われたことを忘れずに、日々やりたいことや楽しむことを忘れずにやっていきます!

非厳密計算で確率的に解釈するコンピューティングへの流れ

ここ数ヶ月、沢山の国際会議や自分の専門分野外のトップカンファレンスに採録されるような多種多様な研究発表を聞いていた。そんな中、自分の中で各発表に共通するコンピューティングの流れみたいなものが少し見えてきた気がするのでメモしておく。

機械学習やコンピュータビジョン、ヒューマンインターフェース、コンピュータセキュリティ、計算機アーキテクチャ、量子コンピューティング等のトップカンファレンス発表報告を聞く中で、印象的だったのは、まさに新しい発見という研究もある中で、もはや枯れた技術で確立されたアーキテクチャにおいて、新たな貢献を示す研究があったことだ。例えばデータベースにおけるメモリ管理の話やCPUのパイプライン処理の効率化、スパコンの文脈におけるネットワーク通信の高速化の話など、いずれも登壇者が冒頭で随分と研究されてきた確立されたテーマであることを述べていた。

そういった確立された領域の中で、どの研究にも共通していると思えたアプローチは「実際のリアルシステムにおいては、こういう振る舞いがほとんどなので、そのケースに合わせて設計した上で、それで足りなければ元のアーキテクチャにフォールバックする」「リアルシステムではこういう使い方が多数なのでそこの優先度を変えられるようにすることで、大抵の場合有効であることを示した」「計算を大雑把に行って、だいたいあっているけど、間違っている時は厳密に計算し直す」といったような考え方である。

コンピュータそのものの原理に近い領域ではどれも汎用的に作られていることがほとんどである。一方、例えば僕が専門とするインターネット技術の領域では実践的な領域からフィードバックした課題や問題意識に基づいて特定状況における最適化を行い、全体として実運用上は有効であることを検討することが多い。そして、そういった考え方が、まさにコンピュータのアーキテクチャとしてその根幹の厳密計算が求められる領域であっても、そのようなリアルシステムにおける現実的な振る舞い、すなわち、確率的解釈や優先度を考慮したアプローチや非厳密計算に基づいたアプローチがまさにコンピュータの基本原理に近い領域にまで降りてきているように感じた。

これはまさに、他の歴史ある研究分野や実践が伴う応用研究分野のように、汎用的かつ厳密計算ができるように作られてきたコンピューティングが社会に溶け込み始めたことによってその使い方やデータが揃い始め、改めてリアルシステムからのフィードバックからコンピューティングを構成する各種計算機原理に近いコンピューティングアーキテクチャのコンポーネントにおいても問題意識や課題が充実し、特定条件に基づいて最適化して、それが全体として見た時も有効であることが示せるような時代になってきたことを意味するのではないか。

そして、量子コンピューティングである。まさにビットの扱いを物理現象や粒子の扱いにまで落とし込み、ビットの重ね合わせと確率論的かつ非厳密計算によるアプローチ、すなわち、得られる解の確率を確率振幅から厳密に制御しようとするアプローチにより、これまでのコンピュータ・アーキテクチャが解くことが困難であった問題を効率的に解くというコンピューティングのあり方である。ここまで聞いた時に、自然と自分は、ああ、コンピューティングはこれから厳密計算の流れから非厳密計算かつ確率的コンピューティングのハイブリッド、そして、粒子の振る舞いをビットとして解釈した汎用コンピューティングの世界になっていくのか、というのを肌で感じたとともに、自然科学や社会にコンピュータが溶け込んでいくことによって、量子化学のようなほとんどが確率的に示される世界へと、コンピュータが本当の意味で統合されていく世界になっていくのかと思えた。

これから、長い時をかけて、コンピュータがどんどん非厳密計算で確率的なコンピューティングの形態へと変化し、それぞれのコンピューティング層で扱われるアーキテクチャも、より自然科学や社会のあり方、さらには、人のあり方に近い振る舞いになっていくのであろうか。少なくとも、自分はそのように感じざるを得なかった。このノーフリーランチの定理的な流れは色々な研究の場面で生じているのは研究の歴史的に理解しているのだけど、またそういう流れが生まれているのを目の当たりにしている。歴史は繰り返す。

そして意図せずとも、この話は以前のDICOMOでの招待講演の質疑の中でもお話ししていて、インターネットと運用技術とか研究に今後なっていくのかという問いに対し、リアルシステムは更に多様になって使い方も変わるので、そういう問題の特徴や課題を捉えることでここに述べたような最適化が可能になり面白いと答えたのであった。

ラストオーサーとしての国際会議投稿やジャーナル執筆のサポートについて

先日、アメリカのミルウォーキーで開催されたIEEE Computer SocietyのフラッグシップカンファレンスとされているCOMPSAC 2019に参加・登壇してきました。

ファーストオーサーの論文をメインシンポジウムのNCIWにショートペーパーとして1本、ラストオーサーとしての共著の論文を同じくNCIWにショートペーパーとして1本、併設ワークショップのNETSAPに1本の計3本の論文を通したことになります。採択率などについては、ゆううきさんの論文に詳細が書かれているのでそちらを見て頂くとして、257本の投稿の中でフルペーパー63本の採択率24.5%、ショートペーパー50本というデータを踏まえると、なかなか頑張ったのではないかと思います。

そこで、ファーストオーサーの発表については、以下の記事で十分に触れられているので、僕は共著としてどのようにやっているかについて紹介しようと思います。

blog.yuuk.io

rand.pepabo.com

日本語でしっかり論文をレビューする

国際会議に何度か参加していた数年前に、一時期は英語で最初から書いた方が良いのではないかと思っていた時期がありました。一方、日本語でジャーナルを何本か通したり共著レビュー、査読をしていく中で、論文というのは母国語で書くにしてもここまで緻密で困難な、極めて高度な技術が要求される作業なのかと思うようになりました。

母国語であっても、正しく論文を書くというのは、ストーリーや章構成、背景から従来の課題をあぶり出していく書き方、そこから自然と見え始める目的や提案手法、その有効性や貢献は何かについて、複雑に絡み合った前提や条件を提示しながら適切に読者に伝えることは非常に高等な技術であると思うようになりました。そう考えると、僕の場合英語は明らかに母国語と比べた時には不自由であるはずで、そこから母国語以上に正しい論文を、緻密に議論できるわけがありません。

また、英語のスキルが足りないことによって制約を与え物事をシンプルに説明するという考え方もありえますが、実際には非常に複雑な条件や前提をうまくまとめて表現しないといけないことも多く、そこを英語で変にシンプルにしてしまうと、情報が欠落することにもなりかねないのです。英語のスキルが充分に高くないと、物事をシンプルに表現できたと思えたとしても実は議論すべき情報が欠落して、本来主張すべき内容が希薄になってしまうのです。日本語で複雑に思える議論は、まずは日本語でどこまで本質に迫れるかを試した方がよく、そこでなされた整理こそが本当の意味での論旨を表現できるのだと思えました。これは、箇条書きにも言えることで、不用意に箇条書きにしてまとめてしまうと、相互に関連する情報などが欠落し、本来議論すべき内容が見えなくなってしまいます。

そこで、共著においても、まずは国内の研究会やシンポジウムへの投稿を推奨しており、まずは論文を書いてみてそれをしっかり共著としてレビューし、母国語での議論を反映していきます。また、少し実績が加わればそれが小さく思えてもできるだけ切り出して論文に加えてレビューし、また研究会などに投稿して発表するようにしました。その際にも、ジャーナルほどとまではいきませんが、上述した正しい論文になるように母国語でレビューをしました。特に、全体のストーリーや概要を重点的に見るようにしています。それが、複雑になりがちな研究の議論を母国語で正しく筋を通しながら整理し、研究の貢献たりうる本質に迫れる一番の近道だと考えているからです。また、そのストーリーと概要が整理されれば、そこから付随して詳細に踏み込みつつ、貢献の正しさと広がりを順次あぶりだしていけるからです。

そうすると、自ずと日本語ではありますが、前述したような複雑な内容の研究がよい論文に育っていきます。論文を育てることは自身の研究の正しさと広がりを適切に言語化する作業であり、結果的に研究会を通じて自分の研究そのものを学会と一緒に育てていくことができます。

英語化する際は必ず校正をかける

上述した通り、母国語でも執筆困難な論文である以上、母国語でまずは執筆し、母国語で議論可能な研究会を介して論文と研究を育てるサイクルを回すという話をしました。そのようにして品質が上がってきた論文はやはり国際会議でも発表し、グローバルに人類の叡智に触れ、正しさと広がりを更に高いレベルで評価すべきです。そこで、研究会を通じて育ててきた論文をさらに加筆した上で、英語化していきます。

英語化については、昨今オンライン辞書や翻訳ツールなどが随分と充実してきたので、まずは自分で全ての日本語を英語化していきましょう。英語化した後に改めてその英語を何度も読み直して、明らかに変だと思えたり、構文解析できないと思えた時は、他人が読んでもほとんどの場合わかりません。また、日本語は接続詞を多用して文を繋げていくのに対し、英語は論理的に繋がる文を続けて置いていけば良いこと、また、日本語でこなれて書くと主語が不明、あるいは、複数の主語が存在するような長い文章になります。しかし、それらは英語にする場合、主語を明確にして、文章を分けて短い文章を繋げていけば良いだけなので、そこを意識して英語化します。

そこまできたら、次はGrammarlyを使って、自動的な処理でも気づけるような文法や単語、文法、文章構造を修正していきます。Grammarlyのプレミアムを使えば、アカデミックな論文を書く際に使われるような単語やformalな言い回し、感情の表現の強弱、読み手を説得させるような書き方など、かなりの部分を自動で修正、提案してくれます。そこでの提案などを勉強しながら、更に修正していきましょう。凡ミスなどはここで極力修正します。

ここまできたら、あとはしっかりとネイティブの専門家に英文校正をかけましょう。僕の場合はFastekさんに校正をお願いしています。実際に僕の分野で、昨年のCOMPSACでは英語についてフルボッコされたのですが、Fastekさんに校正をお願いして出した今回の論文は、英語についての指摘はほぼありませんでした。8ページの論文で大体3万から4万であることを考えるととてもコストメリットが高いと思います。Grammarlyで凡ミスをできる限り修正しておくことで、校正での指摘をより自分が気付けないようなものにすることができて、差分を読むだけでも非常に勉強になります。

これらの校正についても前述した通り、母国語であっても正しい文章で論文を書くというのは非常に困難な作業です。英語が間違いだらけで意味不明だから国際会議で採録されないというのは、非常にクリティカルな機会損失だと考えているので、この辺りは必ず校正をかけるようにしましょう。

ちなみにジャーナルでは、校正で指摘されなかったような更に厳密な不定冠詞の指摘や係り受けの指摘などを受けます。これは、行間を読ませないように、係り受けや指示語を厳格に行い、文意をより正確に表現できるようにするためだと思えます。もはやここまで行くと最初から母国語以外で完全に論文を書くことは不可能に思えます。しかし、このプロセスを踏むと、自分ができることと自分ができなかったことがあぶり出されるため、振り返りを意義あるものにし、自分が今アプローチすべき不得意領域を明確にできます。だからこそ、英語の論文を育てていくためのサイクルをきちんと設計し、時間をかけて自分達のできる最高の取り組み方を考えるべきなのです。

英語の発表の準備

これは、同僚のゆううきさんがしっかりと書いてくれていましたが、スライドを書いた後にはまずスクリプトをしっかり用意します。

blog.yuuk.io

このスクリプトも、読んでわかるような長文を用意するのではなく、話して自分が理解できるように、できるだけ短い文章で一息で話しきれるようなセンテンスを用意します。話しながらそのセンテンスのどこを強調し、どういう意味なのかを頭の中で理解しながら話せるように練習します。このセンテンスが話しながら頭の中に入ってこない場合は、その時点での自分の英語力から見ると長すぎますので、短くしましょう。

強調する単語については、できるだけ自分の発表内容に近い発表が行われているカンファレンスをUSENIXやLinux関連のカンファレンスなど、youtubeで公開されている発表を英語字幕で見ながら、抑揚のつけ方や強調の仕方、センテンスをどこまで一息で話し切るかなどを研究します。センテンスとして長くても、例えば、for highly-integrated multi tenant architecture like virtualhost for apache httpdとかだと、一気に素早く言えるような練習をします。highが見えたら大体こんな感じだよなみたいな感じで抑揚を覚えると良いです。そうすると、なんとなく自分の強調したい単語やセンテンスが抑揚を持って言えてるような気になってきます。

抑揚が日本語からすると少し派手に思えても、英語の場合は派手すぎると思えても大体足りないぐらいなのでその抑揚に慣れると良いです。これをやらないと、日本語で言う完全なる棒読みになってしまって、ほとんど聞こえなくて良い単語などもきちんと発音しすぎてしまって、聴衆には理解が困難な発表になってしまいます。また、自分が理解できない長さだと、強調すべき単語もわからず、完全な棒読みに陥ってしまいます。このような読み方は、母国語であっても聞き取れないことがほとんどです。例えば以下の文章を棒読みで読んでみると良いでしょう。

「ぐりんぐりんおかのうえにはららことりがうたい」

英語の質疑の準備

最後に、国際会議で発表する以上、やはり最も重要な挑戦は発表後の質疑をできるだけ充実させることです。そのために英語力を上げると言うのはもちろんなのですが、英語力を上げるのはそんな簡単ではなく長期的な学習になるかと思います。だからと言って質疑はメールやSNSでとしてしまうと、英語力の成長やグローバルな視点での自分の研究に対する質疑や議論という、研究活動が最も成長する機会を失ってしまいます。これは、研究としても英語としても多大な機会損失になります。

そこで、英語が母国語ではない研究者である、という状況を、別のアプローチでできるだけ活かすようにすると良いです。まずは想定質問をできるだけ用意することです。これは、質疑に対して答えられるパターンやセンテンスを用意しておくことで、関連する質問にもそのセンテンスや文章構造、単語をうまく組み合わせて議論できる可能性が高まります。また同時に、自分の研究の疑問がとこにあって、それはどのように解釈できるのか、というような研究の本質に迫る作業にもなります。

日本語だとここを曖昧なまま、発表と質疑の中で持って回った言い回しでなんとなく乗り切ってしまうことがありますが、事前に英語で議論できるレベルにまで論旨を明確化しておくと、研究自体の理解も深まります。ただここで注意すべきは、自分の英語のレベルに引きずられて物事を単純化しすぎないことです。そこは、しっかり自分が書いてきた英語の論文を参考にしましょう。採録されたのであれば、そこに全てがあるはずです。

あとは、質疑のパターンの構造や、聞き取れなかった時にコミュニケーションの中で内容を理解するためのテンプレを用意しておくことも重要です。ネイティブの質問者が流暢に早口で質問すると、英語発表をスクリプトを見ずに話せるような研究者であっても聞き取れなくて答えられないことは多々あります。だからこそ、聞こえなかった単語はどこかとか、意図はこういうことかとか、そういうコミュニケーションのためのパターンをいくつか用意しておきましょう。運良くスライドの発表者ノートには沢山の情報を書いておくことができます。

まとめ

このように、自分のファーストオーサーの論文はもちろん、ラストオーサーとして国際会議やジャーナル執筆までをサポートする際にはこのようなことを考えながら、状況に応じてこれらの内容を伝えています。このように取り組んでいくと、国際会議の本番では、日本語の発表の時よりも比較にならないような準備をしているので、緊張があまりなく、むしろ楽しみたいという気持ちで発表に望めることもあります。

緊張は、何か失敗するんじゃないか、自分ができないことを要求されるんじゃないか、でも自分はその準備ができていないような時に生じたりします。しかし、このようにできる限り準備する、すなわち自分のベストを尽くすことにアプローチすれば、本番は自ずと武者震いのような気持ちになるのではないかと思います。ベストを尽くす以上のことはできないのですから。

とにかく国際会議や質疑、ジャーナルの査読結果などを有意義なものにし、更にはそのような機会を楽しみながら仮に落ちたとしても次の挑戦へと後押しすることが、ラストオーサーとしての共著の役割であると考えています。そのためにも具体的にこのような機会でベストを尽くすことがどういうことか、それが次のフェーズへと連続的に成長させるためにはどのように繋がっていくかを明確に伝えてあげれば良いのではないかと思います。