Skip to main content

Install SonarQube on CentOS 7

1. Setting up SonarQube

1.1. Install Prerequisites.

yum install -y epel-release unzip vim wget

1.2. Install openJDK.

yum install -y java-11-openjdk java-11-openjdk-devel

1.3. Install PostgreSQL 10.

# Add PostgreSQL 10 YUM Repository
rpm -Uvh https://download.postgresql.org/pub/repos/yum/10/redhat/rhel-7-x86_64/pgdg-centos10-10-2.noarch.rpm

# Install PostgreSQL 10 Server
yum install -y postgresql10-server postgresql10

# Initialize PGDATA
/usr/pgsql-10/bin/postgresql-10-setup initdb

1.4. Edit /var/lib/pgsql/10/data/pg_hba.conf to enable MD5-based authentication.

host        all        all        127.0.0.1/32        md5
tip

By default, PostgreSQL server listen address is set to 'localhost'. If you need to remotely connect PostgreSQL server, change listen_address in /var/lib/pgsql/10/data/postgresql.conf as follows

listen_addresses = '*'

To allow connections from absolutely any address with MD5 password authentication, add the following line at the bottom of /var/lib/pgsql/10/data/pg_hba.conf

host        all        all        0.0.0.0/0        md5

Open TCP port 5432 through Firewall

firewall-cmd --permanent --add-port=5432/tcp
firewall-cmd --reload

1.5. Start and Enable postgres service.

systemctl start postgresql-10
systemctl enable postgresql-10
systemctl status postgresql-10

1.6. Check whether the postgres service is running.

netstat -tulpn | grep 5432

1.7. Create PostgeSQL database for SonarQube.

sudo -u postgres psql

CREATE DATABASE sonar;
CREATE USER sonar WITH ENCRYPTED PASSWORD '<sonar-user-password>';
GRANT ALL PRIVILEGES ON DATABASE sonar TO sonar;
ALTER DATABASE sonar OWNER TO sonar;
\q
tip

If you are migrating PostgreSQL database from another SonarQube instance, please follow these steps

# Backup Postgres Database (This will create the file backup under /var/lib/pgsql)
sudo su - postgres
pg_dump sonar > sonar.pgsql

# Restore Postgres Database (You need to copy sonar.pgsql file into /var/lib/pgsql)
sudo su - postgres
psql sonar < sonar.pgsql

# Change ownership of all Tables, Sequences and Views
sudo su - postgres
# Tables
for tbl in `psql -qAt -c "select tablename from pg_tables where schemaname = 'public';" sonar` ; do psql -c "alter table \"$tbl\" owner to sonar" sonar ; done
# Sequences
for tbl in `psql -qAt -c "select sequence_name from information_schema.sequences where sequence_schema = 'public';" sonar` ; do psql -c "alter table \"$tbl\" owner to sonar" sonar ; done
# Views
for tbl in `psql -qAt -c "select table_name from information_schema.views where table_schema = 'public';" sonar` ; do psql -c "alter table \"$tbl\" owner to sonar" sonar ; done

# Vacuum database in order to reclaim storage occupied by dead tuples
sudo su - postgres
vacuumdb sonar
danger

If you are migrating from another SonarQube instance, you may get the following ERROR message in sonar web server logs

tail -f /opt/sonarqube/logs/web.log

ERROR web[][o.s.s.p.d.m.DatabaseMigrationImpl] DB migration ended with an exception

org.sonar.server.platform.db.migration.step.MigrationStepExecutionException: Execution of migration step #3002 'Make index on DEPRECATED_RULE_KEYS.RULE_ID non unique' failed

Caused by: org.postgresql.util.PSQLException: ERROR: cannot drop index rule_id_deprecated_rule_keys because constraint rule_id_deprecated_rule_keys on table deprecated_rule_keys requires it

Hint: You can drop constraint rule_id_deprecated_rule_keys on table deprecated_rule_keys instead.

Read the logs and follow its instructions

sudo -u postgres psql

# Show databases
\list

# Switch to sonar database
\connect sonar

ALTER TABLE deprecated_rule_keys DROP CONSTRAINT IF EXISTS rule_id_deprecated_rule_keys;
DROP INDEX IF EXISTS rule_id_deprecated_rule_keys;

\q

1.8. Install SonarQube.

# Download latest sonarqube binaries
wget -O /tmp/sonarqube.zip https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-8.0.zip

# Extract it to /opt
unzip /tmp/sonarqube.zip -d /opt

# Rename sonarqube directory
mv /opt/sonarqube-8.0 /opt/sonarqube

# Adding a service account for sonarqube
sudo useradd --system --no-create-home sonar

# Provide necessory folder permissions
chown -R sonar:sonar /opt/sonarqube

1.9. Configure environment variables.

# Setting up the default JDK
alternatives --config java

# Setting up JAVA_HOME by adding the following line at the bottom of /etc/bashrc
export JAVA_HOME=$(dirname $(dirname $(readlink $(readlink $(which javac)))))

