Цей цикл статей присвячений файлам журналів (логам) веб-сервера Apache, розглядається їх налаштування, формат, команди, а також спеціальні програми для аналізу журналів веб-сервера. HTTP-сервер Apache надає безліч різних механізмів для реєстрації всього, що відбувається на вашому сервері, від початкового запиту та процесу зіставлення URL-адрес до остаточного дозволу з’єднання, включаючи будь-які помилки, які могли виникнути в процесі. На додаток до цього сторонні модулі можуть надавати можливості ведення журналів або вставляти записи в існуючі файли журналів, а програми, такі як програми CGI, сценарії PHP або інші обробники, також можуть надсилати повідомлення до журналу помилок сервера. Журнали веб-сервер містять багато цікавої інформації! За логами доступу сервера можна скласти збірний портрет аудиторії: в яких країнах і містах живуть, якими операційними системами користуються, якими браузерами переглядають сайт, в який час найбільше активні.
З яких сайтів прийшли до вас, які пошукові системи воліють, скільки сторінок переглядають за кожне відвідування сайту. І не менш важливими є логи для моніторингу стану веб-сервера та сайтів: які сторінки не були знайдені, помилки веб-сервера, ступінь завантаженості, виявлення активності ботів, виявлення шкідливої активності, пошук слідів злому, виявлення шляхів злому. Загалом, журнали доступу сервера повинні розуміти, вміти налаштовувати та використовувати, насамперед, веб-майстри та системні адміністратори, які обслуговують сервер. У той же час, атакуючому, або тому, хто досліджує наслідки дій атакуючого, також потрібно розуміти, що саме зберігається в логі веб-сервера, яку вигоду з них можна отримати або як замаскувати свої сліди, або як аналізувати файли журналів доступу для пошуку проблем, атак та слідів злому.
Різні види журналів Apache керуються різними модулями веб-сервера і мають різні директиви, що управляють, і можливості вказати формат рядка лога. Є такі види логів веб-сервера Apache:
Журнали виконання CGI скриптів – Якщо не вказано ScriptLog, журнал помилок не створюється. Якщо ScriptLog встановлений, будь-які помилки CGI реєструються у файлі, вказаному як аргумент.
Per-module logging (журналювання подій модулів) – Директива LogLevel дозволяє вказувати рівень важливості журналу для кожного модуля. Таким чином, якщо ви вирішуєте проблему тільки з одним конкретним модулем, ви можете збільшити його обсяг у журналі, при цьому не отримуючи зайвої інформації про інші модулі, які вас не цікавлять. Це особливо корисно для таких модулів, як mod_proxy або mod_rewrite, де ви хочете дізнатися подробиці про те, що він намагається зробити.
Модуль mod_log_config забезпечує гнучке журналування запитів клієнта. Логи пишуться у форматі, що настроюється і можуть бути записані безпосередньо у файл або у зовнішню програму. Надається запис логів за умови, тобто індивідуальні запити можуть бути включені або виключені з журналу на основі характеристики запиту. Цей модуль є ключовим для забезпечення роботи Access Log (журнал доступу). Цей модуль підтримує такі директиви:
Директиви TransferLog та CustomLog на кожному сервері можуть використовуватися кілька разів, щоб кожен запит реєструвався у кількох файлах.
Журнал доступу до сервера записує всі запити, оброблені сервером. Розташування та вміст журналу доступу контролюються директивою CustomLog. Директиву LogFormat можна використовувати для спрощення вибору вмісту журналів. У цьому розділі описано, як настроїти сервер для запису інформації в журнал доступу. Звичайно, збереження інформації в журналі доступу – це лише початок керування журналом. Наступним кроком є аналіз цієї інформації для отримання корисної статистики. Аналіз журналів загалом виходить не є частиною роботи самого веб-сервера, але буде розглянуто в одній із наступних статей цього циклу. Різні версії Apache httpd використовували інші модулі та директиви для керування журналом доступу, включаючи mod_log_referer, mod_log_agent та директиву TransferLog. Директива CustomLog тепер включає функціональність всіх старих директив. Формат журналу доступу легко налаштовується. Формат вказується з використанням рядка формату, який дуже схожий на рядок формату printf(1) у стилі C.
Тобто з практичної точки зору, Access Log (журнал доступу) це те саме, що і mod_log_config, оскільки саме цей модуль забезпечує функціональність Access Log. Додатково Access Log використовують модулі mod_logio та mod_setenvif для розширення функціональності. Наприклад, модуль mod_logio дозволяє записувати в журнал точний розмір переданих та/або отриманих даних під час запиту користувача та відповіді йому. Оскільки це те саме, то директиви у Access Log і mod_log_config однакові.
Аргументом формату для директив LogFormat та CustomLog є рядок. На основі цього рядка буде сформовано запис у файл журналу для кожного запиту. Цей рядок може містити буквальні символи, які будуть скопійовані у файли журналу такими, як вони є, та керуючі символи в стилі C “n” і “t” для запису символів new-line (новий рядок) та tab (табуляція).
Буквальні лапки та зворотні слеші слід екранувати за допомогою зворотної косої межі (). Різні характеристики запиту позначаються рядками, які починаються із символу %. У файлі журналу такі рядки будуть замінені такими значеннями, наведеними у Скріншоті 1, Скріншоті 2, Скріншоті 3.
Окремі елементи можуть бути обмежені для друку тільки для відповідей з певними кодами стану HTTP, розміщуючи розділений комами список кодів стану відразу після “%”. Списку кодів стану може передувати “!” щоб вказати заперечення. Модифікатори «<» і «>» потрібні для вибору, чи слід записувати вихідний або остаточний запит. Це можна використовувати для запитів, які перенаправлені всередині (Скріншот 4). За замовчуванням % директиви %s, %U, %T, %D і %r дивляться на вихідний запит, а решта дивляться на остаточний запит.
Так, наприклад, %>s можна використовувати для запису остаточного стану запиту, а % можна використовувати для запису вихідного автентифікованого користувача на запит, внутрішньо перенаправленому на неавтентифікований ресурс.
З міркувань безпеки, починаючи з версії 2.0.46, недруковані та інші спеціальні символи %r, %i і %o екрануються за допомогою послідовностей xhh, де hh позначає шістнадцяткове уявлення необробленого байта. Винятками з цього правила є ” і , які екрануються шляхом додавання зворотної косої риси та всіх пробілових символів, які записуються з використанням нотації в стилі C (n, t і т. д.). У версіях, що передують 2.0.46, для цих рядків екранування не виконувалось, тому ви повинні бути досить обережними при роботі з необробленими лог-файлами в цих версіях. Оскільки в httpd 2.0, на відміну від 1.3, рядки формату %b і %B представляють не кількість байтів, відправлених клієнту, а просто розмір у байтах відповіді HTTP (який буде відрізнятися, наприклад, якщо з’єднання перервано) або якщо використовується SSL). Формат %O, що надається mod_logio, реєструє фактичну кількість байтів, надісланих по мережі. Примітка: mod_cache реалізований як швидкий обробник, а не стандартний обробник. Отже, рядок формату %R не повертатиме жодної інформації оброблювача, коли задіяно кешування вмісту. Символ «^» на початку трьохсимвольних форматів не має значення, але він має бути першим символом будь-якого новоствореного трисимвольного формату, щоб уникнути потенційних конфліктів з форматами журналів, у яких використовуються буквальні рядки, суміжні зі специфікатором формату, такі як %Dus.
Контекст:
Директива BufferedLogs змушує mod_log_config зберігати кілька записів журналу в пам’яті та записувати їх разом на диск, а не записувати їх після кожного запиту. У деяких системах це може призвести до більш ефективного доступу до диска і, отже, до більш високої продуктивності. Він може бути встановлений лише один раз для всього сервера; його не можна налаштувати для кожного віртуального хоста. Цю директиву слід використовувати обережно, оскільки збій може призвести до втрати даних журналу.
Синтаксис:
Контекст:
Директива CustomLog використовується для реєстрації запитів на сервер. Вказується формат журналу, спосіб запису в журнал, тут же можна вказати умову на основі характеристик запиту з використанням змінних середовища при якому буде зроблено запис в журналі. Перший аргумент, який вказує місце, в яке записуватимуться журнали, може приймати одне з наступних трьох типів значень:
Другий аргумент показує, що буде записано у файл журналу. Він може вказувати або псевдонім, визначений попередньою директивою LogFormat, або це може бути явний рядок формату. Наприклад, такі два набори директив мають абсолютно однаковий ефект:
Третій аргумент є необов’язковим та визначає, реєструвати чи ні конкретний запит. Умовою може бути наявність або відсутність (у разі пропозиції ‘env=!name‘) певної змінної серед сервера. Альтернативно, умова може бути виражена як довільний логічний вираз. Якщо умова не виконана, запит не буде зареєстровано. Посилання на заголовки HTTP у виразі не призводять до додавання імен заголовків у заголовок Vary. Змінні середовища можуть бути встановлені для кожного запиту за допомогою модулів mod_setenvif та/або mod_rewrite. Наприклад, якщо ви хочете записати запити для всіх зображень GIF на вашому сервері в окремий файл журналу, але не в основний журнал, ви можете використовувати:
Або, щоб відтворити поведінку старої директиви RefererIgnore, можна використовувати таке:
Синтаксис:
Контекст:
Сумісність: Доступна в Apache HTTP Server 2.4.19 та пізніших. Директива GlobalLog визначає журнал, загальний для конфігурації основного сервера та всіх налаштованих віртуальних хостів. Директива GlobalLog ідентична директиві CustomLog, за винятком таких відмінностей:
GlobalLog неприпустима у контексті віртуального хоста.
GlobalLog використовується віртуальними хостами, які визначають власний CustomLog, а не глобально визначений CustomLog.
Контекст:
Ця директива визначає формат файлу журналу доступу. Директива LogFormat може приймати одну із двох форм. У першій формі, де вказано лише один аргумент, ця директива встановлює формат журналу, який використовуватиметься журналами, зазначеними у наступних директивах TransferLog. Один аргумент може вказувати явний формат, як обговорювалося в розділі про формати користувача журналів вище. Крім того, він може використовувати псевдонім для посилання на формат журналу, визначений у попередній директиві LogFormat, як описано нижче. Друга форма директиви LogFormat пов’язує явний формат із псевдонімом. Цей псевдонім можна використовувати в наступних директивах LogFormat або CustomLog, а не повторювати весь рядок формату. Директива LogFormat, що визначає псевдонім, більше нічого не робить, тобто визначає лише псевдонім, фактично не застосовує формат і не встановлює його за умовчанням. Це не вплине на наступні директиви TransferLog. Крім того, LogFormat не може використовувати один псевдонім для визначення іншого псевдоніму. Зауважте, що псевдонім не повинен містити знаки відсотка (%).
Приклад:
Ця директива має ті ж аргументи та ефект, що й директива CustomLog, за винятком того, що вона не дозволяє явно вказувати формат журналу або реєструвати запити на основі умов. Натомість формат журналу визначається останньою зазначеною директивою LogFormat, яка не визначає псевдонім. Загальний формат журналу використовується, якщо не вказано інший формат.
Приклад:
Типова конфігурація для журналу доступу може виглядати так:
Вона задає псевдонім common і пов’язує його з певним рядком формату журналу. Рядок формату складається з директив зі знаком відсотка, кожна з яких вказує серверу реєструвати певний фрагмент інформації. Буквальні символи також можуть бути поміщені у рядок формату та будуть скопійовані безпосередньо у виведення журналу. Символ лапки (“) повинен бути екранований шляхом розміщення зворотної косої межі перед ним, щоб він не інтерпретувався як кінець рядка формату. Рядок формату також може містити спеціальні керуючі символи “n” для нового рядка і “t” для табуляції. Директива CustomLog встановлює новий файл журналу, використовуючи певний псевдонім. Ім’я файлу для журналу доступу визначається щодо ServerRoot, якщо воно не починається з косої межі. Наведена вище конфігурація записуватиме записи журналу у форматі, відомому як Common Log Format (CLF). Цей стандартний формат може створюватися багатьма різними веб-серверами та зчитуватись багатьма програмами аналізу журналів. Записи файлу журналу, створені в CLF, будуть виглядати приблизно так:
[день/місяць/рік:година:хвилина:секунда зона]
день = 2 * цифри
місяць = 3*літери
рік = 4*цифри
година = 2*цифри
хвилина = 2 * цифри
секунда = 2 * цифри
зона = (`+’ | `-‘) 4*цифри
Можна відобразити час в іншому форматі, вказавши %{format}t у рядку формату журналу, де формат такий самий, як у strftime(3) зі стандартної бібліотеки C, або один із спеціальних маркерів, що підтримуються.
Інший широко використовуваний рядок формату називається Combined Log Format. Може використовуватися в такий спосіб:
Цей формат такий самий, як Common Log Format, з додаванням ще двох полів. Кожне з додаткових полів використовує директиву з відсотком %{header}i, де header може бути будь-яким заголовком HTTP-запиту. Журнал доступу в цьому форматі виглядатиме так:
Журнали множинного доступу можуть бути створені шляхом вказівки кількох директив CustomLog у файлі конфігурації. Наприклад, такі директиви створять три журнали доступу. Перший містить основну інформацію CLF, а другий і третій містять інформацію про реферера та браузер. Останні два рядки CustomLog показують, як імітувати ефекти директив ReferLog та AgentLog.
Цей приклад також показує, що немає потреби визначати псевдонім за допомогою директиви LogFormat. Натомість формат журналу може бути вказаний безпосередньо в директиві CustomLog.
Є моменти, коли зручно виключати певні записи журналів доступу на основі характеристик клієнтського запиту. Це легко зробити за допомогою змінних середовища. По-перше, необхідно встановити змінне середовище, щоб вказати, що запит задовольняє певним умовам. Зазвичай це досягається за допомогою SetEnvIf. Потім умова env= директиви CustomLog використовується для включення або виключення запитів, в яких встановлено змінне середовище. Деякі приклади:
Як інший приклад розглянемо запис запитів від англомовних користувачів до одного файлу журналу, а чи не говорять англійською — до іншого файл журналу.
У сценарії кешування хотілося б дізнатися про ефективність кешу. Дуже простий спосіб з’ясувати це буде:
mod_cache буде запущено до mod_env і, у разі успіху, доставить контент без нього. У цьому випадку кеш призведе до появи запису, а якщо кеш відсутній, то буде записано 1. Крім синтаксису env=, LogFormat підтримує значення реєстрації змінних, що залежать від коду відповіді HTTP:
У першому прикладі User-agent буде записано в журнал, якщо код стану HTTP дорівнює 400 або 501. В інших випадках замість нього буде записано буквальний рядок «-». Аналогічно, у другому прикладі Referer буде записано в журнал, якщо код стану HTTP не дорівнює 200, 204 або 302 (зверніть увагу на «!» перед кодами стану). Хоча ми щойно показали, що умовне ведення журналу є дуже потужним і гнучким, це єдиний спосіб управління вмістом журналів. Файли журналу корисніші, коли вони містять повний запис активності сервера. У більшості випадків простіше обробити повні файли журналів, щоб витягти з них лише потрібні вам дані або прибрати певну інформацію.
Навіть на помірно завантаженому сервері кількість інформації, що зберігається у файлах журналу, дуже велика. Файл журналу доступу зазвичай збільшується на 1 МБ або більше 10 000 запитів. Отже, необхідно періодично ротувати файли журналів, переміщуючи чи видаляючи наявні журнали. Це неможливо зробити під час роботи сервера, оскільки Apache httpd продовжить запис до старого файлу журналу, поки він утримує цей файл відкритим. Натомість сервер має бути перезапущено після переміщення або видалення файлів журналу, щоб відкрити нові файли журналу. Використовуючи graceful перезапуск сервер може отримати команду відкривати нові файли журналу, не втрачаючи ні існуючих, ні очікуваних з’єднань від клієнтів. Однак для цього сервер повинен продовжувати запис у старі файли журналу до завершення обслуговування старих запитів. Тому потрібно почекати деякий час після перезапуску, перш ніж виконувати будь-яку обробку файлів журналу. Типовий сценарій, який просто ротує журнали та стискає старі журнали для економії місця:
Apache httpd здатний записувати файли журналів доступу та помилок по трубі (через канал) до іншого процесу, а не безпосередньо у файл. Ця можливість значно підвищує гнучкість ведення журналу без додавання коду головний сервер. Щоб записувати журнали в трубу, просто замініть ім’я файлу на символ труби «|», за яким слідує ім’я файлу, що виконується, який повинен приймати записи журналу на своєму стандартному вході. Сервер при старті сервера запустить процес piped-log і перезапустить його, якщо він вийде з ладу під час роботи сервера (ця остання функція дозволяє назвати цю техніку “надійна передача журналу трубою”.) Процеси конвеєрного журналу породжуються батьківським процесом Apache httpd і успадковують ідентифікатор цього процесу. Це означає, що програми логів за конвеєром зазвичай запускаються з правами root. Тому дуже важливо, щоб програми були простими та безпечними. Одне з важливих застосувань конвеєрних журналів – дозволити ротацію журналів без перезавантаження сервера. HTTP-сервер Apache включає в себе просту програму rotatelogs для цієї мети. Наприклад, щоб ротувати журнали кожні 24 години, можна використовувати:
Зверніть увагу, що лапки використовуються для включення всієї команди, яка буде викликатись для труби. Хоча ці приклади належать до журналу доступу, той самий метод може бути використаний для журналу помилок. Як і у випадку з веденням логів з умовами, журнали конвеєра є дуже потужним інструментом, але їх не слід використовувати там, де доступне простіше рішення, таке як автономна постобробка. За умовчанням процес журналу, що передається по трубі, породжується без виклику оболонки. Використовуйте “|$” замість “|” для запуску з оболонкою (зазвичай з /bin/sh-c):
Це була поведінка за промовчанням для Apache 2.2. Залежно від специфіки оболонки це може призвести до додаткового процесу оболонки на час життя програми каналу журналу та проблем з обробкою сигналів під час перезапуску. З причин сумісності з Apache 2.2 позначення “||” також підтримується та еквівалентно використанню «|». Примітка для Windows: Зверніть увагу, що у Windows ви можете зіткнутися з проблемами під час запуску багатьох процесів журналювання, особливо коли HTTPD працює як служба. Це викликано нестачею heap space робочого столу. Простір робочого столу, який надається кожній службі, задається третім аргументом параметра SharedSection у значенні реєстру HKEY_LOCAL_MACHINESystemCurrentControlSetControlSessionManagerSubSystemsWindows. Змініть це значення з обережністю; застосовуються звичайні застереження для зміни реєстру Windows, але ви також можете вичерпати пул heap space робочого столу, якщо число буде встановлено занадто високо.
При запуску сервера з багатьма віртуальними хостами є кілька варіантів роботи з файлами журналів. По-перше, можна використовувати журнали так само, як на сервері з одним хостом. Просто розмістивши директиви ведення журналів за межами розділів <VirtualHost> у контексті основного сервера, можна зареєструвати всі запити в одному журналі доступу та журналі помилок. Цей метод не дозволяє легко збирати статистику з окремих віртуальних хостів. Якщо директиви CustomLog або ErrorLog містяться в розділі <VirtualHost>, всі запити або помилки для цього віртуального хоста записуватимуться лише у вказаний файл. Будь-який віртуальний хост, у якого немає директив журналування, все одно надсилатиме свої запити до журналів головного сервера. Цей метод дуже корисний для невеликої кількості віртуальних хостів, але якщо кількість хостів дуже велика, ним може бути складно керувати. Крім того, це часто може створювати проблеми з недостатньою кількістю файлових дескрипторів. Для журналу доступу є дуже непоганий компроміс. Додавши інформацію про віртуальний хост у рядок формату журналу, можна зареєструвати всі хости в одному журналі, а потім розділити журнал на окремі файли. Наприклад, розглянемо такі директиви.
%v використовується для запису до журналу імені віртуального хоста, який обслуговує запит. Потім таку програму, як split-logfile, можна використовувати для подальшого оброблення журналу доступу, щоб розділити його на один файл для кожного віртуального хоста.
Будь-хто, хто може писати в каталог, де Apache httpd пише файл журналу, майже напевно може отримати доступ до uid, з якого запущено сервер, який зазвичай є користувачем root. НЕ надавайте людям доступ до запису в каталог, де зберігаються журнали, не знаючи про наслідки. Крім того, файли журналів можуть містити інформацію, надану безпосередньо клієнтом, без екранування. Тому зловмисні клієнти можуть вставляти керуючі символи у файли журналів, тому необхідно бути обережними при роботі з необробленими журналами.
У звичайній роботі Apache запускається користувачем root і перемикається на користувача, визначеного директивою User, для обслуговування звернень. Як і у випадку з будь-якою командою, яку виконує користувач root, ви повинні подбати про те, щоб вона була захищена від зміни користувачами без повноважень root. Не тільки самі файли повинні бути доступні для запису лише root, а й каталоги та батьки всіх каталогів.
Наприклад, якщо ви вирішите помістити ServerRoot в /usr/local/apache, то рекомендується створити цей каталог як root за допомогою таких команд:
Передбачається, що /, /usr та /usr/local можуть бути змінені лише користувачем root. При встановленні файлу httpd, що виконується, ви повинні переконатися, що він захищений аналогічним чином:
Ви можете створити підкаталог htdocs, який може бути змінений іншими користувачами – оскільки root ніколи не виконує ніяких файлів звідти і не повинен створювати файли там. Якщо ви дозволяєте користувачам без повноважень root змінювати будь-які файли, які root виконує або виконує запис, ви відкриваєте свою систему для компрометації root. Наприклад, хтось може замінити двійковий файл httpd, щоб під час наступного запуску він виконував довільний код. Якщо каталог журналів доступний для запису (користувач без root), хтось може замінити файл журналу символічним посиланням на будь-який інший системний файл, а потім root може перезаписати цей файл довільними даними. Якщо самі файли журналу доступні для запису (користувач без root), то хтось може перезаписати сам журнал підробленими даними.