This page looks best with JavaScript enabled

GitLab on FreeBSD

 ·  🎃 kr0m

In this article, we will explain how to install a GitLab server on FreeBSD. For greater flexibility, I will install it in a jail using Bastille .
Before we begin, it should be noted that GitLab is an application written in Ruby, so we will have the Ruby server that incorporates GitLab and the web server of our choice that will forward requests to GitLab and serve static content.

We create the jail:

bastille create -T BaudBeauty 13.1-RELEASE 192.168.69.16/24 nfe0

We enable and start the SSH service:

bastille sysrc BaudBeauty sshd_enable=YES
bastille service BaudBeauty sshd start

We access the jail:

bastille console BaudBeauty

According to the documentation , we must use the latest repositories:

mkdir -p /usr/local/etc/pkg/repos
vi /usr/local/etc/pkg/repos/FreeBSD.conf

FreeBSD: {
    url: "pkg+http://pkg.FreeBSD.org/${ABI}/latest",
    mirror_type: "srv",
    signature_type: "fingerprints",
    fingerprints: "/usr/share/keys/pkg",
    enabled: yes
}

We install GitLab:

pkg install www/gitlab-ce

We install the latest version of PostgreSQL that is compatible with the version of GitLab we are installing, in my case:

gitlab-ce-15.5.6_1

The only way I have found to find out the correct version is to install the highest version and descend until the installer does not try to uninstall GitLab:

Installed packages to be REMOVED:
	gitlab-ce: 15.5.6_1
	postgresql13-client: 13.9
	rubygem-activerecord-explain-analyze: 0.1.0_4
	rubygem-pg: 1.4.4

New packages to be INSTALLED:
	postgresql15-client: 15.1
	postgresql15-contrib: 15.1
	postgresql15-server: 15.1_1

We install PostgreSQL:

pkg install postgresql13-server postgresql13-contrib

PostgreSQL requires access to sysvipc, but jails are restricted by default.
We allow access per jail, it can also be allowed globally on the parent host for all jails, but it is preferable to be more granular:

bastille config BaudBeauty set sysvmsg=new
bastille config BaudBeauty set sysvsem=new
bastille config BaudBeauty set sysvshm=new

NOTE: Do not use allow.sysvipc as it is considered deprecated and grants more permissions than strictly necessary.

Restart the jail:

bastille stop BaudBeauty
bastille start BaudBeauty

Initialize and start the database:

sysrc postgresql_enable=YES
service postgresql initdb
service postgresql start

Create the git user in the database:

psql -d template1 -U postgres -c "CREATE USER git CREATEDB SUPERUSER;"
psql -d template1 -U postgres -c "ALTER ROLE git WITH PASSWORD 'XXXXXXXXXXXXXXXXXX';"

Create the database that GitLab will use:

psql -d template1 -U postgres -c "CREATE DATABASE gitlabhq_production OWNER git;"

Check that it has been created:

psql -d gitlabhq_production -U postgres -c "\l"

                                   List of databases
        Name         |  Owner   | Encoding | Collate |  Ctype  |   Access privileges   
---------------------+----------+----------+---------+---------+-----------------------
 gitlabhq_production | git      | UTF8     | C       | C.UTF-8 | 
 postgres            | postgres | UTF8     | C       | C.UTF-8 | 
 template0           | postgres | UTF8     | C       | C.UTF-8 | =c/postgres          +
                     |          |          |         |         | postgres=CTc/postgres
 template1           | postgres | UTF8     | C       | C.UTF-8 | =c/postgres          +
                     |          |          |         |         | postgres=CTc/postgres
(4 rows)

Allow access from the server’s own IP:

vi /var/db/postgres/data13/pg_hba.conf

# GitLab:
host    all     git     192.168.69.16/32        md5

Restart the service:

service postgresql restart

Check that we can connect with the git user:

psql -h localhost -U git -d gitlabhq_production

