YAPC::Fukuoka 2025 に登壇しました - 『セキュリティを 「ふつう」にやっていく 技術、体制、文化の追求』

YAPC::Fukuoka 2025 に参加 && 登壇しました。

yapcjapan.org

タイトルは セキュリティを 「ふつう」にやっていく 技術、体制、文化の追求 です

speakerdeck.com

前日に急遽 発表時間を 20分から40分にしていただけましたが、突貫でセキュリティ関連の話を盛り込むのは難しいなとギリギリまで唸っていました。1

X のスターや はてなブックマークの数もそこそこで伸びていて、Cyber-sec+ Slack や、意外なところでも反応を得られて嬉しく思っています。

リアクションはあったものの具体なコメントはあまりいただけていないので、みなさん、どういうアングルで気になったのかは伺いたいところです。


後から振り返って、以下のような話を追加したかったと内省

  • セキュリティ対策室でオーナーシップを持ってない対策については、見送り
    • サービスで実施している対策も事細かに書くと攻撃を試行されたりバイパスされかねない事情もあるしなぁ
    • とはいえ色々対策は盛り込んでいるので、俯瞰した内容をまたいつか出したい。
  • 運用の苦労話なども見送り
    • 多様なサービスを展開してると、エンドポイントのアラートトリアージもいろんなのに遭遇する
    • SaaS や ポリシーの展開で気を使うこと + IT ヘルプデスクとの協働の仕方なども出せそう

余談: ラーメンを全く食べられなかった

登壇が決まった際にこんなエントリを書いていました。

hiboma.hatenadiary.jp

CfP を書いていた際に後輩とこんな会話をしていたのですが、今回の滞在ではラーメンを食べる機会を悉く逃してタスク達成ならず! 2


  1. 採択されたテーマと外れる、内輪っぽいトーンの話や自己開示的な話は奥の手として出しました
  2. ちょいと疲れが溜まりすぎてて、バタリだった。YAPC 本編のトークもあまり集中して聞くことができてなかった。

while (1) は GCC を happy にする - "The loop is added only to keep gcc happy"

とある Linux コンテナで動くプロセスを GDBデバッグしている最中に glibc の関数を追っていて、以下のようなコードを見つけました

(gdb) l
156 void
157 __libc_fatal (const char *message)
158 {
159   /* The loop is added only to keep gcc happy.  */
160   while (1)
161     __libc_message ("%s", message);
162 }

はて ... ?

  • The loop is added only to keep gcc happy. とは?
  • この while(1) はなんのためにあるのだ?

わからんので エイっと AI に聞いてみました。

以下は Microsoft 365 Copilot Chat の回答です。他の AI 回答も予々一緒で、妥当っぽい回答な気がする。


この while (1) ループは、意図的に無限ループにしてプログラムを停止させるために使われています。 関数 __libc_fatal は、glibcGNU Cライブラリ)の内部で致命的なエラーが発生したときに呼ばれる関数です。以下のような目的があります:

目的と意味

  1. 致命的エラーの通知 __libc_message("%s", message); によって、エラーメッセージを表示します。

  2. プログラムの停止 while (1) によって、関数から戻らず、プログラムを永久に停止させます。これは、致命的なエラーが発生した後に、処理を続けることが危険であるためです。

  3. 最適化回避

コメントにある「The loop is added only to keep gcc happy.」は、GCCGNU Compiler Collection)が最適化の際にこの関数を「戻る可能性がある」と誤認しないようにするためです。無限ループにすることで、コンパイラに「この関数は絶対に戻らない」と明示できます。

補足

通常、こうした関数は __attribute__((noreturn)) を使って「戻らない関数」として宣言されますが、念のために while (1) を入れておくことで、コンパイラの警告や最適化の問題を防いでいます。

glibc のソースへのリンク

upsteream である glibc 2.34 へのリンクを貼っておきます 1

github.com

ソース

void
__libc_fatal (const char *message)
{
  /* The loop is added only to keep gcc happy.  */
  while (1)
    __libc_message (do_abort, "%s", message);
}
libc_hidden_def (__libc_fatal)

abort() の文字列を見て過去に書いた記事を思い出す

__libc_message() は内部で abort() を呼び出すようです。

/* Abort with an error message.  */
void
__libc_message (enum __libc_message_action action, const char *fmt, ...)
{


... 略


  if ((action & do_abort))
    /* Kill the application.  */
    abort ();
}

