Geek on the road

open source, open knowledge

Troubleshooting EIGRP

Preparing the CCNA is being challenging, troubleshooting just guessing is consuming time, besides you get quicker results if you define some steps about how to proceed. This is the procedure I will normally follow if something is not working properly.

1. Checking the interfaces are in UP/UP state

Before starting with routing protocols, verify the interfaces are working properly

R1> show ip int brief

2. Checking L2

Review if there is any problem on the serial link:

  • KeepAlive removed from one router, it will appear up/up for that interface, on the other end of the link up/down
  • Authentication with either wrong username or wrong password will show down/down on both ends of the link.
  • Mismatched Encapsulation will show down/down on both ends of the link.

3. EIGRP neighbors and AS

Confirm that you have the expected neighbors, besides examine the AS is the same in all the routers.

R1> show ip eigrp neighbor

4. EIGRP interfaces

It may occur that either some interfaces are not enabled or there are some interfaces enabled with a wrong network command.

R1> show ip eigrp interface

If there is some interface that is enabled (from the previous step), but neighbors routers do not see that network, review the configuration. Regarding a network command, review the command itself or the wilcard.

i.e: network 10.0.0.0 (but you actually have 10.4.0.0)
i.e: network 191.1.1.0 0.0.0.1 (actually you want 0.0.0.3 for /30)

R1> show ip protocols 

The above command will show if there is any error, with the network definition. If the network command for that interface was not added, it will not appear.

5. K-values and passive interfaces

Actually, we can get the next information from the previous check, however I think is much clear to review it aside.

R1> show ip protocols 

This command shows you any passive-interface, what it would actually avoid to establish neighbor relationships. Remember that K-values must match on both routers, you can check the values using the previous command as well.

R1(config-router)# no passive-interface s0/0/0

6. EIGRP Authentication

I’m not sure if this topic is covered for the CCNA, however is for sure one of the issues you may find. In this case, configuring:

  • Key chain
  • Key ID
  • Key String

The previous parameters must agree when setting up EIGRP authentication.

7. Multicast in serial links

If Frame Relay is configured on a physical interface, broadcast will not be supported, the same occurs for point-to-multipoint links. Define subinterfaces or modify the ospf network type.

In multipoint networks add the broadcast keyword at the end. The show frame-relay map shuld show broadcast, otherwise it will not work.

R1(config-if)# frame-relay map ip <IPADDRESS> <DLCI> broadcast

8. Access lists filtering

At this point everything has been configured properly, you can see one router is showing adjacency going up and down. Take a look if there is any access list blocking the IP traffic.

R1# show access-list

Definitely a guess method is not an option when you are trying to narrow down some issues and the clock is not from your side.

In my opinion an analysis through the layers L1, L2, L3 and EIGRP details should point you out the error.

Debugging NAT Overload

It turns out I was asked to get the CCNA certification at work. It’s being quite difficult to find time to prepare it, besides I’m traveling quite often, what makes it even more complicated. I was tinkering with Packet Tracer, reviewing some concepts about NAT and I wond up with an interesting case I did not expect, basically because I did not understand NAT at all, now I’m starting.

A router implementing NAT overload keeps a table with a private IP address (RFC1918) and source port mapped to one external routable IP address and its destination port. Below is the image of the lab I prepared today:

Here is the configuration for R1

1
2
3
4
5
6
7
8
9
10
11
12
interface GigabitEthernet0/0
 ip address 191.1.1.1 255.255.255.252
 ip nat outside
!
interface GigabitEthernet0/1
 ip address 192.168.1.1 255.255.255.0
 ip nat inside
!
ip nat inside source list 1 interface GigabitEthernet0/0 overload
ip route 172.16.1.0 255.255.255.0 GigabitEthernet0/0 
!
access-list 1 permit host 192.168.1.2

The configuration for R2 was exactly the same, but using a different network.

