Claranet gère un grand nombre de comptes AWS différents pour ses besoins internes et pour ses clients. L’industrialisation de la gestion des comptes AWS est donc un enjeu pour Claranet. L’une des principales problématiques à laquelle nous sommes confrontés est la gestion des accès et des droits sur ces différents comptes. Ce besoin nous a amené à étudier en profondeur AWS Identity and Access Management (IAM), le service d’authentification et autorisation d’AWS.

IAM est souvent délaissé à cause de sa complexité. C’est un service méconnu, mais qui permet de faire des choses très intéressantes pour la sécurité de son compte AWS et la gestion des ressources. J’ai récemment dû me pencher plus en détails sur IAM pour configurer un compte « bac à sable » dans lequel les utilisateurs doivent avoir des droits très larges, mais tout de même limités sur certains points :

  • accès minimal à IAM (i.e pouvoir changer son password),
  • accès interdits à certains services non-orientés « web »,
  • limité à eu-west-1 pour EC2,
  • limité à un certain type d’instance pour EC2.

Voici la policy IAM finale:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "iam:ChangePassword",
        "iam:GetAccountPasswordPolicy"
      ],
      "Resource": "*"
    },
    {
      "NotAction": [
        "iam:*",
        "devicefarm:*",
        "cloudtrail:*",
        "cloudhsm:*",
        "devicefarm:*",
        "directconnect:*",
        "aws-marketplace:*",
        "aws-marketplace-management:*",
        "mobilehub:*",
        "appstream:*",
        "gamelift:*",
        "mechanicalturk:*",
        "workdocs:*",
        "workmail:*",
        "workspaces:*",
        "wam:*"
      ],
      "Effect": "Allow",
      "Resource": "*"
    },
    {
      "Action": [
        "ec2:RunInstances"
      ],
      "Effect": "Deny",
      "Resource": "arn:aws:ec2:*:123456789000:instance/*",
      "Condition": {
        "StringNotLikeIfExists": {
          "ec2:InstanceType": [
            "t2.micro",
            "t2.small",
            "t2.medium"
          ]
        }
      }
    },
    {
      "Action": [
        "ec2:RunInstances"
      ],
      "Effect": "Deny",
      "Resource": "arn:aws:ec2:*:123456789000:instance/*",
      "Condition": {
        "StringNotLikeIfExists": {
          "ec2:region": [
            "eu-west-1"
          ]
        }
      }
    }
  ]
}

Lorsque AWS vérifie si un utilisateur a le droit d’effectuer une action, sa chaine de décision est la suivante :

iam.png

Reprenons donc les différents statements pour analyse:

Changer son password

{
  "Effect": "Allow",
  "Action": [
    "iam:ChangePassword",
    "iam:GetAccountPasswordPolicy"
  ],
  "Resource": "*"
}

Ce bloc, relativement simple, autorise l’utilisateur à changer son password.

Autorisation de la plupart des services

{
  "NotAction": [
    "iam:*",
    "devicefarm:*",
    "cloudtrail:*",
    "cloudhsm:*",
    "devicefarm:*",
    "directconnect:*",
    "aws-marketplace:*",
    "aws-marketplace-management:*",
    "mobilehub:*",
    "appstream:*",
    "gamelift:*",
    "mechanicalturk:*",
    "workdocs:*",
    "workmail:*",
    "workspaces:*",
    "wam:*"
  ],
  "Effect": "Allow",
  "Resource": "*"
}

Ce bloc donne accès à la plupart des services AWS. On remarque la combinaison du « NotAction » et du « Effect: Allow ». Ce bloc fonctionne par négation : on autorise toutes les actions non listées ici. Autrement dit, on interdit toutes les actions listées ici.

Restrictions sur le service EC2

{
  "Action": [
    "ec2:RunInstances"
  ],
  "Effect": "Deny",
  "Resource": "arn:aws:ec2:*:123456789000:instance/*",
  "Condition": {
    "StringNotLikeIfExists": {
      "ec2:InstanceType": [
        "t2.micro",
        "t2.small",
        "t2.medium"
      ]
    }
  }
},
{
  "Action": [
    "ec2:RunInstances"
  ],
  "Effect": "Deny",
  "Resource": "arn:aws:ec2:*:123456789000:instance/*",
  "Condition": {
    "StringNotLikeIfExists": {
      "ec2:region": [
        "eu-west-1"
      ]
    }
  }
}

Ces deux statements sont identiques, ils portent juste des conditions différentes. Plusieurs choses à noter :

  • L’action « Deny », qui malgré le « Allow » du bloc au dessus est appliqué, car évalué avant dans la chaîne de décisions.
  • Le bloc « Condition » qui pose, comme son nom l’indique, une condition sur la prise en compte du statement, ici un « StringNotLikeIfExists ». En traduisant en français, le bloc veut dire :
    • Sur la ressource concernée,
    • Si l’attribut « ec2:InstanceType » existe,
    • Et si l’attribut « ec2:InstanceType » est différent de « t2.micro », « t2.small » ou « t2.medium »
    • Alors Deny.

Références

Auteur : Mathieu Cassan