Integrating FreeBSD w/ FreeIPA/SSSD

March 24, 2016 2 comments

 

Integrating FreeBSD w/ FreeIPA/SSSD

One of my more recent projects was to integrate FreeBSD into a Kerberos-secured authentication and authorization system based on the FreeIPA architecture. This post is an aggregate HOWTO with information sourced from a couple public (and one private) websites and a mailing list in addition to my own personal experience. The architecture and infrastructure of the FreeIPA system are beyond the scope of this post and will not be covered here. The focus is installing and configuring a client to use the FreeIPA-based authentication and authorization system.

Prerequisites and Requirements

  1. Client must have A & PTR records in DNS
  2. Client hostname must be set to it’s FQDN
  3. Client host table must include a matching FQDN/IP address record
  4. Client should have a so-called break-glass UNIX account
  5. Binary package repository w/ packages supporting FreeIPA
  6. A Kerberos keytab for the client

It is unknown what behavior results due the lack of meeting requirements 1 – 3.

The break-glass account is a local, UNIX-based account on the client filtered via SSSD policy/config.  It will not use SSSD for authentication and it can be arbitrary in nature, so long as it is filtered.  This is useful when remote access is lost due to service outage or similar.  It is also useful to open a session to this user while this process is executed.

Client services won’t start and the client will not authenticate against FreeIPA without the modules installed with the binary packages without having a prepared repository.  This repository must contain the requisite packages compiled with the custom options as described below.

Finally, the host keytab file is generated in the FreeIPA infrastructure during host enrollment.  The keytab file can be retrieved from the IPA server using ipa-getkeytab.  Securely copy this file to the client with mode 0600 to /etc/krb5.keytab.

Overview

The end goal of this work is to integrate FreeBSD into the FreeIPA architecture utilizing remote ssh and sudo.  No other authentication or authorization methods were attempted.  Additionally, this post is being constructed from notes and memory.  Hopefully nothing has been missed and everything is accurate, but it is possible this is not the case.  Help me keep this correct, if necessary.

There are two high level tasks to be completed; Generate the binary package repository with FreeIPA-enabled packages and client installation/configuration. The binary package repository only needs to be done once. Ongoing maintenance of binary repositories is necessary to make newer versions available when necessary.

Building The Binary Package Repository

Execute this process to build a binary package repository containing the necessary packages w/ custom options enabled. It is assumed a pre-existing Poudriere build and distribution environment is available.

$jail_id is the Poudriere jail ID within which the repo is to be built

$portstree_id is the Poudrere Ports tree ID on which to execute the build

$ cat >> $jail_id-make.conf <EOF
WANT_OPENLDAP_SASL=YES
WITH_GSSAPI=YES
EOF
$ cat > pkg_list <<EOF
security/cyrus-sasl2-gssapi
security/sssd
security/sudo
security/openssh-portable
security/pam_mkhomedir
EOF
$ poudriere options -c -n -p $portstree_id security/sssd security/sudo security/openssh-portable
$ poudriere bulk -f pkg_list -j $jail_id -p $portstree_id

Step 3 presents users with dialog boxes populated with various radio options. A null radio is an unselected option while options denoted by an “X” are selected. Configure options for each of the packages as described below:

security/sssd: Enable SMB (Builds and installs the IPA provider)
security/sudo: Enable SSSD backend
security/openssh-portable: Enable MIT Kerberos

Installing/Configuring The Client

The procedure below includes commands executed on the command line as well as descriptions of operations needed to be completed such as editing or creating a file. Several commands and/or files include hostnames inherently describing a dependency on DNS. Open a standby shell in order to maintain access to the system during this operation as access to the system can be halted in executing it.

$cblrserver is the hostname or IP of the  server from which to grab content.  This is simply an HTTP accessible resource.

$ipaserver is the hostname or IP of the IPA server with which to communicate

$domain is the domain of the target host

$hostname is the fully qualified hostname

1) Configure the binary package repository and install the required packages:

$ cat > /etc/pkg/ipa.conf <<-EOF
ipa: {
  url: "http://$cblrserver/cobbler/repo_mirror/freebsd-10_0-amd64-pkgs-ipa",
  mirror_type: "none",
  enabled: yes
}
EOF
$ pkg update -f
$ mkdir -p /etc/ipa /usr/compat/linux/proc /var/log/sssd /var/run/sss/private /var/db/sss
$ pkg install -y -r ipa cyrus-sasl-gssapi sssd sudo openssh-portable pam_mkhomedir

2) Configure Kerberos and SSSD

$ cat /etc/krb5.conf
[libdefaults]
  default_realm = EXAMPLE.COM
  default_keytab_name = FILE:/etc/krb5.keytab
  default_tkt_enctypes = aes256-cts des-cbc-crc aes128-cts arcfour-hmac
  default_tgs_enctypes = aes256-cts des-cbc-crc aes128-cts arcfour-hmac
  dns_lookup_realm = false
  dns_lookup_kdc = false
  rdns = false
  ticket_lifetime = 24h
  forwardable = yes

[realms]
  EXAMPLE.COM = {
    kdc = $ipaserver:88
    master_kdc = $ipaserver:88
    admin_server = $ipaserver:749
    default_domain = $domain
    pkinit_anchors = FILE:/etc/ipa/ca.crt
  }

[domain_realm]
  .example.com = EXAMPLE.COM

[logging]
  kdc = FILE:/var/log/krb5/krb5kdc.log
  admin_server = FILE:/var/log/krb5/kadmin.log
  kadmin_local = FILE:/var/log/krb5/kadmin_local.log
  default = FILE:/var/log/krb5/krb5lib.log

[edit]
$
$
$ cat /usr/local/etc/sssd/sssd.conf
[domain/$domain]
#debug_level = 9
cache_credentials = True
krb5_store_password_if_offline = True
krb5_realm = EXAMPLE.COM
ipa_domain = $domain
id_provider = ipa
auth_provider = ipa
access_provider = ipa
ipa_hostname = $hostname
chpass_provider = ipa
ipa_server = _srv_, $ipaserver
ldap_tls_cacert = /etc/ipa/ca.crt
krb5_keytab = /etc/krb5.keytab

[sssd]
services = nss, pam, ssh, sudo
config_file_version = 2
domains = $domain

[nss]
filter_users = root,smash
homedir_substring = /home

[pam]

[sudo]

[ssh]
$
$

3) Configure NSS similarly to the below:

$ cat /etc/nsswitch.conf
group: files sss
group_compat: nis
hosts: files dns
networks: files
passwd: files sss
passwd_compat: nis
shells: files
services: compat
services_compat: nis
protocols: files
rpc: files
sudoers: files sss
netgroup: files

4) Configure and mount the linproc filesystem

# echo "linproc /usr/compat/linux/proc linprocfs rw 0 0" >> /etc/fstab
# mount -a

5) Configure rc.conf and domainname

$ cat /etc/rc.conf
rpcbind_enable=“YES”
sshd_enable=“NO”
openssh_enable="YES"
ntpd_enable="YES"
nisdomainname=“example.com”
sssd_enable="YES"
$ sudo domainname example.com

6) Configure openssl-portable similarly to:

$ cat /usr/local/etc/ssh/sshd_config
...
GSSAPIAuthentication yes
GSSAPICleanupCredentials yes
PasswordAuthentication yes
...

7) Download the CA certificate

$ wget -O /etc/ipa/ca.crt http://$ipaserver/ipa/config/ca.crt

8) Configure PAM according to these configs:

$ cat /etc/pam.d/sshd
# auth
auth sufficient pam_opie.so no_warn no_fake_prompts
auth requisite pam_opieaccess.so no_warn allow_local
#auth sufficient pam_krb5.so no_warn try_first_pass
#auth sufficient pam_ssh.so no_warn try_first_pass
auth sufficient /usr/local/lib/pam_sss.so use_first_pass
auth required pam_unix.so no_warn try_first_pass

# account
account required pam_nologin.so
#account required pam_krb5.so
account required pam_login_access.so
account sufficient /usr/local/lib/pam_sss.so ignore_unknown_user
account required pam_unix.so

# session
#session optional pam_ssh.so want_agent
session required /usr/local/lib/pam_mkhomedir.so
session required pam_permit.so

# password
#password sufficient pam_krb5.so no_warn try_first_pass
password sufficient /usr/local/lib/pam_sss.so
password required pam_unix.so no_warn try_first_pass
$
$
$ cat /etc/pam.d/system
# auth
auth sufficient pam_opie.so no_warn no_fake_prompts
auth requisite pam_opieaccess.so no_warn allow_local
#auth sufficient pam_krb5.so no_warn try_first_pass
#auth sufficient pam_ssh.so no_warn try_first_pass
auth sufficient /usr/local/lib/pam_sss.so
auth required pam_unix.so no_warn try_first_pass nullok

