Skanuj projekt z SonarQube

Darmowa aplikacja ułatwiająca poprawę jakości kodu i łatania luk bezpieczeństwa w projekcie zespołowym

Skanuj projekt z SonarQube

Aplikacja usprawniająca pracę w zespole

SonarQube to darmowa aplikacja, która identyfikuje oraz pomaga naprawiać luki bezpieczeństwa i poprawiać jakość kodu w projekcie zespołowym. Podobnie jak SonarLint jest projektem Open Source.

SonarLint i SonarQube korzystają z tej samej listy reguł, jednakże reguły te można włączać i wyłączać. SonarLint to rozszerzenie IDE (np. InteliJ IDEA), więc włączanie/wyłączanie reguł w jego ustawieniach dotyczy tylko Twojego IDE.

Lista włączonych/wyłączonych reguł w ustawieniach SonarLint

Co gdy pracujesz zespołowo i chciałbyś by wszyscy w zespole posiadali taką samą listę reguł? Do tego właśnie służy aplikacja SonarQube, gdzie lista włączonych/wyłączonych reguł jest w jednym miejscu, czyli w uruchomionej aplikacji na serwerze. Pozostałe osoby w zespole korzystają z tej listy w swoim IDE poprzez połączenie pluginu SonarLint z aplikacją SonarQube. W tym przypadku lista reguł w ustawieniach SonarLint jest ignorowana (poza kategorią Secrets).

Wymagane ustawienia systemowe

Do uruchomienia aplikacji SonarQube użyjemy najprostszego i polecanego przeze mnie sposobu, czyli Dockera. Skorzystamy w tym celu z oficjalnego obrazu SonarQube znajdującego się na Docker Hub. Jednakże zanim uruchomimy kontener w systemie Linux musimy sprawdzić wartości jakie wyświetlają poniższe komendy:

sysctl vm.max_map_count
sysctl fs.file-max
ulimit -n
ulimit -u
Wartości na wirtualnej maszynie Javowiec.pl przed zmianą

SonarQube korzysta z Elastic Search, który wymaga następujących ustawień do prawidłowego działania:

  • vm.max_map_count większego lub równego 524288, inaczej możemy spodziewać się wyjątków Out of Memory (brakuje pamięci RAM)
  • fs.file-max większego lub równego 131072, inaczej możemy spodziewać się utraty danych
  • użytkownik uruchamiający SonarQube może otworzyć przynajmniej 131072 deskryptory pliku (ulimit -n), inaczej możemy spodziewać się utraty danych
  • użytkownik uruchamiający SonarQube może otworzyć przynajmniej 8192 wątki (ulimit -u), aby aplikacja mogła wykonywać wszystkie swoje operacje

Aby ustawić te wartości tymczasowo dla otwartej sesji terminala należy wykonać poniższe komendy logując się wcześniej na użytkownika root (komendą sudo su, do poprzedniego użytkownika wracamy komendą su nazwa-użytkownika):

sysctl -w vm.max_map_count=524288
sysctl -w fs.file-max=131072
ulimit -n 131072
ulimit -u 8192

Aby ustawić je na stałe dla użytkownika root (wymagane przez Dockera) oraz innych (co może przydać się później przy korzystaniu z Elastic Search w swojej aplikacji) należy:

  • dla vm.max_map_count i fs.file-max do pliku /etc/sysctl.conf dodać poniższe linie:
    • vm.max_map_count = 524288
    • fs.file-max = 131072
  • dla ulimit -n:
    • do plików /etc/systemd/system.conf i /etc/systemd/user.conf dodać linię DefaultLimitNOFILE=131072
    • do pliku /etc/security/limits.conf dodać poniższe linie:
      • root soft nofile 131072
      • root hard nofile 131072
      • * soft nofile 131072
      • * hard nofile 131072
  • dla ulimit -n:
    • do plików /etc/systemd/system.conf i /etc/systemd/user.conf dodać linię DefaultLimitNPROC=8192
    • do pliku /etc/security/limits.conf dodać poniższe linie:
      • root soft nproc 8192
      • root hard nproc 8192
      • * soft nproc 8192
      • * hard nproc 8192

Następnie należy zrestartować Wirtualną Maszynę, aby zobaczyć zmiany.

Wartości na wirtualnej maszynie Javowiec.pl po zmianie.