1
2
3
4
5
6
7
8
9
10
11
12
interface GigabitEthernet0/0
 ip address 191.1.1.2 255.255.255.252
 ip nat outside
!
interface GigabitEthernet0/1
 ip address 172.16.1.1 255.255.255.0
 ip nat inside
!
ip nat inside source list 1 interface GigabitEthernet0/0 overload
ip route 192.168.1.0 255.255.255.0 GigabitEthernet0/0 
!
access-list 1 permit host 172.16.1.2

The first test was a simple ping form R2 towards R1, it did not work, requests timeout. Taking a look to the translation table on both routers show me the error.

1
2
3
R2# show ip nat translations
Pro  Inside global     Inside local       Outside local      Outside global
icmp 191.1.1.2:21      172.16.1.2:21      192.168.1.2:21     192.168.1.2:21

On R1 we found:

1
2
3
R1# show ip nat translations
Pro  Inside global     Inside local       Outside local      Outside global
icmp 191.1.1.1:21      192.168.1.2:21     191.1.1.2:21       191.1.1.2:21

The problem, R1 was performing NAT, modifying the source IP address from 192.168.1.2 to 191.1.1.1. When the packet arrived to R2, this looked up 192.168.1.2 (outside global ), trying to know the Inside local and proceed to forward the packet, however there is not a translation for this packet. Verifying the statistics, I see the misses (increasing during the ping)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
R2# show ip nat statistics 
Total translations: 4 (0 static, 4 dynamic, 3 extended)
Outside Interfaces: GigabitEthernet0/0
Inside Interfaces: GigabitEthernet0/1
Hits: 1  Misses: 3
Expired translations: 0
Dynamic mappings:

R2# show ip nat statistics 
Total translations: 5 (0 static, 5 dynamic, 4 extended)
Outside Interfaces: GigabitEthernet0/0
Inside Interfaces: GigabitEthernet0/1
Hits: 1  Misses: 5
Expired translations: 0

One of the solutions was to disable NAT on either R1 or R2. However, defining a static map for that miss would do the ping work. The only drawback, this mapping only works for the IP address 192.168.1.2, any other host would fail answering.

1
R2(config)# ip nat outside source static 191.1.1.1 192.168.1.2

The conclusion is that the best approach before debugging is to understand how things work.

Recovering a VDI Disk

Recently I ran into an issue regarding a vm’s storage. It turns out one of the VDI on my virtual machine was faulty. I had some data inside and I didn’t want to lose it.

First of all, we can convert from VDI to raw. I did the conversion from Windows, I guess it should be the same from Linux.

converting
1
 C:/VDIs/VboxManage internalcommands converttoraw  disk-vm-testing.vdi  vdisk.raw

Now let’s see what is inside the raw disk:

fdisk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
dm@testing:#{~} fdisk  -l vdisk.raw
You must set cylinders.
You can do this from the extra functions menu.

Disk vdisk.raw: 0 MB, 0 bytes
255 heads, 63 sectors/track, 0 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x000d598a

    Device Boot      Start         End      Blocks   Id  System
vdisk.raw1   *           1         996     7993344   83  Linux
Partition 1 does not end on cylinder boundary.
vdisk.raw2             996        1045      392193    5  Extended
Partition 2 has different physical/logical endings:
     phys=(1023, 254, 63) logical=(1044, 52, 32)
vdisk.raw5             996        1045      392192   82  Linux swap / Solaris

In this case I only have a first partition, because the second one was used as swap device. I have to find out the offset where the data is.

offset
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
dm@testing:#{~}  parted vdisk.raw
GNU Parted 2.3
Using /media/sf_Downloads/vdisk.raw
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) unit
Unit?  [compact]? B
(parted) p
Model:  (file)
Disk /media/sf_Downloads/vdisk.raw: 8589934592B
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number  Start        End          Size         Type      File system     Flags
 1      1048576B     8186232831B  8185184256B  primary   ext4            boot
 2      8187280384B  8588886015B  401605632B   extended
 5      8187281408B  8588886015B  401604608B   logical   linux-swap(v1)