# account
#account required pam_krb5.so
account sufficient /usr/local/lib/pam_sss.so ignore_unknown_user
account required pam_login_access.so
account required pam_unix.so

# session
#session optional pam_ssh.so want_agent
session required pam_lastlog.so no_fail

# password
#password sufficient pam_krb5.so no_warn try_first_pass
password sufficient /usr/local/lib/pam_sss.so
password required pam_unix.so no_warn try_first_pass

9) Create /etc/netgroup with the following script. Set this script to run via cron to rebuild the file periodically.

This script is ported from one of the references internally to update /etc/netgroup locally on FreeBSD hosts. LDAPSRV is the hostname or IP of the LDAP server to query. This is generally the same as the IPA servers internally. Executing this script via cron will keep the file updated.

#!/bin/sh
#
# Construct a netgroup file from LDAP hostgroup definitions.
# This is a hack for FreeBSD IPA clients because they can't get netgroup
# data through LDAP or sssd backends (lacking nsswitch/nsdb support).
#

PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin
LDAPSRV=ipa.example.com
export PATH

progname=$(basename $0)
tmpf=$(mktemp)

trap "rm -f $tmpf" EXIT

ldapsearch -LLLx -H ldap://${LDAPSRV} \
  -b 'dc=example,dc=com' \
  '(objectClass=$netgroup)' cn $netgroupobj \
| while read line; do
# new line between records; this means a record ended.
if [ "$line" = "" ]; then
  # output netgroup line if we have members.
  if [ "$members" != "" ]; then
    echo "$groupname \\" >>$tmpf
    for host in $members; do
      echo "$host \\" >>$tmpf
    done
    echo "" >>$tmpf
  fi

# reset data
groupname=""
members=""
continue
fi