そういえば、ずいぶん前に abort() の実装を追って調べて、テックブログにまとめたのを思い出しました。

tech.pepabo.com

abort() にも while(1) で HLT 命令を呼び出すコードがありますね ! 詳細はブログ読んでね (宣伝)

  while (1)
    /* Try for ever and ever.  */
    ABORT_INSTRUCTION; 

  1. ⚠️ gdb で調べていた glibcglibc-2.34-196.amzn2023.0.1.x86_64 でした。Amazon Linux 向けのパッチが当たっているのか、__libc_message() の do_abort の引数がありません。本筋には関係ないので詳細は調べていません

toruby 221th に参加しました

toruby 221st - connpass に参加しました。

開催は 18:30 からですが、もう真っ暗。

みなさん、いろんな話題を繰り広げていました

... などで盛り上がりました。自分にはあまり馴染みのないテクニカルな話でワイワイしてました。

後半は応用情報技術者試験の過去問を取り上げてみんなで解いてみる時間でした。

おまけ

公民館の掲示です 。何えもんだ?

YAPC::Fukuoka 2025 に登壇します - ressy 次の打席に立とう

YAPC::Fukuoka 2025 に登壇します。プロポーザルが採択された!

fortee.jp

タイトルは セキュリティを 「ふつう」にやっていく 技術、体制、文化の追求 としています。

fortee.jp

「ふつう」 ってなんじゃい〜? という反応になると思いますが、事業の中でセキュリティをやるのは前提・当たり前に粘り強く進める (シフトレフトとか DevSecOps だとかも近しいところ) ... といった話を平易に表現して話してみたいと思います。


過去をふり返って、もちろん私だけの力で「ふつう」にやってきた・今もやれているとは思っていなくて、過去・現在で いろんな リーダー の力を借りることで協働で実践してきた感じです。

ちょうど 4月に GMOペパボ株式会社に勤めて18年目に突入 - シニアプリンシパル エンジニアに昇格しました - hibomaの日記 ということもあり、時期的にも立場的にも、自分が代表として 全体感をまとめた話をカンファレンスに持っていくのがいいかな〜と考えました。

雑談(1) @rsym1290 / ressy 次の打席に立とう

今回のプロポーザルは、先んじて弊社の三上 ( @rsym1290 / ressy ) が出すぞ!!! と公言していて、彼からプロポーザル概要のレビューも依頼されていました。レビュー作業中に触発されて、"よーし" と私もやる気になった次第です 1

fortee.jp

今回は応募も多数で倍率も高く、残念ながら彼のプロポーザルは採択とはなりませんでした。だがしかし、まず「出すぞ!」 という気持ち・勢いを出して周りを鼓舞していたので 💯

レビューのやり取り

雑談(2)

「テーマが "きゅう" だって? "休" ならまかせろ〜 」 と株式会社 一休 さんからもプロポーザルが出るか ... !? と期待していました 🐘



  1. それまで どうも気分がモニャモニャとしていて、プロポーザルを書くのを一念発起できずにいた。後輩に背中を見せないといけない ... !

toruby 220th に参加しました - 「あそび」がたりない

久しぶりに toruby に参加しました。

toruby.connpass.com

家族(👦)が増えてからは、しばらく足が遠のいていました。

会の前半は、参加者の皆さんによる最近のあれこれの話。あぶくま洞(?)、学生向けのプログラミングイベント、ライフステージの変化、2025年度新聞技術賞、就活(転職)などなど、雑談が続きました。

後半は『研鑽Rubyプログラミング』の「第11章 きちんとコードが動くことをテストする」を読みました。TAD、TDD、BDD といったキーワードが並び、サンプルコードも紹介されましたが、皆さんそれぞれに思うところがあったようで、ぽつぽつと突っ込みが入っていました。


toruby 終了後は、セコンさんとフライングガーデンで晩ごはん。

お互いの家族(👦、👧)の話や、K-POP にはまる家族の話、遠出する話、早朝サイクリングの話、那須は熊が怖い話、那須の人たちの友達ネットワークなど、いろいろ話しました。ごはんはセコンさんにご馳走になりました。


ごはんをご馳走になったの話で、ずいぶん前に danjou さんに焼肉をご馳走になった記憶が蘇った。確か、mizzy さんに連れられて参加したはずで Perl Hacker な皆さんとも席をご一緒したんだったかな ( それはそうと、mizzy さんにもご飯をご馳走になったことは何度もあるな )

ずいぶんしばらく会社の職位制度・評価制度と睨めっこしていたせいか 「コミュニティに参加して、アウトウプットして、会社のプレゼンス向上に寄与する!!1」な考えに凝り固まっていた節がある。「楽しいことやってる話をみんなで交わした」「インターネットで見る、あの人たちとご飯食べた」みたいな "あそび" が足りてないと思ったりした。

GMOペパボ株式会社に勤めて18年目に突入 - シニアプリンシパル エンジニアに昇格しました

2007年から GMOペパボ株式会社 ( 2007年当時の社名は paperboy&co. でしたが ) に勤めて、18年目になりました。

pepabo.com

シニアプリンシパルエンジニアに昇格

先月(3月) 半ばにエンジニア職位制度に立候補し、1次でCTO・技術責任者の2者と面談を実施、2次で経営会議での決議をいただき、シニアプリンシパル エンジニア 1 へ昇格となりました。

tech.pepabo.com

現在の等級表は下図の通りです。等級の詳細は引用元ページを参照してください。

昇格前後の心もち

職位制度が開始してから、10年以上もずっと5等級プリンシパ ルエンジニア 2 の職位でいましたが、その間はずっと プラトー停滞 している感覚に悩まされていました。技術の修練も積み、事業にも結果を出してコミットしている つもり なのだが、次に昇っていける手ごたえがない。

その辺の焦燥感の吐露も含めて、自身のキャリアを振り返りしたのが下記の発表でもありました。

speakerdeck.com

長年、色々とドリフトしながらも ようやく ここ 2-3年内で足枷になっていた何かを振り切った感が出てきました。そして、 昇格 = 外的な変化 を持って解放された!!! という気分になっているのが今の正直な気分です。嬉しいという気持ちももちろんあるけど。

これから何をするのか?

現在はセキュリティ対策室に所属しており、下記のミッションの元、業務遂行に当たっています。

GMOペパボのセキュリティ対策室とは情報セキュリティ基本方針を遵守し お客様、お取引先様、従業員から預る情報資産を適切に扱える 文化形成、技術的仕組みをリードする組織

今後もこのミッションの元に活躍できる領域を広げていきたい気持ちです。おおよそ、 事業の脅威となるもの、全部から守る!!! ( ... と大風呂敷を広げつつ)、 お客様、ステークホルダーの皆様、共に働くパートナー 3 に「安心と安全を届ける」を掲げていきたいと思っています。

余談

改めてこのエントリを書く際、弊社の職位制度についてまとめたメディアを見て回りました。pyama さんがこの (↓ ) 記事書いてから もう7年も経過していたのがびっくりですね。

tech.pepabo.com


  1. GMOペパボ株式会社の業種は情報・通信業です。よって、この文章で「エンジニア」の呼称は情報技術に関わる技術者、いわゆる ITエンジニア を指します。
  2. 5等級の旧称は、アドバンスドシニア・エンジニア
  3. ペパボでは、いっしょに働いている仲間のことを「パートナー」と呼んでいます。 参考: わたしたちが大切にしている3つのこと | GMOペパボ株式会社 採用サイト

AUDIT_LOGIN ( type=LOGIN) , /proc/self/loginuid についての調べ物

/proc/self/loginuid に write すると記録される Audit ログについて調べた

type=LOGIN msg=audit(1727786101.781:162): pid=4232 uid=0 subj=unconfined old-auid=4294967295 auid=0 tty=(none) old-ses=4294967295 ses=3 res=1UID="root" OLD-AUID="unset" AUID="root"

include/uapi/linux/audit.h

AUDIT_LOGIN 定数から追いかけていく。

#define AUDIT_LOGIN             1006    /* Define the login id and information */

AUDIT_LOGIN は audit_set_loginuid() で登場する (後述)。ユーザランドがら追う場合は /proc/self/loginuid の実装から潜っていくのがいいか

ユーザランド

----- /proc/self/loginuid ------------------

Linux カーネル 🐧 ( システムコール や VFS のレイヤは省略 )

-> proc_loginuid_write
  -> audit_set_loginuid
  .. audit_set_loginuid_perm
   -> audit_log_set_loginuid // AUDIT_LOGIN

/proc/self/loginuid

$ ls -hal /proc/self/loginuid 
-rw-r--r-- 1 hiboma hiboma 0 Oct  1 22:33 /proc/self/loginuid

fs/proc/base.c に loginuid の定義がある

REG("loginuid",  S_IWUSR|S_IRUGO, proc_loginuid_operations),

