Integracja SQL Server 2005 i CLR z punktu widzenia administratora baz danych, cz. II

cz. II: bezpieczeństwo, monitorowanie, zastosowania

Opublikowano: 11 października 2007
Zawartość strony
BezpieczeństwoBezpieczeństwo
MonitorowanieMonitorowanie
Zastosowania CLRZastosowania CLR
PodziękowaniaPodziękowania
PodsumowaniePodsumowanie
Przeczytaj pozostałe części tego artykułuPrzeczytaj pozostałe części tego artykułu

Bezpieczeństwo

Model bezpieczeństwa stosowany przy integrowaniu aparatu baz danych SQL Server ze środowiskiem CLR zarządza i zabezpiecza dostęp do różnego typu obiektów (utworzonych nie tylko przy pomocy kodu zarządzanego) uruchamianych na serwerze baz danych. Obiekty te mogą być wywoływane w zapytaniach TSQL lub przez inne obiekty CLR. Wspominany model bezpieczeństwa musi spełniać następujące cele:

uruchomiony kod zarządzany (użytkownika) nie może naruszyć stabilności i integralności serwera baz danych. Na operacje nie spełniające tego założenia muszą być nałożone odpowiednio wysokie poziomy zabezpieczeń.

kod zarządzany nie może uzyskać dostępu do danych użytkownika (lub innego kodu) w bazie danych. Kod powinien być uruchomiony z odpowiednimi uprawnieniami w kontekście sesji użytkownika, który ten kod wywołuje.

musi istnieć mechanizm kontroli, który uniemożliwia odwołanie się do jakichkolwiek zasobów spoza procesu SQL Server.

kod użytkownika nie może uzyskać nieuprawnionego dostępu do zasobów systemowych przy pomocy procesu, w którym jest uruchomiony (proces silnika SQL Server).

W SQL Server 2005 zaimplementowano model bezpieczeństwa składający się z trzystopniowego mechanizmu zabezpieczeń. Jeden z zestawów zabezpieczeń musi być zawsze użyty w zależności od rodzaju operacji, które musi wykonać kod użytkownika:

SAFE

EXTERNAL _ACCESS

UNSAFE

Domyślnym (a zarazem najbardziej restrykcyjnym) poziomem zabezpieczeń jest ‘SAFE’. Jest to również ustawienie rekomendowane przez firmę Microsoft, ponieważ w tym trybie kod użytkownika nie uzyska dostępu do żadnych zewnętrznych zasobów systemowych, sieciowych (takich jak pliki, rejestr, itd.) , innych bibliotek czy komponentów.

Poziom EXTERNAL_ACCESS pozwala uzyskać dostęp do niektórych zasobów systemowych oraz sieciowych, takich jak pliki, zmienne środowiskowe oraz rejestr. Jest to rekomendowany poziom zabezpieczeń w przypadku konieczności do odwołania się do zewnętrznych zasobów.

Załadowanie assembly z ustawieniem UNSAFE pozwala na nieograniczony dostęp do zasobów zarówno wewnątrz aparatu SQL Server, jak i poza nim. Kod użytkownika może więc odwołać się do kodu niezarządzanego, nie utworzonego przy użyciu klas .NET Framework (np. biblioteki komponentów COM, funkcje API).

Rekomenduje się ograniczenie dostępu do kodu, który został załadowany w trybie innym niż SAFE, ale codzienność bywa inna i znaczną większość assemblies stanowią te, które używają ustawień EXTERNAL_ACCESS lub UNSAFE.

Na szczęście utworzenie assembly z opcją EXTERNAL_ACCESS lub UNSAFE wymusza na administratorze baz danych wykonanie kilku operacji, co powoduje, że to administrator przejmuje kontrolę nad procesem importowania assemblies do bazy danych. Można to zrobić na jeden z dwóch sposobów:

W przypadku, gdy assembly zostało podpisane kluczem bądź certyfikatem na etapie kompilacji, na tej podstawie SQL Server utworzy klucz asymetryczny (lub certyfikat) oraz odpowiedni login bazujący na tym kluczu. Następnie login musi otrzymać odpowiednie uprawnienia (GRANT UNSAFE (EXTERNAL_ACCESS) ASSEMBLY TO Login).

Właściciel bazy danych posiada uprawnienia EXTERNAL ACCESS ASSEMBLY (lub UNSAFE ASSEMBLY) a baza danych ma ustawiona opcję TRUSTWORTHY na ON.

Obydwa wymienione warunki są sprawdzane przy ładowaniu oraz podczas uruchamiania assembly. Co najmniej jeden z wymienionych warunków musi zostać spełniony, w przeciwnym przypadku SQL Server nie zezwoli na załadowanie assembly.

Poniższe przykłady omawiają obydwa sposoby załadowania assembly z opcją inną niż SAFE.

W pierwszym przypadku assembly UnsafeAssembly.dll zostało podpisane na etapie kompilacji. Na początku należy utworzyć klucz asymetryczny (lub certyfikat) bazując na assembly:

USE AdventureWorks
GO
CREATE ASYMMETRIC KEY CLRAsmKey
FROM EXECUTABLE FILE =  N'C:\Resources\UnsafeAssembly.dll'
GO

Drugim krokiem jest utworzenie loginu na podstawie klucza asymetrycznego:

CREATE LOGIN CLRUtilitiesLogin FROM ASYMMETRIC KEY CLRAsmKey
GO

Następnie login musi uzyskać prawa UNSAFE (bądź EXTERNAL ACCESS) ASSEMBLY:

GRANT UNSAFE  ASSEMBLY TO CLRUtilitiesLogin
GO	

Dopiero teraz można załadować assembly do bazy danych, wykorzystując utworzony login oraz podając odpowiedni poziom zabezpieczeń:

CREATE ASSEMBLY UnsafeAssembly
AUTHORIZATION CLRUtilitiesLogin
FROM N'C:\Resources\UnsafeAssembly.dll'
WITH PERMISSION_SET = UNSAFE
GO

Ostatnim krokiem jest zarejestrowanie typu zawartego w assembly – w tym przykładzie zostanie zarejestrowana procedura składowana uspGetDrives:

CREATE PROC dbo.uspGetDrives
AS EXTERNAL NAME UnsafeAssembly.StoredProcedures.GetDrives
GO

W drugim przypadku właściciel bazy danych musi mieć uprawnienia do załadowania i uruchomienia assemblies w trybie EXTERNAL_ACCESS lub UNSAFE oraz baza danych musi mieć opcję TRUSTWORTHY ustawioną na ON.

Jednym ze sposobów nadania odpowiednich praw właścicielowi bazy danych jest jego zmiana na takiego, który już te prawa ma:

ALTER AUTHORIZATION ON Database::AdventureWorks TO sa
GO

Innym sposobem jest nadanie praw UNSAFE (bądź EXTERNAL ACCESS) ASSEMBLY:

GRANT UNSAFE  ASSEMBLY TO some_db_owner
GO

W ostatnim kroku należy ustawić atrybut TRUSTWORTHY bazy danych na ON:

ALTER DATABASE AdventureWorks SET TRUSTWORTHY ON
GO

Na zakończenie rozważań należy wspomnieć o jednej, niezmiernie istotnej rzeczy odnoszącej się do atrybutu TRUSTWORTHY bazy danych. Po każdym procesie odtworzenia (RESTORE) bądź podłączenia (ATTACH) bazy danych atrybut TRUSTWORTHY ustawiany jest na wartość domyślną (OFF) niezależnie od tego, jaka była jego poprzednia wartość. Jest to o tyle istotne, iż assembly jest odtwarzane bądź dołączane zawsze razem z bazą danych, jednakże przestanie ono funkcjonować, jeżeli opcja TRUSTWORTHY nie zostanie ponownie ustawiona przez administratora.

