Le Blog Amazon Web Services

Comment contrôler l’accès à votre domaine Amazon Elasticsearch Service

Avec Amazon Elasticsearch Service (Amazon ES), vous pouvez créer des applications sans configurer ni gérer votre propre cluster de Elasticsearch sur Amazon EC2. L’un des principaux avantages de l’utilisation d’Amazon ES est de pouvoir utiliser AWS Identity and Access Management pour accorder ou refuser l’accès à vos domaines de recherche. En revanche, si vous exécutiez un cluster Elasticsearch non géré sur AWS, utiliser IAM pour autoriser l’accès à vos domaines nécessiterait davantage d’efforts.

Dans cet article de blog, nous couvrons les différentes approches permettant de définir des autorisations sur un domaine Amazon ES avec AWS IAM. Nous commencerons par examiner les deux options de gestion d’autorisation disponibles sur Amazon ES: les autorisations basées sur les ressources et les autorisations basées sur l’identité. Nous allons également expliquer le processus de signature Version 4 et examiner quelques scénarios et approches issus de cas réels pour la définition des autorisations Amazon ES. Enfin, nous présenterons une architecture permettant de sécuriser votre déploiement Amazon ES en utilisant un proxy, tout en permettant d’utiliser Kibana pour les analyses.

Remarque: Ce post suppose que vous êtes déjà familiarisé avec la configuration d’un cluster Amazon ES. Pour apprendre à configurer un cluster Amazon ES, consultez la section Nouveau – Service Amazon Elasticsearch.

Méthodes de contrôle d’accès aux points de terminaison Amazon ES

Dans cette section, nous détaillons les options de configuration de vos domaines Amazon ES permettant de limiter l’accès aux utilisateurs et applications approuvées. En bref, Amazon ES ajoute un mécanisme d’autorisation qui s’intègre à AWS IAM. Vous définissez une stratégie IAM pour contrôler l’accès au point de terminaison du cluster, autorisant ou interdisant les actions (méthodes HTTP) sur des ressources (le point de terminaison du domaine, les index et les appels d’API à Amazon ES). Pour plus de détails sur les stratégies IAM, voir Présentation des stratégies IAM.

Vous associez ensuite les stratégies que vous avez créées dans IAM ou dans la console Amazon ES à des entités IAM spécifiques (en d’autres termes, le domaine Amazon ES, les utilisateurs, les groupes et les rôles) :

L’union de toutes les stratégies couvrant une entité, une ressource et une action spécifiques détermine si l’entité appelante est autorisée à effectuer l’action demandée sur cette ressource.

Remarque: Vous pouvez utiliser 2 stratégies pour authentifier les demandes sur Amazon ES.

La première est basée sur l’adresse IP d’origine. Vous pouvez omettre le principal de votre stratégie et spécifier une condition IP . Dans ce cas, et à moins qu’une stratégie d’accès conflictuelle, tout appel provenant de cette adresse IP sera autorisé à accéder ou se verra refuser l’accès à la ressource en question.

La deuxième méthode est basée sur le principal d’origine. Dans ce cas, vous devez inclure les informations qu’AWS doit utiliser pour authentifier le demandeur dans le cadre de chaque demande adressée à votre point de terminaison Amazon ES. Pour ce faire, vous devez signer la demande à l’aide d’une signature Signature Version 4. Plus loin dans cet article, je fournis un exemple montrant comment vous pouvez signer une requête pour Amazon ES à l’aide de Signature version 4.

1. Comment configurer des stratégies basées sur des ressources

Une stratégie basée sur des ressources est attachée au domaine Amazon ES (accessible via la console du domaine) et vous permet de spécifier quel compte AWS et quels utilisateurs ou rôles AWS peuvent accéder à votre point de terminaison Amazon ES. En outre, une stratégie basée sur les ressources vous permet de spécifier une condition IP pour restreindre l’accès en fonction des adresses IP source. La capture d’écran suivante montre le volet de la console Amazon ES dans lequel vous configurez la stratégie basée sur les ressources de votre point de terminaison.

Remarque importante qui s’applique tout au long de cet article : Les stratégies contenant es: * sont utilisées uniquement à des fins d’illustration. Assurez-vous de limiter les actions de manière appropriée pour les utilisateurs souhaités et de suivre le principe de moindre privilège.

