GithubHelp home page GithubHelp logo

pam-ussh's Introduction

Uber's SSH certificate pam module.

This is a pam module that will authenticate a user based on them having an ssh certificate in their ssh-agent signed by a specified ssh CA.

This is primarily intended as an authentication module for sudo. Using it for something else may be unsafe (we haven't tested it anyway). We'd be happy to learn of other potential uses though.

An example usage would be you ssh to a remote machine and sshd authenticates you (probably using your ssh cert, because if you're using it for this, you're probably using it for sshd as well). At that point when you want to run a command that requires authentication (eg. sudo), you can use pam-ussh for authentication.

Works on linux and osx. BSD doesn't work because go doesn't (yet) support buildmode=c-shared on bsd.

Building:

  1. clone the repo and run 'make'
  $ git clone github.com/uber/pam-ussh

  ...

  $ make
  mkdir -p /home/pmoody/tmp/pam-ussh/.go/src
  GOPATH=/home/pmoody/tmp/pam-ussh/.go go get golang.org/x/crypto/ssh
  GOPATH=/home/pmoody/tmp/pam-ussh/.go go get golang.org/x/crypto/ssh/agent
  GOPATH=/home/pmoody/tmp/pam-ussh/.go go get github.com/stretchr/testify/require
  GOPATH=/home/pmoody/tmp/pam-ussh/.go go test -cover
  PASS
  coverage: 71.8% of statements
  ok  	_/home/pmoody/tmp/pam-ussh	0.205s
  GOPATH=/home/pmoody/tmp/pam-ussh/.go go build -buildmode=c-shared -o pam_ussh.so

  $

Usage:

  1. put this pam module where ever pam modules live on your system, eg. /lib/security

  2. add it as an authentication method, eg.

  $ grep auth /etc/pam.d/sudo
  auth [success=1 default=ignore] /lib/security/pam_ussh.so
  auth requisite                  pam_deny.so
  auth required                   pam_permit.so
  1. make sure your SSH_AUTH_SOCK is available where you want to use this (eg. ssh -A user@host)

Runtime configuration options:

  • ca_file - string, the path to your TrustedUserCAKeys file, default /etc/ssh/trusted_user_ca. This is the pubkey that signs your user certificates.

  • authorized_principals - string, comma separated list of authorized principals, default "". If set, the user needs to have a principal in this list in order to use this module. If this and authorized_principals_file are both set, only the last option listed is checked.

  • authorized_principals_file - string, path to an authorized_principals file, default "". If set, users need to have a principal listed in this file in order to use this module. If this and authorized_principals are both set, only the last option listed is checked.

  • group - string, default, "" If set, the user needs to be a member of this group in order to use this module.

Example configuration:

the following looks for a certificate on $SSH_AUTH_SOCK that have been signed by user_ca. Additionally, the user needs to have a principal on the certificate that's listed in /etc/ssh/root_authorized_principals

auth [success=1 default=ignore] /lib/security/pam_ussh.so ca_file=/etc/ssh/user_ca authorized_principals_file=/etc/ssh/root_authorized_principals

FAQ:

  • How do I report a security issue?

  • does this work with non-certificate ssh-keys?

    • No, not at the moment.
    • There's no reason it can't though, we just didn't need it to do that so I never added the functionality
  • why aren't you using $DEP_SYSTEM?

    • We didn't need to so we didn't bother
  • can you make it do $X?

    • Submit a feature request, or better yet a pull request

Information on ssh certificates:

pam-ussh's People

Contributors

brianredbeard avatar flxpeters avatar jessespears avatar pmoody- avatar scottshuffler avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pam-ussh's Issues

Documentation should clearly state that the module is only meant for use with sudo, and any other use may be unsafe

Apparently, pam-ussh itself and its recently added euid switching were designed with primarily, or even exclusively, sudo in mind. With sudo, the user controlling the SSH_AUTH_SOCK env var and the user being authenticated are the same, so it's reasonable to switch to that user's euid for accessing the socket (of the user's choice).

Now imagine having the module stacked for su instead. The target user may be e.g. root (this is different from sudo's case), but the env var is under the invoking user's control (just like with sudo). Thus, the invoking user may make pam-ussh access a pathname that is not normally accessible by the user directly. The "pam-ussh: check cert against the pam username" commit hopefully defeats the obvious attack on authentication (although this depends on external/setup detail), but it doesn't deal with the issue of pathname probing (with pam-ussh becoming an oracle, potentially providing 1-bit responses via side-channels), nor with potential side-effects of being able to connect to (even though not talk over) arbitrary Unix domain sockets (not necessarily those of any ssh-agent, bypassing Unix permissions on parent directories and the sockets themselves).

Maybe the README should be re-worded some further, stating that the module is only meant for use with sudo, and any other use may be unsafe? I don't find use e.g. along with su reasonable anyway.

Project still alive?

Hey there and first a big thanks for providing this project. We try around with certificate based authentication at the moment and this seams to be the perfect fit to enable sudo via certs without having passwordless sudo.
Before using it in production I have some concerns on the health of the project as there is no release and some open PRs.

So just a simple question: Is this project still alive and maintained?

Thanks :-)

Unexpected checking of principals

I am testing pam-ussh. I am logging in to the target machine as user "web", and my certificate has principals ["web@anywhere" "database@anywhere" "root@anywhere"]. This works for ssh login because my sshd_config has:

TrustedUserCAKeys /etc/ssh/trusted_user_ca
AuthorizedPrincipalsCommand /etc/ssh/authprinc.sh %u
AuthorizedPrincipalsCommandUser nobody

and the script /etc/ssh/authprinc.sh is

#!/bin/sh
echo "$1@webserver"
echo "$1@anywhere"

This gives me a way of authorizing access to machines in groups, similar to zones described here.

So far so good. However, pam-ussh refuses to permit sudo. I have configured it with default options:

auth [success=1 default=ignore] pam_ussh.so
auth requisite                  pam_deny.so
auth required                   pam_permit.so

Note in particular that I have not specified authorized_principals or authorized_principals_file, and the documentation says they both default to ""

Initially, pam-ussh logs only "no valid certs found". So I made a small patch to log some more info:

--- a/pam_ussh.go
+++ b/pam_ussh.go
@@ -133,6 +133,11 @@ func authenticate(w io.Writer, uid int, username, ca string, principals map[stri
                in = rest
        }

+       if len(caPubkeys) == 0 {
+               pamLog("No certificate authority keys available.")
+               return AuthError
+       }
+
        c := &ssh.CertChecker{
                IsUserAuthority: func(auth ssh.PublicKey) bool {
                        for _, k := range caPubkeys {
@@ -147,15 +152,18 @@ func authenticate(w io.Writer, uid int, username, ca string, principals map[stri
        for idx := range keys {
                pubKey, err := ssh.ParsePublicKey(keys[idx].Marshal())
                if err != nil {
+                       pamLog("pubkey %d could not be parsed: %v\n", idx, err)
                        continue
                }

                cert, ok := pubKey.(*ssh.Certificate)
                if !ok {
+                       pamLog("pubkey %d is not a certificate\n", idx)
                        continue
                }

                if err := c.CheckCert(username, cert); err != nil {
+                       pamLog("pubkey %d fails CheckCert: %v\n", idx, err)
                        continue
                }

Now what I get is this:

May  2 13:31:55 sagan pam-ussh[2355]: pubkey 0 is not a certificate
May  2 13:31:55 sagan pam-ussh[2355]: pubkey 1 is not a certificate
May  2 13:31:55 sagan pam-ussh[2355]: pubkey 2 is not a certificate
May  2 13:31:55 sagan pam-ussh[2355]: pubkey 3 fails CheckCert: ssh: principal "web" not in the set of valid principals for given certificate: ["web@anywhere" "database@anywhere" "root@anywhere"]
May  2 13:31:55 sagan pam-ussh[2355]: no valid certs found

(My ssh agent has three private keys and one cert)

It appears that ssh.CertChecker.CheckCert is rejecting the certificate on the basis that it requires a particular principal, equal to the logged-in username. Digging further, it's the "username" argument being passed into CheckCert, which is checked against the list of principals in the certificate, if this list is non-empty. This is taking place before pam-ussh does its own principal checks.

As a result, there seems to be a hard-coded policy that if the certificate contains principals, then the bare ssh login name must exist as a principal in that certificate - which unfortunately doesn't work with my scheme for principals.

As far as I can see, this restriction is in the go ssh library, and there's not much that pam-ussh can do about it short of re-implementing the CheckCert function. I could propose a change to the go library here so that if the principal you pass in is empty string, this check is skipped:

	if len(principal) > 0 and len(cert.ValidPrincipals) > 0 {

A better option might be to have a setting for pam-ussh to pass a fixed string, e.g. "sudo" to CheckCert. This would then accept any certificate which includes that principal.

However, assuming that neither of the above happens, I still think it would be worthwhile to:

  1. Document this limitation, i.e. that principals are checked even if you don't configure authorized_principals or authorized_principals_file
  2. Add a "debug" flag to pam-ussh so that information like that shown above can be printed, to help diagnose such problems

[TESTING] Create tests using cwrap

Hi,

at https://cwrap.org/ we created a pam_wrapper to make it possible to test PAM modules.

You using socket_wrapper, nss_wrapper and pam_wrapper you can also start an openssh server in the test environment and run your PAM tests against this server.

The libssh project starts an openssh server to run libssh torture tests against it.

Clarify interaction with sudoers file

This is request to fill what seems to be a gap in the docoumentation around how pam-ussh hooks into the sudoers file (or its includes).

I have just started investigating ssh certs and setup a simple test environment to learn how this works.

I was pleased it only took a couple of hours to have my test server authenticating with certs working so known_hosts messages no longer appear and authorized_keys are not needed.

The next step was to integrate pam-ussh into the solution but can't get it to work.

It appears pam-ussh is accepting the certificate I'm presenting it with.

My confusion is how this hooks into my sudo rules.

I assume when the CA signs the public key, it gives the user key a number of principals: the user's name and the roles that user has, let say one is "allowed_root".
That "allowed_root" would be listed in /etc/ssh/root_authorized_principals.
But what should the sudoers file contain? Something like:
allowed_root ALL =(ALL) ALL
I'm having trouble seeing how the pam-ussh hooks into the sudoers rules.
Could the documentation fill out this final missing piece of the puzzle.

Restrict sudo command for SSH Certificate ForcedCommand

One of the options for an SSH certificate is force-command (https://man.openbsd.org/ssh-keygen.1#force-command).

Would it be possible to limit the command that a user is able to execute with sudo if the force-command is set in the SSH certificate?

This would be super useful to be able to issue a certificate for a user with sudo permissions for one particular command without knowing the command in advance (otherwise you could user the sudoers file).

Is this possible in the PAM? Do you have access to the command that is to be run? I'm happy to contribute this feature with a little guidance in the right direction :)

Exit status when user is not member of group

Perhaps I am being extremely dense, but I am scratching my head when I look at the code here:

	if len(group) == 0 || isMemberOf(group) {
		return authenticate(w, uid, username, userCA, authorizedPrincipals)
	}

	return AuthSuccess

It seems to me that if the "group" option is set, but the user is not a member of that group, the module will unconditionally return AuthSuccess - which implies that sudo access is granted.

I was expecting the final statement to say return AuthError. If that's true, it could be more clearly expressed as:

	if len(group) > 0 && isMemberOf(group) != true {
		return AuthError
	}

	return authenticate(w, uid, username, userCA, authorizedPrincipals)

However if the current behaviour is as intended, then I think the documentation of the "group" option could be made much clearer. "Users who are in this group must authenticate with ssh-agent; all other users are granted access without authenticating at all"

Make fails on centos 6

Centos 6 using latest go in repository:

# make test
mkdir -p /lib/security/pam-ussh/.go/src
GOPATH=/lib/security/pam-ussh/.go go get golang.org/x/crypto/ssh
GOPATH=/lib/security/pam-ussh/.go go get golang.org/x/crypto/ssh/agent
GOPATH=/lib/security/pam-ussh/.go go get github.com/stretchr/testify/require
GOPATH=/lib/security/pam-ussh/.go go test -cover
# _/lib/security/pam-ussh
/tmp/go-build020625526/_/lib/security/pam-ussh/_test/_obj_test/pam_ussh.go:128: unknown ssh.CertChecker field 'IsAuthority' in struct literal
FAIL	_/lib/security/pam-ussh [build failed]
make: *** [test] Error 2

Update README to reflect new Go build process

I don't work with Go but would like to build this.

The description of how to build it seem to be out of date and apply to older versions of Go.

Could someone with more knowledge than myself update the README to show how to build using the current version of Go.

How security vulnerabilities for this package is tracked in NIST/NVD?

Hi,

I was trying to find cpe_uri associated with this package in NIST/NVD so that COS (https://cloud.google.com/container-optimized-os/docs) can track security vulnerabilities associated with it. However, based on the search there was no cpe_uri associated. From the past security vulnerabilities, I could find https://hackerone.com/reports/204802 security vulnerability but there was no CVE Number assigned for that in that bug.

Could you help in providing information as what cpe_uri can be used by downstream users to track security vulnerability in this package from NIST/NVD?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.