loginuid の file_operations は下記の通り

static const struct file_operations proc_loginuid_operations = {
        .read           = proc_loginuid_read,
        .write          = proc_loginuid_write,
        .llseek         = generic_file_llseek,
};

proc_loginuid_write 以下に潜っていく

proc_loginuid_write()

procfs レイヤと audit のレイヤを橋渡しするような役割になっている。write されたバッファから loginuid を読み取る。

static ssize_t proc_loginuid_write(struct file * file, const char __user * buf,
                   size_t count, loff_t *ppos)
{
    struct inode * inode = file_inode(file);
    uid_t loginuid;
    kuid_t kloginuid;
    int rv;

    /* Don't let kthreads write their own loginuid */
    if (current->flags & PF_KTHREAD)
        return -EPERM;

    rcu_read_lock();
    if (current != pid_task(proc_pid(inode), PIDTYPE_PID)) {
        rcu_read_unlock();
        return -EPERM;
    }
    rcu_read_unlock();

    if (*ppos != 0) {
        /* No partial writes. */
        return -EINVAL;
    }

    rv = kstrtou32_from_user(buf, count, 10, &loginuid);
    if (rv < 0)
        return rv;

    /* is userspace tring to explicitly UNSET the loginuid? */
    if (loginuid == AUDIT_UID_UNSET) {
        kloginuid = INVALID_UID;
    } else {
        kloginuid = make_kuid(file->f_cred->user_ns, loginuid);
        if (!uid_valid(kloginuid))
            return -EINVAL;
    }

    rv = audit_set_loginuid(kloginuid); ⬇️
    if (rv < 0)
        return rv;
    return count;
}

audit_set_loginuid()

  • 権限の確認
  • sessionid の生成
  • current->sessionid をセット
  • current->logind をセット
/**
 * audit_set_loginuid - set current task's loginuid
 * @loginuid: loginuid value
 *
 * Returns 0.
 *
 * Called (set) from fs/proc/base.c::proc_loginuid_write().
 */
int audit_set_loginuid(kuid_t loginuid)
{
    unsigned int oldsessionid, sessionid = AUDIT_SID_UNSET;
    kuid_t oldloginuid;
    int rc;

    oldloginuid = audit_get_loginuid(current); ✍️
    oldsessionid = audit_get_sessionid(current); ✍️

    rc = audit_set_loginuid_perm(loginuid); ✍️
    if (rc)
        goto out;

    /* are we setting or clearing? */
    if (uid_valid(loginuid)) {
        sessionid = (unsigned int)atomic_inc_return(&session_id); ✍️
        if (unlikely(sessionid == AUDIT_SID_UNSET))
            sessionid = (unsigned int)atomic_inc_return(&session_id);
    }

    current->sessionid = sessionid;
    current->loginuid = loginuid;
out:
    audit_log_set_loginuid(oldloginuid, loginuid, oldsessionid, sessionid, rc); ⬇️
    return rc;
}

✍️ audit_get_sessionid, audit_get_sessionid はインライン関数

static inline kuid_t audit_get_loginuid(struct task_struct *tsk)
{
        return tsk->loginuid;
}

static inline unsigned int audit_get_sessionid(struct task_struct *tsk)
{
        return tsk->sessionid;
}

✍️ audit_set_loginuid_perm は 権限の確認を入れている。値がセットされてなければ権限が必要ない。

  • AUDIT_FEATURE_LOGINUID_IMMUTABLE
  • CAP_AUDIT_CONTROL
  • AUDIT_FEATURE_ONLY_UNSET_LOGINUID
static int audit_set_loginuid_perm(kuid_t loginuid)
{
    /* if we are unset, we don't need privs */
    if (!audit_loginuid_set(current))
        return 0;
    /* if AUDIT_FEATURE_LOGINUID_IMMUTABLE means never ever allow a change*/
    if (is_audit_feature_set(AUDIT_FEATURE_LOGINUID_IMMUTABLE))
        return -EPERM;
    /* it is set, you need permission */
    if (!capable(CAP_AUDIT_CONTROL))
        return -EPERM;
    /* reject if this is not an unset and we don't allow that */
    if (is_audit_feature_set(AUDIT_FEATURE_ONLY_UNSET_LOGINUID)
                 && uid_valid(loginuid))
        return -EPERM;
    return 0;
}

✍️ session_id は static atomic_t で管理されてる

/* global counter which is incremented every time something logs in */
static atomic_t session_id = ATOMIC_INIT(0);

