커널이 알려줍니다.

커널이 알려줍니다.

확인해 보니 journalctl로그 항목의 PID와 프로그램 이름(또는 서비스 이름?)을 알려줍니다.

그런 다음 로그는 다른 프로세스에 의해 생성되며 프로세스가 수신 대기 중인 Unix 도메인 소켓에만 원시 문자열을 쓸 수 있는 경우 systemd-journald이러한 프로세스의 PID를 아는 방법이 궁금합니다 systemd-journald. 또한 sytemd-journald프로세스가 다음과 같은 기능을 사용하여 로그를 생성하더라도 로그 데이터 조각의 PID를 감지하는 데 항상 동일한 기술이 사용됩니까 sd_journal_sendv()?

읽어야 할 문서가 있나요?

나는 읽었다JdeBP의 답변Unix Domian 소켓을 수신하는 것을 알고 있지만 systemd-journald로그 메시지를 보내는 피어 소켓 주소를 알 수 있더라도 PID를 어떻게 알 수 있습니까? 송신 소켓이 부모가 아닌 프로세스와 자식 프로세스에 의해 열리면 어떻게 되나요?

답변1

SCM_CREDENTIALS이는 유닉스 소켓의 보조 데이터를 통해 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().리눅스 프로그래머 매뉴얼. 2017년 9월 15일.
  • socket.리눅스 프로그래머 매뉴얼. 2018년 2월 2일.
  • 조나단 데보인 폴라드(2017).local-stream-socket-accept.스낵 가이드. 소프트웨어.
  • 조나단 데보인 폴라드(2015). "환경 변수". UNIX 클라이언트-서버 프로그래밍 인터페이스의 gen. 일반적인 답변.

관련 정보