Skip to content

Commit

Permalink
Add Kerberos authentication for SSH
Browse files Browse the repository at this point in the history
  • Loading branch information
benh-palantir authored and bluekeyes committed Aug 8, 2015
1 parent 59ca429 commit fb462f4
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* Copyright 2015 Palantir Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.palantir.giraffe.ssh;

import java.io.IOException;

/**
* Authenticates SSH connections via GSS-API in a Kerberos environment.
*
* @author benh
*/
public class KerberosSshCredential extends SshCredential {

public static KerberosSshCredential of(String username, String realm, String kdcHostname) {
return new KerberosSshCredential(username, realm, kdcHostname);
}

private final String realm;
private final String kdcHostname;

private KerberosSshCredential(String username, String realm, String kdcHostname) {
super(username);
this.realm = realm;
this.kdcHostname = kdcHostname;
}

@Override
public void authenticate(SshAuthenticator authenticator) throws IOException {
authenticator.authByKerberos(this, realm, kdcHostname);
}

public String getRealm() {
return realm;
}

public String getKdcHostname() {
return kdcHostname;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,7 @@ public interface SshAuthenticator extends Authenticator {

void authByPublicKey(SshCredential credential, byte[] privateKey) throws IOException;

void authByKerberos(SshCredential credential, String realm, String kdcHostname)
throws IOException;

}
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ public static SshHostAccessor forKey(String hostname, String username, String pr
return forCredential(host, PublicKeySshCredential.of(username, privateKey));
}

public static SshHostAccessor forKerberos(String hostname, String username, String realm,
String kdcHostname) {
Host host = Host.fromHostname(hostname);
return forCredential(host, KerberosSshCredential.of(username, realm, kdcHostname));
}

/**
* Returns a new {@code SshHostAccessor} that authenticates using the given
* {@link SshCredential}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,16 @@
package com.palantir.giraffe.ssh.internal;

import java.io.IOException;
import java.nio.charset.Charset;

import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

import org.ietf.jgss.GSSException;
import org.ietf.jgss.Oid;

import com.palantir.giraffe.file.MoreFiles;
import com.palantir.giraffe.file.TempPath;
import com.palantir.giraffe.ssh.SshAuthenticator;
import com.palantir.giraffe.ssh.SshCredential;
import com.palantir.giraffe.ssh.SshSystemRequest;
Expand All @@ -25,6 +34,7 @@
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
import net.schmizz.sshj.userauth.UserAuthException;
import net.schmizz.sshj.userauth.keyprovider.KeyProvider;

final class SshConnectionFactory {
Expand Down Expand Up @@ -69,5 +79,61 @@ public void authByPublicKey(SshCredential credential, byte[] privateKey)
KeyProvider keyProvider = client.loadKeys(new String(privateKey), null, null);
client.authPublickey(credential.getUsername(), keyProvider);
}

private static final String REALM_PROPERTY = "java.security.krb5.realm";
private static final String KDC_PROPERTY = "java.security.krb5.kdc";
private static final String CONFIG_PROPERTY = "java.security.auth.login.config";

@Override
public void authByKerberos(SshCredential credential, String realm, String kdcHostname)
throws IOException {

final String originalRealm = System.getProperty(REALM_PROPERTY);
final String originalKdc = System.getProperty(KDC_PROPERTY);
final String originalConfig = System.getProperty(CONFIG_PROPERTY);

try (TempPath jaasConfFile = TempPath.createFile()) {
MoreFiles.write(jaasConfFile.path(),
"Krb5LoginContext { com.sun.security.auth.module.Krb5LoginModule " +
"required refreshKrb5Config=true useTicketCache=true debug=true ; };",
Charset.defaultCharset()
);

// set properties necessary for Krb5LgoinContext
System.setProperty(REALM_PROPERTY, realm);
System.setProperty(KDC_PROPERTY, kdcHostname);
System.setProperty(CONFIG_PROPERTY,
jaasConfFile.path().toAbsolutePath().toString());

LoginContext lc = null;
try {
lc = new LoginContext("Krb5LoginContext");
lc.login();
} catch (LoginException e) {
throw new UserAuthException(e);
}

Oid krb5Oid;
try {
krb5Oid = new Oid("1.2.840.113554.1.2.2");
} catch (GSSException e) {
throw new UserAuthException("Failed to create Kerberos OID", e);
}

client.authGssApiWithMic(credential.getUsername(), lc, krb5Oid);
} finally {
restoreProperty(REALM_PROPERTY, originalRealm);
restoreProperty(KDC_PROPERTY, originalKdc);
restoreProperty(CONFIG_PROPERTY, originalConfig);
}
}

private void restoreProperty(String key, String originalValue) {
if (originalValue != null) {
System.setProperty(key, originalValue);
} else {
System.clearProperty(key);
}
}
}
}

0 comments on commit fb462f4

Please sign in to comment.