Do początku stronyDo początku strony

Monitorowanie

Monitorowania obiektów dostarczonych do silnika baz danych jest jedną z kluczowych kwestii dla każdego DBA. Zarówno system operacyjny Windows, jak i SQL Server umożliwiają szybkie odnalezienie poszukiwanych informacji pod warunkiem, że wiadomo czego należy szukać. Chcielibyśmy w tym podrozdziale przybliżyć różne sposoby monitorowania zagadnień związanych z użyciem CLR w SQL Server.

Na początek zajmiemy się monitorem wydajności (PerfMon). Znajdziemy w nim wiele zestawów liczników, które pomogą w zdiagnozowaniu stanu aplikacji używającej SQL Server jako hosta:

.NET CLR Exceptions – licznik Excetions/Sec ostrzega przed problemami w kodzie aplikacji. Jeżeli wartość ta jest większa niż normalnie (niektórzy programiści używają wyjątków podczas normalnej pracy programu) to powinien być to bardzo wczesny sygnał ostrzegawczy o konieczności rewizji kodu.

.NET CLR Loading – SQL Server izoluje kod pomiędzy bazami danych za pomocą domen aplikacji (App Domain) – koncepcji znanej z CLR. Liczniki zawarte w tej kategorii umożliwiają określenie jak wiele domen oraz assembly jest załadowanych do pamięci.

.NET CLR Memory – dostarcza dokładnych informacji o trzech typach pamięci CLR oraz mechanizmie automatycznego czyszczenia pamięci. Liczniki te monitorują użycie pamięci przez CLR i w przypadku zbyt dużego jej zużycia należałoby się zastanowić nad rewizją kodu zawartego w assembly.

.NET Data Provider for SQL Server – dostarcza informacji o liczbie połączeń i odłączeń na sekundę. Informacja ta jest bardzo użyteczna, ponieważ umożliwia przechwycenie aktywności na poziomie bazy danych (dla kodu, który łączy się do SQL Server zamiast zwracać wartość przez parametry).

Drugim ważnym sposobem na otrzymanie wartościowej informacji jest użycie odpowiednich widoków i funkcji dynamicznych. Poniżej przedstawiamy kilka użytecznych zapytań.

Informacja o załadowanych assembly (należy uruchomić na wybranej bazie danych):

SELECT  a.[name] AS [Nazwa assmebly],
        ad.[appdomain_name] AS [Nazwa domeny],
        clr.[load_time] AS [Czas zaladowania ]
FROM    sys.dm_clr_loaded_assemblies AS clr
        INNER JOIN sys.assemblies AS a 
        ON clr.assembly_id = a.assembly_id
        INNER JOIN sys.dm_clr_appdomains AS ad 
  ON clr.appdomain_address = ad.appdomain_address

Informacja o zużyciu pamięci przez CLR w SQL Server:

SELECT  mo.[type] AS [Typ obiektu],
        SUM(mo.pages_allocated_count * mo.page_size_in_bytes / 1024) 
          AS [Aktualna wartosc (kB)],
        SUM(mo.max_pages_allocated_count * mo.page_size_in_bytes / 1024) 
          AS [Maksymalna wartosc (kB)]
FROM    sys.dm_os_memory_objects AS mo
WHERE   mo.[type] LIKE '%clr%'
GROUP BY mo.[type]
ORDER BY mo.[type]

Informacja o czasach oczekiwania dla obiektów CLR:

SELECT  wait_type AS [Typ oczekiwania],
        waiting_tasks_count AS [Liczba oczekujacych zadan],
        wait_time_ms AS [Czas oczekiwania (ms)],
        max_wait_time_ms AS [Maksymalny czas oczekiwania (ms)]
FROM    sys.dm_os_wait_stats
WHERE   wait_type LIKE '%clr%'

