본문 바로가기
카테고리 없음

하둡(Hadoop)에서 커버로스(Kerberos) 인증 사용하기 - 여러개 인증 동시 사용

by 왕 달팽이 2020. 3. 8.
반응형

하둡(Hadoop)에 보안을 강화하기 위해 '커버로스(Kerberos)' 프로토콜을 사용할 수 있습니다. Principal과 Keytab 혹은 패스워드를 이용해 계정을 인증 받은 후 커버로스 티켓을 이용해 하둡의 서비스를 사용할 수 있게해서 클러스터의 보안을 강화할 수 있습니다.

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

Maven Dependency

하둡 클라이언트를 작성할 때 커버로스 인증을 받으려면 UserGroupInformation 클래스를 이용하면 됩니다. UserGroupInformation 클래스를 이용하기 위해서는 maven dependency에 hadoop-common을 추가해주면 됩니다.

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

UserGroupInformation.setConfiguration()

core-site.xml, hdfs-site.xml 등의 하둡 설정 파일을 불러들여야 합니다. UserGroupInformation 클래스는 setConfiguration()이라는 메소드를 제공하고 있습니다.

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

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

Configruration 객체를 생성하면 JVM의 CLASSPATH에서 core-default.xml 혹은 core-site.xml 파일을 읽어서 설정들을 로드해줍니다. core-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 인증 절차를 거치게 됩니다.

주의해야 할 점은 setConfiguration() 메소드는 설정 정보를 JVM의 static 영역에 내용을 기록한다는 점입니다. 즉, 멀티쓰레드 실행 환경이나 콜스택에서 여러번 setConfiguration() 메소드를 호출하면 예상치 못한 부작용(Side Effect)이 발생할 수 있습니다.

UserGroupInformation.isSecurityEnabled()

isSecurityEnabled() 메소드는 현재 실행 컨텍스트에서 하둡 보안 설정이 활성화 되어 있는지 확인해주는 static 메소드입니다. 메소드의 구현은 다음과 같습니다.

  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이 아닌지 확인해봅니다. SIMPLE은 보안 인증을 하지 않겠다는 기본 설정입니다.

다시한번 말씀드리지만 이 메소드가 접근하는 authenticationMethod 멤버가 static 멤버이기 때문에 멀티 스레드 환경이나 콜스택에서 여러번 setConfiguration()을 수행하는 경우 덮어 쓰여질 우려가 있습니다.

인증하기 - loginUserFromKeytab()

인증설정을 core-site.xml에 명시하고 setConfiguration() 메소드 호출을 했으니 이제 하둡과 관련된 작업에서 사용자 인증을 체크하게 됩니다. 즉, 하둡 작업을 하려면 로그인을 해야합니다.

사용자 인증을 할 수 있는 첫번째 메소드는 UserGroupInformation.loginUserFromKeytab() 메소드입니다. 이름에서 알 수 있듯이 키탭(Keytab) 정보를 이용해서 특정 Principal에 로그인을 할 수 있는 메소드입니다.

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) 메소드를 이용해 특정 유저 인증으로 필요한 동작을 실행할 수 있습니다.

반응형

댓글