Kafka安全模式之身份认证

这篇具有很好参考价值的文章主要介绍了Kafka安全模式之身份认证。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、简介

Kafka作为一个分布式的发布-订阅消息系统,在日常项目中被频繁使用,通常情况下无论是生产者还是消费者只要订阅Topic后,即可进行消息的发送和接收。而kafka在0.9.0.0版本后添加了身份认证和权限控制两种安全服务,本文主要介绍在实际项目使用过程中遇到第三方kafka需身份认证时如何解决,以及对可能会碰到的问题进行总结。

二、原理介绍

Kafka身份认证主要分为以下几种:

(1)客户端与broker之间的连接认证

(2)broker与broker之间的连接认证

(3)broker与zookeeper之间的连接认证

日常项目中,无论是生产者还是消费者,我们都是作为客户端与kafka进行交互,因此使用的最多的是客户端与broker之间的连接认证。图1是客户端与服务端broker之间的认证过程图,客户端提交认证数据,服务端会根据认证数据对当前客户端进行身份校验,校验成功后的客户端即可成功登录kafka,进行后续操作。

kafka 认证,kafka,安全,分布式

图1 客户端与broker之间认证过程图

目前Kafka提供了SASL、SSL、Delegation Tokem三种安全认证机制,而SASL认证又分为了以下几种方式:

(1)基于Kerberos的GSSAPI

SASL-GSSAPI提供了一种非常安全的身份验证方法,但使用前提是企业中有Kerberos基础,一般使用随机密码的keytab认证方式,密码是加密的,在0.9版本中引入,目前是企业中使用最多的认证方式。

(2)SASL-PLAIN

SASL-PLAIN方式是一个经典的用户名/密码的认证方式,其中用户名和密码是以明文形式保存在服务端的JAAS配置文件中的,当客户端使用PLAIN模式进行认证时,密码是明文传输的,因此安全性较低,但好处是足够简单,方便我们对其进行二次开发,在0.10版本引入。

(3)SASL-SCRAM

SASL-SCRAM是针对SASL-PLAIN方式的不足而提供的另一种认证方式,它将用户名/密码存储在zookeeper中,并且可以通过脚本动态增减用户,当客户端使用SCRAM模式进行认证时,密码会经过SHA-256或SHA-512哈希加密后传输到服务器,因此安全性较高,在0.10.2版本中引入。

对Kafka集群来说,要想实现完整的安全模式,首先为集群中的每台机器生成密钥和证书是第一步,其次利用SASL对客户端进行身份验证是第二步,最后对不同客户端进行读写操作的授权是第三步,这些步骤即可以单独运作也可以同时运作,从而提高kafka集群的安全性。

三、具体实现

本文主要介绍作为kafka生产者,如何基于Kerberos进行身份认证给第三方kafka发送数据。

Kerberos主要由三个部分组成:密钥分发中心Key Distribution Center(即KDC)、客户端Client、服务端Service,大致关系图如下图2所示,其中KDC是实现身份认证的核心组件,其包含三个部分:

  1. Kerberos Database:储存用户密码以及其他信息
  2. Authentication Service(AS):进行用户身份信息验证,为客户端提供Ticket Granting Tickets(TGT)
  3. Ticket Granting Service(TGS):验证TGT,为客户端提供Service Tickets

kafka 认证,kafka,安全,分布式

我们作为生产者向第三方kafka发送数据,因此需要第三方提供以下安全认证文件:

  • 用户名principle:标识客户端的用户身份,也即用于登录的用户名
  • 指定用户名对应的秘钥文件xx.keytab:存储了用户的加密密码
  • 指定安全认证的服务配置文件krb5.conf:客户端根据该文件中的信息去访问KDC

获取以上安全认证文件后,即可编写java代码连接第三方kafka,步骤如下:

1、将安全认证文件xx.keytabkrb5.conf放置于某一路径下,确保后续java代码可进行读取

2、添加kafka配置文件,开启安全模式认证,其中kerberos.path是第一步中认证文件所在的目录

kafka 认证,kafka,安全,分布式

3、修改Kafka生产者配置,开启安全连接

kafka 认证,kafka,安全,分布式

4、调用认证工具类进行登录认证

kafka 认证,kafka,安全,分布式

