Puncte:1

Formatați jurnalele SFTP pentru a avea un nume de utilizator pe fiecare intrare

drapel cn

Rulez un server de producție (Debian 10, pachet standard OpenSSH) care rulează Pure-FTPD pentru conexiunile vechi și SFTP pentru toate conexiunile noastre actuale. Serverul SFTP este configurat cu o închisoare chroot care se înregistrează printr-un dispozitiv legat în închisoarea chroot a utilizatorului. Acesta este preluat de rsyslog și trimis la /var/log/sftp.log, după care folosesc logstash pentru a analiza acel fișier și a trimite totul către un server de vizualizare pentru superutilizatorii noștri. Superutilizatorii se conectează la vizualizare pentru a vedea toate jurnalele SFTP și FTP/FTPS într-un singur loc.

Jurnalele pur-ftpd sunt formatate într-un mod în care super-utilizatorii noștri le place:

pure-ftpd: (testuser@hostname) [NOTIZARE] /home/ftpusers/testuser//outbound/testfile.pdf descărcat (1765060 bytes, 5989.55KB/sec)

Acest lucru este grozav, deoarece într-o singură linie arată exact utilizatorul și fișierul exact pe care l-au încărcat sau descărcat. Cu toate acestea, pentru SFTP situația nu este la fel de frumoasă:

internal-sftp[8848]: sesiune deschisă pentru utilizatorul local testuser de la [{ip_address}]
internal-sftp[8848]: opendir „/inbound”
internal-sftp[8848]: realpath "/inbound/."
internal-sftp[8848]: deschideți steaguri „/inbound/testfile.pdf” modul WRITE,CREATE,TRUNCATE 0666
internal-sftp[8848]: închideți „/inbound/testfile.pdf” octeți citiți 0 scrisi 1734445

În acest caz, jurnalele sunt ușor de urmărit. testuser se conectează, scrie fișierul, gata. DAR avem mulți utilizatori care se conectează la un moment dat, iar jurnalele din mai multe instanțe de internal-sftp pot apărea simultan. Dacă se întâmplă acest lucru, singura modalitate de a urmări activitatea unui utilizator este să căutați numele de utilizator testuser, găsiți ID-ul procesului care este înregistrat (8848 în exemplul de mai sus), apoi găsiți orice mesaje care au acel ID de proces. Mulți utilizatori se conectează printr-un cronjob, așa că acest lucru se întâmplă la fiecare 2 minute sau cam asa ceva... când avem 300 de utilizatori care se conectează la intervale regulate, vă puteți imagina că vânătoarea prin atâtea ID-uri de proces ar fi o durere.

Intrebarea mea

Există o modalitate de a prefața fiecare mesaj de jurnal de la sftp-internal cu numele utilizatorului care generează jurnalul? Acest lucru ar trebui să funcționeze într-o închisoare chroot. Nu găsesc nimic despre cum să modific mesajul pe care rsyslog îl generează pentru a include numele de utilizator.

Aș dori să văd ceva similar din jurnalele mele SFTP:

internal-sftp[8848]: (testuser) deschide „/inbound/testfile.pdf” flags WRITE,CREATE,TRUNCATE 0666
internal-sftp[8848]: (testuser) închide „/inbound/testfile.pdf” octeți citiți 0 scrisi 1734445

Starea actuală a configurației

Lanțul meu de proces merge:

ssh -> sftp-internal -> rsyslog (pe local3.*) -> fișier /var/log/sftp.log -> logstash -> export pe serverul de vizualizare

Extras din grupul meu chroot din /etc/ssh/sshd_config

Grupul de meciuri sftpusers 
        ChrootDirectory %h
        AuthorizedKeysFile %h/.ssh/authorized_keys
        ForceCommand internal-sftp -f local3 -l INFO
        # ForceCommand internal-sftp -l VERBOSE
        AllowTcpForwarding nr
        X11Redirecționare nr

și /etc/rsyslog.d/sftp.conf al meu

local3.* -/var/log/sftp.log

Întrebări similare:

Această întrebare este despre înregistrarea SFTP în fișiere separate, dar menționează acest intrare waybackmachine pentru un articol vechi care includea intrări de jurnal SFTP destul de formatate pentru a arăta ca xferlog-urile standard.Articolul menționează un script Perl (sfântul graal) care îl va formata pentru tine, dar din păcate, linkul este mort. Aș putea scrie un script Python sau Perl care să găsească mesajul specific pentru transferuri, să ia id-ul procesului și să caute invers pentru a găsi utilizatorul, apoi să imprime un mesaj xfer reformatat cu numele de utilizator într-un fișier. Dar cu siguranță cineva a rezolvat această problemă înainte și are o soluție mai bună.