Informacja o licznikach wydajności związanych z CLR:

SELECT  object_name AS [Nazwa obiektu],
        counter_name AS [Nazwa licznika],
        cntr_value AS [Wartosc],
        cntr_type AS [Typ licznika]
FROM    sys.dm_os_performance_counters
WHERE   counter_name LIKE '%CLR%'

Informacja o wydajności zapytań oraz czasie przeznaczonym na wykonywanie zapytań wykorzystujących CLR:

SELECT  qs.*,
        s.text
FROM    sys.dm_exec_query_stats AS qs
        CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS s
WHERE   qs.total_clr_time > 0
ORDER BY qs.total_clr_time DESC

Informacja o aktualnych żądaniach w stosunku do obiektów CLR:

SELECT  session_id,
        request_id,
        start_time,
        status,
        command,
        database_id,
        wait_type,
        wait_time,
        last_wait_type,
        wait_resource,
        cpu_time,
        total_elapsed_time,
        nest_level,
        executing_managed_code
FROM    sys.dm_exec_requests
WHERE   executing_managed_code = 1

Dodatkowe możliwości monitorowania zapytań wykorzystujących CLR daje administratorom baz danych SQL Server Profiler. Zwłaszcza jego połączenie z danymi z monitora wydajności, korelacja i analiza wyników mogą dać najpełniejszy obraz działania obiektów CLR w SQL Server. Temat użycia SQL Profiler wraz z danymi z PerfMona został poruszony w jednym z wcześniejszych artykułów na stronach TechNet – Co nowego w SQL Server Profiler.

Do początku stronyDo początku strony

Zastosowania CLR

Do jakich zastosowań nadają się obiekty CLR? Poniżej staramy się odpowiedzieć na to pytanie.

Na początek zdefiniujmy sytuacje, w których zastosowanie CLR w SQL Server na pewno będzie błędem:

Duża ilość danych pobierana do biblioteki CLR. Do operacji na zbiorach danych najlepiej nadają się zapytania T-SQL uruchamiane w silniku baz danych. Należy pamiętać o jeszcze jednym czynniku - operacje wykonywane na zbiorach w SQL Server zamieniane są na operacje na kursorach w aplikacjach pisanych w bibliotekach zewnętrznych.

Długotrwałe odwołania do zewnętrznych bibliotek. Bardzo ważnym jest upewnienie się, iż działania użytkownika nie spowodują uruchomienia wywołań do zewnętrznych aplikacji lub API. Wpływ takich wywołań jest najsilniejszy w przypadku funkcji definiowanych przez użytkownika (UDF), które mogą być wywoływane dla każdego wiersza zwracanego w wyniku działania zapytania. Wywołanie zewnętrznej funkcji, które może zająć ok. 1 sekundy na wiersz będzie nieporozumieniem w przypadku konieczności powtórzenia tego kilka tysięcy razy.

Nadużywanie w tworzeniu typów UDT oraz agregatów UDA. Podczas tworzenia własnego typu danych należy być świadomym ograniczeń wynikających ze sposobu przechowywania tego typu na stronie z danymi. Najsilniejsze ograniczenie nakładane na UDT mówi, że jego wielkość musi być mniejsza niż 8kB. Drugim istotnym ograniczeniem, o którym należy pamiętać, jest fakt iż cały typ danych lub agregat musi zostać wczytany i przepisany w momencie jego aktualizacji (przez komendę UPDATE). Wynika z tego, iż nie można odczytywać tylko poszczególnych składników typu - jest on traktowany jako spójny obiekt.

UDA i tworzenie raportów w czasie rzeczywistym. Agregaty tworzone przez użytkownika nie mogą być używane w połączeniu z indeksowanymi widokami w SQL Server, nie jest więc możliwe automatyczne wstępne preagregowanie danych dla poprawy wydajności dla raportów wykonywanych w czasie rzeczywistym, co znacznie poprawiłoby ich wydajność.