(parted)q

The ‘Start’ column shows me the offset for the partition I’m interested in. Next step is to map this offset with a loopback device and mount it.

mount
1
2
3
4
5
6
7
dm@testing:#{~} losetup -o 1048576  /dev/loop0  vdisk.raw
dm@testing:#{~} mount /dev/loop0 /mnt/vdi
dm@testing:#{~} mount | grep loop
/dev/loop0 on /mnt/vdi type ext4 (rw)
dm@testing:#{~} ls /mnt/vdi
ls /mnt/
bin  boot  dev  etc  home  initrd.img  lib  lost+found  media  mnt  opt  proc  root  sbin  selinux  srv  sys  tmp  usr  var  vmlinuz

At this point you should be able to access your data, or at least some part of it.

Strace to the Rescue

Today in the IRC somebody asked which would be the best way to know if a process already exists in the system. The choice was between using test -d /proc/PID or kill -0 PID.

Both of them do the job, the question here, we want to use the best one. Suddenly I remembered an option that comes with strace and lets you query the number of syscalls for a given trace. Besides we can order based on the number of syscalls.

cmd1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
adm@testing:${~} strace -c -S calls kill -0 1234
kill: No such process
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
  -nan    0.000000           0        12           mmap2
  -nan    0.000000           0         6           close
  -nan    0.000000           0         5           open
  -nan    0.000000           0         5           fstat64
  -nan    0.000000           0         4           read
  -nan    0.000000           0         4         4 access
  -nan    0.000000           0         3           brk
  -nan    0.000000           0         3           munmap
  -nan    0.000000           0         2           mprotect
  -nan    0.000000           0         1           write
  -nan    0.000000           0         1           execve
  -nan    0.000000           0         1           getpid
  -nan    0.000000           0         1         1 kill
  -nan    0.000000           0         1           dup
  -nan    0.000000           0         1         1 _llseek
  -nan    0.000000           0         1           fcntl64
  -nan    0.000000           0         1           set_thread_area
------ ----------- ----------- --------- --------- ----------------
100.00    0.000000                    {52}       6 total

On the other hand, the second command’s output:

cmd2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
adm@testing:${~}strace -c -S calls test -d /proc/1234
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
  -nan    0.000000           0         7           mmap2
  -nan    0.000000           0         5           close
  -nan    0.000000           0         3           open
  -nan    0.000000           0         3         3 access
  -nan    0.000000           0         3           brk
  -nan    0.000000           0         3           fstat64
  -nan    0.000000           0         2           mprotect
  -nan    0.000000           0         1           read
  -nan    0.000000           0         1           execve
  -nan    0.000000           0         1           munmap
  -nan    0.000000           0         1         1 stat64
  -nan    0.000000           0         1           set_thread_area
------ ----------- ----------- --------- --------- ----------------
100.00    0.000000                   {31}         4 total

The column calls helps to know the number of performed syscalls. Using test -d /proc/PID gives a better performance due to a minor number of syscalls.

I really like strace, is a tool you had better know, here I got syscalls statistics, but you can trace syscalls either specific or a bunch of them, follow forked processes and much more, this is only a simple example. I hope it helps.

Interesting Shell

Finally here is my last compilation about shell environment variables.

1.CDPATH

If this is set, keeps a directory list separated by ‘:’ and every time that you type the command “cd DIR” it searches for the directory in the above list, even if your current directory is not the right one. Let’s see an example, I define the directory holding all my git repositories, if I try to make cd repo from any part on the system, even if I not in the right place, I will get there:

shell
1
2
3
4
5
export CDPATH="~/mygits"
devadm@testing$(~)ls mygits
blog scripts org-mode cv
devadm@testing$(/usr/share/doc) cd  blog
devadm@testing$(~/mygits/blog)