Enable the pg_trgm and btree_gist extensions:

psql -U postgres -d gitlabhq_production -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;"
psql -U postgres -d gitlabhq_production -c "CREATE EXTENSION IF NOT EXISTS btree_gist;"

GitLab will have installed a Redis as a dependency, enable socket access:

echo 'unixsocket /var/run/redis/redis.sock' >> /usr/local/etc/redis.conf

Indicate the socket permissions:

echo 'unixsocketperm 770' >> /usr/local/etc/redis.conf

Enable and start Redis:

sysrc redis_enable=YES
service redis restart

Add the git user to the redis group:

pw groupmod redis -m git

We dont configure GitLab to use SSL because I use an HTTP balancer where the SSL tunnel is finished, I must indicate the IP of said balancer in trusted_proxies. Finally, I need to specify the email account to use for sending notifications:

cd /usr/local/www/gitlab-ce
vi config/gitlab.yml
...
  gitlab:
    ## Web server settings (note: host is the FQDN, do not include http://)
    host: flatland.alfaexploit.com
    port: 80 # Set to 443 if using HTTPS, see installation.md#using-https for additional HTTPS configuration details
    https: false # Set to true if using HTTPS, see installation.md#using-https for additional HTTPS configuration details
...
    trusted_proxies:
      - 192.168.69.19/32
...
    email_from: gitlab@alfaexploit.com
    email_reply_to: gitlab@alfaexploit.com

We create an email user on the email server:

gitlab@alfaexploit.com
adduser

We copy the example SMTP configuration and adapt it:

cp config/initializers/smtp_settings.rb.sample config/initializers/smtp_settings.rb
vi config/initializers/smtp_settings.rb
if Rails.env.production?
  Rails.application.config.action_mailer.delivery_method = :smtp
  secrets = Gitlab::Email::SmtpConfig.secrets

  ActionMailer::Base.delivery_method = :smtp
  ActionMailer::Base.smtp_settings = {
    address: "192.168.69.17",
    port: 25,
    user_name: "gitlab@alfaexploit.com",
    password: "XXXXXXXXXXX",
    ## If you are using encrypted smtp credentials then you should instead use the secrets user_name/password
    ## See: https://docs.gitlab.com/ee/administration/raketasks/smtp.html#secrets
    # user_name: secrets.username,
    # password: secrets.password,
    domain: "flatland.alfaexploit.com",
    authentication: :login,
    enable_starttls_auto: true,
    openssl_verify_mode: 'peer' # See ActionMailer documentation for other possible options
  }
end

We configure the key with which the database will be encrypted:

vi config/secrets.yml
db_key_base: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

We check the number of cores the server has:

sysctl hw.ncpu
hw.ncpu: 2

For a server with 2GB of RAM, 3 workers usually work well, but workers>=hw.ncpu must always be met:

vi config/puma.rb
workers 3

We configure some options for the git user so that GitLab works correctly:

su -l git -c "git config --global core.autocrlf input"
su -l git -c "git config --global gc.auto 0"
su -l git -c "git config --global repack.writeBitmaps true"
su -l git -c "git config --global receive.advertisePushOptions true"
su -l git -c "git config --global core.fsync objects,derived-metadata,reference"

We make sure that the .ssh directory exists:

su -l git -c "mkdir -p /usr/local/git/.ssh"

We make sure that the repository directory exists and has the necessary permissions:

su -l git -c "mkdir -p /usr/local/git/repositories"
chown git /usr/local/git/repositories
chgrp git /usr/local/git/repositories
chmod 2770 /usr/local/git/repositories

We configure the access credentials to PostgreSQL:

vi config/database.yml
production:
  main:
    adapter: postgresql
    encoding: unicode
    database: gitlabhq_production
    username: git
    password: "XXXXXXXXXXXXXXX"
    host: localhost

  ci:
    adapter: postgresql
    encoding: unicode
    database: gitlabhq_production
    database_tasks: false
    username: git
    password: "XXXXXXXXXXXXXXX"
    host: localhost