Dans l’image précédente, vous pouvez voir que la stratégie est liée à un domaine Amazon ES appelé recipes1 , défini dans la section Ressources de la stratégie. La stratégie elle-même contient une condition spécifiant que seules les demandes provenant d’une adresse IP spécifique doivent être autorisées à émettre des demandes concernant ce domaine (bien que cela ne soit pas indiqué ici, vous pouvez également spécifier une plage IP à l’aide de la notation CIDR (Classless Inter-Domain Routing)).

Outre les restrictions basées sur l’IP, vous pouvez limiter l’accès des points de terminaison Amazon ES à certains utilisateurs ou comptes AWS. Le code suivant montre un exemple de stratégie basée sur les ressources qui permet uniquement à l’utilisateur IAM recipes1alloweduser d’ émettre des demandes. (Veillez à remplacer les valeurs en rouge par vos propres références de ressources AWS.)

{
 "Version": "2012-10-17",
 "Statement": [
  {
   "Resource": "arn:aws:es:us-west-2:111111111111:domain/recipes1/*",
   "Action": ["es:*"],
   "Effect": "Allow"
  }
 ]
}

Cet exemple de stratégie accorde à recipes1alloweduser la possibilité d’effectuer toute action liée à Amazon ES (représentée par «Action»: «es: *» ) sur le domaine recipes1 .

Pour cette stratégie, vous devez émettre une demande signée Signature Version 4; voir Exemples du processus de signature complet de la version 4 (Python) pour plus d’informations. Comme ces exemples sont en Python, nous incluons pour les développeurs Java le code suivant qui montre comment envoyer une demande signée Signature Version 4 à un point de terminaison Amazon ES. L’ exemple de code présenté décompose le processus de signature en trois parties principales contenues dans les fonctions: generateRequest() , performSigningSteps() et sendRequest(). La plupart des actions liées à la signature ont lieu dans la fonction performSigningSteps() et vous devrez télécharger et faire référence au kit AWS SDK for Java pour disposer des classes telles que AWS4Signer qui sont utilisées dans cette fonction.

En utilisant le Kit de développement logiciel (SDK), vous déléguez toute la complexité des tâches de signature au SDK AWS. Vous devez simplement configurer la requête, fournir les paramètres de clé requis pour la signature (tels que le nom du service, la région et vos informations d’identification) et appeler la méthode de signature sur la classe AWS4Signer . Veillez à éviter de coder en dur vos informations d’identification.

/// Set up the request
private static Request<?> generateRequest() {
       Request<?> request = new DefaultRequest<Void>(SERVICE_NAME);
       request.setContent(new ByteArrayInputStream("".getBytes()));
       request.setEndpoint(URI.create(ENDPOINT));
       request.setHttpMethod(HttpMethodName.GET);
       return request;
}

/// Perform Signature Version 4 signing
private static void performSigningSteps(Request<?> requestToSign) {
       AWS4Signer signer = new AWS4Signer();
       signer.setServiceName(SERVICE_NAME);
       signer.setRegionName(REGION);      

       // Get credentials
       // NOTE: *Never* hard-code credentials
       //       in source code
       AWSCredentialsProvider credsProvider =
                     new DefaultAWSCredentialsProviderChain();

       AWSCredentials creds = credsProvider.getCredentials();

       // Sign request with supplied creds
       signer.sign(requestToSign, creds);
}

/// Send the request to the server
private static void sendRequest(Request<?> request) {
       ExecutionContext context = new ExecutionContext(true);

       ClientConfiguration clientConfiguration = new ClientConfiguration();
       AmazonHttpClient client = new AmazonHttpClient(clientConfiguration);

       MyHttpResponseHandler<Void> responseHandler = new MyHttpResponseHandler<Void>();
       MyErrorHandler errorHandler = new MyErrorHandler();

       Response<Void> response =
                     client.execute(request, responseHandler, errorHandler, context);
}

public static void main(String[] args) {
       // Generate the request
       Request<?> request = generateRequest();

       // Perform Signature Version 4 signing
       performSigningSteps(request);

       // Send the request to the server
       sendRequest(request);
}