# Get the updated JAVA_HOME and NEXUS_HOME into current shell
source /etc/bashrc

# Check the JAVA version
java -version

1.10. Add the following lines at the bottom of /opt/sonarqube/conf/sonar.properties. Make sure to do changes accordingly.

# DATABASE
sonar.jdbc.username=sonar
sonar.jdbc.password=<sonar-user-password>
sonar.jdbc.url=jdbc:postgresql://localhost/sonar
sonar.jdbc.maxActive=60
sonar.jdbc.maxIdle=5
sonar.jdbc.minIdle=2
sonar.jdbc.maxWait=5000
sonar.jdbc.minEvictableIdleTimeMillis=600000
sonar.jdbc.timeBetweenEvictionRunsMillis=30000
sonar.jdbc.removeAbandoned=true
sonar.jdbc.removeAbandonedTimeout=60

# WEB SERVER
sonar.web.host=127.0.0.1
sonar.web.port=9000
sonar.web.javaOpts=-server -Xms512m -Xmx512m -XX:+HeapDumpOnOutOfMemoryError
sonar.search.javaOpts=-server -Xms512m -Xmx512m -XX:+HeapDumpOnOutOfMemoryError
sonar.ce.javaOpts=-server -Xms512m -Xmx512m -XX:+HeapDumpOnOutOfMemoryError

# ACTIVE DIRECTORY CONFIGURATIONS
sonar.security.realm=LDAP
sonar.security.savePassword=true
sonar.authenticator.downcase = true
ldap.url=ldap://<ldap-server>.example.local:389
ldap.bindDn=<ldap-user>@example.local
ldap.bindPassword=<ldap-password>
ldap.user.baseDn=dc=example,dc=local
ldap.user.request=(&(objectClass=User)(sAMAccountName={login}))
ldap.user.realNameAttribute=cn
ldap.user.emailAttribute=mail

1.11. Create SystemD service file in /etc/systemd/system/sonar.service.

[Unit]
Description=SonarQube Server
After=syslog.target network.target

[Service]
Type=forking
ExecStart=/opt/sonarqube/bin/linux-x86-64/sonar.sh start
ExecStop=/opt/sonarqube/bin/linux-x86-64/sonar.sh stop
LimitNOFILE=65536
LimitNPROC=4096
User=sonar
Group=sonar
Restart=on-failure

[Install]
WantedBy=multi-user.target

1.12. Add the following lines under /etc/sysctl.d/00-sysctl.conf in order to increase virtual memory for ElasticSearch

# Increase virtual memory for ElasticSearch
vm.max_map_count = 262144

# Apply changes
sudo sysctl -p /etc/sysctl.d/00-sysctl.conf

1.13. Start and Enable sonar service.

sudo systemctl daemon-reload
sudo systemctl start sonar.service
sudo systemctl enable sonar.service
info

When you perform a SonarQube Version Upgrade, you may need to reindex elasticsearch data

sudo systemctl stop sonar.service
sudo rm -rf /opt/sonarqube/data/es*
sudo systemctl start sonar.service

1.14. Check whether the sonar service is running.

netstat -tulpn | grep 9000

1.15. Monitor sonar log files for issues.

# SonarQube service log
tail -f /opt/sonarqube/logs/sonar.log

# Web Server logs
tail -f /opt/sonarqube/logs/web.log

# ElasticSearch logs
tail -f /opt/sonarqube/logs/es.log

# Compute Engine logs
tail -f /opt/sonarqube/logs/ce.log

2. Setting up Nginx Reverse Proxy

2.1. Install Nginx.

yum install -y nginx

2.2. Configure SSL.

# Create SSL folder
mkdir /etc/nginx/ssl

# Generate custom DH parameters
openssl dhparam -out /etc/nginx/ssl/dhparams.pem 2048

# Create a Self-Signed SSL certificate for *.example.local
openssl req -newkey rsa:2048 -nodes -keyout /etc/nginx/ssl/tls.key -x509 -days 365 -out /etc/nginx/ssl/tls.crt -subj "/C=LK/ST=WP/L=Colombo/O=Example (Private) Limited/CN=*.example.local"

# Restores default SELinux contexts
restorecon -RF /etc/nginx/ssl

2.3.Replace the content of /etc/nginx/nginx.conf with the following.

# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Official Russian Documentation: http://nginx.org/ru/docs/

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {

worker_connections 1024;
multi_accept on;
use epoll;

}

http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;

# Character set
charset utf-8;

# Required to prevent bypassing of DNS cache!!
resolver 127.0.0.1 ipv6=off;

# allow the server to close the connection after a client stops responding. Frees up socket-associated memory.
reset_timedout_connection on;

# Security Headers
server_tokens off;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options nosniff always;
add_header X-XSS-Protection "1; mode=block" always;
add_header "X-Permitted-Cross-Domain-Policies" "master-only";
add_header "X-Download-Options" "noopen";