In my opinion is useful to keep just one directory or probably up to two, more than this, it could get messy.

2.FIGNORE

If you want to narrow down the ouput when performing filename completion, this is without a doubt your shell env. When set, it ignores suffixes while performing filename completion.

shell
1
2
3
4
5
devadm@testing$(~)export FIGIGNORE="#:.o:~"
devadm@testing$(~/application)ls file*
file1.o file2.o file1 file1 file~
devadm@testing$(~)ls [TAB] [TAB]
file1 file2

I mentioned this shell env because it was curious to me. I guess I have never found any case where I had to use such a thing, but that does not mean it wouldn’t be useful to somebody else.

3.HISTCONTROL

It can be set to ignorespace , and it does not record words starting with empty space. On the other hand I do not like to have duplicated lines, ingnoredups does not repeat entries in the history. Finally, I like to join both options so I use ignoreboth instead.

.bashrc
1
export HISTCONTROL=ignoreboth

4.HOSTFILE

It holds the path to a file containing a list of hosts in the same format as /etc/hosts , if it is set, tries to complete the hostname with one of the entries on the file. Otherwiese will look for /etc/hosts.

It could be pretty useful if you want to keep a personal file with your hosts.

.bashrc
1
export HOSTFILE="~/.myhosts"

The only problem I see here, if I you want to access hosts defined in /etc/hosts. You could make a script that checks if there is a new entry in the /etc/hosts and then appends the last entries to your personal list. Once again, this is only an idea I came up with, but actually I did not follow through.

5.TMPDIR

If set, bash uses its value as the name of a directory in which creates temporary files. With this option I can set my personal temp directory.

.bashrc
1
2
 export TMPDIR="~/.tmpbash"

6.TMOUT

Sets a timeout and affects to read or select builtin commands, when not input is given. An interesting case is if you set this to your current shell. After N seconds without providing any input it will kill your shell, so keep this in mind.

tmout-read.sh
1
2
3
4
5
6
7
8
9
#!/bin/bash

# Sets to 3 seconds timeout
TMOUT="3"

printf 'Could you please give me an absolute path ?'
read -r -s -n10 absolute_path

[ -z name ] && printf 'Your path is : %s\n' $absolute_path

Well, the last posts were focused on bash shell environment because I was digging into the man bash and I found quite interesting things I did not know, I hope some of them were useful for you as well.

Shell Environment Variables

In the previous post I wrote about different topics such as BASH_ENV, subshells or expressions. Today I’m going to talk about shell enviroment variables.

1. BASHPID

This is the PID of the current bash process. This behaviour is different from $$ in cases such a subshell where $BASHPID says the PID of the subshell, whereas $$ shows the PID of the bash holding the subshell.

bashpid.sh
1
2
$ echo $$ $BASHPID # 23353 23353
$ echo 'Subsshell' $(echo $$; echo $BASHPID)# 21060 23353

2. BASH_LINENO

Number of lines in the current script, from the beginning to the line the function was called from.

bash_lineno.sh
1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash 

function show_env_vars()

  echo "$FUNCNAME"    # show_env_vars
  echo "$BASH_LINENO" # Number on lines till show_env_vars was call (11)
  echo "$LINENO"      # Current line 7

}

show_env_vars

3. DIRSTACK

Array with the directories you are moving using popd and pushd builtins to add/remove directories.

dirstack.sh
1
2
3
4
5
6
7
8
9
10
# After doing some pushd 

$ for i in  ${DIRSTACK[@]}; do printf  'DIR: %s\n' $i; done                                                                                                                                                               

DIR: /tmp
DIR: /var/tmp/testing
DIR: /home/cartoon
DIR: /usr/share/doc
DIR: /var/tmp
DIR: /var/www

4. EUID and GROUPS

EUID of the current user, initialized at shell startup. This variable is read only. On the other hand GROUPS is an array of groups which the current user is member of.