Pierwsze uruchomienie - Docker

Do pierwszego uruchomienia SonarQube będzie nam potrzebna poniższa komenda:

docker container run --name sonarqube -d -p 9000:9000 -v sonardata:/opt/sonarqube/data -v sonarlogs:/opt/sonarqube/logs -v sonarextensions:/opt/sonarqube/extensions sonarqube:9.2.3-community
UWAGA!
Wersja 9.2.1 była najnowsza w chwili pisania postu, ale polecem użyć 9.2.3 lub wyższą z uwagi na podatność Log4J. Informacja o SonarQube jest dostępna tutaj.
Uruchomienie kontenera SonarQube Community 9.2.1 wraz z woluminami

Uruchomiliśmy SonarQube Community, czyli darmową edycję. Za inne edycje trzeba zapłacić. Porównanie możesz znaleźć tutaj.

Jeżeli nie wiesz co oznaczają poszczególne części komendy, zajrzyj do artykułu "Jak zacząć pracę z Dockerem?", gdzie dokładnie je wytłumaczyłem. Jedyną nową częścią komendy jest flaga -v (można używać zamiennie z --volume). Służy ona do tworzenia woluminów z nazwami (ang. named volumes), w których przechowywane są dane. Dzięki tym woluminom nie stracimy danych, gdy zatrzymamy i usuniemy kontener. Wystarczy, że przy uruchamianiu nowego kontenera ponownie wskażemy te same woluminy. Stworzyliśmy więc następujące woluminy:

  • sonardata przechowujący pliki z danymi wykorzystywanymi m.in. przez indeksy ElasticSearch i embedowaną bazę danych H2
  • sonarlogs przechowujący logi SonarQube i ElasticSearch
  • sonarextensions przechowujący pluginy

Pierwsze uruchomienie - Przeglądarka

Po uruchomieniu kontenera przejdź w przeglądarce pod adres internetowy http://localhost:9000, gdzie przywita Cię ekran logowania do SonarQube. Domyślne dane logowania wyglądają następująco:

  • Login: admin
  • Password: admin
Ekran logowania SonarQube

Po kliknięciu przycisku Log in będziemy musieli zmienić hasło domyślne:

Ekran zmiany domyślnego hasła SonarQube

Następnie będziemy mieli możliwość stworzenia naszego pierwszego projektu:

Ekran tworzenia pierwszego projektu SonarQube

Wybieramy opcję Manually (manualnie) i w polu Project display name wpisujemy wyświetlaną nazwę projektu, zaś w polu Project key unikalny identyfikator projektu (mogą być takie same).

Ekran tworzenia pierwszego projektu SonarQube

Następnie po wciśnięciu przycisku Set Up możemy wybrać sposób w jaki będziemy analizowali (skanowali) nasz projekt. W tej chwili interesuje nas opcja Locally (lokalnie), którą wybieramy.

W kolejnym oknie będziemy musieli podać nazwę tokena. Potem wystarczy kliknąć przycisk Generate by wygenerować token, który będzie Ciebie identyfikował podczas operacji skanowania. Bez niego nie będzie możliwe przeskanowanie Twojego projektu.

Ekran generowania tokenu potrzebnego do skanowania projektu
Ekran z wygenerowanym tokenem potrzebnym do skanowania projektu

Zapisz sobie gdzieś token (twój będzie inny). Po kliknięciu przycisku Continue będziemy musieli wybrać metodę skanowania projektu. Nas interesuje technologia Maven.

Ekran z przykładem skanowania projektu w technologii Maven

Aby wykonać pierwsze skanowanie należy w terminalu przejść do folderu z projektem i następnie uruchomić wyświetloną komendę.

mvn clean verify sonar:sonar \
  -Dsonar.projectKey=skanuj-projekt-z-sonarqube \
  -Dsonar.host.url=http://localhost:9000 \
  -Dsonar.login=ed9360f4160db9d320a25f572362ac3665f10700

Jednakże polecenie jest dość długie, więc jak je skrócić?

Plugin Mavena, czyli jak skanować kod

Użyjemy w tym celu plugin Mavena sonar-maven-plugin i zdefiniujemy profil w pliku pom.xml znajdującym się w projekcie, który chcemy skanować. Na początek dodajmy sekcję właściwości po tagu </artifactId> z najnowszymi wersjami (w chwili pisania posta) już wspomnianego sonar-maven-plugin oraz properties-maven-plugin, który przyda się nam już za chwilę:

<properties>
    <properties-maven-plugin.version>1.0.0</properties-maven-plugin.version>
    <sonar-maven-plugin.version>3.9.1.2184</sonar-maven-plugin.version>
</properties>

Następnie stwórzmy sobie profil mavenowy o nazwie (id) sonar:

<profiles>
        <profile>
            <id>sonar</id>
            <properties>
                <sonar.host.url>http://localhost:9000</sonar.host.url>
                <sonar.projectKey>skanuj-projekt-z-sonarqube</sonar.projectKey>
            </properties>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.codehaus.mojo</groupId>
                        <artifactId>properties-maven-plugin</artifactId>
                        <version>${properties-maven-plugin.version}</version>
                        <executions>
                            <execution>
                                <id>read-sonar-properties</id>
                                <phase>initialize</phase>
                                <goals>
                                    <goal>read-project-properties</goal>
                                </goals>
                                <configuration>
                                    <files>
                                        <file>src/main/resources/sonar.properties</file>
                                    </files>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>
                    <plugin>
                        <groupId>org.sonarsource.scanner.maven</groupId>
                        <artifactId>sonar-maven-plugin</artifactId>
                        <version>${sonar-maven-plugin.version}</version>
                        <executions>
                            <execution>
                                <id>sonar-scan</id>
                                <phase>verify</phase>
                                <goals>
                                    <goal>sonar</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>

Profil ten składa się z właściwości sonar.host.url oraz sonar.projectKey. Pierwsza to adres do naszej aplikacji SonarQube: http://localhost:9000. Druga to klucz projektu, który musi być taki sam jak ten ustawiony wcześniej w aplikacji SonarQube: skanuj-projekt-z-sonarqube. Jest jeszcze potrzebna trzecia właściwość, a mianowicie sonar.login, który jest naszym tokenem.

Token jest przypisany do naszego konta w SonarQube, w związku z czym nie powinniśmy go nikomu pokazywać, a tym bardziej commitować do wspólnego repozytorium Git. Dlatego w pliku .gitignore musimy najpierw dodać następujący wpis, który sprawi, że plik z tokenem będzie ignorowany przy wykonywaniu commita:

### Project ###
sonar.properties

Następnie plik sonar.properties możemy stworzyć pod ścieżką w projekcie src/main/resources z poniższą zawartością (pamiętaj by podmienić wartość ze swoim tokenem):

sonar.login=ed9360f4160db9d320a25f572362ac3665f10700

Maven musi w jakiś sposób odczytać właściwość znajdującą się w pliku. Właśnie do tego będzie potrzebny nam properties-maven-plugin. Został ustawiony w taki sposób by cel (ang. goal) read-project-properties, który czyta właściwości projektu jak sama nazwa wskazuje, uruchamiał się podczas fazy (ang. phase) initialize. Faza ta jest drugą fazą w domyślnym cyklu życia Mavena i ma za zadanie inicjalizować stan buildu. Pozostaje jeszcze konfiguracja celu, czyli wskazanie z którego pliku Maven ma odczytać właściwości: src/main/resources/sonar.properties. W praktyce właściwość z tego pliku jest tym samym co poniższy zapis:

<properties>
    <sonar.login>ed9360f4160db9d320a25f572362ac3665f10700</sonar.login>
</properties>
UWAGA!
Listę wszystkich właściwości skanera SonarQube znajdziesz tutaj.

Teraz, gdy mamy ustawione wszystkie trzy właściwości potrzebne do wykonania skanowania, sonar-maven-plugin może zostać skonfigurowany tak by uruchamiał cel sonar podczas fazy verify. Faza ta jest jedną z ostatnich w domyślnym cyklu życia Mavena i ma za zadanie zweryfikować czy nasz projekt spełnia założone wymagania.

Po stworzeniu profilu i pliku wystarczy przejść do folderu z projektem w wierszu poleceń i uruchomić komendę: mvn clean verify -Psonar by wykonać skanowanie.

Część komendy mvn wywołuje Mavena, część clean czyści wygenerowane pliki w naszym projekcie (folder target), verify już znamy, a część -Psonar wywołuje profil o nazwie sonar. Łatwiejsze do zapamiętania prawda? 😁

