본문 바로가기
Old Posts/Hadoop

[Hadoop] 하둡 클라이언트에서 커버로스(Kerberos) 인증 사용하기 - 여러 계정 인증 동시사용 (UserGroupInformation)

by A6K 2021. 8. 8.

여러 사용자가 동시에 접근하는 하둡 클러스터를 서비스할 때, 데이터나 리소스에 대한 보안 강화를 위해 '커버로스(Kerberos)' 인증을 사용할 수 있다. 커버로스 인증을 사용하는 클러스터에 접근하려면 Principal과 Keytab 혹은 패스워드를 이용해 인증을 받고, 발급받은 커버로스 티켓을 이용해서 하둡을 사용하게 된다.

HDFS 클라이언트가 네임노드 혹은 데이터 노드와 통신하면서 데이터를 받아오는 과정에서도 커버로스 인증을 사용할 수 있다. 하둡은 UserGroupInformation이라는 클래스를 이용해 커버로스 프로토콜을 사용할 수 있도록 프로그래머에게 편의를 제공한다.

메이븐 의존성

일단은 커버로스 인증을 사용하는 하둡 클라이언트를 테스트해보기 위해 다음 메이븐 의존성을 추가해보자.

<dependency>
    <groupId>org.apache.hadoop</groupId>
    <artifactId>hadoop-common</artifactId>
    <version>2.7.2</version>
</dependency>

이제 작성하는 하둡 클라이언트 프로그램에서 UserGroupInformation 클래스를 이용해서 코드를 작성해보자.

UserGroupInformation.setConfiguration()

우선 하둡 클라이언트에서 core-site.xml, hdfs-site.xml 등의 설정 파일을 불러와야한다. 

// org.apache.hadoop.conf.Configuration
Configuration conf = new Configuration();

// org.apache.hadoop.security.UserGroupInformation
UserGroupInformation.setConfiguration(conf);

Configuration 객체를 생성하면 하둡 설정파일들을 로드해서 configuration 객체에 담아둔다. core-site.xml, hdfs-site.xml 파일에는 사이트에 특화되어 있는 설정들이 들어있고, 여기에 없는 설정들은 core-default.xml 파일에서 기본값을 가져온다.

UserGroupInformation 클래스는 setConfiguration() 메소드를 제공해 로드한 Configuration 객체를 UserGroupInformation 클래스가 접근할 수 있는 메모리 공간에 저장해두고, 뒤이어 오는 하둡 클라이언트 연산들의 인증에 사용할 수 있도록한다.

예를 들어 core-site.xml 파일에 다음과 같이 인증 방법에 대한 설정이 들어있다고 생각해보자.

<property>
    <name>hadoop.security.authentication</name>
    <value>kerberos</value>
</property>

그러면 UserGroupInformation.setConfiguration() 메소드 수행 이후 HDFS 클라이언트 연산들은 kerberos 인증 절차를 거치게 된다.

주의해야할 점은 UserGroupInformation.setConfiguration() 메소드는 설정 정보를 static 영역에 저장한다는 점이다. 즉, 멀티스레드 환경에서 UserGroupInformation.setConfiguration() 메소드를 여러번 실행하면 예상하지 못한 부작용이 발생할 수 있다는 점이다.

UserGroupInformation.isSecurityEnabled()

isSecurityEnabled() 메소드는 현재 실행 환경에서 하둡의 보안 설정이 활성화되어 있는지 확인해준다. isSecurityEnabled() 메소드는 다음과 같이 구현되어 있다.

public static boolean isSecurityEnabled() {
  return !isAuthenticationMethodEnabled(AuthenticationMethod.SIMPLE);
}

@InterfaceAudience.Private
@InterfaceStability.Evolving
private static boolean isAuthenticationMethodEnabled(AuthenticationMethod method) {
  ensureInitialized();
  return (authenticationMethod == method);
}

authenticationMethod라는 static 멤버 변수에 인증 방법이 저장되어 있다. AuthenticationMethod.SIMPLE은 보안 인증을 하지 않겠다는 기본 설정이다. (다시 언급하지만 authenticationMethod 변수는 static 멤버 변수다. 멀티 스레드 환경에서 이 값을 조작하면 다른 스레드에서 부작용이 발생할 수 있다)

loginUserFromKeytab()

core-site.xml 파일에 인증설정을 적어두고, Configuration 객체를 생성한 다음 setConfiguration() 메소드 호출을 했으니 이제 하둡과 관련된 작업에서 사용자 인증을 체크하게 된다. 즉, 하둡 작업을 하려면 로그인을 해야한다는 의미다.

사용자 인증을 할 수 있는 첫번째 메소드는 UserGroupInformation.loginUserFromKeytab() 메소드다. 이름에서 알 수 있듯이 '키 탭(Keytab)' 정보를 이용해서 커버로스 인증을 해주는 메소드다.

Configuration conf = new Configuration();
UserGroupInformation.setConfiguration(conf);

// 로그인
UserGroupInformation.loginFromKeytab("dave@MY-REALM", "/etc/keytabs/my.keytab");

loginFromKeytab() 메소드는 로그인 할 ID에 해당하는 Principal과 비밀번호 정보가 담겨있는 Keytab파일 경로를 인자받는다. 역시 static 메소드이며 정상적으로 인증 절차가 완료되면 메소드 수행 이후 컨테스트에서의 하둡 작업에 인증 정보를 사용한다. (역시나 static 영역에 현재 유저 정보를 저장하기 때문에 멀티스레드 환경과 콜스택이 복잡한 환경에서 로그인한 유저 정보가 덮어쓰여질 수 있다.)

loginUserFromKeytabAndReturnUGI()

loginUserFromKeytab() 메소드가 인증 정보를 static 영역에 저장하기 때문에 발생할 수 있는 불편함을 극복하고자 UserGroupInformation의 인증 정보를 객체로 리턴하여 필요할 때 적절한 인증을 가져다 사용할 수 있는 메소드를 제공한다. loginUserFromKeytabAndReturnUGI()는 인증 정보를 static 영역에 덮어쓰는 대신 객체로 만들어서 리턴해준다.

한번에 여러개의 Principal 인증 정보를 만들어 필요할 때 적절한 인증을 가져다 사용할 수 있다.

Configuration conf = new Configuration();
UserGroupInformation.setConfiguration(conf);

// 로그인 - dave@MY-REALM
UserGroupInformation ugi1 = UserGroupInformation.loginFromKeytabAndReturnUGI("dave@MY-REALM", "/etc/keytabs/dave.keytab");

// 로그인 - jack@MY-REALM
UserGroupInformation ugi2 = UserGroupInformation.loginFromKeytabAndReturnUGI("jack@MY-REALM", "/etc/keytabs/jack.keytab");

Path path = new Path("/user/dave");
bool exists= ugi1.doAs(new PrivilegedExceptionAction<Boolean>() {
  @Override
  public Boolean run() throws Exception {
    FileSystem fs = FileSystem.get(conf);
    try (FileSystem fs = FileSystem.get(conf)) {
            return fs.exists(path);
    }
  }
});

Path path = new Path("/user/jack");
bool exists= ugis.doAs(new PrivilegedExceptionAction<Boolean>() {
  @Override
  public Boolean run() throws Exception {
    FileSystem fs = FileSystem.get(conf);
    try (FileSystem fs = FileSystem.get(conf)) {
            return fs.exists(path);
    }
  }
});

필요할 때 UserGroupInformation 객체의 doAs(PrivilegedAction) 메소드를 이용해 특정 유저 인증으로 필요한 동작을 실행할 수 있다.

댓글