Ubuntu 시스템 로그 모니터링 및 알림 시스템 구현하기(with rsyslog)

이번 포스트에서는 Python을 사용하여 시스템 로그를 모니터링하고, 웹 대시보드로 표시하며, Telegram으로 알림을 보내는 통합 시스템을 구현하는 방법을 알아보겠습니다.

1. 프로젝트 개요

이 시스템은 다음과 같은 주요 기능들을 제공합니다:

  • 시스템 로그 실시간 모니터링
  • SQLite 데이터베이스에 로그 저장
  • Flask 기반 웹 대시보드
  • Telegram을 통한 알림 전송
  • 주기적인 로그 통계 보고

2. 필요한 라이브러리 및 초기 설정

python
 
import time
import os
import requests
import sqlite3
import threading
from flask import Flask, render_template_string, request, jsonify
TELEGRAM_TOKEN = “YOUR_TOKEN”
CHAT_ID = “YOUR_CHAT_ID”
TELEGRAM_LEVELS = [‘경고’, ‘마이너’, ‘메이저’, ‘치명적’]
LOG_FILE_PATH = “/var/log/syslog”

주요 라이브러리들을 임포트하고 필요한 상수들을 정의합니다. Telegram 봇 토큰과 채팅 ID는 실제 값으로 교체해야 합니다.

3. 데이터베이스 설정

python
 
def create_db_connection():
conn = sqlite3.connect('logs.db', check_same_thread=False)
conn.row_factory = sqlite3.Row
return conn
conn = create_db_connection()
cursor = conn.cursor()
cursor.execute(”’
CREATE TABLE IF NOT EXISTS logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp TEXT,
hostname TEXT,
program TEXT,
level TEXT,
message TEXT
)
”’)
conn.commit()

SQLite 데이터베이스를 설정하고 로그를 저장할 테이블을 생성합니다. check_same_thread=False는 다중 스레드 환경에서 데이터베이스 접근을 가능하게 합니다.

4. Telegram 알림 기능

python
 
def send_to_telegram(message):
url = f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage"
data = {
'chat_id': CHAT_ID,
'text': message
}
try:
response = requests.post(url, data=data)
if not response.ok:
print(f"텔레그램 메시지 전송 실패: {response.text}")
except Exception as e:
print(f"텔레그램 메시지 전송 중 오류 발생: {e}")

Telegram API를 사용하여 메시지를 전송하는 함수입니다. 예외 처리를 통해 전송 실패 시 적절한 에러 메시지를 출력합니다.

5. 로그 파싱 및 처리

python
 