euid_groups.sh
1
2
3
for ((i=0; i < ${#GROUPS[@]};  i++)); do printf 'gid:%d\n' ${GROUPS[$i]}" ; done
gid: 1002
gid: 1001

5. Gathering OS info

Below there are some of the shell environment vars you could use, for instance if you need to check the architecture. I have not tried in Sparc yet but I would like.

1
2
3
4
HOSTNAME='Self explanatory'
HOSTTYPE='Arhcitecture i486'.
OSTYPE='Operating System where bash is running i.e: linux-gnu'
PPID='Parent process ID of the current shell'

6. Random numbers

1
2
3
4
RANDOM A random number betwwen 0 and 32767.
# Getting a random number between 0 and 99

echo "RANDOM: $(( $RANDOM % 100))

In sum, there are a wide number of shell environment variables to keep in mind if you are scripting. Next time I will post more shell vars, but focused on customizing your bash shell.

Nifty Bash Scripting

One of the things I should do more often is to read the man pages. Recently I have spent some time digging into the bash man pages, and I would like to share some interesting stuff.

1.BASH_ENV

If you run a script, it looks for this variable and if it’s set, expands the value to the name of a file and reads its content. The filename should content the absolute pathmane otherwise it will no be able to locate the file.

.bashrc
1
export BASH_ENV="$HOME/.customs"

Take a look to the above example. From now, and beacuse I set BASH_ENV in my .bashrc, my scripts will have a set of fuctions or whatever I defined in there. It would be the same doing source $HOME/.customs in every script. This is really good, you have a set of custom functions or variables available to all your scripts.

A good idea would be to set this variable in either /etc/profile or /etc/bash.bashrc , if you want to share it with the rest of users in the system.

2.Run in a subshell ( ) vs current shell{ }

This is someting you might not pay attention. In order to assign the ouput of some shell commands to a variable, try first to use { }. Let’s see an example:

colors.sh
1
2
3
4
5
6
7
8
9
# Current shell
declare -A colors=();

line=''
{ for key in ${!colors[@]}; do line+="$key" ; done; }

# Subshell
line=
$(for key in ${!colors[@]}; do line+="$key" ; done;)

Perhaps you realized the problem it crops up here.

Current shell {}

-Variables are available while the script is running. In the previus example I can access both variables, colors and line. Commands are separated by ‘;’. It’s also quite handy when using conditionals.

1
 if_true  &&  { cmd1; cmd2; cmd3; }

Subshell $()

-Variable assigments do not remain. Any change to any variable in the script would not take effect after ending the command execution. However it would be possible to get the output by means of either echo or printf commands. Besides variable scope, performance could be worse due to new subshell execution.

3.Using [[ ]] expressions

You might know the old form [ ] , but this is the new one, and has some pretty cool properties as Pattern Matching. I guess the best way to get an idea is watching an example.

bash_regex.sh
1
2
3
4
5
6
7
8
9
10
ip_address="10.20.30.40"
[[ $ip_address =~ ^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$ ]] && echo "Valid IP address"

# Iterating matches
for n in ${BASH_REMATCH[@]}; do echo $n; done
10.20.30.40
10
20
30
40

Of course the above example is just for purpose, actually it does not validate a real ip address, but does the trick. Most interesting is the variable BASH_REMATCH, an array holding each substring matched by parenthesized subexpressions. Regarding regular expressions you should look for in the man as in regex(3) and regex(7).

4. Case and ;& operator

This operator continues the execution to next option if available. I came up with an example and you will see how it works:

pickcolor.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
options="ac:lh"
while getopts $options  flag
do
        case $flag in
                a)
                        list_all="on"
                        ;&
                c)

                        c=${OPTARG,,}
                        [ $list_all == "on" ]  && shift
                        [ $list_all == "off" ] && shift && shift

                        args="$*"
                        msg="${args:-$default}"


                        if [ $list_all == "on" ];then
                                for color in ${!colors[@]}
                                do
                                        draw_with_color "$color" "$msg"
                                done
                        else
                                check_color "$c" && draw_with_color "$c" "$msg"
                        fi

                           ;;
                l|-list)
                        printf 'Available colors: \n'
                        _show_colors && exit
                        ;;
                h|-help)
                        usage && exit
                        ;;
                *)
                        usage && exit
                        ;;
        esac