Pomyślne skanowanie wykonane komendą mvn clean verify -Psonar
UWAGA!
Listę faz Mavena, ich kolejność i opis znajdziesz tutaj.

Po pomyślnym skanowaniu naszego projektu z wykorzystaniem Mavena, strona SonarQube odświeży się automatycznie i pokaże nam raport, z którego można wyczytać wiele ciekawych rzeczy. Więcej niż jest dostępne w pluginie SonarLint.

Wynik skanowania w SonarQube
Wykryte problemy podczas skanowania w SonarQube

Połączenie SonarLint z SonarQube

To jest najłatwiejsza część. Otwierasz ustawienia InteliJ skrótem CTRL + ALT + S, przechodzisz do Tools -> SonarLint i w zakładce Settings w pozycji SonarQube / SonarCloud connections klikasz ikonę plusa i dodajesz połączenie:

Dodawanie połączenia SonarQube w InteliJ IDEA
Dodawanie tokena do połączenia z SonarQube. Pamiętaj, by podać swój token (będzie inny)

Opcje po kliknięciu przycisku Next wypełnij według uznania. Po zakończeniu dodawania połączenia z SonarQube przejdź w ustawieniach InteliJ do Tools -> SonarLint -> Project Settings, zaznacz opcję Bind project to SonarQube / SonarCloud co umożliwi połączenie projektu z naszym lokalnym SonarQube i wypełnij resztę jak poniżej:

Łączenie pluginu SonarLint i projektu z aplikacją SonarQube

Od teraz korzystasz z listy reguł zarządzanej w aplikacji SonarQube, a nie lokalnej zdefiniowanej w InteliJ.

Wykryte problemy podczas skanowania w SonarLint. Są takie same jak w SonarQube

GitHub

Kod źródłowy jest udostępniony na GitHubie. Poniżej link 🙂

javowiec-pl-java/skanuj-projekt-z-sonarqube at main · JavowiecPL/javowiec-pl-java
Gotowe projekty z kategorii Java stworzone na potrzeby postów publikowanych na blogu Javowiec.pl - javowiec-pl-java/skanuj-projekt-z-sonarqube at main · JavowiecPL/javowiec-pl-java

Podsumowanie

W tym artykule przedstawiłem Ci SonarQube, czyli darmową aplikację, która identyfikuje oraz pomaga naprawiać luki bezpieczeństwa i poprawiać jakość kodu w projekcie zespołowym. Ponadto pokazałem Ci:

  • jak przygotować system operacyjny Ubuntu do uruchomienia SonarQube
  • jak uruchomić SonarQube przy pomocy Dockera
  • jak ustawić projekt SonarQube w przeglądarce
  • jak wykonać skanowanie projektu przy pomocy Mavena
  • jak połączyć plugin SonarLint i projekt z SonarQube.

W projekcie zespołowym aplikacja SonarQube powinna być postawiona na serwerze tak by każdy z członków zespołu mógł się z nią połączyć. W artykule SonarQube działa na lokalnej wirtualnej maszynie by móc Ci w prosty sposób go zademostrować.

Ponadto skan projektu powinien odbywać się automatycznie np. na Jenkinsie po każdym commicie członka zespołu. Jenkins to aplikacja Continuous Integration (w skrócie CI) i Continuous Delivery (w skrócie CD), która automatyzuje integrację i dostawę naszego projektu. W artykule projekt skanujemy ręcznie w ramach demonstracyjnych.

P.S. Skoro już o zespole mowa to Kubo, jeśli to czytasz, dziękuję za prawie 6 lat wspólnej pracy! Dzięki rozmowie z Tobą pierwszego dnia pracy, stres minął co umożliwiło mi późniejsze, komfortowe wdrożenie się w trening i pierwszy w życiu komercyjny projekt Java. Twoje pozytywne nastawienie do życia jest czymś co napędza ludzi do działania! Daje nadzieję, że z każdej negatywnej sytuacji można wyciągnąć coś pozytywnego. Wszystkiego dobrego i do zobaczenia w przyszłości!

Liczba komentarzy:

Zaloguj się lub dołącz do społeczności Javowców, aby móc uczestniczyć w dyskusji 🙂