-
Notifications
You must be signed in to change notification settings - Fork 78
Description
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:
- Document this limitation, i.e. that principals are checked even if you don't configure
authorized_principals
orauthorized_principals_file
- Add a "debug" flag to pam-ussh so that information like that shown above can be printed, to help diagnose such problems