public static class MyHttpResponseHandler<T> implements HttpResponseHandler<AmazonWebServiceResponse<T>> {

       @Override
       public AmazonWebServiceResponse<T> handle(
                        com.amazonaws.http.HttpResponse response) throws Exception {

               InputStream responseStream = response.getContent();
               String responseString = convertStreamToString(responseStream);
               System.out.println(responseString);

               AmazonWebServiceResponse<T> awsResponse = new AmazonWebServiceResponse<T>();
               return awsResponse;
       }

       @Override
       public boolean needsConnectionLeftOpen() {
               return false;
       }
}

public static class MyErrorHandler implements HttpResponseHandler<AmazonServiceException> {

       @Override
       public AmazonServiceException handle(
                        com.amazonaws.http.HttpResponse response) throws Exception {
               System.out.println("In exception handler!");

               AmazonServiceException ase = new AmazonServiceException("Fake service exception.");
               ase.setStatusCode(response.getStatusCode());
               ase.setErrorCode(response.getStatusText());
               return ase;
         }

       @Override
       public boolean needsConnectionLeftOpen() {
               return false;
         }
}

N’oubliez pas que votre propre méthode generateRequest sera spécialisée dans votre application, y compris le type de requête et le corps du contenu. Les valeurs des variables référencées (indiquées en rouge) sont les suivantes :

private static final String SERVICE_NAME = "es";
private static final String REGION = "us-west-2";
private static final String HOST = "search-recipes1-xxxxxxxxx.us-west-2.es.amazonaws.com";
private static final String ENDPOINT_ROOT = "https://" + HOST;
private static final String PATH = "/";
private static final String ENDPOINT = ENDPOINT_ROOT + PATH;

Encore une fois, veillez à remplacer les valeurs en rouge par vos propres informations sur les ressources AWS, y compris la valeur d’hôte, générées dans le cadre du processus de création de cluster.

2. Comment configurer des stratégies basées sur l’identité

Contrairement aux stratégies basées sur les ressources, vous pouvez spécifier les actions qu’une identité IAM peut effectuer sur une ou plusieurs ressources AWS, telles qu’un domaine Amazon ES ou un compartiment S3. Par exemple, l’exemple de stratégie IAM suivant est associé à un utilisateur IAM.

{
 "Version": "2012-10-17",
 "Statement": [
  {
   "Resource": "arn:aws:es:us-west-2:111111111111:domain/recipes1/*",
   "Action": ["es:*"],
   "Effect": "Allow"
  }
 ]
}

En associant la stratégie précédente à une identité, vous lui accordez le droit d’effectuer toute action sur le domaine recipes1 . Pour émettre une demande sur le domaine recipes1 , vous devez utiliser la signature Signature Version 4 comme décrit plus haut dans cet article.

Avec Amazon ES, vous pouvez affiner davantage les contrôles d’accès. Supposons que vous souhaitiez configurer l’accès par fonctions et rôles et que vous ayez trois utilisateurs correspondant à trois fonctions :

  • esadmin : l’administrateur de vos clusters Amazon ES.
  • utilisateur privilégié : utilisateur expérimenté pouvant accéder à tous les domaines, mais ne peut pas effectuer de tâches de gestion.
  • analyticsviewer : utilisateur pouvant uniquement lire des données à partir de l’index analytics.

Compte tenu de cette répartition des responsabilités, les stratégies suivantes correspondent à chaque utilisateur.

Stratégie pour esadmin

{
 "Version": "2012-10-17",
 "Statement": [
  {
   "Resource": "arn:aws:es:us-west-2:111111111111:domain/*",
   "Action": ["es:*"],
   "Effect": "Allow"
  }
 ]
}

Cette stratégie permet à l’utilisateur esadmin d’exécuter toutes les actions ( es: * ) sur tous les domaines Amazon ES de la région us-west-2 .

Stratégie pour poweruser

{
 "Version": "2012-10-17",
 "Statement": [
  {
   "Resource": "arn:aws:es:us-west-2:111111111111:domain/*",
   "Action": ["es:*"],
   "Effect": "Allow"
  },
  {
   "Resource": "arn:aws:es:us-west-2:111111111111:domain/*",
   "Action": ["es: DeleteElasticsearchDomain",
              "es: CreateElasticsearchDomain"],
   "Effect": "Deny"
  }
 ]
}

