Posts tagged sysadmin

[PL] SQL Server – Logowanie wejść sysadminów na serwer

VN:F [1.7.9_1023]
Rating: 0.0/5 (0 votes cast)

Wstęp

Jakiś czas temu napisałem, dlaczego uważam, że logon trigger nie jest dobrym rozwiązaniem w sytuacji, gdy zależy nam na logowaniu udanych prób logowania do instancji SQL Servera członków roli sysadmin. Napisałem wówczas, że mechanizmem, który nadaje się do takiego zadania, są Extended Events. Ale okazało się, że Extended Events nie dysponują zdarzeniem, które takie logowanie by umożliwiło. W międzyczasie wpadłem na pomysł, by zastosować w takim przypadku events notifications (dostępne od SQL Server 2005).

Rozwiązanie

Zadanie: zapisywać do tabeli wszystkie udane próby logowania członków roli sysadmin.

Rozwiązanie:

-- (0) Wlaczamy Service Brokera, jezeli jest wylaczony
IF EXISTS (SELECT * FROM sys.databases WHERE name = N'msdb' AND is_broker_enabled = 0)
  ALTER DATABASE msdb SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE;
GO

-- (1) Wybieramy baze msdb, bo zawsze jest...
USE msdb;
GO

-- (2) Kasujemy obiekty, ktore zaraz utworzymy
IF EXISTS (SELECT * FROM sys.server_event_notifications WHERE name = N'AuditSysadminLoginNotification')
  DROP EVENT NOTIFICATION AuditSysadminLoginNotification ON SERVER;
GO
IF EXISTS (SELECT * FROM sys.services WHERE name = N'AuditSysadminLoginService')
  DROP SERVICE AuditSysadminLoginService;
GO
IF EXISTS (SELECT * FROM sys.service_queues WHERE name = N'AuditSysadminLoginQueue' AND [schema_id] = 1)
  DROP QUEUE dbo.AuditSysadminLoginQueue;
GO