LoginUtil认证工具类的核心是根据第一步中提供的安全认证文件自动生成jaas配置文件,该文件是kafka安全模式下认证的核心。代码如下:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

/**
 * @ProjectName: stdp-security-demo
 * @Package: 
 * @ClassName: LoginUtil
 * @Author: stdp
 * @Description: ${description}
 */
public class LoginUtil {
	
	public enum Module {
		KAFKA("KafkaClient"), ZOOKEEPER("Client");

		private String name;

		Module(String name) {
			this.name = name;
		}

		public String getName() {
			return name;
		}
	}

	private static final Logger LOGGER = LoggerFactory.getLogger(LoginUtil.class);

	/**
	 * line operator string
	 */
	private static final String LINE_SEPARATOR = System.getProperty("line.separator");

	/**
	 * jaas file postfix
	 */
	private static final String JAAS_POSTFIX = ".jaas.conf";

	private static final String JAVA_SECURITY_KRB5_CONF_KEY = "java.security.krb5.conf";

	public static final String JAVA_SECURITY_LOGIN_CONF_KEY = "java.security.auth.login.config";

	private static final String ZOOKEEPER_SERVER_PRINCIPAL_KEY = "zookeeper.server.principal";

	
	private static final boolean IS_IBM_JDK = System.getProperty("java.vendor").contains("IBM");

	/**
	 * oracle jdk login module
	 */
	private static final String SUN_LOGIN_MODULE = "com.sun.security.auth.module.Krb5LoginModule required";


	public synchronized static void login(String userPrincipal, String userKeytabPath, String krb5ConfPath)
			throws IOException
	{
		// 1.check input parameters
		if ((userPrincipal == null) || (userPrincipal.length() <= 0))
		{
			LOGGER.error("input userPrincipal is invalid.");
			throw new IOException("input userPrincipal is invalid.");
		}

		if ((userKeytabPath == null) || (userKeytabPath.length() <= 0))
		{
			LOGGER.error("input userKeytabPath is invalid.");
			throw new IOException("input userKeytabPath is invalid.");
		}

		if ((krb5ConfPath == null) || (krb5ConfPath.length() <= 0))
		{
			LOGGER.error("input krb5ConfPath is invalid.");
			throw new IOException("input krb5ConfPath is invalid.");
		}

		// 2.check file exsits
		File userKeytabFile = new File(userKeytabPath);
		if (!userKeytabFile.exists())
		{
			LOGGER.error("userKeytabFile(" + userKeytabFile.getAbsolutePath() + ") does not exsit.");
			throw new IOException("userKeytabFile(" + userKeytabFile.getAbsolutePath() + ") does not exsit.");
		}
		if (!userKeytabFile.isFile())
		{
			LOGGER.error("userKeytabFile(" + userKeytabFile.getAbsolutePath() + ") is not a file.");
			throw new IOException("userKeytabFile(" + userKeytabFile.getAbsolutePath() + ") is not a file.");
		}

		File krb5ConfFile = new File(krb5ConfPath);
		if (!krb5ConfFile.exists())
		{
			LOGGER.error("krb5ConfFile(" + krb5ConfFile.getAbsolutePath() + ") does not exsit.");
			throw new IOException("krb5ConfFile(" + krb5ConfFile.getAbsolutePath() + ") does not exsit.");
		}
		if (!krb5ConfFile.isFile())
		{
			LOGGER.error("krb5ConfFile(" + krb5ConfFile.getAbsolutePath() + ") is not a file.");
			throw new IOException("krb5ConfFile(" + krb5ConfFile.getAbsolutePath() + ") is not a file.");
		}

		// 3.set and check krb5config
		setKrb5Config(krb5ConfFile.getAbsolutePath());

//        LOGGER.info("check zookeeper server Principal =============================================");
        setZookeeperServerPrincipal(userPrincipal);
//        LOGGER.info("check jaas.conf +++++++++++++++++++++++++++++++++++++++++++++++++");
        setJaasFile(userPrincipal,userKeytabPath);
		LOGGER.info("Login success!!!!!!!!!!!!!!");
	}