We adjust the permissions of the /usr/local/share/gitlab-shell directory so that the git user has access while initializing the database and performing the initial configuration:

chown git /usr/local/share/gitlab-shell

We initialize the database indicating the access password for root. The first time, RSA keys are generated based on the indicated db_key_base. For this reason, it will say missing Rails.application.secrets.secret_key_base, but it refers to the RSA key, not the password, so everything is fine:

su -l git -c "cd /usr/local/www/gitlab-ce && rake gitlab:setup RAILS_ENV=production GITLAB_ROOT_PASSWORD=XXXXXXXXXXXXXXXXXX"

Missing Rails.application.secrets.secret_key_base for production environment. The secret will be generated and stored in config/secrets.yml.
Missing Rails.application.secrets.otp_key_base for production environment. The secret will be generated and stored in config/secrets.yml.
Missing Rails.application.secrets.openid_connect_signing_key for production environment. The secret will be generated and stored in config/secrets.yml.
This will create the necessary database tables and seed the database.
You will lose any previous data stored in the database.
Do you want to continue (yes/no)? yes

Dropped database 'gitlabhq_production'
Created database 'gitlabhq_production'

== Seed from /usr/local/www/gitlab-ce/db/fixtures/production/001_application_settings.rb
Creating the default ApplicationSetting record.

== Seed from /usr/local/www/gitlab-ce/db/fixtures/production/002_admin.rb
Administrator account created:

login:    root
password: ******

Redis#sadd will always return an Integer in Redis 5.0.0. Use Redis#sadd? instead.(called from: /usr/local/lib/ruby/gems/3.0/gems/redis-namespace-1.9.0/lib/redis/namespace.rb:479:in `call_with_namespace')

== Seed from /usr/local/www/gitlab-ce/db/fixtures/production/003_create_base_work_item_types.rb

OK

== Seed from /usr/local/www/gitlab-ce/db/fixtures/production/004_add_security_training_providers.rb

OK

== Seed from /usr/local/www/gitlab-ce/db/fixtures/production/010_settings.rb
Saved CI JWT signing key

== Seed from /usr/local/www/gitlab-ce/db/fixtures/production/998_gitlab_instance_administration_project.rb
Successfully created self monitoring project.
Redis#sadd will always return an Integer in Redis 5.0.0. Use Redis#sadd? instead.(called from: /usr/local/lib/ruby/gems/3.0/gems/redis-namespace-1.9.0/lib/redis/namespace.rb:479:in `call_with_namespace')
Redis#sadd will always return an Integer in Redis 5.0.0. Use Redis#sadd? instead.(called from: /usr/local/lib/ruby/gems/3.0/gems/redis-namespace-1.9.0/lib/redis/namespace.rb:479:in `call_with_namespace')

== Seed from /usr/local/www/gitlab-ce/db/fixtures/production/999_common_metrics.rb

If it complains about unsupported database names: embedding, we should comment the embedding section:

ERROR: This installation of GitLab uses unsupported database names in 'config/database.yml': embedding. The only supported ones are main, ci, main_clusterwide.
vi config/database.yml
test: &test
#  embedding:
#    adapter: postgresql
#    encoding: unicode
#    database: gitlabhq_embedding_test
#    username: postgres
#    password:
#    host: localhost

We backup the secrets:

cat config/secrets.yml

Now that we have initialized the database, we can revert the assigned permissions:

chown root /usr/local/share/gitlab-shell

We check that the entire GitLab environment is working correctly. The Go version can be ignored:

su -l git -c "cd /usr/local/www/gitlab-ce && rake gitlab:env:info RAILS_ENV=production"

System information
System:		
Current User:	git
Using RVM:	no
Ruby Version:	3.0.5p211
Gem Version:	3.3.23
Bundler Version:2.3.23
Rake Version:	13.0.6
Redis Version:	7.0.7
Sidekiq Version:6.4.2
Go Version:	unknown

