カーネルが教えてくれます。

カーネルが教えてくれます。

確認してみると、journalctlログエントリのPIDとプログラム名(またはサービス名?)を教えてくれます。

その後、ログは他のプロセスによって生成され、プロセスがリッスンしているUnixドメインソケットにのみ生の文字列を書き込むことができる場合は、systemd-journaldこれらのプロセスのPIDを知る方法がわかりますsystemd-journald。またsytemd-journald、プロセスが次の機能を使用してログを生成しても、ログデータフラグメントのPIDを検出するために常に同じテクノロジが使用されますかsd_journal_sendv()

読むべき文書がありますか?

私は読んだJdeBPの答えUnix Domian ソケットを受け取ることを知っていますが、systemd-journaldログメッセージを送信するピアソケットアドレスを知っていても、PID をどのように知ることができますか?送信ソケットが親以外のプロセスと子プロセスによって開かれるとどうなりますか?

答え1

SCM_CREDENTIALSこれはUnixソケットの補助データを通してpidを受け取ります。recvmsg()参照unix(7)。資格情報を明示的に送信する必要はありません。

例:

$ cc -Wall scm_cred.c -o scm_cred
$ ./scm_cred
scm_cred: received from 10114: pid=10114 uid=2000 gid=2000

データを含むプロセスはCAP_SYS_ADMIN希望のpidを送信できますSCM_CREDENTIALS。つまりsystemd-journald、他のプロセスが記録したようにアイテムを偽造することができます。

# cc -Wall fake.c -o fake
# setcap CAP_SYS_ADMIN+ep fake

$ ./fake `pgrep -f /usr/sbin/sshd`

# journalctl --no-pager -n 1
...
Dec 29 11:04:57 debin sshd[419]: fake log message from 14202
# rm fake
# lsb_release -d
Description:    Debian GNU/Linux 9.6 (stretch)

systemd-journald補助データを介して送信されたデータグラムと資格情報を処理します。server_process_datagram()journald-server.cデフォルトでは、標準関数syslog(3)はソケットを介してデータを送信し、データグラム(接続なし)ソケットでは機能しませんlibc。接続を受け入れたり受け入れたりしません。sd_journal_sendv()libsystemdSOCK_DGRAMgetsockopt(SO_PEERCRED)systemd-journaldrsyslogdSOCK_STREAM/dev/log

scm_cred.c

#define _GNU_SOURCE     1
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <err.h>

int main(void){
        int fd[2]; pid_t pid;
        if(socketpair(AF_LOCAL, SOCK_DGRAM, 0, fd)) err(1, "socketpair");
        if((pid = fork()) == -1) err(1, "fork");
        if(pid){ /* parent */
                int on = 1;
                union {
                        struct cmsghdr h;
                        char data[CMSG_SPACE(sizeof(struct ucred))];
                } buf;
                struct msghdr m = {0};
                struct ucred *uc = (struct ucred*)CMSG_DATA(&buf.h);
                m.msg_control = &buf;
                m.msg_controllen = sizeof buf;
                if(setsockopt(fd[0], SOL_SOCKET, SO_PASSCRED, &on, sizeof on))
                        err(1, "setsockopt");
                if(recvmsg(fd[0], &m, 0) == -1) err(1, "recvmsg");
                warnx("received from %d: pid=%d uid=%d gid=%d", pid,
                        uc->pid, uc->uid, uc->gid);
        }else   /* child */
                write(fd[1], 0, 0);
        return 0;
}

偽C

#define _GNU_SOURCE     1
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <err.h>

int main(int ac, char **av){
        union {
                struct cmsghdr h;
                char data[CMSG_SPACE(sizeof(struct ucred))];
        } cm;
        int fd; char buf[256];
        struct ucred *uc = (struct ucred*)CMSG_DATA(&cm.h);
        struct msghdr m = {0};
        struct sockaddr_un ua = {AF_UNIX, "/dev/log"};
        struct iovec iov = {buf};
        if((fd = socket(AF_LOCAL, SOCK_DGRAM, 0)) == -1) err(1, "socket");
        if(connect(fd, (struct sockaddr*)&ua, SUN_LEN(&ua))) err(1, "connect");
        m.msg_control = &cm;
        m.msg_controllen = cm.h.cmsg_len = CMSG_LEN(sizeof(struct ucred));
        cm.h.cmsg_level = SOL_SOCKET;
        cm.h.cmsg_type = SCM_CREDENTIALS;
        uc->pid = ac > 1 ? atoi(av[1]) : getpid();
        uc->uid = ac > 2 ? atoi(av[2]) : geteuid();
        uc->gid = ac > 3 ? atoi(av[3]) : getegid();
        iov.iov_len = snprintf(buf, sizeof buf, "<13>%s from %d",
                ac > 4 ? av[4] : "fake log message", getpid());
        if(iov.iov_len >= sizeof buf) errx(1, "message too long");
        m.msg_iov = &iov;
        m.msg_iovlen = 1;
        if(sendmsg(fd, &m, 0) == -1) err(1, "sendmsg");
        return 0;
}

答え2

カーネルが教えてくれます。

AF_LOCALストリームソケットに接続されている元のクライアントプロセスのEUID、EGID、およびPIDは、ソケット/run/systemd/journal/stdoutオプションを介してカーネルから取得できますSO_PEERCREDそれはどちらを使用しますか?。 UCSPI-UNIX ツールは、同じシステムコールを通じて同じ情報を取得します。

もちろん、子サービスプロセスはオープンスタンダードI / Oファイル記述子を継承します(もちろん、親サービスプロセスがそれを変更しない限り)、systemd-journaldすべてのログ出力には元の親プロセスの資格情報があります。

AF_LOCALソケットによって生成されたログ出力は、/run/systemd/journal/socket特殊systemd-journaldプロトコルがストリームソケットではなくデータグラムソケットを介して提供されていることを示します。このソケットSO_PASSCREDソケットオプションで表示カーネルは、送信されたすべてのデータグラムに同じ情報を書き込みます。各データグラムからインポート合格systemd-journald

追加読書

  • getsockopt()Linuxプログラマーズマニュアル。 2017年9月15日。
  • socketLinuxプログラマーズマニュアル。 2018年2月2日。
  • ジョナサンデボインポラード(2017)。local-stream-socket-acceptスナックガイド。ソフトウェア。
  • ジョナサンデボインポラード(2015)。 」環境変数」。 UNIXクライアント - サーバープログラミングインターフェースのgen。一般的な答え。

関連情報