done

Previus chunk of code is part of the pickcolor script. The main idea was to jazz some text up with a chosen color, then I thought it would be more practical to enable the option of pating the same text with all the available colors.

1
2
3
4
5
6
7
8
9
10
11
12
13
14

pickcolor.sh
usage: pickcolor.sh [OPTIONS] message
Options:
       -a --all    Test all colors for message
       -c --color  Set font color.
       -l --list   List available colors.
       -h --help   Help

# Colors up using all the colors
$ pickcolor -a  Show me the colors

# Color up using just one color
$ pickcolor -c red Paint my room !!

The special operator ;& was really useful and did the trick. Next part I will write about shell vars and some builtin commands to have in mind.

Scripting With VIM

I have been an Emacs geek for really long while, it’s my favourite editor. Whether I have to spend time or not, is always the first option that comes into my mind. Some time ago, I struggled with vim, but due to my previous position I had to work with different OS such as Solaris,AIX or HP-UX. I realized that is good to know several ways to do the same thing, and unfortunately your favourite editor is not always available.

In my daily work I have to fill in many templates in order to configure some network elements such as switches or routers. Likewise, I decided to make a small vim script that will help me out in such an unpleasant task.

helpers.vim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function! Net_acl()
  let s:vip_a  = input('VIP-A:  ')
  let s:vip_b  = input('VIP-B:: ')
  let s:vip_c  = input('VIP-C:  ')
  let s:vip_d  = input('VIP-D:  ')

  :%s/<vlab-a>/\=s:vip_a/g
  :%s/<vlan-b>/\=s:vip_b/g
  :%s/<vlan-c>/\=s:vip_c/g
  :%s/<vlan-dP>/\=s:vip_d/g
endfunction

function! Net_vlan_names()
  :%s/<vlan-\(\w*\)>/vlan-\1/g
  :%s/<\(extravlan\)>/\1/g
endfunction

When the function Net_acl() is called, it will ask the ips addresses you’d like to use for each of the vlans you have defined. Keep in mind, when you define a function in VIM, the first letter must be capital.

using_helpers.vim
1
:call Net_acl()

On the other hand, the function Net_vlan_names() takes a pattern inside angle brackets off. If you have to do repetitive tasks, scripting is the right approach no matter what you use. Just get the job done.

From now on, I will keep on adding more functions to my helpers.vim. I really enjoyed.

Shared Libraries

If you have your own shared libraries with the whole set of your favorite functions, probably you will have seen this common error:

./myapp: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or direct

Let’s take a look inside the binary:

tuxedo@host:$> ldd test
    linux-gate.so.1 =>  (0xb7ef6000)
    libatest.so => not found
    libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb7d81000)

By default the system is looking for in the paths defined in the /etc/ld.so.conf, which recursively adds the definitions in the folder /etc/ld.so.conf.d Here’s the quick trick:

tuxedo@host:$> export LD_LIBRARY_PATH=`pwd` 

I use this whereas I’m implementing my library, after that you can put it wherever you feel like.

tuxedo@host:$> ldd test
linux-gate.so.1 =>  (0xb7f5e000)
    libtest.so => /home/tuxedo/syslib/libtest.so (0xb7f56000)
    libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb7de3000)
    /lib/ld-linux.so.2 (0xb7f5f000)

Now the executable will work. I’ll retake this issue, I have some interesting things to tell about shared libraries. Happy coding!!

Sudo LDAP (II)

The second part of this article is here, so if you missed the first one, you might take a look Part One