GitLab information
Version:	15.5.6
Revision:	Unknown
Directory:	/usr/local/www/gitlab-ce
DB Adapter:	PostgreSQL
DB Version:	13.9
URL:		http://flatland.alfaexploit.com
HTTP Clone URL:	http://flatland.alfaexploit.com/some-group/some-project.git
SSH Clone URL:	git@flatland.alfaexploit.com:some-group/some-project.git
Using LDAP:	no
Using Omniauth:	yes
Omniauth Providers: 

GitLab Shell
Version:	14.12.0
Repository storage paths:
- default: 	/usr/local/git/repositories
GitLab Shell path:		/usr/local/share/gitlab-shell

We compile the assets:

su -l git -c "cd /usr/local/www/gitlab-ce && yarn install --production --pure-lockfile"
su -l git -c "cd /usr/local/www/gitlab-ce && RAILS_ENV=production NODE_ENV=production USE_DB=false SKIP_STORAGE_VALIDATION=true NODE_OPTIONS='--max_old_space_size=3584' bundle exec rake gitlab:assets:compile"

NOTE: This will take a while: Done in 1134.20s.

We remove superuser permissions from the database:

psql -d template1 -U postgres -c "ALTER USER git WITH NOSUPERUSER;"

We enable and start GitLab:

sysrc gitlab_enable=YES
service gitlab start

We install Nginx:

pkg install nginx

We include the GitLab configuration:

vi /usr/local/etc/nginx/nginx.conf

