Upgrading Debian 8 Jessie to Debian 9 Stretch

If configuration files are changed the old version will usually be copied to a backup file (*.dpkg-old). Nevertheless it is a good idea to make a system backup yourself before upgrading.

Description how to upgrade

  • https://www.cyberciti.biz/faq/how-to-upgrade-debian-8-jessie-to-debian-9-stretch/


  • Device names stay the same (eth0, ...). Debian 9 only uses a new naming scheme for new installations.

Bacula 7.4.4

  • So far I had no problems to connect bacula-fd v7.4.4 to a bacula server v7.0.5

FreeRadius 3.0.12

  • Major upgrade from version 2. The configuration will not be automatically merged. You have to do this manually.
  • Basic configuration stays pretty much the same. Some configuration variables have been renamed or moved to a different position.
  • New configuration directories:
  • https://github.com/FreeRADIUS/freeradius-server/blob/v3.0.x/raddb/README.rst

ejabberd 16.09

Postfix 3.1.4

  • Had no problems with a basic configuration and a couple of virtual mailbox domains.
  • http://www.postfix.org/announcements.html

amavisd-new 2.10.1-4

  • Almost no changes from previous version 2.10.1-2
  • https://launchpad.net/debian/+source/amavisd-new/+changelog

spamassassin 3.4.1

  • No need to change anything if you have a default installation
  • https://svn.apache.org/repos/asf/spamassassin/branches/3.4/build/announcements/3.4.1.txt


  • New user/group "courier". File permissions need to be adjusted:
  • Some configuration changes (pid file, certificates location, etc.)

ntp 4.2.8p10

  • No longer subject to DRDoS Amplification Attack
  • Option "limited" added (to default restriction in configuration file)
  • Source restriction added (to configuration file)

OpenSSH 7.4

  • Major upgrade from version 6.7
  • No longer subject to ssh client roaming problem (s. Qualys Security Advisory)
  • New "AddKeysToAgent" client parameter (a private key that is used during authentication will be added to ssh-agent)
  • Default for "PermitRootLogin" changed from "yes" to "prohibit-password".
  • Default for "UsePrivilegeSeparation" changed from "yes" to "sandbox"
  • Default for "UseDNS" changed from "yes" to "no"
  • New option to require 2 different public keys for authentication; may be used for two-man rule / four-eyes principle (s. "AuthenticationMethods=publickey,publickey")
  • https://www.openssh.com/txt/

Squid, c-icap, ClamAV: Bug in the service. Please report to the service author!!!!

If you see this error in your c-icap server logfile, it might just be that c-icap is running out of temporary disk space and that the clamav/virus scanner configuration for c-icap is wrong:

Service antivirus_module virus_scan.so
ServiceAlias  avscan virus_scan?allow204=on&sizelimit=off&mode=simple
virus_scan.MaxObjectSize  5M
TmpDir /tmp

The option "... sizelimit=off..." for the virus_scan service means that the configuration value for "MaxObjectSize" will be ingored. If you have too many parallel squid client connections open or large files to download, c-icap is running out of temporary disk space. It will then log the following error message without further explanation:

Bug in the service. Please report to the service author!!!!

The webbrowser download will be terminated with an error message (something like "internal server error").

To solve this problem, add more free space to the partition where TmpDIr resides, and change the virus_scan service option to "... sizelimit=on ...".

In the worst case, free disk space for the c-icap TmpDIr has to be:
MaxServers * ThreadsPerChild * virus_scan.MaxObjectSize


grub-install: error: disk '...' not found

If you get an error like the following, the reason for this might not be so obvious. In my case I got the following error message trying to run grub-install:

# grub-install /dev/mapper/vg1-lv_boot
Installing for i386-pc platform.
grub-install: error: disk `lvmid/OffQLW-SofZ-KH38-jrbl-RXyw-dmDc-VOJuPf/lbiWU0-SkvY-nDET-EGvy-A1PP-fmGb-dGv7yX' not found.

The logical volume I tried to install grub onto was ok (/dev/mapper/vg1-lv_boot). The problem was somewhere else: I previously had a disk failure in a RAID0 md raid. The faulty drive was replaced online by a hot spare drive. But there was still an encrypted swap device configured for the old drive. And that swap device was not part of the md raid, so it was not automatically transferred to the new spare drive.

Only after removing this non-existing swap partition (swapoff <device>) grub-install was working again. So if you come across any error message from grub-install like the one above, the reason for it might be a problem with ANY configured disk on your system. Check for the following errors:

# swapon -s

Are there any swap partitions in use that no longer exist physically?

# pvdisplay
/dev/mapper/cryptswap2: read failed after 0 of 4096 at 0: Input/output error 
/dev/mapper/cryptswap2: read failed after 0 of 4096 at 1998520320: Input/output error 
/dev/mapper/cryptswap2: read failed after 0 of 4096 at 1998577664: Input/output error 
/dev/mapper/cryptswap2: read failed after 0 of 4096 at 4096: Input/output error 
/dev/sdb: read failed after 0 of 4096 at 0: Input/output error 
/dev/sdb: read failed after 0 of 4096 at 1000204795904: Input/output error 
/dev/sdb: read failed after 0 of 4096 at 1000204877824: Input/output error 
/dev/sdb: read failed after 0 of 4096 at 4096: Input/output error 
/dev/sdb1: read failed after 0 of 4096 at 1998520320: Input/output error 
/dev/sdb1: read failed after 0 of 4096 at 1998577664: Input/output error 
/dev/sdb1: read failed after 0 of 4096 at 0: Input/output error 
/dev/sdb1: read failed after 0 of 4096 at 4096: Input/output error 
/dev/sdb5: read failed after 0 of 4096 at 998203392000: Input/output error 
/dev/sdb5: read failed after 0 of 4096 at 998203449344: Input/output error 
/dev/sdb5: read failed after 0 of 4096 at 0: Input/output error 
/dev/sdb5: read failed after 0 of 4096 at 4096: Input/output error 
--- Physical volume ---

Are there any error messages for physical LVM2 volumes? If so, try to remove the erroneous physical volumes from your running configuration. Maybe there are still active mount points on the faulty disks (including swap partitions).

# dmsetup status

All entries in the device mapper list have to be valid. There might not be an obvious error message in the output, so you have to check each dm device manually.

Important things to note:

  • Grub2 no longer relies on the file /boot/grub/device.map . You can create the file with "grub-mkdevicemap", but grub-install does not use it and performs a full system scan by itself.
  • grub-install also examines swap devices, even though it obviously will not use them.

Configuring wireless networks in Linux

1. Overview

This post assumes that you are already familiar with connecting Windows or Mac OS to an existing accesspoint. It also assumes that you have a working wireless network card.  If you are looking for an inexpensive wifi card that you can attach to a USB 2.0 port, take a look at my previous post (CSL 300 Mbit/s wifi adapter with Debian 8 Jessie). You might have to install additional firmware packages.

Here is a list of supported wifi devices by the Linux kernel:

Check with iwconfig that there is a working WiFi device on your computer:

$ iwconfig

wlan0     IEEE 802.11bgn  ESSID:off/any   
          Mode:Managed  Access Point: Not-Associated   Tx-Power=15 dBm    
          Retry short limit:7   RTS thr:off   Fragment thr:off 
          Encryption key:off 
          Power Management:on

This tells us that there is a WiFi device called "wlan0" capable to connect to any 802.11b/g/n accesspoint.

There are 2 ways to configure wireless networks in Linux:

  • Using the graphical tool "NetworkManager"
    The preferred method if you are using a graphical desktop environment. Very similar to Windows or Mac OS and easy to use.
  • On the command line using "wpa_supplicant"
    Only recommended for experienced Linux users.

Both of them are included in every modern Linux distribution and have advantages and disadvantages which I will explain later in this post. You should not mix both methods, just decide for one of them and stick with it. So if you already use NetworkManager to manage ethernet connections, it is easy to add one or more WiFi connections.

Both NetworkManager and the native command line method rely on the package "wpa_supplicant" (or "wpasupplicant") to actually use a wifi network. Nevertheless I will use the term "wpa_supplicant" to refer to the command line method.

There is a plethora of additional graphical network tools in Linux, e.g. graphical front ends for wpa_supplicant. Once you know the basics of wpa_supplicant it is easy to use other tools too. Therefore in this post I will only describe how to configure wpa_supplicant on the command line.

2. Encryption Protocols

WPA2 (802.11i) is today's standard for wireless data encryption. It uses 2 different keys for encrypting traffic between accesspoint and client stations.

NameDescriptionConfiguration OptionRekeying Interval (Default Value)Notes
PTK ("Pairwise Transient Key":)- Consists of several other keys / fields used to encrypt data and distribute GTK to client stations

- Unique to every client station

- Only used for unicast traffic
"wpa_ptk_rekey" in wpa_supplicant.conf?
GTK ("Group Transient Key")- Generated by accesspoint and sent to client stations

- Shared by all client stations

- Only used for multicast, / broadcast traffic
"Group Key Interval" in accesspoint configuration

rekey interval is not configurable in NetworkManager or wpa_supplicant
30 seconds- Not configurable in NetworkManager or wpa_supplicant

- Rekeying is completely up to accesspoint, so there is no way to print the rekey interval on client station (wpa_cli or nmcli)

- wpa_supplicant generates log entries like the following:
wpa_supplicant[1652]: wlan0: WPA: Group rekeying completed with 00:2a:0e:ab:cd:ef [GTK=CCMP]

Both keys are then used to encrypt traffic between accesspoint and client stations. There are 2 protocols for symmetric data encryption:

  • TKIP (Temporal Key Integrity Protocol)
    based on RC4
    insecure and obsolete
    use only in combination with additional encryption layers like VPN or SSH tunnels
  • CCMP (CCM Mode Protocol)
    based on AES
    today's standard

3. Authentication Methods

There are 2 different authentication methods for wireless networks:

  • All users share the same single key
    Primarily used for a smaller number of client stations, e.g. in home networks or small guest networks
  • Every user has his own username / password (or unique client certificate)
    Useful for a larger number of client stations, e.g. in corporate environments or where you have a lot of guest users

WPA2 Personal / PSK (Preshared Key)

The same key (8 - 63 characters) must be configured on accesspoint and client stations. It is directly used as PMK (Pairwise Master Key) by accesspoint, and then used to calculate PTK (Pairwise Transient Key). PTK is then used to calculate GTK.

WPA2 Enterprise / 802.1x

Actual authentication is not performed by the accesspoint, but by a 3rd party server called "authentication server". This is usually a Radius server running "freeradius".

Even though authentication is performed by a separate authentication server, it only knows the MK (Master Key) and its derived PMK (Pairwise Master Key). The PMK is transferred (moved, not copied) from the authentication server to the accesspoint and used to calculate a PTK (Pairwise Transient Key). So the authentication server has no access to neither PTK nor GTK and therefore cannot decrypt traffic (unicast or multicast) between accesspoint and client stations.

  • WPA2 Enterprise usually requires a username / password combination for authentication
    (authentication methods LEAP, FAST, PEAP, and TTLS)
  • Using TLS as the authentication method the client authenticates with a client X.509 certificate.
  • The client itself may use a CA certificate to verify that it is connecting to the right accesspoint (similar to HTTPS connections in webbrowsers).

4. NetworkManager

NetworkManager is part of every modern LInux distribution. After a standard installation of Linux you will see a network icon in the system bar of desktop environment. If you click on it you will see a list of options to configure NetworkManager.

Connection settings that you make in the GUI are stored as plain text files under /etc/NetworkManager/system-connections . (Explanation of all settings:
https://developer.gnome.org/NetworkManager/stable/ref-settings.html )

In addition to configure wireless networks, NetworkManager offers some other useful features:

  • You can integrate NetworkManager with desktop encryption tools like kwallet to prevent passwords from being saved in plain text to the configuration files.
  • You can integrate NetworkManager with firewalld to automatically assign WiFi networks to firewall zones.
  • You can configure NetworkManager to automatically use a VPN connection once you are connected to a specific WiFi network.

General configuration

NetworkManager screenshot: General configuration

Automatically connect to this network when it is available
In most cases leave this unchecked. Otherwise there might be occasions where you involuntary connect to the WiFi network.

All users may connect to this network
Only check this option if you want to share your wifi configuration with other Linux user accounts.

Automatically connect to VPN when using this connection
Useful when using an insecure public WiFi hotspot that you only want to use in combination with a VPN tunnel.

Firewall zone
If you are using firewalld and firewall-config, you may associate this WiFi network with a specific firewall zone. If empty the default firewall zone will be used automatically.

The dialog box layout is a little bit misleading because this field has nothing to do with the previous "Firewall zone" field. If there is more than one of the "Automatically connect to this network ..." wifi networks available, "Priority" defines the order in which those networks will be activated. The first successful connection will be used.

Command line

NetworkManager can also be controlled from the command line with "nmcli".

Display current state of NetworkManager service
$ nmcli g
connected  full          enabled  enabled  enabled  enabled

Show a list of all network connections
$ nmcli c  
mynetwork           abababab-cdcd-12cc-bbef-1212121212ab  802-11-wireless  wlan0 

Stop wifi network
$ nmcli c down id mynetwork

Start wifi network
$ nmcli c up id mynetwork


5. wpa_supplicant

wpa_supplicant runs as a service process in the background. Connections are stored by default in /etc/wpa_supplicant/wpa_supplicant.conf .

Sample configuration file with detailed explanations:

The wpa_supplicant background service can be controlled from the command line with "wpa_cli".

Display list of all command line parameters
$ wpa_cli help

Display a list of configured networks
$ wpa_cli list_networks
0       mynetwork 0a:ab:ee:ef:2a:ef       [CURRENT]

Start wifi network
$ wpa_cli enable_network 0

Stop wifi network
$ wpa_cli disable_network 0

Show current wifi connection status
$ wpa_cli status



BIND9 network ports

List of network ports that the DNS nameserver ISC BIND v9.10 listens to by default:

Port NumberUDP / TCPDescription
53UDPstandard port to respond to name queries
53TCPused for master/slave zone transfers or if query answers don't fit in UDP packet
953TCPcommunicate with rndc client utility
2200TCPstatistics channel (built-in webserver to display statistics page)

Connect to OpenLDAP server with PHP5 (CentOS 7)

Here is a short PHP sample script of how to connect to an OpenLDAP server using the secure LDAPS protocol (port 636).

PHP uses the LDAP settings from the LDAP base packages. in the case of CentOS 7 they are configured in /etc/openldap/ldap.conf . Following two entries are the only ones that are important:

TLS_CACERTDIR   /etc/openldap/certs
TLS_REQCERT     demand

The first line gives the location of the public CA certificate that was used to sign the LDAP server certificate. The second line rejects all invalid certificates. To make the first line work, we need to import the public CA certificate into the local NSS database. For that we use the certutil command line utility (root privileges required):

certutil -A -n ldap -t "C,," -d dbm:/etc/openldap/certs -i /etc/ssl/certs/ldap-ca.pem
certutil -L -d dbm:/etc/openldap/certs

The first line imports an existing CA certificate into the database (with the nickname "ldap" which should be unique). The certificate database uses the old Berkeley DB format, so we need to prefix the location with "dbm:". There are 2 files that make up the certificate database:

  • cert8.db
  • key3.db

The second line of the code example merely lists all existing database entries. It should now include our new CA certificate for LDAP connections:

[root@centos7]# certutil -L -d dbm:/etc/openldap/certs 
Certificate Nickname                                         Trust Attributes 
ldap                                                         C,,

Notice the 3 trust attributes for our new CA certificate. In our case the first field needs to include the trust "C". For a description of all possible values, see "man certutil".

Now that we installed the CA certificate for LDAPS connections, we can actually try to make a connection to the LDAP server with PHP5.

$server = "ldaps://ldap.example.org"; 
echo "Connecting to $server ...\n"; 

#ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7); 
$ldapconn = ldap_connect($server, 636) 
        or die("ERROR: Unable to connect to $server\n"); 
ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3); 
$ldapbind = ldap_bind($ldapconn) 
        or die("ERROR: Unable to bind to $server\n"); 
echo "Ok, now connected to $server\n"; 

Here we make an anonymous connection to the LDAP server. You can also provide a username and password for the ldap_bind() function call. Now call this script from the command line (needs yum package "php-cli"):

$ php php-test.php
Connecting to ldaps://ldap.example.org ... 
Ok, now connected to ldaps://ldap.example.org

Important things to note:

  • Call ldap_set_option() to activate debug output.
  • ldap_connect() does not actually connect to the LDAP server. It only initializes internal data structures and variables. The network connection to port 636 will be made by ldap_bind().
  • You need to explicitly set the LDAP protocol version to 3. Otherwise version 2 will be used, which will not work with contemporary OpenLDAP servers.

Restart graphical user interface in Linux

To help you better understand how to restart the GUI in Linux, here are some background information about how the GUI works in Linux:

If you are familiar with Windows, you know that the graphical Windows interface is tightly integrated into Windows. If you start a Windows computer, you automatically start the graphical user interface (GUI). First the Windows logo is twirling around, and then you are presented with the login screen. Only recent server versions of Windows have the option to be installed without graphical user interface. It is called "Server Core", and even in this mode Windows displays a very basic graphical surface that has only a terminal window. This desktopless installation option was a big deal for Windows, as the GUI is so tightly integrated into the operating system. Also there was no adequate command line interface to control and configure all Windows functions. Therefore Microsoft put great effort into making the PowerShell just as "powerful" as the graphical Windows interface.

Linux follows a completely different approach. By default if you start Linux, only the basic command line interface is started. You are presented with a text login screen, and after you login you see the command prompt of your shell in text mode.

The GUI in Linux is built on top of this minimal system. It is made up of a couple of processes that are launched by the root user and the user that is currently logged in. The starting point for the GUI is a program called the "display manager". The display manager starts the display server and presents you with a graphical login screen. After successful login it starts your desktop environment and your programs like web browser or email client.

So here is the chain of events that start your Linux desktop:

1display managerStarts x-server and displays graphical login screen. After successful login, the desktop environment will be startedGDM, LightDM, KDM, SDDM, etc.
2desktop environmentStarts its window manager and runs user programs.Gnome, Unity, KDE, etc.
3window managerDetermines the look-and-feel of your GUI and is responsible for any 2D / 3D effects.Mutter, Compiz, KWin, etc.

The display server is the actual component that is responsible for drawing pixels on the screen and communicates with graphics card, mouse and keyboard under a graphical environment. Therefore it needs to be present both before and after graphical login. Notice that this is not the case for the desktop environment nor the window manager. They are only started after successful login. As of today the by far most popular display server is the x-server (also known as X11 or x.org server), although it will probably be replaced soon by other display servers like Wayland or Mir. When it comes to 2D- and 3D effects, this is performed by the window manager.

While there is a tight integration of desktop environment and window manager, you can use any display manager to start your favorite desktop environment. Usually there is an option on the login screen to tell the display manager which desktop environment to start after successful login. That might be useful if you accidentally installed and activated another login manager. Let's say you are running Gnome with the GDM display manager. After installing a KDE program, the dependencies of the program package pulled in the whole QT libraries along with the KDM display manager. The next time you login you are presented with the KDM display manager. This is actually no big deal. Just select the GNOME desktop environment from the options menu of KDM. After typing in username and password GNOME will be started.

Here is a list of desktop environments with their default display manager:

Desktop EnvironmentDefault Display Manager
KDEKDM (obsolete) / SDDM

How does Linux know which display manager to start if there are more than one installed?
The file /etc/X11/default-display-manager contains the full path of the default display manager to start.

Back to the original question: How do you restart the graphical user interface of Linux without rebooting the whole computer? The answer is easy: Just restart the display manager.

As we have seen earlier, the display manager is actually the starting point for the whole GUI. By shutting down the display manager, all graphical processes will be stopped too. Here is an example how to stop SDDM (run as root):

$ sudo killall sddm

SDDM will be restarted automatically. With other display managers you may have to start them manually as root.

There is also a way to bypass the display manager. If you are already logged in as a regular user in text mode, you can also start the desktop environment manually. E.g. for KDE you would type in:

$ /usr/bin/startkde

Always make sure to start the desktop environment as a non-privileged user, not as root. Otherwise all programs will be running with root privileges, which is something you should avoid at all costs. If you want to start individual programs with root privileges (e.g. WireShark), there are tools like "kdesudo" that launch a program under the root user.

Important things to note:

  • Unlike Windows there is a strict separation of the graphical user interface and the basic system in Linux.
  • The display manager is started by the root user. It is the entry point for the graphical user interface.
  • The desktop environment is run by a non-privileged user, typically the user that logs into the display manager.
  • In general you can use any display manager to start any desktop environment. You can use GDM to start KDE, or SDDM to start GNOME.
  • To restart the graphical user interface, you need to restart the display manager.

Pinentry not working over SSH with x11forwarding (Thunderbird with Enigmail)

Using Thunderbird with Enigmail over SSH sometimes does not work because you cannot input the passphrase for your private GPG key. Starting pinentry-qt / pinentry-gnome3 / pinentry-gtk2 does not work. Here is a workaround. You can cache the passphrase with gpg-agent before starting Thunderbird. Enigmail will then use the cached passphrase because it runs only gpg2 commands in a subshell in order to encrypt or sign messages.

Connect to the server using x11forwarding:

$ ssh -Y server

Note your DISPLAY environment variable:

$ echo $DISPLAY

Unset / delete the DISPLAY environment variable:


Export GPG_TTY environment variable for gpg:

export GPG_TTY=$(tty)

Make sure that gpg-agent is running:

$ ps aux | grep gpg-agent
user 2058 0.0 0.0 168068 2228 ? Ss Nov10 0:07 gpg-agent --homedir /home/user/.gnupg --use-standard-socket --daemon

Insert the passphrase for your GPG key in gpg-agent by signing a dummy message. Make sure that you enter your passphrase in the pinentry tui not the gpg command prompt.

$ echo test | gpg2 --use-agent -s

The passphrase you are about to enter should be cached by gpg-agent. The cache lifetime is controlled by settings in ~/.gnupg/gpg-agent.conf . Now set the DISPLAY environment variable again to run Thunderbird. Use the value from previous command.

export DISPLAY=localhost:10.0

Start Thunderbird. You should now be able to sign and encrypt email messages with Enigmail without having to enter your gpg passphrase again because it is already cached by gpg-agent.

thunderbird &



PAM authentication per application (Debian 8 Jessie)

PAM is the default authentication mechanism in Linux. It is very flexible and powerful, and even allows you to configure different authentication options for each application. In this example we will use the PAM module "pam_listfile" which is already included in the standard package "libpam-modules".

The application name has to match the name of the file under /etc/pam.d . So for example for application "abc" you have the following PAM configuration file /etc/pam.d/abc :

auth required pam_listfile.so onerr=fail item=group sense=allow file=/etc/group.allow
@include common-auth
@include common-account
@include common-password
@include common-session

The first line only authenticates users that are member of any group listed in /etc/group.allow. The contents of /etc/group.allow may only contain a single line and looks like this:


This will allow only members of the group "abc_group" to login to application "abc". After adding the new configuration files, make sure to always test your PAM settings with pamtester:

# id bob
uid=1003(bob) gid=1003(bob) groups=1003(bob)
# pamtester abc bob authenticate
pamtester: Authentication failure
# usermod -aG abc_group bobpamtester abc bob authenticate
pamtester: successfully authenticated

First the user "bob" is not member of the group "abc_group". pamtester fails to authenticate the user even if you provide the right password. Then after adding "bob" to the group "abc_group" authentication succeeds.



Connect to OpenLDAP server with Perl

This little code example in Perl shows how to connect to an OpenLDAP server using the ldaps protocol. It tries several servers and uses the first one it can connect to.

use strict;

use Net::LDAP;
use Net::LDAP::Extension::WhoAmI;
# LDAPS is basically the same as the LDAP-Module using ldaps:// URIs
#use Net::LDAPS;

my $userName = 'USERNAME';
my $passWord = 'PASSWORD';

my @Servers = ("server1", "server2", "server3");
my $ldap = undef;

# Code = 34, Name: LDAP_INVALID_DN_SYNTAX (dn is not a full path)
# Code = 48, Name: LDAP_INAPPROPRIATE_AUTH (empty dn or password)
# Code = 49, Name: LDAP_INVALID_CREDENTIALS (wrong dn or password)
sub lErr {
  my $mesg = shift;
  printf STDERR "Error: %s\n", $mesg->error();
  printf STDERR "Error Code: %s\n", $mesg->code();
  printf STDERR "Error Name: %s\n", $mesg->error_name();
  printf STDERR "Error Text: %s", $mesg->error_text();
  printf STDERR "Error Description: %s\n", $mesg->error_desc();
  printf STDERR "Server Error: %s\n", $mesg->server_error();

foreach my $server (@Servers) {
  $ldap = Net::LDAP->new("ldaps://$server:636",
  verify => 'require',
  inet4 => 1,
  timeout => 3,
  cafile => '/etc/ssl/certs/ldap_slapd_cacert.pem' );

  if($ldap) {
    print "Ok connecting to $server\n";
  else {
    print "Error connecting to $server: $@\n";

if($ldap) {
  print "Now connected to " . $ldap->host() . "\n";
else {
  exit -1;
my $mesg = $ldap->bind("uid=$userName,ou=People,dc=example,dc=com",
  password => "$passWord");
if($mesg->is_error()) {
  exit $mesg->code;

# Using $ldap->bind again after $ldap->unbind doesn't work

There is also an option to connect to an array of servers with only one function call. It basically does the same thing: Looping through a list of servers and use the first successful connection. But you have to be careful, there is a known bug if "verify" is set to "optional" (s. https://rt.cpan.org/Public/Bug/Display.html?id=118477).