# parse "key: value" from LDAP
key=${line%%: *}
value=${line##*: }

if [ "$key" = "dn" ]; then
  continue
elif [ "$key" = "cn' ]; then
  groupname=$value
elif [ "$key" = "$netgroupobj" ]; then
  host=${value%%,cn*}
  host=${host##fqdn=}
  members="$members $host"
fi
done

if [ ! -s "$tmpf" ]; then
  echo "$progname: refusing to install an empty file, bailing" >&2
  exit 1
fi

install -m 0644 -o root -g wheel $tmpf /etc/netgroup
rc=$?
if [ $rc -ne 0 ]; then
  echo "$progname: error installing /etc/netgroup (rc = $rc)" >&2
  exit 2
fi

exit 0

10) Restart services or reboot

$ sudo service sssd start
$ sudo service sshd stop &amp;amp;&amp;amp; sudo service openssh start

Upon completion of this procedure, the target host has been configured to communicate with the authentication and authorization frameworks. Reboot is not required, but certainly recommended.

References

  1. The FreeBSD Forums
  2. Opposite Blog
  3. FreeIPA-USERS Mailing List

Disclaimer

Data and information described on this blog are for informational purposes only. The author of this blog provides no warranty/guarantee, expressed or implied, that this data and information will function as described here. Readers are expected to exercise due diligence when researching, developing, and deploying techniques and methods for use within their environments.

Comments posted are the explicit opinions of the comment poster themselves and does not necessarily reflect the views and opinions of the author of this blog.

Categories: FreeBSD

Verisign Announces vBSDcon 2015

May 1, 2015 1 comment

Verisign has recently announced the dates for a second vBSDcon to occur September 11 – 13, 2015 in Reston, VA. vBSDcon is technical, BSD-based conference for users, developers, engineers, and innovators working with BSD-based operating systems such as FreeBSD, OpenBSD, NetBSD, and more…

The program is being expanded in 2015 to colocate a FreeBSD Developer’s Summit the day preceding main conference activities. Additionally, space will be allotted for use of Hacker Lounges and Doc Sprints. These self-moderated sessions are open time during the off hours of the conference for use by members of all the BSD communities to collaborate and work on varying projects.

The inaugural vBSDcon was a resounding success and included speakers from the FreeBSD and OpenBSD communities.  It also featured prestigious sponsors including The FreeBSD Foundation, who is returning as a Developer Summit sponsor in 2015, RootBSD, Daemon Security, iXsystems, HP, Juniper, and CDW.

Starting A Daemon via daemon(8) in FreeBSD

January 27, 2015 1 comment

Starting A Daemon via daemon(8) in FreeBSD

Daemonizing an application that must persist across reboots while including native process management capability is easily accomplished by combining the functions of rc(8) scripts and commands with daemon(8), a utility that detaches itself from the controlling terminal and executing a program. For example, a service written in Python implementing an HTTP API front-end.

Combing these two utilities enables automated process initialization during system boot and provides the ability to stop, start, and restart services manually using either the rc(8) script or service(8). For example:

/usr/local/etc/rc.d/httpapi start

or

service httpapi start

Create an rc(8) script in /usr/local/etc/rc.d with a name descriptive of the application. httpapi is a descriptive name in this case. Writing the rc(8) script is covered extensively well in the “Practical rc.d scripting in BSD” article on FreeBSD.org and is a very good read. Those reading this blog are encouraged to go read that article though, for expediency, a basic implementation may look very similar to:


#!/bin/sh

# REQUIRE: LOGIN

. /etc/rc.subr

name=httpapi
rcvar=httpapi_enable
command=/usr/local/bin/httpapi
command_interpreter=python
httpapi_user=httpapi
start_cmd=”/usr/sbin/daemon -u $httpapi_user $command”

load_rc_config $name
run_rc_command “$1”

The value of the key rcvar is used to automate the startup of the daemon at system boot within /etc/rc.conf. Append a line to the file containing a combination of this value as key a key with a boolean “YES” or “NO” similarly to:

echo "httpapi_enable=\"YES\"" >> /etc/rc.conf

Disclaimer

Data and information described on this blog are for informational purposes only. The author of this blog provides no warranty/guarantee, expressed or implied, that this data and information will function as described here. Readers are expected to exercise due diligence when researching, developing, and deploying techniques and methods for use within their environments.

Comments posted are the explicit opinions of the comment poster themselves and does not necessarily reflect the views and opinions of the author of this blog.

Categories: FreeBSD

QEMU: Convert non-Sparse Image to qcow/qcow2 Sparse Image

December 15, 2014 Leave a comment

Convert non-Sparse Image to qcow/qcow2 Sparse Image

While this is not always ideal, I’ve found it handy to build qcow/qcow2 images (for use in OpenStack) by converting a vmdk on FreeBSD. Often times, VMWare images are created with thick provisioning meaning that the disk consumed on the hypervisor will equal the disk allocated to the virtual though there may be an overwhelming quantity of contiguous zero bytes.  For example, creating a thick provisioned disk for a VM of, say, 20GB will result in approximately 20GB being consumed on the hypervisor’s disk even though, perhaps, only 50% of that is in use. The remaining 50% will be zero’d bytes and, while technically empty, still consuming space.

VMDK’s can be converted to qcow/qcow2 using qemu-img(1) similarly to:

qemu-img convert -f vmdk -O qcow2 freebsd-10.0.0-amd64.vmdk freebsd-10.0.0-amd64.qcow2

Excellent, we now have qcow2 image suitable for use within OpenStack. However, the size of this file is likely to be the same or very similar to the original vmdk. This can be taken a step further to significantly reduce the amount of space the file consumes using a sparse file.

Using the ‘-S’ command line option enables us to ensure the output file becomes sparse, therefore freeing the space it consumed with a series of zero bytes. This option, while it defaults to 4k, accepts an argument* (size) permitting users to specify the quantity of bytes which must be zeros before it can be that portion is made “sparse”.

The resulting command line is very similar to the above, except that it includes ‘-S’ and the argument, for example:

qemu-img convert -f vmdk -O qcow2 -S 4k freebsd-10.0.0-amd64.vmdk freebsd-10.0.0-amd64.qcow2

* NOTE: if installed from FreeBSD Ports, the argument was required despite having a default defined.

Disclaimer

Data and information described on this blog are for informational purposes only. The author of this blog provides no warranty/guarantee, expressed or implied, that this data and information will function as described here. Readers are expected to exercise due diligence when researching, developing, and deploying techniques and methods for use within their environments.

Comments posted are the explicit opinions of the comment poster themselves and does not necessarily reflect the views and opinions of the author of this blog.

Categories: Technical Miscellany

Troubleshooting Large, Stalling git/ssh Transfers

July 14, 2014 1 comment

Why is git/ssh stalling?

While working with the freebsd-ports repo on github in order to track and combine it with an internal remote repo, it was found to apparently stall during a git push to the internal remote repo. Thinking this was likely a fluke in the network or something, the process was re-executed, but exhibited the same behavior. The OS in this case is FreeBSD 10.0-RELEASE-p6.

Troubleshooting

Identifying the problem started by observing the git process in top which was in a “piperd” wait state. An email thread on freebsd-questions@[1] suggested the process was waiting on a read from a pipe. I only needed to identify the process it was waiting for.

Using lsof, according to the same reference, one could identify the offending process by matching the hex value of the pipe file descriptor to the piped process, in this case ssh. Attaching truss to ssh showed that it had apparently stalled on a getsockopt() operation.

A web search turned up related results[2] (found in References below) which indicated the likely problem was a bug in OpenSSH related to TcpRcvBufPoll. Explicitly disabling this in sshd_config and restarting the service did appear to work around the problem. Upon re-execution of my git push, the process didn’t stall and completed successfully.

Oddly enough, after working around this, I was unable to duplicate the behavior on the same host later. Therefore, there is some doubt that this was indeed the problem, but I post this anyway as it did appear to successfully work around it.

References

[1] FreeBSD Mailing List Archives
[2] stackoverflow post

Disclaimer

Data and information described on this blog are for informational purposes only. The author of this blog provides no warranty/guarantee, expressed or implied, that this data and information will function as described here. Readers are expected to exercise due diligence when researching, developing, and deploying techniques and methods for use within their environments.

Comments posted are the explicit opinions of the comment poster themselves and does not necessarily reflect the views and opinions of the author of this blog.

Manual Package Builds For FreeBSD < 10.0-RELEASE

May 28, 2014 1 comment

Just wanted to link to an older blog post I found useful recently…

Given the current state of migration from legacy pkg_* tools to pkg(8) within FreeBSD, the content here is old, but still useful for those that haven’t migrated to 10.x (or newer) or the new pkg(8) framework.

Check out Troy’s Unix Space post on package builds on blogspot.

Disclaimer

Data and information described on this blog are for informational purposes only. The author of this blog provides no warranty/guarantee, expressed or implied, that this data and information will function as described here. Readers are expected to exercise due diligence when researching, developing, and deploying techniques and methods for use within their environments.

Comments posted are the explicit opinions of the comment poster themselves and does not necessarily reflect the views and opinions of the author of this blog.

Categories: FreeBSD

BSDCan 2014 Travel Woes

May 16, 2014 3 comments
I wasn’t going to post anything about my travel woes to Ottawa, ON for BSDCan 2014, but upon receiving the revised itinerary after the 3rd flight cancellation, I decided it was too humorous to pass up…

Originally scheduled to fly out on Wednesday via United, I arrived at the airport, parked my vehicle, removed my bags, and proceeded to walk to the main terminal.  My phone vibrated while walking to United’s check-in counters…I just received a flight cancellation notice citing “severe weather conditions in our route network” despite there being little weather in the areas of Dulles and Ottawa that day.  A United representative rescheduled the flight the following morning which was subsequently cancelled for the same reason…still no real weather to be seen, but there was certainly weather on the way for Thursday evening into Friday.

I contacted the travel agent following the 2nd cancellation and discussed two options;  A connecting flight with a 4 hour layover that day which ended up being cancelled or a non-stop flight Friday morning.  Seeing as how the total time on the former flight would have been 8 – 10 hours, I opted for the latter, shorter, 1.5 hour flight Friday morning, just in time to attend most of BSDCan’s main conference activities.

I awoke Friday morning to a deluge of rain thinking the flight would certainly be cancelled, but the flight status, according to United, was “on schedule”.  I got up and continued my daily morning routine intending to make the drive to the airport while checking the phone periodically for flight status updates.  The phone vibrated as I was preparing to leave home with an email update that the flight was cancelled.  It was the cancellation I had been expecting so, instead of driving to the airport, I continued into the office.

Shortly after arriving at the office, I received an email from United with the revised itinerary:

The irony in the revised itinerary is two-fold in that I depart Dulles to Ottawa on the same plane I was originally scheduled to return to Dulles from Ottawa on and that United thinks it’s ok for me to fly from Dulles to Ottawa, stay on the plane, and fly back to Dulles approximately 30 minutes later.

Categories: Conferences & Summits
Follow

Get every new post delivered to your Inbox.

Join 307 other followers