Skip to content

Commit

Permalink
admin: add kerberos authentication support to admin ssh server
Browse files Browse the repository at this point in the history
Motivation:

Existing admin ssh server supports only password and public key
authentication mechanisms. Both are considered a violation of computer
security policy at Fermilab (and possibly elsewhere), whereas kerberos
authentication is allowed.

Modification:

Implemented kerberos authentication mechanism in admin ssh server. Added
ability to enable desired authentication mechanism(s).

Target: master
Request: 4.2.0
RB: https://rb.dcache.org/r/10979/
Acked-by: Paul
Require-notes: yes
Require-book: yes

RELEASE NOTES:

dCache admin ssh server now supports kerberos authentication mechanism and
abilty to enable desired set of authentication mechanism(s). The following
variables are added to configure admin ssh server authentication:

Supported ssh authentication mechanisms : kerberos, password, publickey

(any-of?kerberos|password|publickey)admin.ssh.authn.enabled = password,publickey

Location of keytab file which is needed when kerberos authentication
mechanism is enabled

admin.ssh.authn.kerberos.keytab-file = /etc/krb5.keytab
  • Loading branch information
DmitryLitvintsev committed Jun 8, 2018
1 parent 700e97a commit 64ea49e
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 9 deletions.
@@ -1,6 +1,8 @@
package org.dcache.services.ssh2;

import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import org.apache.sshd.common.Factory;
import org.apache.sshd.common.FactoryManager;
import org.apache.sshd.common.NamedFactory;
Expand All @@ -12,10 +14,17 @@
import org.apache.sshd.common.util.security.SecurityUtils;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.auth.gss.GSSAuthenticator;
import org.apache.sshd.server.auth.password.PasswordAuthenticator;
import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator;
import org.apache.sshd.server.session.ServerSession;

import org.dcache.auth.LoginNamePrincipal;
import org.dcache.auth.LoginReply;
import org.dcache.auth.LoginStrategy;
import org.dcache.auth.Origin;
import org.dcache.auth.PasswordCredential;
import org.dcache.auth.Subjects;
import org.dcache.util.Glob;
import org.dcache.util.Subnet;

Expand All @@ -24,6 +33,7 @@
import org.springframework.beans.factory.annotation.Required;

import javax.security.auth.Subject;
import javax.security.auth.kerberos.KerberosPrincipal;

import java.io.File;
import java.io.FileNotFoundException;
Expand All @@ -36,12 +46,14 @@
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import diskCacheV111.util.AuthorizedKeyParser;
Expand All @@ -51,11 +63,6 @@
import dmg.cells.nucleus.CellCommandListener;
import dmg.cells.nucleus.CellLifeCycleAware;

import org.dcache.auth.LoginReply;
import org.dcache.auth.LoginStrategy;
import org.dcache.auth.Origin;
import org.dcache.auth.PasswordCredential;
import org.dcache.auth.Subjects;
import org.dcache.util.Files;
import org.dcache.util.NetLoggerBuilder;

Expand Down Expand Up @@ -89,6 +96,10 @@ public class Ssh2Admin implements CellCommandListener, CellLifeCycleAware
private TimeUnit _idleTimeoutUnit;
private long _idleTimeout;
private enum Outcome { ALLOW, DENY, DEFER }
// switches for different Authenticators
private enum AuthenticationMechanism {KERBEROS, PASSWORD, PUBLICKEY}
private EnumSet<AuthenticationMechanism> enabledAuthenticationMechanisms;
private String keyTabFile;