Vă mulțumesc pentru orice ajutor.

Puncte:0
drapel cn

Am reușit să construiesc o soluție cu Python și systemd. Aceasta este foarte rapid și murdar, dar funcționează pentru scopurile mele. Iau un fișier jurnal intern sftp și îl arunc într-unul reformatat. eu nu modifica originalul în cazul în care acest formatator face erori.

Scriptul Python

Aceasta se deconectează la rsyslog pentru monitorizare și răspunde la SEGINT de la systemd. Da, ar trebui să folosească ceva mai bun decât o listă, dar python nu are buffer-uri de inel sau sistem formal de așteptare încorporat (trimite-mi un comentariu dacă îmi lipsește ceva). Oricum... acesta nu este C!

#!/usr/bin/python3

jurnal de import
import re
import sys
timpul de import


clasa SFTPLogFormatter:

    def __init__(self, infile: str, outfile: str):
        self.logger = logging.getLogger(__name__)
        self.logger.setLevel(logging.DEBUG)
        stdout_handler = logging.StreamHandler()
        stdout_handler.setLevel(logging.DEBUG)
        stdout_handler.setFormatter(logging.Formatter('%(levelname)8s | %(message)s'))
        self.logger.addHandler(stdout_handler)

        self.infile = deschis(infile, 'r')

        # atașați fișierului și păstrați doar 1 rând într-un buffer de scriere (scrieți aproape
        # imediat)
        self.outfile = open(outfile, 'a', 1)

    def start(self):
        încerca:
            self.logger.info('pornirea formatatorului')
            self.run()
        cu excepția KeyboardInterrupt:
            self.logger.warning('SIGINT primit, ieșire cu grație')
            self.stop()

    @staticmethod
    def tail_file(fișier_obj):
        în timp ce este adevărat:
            linie = file_obj.readline()
            # sleep dacă fișierul nu a fost actualizat
            daca nu linie:
                timp.somn(1)
                continua

            linie de randament

    def run(self):
        self.infile.seek(0, 2) # sări la sfârșitul fișierului pentru comportamentul de tip `tail -f`
        lines_read = []
        pentru linia din self.tail_file(self.infile): # coadă un fișier ca `tail -f`
            lines_read.insert(0, line) # tratează lista ca pe o stivă
            lines_read = lines_read[:2000] # trim stack, deoarece python nu are tampon inel

            modifyline_match = re.match(r'(.*)\[(\d+)\]: (deschide|închide|elimină numele) (.*)', linie) 
            dacă nu modifyline_match:
                self.logger.info(linie)
                self.outfile.write(linie)
                continua

            modify_line_procid = modifyline_match.group(2)

            self.logger.debug(f'căutând instrucțiunea de deschidere a sesiunii pentru șirul de potrivire a fișierului deschis|închidere: \"{modifyline_match.group(0)}\"')
            open_session_regex = rf'.*\[{modify_line_procid}\]: sesiune deschisă pentru utilizatorul local (.*) din.*'
            open_session_match = Nici unul
            pentru prevline în lines_read[1:]:
                open_session_match = re.match(open_session_regex, prevline)
                dacă open_session_match:
                    self.logger.debug(f'found sesiune deschisă șir: \"{open_session_match.group(0)}\"')
                    pauză
            altceva:
                # nu am găsit nimic
                self.logger.debug('nu a putut găsi șirul de sesiune deschisă pentru: \"{modifyline_match.group(0)}\"')
                continua

            modify_line_start = modifyline_match.group(1)
            modify_line_operator = modifyline_match.group(3)
            modify_line_details = modifyline_match.group(4)

            nume de utilizator = open_session_match.group(1)

            log_str = f'{modify_line_start}[{modify_line_procid}]: (user={username}) {modify_line_operator} {modify_line_details}\n'
            self.logger.info(log_str)
            self.outfile.write(log_str)

    def stop(self):
        self.logger.info(„curățare”)
        încerca:
            self.infile.close()
        cu excepția excepției ca e:
            self.logger.error(f'eșec la închiderea fișierului intern: {e}')

        încerca:
            self.outfile.close()
        cu excepția excepției ca e:
            self.logger.error(f'eșec la închiderea fișierului de ieșire: {e}')

        self.logger.info('exit')
        sys.exit(0)