	public static void setKrb5Config(String krb5ConfigFile) throws IOException {
		System.setProperty(JAVA_SECURITY_KRB5_CONF_KEY,krb5ConfigFile);
		String ret = System.getProperty(JAVA_SECURITY_KRB5_CONF_KEY);
		if (ret == null) {
			LOGGER.error(JAVA_SECURITY_KRB5_CONF_KEY + " is null.");
			throw new IOException(JAVA_SECURITY_KRB5_CONF_KEY + " is null.");
		}
		if (!ret.equals(krb5ConfigFile)){
			LOGGER.error(JAVA_SECURITY_KRB5_CONF_KEY + " is " + ret + " is not " + krb5ConfigFile + ".");
			throw new IOException(JAVA_SECURITY_KRB5_CONF_KEY + " is " + ret + " is not " + krb5ConfigFile + ".");
		}
	}

	public static void setJaasFile(String userPrincipal,String userKeytabPath) throws IOException {
		String jaasPath = new File(System.getProperty("java.io.tmpdir")) + File.separator + System.getProperty("user.name") + JAAS_POSTFIX;
		LOGGER.info("jaasPath = {}",jaasPath);
		//windows路径下分隔符替换
		jaasPath = jaasPath.replace("\\","\\\\");
		userKeytabPath = userKeytabPath.replace("\\","\\\\");
		//删除jaas文件
		deleteJaasFile(jaasPath);
		writeJaasFile(jaasPath,userPrincipal,userKeytabPath);
		System.setProperty(JAVA_SECURITY_LOGIN_CONF_KEY,jaasPath);
	}

	private static void deleteJaasFile(String jaasPath) throws IOException {
		File jaasFile = new File(jaasPath);
		if (jaasFile.exists()){
			if (!jaasFile.delete()){
				throw new IOException("failed to delete exists jaas file.");
			}
		}
	}

	private static void writeJaasFile(String jaasPath,String userPrincipal,String userKeytabPath) throws IOException {
		FileWriter writer = new FileWriter(new File(jaasPath));
		try{
			writer.write(getJaasConfContext(userPrincipal,userKeytabPath));
			writer.flush();
		}catch (IOException e){
			throw new IOException("Failed to create jaas.conf File.");
		}finally {
			writer.close();
		}
	}


	private static String getJaasConfContext(String userPrincipal,String userKeytabPath) throws IOException{
		Module[] allModule = Module.values();
		StringBuffer builder = new StringBuffer();
		for (Module module: allModule){
			String serviceName = null;
			if ("Client".equals(module.getName())){
				serviceName = "zookeeper";
			}else if ("KafkaClient".equals(module.getName())){
				serviceName = "kafka";
			}
			builder.append(getModuleContext(userPrincipal,userKeytabPath,module,serviceName));
		}
		return builder.toString();
	}

	private static String getModuleContext(String userPrincipal,String userKeytabPath,Module module,String serviceName) throws IOException {
		StringBuffer builder = new StringBuffer();
		if (IS_IBM_JDK){
			builder.append(module.getName()).append(" {").append(LINE_SEPARATOR);
			builder.append("credsType=both").append(LINE_SEPARATOR);
			builder.append("principal=\"" + userPrincipal.trim() + "\"").append(LINE_SEPARATOR);
			builder.append("useKeytab=\"" + userKeytabPath + "\"").append(LINE_SEPARATOR);
            builder.append("serviceName=\""+serviceName + "\"").append(LINE_SEPARATOR);
			builder.append("debug=true;").append(LINE_SEPARATOR);
			builder.append("};").append(LINE_SEPARATOR);
		}else {
			builder.append(module.getName()).append(" {").append(LINE_SEPARATOR);
			builder.append(SUN_LOGIN_MODULE).append(LINE_SEPARATOR);
			builder.append("useKeyTab=true").append(LINE_SEPARATOR);
			builder.append("keyTab=\"" + userKeytabPath + "\"").append(LINE_SEPARATOR);
			builder.append("principal=\"" + userPrincipal.trim() + "\"").append(LINE_SEPARATOR);
            builder.append("serviceName=\""+serviceName + "\"").append(LINE_SEPARATOR);
			builder.append("useTicketCache=false").append(LINE_SEPARATOR);
			builder.append("storeKey=true").append(LINE_SEPARATOR);
			builder.append("debug=true;").append(LINE_SEPARATOR);
			builder.append("};").append(LINE_SEPARATOR);
		}
		return builder.toString();
	}