public Ssh2Admin() {
_server = SshServer.setUpDefaultServer();
Expand Down Expand Up @@ -163,9 +174,31 @@ public void setIdleTimeoutUnit(TimeUnit unit) {
_idleTimeoutUnit = unit;
}

public void configureAuthentication() {
_server.setPasswordAuthenticator(new AdminPasswordAuthenticator());
_server.setPublickeyAuthenticator(new AdminPublickeyAuthenticator());
@Required
public void setEnabledAuthenticationMechanisms(String mechanisms) {
enabledAuthenticationMechanisms = EnumSet.copyOf(
Arrays.stream(
Iterables.toArray(
Splitter.on(',')
.trimResults()
.split(mechanisms.toUpperCase()),
String.class))
.map(AuthenticationMechanism::valueOf)
.collect(Collectors.toList()));
}

@Required
public void setKeyTabFile(String name) {
keyTabFile = name;
}

private void configureAuthentication() {
_server.setGSSAuthenticator(enabledAuthenticationMechanisms.contains(AuthenticationMechanism.KERBEROS) ?
new AdminGssAuthenticator() : null);
_server.setPasswordAuthenticator(enabledAuthenticationMechanisms.contains(AuthenticationMechanism.PASSWORD) ?
new AdminPasswordAuthenticator() : null);
_server.setPublickeyAuthenticator(enabledAuthenticationMechanisms.contains(AuthenticationMechanism.PUBLICKEY) ?
new AdminPublickeyAuthenticator() : null);
}

@Override
Expand Down Expand Up @@ -388,6 +421,8 @@ private Outcome patternMatchesHost(String pattern, ServerSession session) {
}
}



private class AdminConnectionLogger implements SessionListener {

@Override
Expand All @@ -409,4 +444,50 @@ private void logEvent(String name, Session session) {
.toLogger(_accessLog);
}
}

private class AdminGssAuthenticator extends GSSAuthenticator {

AdminGssAuthenticator() {
setKeytabFile(keyTabFile);
}

@Override
public boolean validateInitialUser(ServerSession session, String user) {
session.setUsername(user);
return true;
}

@Override
public boolean validateIdentity(ServerSession session, String identity) {
boolean successful = false;
String reason = null;
Subject subject = new Subject();
subject.getPrincipals().add(new KerberosPrincipal(identity));
String userName = session.getUsername();
if (userName != null) {
subject.getPrincipals().add(new LoginNamePrincipal(userName));
}
addOrigin(session, subject);
try {
LoginReply reply = _loginStrategy.login(subject);
Subject authenticatedSubject = reply.getSubject();
if (!Subjects.hasGid(authenticatedSubject, _adminGroupId)) {
throw new PermissionDeniedCacheException("not member of admin gid");
}
successful = true;
} catch (PermissionDeniedCacheException e) {
_log.error("Login for {} denied: {}",
Strings.nullToEmpty(userName),
e.getMessage());
reason = e.getMessage();
} catch (CacheException e) {
reason = e.toString();
_log.error("Login for {} failed: {}",
Strings.nullToEmpty(userName),
e.toString());
}
logLoginTry(userName, session, "GSS", successful, reason);
return successful;
}
}
}
Expand Up @@ -117,5 +117,7 @@
</property>
<property name="idleTimeout" value="#{T(org.dcache.util.Strings).parseLong('${admin.ssh.idle-timeout}')}" />
<property name="idleTimeoutUnit" value="${admin.ssh.idle-timeout.unit}" />
<property name="enabledAuthenticationMechanisms" value="${admin.ssh.authn.enabled}"/>
<property name="keyTabFile" value="${admin.ssh.authn.kerberos.keytab-file}" />
</bean>
</beans>
2 changes: 1 addition & 1 deletion pom.xml
Expand Up @@ -536,7 +536,7 @@
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-core</artifactId>
<version>1.6.0</version>
<version>1.6.0-dcache</version>
</dependency>
<dependency>
<!-- Newer versions have two problems. A performance regression causes it to block on non-responding network
Expand Down
9 changes: 9 additions & 0 deletions skel/share/defaults/admin.properties
Expand Up @@ -65,6 +65,15 @@ admin.paths.authorized-keys = ${dcache.paths.admin}/authorized_keys2
(obsolete)admin.paths.dsa-host-key.public = No longer used
admin.paths.host-keys = ${dcache.paths.admin}/ssh_host_rsa_key

# ---- Authentication mechanisms
#
# Supported ssh authentication mechanisms : kerberos, password, publickey
#
(any-of?kerberos|password|publickey)admin.ssh.authn.enabled = password,publickey

# location of keytab file needed if kerberos authentication mechanism is used
admin.ssh.authn.kerberos.keytab-file = /etc/krb5.keytab

# Cell addresses and timeouts of other services
admin.service.gplazma=${dcache.service.gplazma}
admin.service.gplazma.timeout=30000
Expand Down
2 changes: 2 additions & 0 deletions skel/share/services/admin.batch
Expand Up @@ -20,6 +20,8 @@ check -strong admin.service.pnfsmanager.timeout.unit
check -strong admin.service.acm
check -strong admin.service.acm.timeout
check -strong admin.service.acm.timeout.unit
check -string admin.ssh.authn.enabled
check -strong admin.ssh.authn.kerberos.keytab-file
check admin.loginbroker.request-topic
check -strong admin.authz.gid
check admin.paths.authorized-keys
Expand Down

0 comments on commit 64ea49e

Please sign in to comment.