使用JAAS文件登陆kerberos(zookeeper)

Kerberos 5 Configuration

Since the SPNEGO mechanism will call JGSS, which in turns calls the Kerberos V5 login module to do real works. Kerberos 5 configurations are needed. which includes:

  • Some way to provide Kerberos configurations. This can be achieved with the Java system property java.security.krb5.conf. For example:
            java -Djava.security.krb5.conf=krb5.conf \
                 -Djavax.security.auth.useSubjectCredsOnly=false \
                 ClassName
  • A JAAS config file denoting what login module to use. HTTP SPNEGO codes will look for the standard entry named com.sun.security.jgss.krb5.initiate.

For example, you can provide a file spnegoLogin.conf:

          com.sun.security.jgss.krb5.initiate {
              com.sun.security.auth.module.Krb5LoginModule
                  required useTicketCache=true;
          };

and run java with:

            java -Djava.security.krb5.conf=krb5.conf \
                 -Djava.security.auth.login.config=spnegoLogin.conf \
                 -Djavax.security.auth.useSubjectCredsOnly=false \
                 ClassName

JAAS:https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_73/rzaha/rzahajaas10.htm

ZooKeeperSaslClient

在使用带Kerberos的zookeeper时,连接前需要进行认证,但是需要通过jaas机制去登陆Kerberos,这时候需要给JVM设置jaas.conf配置文件,zookeeper源码种等登陆模块如下:

public ZooKeeperSaslClient(String serverPrincipal, ZKClientConfig clientConfig) throws LoginException {
        this.saslState = ZooKeeperSaslClient.SaslState.INITIAL;
        this.gotLastPacket = false;
        String clientSection = clientConfig.getProperty("zookeeper.sasl.clientconfig", "Client");
        this.clientConfig = clientConfig;
        AppConfigurationEntry[] entries = null;
        Object runtimeException = null;

        try {
            entries = Configuration.getConfiguration().getAppConfigurationEntry(clientSection);
        } catch (SecurityException var8) {
            runtimeException = var8;
        } catch (IllegalArgumentException var9) {
            runtimeException = var9;
        }

        if (entries != null) {
            this.configStatus = "Will attempt to SASL-authenticate using Login Context section '" + clientSection + "'";
            this.saslClient = this.createSaslClient(serverPrincipal, clientSection);
        } else {
            this.saslState = ZooKeeperSaslClient.SaslState.FAILED;
            String explicitClientSection = clientConfig.getProperty("zookeeper.sasl.clientconfig");
            if (explicitClientSection != null) {
                if (runtimeException != null) {
                    throw new LoginException("Zookeeper client cannot authenticate using the " + explicitClientSection + " section of the supplied JAAS configuration: '" + clientConfig.getJaasConfKey() + "' because of a RuntimeException: " + runtimeException);
                }

                throw new LoginException("Client cannot SASL-authenticate because the specified JAAS configuration section '" + explicitClientSection + "' could not be found.");
            }

            String msg = "Will not attempt to authenticate using SASL ";
            if (runtimeException != null) {
                msg = msg + "(" + runtimeException + ")";
            } else {
                msg = msg + "(unknown error)";
            }

            this.configStatus = msg;
            this.isSASLConfigured = false;
            if (clientConfig.getJaasConfKey() != null) {
                if (runtimeException != null) {
                    throw new LoginException("Zookeeper client cannot authenticate using the '" + clientConfig.getProperty("zookeeper.sasl.clientconfig", "Client") + "' section of the supplied JAAS configuration: '" + clientConfig.getJaasConfKey() + "' because of a RuntimeException: " + runtimeException);
                }

                throw new LoginException("No JAAS configuration section named '" + clientConfig.getProperty("zookeeper.sasl.clientconfig", "Client") + "' was found in specified JAAS configuration file: '" + clientConfig.getJaasConfKey() + "'.");
            }
        }

    }

可以看处,默认读取的时Client模块下的配置,也会从zookeeper.sasl.clientconfig种读取。其登陆代码如下:

private SaslClient createSaslClient(String servicePrincipal, String loginContext) throws LoginException {
        try {
            if (!this.initializedLogin) {
                synchronized(this) {
                    if (this.login == null) {
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("JAAS loginContext is: " + loginContext);
                        }

                        this.login = new Login(loginContext, new ZooKeeperSaslClient.ClientCallbackHandler((String)null), this.clientConfig);
                        this.login.startThreadIfNeeded();
                        this.initializedLogin = true;
                    }
                }
            }

            Subject subject = this.login.getSubject();
            SaslClient saslClient;
            if (subject.getPrincipals().isEmpty()) {
                LOG.info("Client will use DIGEST-MD5 as SASL mechanism.");
                String[] mechs = new String[]{"DIGEST-MD5"};
                String username = (String)((String)subject.getPublicCredentials().toArray()[0]);
                String password = (String)((String)subject.getPrivateCredentials().toArray()[0]);
                saslClient = Sasl.createSaslClient(mechs, username, "zookeeper", "zk-sasl-md5", (Map)null, new ZooKeeperSaslClient.ClientCallbackHandler(password));
                return saslClient;
            } else {
                boolean usingNativeJgss = this.clientConfig.getBoolean("sun.security.jgss.native");
                if (usingNativeJgss) {
                    try {
                        GSSManager manager = GSSManager.getInstance();
                        Oid krb5Mechanism = new Oid("1.2.840.113554.1.2.2");
                        GSSCredential cred = manager.createCredential((GSSName)null, 0, krb5Mechanism, 1);
                        subject.getPrivateCredentials().add(cred);
                    } catch (GSSException var16) {
                        LOG.warn("Cannot add private credential to subject; authentication at the server may fail", var16);
                    }
                }

                Object[] principals = subject.getPrincipals().toArray();
                Principal clientPrincipal = (Principal)principals[0];
                KerberosName clientKerberosName = new KerberosName(clientPrincipal.getName());
                String serverRealm = this.clientConfig.getProperty("zookeeper.server.realm", clientKerberosName.getRealm());
                KerberosName serviceKerberosName = new KerberosName(servicePrincipal + "@" + serverRealm);
                final String serviceName = serviceKerberosName.getServiceName();
                final String serviceHostname = serviceKerberosName.getHostName();
                final String clientPrincipalName = clientKerberosName.toString();

                try {
                    saslClient = (SaslClient)Subject.doAs(subject, new PrivilegedExceptionAction<SaslClient>() {
                        public SaslClient run() throws SaslException {
                            ZooKeeperSaslClient.LOG.info("Client will use GSSAPI as SASL mechanism.");
                            String[] mechs = new String[]{"GSSAPI"};
                            ZooKeeperSaslClient.LOG.debug("creating sasl client: client=" + clientPrincipalName + ";service=" + serviceName + ";serviceHostname=" + serviceHostname);
                            SaslClient saslClient = Sasl.createSaslClient(mechs, clientPrincipalName, serviceName, serviceHostname, (Map)null, new ZooKeeperSaslClient.ClientCallbackHandler((String)null));
                            return saslClient;
                        }
                    });
                    return saslClient;
                } catch (Exception var15) {
                    LOG.error("Exception while trying to create SASL client", var15);
                    var15.printStackTrace();
                    return null;
                }
            }
        } catch (LoginException var18) {
            throw var18;
        } catch (Exception var19) {
            LOG.error("Exception while trying to create SASL client: " + var19);
            return null;
        }
    }

This is jaas.conf:

Client {
      com.sun.security.auth.module.Krb5LoginModule required
      useKeyTab=true
      doNotPrompt=true
      useTicketCache=true
      principal="test"
      keyTab="/home/keytab/user.keytab";
   };

Then set Java Options like :

--Djava.security.auth.login.config=/data/disk1/conf/jaas.conf -Djavax.security.auth.useSubjectCredsOnly=false 

其他情况下,会使用com.sun.security.jgss.krb5.initiate来登陆,所以jaas.conf的配置如下:

com.sun.security.jgss.krb5.initiate {
  com.sun.security.auth.module.Krb5LoginModule required
  useKeyTab=true
  keyTab="/data/disk1/conf/test.keytab"
  principal="test"
  doNotPrompt=true;
};
posted @ 2020-12-26 11:03  风雨咒之无上密籍  阅读(3330)  评论(0编辑  收藏  举报