	public static void setZookeeperServerPrincipal(String zkServerPrincipal) throws IOException {
		System.setProperty(ZOOKEEPER_SERVER_PRINCIPAL_KEY,zkServerPrincipal);
		String ret = System.getProperty(ZOOKEEPER_SERVER_PRINCIPAL_KEY);
		if (ret == null) {
			LOGGER.error(ZOOKEEPER_SERVER_PRINCIPAL_KEY + " is null.");
			throw new IOException(ZOOKEEPER_SERVER_PRINCIPAL_KEY + " is null.");
		}
		if (!ret.equals(zkServerPrincipal)){
			LOGGER.error(ZOOKEEPER_SERVER_PRINCIPAL_KEY + " is " + ret + " is not " + zkServerPrincipal + ".");
			throw new IOException(ZOOKEEPER_SERVER_PRINCIPAL_KEY + " is " + ret + " is not " + zkServerPrincipal + ".");
		}
	}
}

经过以上四步的配置,启动项目后即可自动连接kafka进行身份校验,若登录成功,会输出如下提示信息:Login success,并且会将生成的jaas文件路径打印出来。

kafka 认证,kafka,安全,分布式

四、常见问题

1、认证文件找不到

kafka 认证,kafka,安全,分布式

这是因为步骤1中kerberos.path配置有问题,检查path路径下是否存在认证文件keytab和krb5.conf。

2、 principal和keytab不匹配

kafka 认证,kafka,安全,分布式

不同的用户名对应不同的密码,在身份校验时,需保证用户名principle和密码keytab的一致性,否则无法验证通过。而principal和keytab不匹配可能存在以下两种场景:

  •  配置文件中出现问题:检查kerberos.principle和kerberos.keytab中的用户名(即hkjj)是否一致。

kafka 认证,kafka,安全,分布式

  •  检查生成的jaas文件中用户名和配置的用户名是否相同

如果步骤1检查没用问题,则可根据日志中输出的jaas文件路径查看自动生成的jaas文件中的principal和配置文件中的kerberos.principle是否一致。比如我的这个项目中,就是由于现场技术配置kerberos.principle时后面多打了一个空格,导致自动生成的jaas文件中的principle后多一个空格,因此和keytab认证失败。

kafka 认证,kafka,安全,分布式

为了彻底解决这个误打空格的问题,可以直接修改认证工具类LoginUtil,在生成jaas文件的principle时去掉可能存在的空格。

kafka 认证,kafka,安全,分布式

3、用户密码keytab更新,导致出现checksum failed

kafka 认证,kafka,安全,分布式

这是由于principal对应的密码修改了,但是程序中使用的还是旧的密码,就会出现这个问题。解决办法是找第三方提供principal对应的最新的密码文件keytab

4jaas文件找不到

kafka 认证,kafka,安全,分布式

该问题是由于找不到jaas.conf 这个文件导致的,而基于kerberos认证时一般不会出现,这是因为kerberos认证时jaas文件是由LoginUtil工具类根据安全认证文件自动生成并且存储在指定路径下的。

该问题通常出现在SASL-PLAIN方式的认证中,因为该方式需要添加一个配置参数java.security.auth.login.config来标识jaas文件的路径,如果文件路径出错则会报以上错误。

五、总结

在kafka身份认证的过程中,需要的principal,keytab,ServiceName等信息均配置在jaas文件中,因此保证认证的服务可以读取到正确的文件及正确的配置是kafka安全模式下认证的核心。

基于kerberos认证时,可根据安全认证文件自动生成jaas配置文件,从而保证了密码加密传输,相比于SASL-PLAIN模式更具安全性,并且认证实现过程也较为简单。文章来源地址https://www.toymoban.com/news/detail-850175.html