Cette stratégie donne à l’utilisateur poweruser les même permissions que l’utilisateur esadmin , à l’exception de l’autorisation de créer et de supprimer des domaines (l’instruction Deny).

Politique pour analyticsviewer

{
 "Version": "2012-10-17",
 "Statement": [
  {
   "Resource":
    "arn:aws:es:us-west-2:111111111111:domain/recipes1/analytics",
   "Action": ["es:ESHttpGet"],
   "Effect": "Allow"
  }
 ]
}

La stratégie précédente donne à l’utilisateur analyticsviewer la possibilité d’émettre des requêtes HttpGet sur l’index analytics faisant partie du domaine recipes1. Il s’agit d’une stratégie limitée qui empêche l’utilisateur analyticsviewer d’effectuer d’autres actions sur cet index ou ce domaine.

Pour plus d’informations sur la configuration des stratégies d’accès Amazon ES, consultez la section Configuration des stratégies d’accès. Les stratégies spécifiques que je viens de partager et toutes les autres stratégies que vous créez peuvent être associées à une identité, un groupe ou un rôle AWS, comme décrit dans Présentation des stratégies IAM.

3. Combinaison de stratégies basées sur les ressources et sur l’identité

Maintenant que nous avons abordé les deux types de stratégies que vous pouvez utiliser pour accorder ou refuser l’accès aux points de terminaison Amazon ES, examinons ce qui se passe lorsque vous combinez des stratégies basées sur des ressources et sur une identité. Premièrement, pourquoi voudriez-vous combiner ces deux types de stratégies? Un cas d’utilisation implique un accès entre comptes: vous souhaitez autoriser les identités d’un autre compte AWS à accéder à votre domaine Amazon ES. Vous pouvez configurer une stratégie basée sur une ressource pour accorder l’accès à cet ID de compte, mais un administrateur de ce compte devra toujours utiliser des stratégies basées sur une identité pour permettre aux identités de ce compte d’effectuer des actions spécifiques sur votre domaine Amazon ES. Pour plus d’informations sur la configuration de l’accès entre comptes, voir Didacticiel: Déléguer l’accès entre comptes AWS à l’aide de rôles IAM.

Le tableau suivant résume les résultats de l’association de types de stratégie :

L’un des éléments clés du tableau précédent est qu’un Deny prévaut toujours sur un type de stratégie ayant un Allow et qu’il a un Deny en concurrence dans l’autre type de stratégie. Notez également que lorsque vous ne spécifiez pas explicitement un Deny ou Allow , l’accès est refusé par défaut. Pour plus d’informations sur la combinaison de stratégies, reportez-vous à la section Notions fondamentales sur l’évaluation des stratégies.

4. Considérations pour le déploiement

Gardons à l’esprit la discussion sur les deux types de stratégies et revenons en arrière pour examiner les considérations relatives au déploiement. Kibana, une interface utilisateur en JavaScript disponible avec Elasticsearch et Amazon ES, vous permet d’extraire des informations précieuses à partir des données stockées sur le domaine. Lorsque vous déployez Amazon ES, vous devez vous assurer que les utilisateurs appropriés (tels que les administrateurs et les analystes d’aide à la décision) ont accès à Kibana, tout en veillant à fournir un accès sécurisé à partir de vos applications à vos différents domaines de recherche Amazon ES.

Lorsqu’ils exploitent des stratégies basées sur des ressources ou des identités pour accorder ou refuser l’accès aux points de terminaison Amazon ES, les clients peuvent utiliser des stratégies anonymes ou basées sur IP, ou des stratégies qui spécifient un principal pour les demandes signées Signature Version 4. De plus, comme Kibana est basé sur JavaScript, les demandes proviennent de l’adresse IP de l’utilisateur final. Cela rend le contrôle d’accès non authentifié basé sur IP peu pratique dans la plupart des cas en raison du nombre d’adresses IP que vous devriez ajouter à la liste blanche.