audit_log_set_loginuid()

audit_log_start() 〜 audit_log_format 〜 audit_log_end() で audit_buffer バッファに文字列を書き込んでいく。バッファを扱う実装は複雑なので、別記する

static void audit_log_set_loginuid(kuid_t koldloginuid, kuid_t kloginuid,
                   unsigned int oldsessionid,
                   unsigned int sessionid, int rc)
{
    struct audit_buffer *ab;
    uid_t uid, oldloginuid, loginuid;
    struct tty_struct *tty;

    if (!audit_enabled)
        return;

err ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_LOGIN);
    if (!ab)
        return;

    uid = from_kuid(&init_user_ns, task_uid(current));
    oldloginuid = from_kuid(&init_user_ns, koldloginuid);
    loginuid = from_kuid(&init_user_ns, kloginuid);
    tty = audit_get_tty();

    audit_log_format(ab, "pid=%d uid=%u", task_tgid_nr(current), uid);
    audit_log_task_context(ab);
    audit_log_format(ab, " old-auid=%u auid=%u tty=%s old-ses=%u ses=%u res=%d",
             oldloginuid, loginuid, tty ? tty_name(tty) : "(none)",
             oldsessionid, sessionid, !rc);
    audit_put_tty(tty);
    audit_log_end(ab);
}

派生で調べ物

pam_loginuid(8) - Linux manual page

The pam_loginuid module sets the loginuid process attribute forthe process that was authenticated. This is necessary forapplications to be correctly audited. This PAM module should onlybe used for entry point applications like: login, sshd, gdm,vsftpd, crond and atd. There are probably other entry pointapplications besides these. You should not use it forapplications like sudo or su as that defeats the purpose bychanging the loginuid to the account they just switched to.

pam_loginuidモジュールは、認証されたプロセスのloginuidプロセス属性を設定する。これは、アプリケーションを正しく監査するために必要です。このPAMモジュールは、login、sshd、gdm、vsftpd、crond、atdなどのエントリーポイントアプリケーションにのみ使用されるべきである。これら以外にもエントリーポイントのアプリケーションはあるでしょう。sudoやsuのようなアプリケーションには使用しないでください。loginuidを切り替えたアカウントに変更することで、目的が達成されないからです。

root@develop-hiboma:~# grep -R pam_loginuid /etc/
/etc/pam.d/login:session    required     pam_loginuid.so
/etc/pam.d/cron:session    required     pam_loginuid.so
/etc/pam.d/sshd:session    required     pam_loginuid.so

kernel.org/doc/Documentation/ABI/stable/procfs-audit_loginuid

  • AUDIT_FEATURE_LOGINUID_IMMUTABLE
  • AUDIT_FEATURE_ONLY_UNSET_LOGINUID
What:        Audit Login UID
Date:       2005-02-01
KernelVersion:  2.6.11-rc2 1e2d1492e178 ("[PATCH] audit: handle loginuid through proc")
Contact:    linux-audit@redhat.com
Users:      audit and login applications
Description:
        The /proc/$pid/loginuid pseudofile is written to set and
        read to get the audit login UID of process $pid as a
        decimal unsigned int (%u, u32).  If it is unset,
        permissions are not needed to set it.  The accessor must
        have CAP_AUDIT_CONTROL in the initial user namespace to
        write it if it has been set.  It cannot be written again
        if AUDIT_FEATURE_LOGINUID_IMMUTABLE is enabled.  It
        cannot be unset if AUDIT_FEATURE_ONLY_UNSET_LOGINUID is
        enabled.

What:       Audit Login Session ID
Date:       2008-03-13
KernelVersion:  2.6.25-rc7 1e0bd7550ea9 ("[PATCH] export sessionid alongside the loginuid in procfs")
Contact:    linux-audit@redhat.com
Users:      audit and login applications
Description:
        The /proc/$pid/sessionid pseudofile is read to get the
        audit login session ID of process $pid as a decimal
        unsigned int (%u, u32).  It is set automatically,
        serially assigned with each new login.

CAP_AUDIT_CONTROL

       CAP_AUDIT_CONTROL (since Linux 2.6.11)
              Enable and disable kernel auditing; change auditing filter
              rules; retrieve auditing status and filtering rules.

一般ユーザで write しようとすると EPERM を返す

hiboma@pcamp-develop-hiboma:~$ echo 1000 > /proc/self/loginuid 
-bash: echo: write error: Operation not permitted