到了这里,关于Kafka安全模式之身份认证的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 指纹统一身份认证系统系统安全设计

    2.10.1 身份认证 系统是为广大工作人员提供服务的,为了区分各个用户以及不同级别的用户,需要对他们的 身份和操作的合法性进行检查。体系应该规定实现身份认证与权限检查的方式、方法以及对 这些用户的管理要求。 2.10.2 权限管理 权限管理系统是保证业务系统安全的一

    2024年02月10日
    浏览(52)
  • 【网络安全】图解 Kerberos:身份认证

    Kerberos 是一种身份认证协议,被广泛运用在大数据生态中,甚至可以说是大数据身份认证的事实标准。本文将详细说明 Kerberos 原理。 Kerberos 一词来源于古希腊神话中的 Cerberus —— 守护地狱之门的三头犬。下图是 Kerberos 的 LOGO。 一句话来说,Kerberos 是一种基于加密 Ticket 的身

    2024年02月06日
    浏览(55)
  • 2023身份识别技术大会 | 安全证件 | 数字认证 | 生物识别 | 公共安全安防身份技术展览会

    展会名称:  2023身份识别技术大会 | 安全证件 | 数字认证 | 生物识别 | 公共安全安防身份技术展览会 举办时间: 2023年5月17-18日 举办地点:  北京 国家会议中心 指导单位:  公安部科技信息化局 主办单位:  多维身份识别与可信认证技术国家工程研究中心 中国国际科技促

    2024年02月07日
    浏览(56)
  • 【安全】简单解析统一身份认证:介绍、原理和实现方法

    随着互联网的发展和各种在线服务的普及,用户在不同的应用和平台上需要进行多次身份验证。为了简化用户的登录和减少重复操作,统一身份认证(Single Sign-On,简称SSO)技术应运而生。本文将简单介绍统一身份认证的概念、原理和实现方法,希望能帮助你更好地理解和应

    2024年02月15日
    浏览(50)
  • 多因素认证与身份验证:分析不同类型的多因素认证方法,介绍如何在访问控制中使用身份验证以增强安全性

    随着数字化时代的到来,信息安全问题变得愈发重要。在网络世界中,用户的身份往往是保护敏感数据和系统免受未经授权访问的第一道防线。单一的密码已经不再足够,多因素认证(MFA)应运而生,成为提升身份验证安全性的重要工具之一。本文将深入探讨不同类型的多因

    2024年02月10日
    浏览(51)
  • 通付盾发布UIAM白皮书,利用区块链、大模型AI,以及无证书分布式身份认证赋能工业互联网

    简介 UIAM白皮书 结合各行业与国内外IAM发展状况,对IAM发展历程、核心能力以及现代增强型IAM技术的演进路线进行探讨。探索身份和信息安全管理与区块链、大模型AI、无证书分布式身份认证等技术趋势,以及UIAM技术在工业互联网的应用。期望能够帮助企业组织更加深入全面

    2024年02月11日
    浏览(63)
  • AI 欺诈事件频出,如何重塑身份认证的安全性?

    据报告表示,生成式人工智能每年可为世界经济注入相当于 4.4 万亿美元的资金。预计到 2030 年,人工智能对全球财政的潜在贡献将达到 15.7 万亿美元。人们惊叹于 AI 强大工作效率,期待能帮忙节省不必要的劳动力,但事实上 AI 出现之后,AI 造假的恐慌也随之不断蔓延。 借

    2024年02月19日
    浏览(41)
  • 信息安全概论复习笔记 第九章 身份认证(不是重点,做了解吧)

    身份认证(Authentication)的定义: 宣称者向验证方出示证据,证明其身份的交互过程 至少涉及两个参与者,是一种协议 分为双向认证和单向认证 身份认证并不是一种静态的加密,而是一个 协议过程 区分两个概念 身份认证和报文鉴别 区别: ①报文鉴别是静态附加在报文之

    2024年02月02日
    浏览(36)
  • 安全开发:身份认证方案之 Google 身份验证器和基于时间的一次性密码 TOTP 算法

    参考资料在文末注明,如本文有错漏欢迎评论区指出👏 目前很多应用都逐步采用了双因子认证或者说MFA认证方案,因此本文介绍一下背后的机制和 TOTP算法 原理。使用TOTP算法,只要满足两个条件:1)基于相同的密钥;2)时钟同步;只需要事先约定好密钥,TOTP算法就可以保

    2024年02月04日
    浏览(61)
  • Java中JWT技术解析与实践:安全高效的身份认证

    什么是JWT(JSON Web Token)? JWT是一种用于身份验证和授权的开放标准(RFC 7519),它是基于JSON格式的轻量级安全令牌。JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。通常,JWT被用于在不同的系统之间传递安全性的声明信息,以便用户在跨域应用中进行身

    2024年02月05日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包