def parse_log_line(line):
try:
parts = line.split(' ')
if len(parts) < 5:
return None
timestamp = ‘ ‘.join(parts[:3])
hostname = parts[3]
program_part = parts[4]
program = program_part.split(‘[‘)[0].strip(‘:’)
message = ‘ ‘.join(parts[5:])
level = “정보”

for l in TELEGRAM_LEVELS:
if f”[{l}]” in message:
level = l
message = message.replace(f”[{l}]”, “”).strip()
break

return {
‘timestamp’: timestamp,
‘hostname’: hostname,
‘program’: program,
‘level’: level,
‘message’: message
}
except Exception as e:
print(f”로그 파싱 중 예외 발생: {e})
return None

로그 라인을 파싱하여 구조화된 데이터로 변환하는 함수입니다. 시간, 호스트명, 프로그램명, 로그 레벨, 메시지를 추출합니다.

6. 실시간 로그 모니터링

python
 
def follow(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
f.seek(0, os.SEEK_END)
while True:
line = f.readline()
if line:
yield line.strip()
else:
time.sleep(0.1)
def log_monitor():
thread_conn = create_db_connection()
for line in follow(LOG_FILE_PATH):
parsed = parse_log_line(line)
if parsed:
save_to_db(
thread_conn,
parsed[‘timestamp’],
parsed[‘hostname’],
parsed[‘program’],
parsed[‘level’],
parsed[‘message’]
)

follow 함수는 파일의 새로운 라인을 지속적으로 모니터링하고, log_monitor 함수는 이를 파싱하여 데이터베이스에 저장합니다.

7. 웹 대시보드 구현

python
 
@app.route('/')
def index():
return render_template_string(TEMPLATE)
@app.route(‘/api/logs’)
def get_logs():
cursor = conn.cursor()
cursor.execute(“SELECT timestamp, hostname, program, level, message FROM logs ORDER BY timestamp DESC LIMIT 20”)
logs = cursor.fetchall()
return jsonify([dict(log) for log in logs])

Flask를 사용하여 웹 대시보드를 구현했습니다. 메인 페이지와 로그를 제공하는 API 엔드포인트를 제공합니다.

 

8. 실행 코드

python
 
if __name__ == '__main__':
threading.Thread(target=log_monitor, daemon=True).start()
threading.Thread(target=send_log_count, daemon=True).start()
app.run(host='0.0.0.0', port=5000)

두 개의 백그라운드 스레드(로그 모니터링, 통계 전송)를 시작하고 Flask 애플리케이션을 실행합니다.

import time
import os
import requests
import sqlite3
import threading
from flask import Flask, render_template_string, request, jsonify

# 텔레그램 토큰 및 채팅 ID 하드코딩
TELEGRAM_TOKEN = “YOUR_TELEGRAM_TOKEN”
CHAT_ID = “YOUR_CHAT_ID”

# 텔레그램으로 전송할 로그 레벨
TELEGRAM_LEVELS = [‘경고’, ‘마이너’, ‘메이저’, ‘치명적’]

# 로그 파일 경로 설정
LOG_FILE_PATH = “/var/log/syslog”

# Flask 애플리케이션 설정
app = Flask(__name__)

# 데이터베이스 설정
def create_db_connection():
conn = sqlite3.connect(‘logs.db’, check_same_thread=False)
conn.row_factory = sqlite3.Row # 딕셔너리 형식으로 결과 반환
return conn

# 데이터베이스 초기화
conn = create_db_connection()
cursor = conn.cursor()
cursor.execute(”’
CREATE TABLE IF NOT EXISTS logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp TEXT,
hostname TEXT,
program TEXT,
level TEXT,
message TEXT
)
”’)
conn.commit()

# 텔레그램 메시지 전송 함수
def send_to_telegram(message):
url = f”https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage”
data = {
‘chat_id’: CHAT_ID,
‘text’: message
}
try:
response = requests.post(url, data=data)
if not response.ok:
print(f”텔레그램 메시지 전송 실패: {response.text}”)
except Exception as e:
print(f”텔레그램 메시지 전송 중 오류 발생: {e}”)

# 데이터베이스에 로그 저장
def save_to_db(conn, timestamp, hostname, program, level, message):
try:
cursor = conn.cursor()
cursor.execute(”’
INSERT INTO logs (timestamp, hostname, program, level, message)
VALUES (?, ?, ?, ?, ?)
”’, (timestamp, hostname, program, level, message))
conn.commit()
except Exception as e:
print(f”데이터베이스 저장 중 오류 발생: {e}”)

# 로그 라인 파싱
def parse_log_line(line):
try:
parts = line.split(‘ ‘)
if len(parts) < 5:
return None

timestamp = ‘ ‘.join(parts[:3])
hostname = parts[3]
program_part = parts[4]
program = program_part.split(‘[‘)[0].strip(‘:’)
message = ‘ ‘.join(parts[5:])
level = “정보”

for l in TELEGRAM_LEVELS:
if f”[{l}]” in message:
level = l
message = message.replace(f”[{l}]”, “”).strip()
break

return {
‘timestamp’: timestamp,
‘hostname’: hostname,
‘program’: program,
‘level’: level,
‘message’: message
}
except Exception as e:
print(f”로그 파싱 중 예외 발생: {e}”)
return None

# 로그 파일 실시간 읽기
def follow(file_path):
with open(file_path, ‘r’, encoding=’utf-8′) as f:
f.seek(0, os.SEEK_END)
while True:
line = f.readline()
if line:
yield line.strip()
else:
time.sleep(0.1)

# 로그 모니터링 스레드
def log_monitor():
thread_conn = create_db_connection()
for line in follow(LOG_FILE_PATH):
parsed = parse_log_line(line)
if parsed:
save_to_db(
thread_conn,
parsed[‘timestamp’],
parsed[‘hostname’],
parsed[‘program’],
parsed[‘level’],
parsed[‘message’]
)

# 3분마다 로그 개수 텔레그램으로 전송
def send_log_count():
thread_conn = create_db_connection()
while True:
cursor = thread_conn.cursor()
cursor.execute(“SELECT COUNT(*) as count FROM logs”)
log_count = cursor.fetchone()[‘count’]
send_to_telegram(f”지난 3분 동안 데이터베이스에 쌓인 로그 개수: {log_count}”)
time.sleep(180)

# Flask 웹 라우트
@app.route(‘/’)
def index():
return render_template_string(TEMPLATE)

@app.route(‘/api/logs’)
def get_logs():
cursor = conn.cursor()
cursor.execute(“SELECT timestamp, hostname, program, level, message FROM logs ORDER BY timestamp DESC LIMIT 20”)
logs = cursor.fetchall()
return jsonify([dict(log) for log in logs])

# HTML 템플릿
TEMPLATE = ”’
<!DOCTYPE html>
<html lang=”ko”>
<head>
<meta charset=”UTF-8″>
<title>로그 대시보드</title>
<style>
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #ddd; padding: 8px; }
th { background-color: #f2f2f2; text-align: left; }
</style>
<script>
async function fetchLogs() {
const response = await fetch(‘/api/logs’);
const logs = await response.json();
const tableBody = document.getElementById(‘logs-body’);
tableBody.innerHTML = ”;
logs.forEach(log => {
const row = `<tr>
<td>${log.timestamp}</td>
<td>${log.hostname}</td>
<td>${log.program}</td>
<td>${log.level}</td>
<td>${log.message}</td>
</tr>`;
tableBody.innerHTML += row;
});
}
setInterval(fetchLogs, 5000); // 5초마다 갱신
window.onload = fetchLogs;
</script>
</head>
<body>
<h1>로그 대시보드</h1>
<table>
<thead>
<tr>
<th>시간</th>
<th>호스트명</th>
<th>프로그램</th>
<th>레벨</th>
<th>메시지</th>
</tr>
</thead>
<tbody id=”logs-body”></tbody>
</table>
</body>
</html>
”’

# Flask 앱 실행
if __name__ == ‘__main__’:
threading.Thread(target=log_monitor, daemon=True).start()
threading.Thread(target=send_log_count, daemon=True).start()
app.run(host=’0.0.0.0′, port=5000)

OpenSSH의 치명적 취약점: regreSSHion (CVE-2024-6387)

OpenSSH의 치명적 취약점: regreSSHion (CVE-2024-6387)

최근, 보안 업계에서 큰 관심을 끄는 취약점이 발견되었습니다. Qualys Threat Research Unit(TRU)은 glibc 기반 Linux 시스템에서 실행되는 **OpenSSH 서버(sshd)**에서 인증되지 않은 원격 코드 실행(RCE) 취약점을 발견했으며, 이를 **regreSSHion (CVE-2024-6387)**이라 명명했습니다. 이 취약점은 심각한 악용 가능성을 내포하고 있으며, 기본 설정으로도 전체 루트 권한을 부여할 수 있어 보안에 큰 위협이 되고 있습니다.

regreSSHion: 치명적인 RCE 취약점

regreSSHion은 인증 없이도 원격에서 코드 실행이 가능하여 공격자가 서버의 루트 권한을 획득할 수 있는 취약점입니다. 이로 인해 사용자 상호 작용이 필요 없으며, OpenSSH 서버가 실행되고 있는 기본 구성에서도 즉시 악용될 수 있습니다. 이 취약점은 OpenSSH 보안에 있어 거의 20년 만에 처음 발견된 큰 결함으로, 특히 주목을 받고 있습니다.

regreSSHion의 배경과 회귀의 의미

이번에 발견된 regreSSHion 취약점은 2006년의 CVE-2006-5051 취약점과 관련이 있습니다. Qualys TRU의 분석에 따르면, 이 취약점은 2006년에 보고되고 패치된 결함이 시간이 지나면서 회귀(regression) 현상을 통해 다시 나타난 경우입니다. 회귀란 이전에 수정되었던 문제가 코드 변경이나 소프트웨어 업데이트 과정에서 실수로 다시 발생하게 되는 상황을 의미합니다. regreSSHion이라는 이름도 회귀 현상과 SSH를 합친 것입니다.

이 회귀는 OpenSSH의 8.5p1 버전(2020년 10월 출시)에서 도입되었으며, 최신 버전인 9.8p1까지 영향을 미치고 있습니다. 이 사건은 철저한 회귀 테스트의 필요성을 다시금 일깨워 주며, 알려진 취약점이 실수로 재도입되는 것을 방지하는 데 매우 중요한 역할을 합니다.

영향을 받는 OpenSSH 버전

  • 4.4p1 이전 버전: CVE-2006-5051 및 CVE-2008-4109의 패치가 적용되지 않았다면, 신호 핸들러 경쟁 조건 취약점에 노출됩니다.
  • 4.4p1부터 8.5p1까지: CVE-2006-5051에 대한 혁신적인 패치 덕분에 이 취약점에 안전합니다.
  • 8.5p1부터 9.8p1까지: 함수의 중요한 구성 요소가 실수로 제거되며, 이로 인해 regreSSHion 취약점이 다시 나타나게 되었습니다.

OpenSSH란 무엇인가?

OpenSSH는 SSH 프로토콜 기반의 보안 네트워킹 유틸리티로, 보안이 요구되는 네트워크 환경에서 강력한 암호화와 원격 서버 관리를 지원합니다. Linux 및 macOS와 같은 Unix 계열 시스템에서 널리 사용되며, 여러 암호화 기술과 강력한 액세스 제어를 통해 네트워크 통신의 기밀성과 무결성을 유지하는 필수 도구로 자리 잡았습니다. 최근 발생한 취약점에도 불구하고, OpenSSH는 오랜 기간 신뢰받아 온 보안 네트워킹 도구입니다.

마무리

이번 regreSSHion(CVE-2024-6387) 취약점은 OpenSSH의 신뢰성을 크게 흔드는 사건이지만, 이를 통해 보안 소프트웨어의 지속적인 회귀 테스트와 강화된 검토가 필수적임을 알게 되었습니다. 향후 보안 패치가 발표될 때까지는 네트워크 보안 관리자가 OpenSSH 서버를 주의 깊게 모니터링하고, 잠재적인 공격에 대비할 필요가 있습니다.

홈 서버 구축을 위한 최적의 선택, Proxmox VE

가정에서 홈 서버나 홈랩을 구축하려고 할 때, 많은 분들이 처음 고민하는 것이 가상화 플랫폼의 선택일 것입니다. VMware vSphere는 업계 표준이지만 라이선스 비용이 만만치 않죠. 오늘은 무료로 사용할 수 있으면서도 엔터프라이즈급 기능을 제공하는 Proxmox VE를 소개하고자 합니다.

비용 부담 없는 시작

  • VMware vSphere의 경우 가정에서 테스트용으로 사용하더라도 라이선스 비용이 발생
  • Proxmox VE는 완전한 오픈소스로 무료로 사용 가능
  • 기업용 서브스크립션이 있지만, 홈랩 용도라면 커뮤니티 버전으로 충분

다운로드 : https://www.proxmox.com/en/downloads

(N100 서버에 실제 구동하고 있는 다양한 서비스들)

놀라운 기본 기능들

Proxmox VE는 생각보다 훨씬 더 많은 기능들을 기본으로 제공합니다:

1. 통합된 스토리지 관리

  • ZFS 기본 지원으로 데이터 안정성 보장
  • Ceph 통합으로 분산 스토리지 구성 가능
  • NFS, iSCSI, SMB 등 다양한 스토리지 프로토콜 지원

2. 백업 및 복구

  • 내장된 백업 기능으로 VM/CT 전체 백업
  • 증분 백업 지원으로 효율적인 백업 관리
  • 스냅샷 기능으로 빠른 복구 지점 생성

3. 고가용성(HA) 기능

  • 무료 버전에서도 클러스터링 지원
  • 노드 간 실시간 마이그레이션
  • 자동 페일오버 구성 가능

4. 컨테이너와 VM 통합 관리

  • KVM 가상머신과 LXC 컨테이너 동시 지원
  • 웹 인터페이스에서 통합 관리
  • 템플릿을 통한 빠른 배포

5. 네트워크 가상화

  • 내장된 방화벽 기능
  • VLAN, 본딩 등 고급 네트워크 기능
  • SDN(Software Defined Networking) 지원

사용자 친화적인 관리

  • 직관적인 웹 인터페이스 제공
  • CLI를 통한 고급 관리 가능
  • REST API로 자동화 구현 용이
  • 상세한 리소스 모니터링 제공

활발한 커뮤니티

  • 광범위한 문서화
  • 활발한 포럼 활동
  • 지속적인 업데이트와 버그 수정
  • 다양한 커뮤니티 템플릿 제공

그렇기에,

Proxmox VE는 단순히 “무료 대안”이 아닌, 엔터프라이즈급 기능을 제공하는 완성도 높은 가상화 플랫폼입니다. VMware의 대안을 찾고 계신다면, 특히 홈랩 환경에서는 Proxmox VE가 최적의 선택이 될 수 있습니다. 무료로 제공되는 기능만으로도 대부분의 홈랩 시나리오를 충분히 커버할 수 있으며, 고급 기능들도 별도의 비용 없이 사용할 수 있다는 점이 큰 장점입니다.

추가로, Proxmox VE는 계속해서 발전하고 있으며, 매 릴리스마다 새로운 기능들이 추가되고 있습니다. 홈랩을 시작하시는 분들께 자신 있게 추천드릴 수 있는 플랫폼입니다.

옵시디언에 로컬 LLM(LM Studio 0.35 최신 버전 기준)연결

LM스튜디오를 처음 실행하면 왼쪽 하단에 디벨로퍼 혹은 파워 유저를 선택한다.

(그래야 디벨로퍼 메뉴가 나옴)

디벨로퍼 메뉴에 들어 오면 우측에
The local server is reachable at this address
요기에 표시되는 사설 ip를 복사한다.
(물론 상단에 스타트 서버를 꼭 눌러 주도록)


옵시디언 -> 설정 -> 커뮤니티 플러그인 -> 탐색에 가서 copilot을 설치해주고 활성화 해준다.

코파일럿 설정으로 와서 Add Custom Model을 선택하고 (*이후 부터 중요함)

모델네임(이미지와 같이 당신이 lm스튜디오에 로드한 모델이름을 입력해 준다)을 잘 작성해주고

프로바이더는 lmstudio를 선택하고

가장 중요한 베이스 url을 잘 입력해 줘야 한다.

lm스튜디오 디벨로퍼 메뉴에 있다. 참고 이미지 참조.

마지막으로 코파일럿에서 애드 모델을 해 주면 윗윗 짤 처럼 모델이 추가 되었을것이다.

CORS항목에 꼭 체크를 잊지 말것! (이거 체크 안하면 서로 통신이 안됨)

로컬 설치된 옵시디언에 웹훅 적용하기.

  1. 옵시디언 실행
  2. 왼쪽 하단의 설정(톱니바퀴) 진입
  3. 커뮤니티 플러그인 메뉴 선택
  4. 커뮤니티 플러그인 탐색 버튼 클릭
  5. 플러그인 검색 항목에서 “webhook” 검색

설치하고 활성화

https://obsidian-buffer.web.app 사이트로 이동 하여

토큰 발행 하고 하단에 webhook url을 받으면 끝!

마지막 테스트로 윈도우에서 터미너이나 쉘 접속 하여

Invoke-WebRequest -Uri “https://us-central1-obsidian-buffer.cloudfunctions.net/webhook/499b114f389f8c050e560acc6e4485ec419b324ab54251cc?path=test/spotify.md” -Method POST
-Headers @{ “Content-Type” = “text/plain” } `
-Body “- This is a test note from the webhook”

실행한 뒤 노트가 생성되었는지 확인 하면 끝.