-- (3) Tworzymy obiekty: kolejke, usluge i powiadomienie o zdarzeniu
CREATE QUEUE dbo.AuditSysadminLoginQueue WITH STATUS = OFF;
GO
CREATE SERVICE AuditSysadminLoginService
ON QUEUE AuditSysadminLoginQueue
(
[http://schemas.microsoft.com/SQL/Notifications/PostEventNotification]
);
GO
CREATE EVENT NOTIFICATION AuditSysadminLoginNotification
ON SERVER
FOR AUDIT_LOGIN
TO SERVICE 'AuditSysadminLoginService', 'current database';
GO

-- (4) Tworzymy tabele na potrzeby logowania
IF OBJECT_ID(N'dbo.SysadminLogins', N'U') IS NOT NULL
  DROP TABLE dbo.SysadminLogins;
GO
CREATE TABLE dbo.SysadminLogins (
  LoginID int NOT NULL IDENTITY(1,1) PRIMARY KEY,
  LoginName sysname NOT NULL,
  ApplicationName nvarchar(256) NULL,
  HostName nvarchar(128),
  LoginDate datetime NOT NULL DEFAULT(GETDATE())
);
GO

-- (5) Tworzymy procedure do logowania
IF  OBJECT_ID(N'dbo.usp_LogSysadminLogin', N'P') IS NOT NULL
  DROP PROCEDURE dbo.usp_LogSysadminLogin
GO
CREATE PROC dbo.usp_LogSysadminLogin
AS
DECLARE
  @ErrorMessage nvarchar(4000),
  @EventData xml,
  @LoginName sysname,
  @ApplicationName nvarchar(256),
  @HostName nvarchar(128),
  @ConversationHandle uniqueidentifier;
WHILE (1=1)
BEGIN
  BEGIN TRAN;
  BEGIN TRY;
    RECEIVE TOP (1)
      @EventData = CAST(message_body AS xml),
      @ConversationHandle = [conversation_handle]
    FROM dbo.AuditSysadminLoginQueue;
    IF @@ROWCOUNT = 0
    BEGIN
      IF @@TRANCOUNT > 0
      BEGIN
          ROLLBACK;
      END;
      BREAK;
    END;
    SELECT
      @LoginName = @EventData.value('(/EVENT_INSTANCE/LoginName/text())[1]', 'sysname'),
      @ApplicationName = @EventData.value('(/EVENT_INSTANCE/ApplicationName/text())[1]', 'nvarchar(256)'),
      @HostName = @EventData.value('(/EVENT_INSTANCE/HostName/text())[1]', 'nvarchar(128)');
    IF IS_SRVROLEMEMBER(N'sysadmin', @LoginName) = 1 BEGIN
      INSERT INTO dbo.SysadminLogins (LoginName, ApplicationName, HostName)
      SELECT @LoginName, @ApplicationName, @HostName;
    END;
    IF @@TRANCOUNT > 0
      COMMIT;
  END TRY
  BEGIN CATCH;
    IF @@TRANCOUNT > 0
    BEGIN
        ROLLBACK;
        END CONVERSATION @ConversationHandle;
        BREAK;
    END;
  END CATCH;
END;
GO

-- (6) Przypinamy procedure do kolejki i wlaczamy kolejke
ALTER QUEUE dbo.AuditSysadminLoginQueue
  WITH STATUS = ON,
  RETENTION = OFF,
  ACTIVATION (
    STATUS = ON ,
    PROCEDURE_NAME = msdb.dbo.usp_LogSysadminLogin,
    MAX_QUEUE_READERS = 2,
    EXECUTE AS OWNER
  );
GO

-- (7) Testujemy, czy cos sie zalogowalo
SELECT * FROM msdb.dbo.SysadminLogins;

Komentarz do kodu:

  • jako bazę docelową, w której zapisuję informacje o zdarzeniach, wybieram bazę msdb (zawsze jest na serwerze, domyślnie ma włączonego Service Brokera),
  • w kroku (0) włączam na bazie msdb Service Brokera, jeżeli jeszcze nie jest on włączony,
  • w kroku (2) dbam o to, by obiekty, które zamierzam stworzyć, nie istniały na serwerze i w bazie msdb,
  • w kroku (3) tworzę obiekty niezbędne do asynchronicznego przechwytywania zdarzeń: kolejkę, usługę oraz event notification (na poziomie serwera, przechwytuje zdarzenia AUDIT_LOGIN – logowania do instancji SQL Servera),
  • w kroku (4) tworzę tabelę, do której będą zrzucane informacje o logowaniu sysadminów (login, nazwa użytej aplikacji klienckiej, nazwa maszyny klienckiej oraz data i czas logowania),
  • w kroku (5) tworzę procedurę składowaną, która zostanie użyta do wybrania informacji z tego, co przechwyci event notification i zwróci za pomocą funkcji EVENTDATA, wybrane informacje zostaną zapisane do tabeli utworzonej w kroku (4), do wychwycenia loginów będących członkami roli sysadmin używam funkcji IS_SRVROLEMEMBER,
  • w kroku (6) przypinam procedurę z kroku (5) do kolejki z kroku (3) i włączam kolejkę, od tej pory zdarzenia klasy AUDIT_LOGIN są już przechwytywane, a stosowne informacje zapisywane w tabeli z kroku (4),
  • w kroku (7) prezentuję zapytanie, dzięki któremu możesz sprawdzać, czy jakieś zdarzenie zostało już zalogowane w tabeli z kroku (4).

Przykładowe informacje o zdarzeniach logowania sysadminów:

image

Podsumowanie

Zastosowanie event notification ma kilka zalet: nie ingeruje w proces logowania (nie ma takiej możliwości, by sysadmin nie zalogował się z powodu błędu w procesie logowania zdarzeń, jak to mogło mieć miejsce w przypadku zastosowania logon triggera), asynchroniczność zapewnia niezłą skalowalność i działanie mechanizmu nawet w przypadku dużego natężenia zdarzeń, ewentualne informacje o błędach są do znalezienia w errorlogu instancji SQL Servera (np. błędy wynikające z wykrycia “zatrutej wiadomości” – poison message) lub w widoku systemowym sys.transmission_queue (w kolumnie transmission_status znajdziesz informację, dlaczego komunikat o zdarzeniu utknął w czasie przesyłania). Co ciekawe, wydaje mi się, że można znaleźć naprawdę sporo kategorii zdarzeń, dla przechwytywania których event notifications nadają się idealnie (m.in. DEACLOCK_GRAPH i BLOCKED_PROCESS_REPORT). Używając event notifications możesz zbudować całkiem niezły framework do monitorowania SQL Servera. Polecam zabawę tym mechanizmem.

[PL] SQL Server – Monitorowanie logowania a logon triggery

VN:F [1.7.9_1023]
Rating: 5.0/5 (1 vote cast)

Dzisiaj na forum WSS.pl wśród wielu ciekawych wątków o tematyce wokół SQL Servera, znalazł się wątek poświęcony monitorowaniu logowań do instancji SQL Servera loginów o uprawnieniach sysadmina. Autor wątku próbował zaatakować temat używając mechanizmu audytów dostępnego od SQL Server 2008, ale poległ na braku możliwości filtrowania logowań. Doradziłem dwa rozwiązania – logon trigger i Extended Events. Taki przykładowy logon trigger mógłby wyglądać tak:

CREATE TRIGGER trg_LogSysadminLogon
ON ALL SERVER
FOR LOGON
AS
BEGIN
SET NOCOUNT ON
IF IS_SRVROLEMEMBER('sysadmin') = 1
  INSERT INTO master.dbo.SysadminLogon (Sysadmin, LogonDate)
  SELECT SUSER_SNAME(), GETDATE()
END
GO

Komentarz do kodu: logon trigger dość prosty, używając funkcji IS_SRVROLEMEMBER sprawdzamy, czy bieżący login (ten, który próbuje się logować), jest sysadminem i jeżeli jest, zapisujemy jeden wiersz (nazwę logina i datę logowania) w tabeli dbo.SysadminLogon w bazie master.

Już po fakcie zacząłem się jednak zastanawiać, czy nie pospieszyłem się ze swoją poradą i doradzaniem logon triggera. Swego czasu napisałem wspólnie z Markiem Adamczukiem artykuł o logon triggerach dla polskiej strony Technet. W artykule tym wyraźnie zaznaczyliśmy, że istnieje co najmniej kilka przyczyn, z których działanie logon triggera może skutecznie uniemożliwić zalogowanie się użytkownika do serwera baz danych. I tu mnie tknęło. Chcemy mieć mechanizm monitorowania. Coś, co nie ingeruje w sam proces logowania, a jedynie zapisuje informację o tym, że jakiś sysadmin się zalogował. A skoro logon trigger przez swą naturę może skutecznie uniemożliwić owe logowanie (albo na przykład opóźnić je na skutek problemów wydajnościowych operacji w nim zapisanych), niespecjalnie nadaje się na mechanizm bezpiecznego (bezinwazyjnego, jak bym to nazwał) logowania. Wystarczy jeden błąd, jeden brak uprawnień, jeden wykonany w ciele triggera SELECT “w powietrze” (tak, tak, wykonanie zapytania SELECT w logon triggerze też powoduje błąd logowania!) i użytkownik zobaczy komunikat nieciekawej treści:

Logon failed for login ‘jkowalsk’ due to trigger execution.

Changed database context to ‘master’.

Changed language setting to us_english. (Microsoft SQL Server, Error: 17892)

Powiało grozą :-)

I tu Extended Events (w skrócie XE) są jednak mechanizmem o wiele bezpieczniejszym i już wiem, o czym będzie jeden z następnych wpisów na moim blogu. Cierpliwości, XE nadchodzą :-)

[EDYCJA: 2010-09-03]

A jednak wygląda na to, że Extended Events nie nadają się do śledzenia logowania (nie udało mi się znaleźć zdarzenia XE, które dałoby możliwość takiego śledzenia). Słuszną opcją za to wydaje się użycie Event Notifications, które oferują asynchroniczność i brak ingerencji w sam proces logowania.

[/EDYCJA]