if __name__ == '__main__':
    infile = sys.argv[1]
    outfile = sys.argv[2]
    serviciu = SFTPLogFormatter (fișier de intrare, fișier de ieșire)
    service.start()

Fișier de serviciu

Următorul fișier de serviciu a fost creat și activat în systemd.

[Unitate]
Description=Formatează mesajele de jurnal de la sftp pentru a avea numele de utilizator pe orice fișier citește, scrie și șterge, făcând jurnalele multi-utilizator mult mai ușor de citit.
După=rețea.țintă

[Serviciu]
Utilizator=rădăcină
Tip=simplu
ExecStart=/usr/bin/python3 /home/admin/services/format_sftp_logs_with_username.py /var/log/sftp.log /var/log/sftp_with_usernames.log
KillSignal=SIGINT

[Instalare]
WantedBy=multi-user.target

Rezultate

Rezultă următoarele mesaje de jurnal. Observați completările (utilizator=XYZ).

11 februarie 21:22:01 ip-10-20-0-96 internal-sftp[18241]: sesiune deschisă pentru utilizatorul local testuser de la [127.0.0.1]
11 februarie 21:22:02 ip-10-20-0-96 internal-sftp[18241]: opendir „/”
11 februarie 21:22:02 ip-10-20-0-96 internal-sftp[18241]: closedir „/”
11 februarie 21:22:05 ip-10-20-0-96 internal-sftp[18241]: opendir „/inbound”
11 februarie 21:22:05 ip-10-20-0-96 internal-sftp[18241]: closedir „/inbound”
11 februarie 21:22:10 ip-10-20-0-96 internal-sftp[18241]: opendir „/inbound/”
11 februarie 21:22:10 ip-10-20-0-96 internal-sftp[18241]: closedir „/inbound/”
11 februarie 21:22:12 ip-10-20-0-96 internal-sftp[18241]: (user=testuser) deschide „/inbound/mailhog-deployment.yaml” semnalează modul CITIȚI 0666
11 februarie 21:22:12 ip-10-20-0-96 internal-sftp[18241]: (user=testuser) închideți „/inbound/mailhog-deployment.yaml” octeți citiți 815 scrisi 0
11 februarie 21:22:13 ip-10-20-0-96 internal-sftp[18241]: opendir „/inbound/”
11 februarie 21:22:13 ip-10-20-0-96 internal-sftp[18241]: closedir „/inbound/”
11 februarie 21:22:14 ip-10-20-0-96 internal-sftp[18241]: opendir „/inbound/”
11 februarie 21:22:14 ip-10-20-0-96 internal-sftp[18241]: closedir „/inbound/”
11 februarie 21:22:14 ip-10-20-0-96 internal-sftp[18241]: (user=testuser) eliminați numele „/inbound/mailhog-deployment.yaml”
11 februarie 21:22:18 ip-10-20-0-96 internal-sftp[18241]: (user=testuser) deschide „/inbound/mailhog-deployment.yaml” flags WRITE,CREATE,TRUNCATE mode 0644
11 februarie 21:22:18 ip-10-20-0-96 internal-sftp[18241]: (user=testuser) închideți „/inbound/mailhog-deployment.yaml” octeți citiți 0 scrisi 815
11 februarie 21:22:19 ip-10-20-0-96 internal-sftp[18241]: sesiune închisă pentru utilizatorul local testuser de la [127.0.0.1]

Limitări

Buffer-ul are o linie de 2000 de linii pentru a căuta ID-ul procesului. Mai mult dacă aveți zeci sau sute de utilizatori care se conectează la un moment dat. În caz contrar, aceasta ar trebui să acopere majoritatea nevoilor serverului.

Postează un răspuns

Majoritatea oamenilor nu înțeleg că a pune multe întrebări deblochează învățarea și îmbunătățește legătura interpersonală. În studiile lui Alison, de exemplu, deși oamenii își puteau aminti cu exactitate câte întrebări au fost puse în conversațiile lor, ei nu au intuit legătura dintre întrebări și apreciere. În patru studii, în care participanții au fost implicați în conversații ei înșiși sau au citit transcrieri ale conversațiilor altora, oamenii au avut tendința să nu realizeze că întrebarea ar influența – sau ar fi influențat – nivelul de prietenie dintre conversatori.