Maintenance: GitLab GKE platform upgrade and software upgrade on Friday Oct. 22 at 9 p.m. Service may not be available between 9 p.m. and 9:20 p.m.

Commit 07405157 authored by Karl Kornel's avatar Karl Kornel
Browse files

duo: Rework Duo to be a type, that can be brought in by other code

parent b5520097
This README file explains how to use base::duo.
VERSION 4->5 UPGRADE NOTE: If your code involves base::duo, that's not going to
work anymore. Have a look at base::duo::config instead, or README.ssh.
The term Duo uses is "integration", to refer to a set of credentials that a
client (like your system) will use to authenticate itself to Duo. base::duo
manages the process of fetching and customizing 'duo-unix' integrations, which
is the type of integration that can be used for things like login, sudo, and
web-based two-step (such as something that your web application might trigger).
Duo integrations are keyed on the system name, so if you have multiple Duo uses
on a single system (e.g. SSH and sudo), all uses will share the same Duo
integration, but _may_ use different Duo configuration files: If the Duo uses
on a single system have different needs (such as one failing safe and one
failing secure), that will require separate Duo configuration files.
Duo integration keys do not change or expire, unless a `wallet destroy` or a
Duo administrator manually deletes a Duo integration.
If you do ever need to destroy a Duo integration, here's the command to use:
wallet destroy pam-duo hostname.stanford.edu
To generate a Duo configuration file, instantiate an object of
base::duo::config, where the name is the path to the Duo configuration file.
For example:
base::duo::config { '/etc/secure/duo_webapp.conf':
ensure => present,
failsecure => true,
}
To see the options available, have a look at the header text in
manifests/duo/config.pp. There are other classes in the base::duo namespace,
but they're all invoked as needed by base::duo::config.
To be honest, the only time you'll need to invoke base::duo::config directly is
when you have a custom thing that wants to leverage Duo. If you're interested
in using Duo to authenticate SSH or sudo, have a look in README.ssh or
README.sudo instead.
# Set up Duo. Note that this class does not _enable_ Duo for any service,
# rather, it simply downloads the pam_duo software and the appropriate
# wallet files that allow Duo to be used.
# See base::sudo and base::ssh for services that leverage this class.
# wallet_name: the name for the duo wallet object. Defaults to the
# fully-qualified domain name of the host.
class base::duo(
$wallet_name = $::fqdn
){
# Pull in Duo's PAM integration package
package { 'libpam-duo': ensure => present }
# Install the duo configuration. The object is not written to the
# default loaction because base::wallet will not overwrite the
# configuration file supplied with the package install.
$wallet_name_downcase = downcase($wallet_name)
base::wallet { $wallet_name_downcase:
ensure => present,
type => 'duo-pam',
path => '/etc/security/pam_duo_su.conf',
require => Package['libpam-duo'],
}
}
# Set up a custom Duo configuration. Note that this class does not _enable_ Duo.
# Instead, this type downloads a common Duo integration, copies it, and then
# customizes it according to the parameters you specify.
#
# Your client code is responsible for leveraging the configuration, such as by
# using PAM.
#
# See base::sudo and base::ssh for services that leverage this class.
#
# Here are the parameters:
#
# name: (namevar) The path to place the customized Duo configuration file.
#
# ensure: Set to "present" (the default) or "absent".
#
# wallet_name: the name for the common Duo wallet object. Defaults to the
# fully-qualified domain name of the host.
#
# use_gecos: A boolean, defaults to false. When true, Duo will get the
# username from the GECOS field (known in Puppet as the comment field) in the
# system passwd file. When false, Duo will use the user's username. This is
# used when a user is logging in with an account where their username does not
# match their Duo username.
#
# fail_secure: A boolean, defaults to false. When false, a Duo timeout will
# cause the Duo authentication to pass, allowing the user to continue logging
# in. When true, a Duo timeout will cause the Duo authentication to fail,
# blocking the user from logging in.
define base::duo::config (
$ensure = 'present',
$wallet_name = $::fqdn,
$use_gecos = false,
$fail_secure = false,
) {
# Validate $ensure, $use_gecos, and $fail_secure
if ($ensure != 'present') and ($ensure != 'absent') {
fail('ensure may only be "present" or "absent"')
}
if !is_bool($use_gecos) {
fail('base::duo::use_gecos must be true or false')
}
if !is_bool($fail_secure) {
fail('base::duo::fail_secure must be true or false')
}
# Translate $use_gecos from boolean into present/absent
if $use_gecos {
$include_gecos_line = present
} else {
$include_gecos_line = absent
}
# Translate $fail_secure from boolean into present/absent
if $fail_secure {
$include_fail_line = present
} else {
$include_fail_line = absent
}
# Decide if we even need to do anything!
if $ensure == 'present' {
# If the common config hasn't been loaded, do that now
if !defined(Base::Duo::Config::Common[$wallet_name]) {
base::duo::config::common { $wallet_name:
ensure => present,
}
}
# First, copy our common Duo config file
file { $name:
ensure => present,
source => "/etc/security/pam_duo_${wallet_name}.conf",
replace => false,
require => Base::Duo::Config::Common[$wallet_name],
}
# Next, add our GECOS and failmode lines
file_line { "duo_gecos_${name}":
ensure => $include_gecos_line,
path => $name,
line => 'send_gecos = yes',
require => File[$name],
}
file_line { "duo_fail_secure_${name}":
ensure => $include_fail_line,
path => $name,
line => 'failmode = secure',
require => File[$name],
}
} # Done handling $ensure == 'present'
else {
# Make sure our custom Duo config file is gone
file { $name:
ensure => absent,
}
} # Done handling $ensure == 'absent'
}
# Set up some common Duo configuration. Normally each host has its own Duo
# key, but some hosts have multiple Duo keys (for example, one for the host and
# one for an application running on the host). So, we need a separate type
# that can be called to pull the Duo credentials from Wallet.
#
# The Wallet object will be stored to /etc/security/pam_duo_$name.conf
# If you don't need to customize anything, then your code can use it directly,
# otherwise your code will need to copy it, and then make changes as
# appropriate.
define base::duo::config::common (
$ensure = 'present',
) {
# Validate $ensure
if ($ensure != 'present') and ($ensure != 'absent') {
fail('ensure may only be "present" or "absent"')
}
# Lowercase the name, and then pull the object from Wallet
$wallet_name_downcase = downcase($name)
# Bring in the Wallet object, or get rid of it!
# Also, bring in the Duo package, as a convenience to the client.
if ($ensure == present) {
require base::duo::package
base::wallet { $wallet_name_downcase:
ensure => present,
type => 'duo-pam',
path => "/etc/security/pam_duo_${name}.conf",
}
} else {
file { "/etc/security/pam_duo_${name}.conf":
ensure => absent,
}
}
}
# base::duo::package is used to bring in the packages that provide Duo
# functionality. This does not activate Duo, nor does it configure Duo.
# Clients should `require base::duo::package` in their code, so that multiple
# sources can "require" Duo. Then, your code should declare an appropriate
# base::duo::config to set up the configuration the way you want.
#
# As a convenience, if your code is using the base::duo::config type or the
# base::duo::config::common type, then you get base::duo::package
# automatically!
class base::duo::package (
$wallet_name = $::fqdn,
$use_gecos = false,
$fail_secure = false,
) {
# What we pull in depends on the OS family
case $::osfamily {
redhat: {
# For RHEL, bring in the duo_unix, with Duo's key
include base::rpm::duo
base::rpm::import { 'duo-rpmkey':
url => 'http://yum.stanford.edu/RPM-GPG-KEY-DUO',
signature => 'gpg-pubkey-15d32efc-522883e4';
}
package { 'duo_unix':
ensure => installed,
require => Base::Rpm::Import[ 'duo-rpmkey' ],
}
}
debian: {
# The libpam-duo that ships with Debian doesn't support GECOS, so we need
# a backport. That means we need a pin, and THAT means we need to make
# sure that the debian-stanford backports repository is already set up!
# libpam-duo-gecos is a custom-made virtual package that ensures we are
# using a new-enough version of libpam-duo.
file { '/etc/apt/preferences.d/duo':
ensure => present,
content => template('base/duo/duo.erb'),
require => File['/etc/apt/sources.list.d/stanford.list'],
}
package { 'libpam-duo-gecos':
ensure => present,
require => File['/etc/apt/preferences.d/duo'],
}
}
default: {
fail('Your OS family is not recognized for installing the Duo PAM module')
}
}
}
# Use backported Duo packages
# (This file is maintained by Puppet)
#
# We need a Duo package that includes GECOS support. The existing Duo packages
# in main don't have that, so we need to use the backported package we build.
Package: libduo-dev
Pin: release a=<%= lsbdistcodename %>-backports
Pin-Priority: 995
Package: libduo3
Pin: release a=<%= lsbdistcodename %>-backports
Pin-Priority: 995
Package: libpam-duo
Pin: release a=<%= lsbdistcodename %>-backports
Pin-Priority: 995
Package: login-duo
Pin: release a=<%= lsbdistcodename %>-backports
Pin-Priority: 995
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment