opensearch IAM role 규칙 적용

jinwook han·2022년 7월 25일
0

opensearch에서 특정 role에만 DELETE 권한을 허용하도록 할 수 있다.

role은 user와 비슷하지만, 비밀번호나 access_key 등이 없다는 게 특징이다. 또한 인스턴스 등의 리소스에게 권한을 줄 때 role을 사용한다.
https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html
https://stackoverflow.com/questions/46199680/difference-between-iam-role-and-iam-user-in-aws

opensearch resource based policy

opensearch 도메인 화면에서 Security Configuration 탭으로 들어간다.

수정화면에서 policy json 에디터 화면을 볼 수 있다.

다음 json 코드를 추가한다.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::111111111:role/opensearch-http-role"
        ]
      },
      "Action": "es:ESHttpPut",
      "Resource": "arn:aws:es:ap-northeast-2:11111111:domain/mynewdomain/*"
    }
  ]
}

opensearch-http-role은 아무 permission이 없는 빈 role이다.
permission이 없어도 된다.

인스턴스에 role 부여하기

인스턴스를 만들고, 그 인스턴스에서 opensearch에 요청을 할 것이다.
인스턴스에 방금 opensearch policy에 추가한 opensearch-http-role을 부여한다.

opensearch 호출하는 java 코드 만들기

opensearch를 호출하는 java 코드를 만든다.
위에서 만든 인스턴스(javaOpensearchClient)에서 실행될 것이다.

인스턴스에서 opensearch에게 http 요청을 할 때, 자동으로 role 정보가 추가되지는 않는다.
인스턴스에서 curl 요청을 날렸을 때 부가 헤더를 직접 넣지 않는 이상, ec2에서 http를 자동으로 인터셉트하지 않기에 요청에서 role은 비어있을 것이다.

따라서 role 정보를 http 헤더에 넣는 코드가 필요하다.

다음 문서의 코드를 참고했다.
https://docs.aws.amazon.com/opensearch-service/latest/developerguide/request-signing.html

AmazonOpenSearchServiceSample 코드:


public class AmazonOpenSearchServiceSample {

    private static String serviceName = "es";
    private static String region = ""; // e.g. us-east-1
    private static String host = ""; // e.g. https://search-mydomain.us-west-1.es.amazonaws.com
    private static String index = "my-index";
    private static String type = "_doc";
    private static String id = "1";

    static final AWSCredentialsProvider credentialsProvider = new DefaultAWSCredentialsProviderChain();

    public static void main(String[] args) throws IOException {

        RestHighLevelClient searchClient = searchClient(serviceName, region);

        // Create the document as a hash map
        Map<String, Object> document = new HashMap<>();
        document.put("title", "Walk the Line");
        document.put("director", "James Mangold");
        document.put("year", "2005");

        // Form the indexing request, send it, and print the response
        IndexRequest request = new IndexRequest(index, type, id).source(document);
        IndexResponse response = searchClient.index(request, RequestOptions.DEFAULT);
        System.out.println(response.toString());
    }

    // Adds the interceptor to the OpenSearch REST client
    public static RestHighLevelClient searchClient(String serviceName, String region) {
        AWS4Signer signer = new AWS4Signer();
        signer.setServiceName(serviceName);
        signer.setRegionName(region);
        HttpRequestInterceptor interceptor = new AWSRequestSigningApacheInterceptor(serviceName, signer, credentialsProvider);
        return new RestHighLevelClient(RestClient.builder(HttpHost.create(host)).setHttpClientConfigCallback(hacb -> hacb.addInterceptorLast(interceptor)));
    }
}

위 코드에서 http header에 인증 정보를 추가해주는 역할은 AWSCredentialsProvider, AWSRequestSigningApacheInterceptor에서 하고 있다.

DefaultAWSCredentialsProviderChain의 경우 전역으로 설정된 access_key가 없다면, aws metadata service를 통해 인증을 수행하고 있다.
https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.html
즉 전역으로 설정된 User 정보가 없는 경우, 인스턴스의 role 정보를 참조하여 인증 헤더를 추가한다.
aws metadata service는 인스턴스의 role 정보를 참조하기 때문이다.

인스턴스의 role 정보로 인증을 수행하기 위해, 인스턴스에서 access_key 설정 없이 위 코드를 수행한다.

인스턴스에서 opensearch 호출하는 코드 수행

새로 만든 인스턴스(javaOpensearchClient)에서 opensearch를 호출하는 코드(AmazonOpenSearchServiceSample)를 수행한다.

build.gradle의 jar 설정

jar {
    manifest {
        attributes(
                'Main-Class': 'opensearchLetsgo.App'
        )
    }

  doFirst {
    from {
      configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
    }
  }
  duplicatesStrategy = DuplicatesStrategy.EXCLUDE

  exclude 'META-INF/*.RSA', 'META-INF/*.SF','META-INF/*.DSA'

}

빌드 결과물로 나온 jar을 수행한다.

java -jar app.jar

인증을 통과해서 PUT 요청이 잘 수행됐다.

notPrincipal에서는 role 규칙 실패하는 문제

notPrincipal에서 role을 적용하려고 하니, 인증이 실패했다.
적용한 policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "NotPrincipal": {
        "AWS": [
          "arn:aws:iam::111111111:role/opensearch-http-role"
        ]
      },
      "Action": "es:ESHttpPut",
      "Resource": "arn:aws:es:ap-northeast-2:11111111:domain/mynewdomain/*"
    }
  ]
}

https://serverfault.com/questions/988118/aws-deny-notprincipal-bucket-policy
찾아보니 notPrincipal에서는 role만 적용할 게 아니라, assume-role도 함께 적용해야 한다고 한다.

but is problematic with IAM roles because the role’s Principal value is composed of two Amazon Resource Names (ARNs), the role ARN and the assumed-role ARN. The role ARN is the identifier for the IAM role itself and the assumed-role ARN is what identifies the role session in the logs. When using the NotPrincipal element, you must include both ARNs for this approach to work, and the second of these ARNs should include a variable name.

0개의 댓글