Compte tenu de cette limitation du contrôle d’accès basée sur IP, il vous faut un moyen de présenter à Kibana un point de terminaison ne nécessitant pas de signature Signature Version 4. Une approche consiste à mettre un proxy entre Amazon ES et Kibana, puis à configurer une stratégie qui autorise uniquement les demandes émanant de l’adresse IP de ce proxy. En utilisant un proxy, vous ne devez gérer qu’une seule adresse IP (celle du proxy). Je décris cette approche dans la section suivante.

5. Accès à Amazon ES à partir de Kibana basé sur un proxy

Comme mentionné précédemment, un proxy peut canaliser l’accès des clients devant utiliser Kibana. Cette approche permet toujours un accès sans proxy pour d’autres applications capables d’émettre des demandes signées Signature Version 4. Le diagramme suivant illustre cette approche comprenant un proxy pour canaliser l’accès à Kibana.

Les détails clés du diagramme précédent sont les suivants :

  1. Domaine Amazon ES, qui réside dans votre compte AWS. IAM fournit un accès autorisé à ce domaine. Une stratégie IAM fournit un accès en liste blanche à l’adresse IP du serveur proxy via laquelle votre client Kibana se connectera,
  2. Proxy dont l’adresse IP est autorisée à accéder à votre domaine Amazon ES. Vous pouvez également utiliser un proxy NGINX, comme décrit dans le livre blanc NGINX Plus sur AWS,
  3. Application exécutée sur les instances EC2 qui utilise le processus de signature Signature Version 4 pour envoyer des demandes à votre domaine Amazon ES,
  4. Application cliente Kibana se connectant à votre domaine Amazon ES via le proxy.

Pour faciliter la configuration de la sécurité décrite aux paragraphes 1 et 2, il vous faut une stratégie basée sur des ressources pour verrouiller le domaine Amazon ES. Cette stratégie est présentée ci-dessous.

{
 "Version": "2012-10-17",
 "Statement": [
  {
   "Resource":
    "arn:aws:es:us-west-2:111111111111:domain/recipes1/analytics",
   "Principal": {
        "AWS": "arn:aws:iam::111111111111:role/allowedrole1"
   },
   "Action": ["es:ESHttpGet"],
   "Effect": "Allow"
  },
  {
   "Effect": "Allow",
   "Principal": {
     "AWS": "*"
   },
   "Action": "es:*",
   "Condition": {
     "IpAddress": {
       "aws:SourceIp": [
         "AAA.BBB.CCC.DDD"
       ]
     }
   },
   "Resource":
    "arn:aws:es:us-west-2:111111111111:domain/recipes1/analytics"
  }
 ]
}

Cette stratégie autorise les clients (tels que les serveurs d’applications du sous-réseau VPC présentés dans le diagramme précédent) capables d’envoyer des demandes signées Signature Version 4 à accéder au domaine Amazon ES. En parallèle, la stratégie permet aux clients Kibana d’accéder au domaine via un proxy, dont l’adresse IP est spécifiée dans la stratégie: AAA.BBB.CCC.DDD . Il est vivement recommandé de configurer l’instance EC2 exécutant le proxy avec une adresse Elastic IP. Ainsi, vous pouvez remplacer l’instance si nécessaire et toujours y attacher la même adresse IP publique. Pour plus de sécurité, vous pouvez configurer ce proxy afin qu’il authentifie les clients, comme décrit dans Utilisation de NGINX Plus et NGINX pour authentifier les utilisateurs d’application avec LDAP.

Conclusion

En utilisant les techniques décrites dans cet article, vous pouvez autoriser ou interdire l’accès à vos domaines Amazon ES à l’aide de stratégies basées sur des ressources, de stratégies basées sur une identité ou une combinaison des deux. Comme illustré, lors de l’accès à un domaine Amazon ES, vous devez émettre des demandes signées Signature Version 4, ce que vous pouvez réaliser à l’aide de l’exemple de code Java fourni. En outre, en utilisant l’architecture basée sur un proxy indiquée dans la dernière section de cet article, vous pouvez présenter l’interface utilisateur de Kibana à vos utilisateurs sans compromettre la sécurité.

Article original contribué par Jon Handler, AWS Principal Solutions Architect et traduit en français par Frédéric Nowak, Entreprise Solution Architect travaillant sur la transformation cloud des acteurs du secteur financier, LinkedIn.