Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PAM module for loading keys via ssh-add #2469

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions Makefile.am
Expand Up @@ -252,7 +252,9 @@ include src/ws/Makefile-ws.am
include src/static/Makefile-static.am
include src/remotectl/Makefile-remotectl.am
include src/reauthorize/Makefile-reauthorize.am
include src/retest/Makefile.am
include src/selinux/Makefile-selinux.am
include src/pam-ssh-add/Makefile.am
include branding/default/Makefile.am
include branding/fedora/Makefile.am
include branding/centos/Makefile.am
Expand Down
23 changes: 19 additions & 4 deletions configure.ac
Expand Up @@ -159,9 +159,10 @@ AC_CHECK_HEADER([security/pam_appl.h], ,
AC_CHECK_LIB(pam, pam_open_session, [ true ],
[AC_MSG_ERROR([Couldn't find PAM library. Try installing pam-devel])]
)
COCKPIT_SESSION_LIBS="$COCKPIT_SESSION_LIBS -lpam"
COCKPIT_WS_LIBS="$COCKPIT_WS_LIBS -lpam"
REAUTHORIZE_LIBS="$REAUTHORIZE_LIBS -lpam"
PAM_LIBS="-lpam"
COCKPIT_SESSION_LIBS="$COCKPIT_SESSION_LIBS $PAM_LIBS"
COCKPIT_WS_LIBS="$COCKPIT_WS_LIBS $PAM_LIBS"
REAUTHORIZE_LIBS="$REAUTHORIZE_LIBS $PAM_LIBS"

# pam module directory
AC_ARG_WITH([pamdir],
Expand Down Expand Up @@ -255,8 +256,15 @@ AC_DEFINE_UNQUOTED([PATH_PKEXEC], ["$PKEXEC"], [Location of pkexec binary])
AC_PATH_PROG([SUDO], [sudo], [/usr/bin/sudo], [$PATH:/usr/local/sbin:/usr/sbin:/sbin])
AC_DEFINE_UNQUOTED([PATH_SUDO], ["$SUDO"], [Location of sudo binary])

# Config
# ssh-add
AC_PATH_PROG([SSH_ADD], [ssh-add], [/usr/bin/ssh-add], [$PATH:/usr/local/sbin:/usr/sbin:/sbin])
AC_DEFINE_UNQUOTED([PATH_SSH_ADD], ["$SSH_ADD"], [Location of ssh-add binary])

# ssh-agent
AC_PATH_PROG([SSH_AGENT], [ssh-agent], [/usr/bin/ssh-agent], [$PATH:/usr/local/bin:/usr/bin:/bin])
AC_DEFINE_UNQUOTED([PATH_SSH_AGENT], ["$SSH_AGENT"], [Location of ssh-agent binary])

# Config
AC_DEFINE_UNQUOTED([COCKPIT_CONFIG_FILE],["$sysconfdir/cockpit/cockpit.conf"],[Configuration file])

changequote(,)dnl
Expand Down Expand Up @@ -433,6 +441,7 @@ AC_MSG_RESULT($enable_strict)
#

AC_SUBST(REAUTHORIZE_LIBS)
AC_SUBST(PAM_LIBS)

AC_OUTPUT([
Makefile
Expand Down Expand Up @@ -474,4 +483,10 @@ echo "
With address sanitizer: ${asan_status}
Branding: ${BRAND}
Supports key auth: ${key_auth}

pkexec: ${PKEXEC}
ssh-add: ${SSH_ADD}
ssh-agent: ${SSH_AGENT}
sudo: ${SUDO}
usermod: ${USERMOD}
"
1 change: 1 addition & 0 deletions doc/guide/Makefile-guide.am
Expand Up @@ -6,6 +6,7 @@ GUIDE_INCLUDES = \
doc/guide/api-container.xml \
doc/guide/api-base1.xml \
doc/guide/api-server.xml \
doc/guide/authentication.xml \
doc/guide/embedding.xml \
doc/guide/packages.xml \
doc/guide/https.xml \
Expand Down
34 changes: 34 additions & 0 deletions doc/guide/authentication.xml
@@ -0,0 +1,34 @@
<?xml version="1.0"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
<chapter id="authentication">
<title>Cockpit Authentication</title>

<para>Cockpit allows you to monitor and administer several servers at the same time.
While you will need to connect to the primary server with a password
or kerberos ticket, cockpit does support accessing secondary machines via
public key based authentication.</para>

<para>Once a user is successfully logged in, an ssh-agent is started.
The following keys are then preloaded into the ssh-agent provided
they are supported by your ssh version, present, with the correct
permission, and either unencrypted or encrypted with the same
password that was used to login.</para>

<programlisting>
~/.ssh/identity
~/.ssh/id_rsa
~/.ssh/id_dsa
~/.ssh/id_ed25519
~/.ssh/id_ecdsa
</programlisting>

<para>Cockpit will provide an interface for loading other keys into the agent
that could not be automatically loaded.</para>

<para>Note that when a user is authenticated in this way the authentication
happens without a password, as such the standard cockpit reauthorization
mechanisms do not work. The user will only be able to obtain additional
priviledges if they do not require a password.</para>

</chapter>
1 change: 1 addition & 0 deletions doc/guide/cockpit-guide.xml
Expand Up @@ -24,6 +24,7 @@

<xi:include href="https.xml"/>
<xi:include href="listen.xml"/>
<xi:include href="authentication.xml"/>
<xi:include href="sso.xml"/>
</part>

Expand Down
26 changes: 26 additions & 0 deletions doc/key-auth.md
@@ -0,0 +1,26 @@

Cockpit Key Based Authentication
================================

Cockpit allows you to monitor and administer several servers at the same time.
While you will need to connect to the primary server with a password
or kerberos ticket, cockpit does support accessing secondary machines via
public key based authentication.

Note that when a user is authenticated in this way the authentication happens
without a password, as such the standard cockpit reauthorization mechanisms do
not work. The user will only be able to obtain additional priviledges if they do not require a password.

In order to support key based authentication cockpit adds pam_ssh_add.so
to it's pam stack. Once a user is successfully logged in a new ssh-agent
is started and ssh-add is run to load the default keys, if a password is
requested the one the user logged in with is provided.

When cockpit-ws attempts to establish an ssh connection to a new server,
it will request a "stream" channel with the internal name "ssh-agent"
from the bridge. That channel proxies the running ssh-agent
allowing the new ssh connection to use it as a standard ssh agent and
offer any loaded public keys to the remote server as authentication options.

When the user logs out or has their session terminated, the ssh-agent is also
terminated.
2 changes: 2 additions & 0 deletions doc/man/Makefile-man.am
Expand Up @@ -11,12 +11,14 @@ if WITH_COCKPIT_WS
man_MANS += \
doc/man/cockpit-ws.8 \
doc/man/cockpit.conf.5 \
doc/man/pam_ssh_add.8 \
doc/man/remotectl.8 \
$(NULL)

EXTRA_DIST += \
doc/man/cockpit-ws.xml \
doc/man/cockpit.conf.xml \
doc/man/pam_ssh_add.xml \
doc/man/remotectl.xml \
$(NULL)

Expand Down
91 changes: 91 additions & 0 deletions doc/man/pam_ssh_add.xml
@@ -0,0 +1,91 @@
<refentry id="pam_ssh_add.8">

<!--
This file is part of Cockpit.

Copyright (C) 2015 Red Hat, Inc.

Cockpit is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

Cockpit is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
-->

<refentryinfo>
<title>pam_ssh_add</title>
<productname>pam_ssh_add</productname>
</refentryinfo>

<refmeta>
<refentrytitle>pam_ssh_add</refentrytitle>
<manvolnum>8</manvolnum>
</refmeta>

<refnamediv>
<refname>pam_ssh_add</refname>
<refpurpose>PAM module to auto load ssh keys into an agent</refpurpose>
</refnamediv>

<refsect1><title>DESCRIPTION</title>
<para>
pam_ssh_add provides authentication and session modules that
allow users to start their session with a running ssh-agent with as
many ssh keys loaded as possible.
</para>
<para>
If used, the authentication module simply stores the authentication
token for later use by the session module. Because this module performs
no actual authentication it returns PAM_CRED_INSUFFICIENT on success and
should always be accompanied by an actual authentication module in your
pam configuration.
</para>
<para>
By default the session module will start a new ssh-agent and run
ssh-add, loading any keys that exist in the default path for the
newly logged in user. If any keys prompt for a password, and a authenication
token was successfully stored, that token will be provided as the password.
</para>

</refsect1>

<refsect1 id="options">
<title>Options</title>
<variablelist>
<varlistentry id="debug">
<term><option>debug</option></term>
<listitem>
<para>This option will turn on debug logging to syslog.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>

<refsect1>
<title>Examples</title>
<informalexample>
<programlisting>
auth required pam_unix.so
auth optional pam_ssh_add.so
session optional pam_ssh_add.so
</programlisting>
</informalexample>

</refsect1>

<refsect1>
<title>BUGS</title>
<para>
Please send bug reports to either the distribution bug tracker or the
<ulink url="https://github.com/cockpit-project/cockpit/issues/new">upstream bug tracker</ulink>.
</para>
</refsect1>

</refentry>
2 changes: 1 addition & 1 deletion src/bridge/cockpitdbussetup.c
Expand Up @@ -38,7 +38,7 @@ const gchar *cockpit_bridge_path_shadow = "/etc/shadow";

const gchar *cockpit_bridge_path_newusers = "/usr/sbin/newusers";
const gchar *cockpit_bridge_path_chpasswd = "/usr/sbin/chpasswd";
const gchar *cockpit_bridge_path_usermod = "/usr/sbin/usermod";
const gchar *cockpit_bridge_path_usermod = PATH_USERMOD;

static GVariant *
setup_get_property (GDBusConnection *connection,
Expand Down
16 changes: 16 additions & 0 deletions src/pam-ssh-add/HACKING
@@ -0,0 +1,16 @@

Working on pam_ssh_add.so
-------------------------

For now this is part of Cockpit, but may eventually be split out. So only
basic glibc and linux dependencies are allowed:

* glibc
* pam

Tests
-----
Right now the bulk of the code is unit tested using mock versions of ssh-add
and ssh-agent. However the pam module itself is only tested with integration
tests. Hopefully the cwrap project will get the ability to mock pam. When that
happens we should add unit tests for the actual pam module as well.
51 changes: 51 additions & 0 deletions src/pam-ssh-add/Makefile.am
@@ -0,0 +1,51 @@
noinst_LIBRARIES += libpam_ssh_add.a

libpam_ssh_add_a_SOURCES = \
src/pam-ssh-add/pam-ssh-add.c \
src/pam-ssh-add/pam-ssh-add.h \
$(NULL)

libpam_ssh_add_a_CFLAGS = $(NULL)

libpam_ssh_add_a_LIBS = \
libpam_ssh_add.a \
$(PAM_LIBS)

pam_ssh_add_FILES = src/pam-ssh-add/pam-ssh-add.c src/pam-ssh-add/pam-ssh-add.c
pam_ssh_add.so: libpam_ssh_add.a $(pam_ssh_add_FILES)
$(AM_V_CCLD) $(CC) -fPIC -shared $(CFLAGS) $(libpam_ssh_add_a_CFLAGS) -I$(builddir) \
-o $@ $^ $(libpam_ssh_add_a_LIBS) $(LDFLAGS)

all-local:: pam_ssh_add.so

EXTRA_DIST += \
$(pam_ssh_add_FILES) \
$(NULL)

CLEANFILES += pam_ssh_add.so

install-exec-local::
$(MKDIR_P) $(DESTDIR)$(pamdir)
$(INSTALL) pam_ssh_add.so $(DESTDIR)$(pamdir)
uninstall-local::
$(RM) -f $(DESTDIR)$(pamdir)/pam_ssh_add.so

# -----------------------------------------------------------------------------
# Tests

PAM_SSH_ADD_CHECKS = \
test-ssh-add \
$(NULL)

test_ssh_add_SOURCES = src/pam-ssh-add/test-ssh-add.c
test_ssh_add_CFLAGS = $(libpam_ssh_add_a_CFLAGS)
test_ssh_add_LDADD = $(libpam_ssh_add_a_LIBS) libretest.a

noinst_PROGRAMS += $(PAM_SSH_ADD_CHECKS)
TESTS += $(PAM_SSH_ADD_CHECKS)

EXTRA_DIST += \
src/pam-ssh-add/mock-ssh-agent \
src/pam-ssh-add/mock-ssh-add \
src/pam-ssh-add/mock-environment \
$(NULL)
12 changes: 12 additions & 0 deletions src/pam-ssh-add/mock-environment
@@ -0,0 +1,12 @@
#!/bin/sh
for key in XDG_RUNTIME_DIR HOME PATH LC_ALL OTHER SSH_AUTH_SOCK
do
value=$(/usr/bin/printenv "$key")
result=$?
if [ $result -eq 0 ]; then
echo "$key=$value" 1>&2
else
echo "NO $key" 1>&2
fi
done
exit 1;
40 changes: 40 additions & 0 deletions src/pam-ssh-add/mock-ssh-add
@@ -0,0 +1,40 @@
#!/bin/sh
PASSWORD="foobar"

password_good=0
password_bad=0
password_blanks=0

case "$1" in
"no-socket")
exit 2;;
*)
for dummy_key in 0 1 2
do
echo "Enter passphrase for $dummy_key" >&2
while true
do
read answer
case $answer in
"$PASSWORD")
password_good=$(expr "$password_good" + 1)
break;;
"")
password_blanks=$(expr "$password_blanks" + 1)
break;;
*)
password_bad=$(expr "$password_bad" + 1)
echo "Bad passphrase, try again for $dummy_key" >&2
continue;;
esac
done
done

echo "Correct password $password_good, bad password $password_bad, password_blanks $password_blanks" >&2

if [ $password_good -eq 3 ]; then
exit 0;
else
exit 1;
fi
esac
17 changes: 17 additions & 0 deletions src/pam-ssh-add/mock-ssh-agent
@@ -0,0 +1,17 @@
#!/bin/sh
case "$1" in
"good-vars")
echo "EXTRA=var; export EXTRA"
echo "SSH_AUTH_SOCKET=socket; export SSH_AUTH_SOCKET"
echo "SSH_AGENT_PID=100; export SSH_AGENT_PID"
echo "EXTRA2=100; export EXTRA2"
exit 0;;
"bad-vars")
echo "Just a bunch of nothing"
echo "TEST=var; export TEST"
exit 0;;
*)
echo "Normal output"
echo "Bad things" 1>&2
exit 1;;
esac