확인해 보니 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()
libsystemd
SOCK_DGRAM
getsockopt(SO_PEERCRED)
systemd-journald
rsyslogd
SOCK_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. 일반적인 답변.