Kompatybilność z poprzednią wersją SQL Server. Jeżeli aplikacja musi wspierać poprzednie wersje SQL Server to nie można stosować omawianej w tym artykule funkcjonalności.

Najistotniejsze są jednak zalety wynikające z integracji CLR z SQL Server, które powodują, iż jest to pożądane rozwiązanie i to nie tylko przy tworzeniu nowych systemów bazodanowych:

Wykorzystanie bibliotek .NET Framework oraz środowiska programistycznego Visual Studio. W roku 2005 wydano zarówno SQL Server jak i Visual Studio .NET. Przed aplikacjami bazodanowymi otwarły się zupełnie nowe możliwości wynikające z zastosowania bibliotek .NET Framework. Dostęp do funkcjonalności w nich zawartych jest możliwy bez konieczności posiadania wysokich uprawnień na poziomie kodu.

Zastępowanie rozszerzonych procedur składowanych (XPs). Poprzednio dostęp do zewnętrznych zasobów odbywał się przez wywołanie rozszerzonych procedur składowanych lub procedur z rodziny sp_OA*. Te metody niosą ze sobą olbrzymie ryzyko dla stabilności serwera w związku z uruchamianiem kodu niezarządzanego. Rekomenduje się administratorom baz danych wykorzystanie integracji CLR i SQL Server głównie ze względu na aspekty bezpieczeństwa:

Nie ma możliwości, aby uruchomienie zarządzanego kodu spowodowało zawieszenie silnika bazy danych a w efekcie jego zatrzymanie.

Nie ma możliwości, aby uruchomienie kodu zarządzanego spowodowało wycieki pamięci a w efekcie spowolnienie i zawieszenie instancji SQL Server.

Uzyskiwana jest większa wydajność i skalowalność ze względu na kontrolowanie pamięci, które daje SQL Server.

Nie ma problemów związanych z bezpieczeństwem uruchomionego kodu, ponieważ modele bezpieczeństwa .NET Framework i w SQL Server są ze sobą zintegrowane.

Powyższe rozważania nie są prawdziwe, jeżeli assembly zostanie zarejestrowane w trybie UNSAFE, ponieważ wtedy jest możliwość wywołania zewnętrznego, niezarządzanego kodu.

Zredukowanie ruchu w sieci. Niektóre algorytmy wymagają przesłania dużej ilości danych dla wygenerowania odpowiedniego wyniku. Przesyłanie danych pomiędzy serwerami powoduje zwiększenie przepływu informacji w sieci. Umieszczenie algorytmów wewnątrz bazy danych nie tylko ogranicza ten ruch ale może również poprawić wydajność zauważalną w aplikacji klienckiej. Przykładem takiego algorytmu może być konieczność wykonania specjalistycznych operacji statystycznych, które wymagają otrzymania całego zbioru danych przed rozpoczęciem obliczeń.

Pisanie funkcji ogólnego przeznaczenia. Funkcja ogólnego przeznaczenia jest zdefiniowana następująco:

Dane są przekazywane do funkcji w postaci argumentów

Funkcja nie wymaga dodatkowego dostępu do danych

Złożone obliczenia wykonywane są w kodzie podobnym do działania kursora – jeden wiersz analizowany jest w jednym przebiegu pętli

Przykładem takiej funkcji może być funkcja wyznaczająca odległość pomiędzy dwoma punktami otrzymanymi w wyniku monitorowania ruchu pojazdów. Wewnątrz takiej funkcji przeprowadzane są operacje trygonometryczne – sumy oraz iloczyny. Funkcja jako wynik mogłaby zwracać odległość w jednostce zgodnej z parametrem wejściowym – kilometrach, metrach, milach, jardach itp.

Definiowanie własnych skalarnych typów (UDT). Większość typów danych może być mapowana do modelu relacyjnego, jednak jest wiele przykładów, które nadają się do rozważenia jako nowe typy:

Typy bazujące na istniejących w SQL Server, ale rozszerzające ich możliwości – np. implementacja typu datetime o funkcjonalność czasu UTC

Typy, które są wczytywane do tablic i zapisywane w całości (jako obiekty). Takie typy ukrywają swoje własności i metody przed użytkownikiem. Przykładem może być definicja punktu w przestrzeni lub dobrze wszystkim znanej liczby zespolonej.

Definiowanie własnego agregatu (UDA). W części systemów wykonanie agregacji typu SUM, AVG, MIN, MAX itd. jest niewystarczające. Wraz z możliwością integracji CLR z SQL Server użytkownik ma możliwość definiowania własnego agregatu, który może być użyty w klauzuli GROUP BY. Przykładem takiego agregatu może być obliczanie transformaty Fouriera lub średniej odległości punktu od określonego miejsca.

Pisanie wydajnych funkcji użytkownika zwracających tablice.

Podana przez nas lista zarówno dobrego, jak i złego użycia nowej technologii, może być punktem wyjścia do rozpoczęcia pracy nad integracją swojego serwera z CLR (lub zweryfikowania stanu aktualnego takiej integracji).

Do początku stronyDo początku strony

Podziękowania

Autor dziękuję Pawłowi Potasińskiemu za współpracę podczas pisaniu artykułu. Dzięki owocnej dyskusji, wymianie poglądów oraz trafnym uwagom Pawła artykuł posiada wiele cennej, fachowej wiedzy.

Do początku stronyDo początku strony

Podsumowanie

Integracja SQL Server 2005 z CLR otwiera przed użytkownikami tego systemu nowe, niespotykane dotąd, możliwości. Daje programistom swobodę w implementacji wielu funkcjonalności w bazach danych. Ogromne możliwości technologii .NET powodują, że obiekty CLR wyrastają na doskonałe uzupełnienie tradycyjnych obiektów w bazach danych. Administratorzy pracujący na co dzień z SQL Server muszą przyzwyczaić się do obecności obiektów CLR w ich życiu i podjąć odpowiednie kroki mające na celu zapewnienie poprawnego funkcjonowania nowych mechanizmów.

Do początku stronyDo początku strony

Przeczytaj pozostałe części tego artykułu

Integracja SQL Server 2005 i CLR z punktu widzenia administratora baz danych, cz. I


Damian Widera

Damian Widera, Project Manager & Team Lead (MCT, MCITP – DBA, MCSD.NET)
Od 8 lat zajmuje się projektowaniem, tworzeniem i wdrażaniem aplikacji wykorzystujących platformę .NET, SQL Server oraz Oracle. Obecnie pracuje jako project manager dla LGBS Polska. Pracował także jako trener, programista, administrator baz danych, twórca domumentacji oraz analityk biznesowy.
Aktywnie współpracuje z polskim oddziałem Microsoft publikując atykuły, webcasty oraz porady z zakresu SQL Server na stronach TechNet (http://www.microsoft.com/poland/technet). Jest współautorem książki „Serwer SQL 2008. Administracja i programowanie”.
Speaker na wielu konferencjach, m.in. Microsoft Heroes Happen Here, C2C, European PASS Conference, Microsoft Technology Summit, Energy Launch, TechED.
Od 2004 r. posiada certyfikaty firmy Microsoft: Microsoft Certified Trainer (MCT), Microsoft Certified IT Professional – Database Administrator (MCITP – DBA) oraz Microsoft Certified Solution Developer (MCSD.NET).
Jest współtwórcą oraz liderem jednej z najwiekszych grup pasjonatów SQL Server w Polsce – Śląskiej Regionalnej Grupy Microsoft (PLSSUG Katowice). Od listopada 2008 jest prezesem Polish SQL Server Users Group (PLSSUG) w Polsce.
W styczniu 2009 nagrodzony tytułem MVP w kategorii SQL Server.


Do początku stronyDo początku strony