...
http {
    include       mime.types;
    include       /usr/local/www/gitlab-ce/lib/support/nginx/gitlab;
...

We modify the Nginx configuration to determine the real IP of the requests using proxy protocol:

vi /usr/local/www/gitlab-ce/lib/support/nginx/gitlab

...
server {
  listen 0.0.0.0:80 default_server proxy_protocol;
  server_name flatland.alfaexploit.com; ## Replace this with something like gitlab.example.com
  server_tokens off; ## Don't show the nginx version number, a security best practice

  real_ip_header proxy_protocol; ## X-Real-IP or X-Forwarded-For or proxy_protocol
  real_ip_recursive off;    ## If you enable 'on'
  set_real_ip_from 192.168.69.19;
...

We check that there are no errors:

nginx -t

nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful

We enable and start the service:

sysrc nginx_enable=YES
service nginx restart

We check that everything is correctly configured. We can ignore all errors related to sidekiq, mailroom, and systemD. Also, on the first run, it will complain that the authorized keys file is not accessible. Running the check a second time will work:

su -l git -c "cd /usr/local/www/gitlab-ce && rake gitlab:check RAILS_ENV=production"

Checking GitLab subtasks ...

Checking GitLab Shell ...

GitLab Shell: ... GitLab Shell version >= 14.12.0 ? ... OK (14.12.0)
Running /usr/local/share/gitlab-shell/bin/check
Internal API available: OK
Redis available via internal API: OK
gitlab-shell self-check successful

Checking GitLab Shell ... Finished

Checking Gitaly ...

Gitaly: ... default ... OK

Checking Gitaly ... Finished

Checking Sidekiq ...

Sidekiq: ... Running? ... no
  Try fixing it:
  sudo -u git -H RAILS_ENV=production bin/background_jobs start
  For more information see:
  doc/install/installation.md in section "Install Init Script"
  see log/sidekiq.log for possible errors
  Please fix the error above and rerun the checks.

Checking Sidekiq ... Finished

Checking Incoming Email ...

Incoming Email: ... Reply by email is disabled in config/gitlab.yml

Checking Incoming Email ... Finished

Checking LDAP ...

LDAP: ... LDAP is disabled in config/gitlab.yml

Checking LDAP ... Finished

Checking GitLab App ...

Database config exists? ... yes
All migrations up? ... yes
Database contains orphaned GroupMembers? ... no
GitLab config exists? ... yes
GitLab config up to date? ... yes
Log directory writable? ... yes
Tmp directory writable? ... yes
Uploads directory exists? ... yes
Uploads directory has correct permissions? ... yes
Uploads directory tmp has correct permissions? ... skipped (no tmp uploads folder yet)
Systemd unit files or init script exist? ... no
  Try fixing it:
  Install the Service
  For more information see:
  doc/install/installation.md in section "Install the Service"
  Please fix the error above and rerun the checks.
Systemd unit files or init script up-to-date? ... can't check because of previous errors
Projects have namespace: ... 
GitLab Instance / Monitoring ... yes
Redis version >= 6.0.0? ... yes
Ruby version >= 2.7.2 ? ... yes (3.0.5)
Git user has default SSH configuration? ... yes
Active users: ... 1
Is authorized keys file accessible? ... yes
GitLab configured to store new projects in hashed storage? ... yes
All projects are in hashed storage? ... yes

Checking GitLab App ... Finished


Checking GitLab subtasks ... Finished

Finally, we access GitLab:
https://flatland.alfaexploit.com

To upgrade to higher versions, we will always follow the steps indicated in the documentation .


Troubleshooting:

Check GitLab configuration:

cat /usr/local/www/gitlab-ce/config/gitlab.yml

  gitlab:
    ## Web server settings (note: host is the FQDN, do not include http://)
    host: gitlab.alfaexploit.com
    port: 80 # Set to 443 if using HTTPS, see installation.md#using-https for additional HTTPS configuration details
    https: false # Set to true if using HTTPS, see installation.md#using-https for additional HTTPS configuration details

Check Nginx configuration:

cat /usr/local/www/gitlab-ce/lib/support/nginx/gitlab

Recompile assets:

su -l git -c "cd /usr/local/www/gitlab-ce && yarn install --production --pure-lockfile"
su -l git -c "cd /usr/local/www/gitlab-ce && RAILS_ENV=production NODE_ENV=production USE_DB=false SKIP_STORAGE_VALIDATION=true NODE_OPTIONS=\’--max_old_space_size=3584\’ bundle exec rake gitlab:assets:compile"

Repository directory:

find /usr/local/git/repositories/ -iname config
su -l git
cd /usr/local/git/repositories/@hashed/XX/XX/XX.git
git log

RAKE tasks:

su -l git -c "cd /usr/local/www/gitlab-ce && RAILS_ENV=production NODE_ENV=production USE_DB=false SKIP_STORAGE_VALIDATION=true NODE_OPTIONS='--max_old_space_size=3584' bundle exec rake --tasks"

Version information:

su -l git -c "cd /usr/local/www/gitlab-ce && rake gitlab:env:info RAILS_ENV=production"

Execution environment information:

su -l git -c "cd /usr/local/www/gitlab-ce && rake gitlab:check RAILS_ENV=production"

View logs:

tail -f /var/log/nginx/*
tail -f /usr/local/www/gitlab-ce/log/*

Restart services:

service nginx restart
service gitlab restart

Import repositories:
In GitLab we have to create the new repository and then push old repository content to the new one.
Clone the old repository:

mkdir asd
cd asd

git clone git@gitlab.alfaexploit.com:gitlab-instance-0a98b435/bastille-basicconfiguration.git

Remove repository origin:

cd bastille-basicconfiguration
git fetch --tags
git remote rm origin

Add new repository remote:

git remote add origin git@flatland.alfaexploit.com\:root/bastille-basicconfiguration.git

Upload content:

git push origin --all
git push --tags

Frankly, if all we need is a Git server, I recommend GitOlite over GitLab. GitOlite simply works without issues compiling assets or dealing with Ruby gems, and it is also a much lighter system.

If you liked the article, you can treat me to a RedBull here