Migrating my old git server (on Ubuntu 18.04 LTS) to a new one (on Ubuntu 20.04 LTS), I switched to gitolite3 to manage server authentication. This post explains how.

Refs: https://gitolite.com/gitolite/migr.html and https://gitolite.com/gitolite/basic-admin#appendix-1-bringing-existing-repos-into-gitolite

First, gitolite3 was installed using:

sudo apt install gitolite3
(with administrator's key left blank) and configured with:
sudo dpkg-reconfigure gitolite3
System user name for gitolite: gituser
Repository path: /var/lib/gitolite3
Finally, I copied my public key into /var/lib/gitolite3/.ssh/authorized_keys to give me access to admin SSH authentication to my git server.

Once done, I simply hard copied all bare repository on my old server in the directory /var/lib/gitolite3/repositories of the new server (except for the repository gitolite-admin for which I kept the one installed by gitolite configuration). I also made sure that all copied repositories have the proper owner on the new server:

sudo chown gituser:gituser -R *
(from within /var/lib/gitolite3/repositories).

Then I ran:

su gituser
gitolite compile
gitolite setup --hooks-only
gitolite trigger POST_COMPILE

The last step of the installation was made from my local computer. I first configured the domain name gitolite.mydomain.org to point to my new git server (with the proper addition in /etc/hosts and locally cloned the new admin

git clone gituser@gitolite.mydomain.org:gitolite-admin
In this directory, I then edited the file conf/gitolite.conf to add all repositories and user rights and I added in keydir the corresponding users' keys. Then I pushed that to my new server.

The last step simply consists in asking all users to edit the file .git/config in their repositories to replace the old server URL by gituser@gitolite.mydomain.org.

This post describes how the server can be secured using fail2ban and shorewall. It mostly builds on the procedure that I described earlier in this post with a few additional warnings.


Refs: https://ubuntu.tutorials24x7.com/blog/how-to-install-fail2ban-on-ubuntu-20-04-lts and https://doc.ubuntu-fr.org/fail2ban

fail2ban is a software that is used to protect against brute force and DDoS attacks. The principle is that it blocks IPs that repeatedly fail to authenticate in a variety of services. Here, we explain how to configure it to block failed ssh login attempts. The tutorial will eventually be updated later with other services.

fail2ban is installed with:

sudo apt install fail2ban
and its status can be checked with
sudo systemctl status fail2ban
which, at that state, should return
Active: active (running) since Thu 2020-07-30 18:24:24 CEST; 1min 11s ago

The main configuration files are /etc/fail2ban/fail2ban.conf and /etc/fail2ban/jail.conf, that can be kept as is and amended with the creation of /etc/fail2ban/fail2ban.local and /etc/fail2ban/jail.local for custom configurations. More precisely, I created /etc/fail2ban/fail2ban.local with

loglevel = INFO
logtarget = /var/log/fail2ban.log
and /etc/fail2ban/jail.local with [DEFAULT] ignoreip = XX.XXX.XXX.XX bantime = -1 findtime = 3600 maxretry = 3 destmail = me@mydomain.org action = %(action_mwl)s backend = systemd </pre> that configures fail2ban to ban forever any IP that has at least 3 failed connexion attempts (on sshd service) in the last 3600 seconds. Emails are sent to my address with detailed information on IP and whois who have been banned.

Other specific jails can be configured using custom files in /etc/fail2ban/jail.d/myfile.conf. In my case, I created /etc/fail2ban/jail.d/ssh.conf with

enabled = true
port    = ssh
filter  = sshd
logpath  = /var/log/auth.log
maxretry = 4
that additionnaly ban any IPs failing to connect 4 times as indicated in the file /var/log/auth.log.

The configuration is finally validated with

systemctl restart fail2ban
Note that other interesting commands are:
  • fail2ban-client set JAIL banip IP to ban a given IP address
  • fail2ban-client set JAIL unbanip IP to unban a given IP address
  • fail2ban-client set JAIL addignoreip IP to add an IP to the whitelist


shorewall is a tool to manage easily IP tables and secure the allowed connexions on the server. It is installed with:

sudo apt install shorewall
and a first basic configuration (for a single server) can be obtained with
sudo cp /usr/share/doc/shorewall/examples/one-interface/* /etc/shorewall/.

Starting from that, the default policy is described in /etc/shorewall/policy, where a standard configuration can be:

$FW     net             ACCEPT

net     $FW             DROP            info
net     all             DROP            info      

all     all             REJECT          info
that allows all connexions from the firewall to the net and all other interfaces, drops all connexions from the net and rejects all the other connexions.

Exceptions to these rules are describes in /etc/shorewall/rules that typically should at least contains

SSH(ACCEPT)     net     $FW
if you want to allow ping and ssh connexions to your server.

Be careful that the file /etc/shorewall/interfaces contains the appropriate configuration

net     NET_IF          dhcp,tcpflags,logmartians,nosmurfs,sourceroute=0,physical=YYY
in which YYY is replaced by the name of your network interface as given by ifconfig. Forgetting this setting led me to test the RESCUE mode of Kimsufi after the starting of shorewall...

Finally, shorewall is enabled by editing the file /etc/default/shorewall in which the line starting with startup has to be modified to be:

or (I don't know which one of the two was successful) by running
sudo systemctl enable shorewall
Finally, the value STARTUP_ENABLED of /etc/shorewall/shorewall.conf has to be set to Yes and shorewall is started with
sudo systemctl start shorewall

This post details the installation of OpenLDAP to manage users and authentication.

First configuration

Ref: https://doc.ubuntu-fr.org/slapd (in French)

OpenLDAP is installed using the package slapd:

sudo apt install slapd
sudo dpkg-reconfigure slapd
where the answers have to be:
  • Omit OpenLDAP server configuration? No
  • DNS domain name: mydomain.org
  • Organization name: FunOrg
  • Create database? Yes
  • Administrator password: ***
  • Do you want the database to be removed when slapd is purged? Yes
  • Remove old database? Yes

In addition, the command slappasswd can be used to set an ecrypted administrator password. The file /etc/ldap/ldap.conf is then edited to contain the following files:

BASE	dc=mydomain,dc=org

URI	ldap://localhost:389

DEREF never

At this stage, OpenLDAP is operational and can be started with:

systemctl start slapd.service

Creating groups and users

Ref: https://guide.ubuntu-fr.org/server/openldap-server.html (in French)

First, ldap-utils is installed:

sudo apt install ldap-utils
and can be used to add a new ldiff file that contains the structure of users and groups (hereafter named init_ldap.ldiff:
dn: ou=Someone,dc=mydomain,dc=org
objectClass: organizationalUnit
ou: Someone

dn: ou=AGroup,dc=mydomain,dc=org
objectClass: organizationalUnit
ou: AGroup

dn: cn=tuxette,ou=AGroup,dc=mydomain,dc=org
objectClass: posixGroup
cn: tuxette
gidNumber: 10000

dn: uid=tuxette,ou=Someone,dc=mydomain,dc=org
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: tuxette
sn: Chix
givenName: Tuxette
cn: Tuxette Chix
displayName: Tuxette Chix
uidNumber: 1000
gidNumber: 10000
gecos: Tuxette Chix
loginShell: /bin/bash
homeDirectory: /home/tuxette
shadowExpire: -1
shadowFlag: 0
shadowWarning: 7
shadowMin: 8
shadowMax: 999999
shadowLastChange: 10877
mail: tuxette@mydomain.org
postalCode: 31000
l: Toulouse
mobile: +33 (0)X XX XX XX XX
homePhone: +33 (0)X XX XX XX XX
title: System Administrator
postalAddress: A street in Toulouse
initials: TC

This file indicates that groups are named AGroup, users are named Someone. Then, a first group tuxette and a first user tuxette are declared. The LDAP database is updated with

sudo ldapadd -x -D cn=admin,dc=mydomain,dc=org -W -f init_ldap.ldiff

Authentication with LDAP

Ref: https://doc.ubuntu-fr.org/utilisateurs/fr.laugier/ldap_client (in French) and https://computingforgeeks.com/how-to-configure-ubuntu-18-04-ubuntu-16-04-lts-as-ldap-client/

The following packages are first installed:

sudo apt install libnss-ldap libpam-ldap
with the following answers:
  • LDAP URI: ldap://localhost:389
  • Distinguished name of the search base: dc=mydomain,dc=org
  • LDAP version to use: 3
  • Make local root Database admin: Yes
  • Does the LDAP database require login? No
  • LDAP account for root: cn=admin,cd=mydomain,cn=eu + password

The following step requires the edition of /etc/nsswitch.conf where ldap is added to passwd and group lines.

Then, PAM is configured with the edition of </code>/etc/pam.d/common-password</code> where use_authtok is removed from line 26:

password [success=1 user_unknown=ignore default=die] pam_ldap.so try_first_pass
and the edition of /etc/pam.d/common-session where the following line is added:
session optional pam_mkhomedir.so skel=/etc/skel umask=077
to allow the automatic creation of the home directory at first connexion.

The proper functionning can be tested with

sudo su tuxette
that should create the home directory /home/tuxette and change the current user to tuxette.

LDAP password management

Ref: https://guide.ubuntu-fr.org/server/openldap-server.html#openldap-auth-config

LDAP user passwords can be managed by installing ldapscripts (that is also convenient to add and remove users and more generally to manage the LDAP database):

sudo apt install ldapscripts

It is configured by editing the file /etc/ldapscripts/ldapscripts.conf so as to obtain:


To allow root to automatically use its password, use:

sudo sh -c "echo -n '***' > /etc/ldapscripts/ldapscripts.passwd"
where *** is the admin root password. This file needs to be protected with:
sudo chmod 400 /etc/ldapscripts/ldapscripts.passwd

Then, the command

ldapsetpasswd tuxette
can be used to set tuxette's password. It can be tested by an external SSH connexion (if password connexion is allowed)

Manage LDAP sudoers

Ref: https://doc.ubuntu-fr.org/utilisateurs/fr.laugier/ldap_client#se_connecter_en_tant_qu_utilisateur_ldap

To add a user to the sudoers group, first create the corresponding group (named admin) in LDAP and add tuxette to the admin group:

ldapaddgroup admin
ldapaddusertogroup tuxette admin

This should give tuxette the right to use sudo.

Install phpldapadmin

Ref: https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-openldap-and-phpldapadmin-on-ubuntu-16-04

A web user interface is provided to manage the LDAP database. It is easily installed with:

sudo apt install phpldapadmin
and can be accessed at http://mydomain.org/phpldapadmin right after the installation. A virtual host can be created after commenting the redirection in /etc/apache2/conf-available/phpldapadmin.conf. The configuration file is located at /usr/share/phpldapadmin/config.php with the need to edit
$servers->setValue('server','name','LDAP on myserver');
$servers->setValue('server','base', array('dc=mydomain,dc=org'));
to require login to access your LDAP server information.

Activating memberOf overlay

Refs: http://www.schenkels.nl/2013/03/how-to-setup-openldap-with-memberof-overlay-ubuntu-12-04/, https://blog.debugo.fr/openldap-overlays/, https://www.openldap.org/doc/admin24/overlays.html and https://www.adimian.com/blog/2014/10/how-to-enable-memberof-using-openldap/.

To give access to certain features (typically, access to some PhP services like nextcloud), you need to have another group management besides posixGroup. This group management is included in an overlay (additional module) called **memberOf**. To activate and configure it, you'll need to edit the config part of the database (which can not be handled with phpldapadmin). First create two files: one called activation_memberof.ldif that contains

dn: cn=module,cn=config
objectclass: olcModuleList
objectclass: top
olcmoduleload: memberof.la
olcmodulepath: /usr/lib/ldap
and the other called configuration_memberof.ldif that contains
dn: olcOverlay=memberof,olcDatabase={1}mdb,cn=config
changetype: add
objectClass: olcMemberOf
objectClass: olcOverlayConfig
objectClass: olcConfig
objectClass: top
olcOverlay: memberof
olcMemberOfDangling: ignore
olcMemberOfRefInt: TRUE
olcMemberOfGroupOC: groupOfNames
olcMemberOfMemberAD: member
olcMemberOfMemberOfAD: memberOf
The module is then activated and configured using the command lines:
ldapadd -Y EXTERNAL -H ldapi:/// -f memberof_act.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f memberof_conf.ldif

At this step, **memberOf** is operational and allows you to create groups of types memberOf. These groups have a mandatory field which indicates which users are member of the group. Such a group can thus not be empty. For instance, to create a group called myappuser (intended to grant access to the service provided by myapp to certain users, members of this group), you first create a file myappuser_creation.ldif that contains

dn: cn=myappuser,ou=AGroups,dc=mydomain,dc=org
objectClass: groupOfNames
cn: myappuser
member: uid=tuxette,ou=Someone,dc=mydomain,dc=org
that you import using phpldapadmin menu or with the command line:
ldapadd -x -D cn=admin,dc=mydomain,dc=org -W -f myappuser_creation.ldif

Personal note about unresolved issue: default setting of ldapscripts (with the command lines ldapadduser and ldapaddgroup) does not allow to directly create this type of groups (or does not even create a user with inetOrgPerson), even if the configuration file /etc/ldapscripts/ldapscripts.conf is modified to include:

(this modification leads to a consistency error, stating a relation between posixGroup and memberOf. Templates for these commands are included in /usr/share/doc/ldapscripts/examples and can potentially be modified to solve this issue but this is still to be fixed for me.

The first steps of the installation of a new distribution of Ubuntu 20.04 on Kimsufi (KS7) server comprises the following steps:

Basic installation from the OVH template and upgrade to 20.04 LTS

This step is simply performed by a connexion to Kimsufi interface on which you are directly prompted that your server is available for a fresh install. Clicking on "Réinstaller", you are then asked to choose your distribution (in my case, Ubuntu server 18.04 LTS). I chose a custom installation but did not change anything (except for the hostname and for my ssh public key that I added to allow for a secure SSH connexion). Once the installation performed, the connexion is done using

ssh root@IP

Upgrade to Ubuntu server 20.04 LTS is finally performed with:

apt update
apt upgrade
do-release-upgrade -d

(the -d command is used here to allow the upgrade since 20.04 LTS was still not officially released at the time of my upgrade). During the upgrade, I systematically chose to install the new version of some configuration files and I was also notified of a message on grub not being installed that I ignored. I finally rebooted and edited the file /etc/hostname (with yet another reboot) to give the server its brand new name (with the corresponding update in the file /etc/hosts)!

Setting network time protocol (NTP)

NTP allows to synchronize dates and times through internet. The former ntpd program has been replaced by timedatectl (documentation in French at this link) that provides the same features. The main command is timedatectl that provides the current time status.

First, I switched to my timezone (Europe/Paris) by first checking the European time zones available:

timedatectl list-timezones | grep Europe
and using
timedatectl set-timezone Europe/Paris

Then, I enabled time synchronization through NTP by first editing the configuration file /etc/systemd/timesyncd.conf to add NTP servers (servers can be chosen among this list if you are located in France; otherwise, choose servers close to your location). Servers are sequentially after FallbackNTP (and this line has to be uncommented). Finally, synchronization is allowed with:

timedatectl set-ntp true
and a last check of time status gives:
> timedatectl
               Local time: Fri 2020-07-17 15:57:06 CEST
           Universal time: Fri 2020-07-17 13:57:06 UTC 
                 RTC time: Fri 2020-07-17 13:57:06     
                Time zone: Europe/Paris (CEST, +0200)  
System clock synchronized: yes                         
              NTP service: active                      
          RTC in local TZ: no

Securing SSH connexion

SSH connexion is then secured by forbidding SSH authentication with the edition of /etc/ssh_config and the addition of two lines:

ChallengeResponseAuthentication no
PasswordAuthentication no
followed by a reboot. Only ssh connexion will then be authorized. SSH public keys are stored in the user file ~/.ssh/authorized_keys (with drwx------ on .ssh and rights rw-r--r-- on the file).

I performed the upgrade from Ubuntu (server) 16.04 LTS to Ubuntu 18.04 LTS pretty late this time (on December 2019: it was about time!) and it mostly went smoothly except for a few bugs that I solved as described below:

  • no internet after upgrade: the weirdest bug was the fact that I lost most internet access from server (not to server) after the upgrade. I solved this issue as documented in this thread: I edited the file /etc/systemd/resolved.conf to add the line:
    and re-started the system with
    systemctl restart networking
  • php reconfiguration: php was not working anymore because the previous version was php5, having become obsolete. I re-installed the base php packages:
    sudo apt install php libapache2-mod-php php-mcrypt php-mysql
    and enabled the php module in apache
    a2enmod php7.2
    systemctl reload apache2
  • bug in nextcloud: I was on an old version of nextcloud, which is not upgraded by the system ugrade since it is not a distribution package. Nextcloud showed a blank pages and apache logs (/var/log/apache2/error.log for me) gave the following message:
    Local::copyFromStorage() not compatible with OC\Files\Storage\Common::copyFromStorage
    The bug was documented on nextcloud github repository and I solved it as was done by one of the developer in this commit by editing the file lib/private/Files/Storage/Local.php in nextcloud directory to add the argument $preserveMtime = false to the function copyFromStorage.