May 042018

I recently ran into an issue where I needed to update the SSH host keys on a machine which was running NoMachine. In the process, I came across the following annoying error which was not at all indicative of the actual issue

-- NX SERVER START: -c /usr/bin/nxserver - ORIG_COMMAND=
-- NX SERVER START:  - ORIG_COMMAND=                    
Info: Using fds #4 and #3 for communication with nxnode.
HELLO NXSERVER - Version 3.2.0-74-SVN OS (GPL, using backend: not detected)
NX> 105 hello NXCLIENT - Version 3.2.0
NX> 134 Accepted protocol: 3.2.0
NX> 105 SET SHELL_MODE SHELL                            
NX> 105 login
NX> 101 User: awesomeuser
NX> 102 Password:
Info: Closing connection to slave with pid 6424.
NX> 404 ERROR: wrong password or login          
NX> 999 Bye

I verified repeatedly that other methods for logging in were working properly. This problem was due to us using the SSH protocol for NoMachine logins. Unfortunately, this error is also not at all useful because it doesn’t show the actual problem. The problem was that the “nxserver” user had a known_hosts file containing the old ssh host key and for some reason it wouldn’t allow me to accept the new one. I simply removed the old key.

# rm /var/lib/nxserver/home/.ssh/known_hosts
rm: remove regular file `/var/lib/nxserver/home/.ssh/known_hosts'? y

Once the old key was removed, I was once again able to login successfully. Curiously, the known_hosts file was not regenerated. I’m not exactly sure how it wound up there to begin with since this setup was created before I arrived.

 Posted by at 14:29
Oct 062015

When using Citrix XenCenter 6.5, I was suddenly unable to connect to a XenServer instance running 6.2. The following was in the XenCenter Event log.

Failed to connect to
The request was aborted: Could not create SSL/TLS secure channel.

This was not due solely to the version mismatch but, seemingly, to an update in XenCenter which forces stricter SSL checks. The only difference I found was that the XenServer instance I was unable to connect to had a cert containing an old IP address.

The first thing I did was update the parameters used to generate the SSL certificate

vim /opt/xensource/libexec/generate_ssl_cert

Then I was able to simply regenerate the certificate using the new parameters (and giving the resulting certificate the new IP address)

/opt/xensource/libexec/generate_ssl_cert /etc/xensource/xapi-ssl.pem $(hostname -f) && /etc/init.d/xapi start

Once completed, I was able to connect successfully.

 Posted by at 12:18
Jan 122013

In this post, I will describe how to setup incremental database propagation as described in the MIT documentation. Unfortunately, the instructions at the preceding link are incomplete (as of this writing). The aim of this post is to completely describe how to setup incremental propagation.

For the purposes of this tutorial, I will assume you have 2 KDCs (referred to here as and Installing the KDCs is out of the scope of this post. In this post, I will use the default filesystem paths as they exist on Debian for illustration purposes.

First, you need to add kiprop principals for kdc1 and kdc2.

kdc1 # kadmin.local 
Authenticating as principal solj/admin@EXAMPLE.COM with password.
kadmin.local:  addprinc -policy service -randkey kiprop/
Principal "kiprop/" created.
kadmin.local:  addprinc -policy service -randkey kiprop/
Principal "kiprop/" created.

Once you have done this, you will need to dump the database on kdc1 and load it on kdc2.

kdc1 # kdb5_util dump principal

Use a secure method (e.g. scp) to transfer the principal database to kdc2 and load the database there.

kdc2 # kdb5_util load principal

Add the following options in the appropriate [realms] section of /etc/krb5kdc/kdc.conf on both KDCs.

iprop_enable = true
iprop_port = 2121

The actual port you use is up to you. Note that there are other iprop_ options you can use and they are already documented on the MIT page.

Next, you need to allow the kiprop principals the access they need to propagate the database. On kdc1, add the following to /etc/krb5kdc/kadm5.acl.

kiprop/ p
kiprop/ p

Extract kdc1’s kiprop keytab to /etc/krb5kdc/kadm5.keytab.

kdc1 # kadmin.local 
Authenticating as principal solj/admin@EXAMPLE.COM with password.
kadmin.local: ktadd -k /etc/krb5kdc/kadm5.keytab kiprop/
Entry for principal kiprop/ with kvno 2, encryption type aes256-cts-hmac-sha1-96 added to keytab WRFILE:/etc/krb5kdc/kadm5.keytab.
Entry for principal kiprop/ with kvno 2, encryption type aes128-cts-hmac-sha1-96 added to keytab WRFILE:/etc/krb5kdc/kadm5.keytab.
Entry for principal kiprop/ with kvno 2, encryption type des3-cbc-sha1 added to keytab WRFILE:/etc/krb5kdc/kadm5.keytab.
Entry for principal kiprop/ with kvno 2, encryption type arcfour-hmac added to keytab WRFILE:/etc/krb5kdc/kadm5.keytab.

You also need to extract kdc2’s kiprop keytab, however, it should go to the default keytab file (DEFKTNAME).

kdc2 # kadmin -p solj/admin
Authenticating as principal solj/admin with password.
Password for solj/admin@EXAMPLE.COM: 
kadmin: addprinc -policy service -randkey kiprop/
Principal "kiprop/" created.
kadmin:  ktadd kiprop/
Entry for principal kiprop/ with kvno 2, encryption type aes256-cts-hmac-sha1-96 added to keytab WRFILE:/etc/krb5.keytab.
Entry for principal kiprop/ with kvno 2, encryption type aes128-cts-hmac-sha1-96 added to keytab WRFILE:/etc/krb5.keytab.
Entry for principal kiprop/ with kvno 2, encryption type des3-cbc-sha1 added to keytab WRFILE:/etc/krb5.keytab.
Entry for principal kiprop/ with kvno 2, encryption type arcfour-hmac added to keytab WRFILE:/etc/krb5.keytab.

You should now be able to restart kadmind on kdc1 and run kpropd on kdc2 to successfully propagate the database. Note that you can debug this process by watching the kadmind logs as well as running kpropd in the foreground (kpropd -S -d).

 Posted by at 16:25
Jan 022013

For those who are using IPv6, you will likely also want to setup iptables rules similar to those used for IPv4 traffic. There are some slight differences between the two and this post is meant to point out just a couple.

I have a very basic iptables template that looks like the following.

-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
# allow incoming ssh connections
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
# reject everything else
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited

Here is the equivalent ip6tables template.

-A INPUT -p ipv6-icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
# allow incoming ssh connections
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
# reject everything else
-A INPUT -j REJECT --reject-with icmp6-adm-prohibited
-A FORWARD -j REJECT --reject-with icmp6-adm-prohibited

Here you can see that the icmp protocol is now referred to as ipv6-icmp. Also, there is no icmp-host-prohibited qualifier. The equivalent qualifier for IPv6 is icmp6-adm-prohibited. These are the only two I have encountered (so far). Please feel free to leave a list of more in the comments and I will update the post.

 Posted by at 20:45
Sep 052012

This post will guide you through a simple setup to confine users defined in LDAP using Security-Enhanced Linux. SELinux on RHEL6/Debian Wheezy comes with confined user roles which allows quick and easy setup for restricting users who are given access to your machines.

# semanage login -l

Login Name                SELinux User              MLS/MCS Range            

__default__               unconfined_u              s0-s0:c0.c1023           
root                      unconfined_u              s0-s0:c0.c1023           
system_u                  system_u                  s0-s0:c0.c1023

Looking at these default roles, you can see that not a whole lot is restricted on a cleanly-installed machine. Unfortunately, this means that any users you add will be logging in with an unconfined_r role which essentially delegates access controls to systems other than SELinux.

Depending on the role that this machine will play in your organization, you probably want to set the default to something other than unconfined_u. The various default SELinux users can be found in /etc/selinux/targeted/contexts/users/.

# ls -l /etc/selinux/targeted/contexts/users/
total 24
-rw-r--r--. 1 root root 253 Jun 18 09:01 guest_u
-rw-r--r--. 1 root root 389 Jun 18 09:01 root
-rw-r--r--. 1 root root 514 Jun 18 09:01 staff_u
-rw-r--r--. 1 root root 578 Jun 18 09:01 unconfined_u
-rw-r--r--. 1 root root 353 Jun 18 09:01 user_u
-rw-r--r--. 1 root root 307 Jun 18 09:01 xguest_u

For our purposes, we’ll go ahead and set the __default__ selinux user to user_u. To do this, you simply run semanage login -m -s user_u -r s0 __default__. You can see the change below.

# semanage login -l

Login Name                SELinux User              MLS/MCS Range            

__default__               user_u                    s0                       
root                      unconfined_u              s0-s0:c0.c1023           
system_u                  system_u                  s0-s0:c0.c1023

Now, any user who does not have an explicit entry in the output above will be confined to user_u. Great! So, what about potential privileged users who may need sudo access? The user_u profile is inadequate for these types of users as it’s very restrictive. A more appropriate role would be something like staff_u. My user account only exists in LDAP.

# grep -c solj /etc/passwd
# getent passwd solj
solj:*:1000:2000:Sol Jerome:/home/solj:/bin/bash

This is okay. The first thing to do is add a login mapping for the user.

semanage login -a -s staff_u solj

Now, logging in as that user should give you the proper security context.

$ id
uid=1000(solj) gid=2000(sysadmin) groups=2000(sysadmin) context=staff_u:staff_r:staff_t:s0-s0:c0.c1023

Okay, now the last part is to give this person sudo access. My sudo configuration resides in LDAP. Therefore, I will need to add 2 new sudoOptions in order to allow my user to transition properly to sysadm_r:sysadm_t. The following is an example of the type of errors you will see if these options are not present.

$ sudo -i
-bash: /root/.bash_profile: Permission denied

The correct sudoers entry should look like the following.

cn: solj
objectClass: sudoRole
objectClass: top
sudoHost: ALL
sudoRunAs: ALL
sudoUser: solj
sudoCommand: ALL
sudoOption: role=sysadm_r
sudoOption: type=sysadm_t

And now when I run sudo, I am able to transition no problem.

$ sudo -i
# id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) context=staff_u:sysadm_r:sysadm_t:s0-s0:c0.c1023
 Posted by at 18:03
May 032012

This post will cover post-installation steps necessary to go from a completely unmanaged machine to a machine that is setup to be an LDAP server with a basic DIT. This will also setup phpldapadmin for web-based administration of your LDAP directory.

Note: I use nginx here simply because I find it easier to deal with. There’s no requirement for it and you may find it easier to use apache.

The post-install script used to setup the LDAP server is below. The reason this is used is because there are a lot of one time things that happen during the installation of an LDAP server and I have not yet been able to represent some of these events in bcfg2. The script below depends on some files that are hosted on another web server. I will provide the necessary files needed below.

The custom php packages are available from The reason for using these packages is that php-fpm is not available from the stock RHEL repositories or from EPEL. Since I am already familiar with php-fpm and I prefer to use it, I decided to simply download only the necessary packages rather than use the entire repository.


# ssl settings
SSLSUBJ="/C=Country Code/ST=Some State/L=City/O=Organization Name/OU=Organizational Unit Name/CN=${HOSTNAME}"

# misc settings


    #FIXME: remove when bcfg2 selinux policy works properly
    setenforce 0

    #FIXME: remove when bcfg2 selinux policy works properly
    setenforce 1

    echo -n "Installing custom php packages for phpldapadmin..."
    yum -y --nogpgcheck install ${RPMS} >/dev/null
    # FIXME: update the kernel (kernel panics when not done here)
    yum -y update kernel >/dev/null
    echo "done"

    /usr/bin/openssl req -batch -new -x509 -nodes \
        -subj "${SSLSUBJ}" \
        -out ${WEBCERT} \
        -keyout ${WEBKEY} -days 3600 >/dev/null
    /usr/bin/openssl req -batch -new -x509 -nodes \
        -subj "${SSLSUBJ}" \
        -out ${SLAPDCERT} \
        -keyout ${SLAPDKEY} -days 3600 >/dev/null

    cacertdir_rehash /etc/openldap/cacerts

    # setup ldap admin password
    echo -n "Please enter a new ldap admin password: "
    read -s PASSWD
    # get bcfg2 password
    echo -n "Please enter the bcfg2 password (can be found in /etc/bcfg2.conf on an existing client): "
    read -s BCFG2PASSWD

    if [ -x /usr/sbin/slappasswd ]
        SLAPPASSWD=$(/usr/sbin/slappasswd -s ${PASSWD})
        echo "Failed to find slappasswd. Aborting."
        exit 1

    /usr/bin/curl -o ${LDAPDIR}/fix-admin-account.ldif ${LDIFDIR}/fix-admin-account.ldif
    /usr/bin/curl -o ${LDAPDIR}/new-ldap-setup.ldif ${LDIFDIR}/new-ldap-setup.ldif
    /usr/bin/curl -o ${LDAPDIR}/base.ldif ${LDIFDIR}/base.ldif
    sed -i "s|PWREPLACE|${SLAPPASSWD}|" ${LDAPDIR}/fix-admin-account.ldif ${LDAPDIR}/new-ldap-setup.ldif
    # this seems wrong. if someone knows how to do this better, please inform me.
    echo "olcRootPW: ${SLAPPASSWD}" >> /etc/openldap/slapd.d/cn=config/olcDatabase={0}config.ldif
    /bin/cp /usr/share/openldap-servers/DB_CONFIG.example /var/lib/ldap/DB_CONFIG
    chown -R ldap. /var/lib/ldap
    /sbin/service slapd start && sleep 1 # FIXME: how do you do this properly?
    ldapadd -w ${PASSWD} -x -D "cn=config" -f ${LDAPDIR}/fix-admin-account.ldif
    ldapadd -w ${PASSWD} -x -D "cn=admin,cn=config" -f ${LDAPDIR}/new-ldap-setup.ldif
    ldapadd -w ${PASSWD} -x -D "cn=Manager,dc=uh,dc=edu" -f ${LDAPDIR}/base.ldif

    /usr/bin/curl -o ${LDAPDIR}/sudo-index.ldif ${LDIFDIR}/sudo-index.ldif
    cp /usr/share/doc/$(rpm -q sudo --qf "%{NAME}"-"%{VERSION}")/schema.OpenLDAP /etc/openldap/schema/sudo.schema
    restorecon -F -R -v /etc/openldap/schema
    mkdir ${LDAPDIR}/sudo-ldap
    echo "include /etc/openldap/schema/sudo.schema" > ${LDAPDIR}/sudo-ldap/sudoschema.conf
    slapcat -f ${LDAPDIR}/sudo-ldap/sudoschema.conf -F /tmp \
            -n0 -s "cn={0}sudo,cn=schema,cn=config" > ${LDAPDIR}/sudo-ldap/sudo-tmp.ldif
    sed -i 's/{0}sudo/sudo/' ${LDAPDIR}/sudo-ldap/sudo-tmp.ldif
    head -n-8 ${LDAPDIR}/sudo-ldap/sudo-tmp.ldif > ${LDAPDIR}/sudo-ldap/sudo.ldif
    echo -e "\n$(cat ${LDAPDIR}/sudo-index.ldif)" >> ${LDAPDIR}/sudo-ldap/sudo.ldif # add in our sudo index
    rm ${LDAPDIR}/sudo-index.ldif
    ldapadd -w ${PASSWD} -x -D "cn=admin,cn=config" -f ${LDAPDIR}/sudo-ldap/sudo.ldif

    cp /usr/share/doc/$(rpm -q autofs --qf "%{NAME}"-"%{VERSION}")/autofs.schema /etc/openldap/schema/autofs.schema
    restorecon -F -R -v /etc/openldap/schema
    mkdir ${LDAPDIR}/autofs
    echo "include /etc/openldap/schema/core.schema" > ${LDAPDIR/autofs/autofs.conf
    echo "include /etc/openldap/schema/cosine.schema" >> ${LDAPDIR/autofs/autofs.conf
    echo "include /etc/openldap/schema/autofs.schema" >> ${LDAPDIR/autofs/autofs.conf
    slapcat -f ${LDAPDIR}/autofs/autofs.conf -F /tmp \
            -n0 -s "cn={2}autofs,cn=schema,cn=config" > ${LDAPDIR}/autofs/autofs-tmp.ldif
    sed -i 's/{2}autofs/autofs/' ${LDAPDIR}/autofs/autofs-tmp.ldif
    head -n-8 ${LDAPDIR}/autofs/autofs-tmp.ldif > ${LDAPDIR}/autofs/autofs.ldif
    ldapadd -w ${PASSWD} -x -D "cn=admin,cn=config" -f ${LDAPDIR}/autofs/autofs.ldif

    while true; do
        echo -n "Is this machine a master or a slave? [m/s] "
        read status
        case $status in
                /usr/bin/curl -o ${LDAPDIR}/olcaccess.ldif ${LDIFDIR}/olcaccess.ldif
                /usr/bin/curl -o ${LDAPDIR}/syncprov-module.ldif ${LDIFDIR}/syncprov-module.ldif
                /usr/bin/curl -o ${LDAPDIR}/syncprov.ldif ${LDIFDIR}/syncprov.ldif
                ldapmodify -w ${PASSWD} -D "cn=admin,cn=config" -f ${LDAPDIR}/olcaccess.ldif
                ldapmodify -w ${PASSWD} -D "cn=admin,cn=config" -f ${LDAPDIR}/syncprov-module.ldif
                ldapadd -w ${PASSWD} -D "cn=admin,cn=config" -f ${LDAPDIR}/syncprov.ldif
                # grab master SSL certificate
                /usr/bin/curl -o ${SLAPDMASTERCERT} ${HTTPDIR}/slapd-master.crt
                cacertdir_rehash /etc/openldap/cacerts

                /usr/bin/curl -o ${LDAPDIR}/olcaccess-slave.ldif ${LDIFDIR}/olcaccess-slave.ldif
                /usr/bin/curl -o ${LDAPDIR}/syncrepl.ldif ${LDIFDIR}/syncrepl.ldif
                ldapmodify -w ${PASSWD} -D "cn=admin,cn=config" -f ${LDAPDIR}/olcaccess-slave.ldif
                ldapmodify -w ${PASSWD} -D "cn=admin,cn=config" -f ${LDAPDIR}/syncrepl.ldif
                echo "Invalid response."

    /usr/sbin/bcfg2 -vqe -S https://bcfg2.server:6789 -x ${BCFG2PASSWD} --ca-cert=/etc/ -r packages
    /usr/sbin/bcfg2 -vqer packages

mkdir -p ${LDAPDIR}
echo "Setup complete. Please reboot."

Here are the accompanying ldif files needed.


# Set password for cn=admin,cn=config
dn: olcDatabase={0}config,cn=config
changetype: modify
replace: olcRootPW
replace: olcRootDN
olcRootDN: cn=admin,cn=config


# create modules area
dn: cn=module,cn=config
objectClass: olcModuleList
cn: module{0}
olcModulePath: /usr/lib64/openldap

# set access for the monitor db.
dn: olcDatabase={1}monitor,cn=config
changetype: modify
replace: olcAccess
olcAccess: {0}to * by dn.base="cn=Manager,dc=yourcompany,dc=com" read by * none

# change LDAP domain, password and access rights.
dn: olcDatabase={2}bdb,cn=config
changetype: modify
replace: olcSuffix
olcSuffix: dc=yourcompany,dc=com
replace: olcRootDN
olcRootDN: cn=Manager,dc=yourcompany,dc=com
replace: olcRootPW

# setup SSL
dn: cn=config
replace: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/pki/tls/private/slapd.key
replace: olcTLSCertificateFile
olcTLSCertificateFile: /etc/openldap/cacerts/slapd.crt
replace: olcTLSCipherSuite
olcTLSCipherSuite: HIGH:MEDIUM:-SSLv2


# setup basic tree
dn: dc=yourcompany,dc=com
dc: uh
objectClass: top
objectClass: domain

dn: ou=People,dc=yourcompany,dc=com
ou: People
objectClass: top
objectClass: organizationalUnit

dn: ou=Group,dc=yourcompany,dc=com
ou: Group
objectClass: top
objectClass: organizationalUnit

dn: cn=replicator,dc=yourcompany,dc=com
cn: replicator
objectClass: organizationalRole
objectClass: simpleSecurityObject
objectClass: top
description: LDAP replication user
userPassword: changeme


# add sudo index
dn: olcDatabase={2}bdb,cn=config
changetype: modify
add: olcDbIndex
olcDbIndex: sudoUser eq

These can be changed to match your needs. In this case, anyone in the group cn=ldapadmin,ou=yourorganizationalunit,dc=yourcompany,dc=com is given full access to the LDAP directory (UPDATE: Please note that the ldapadmin cn is a groupOfNames objectClass [_not_ a posixGroup]).

dn: olcDatabase={2}bdb,cn=config
changetype: modify
add: olcAccess
olcAccess: {0}to * by dn.base="cn=replicator,dc=yourcompany,dc=com" read by * break
olcAccess: {1}to * by group.exact="cn=ldapadmin,ou=yourorganizationalunit,dc=yourcompany,dc=com" write by * break
olcAccess: {2}to attrs=userPassword by self write by anonymous auth by * none
olcAccess: {3}to attrs=shadowLastChange by self write by * read
olcAccess: {4}to * by * read


# setup syncprov module
dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: {1}syncprov

You will want to modify these settings according to your replication needs.


dn: olcOverlay={0}syncprov,olcDatabase={2}bdb,cn=config
objectClass: olcSyncProvConfig
olcOverlay: {0}syncprov
olcSpCheckpoint: 100 10
olcSpSessionlog: 100


dn: olcDatabase={2}bdb,cn=config
changetype: modify
add: olcAccess
olcAccess: {0}to * by group.exact="cn=ldapadmin,ou=yourorganizationalunit,dc=yourcompany,dc=com" write by * break
olcAccess: {1}to attrs=userPassword by self write by anonymous auth by * none
olcAccess: {2}to * by * read


dn: olcDatabase={2}bdb,cn=config
changetype: modify
add: olcSyncrepl
olcSyncrepl: {0}rid=000 provider=ldaps://ldap-master-server searchbase=dc=yourcompany,dc=com type=refreshAndPersist retry="5 5 300 +" bindmethod=simple binddn="cn=re
plicator,dc=yourcompany,dc=com" credentials="changeme" tls_cacertdir=/etc/openldap/cacerts                                                                              -

Here are the relevant bits from the ldap bundle in the bcfg2 repository

<Bundle name='ldap'>
        <Group name='ldap-server'>
                <BoundPath name='/etc/openldap/cacerts/slapd.crt' type='permissions' owner='ldap' group='ldap' perms='0600'/>
                <BoundPath name='/etc/pki/tls/private/slapd.key' type='permissions' owner='ldap' group='ldap' perms='0600'/>
                <Package name='ldapvi'/>
                <Package name='openldap-clients'/>
                <Package name='openldap-servers'/>
                        <Path name='/etc/sysconfig/ldap'/>
                        <BoundPath name='/etc/openldap/slapd.d' type='directory' owner='ldap' group='ldap' perms='0700'/>

                <Service name='slapd'/>

                <!-- phpLDAPadmin settings -->
                <Package name='php'/>
                        <BoundPath name='/var/lib/php/session' type='directory' owner='root' group='nginx' perms='0770'/>
                <Package name='php-fpm'/>
                        <Path name='/etc/php-fpm.d/www.conf'/>
                <Package name='php-ldap'/>
                <Package name='nginx'/>
                <Package name='phpldapadmin'/>
                <Service name='php-fpm'/>
                <Service name='nginx'/>
                <Path name='/etc/nginx/conf.d/phpldapadmin.conf'/>
                <Path name='/etc/openldap/ldap.conf'/>
                <Path name='/etc/phpldapadmin/config.php'/>
                <BoundPath name='/var/www/html/phpldapadmin' type='symlink' to='/usr/share/phpldapadmin/htdocs'/>
                <Path name='/usr/share/phpldapadmin/templates/creation/custom_uh.xml'/>

The /etc/sysconfig/ldap file needs to be modified to allow LDAPS by uncommenting SLAPD_LDAPS=yes. In /etc/php-fpm.d/www.conf, you need to make sure the user/group are set to nginx (if you are using nginx as your web server).

My nginx configuration for /etc/nginx/conf.d/phpldapadmin.conf looks like this.

server {
        listen          80;
        server_name     ldap-server-hostname;
        rewrite         ^/(.*) https://ldap-server-hostname/$1 permanent;

server {
        listen                  443; # listen also for IPv4 traffic on "regular" IPv4 sockets
        server_name             ldap-server-hostname;
        access_log              /var/log/nginx/ssl-access.log;
        error_log               /var/log/nginx/ssl-error.log;
        root                    /var/www/html/phpldapadmin;

        ssl                     on;
        ssl_certificate         /etc/pki/tls/certs/phpldapadmin.crt;
        ssl_certificate_key     /etc/pki/tls/private/phpldapadmin.key;

        index           index.php index.html;

        location ~ \.php$ {
                fastcgi_pass    localhost:9000;
                fastcgi_index   index.php;
                fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include         fastcgi_params;
                fastcgi_param   HTTPS on;

I needed the following lines in /etc/openldap/ldap.conf to get phpldapadmin working properly.

URI             ldaps://localhost/
TLS_CACERTDIR   /etc/openldap/cacerts
TLS_REQCERT     never

Lastly, you will need to modify /etc/phpldapadmin/config.php with appropriate values for your site.

 Posted by at 13:16
Mar 162012

The first thing you need to do is install all the required packages. The following is my rt.xml bundle for use with Bcfg2. It details the Packages, Services, and Paths that need to be setup for RT to work properly (the fetchmail configuration is not detailed below).

Note: There are some packages explicitly listed below due to the fact that I configure apt not to install recommended packages by default. You may or may not need to install them in order for your request tracker installation to work properly (the libfcgi-perl is required in order to use the rt4-fcgi method).

<Bundle name='rt'>
        <Package name="mysql-server"/>
        <Service name="mysql"/>
        <!-- rt configuration -->
        <Package name="request-tracker4"/>
                <Package name="libmime-tools-perl"/>
                <Package name="libmouse-perl"/>
                <Package name="libterm-readline-perl-perl"/>
                <Package name="libxml-libxml-perl"/>
                <Package name="rt4-fcgi"/>
                        <Path name="/etc/default/rt4-fcgi"/>
                        <Package name="libfcgi-perl"/>
                <Package name="rt4-db-mysql"/>
                        <Package name="mysql-client"/>
                <Package name="ttf-dejavu-core"/>
        <Service name="request-tracker4"/>
        <Package name="nginx-full"/>
                <Path name="/etc/nginx/sites-available/rt"/>
        <Service name="nginx"/>
        <Service name="rt4-fcgi"/>
        <Path name="/etc/request-tracker4/RT_SiteConfig.d/50-debconf"/>
        <Path name="/etc/request-tracker4/RT_SiteConfig.d/51-dbconfig-common"/>
        <!-- fetchmail configuration -->
        <Package name="fetchmail"/>
        <Path name="/etc/default/fetchmail"/>
        <Path name="/etc/fetchmailrc"/>
        <Service name="fetchmail"/>

You will want to install the mysql-server package first and setup the RT database.

root@rt:~# mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 43
Server version: 5.1.61-2 (Debian)

Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> create database rtdb;
Query OK, 1 row affected (0.00 sec)

mysql> grant all privileges on rtdb.* to 'rt'@'localhost' identified by 'SECRETPASSWORD';
Query OK, 0 rows affected (0.03 sec)

mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)

Next, modify /etc/request-tracker4/RT_SiteConfig.d/50-debconf to suit your custom environment. You also need to reconfigure /etc/request-tracker4/RT_SiteConfig.d/51-dbconfig-common to use mysql with the appropriate values for the database that was created.

# generated by dbconfig-common

# map from dbconfig-common database types to their names as known by RT
my %typemap = (
    mysql   => 'mysql',
    pgsql   => 'Pg',
    sqlite3 => 'SQLite',
Set($DatabaseType, $typemap{mysql} || "UNKNOWN");

Set($DatabaseHost, 'localhost');
Set($DatabasePort, '3306');

Set($DatabaseUser , 'rt');
Set($DatabasePassword , 'SECRETPASSWORD');

# SQLite needs a special case, since $DatabaseName must be a full pathname
#my $dbc_dbname = ''; if ( "" eq "sqlite3" ) { Set ($DatabaseName, '' . '/' . $dbc_dbname); } else { Set ($DatabaseName, $dbc_dbname); }
Set($DatabaseName, 'rtdb');

By default, the RT install uses an sqlite database. The above tells it to use the mysql database that was created in the previous step. Once that is complete, you need to update the SiteConfig by running update-rt-siteconfig. Then you can move on to configuring nginx.

The following nginx configuration works for configuring RT with fcgi (probably not optimal, suggestions for improvement are welcome):

server {
        listen                  80;
        server_name   ;

        access_log              /var/log/nginx/;
        error_log               /var/log/nginx/;
        root                    /usr/share/request-tracker4/html;
        client_max_body_size    20M;

        location /NoAuth/images/ {
                try_files local/html$uri
                expires 1M;
        location / {
                fastcgi_pass    unix:/var/run/rt4-fcgi.sock;
                include         /etc/nginx/fastcgi_params;
                fastcgi_param   SCRIPT_NAME     "";
                fastcgi_param   PATH_INFO       $uri;
        location @main {
                fastcgi_pass    unix:/var/run/rt4-fcgi.sock;
                include         /etc/nginx/fastcgi_params;
                fastcgi_param   SCRIPT_NAME     "";
                fastcgi_param   PATH_INFO       $uri;

The above nginx configuration expects the following in /etc/default/rt4-fcgi (to enable the rt4-fcgi init script).

# Defaults for request-tracker4 initscript
# sourced by /etc/init.d/rt4-fcgi

# This is a POSIX shell fragment


# number of RT workers:

You should now be able to start the rt4-fcgi init script and nginx and login with the default RT username and password.

 Posted by at 17:17
Dec 172011

Update: Post updated to allow for generation of multiple mirrors of varying versions of RHEL as shown at

The latest version of mrepo available in EPEL (mrepo-0.8.7-2.el6.noarch.rpm at the time of this writing) won’t allow you to mirror RHN without some slight modifications. This is a brief howto that will highlight exactly what is needed to allow mrepo to mirror RHN on RHEL6.

NOTE: This howto is loosely based on the RHEL5 mrepo howto available at

You will need to install the following packages:


Next, you need to setup mrepo.conf with your RHN login credentials by adding the following to the [main] section:

rhnlogin = <username>:<password>

Set a silly default for up2date

echo "up2date default" > /etc/sysconfig/rhn/sources

Set your machine’s UUID

UUID=$(uuidgen) ; /bin/echo -e "uuid[comment]=Universally Unique ID for this server\nrhnuuid=$UUID" \
 > /etc/sysconfig/rhn/up2date-uuid

Set up additional repositories (RHEL5 in this case). Note that the path will differ depending on your srcdir as specified in mrepo.conf.

gensystemid -u RHN_username -p RHN_password --release=5Server --arch=x86_64 /srv/mrepo/src/5Server-x86_64/

Make sure the proper certificate is in use

cp `cat /etc/sysconfig/rhn/up2date|grep ^sslCACert=|cut -d= -f2` /usr/share/rhn/RHNS-CA-CERT

Add your RHEL6 (and any additional platforms) mrepo configuration (e.g.):

### Name: Red Hat Enterprise Server v6
### URL:

name = Red Hat Enterprise Server $release ($arch)
release = 6
arch = x86_64
metadata = yum repomd

### RHEL6 repositories
updates = rhns://<your satellite server>/rhel-$arch-server-$release
optional = rhns://<your satellite server>/rhel-$arch-server-optional-$release
rhn-tools = rhns://<your satellite server>/$repo-rhel-$arch-server-$release
supplementary = rhns://<your satellite server>/rhel-$arch-server-$repo-$release


### Name: Red Hat Enterprise Server v5
### URL:

name = Red Hat Enterprise Server $release ($arch)
release = 5
arch = x86_64
metadata = repomd

### RHEL5 repositories
updates = rhns://<your satellite server>/rhel-$arch-server-$release
vt = rhns://<your satellite server>/rhel-$arch-server-$repo-$release
supplementary = rhns://<your satellite server>/rhel-$arch-server-$repo-$release
fastrack = rhns://<your satellite server>/rhel-$arch-server-$repo-$release
hts = rhns://<your satellite server>/rhel-$arch-server-$repo-$release
rhn-tools = rhns://<your satellite server>/$repo-rhel-$arch-server-$release

Lastly, you need to fix a couple bugs in the current mrepo release so that it will run successfully on RHEL6. The first file to change is /usr/share/mrepo/up2date_client/

--- /usr/share/mrepo/up2date_client/     2008-08-14 19:14:47.000000000 -0500
+++ /var/lib/bcfg2/Cfg/usr/share/mrepo/up2date_client/   2011-12-02 09:27:07.500138609 -0600
@@ -13,7 +13,7 @@
 import time
 import rpm
 import string
-import md5
+import hashlib

 sys.path.insert(0, "/usr/share/rhn/")
@@ -158,7 +158,7 @@

 def md5sum(fileName):
-    hashvalue =
+    hashvalue = hashlib.md5()

         f = open(fileName, "r")

The second file to change is /usr/share/mrepo/rhn/

--- /usr/share/mrepo/rhn/  2008-08-14 19:14:47.000000000 -0500
+++ /var/lib/bcfg2/Cfg/usr/share/mrepo/rhn/  2011-12-13 15:04:19.236104253 -0600
@@ -33,6 +33,7 @@

 class Transport(xmlrpclib.Transport):
     user_agent = "" % __version__
+    _use_datetime = False

     def __init__(self, transfer=0, encoding=0, refreshCallback=None,

That should do it. You should then be able to run mrepo -ugv and update your local mrepo mirror with the latest from RHN.

 Posted by at 21:57
Oct 252011

I use Bcfg2 to create and synchronize the /etc/ssh/ssh_known_hosts file across all the machines I manage. The result of this is that the known_hosts file actually contains useful information.

The one case where this bites me is when I want to boot from a live CD and image the drive on the machine itself. Booting into the live CD and starting sshd creates new keys which gives me this ugly message:

Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that the RSA host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
Please contact your system administrator.
Add correct host key in /root/.ssh/known_hosts to get rid of this message.
Offending key in /etc/ssh/ssh_known_hosts:153
Password authentication is disabled to avoid man-in-the-middle attacks.
Keyboard-interactive authentication is disabled to avoid man-in-the-middle attacks.
Permission denied (publickey,keyboard-interactive).

I don’t want to go to the trouble of editing the global known_hosts file since it actually contains correct information (and someone may want to use that before bcfg2 runs again). Therefore, I just want to temporarily disable checking of the file. I found a cool little option for ssh to do just that. It’s called GlobalKnownHostsFile and we can set it to /dev/null to temporarily turn off the feature.

ssh -o GlobalKnownHostsFile=/dev/null

You will probably want to use this in conjunction with the UserKnownHostsFile option so that the client doesn’t save the temporary key to your ~/.ssh/known_hosts.

 Posted by at 16:49
Oct 232011

UPDATE: In response to a comment below, I have added this warning to the top. Please do not use any of these files unmodified. They have been created/tested for my purposes and are meant to be guides which will help you understand how the Debian preseed process works.

In this post, I will walk through a simple preseed file that can be used to install a very minimal Debian (wheezy) machine in ~10 minutes (depending on the mirror used). The installer will only ask for the hostname. Everything else will be automated.

To get started, you will want to download the netboot ISO. You can get this from or any other Debian mirror. If all your machines are on the same network, it may make sense to setup gPXE. Details on that will be covered in a later post.

In order to use the preseed file outlined below, you will need to boot with the following appended options (press TAB at the installer screen). Note that the debugging variables are only necessary if you are having trouble.

DEBCONF_DEBUG=5 locale=en_US.UTF-8 console-keymaps-at/keymap=us domain=unassigned-domain url=

The first thing we will do is configure the networking settings necessary to automate the install.

# Networking

# Uncomment and fill in these in order to preseed the hostname question
#d-i netcfg/get_hostname string unassigned-hostname
#d-i netcfg/get_domain string unassigned-domain
d-i netcfg/choose_interface select eth0
d-i mirror/http/proxy string

I am pointing to the default US Debian archive. You should change this to suit your setup. Also note that here is where we tell the installer to use the “wheezy” installation sources.

# Installation Sources

d-i mirror/country string US
d-i mirror/http/mirror string
d-i mirror/http/directory string /debian/
d-i mirror/suite string wheezy

Here, I am using the default partitioning scheme and wiping any existing partitions. You may need to change this if you want custom partitions.

# Disk Partitioning/Boot loader

d-i partman-auto/disk string /dev/sda
#d-i partman-auto/method string lvm
d-i partman-auto/method string regular
d-i partman-auto/purge_lvm_from_device boolean true

# And the same goes for the confirmation to write the lvm partitions.
#d-i partman-lvm/confirm boolean true

# You can choose from any of the predefined partitioning recipes.
# Note: this must be preseeded with a localized (translated) value.
#d-i partman-auto/choose_recipe \
#       select All files in one partition (recommended for new users)
d-i partman-auto/choose_recipe select /lib/partman/recipes/30atomic
#d-i partman-auto/choose_recipe \
#       select Separate /home partition
#d-i partman-auto/choose_recipe \
#       select Separate /home, /usr, /var, and /tmp partitions

# This makes partman automatically partition without confirmation.
d-i partman/confirm_write_new_label boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true

d-i grub-installer/only_debian boolean true
d-i grub-installer/with_other_os boolean true
d-i grub-pc/install_devices multiselect /dev/sda

Once again, your localization settings will likely differ from these, so modify as needed.

# Localizations

# Keyboard localization
d-i console-keymaps-at/keymap select us
#d-i console-setup/variantcode string dvorak

# Timezone
d-i clock-setup/utc boolean true
d-i time/zone string America/Chicago

d-i apt-setup/wheezy-updates boolean true
d-i apt-setup/non-free boolean true
d-i apt-setup/security-updates boolean true
d-i apt-setup/contrib boolean true

I usually don’t setup a default user when I install servers. These settings just create a root user (with login capabilities) having the password ‘r00tme’. You will not want to use this preseed file unmodified if your machine is connected directly to the internet. You can also configure preseed with a crypted root password, but I still recommend changing it once the install is complete.

# User Creation

d-i passwd/root-login boolean true
d-i passwd/make-user boolean false
d-i passwd/root-password password r00tme
d-i passwd/root-password-again password r00tme
d-i user-setup/allow-password-weak boolean true
d-i user-setup/password-weak boolean true

Setup Bcfg2 to do the post-install business (will be covered in a later post).

# Software Selections

tasksel tasksel/first multiselect
d-i pkgsel/include string openvpn vim openssh-server
d-i base-installer/install-recommends boolean false
d-i popularity-contest/participate boolean false

# don't try and do automatic updates; that's bcfg2's job
d-i pkgsel/update-policy select none

d-i finish-install/reboot_in_progress note

d-i preseed/late_command string \
in-target wget -O /root/; \
in-target /bin/bash /root/
 Posted by at 17:18