3. LDAP setup

Let’s guess your root suffix is dc=company,dc=com, you need to append the next entry to your directory :

dn: ou=sudoers,dc=company,dc=com
  objectClass: top
  objectClass: organizationalunit
  description: Sudo Configuration
  ou: sudoers

Besides we will need a default profile:

dn: cn=defaults,ou=sudoers,dc=company,dc=com
  sudoOption: ignore_local_sudoers
  objectClass: top
  objectClass: sudoRole
  cn: defaults
  description: Our default options
  sudooption: log_host
  sudooption: logfile=/var/log/sudolog
  sudooption: !syslog

Perhaps you would like to get the most of sudo’s powder, take a look in its website. You can add as much profiles as you like, suppose you want to add one for system administration:

dn: cn=sysadmin,ou=sudoers,dc=company,dc=com
 objectClass: top
 objectClass: sudoRole
 cn: unix_admins
 sudoUser: tuxman
 sudoUser: darkman
 sudoUser: bill
 sudoHost: ALL
 sudoCommand: /usr/bin/ls

As far as I concern, how to configure sudo is out of this post, however together the source of sudo there is an utility, _sudoers2ldif__, a perl script that helps you to translate your sudo’s configuration file. Next step requires to modify our profile. Probably you will have a similar profile to this one:

dn: cn=default,ou=profile,dc=company,dc=com
objectClass: DUAConfigProfile
defaultSearchBase: dc=company,dc=com
cn: default
credentialLevel: proxy
defaultServerList: 192.168.76.66
profileTTL: 300
searchTimeLimit: 60
authenticationMethod: simple
serviceSearchDescriptor: passwd:cn=sudoers,dc=company,dc=com

After these modifications you must initialize your client.

4. Setting up /etc/ldap.conf and nsswitch.conf

It’s time to tell our client where to find sudoers file, by means of /etc/ldap.conf, that looks something like this.

   uri ldap://192.168.76.66
   sudoers_base ou=sudoers,dc=company,dc=com
   bindpw  cn=proxyagent,ou=profile,dc=company,dc=com
   binddn  password
   sudoers_debug 0

You might use anonymous access, that’s your choice, just remember to check your ACI’s. Pretty interesting the option sudoers_debug which helps you to debug, at level 3 will show you as much information as possible. The last step, how to find our sudoers’ profile, nsswitch.conf

  sudoers: ldap

Let’s check if is working:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
tuxman@host:$> sudo ls
[sudo] password for client:

sudo ls
LDAP Config Summary
===================
uri              ldap://192.168.76.66
ldap_version     3
sudoers_base     ou=sudoers,dc=company,dc=com
binddn           (anonymous)
bindpw           (anonymous)
ssl              (no)
===================
sudo: ldap_initialize(ld, ldap://192.168.76.66)
sudo: ldap_set_option: debug -> 0
sudo: ldap_set_option: ldap_version -> 3
sudo: ldap_sasl_bind_s() ok
sudo: found:cn=defaults,ou=sudoers,dc=company,dc=com
sudo: ldap sudoOption: 'ignore_local_sudoers'
sudo: ldap sudoOption: 'log_host'
sudo: ldap sudoOption: 'logfile=/var/log/sudolog'
sudo: ldap sudoOption: '!syslog'
sudo: ldap search '(|(sudoUser=tuxman)(sudoUser=%other)(sudoUser=ALL))'
sudo: found:cn=sysadmin,ou=sudoers,dc=company,dc=com
sudo: ldap sudoHost 'ALL' ... MATCH!
sudo: ldap sudoCommand '/usr/bin/ls' ... MATCH!
sudo: Command allowed
sudo: user_matches=1
sudo: host_matches=1
sudo: sudo_ldap_lookup(0)=0x02
tuxman@host:$> files/  sudoers2ldif.pl

At this point everything should be working. Last step, to translate our sudoers file.