# Buffers
client_header_timeout 300;
client_body_timeout 300;
fastcgi_read_timeout 300;
client_max_body_size 32m;
fastcgi_buffers 8 128k;
fastcgi_buffer_size 128k;

# Compression
gzip on;
gzip_vary on;
gzip_comp_level 1;
gzip_min_length 256;
gzip_proxied expired no-cache no-store private auth;
gzip_disable "MSIE [1-6]\.";
gzip_types
application/atom+xml
application/javascript
application/json
application/ld+json
application/manifest+json
application/rss+xml
application/vnd.geo+json
application/vnd.ms-fontobject
application/x-font-ttf
application/x-web-app-manifest+json
application/x-javascript
application/xhtml+xml
application/xml
font/opentype
image/bmp
image/svg+xml
image/x-icon
text/cache-manifest
text/css
text/xml
text/plain
text/javascript
text/vcard
text/vnd.rim.location.xloc
text/vtt
text/x-component
text/x-cross-domain-policy;


include /etc/nginx/mime.types;
default_type application/octet-stream;

# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.

include /etc/nginx/conf.d/*.conf;

}

2.4. Create /etc/nginx/conf.d/sonar.conf file as follows.

server {

listen 80 default_server;
server_name sonar.example.local;
return 301 https://$server_name$request_uri;

}

server {

listen 443 ssl http2 default_server;
server_name sonar.example.local;

client_max_body_size 32M;

ssl_certificate /etc/nginx/ssl/tls.crt;
ssl_certificate_key /etc/nginx/ssl/tls.key;

# openssl dhparam -out /etc/nginx/ssl/dhparams.pem 2048
ssl_dhparam /etc/nginx/ssl/dhparams.pem;

ssl_prefer_server_ciphers on;
ssl_session_timeout 10m;
ssl_session_cache builtin:1000 shared:SSL:10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;

access_log off;
error_log /var/log/nginx/sonar.error;

location / {

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Ssl on;
proxy_read_timeout 300;

# Redirect to Sonar
proxy_pass http://127.0.0.1:9000;

# Fix the “It appears that your reverse proxy set up is broken" error.
proxy_redirect http://127.0.0.1:9000 $scheme://$host;

}

}

2.5. SELinux policy to allow Nginx to connect to the network.

setsebool -P httpd_can_network_connect 1

2.6. Start and Enable Nginx.

systemctl start nginx
systemctl enable nginx

2.7. Open TCP port 80 and 443 through Firewall.

firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --permanent --add-port=443/tcp

firewall-cmd --reload
tip

SonarQube Initial Login Information

  • URL: https://sonar.example.local
  • User: admin
  • Password: admin

3. Update SonarQube to the next version

3.1. Stop SonarQube and Nginx services.

systemctl stop nginx
systemctl stop sonar

3.2. Remove old backups if exist.

rm -rf /opt/sonarqube-backup

3.3. Backup existing version.

mv /opt/sonarqube /opt/sonarqube-backup

3.4. Download the latest SonarQube binaries.

wget -O /tmp/sonarqube.zip https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-8.0.zip

3.5. Extract it to /opt directory.

unzip /tmp/sonarqube.zip -d /opt

3.6. Rename SonarQube directory.

mv /opt/sonarqube-8.0 /opt/sonarqube

3.7. Copy sonar.properties from the backup.

/bin/cp -f /opt/sonarqube-backup/conf/sonar.properties /opt/sonarqube/conf/sonar.properties

3.8. Provide necessory folder permissions.

chown -R sonar:sonar /opt/sonarqube

3.9. Reindex elasticsearch data.

sudo rm -rf /opt/sonarqube/data/es*

3.10. Start SonarQube and Nginx services.

systemctl start sonar
systemctl start nginx

3.11. Monitor sonar log files for issues.

# SonarQube service log
tail -f /opt/sonarqube/logs/sonar.log

# Web Server logs
tail -f /opt/sonarqube/logs/web.log

# ElasticSearch logs
tail -f /opt/sonarqube/logs/es.log

# Compute Engine logs
tail -f /opt/sonarqube/logs/ce.log

3.12. Browse to https://sonar.example.local/setup and follow the setup instructions.

3.13. Manually install the non-default plugins that are compatible with your version of SonarQube. Use the Compatibility Matrix to ensure that the versions you install are compatible with your server version. Note that the most recent versions of all SonarSource code analyzers available in your edition are installed by default. Simply copying plugins from the old server to the new is not recommended; incompatible or duplicate plugins could cause startup errors.

3.14. Remove temp files.

rm -f /tmp/sonarqube.zip

References

  1. How to Install SonarQube on CentOS 7
  2. Install SonarQube with PostgreSQL
  3. How to Install PostgreSQL 10 on CentOS/RHEL 7/6 and Fedora 28-26
  4. Install PostgreSQL 10 on CentOS 7 / RHEL 7
  5. How to import and export a PostgreSQL database
  6. How to enable remote access to PostgreSQL server
  7. How to allow remote connections to PostgreSQL database server
  8. Modify OWNER on all tables simultaneously in PostgreSQL