Introducción a IAM - Día #1 de caminando con un SRE
Tags: aws, sre, devops
Published: 2020-06-19 15:07:53

Bienvenido al día #1 de caminando con un SRE. Por lo general, entre las cosas básicas que debemos saber e implementar en un proyecto está la autenticación de usuarios, roles, etc. En este post, voy a explicar (no en profundidad) qué es el servicio de IAM en AWS y cómo construir políticas para nuestros roles/usuarios usando infraestructura como código: terraform.

<h2>IAM</h2>
Identity and Access Management (IAM) se usa para administrar en AWS:
- Los usuarios
- Grupos
- Roles
- Api Keys
- Políticas de acceso de IAM

y proporciona acceso / permisos de acceso a los recursos de AWS (como EC2, S3 ..).

Si notamos en el lado derecho en la parte superior de la consola, dice `Global`, es decir, crear un usuario / grupos / roles se aplicará a todas las regiones.
Para crear un nuevo usuario, simplemente haga `click` en Usuarios en la barra de navegación izquierda.

![alt text](https://miro.medium.com/max/1400/1*816nqQDqz0jLrCssLStbOQ.png)

Por defecto, cualquier nueva cuenta creada en IAM no tiene acceso a algún servicio de AWS (denegación no explícita). 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/uf96idhb15iur8orytrp.png)

**Políticas de IAM:** Una política es un documento que establece formalmente uno o más permisos. 

Las políticas en AWS tiene el siguiente formato/template:
![alt text](https://miro.medium.com/max/1400/1*4513wAq5vgFCPhHgUbw8Fg.png)

Por ejemplo: IAM proporciona algunas plantillas de políticas preconstruidas para asignar a usuarios y grupos.

- **AdministratorAccess**: acceso completo a los recursos de AWS.
- **Power user access**: acceso de administrador, excepto que no permite la administración de usuarios / grupos.
- **Read only access**: como el nombre sugiere, el usuario solo puede ver los recursos de AWS.

![Alt Text](https://miro.medium.com/max/1400/1*duCFoAWP8bSJzaQjkiyDHw.png)

Demos un vistazo a estas políticas:

<h4>AdministratorAccess</h4>
```
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "*",
      "Resource": "*"
    }
  ]
}
```
En el ejemplo anterior, la política esta en `God mode`, tiene control sobre TODAS las acciones en TODOS los recursos de AWS.

Una buena práctica en asunto de políticas de seguridad, es asignar políticas personalizadas con los permisos estrictamente necesarios. Para esto, podemos crear nuestra propia política personalizada utilizando un generador de políticas (policy generator) o escribirla desde cero. Al menos yo, uso bastante este [policy generator](https://awspolicygen.s3.amazonaws.com/policygen.html)

![Alt Text](https://miro.medium.com/max/1400/1*SI5lEp82sdf-SBtGI5KqfA.png)

Ok, ya sabemos que es un policy, ahora vamos a crear una política personalizada donde se niega todo para los recursos EC2s:

```
{
   "Version":"2012–10–17",
   "Statement":[
      {
         "Sid":"Stmt1491718191000",
         "Effect":"Deny",
         "Action":[
            "ec2:*"
         ],
         "Resource":[
            "*"
         ]
      }
   ]
}
```
Considerar:
- Se puede adjuntar más de una política a un usuario o grupo al mismo tiempo.
- La política no se puede adjuntar directamente a los recursos de AWS (p. Ej., Instancia EC2).

Ya hemos visto como crear políticas manualmente en AWS, pero que tal si hacemos algo más automatizado: como crear nuestros usuarios y políticas usando terraform... Sí, lo sé, como verdaderos cracks.

<h2>Creando un IAM user con terraform</h2>

```
resource "aws_iam_user" "example" {
  name = "falcon"
}
```
Más detalles [aquí] (https://www.terraform.io/docs/providers/aws/r/iam_user.html)

Ahora necesito crear dos usuarios de IAM. Una forma de lograr lo mismo es copiar y pegar la misma pieza de código, pero eso anula todo el propósito de DRY. Terraform proporciona metaparámetros llamados `recuento` para lograr lo mismo, es decir, hacer ciertos tipos de bucles. El recuento es un metaparámetro que define cuántas copias del recurso crear.

```
resource "aws_iam_user" "example" {
  count = 3
  name = "falcon"
}
```

Un problema con este código es que los tres usuarios de IAM tendrían el mismo nombre, lo que provocaría un error ya que los nombres de usuario deben ser únicos. Para lograr crear 3 usuarios con distintos nombres, podemos usar `count.index` para obtener el índice de cada "iteración" en el "bucle":

```
resource “aws_iam_user” “example” {
 count = 3
 name = “falcon.${count.index}”
}
```

Si ejecutamos el plan nuevamente, veremos que Terraform quiere crear tres usuarios de IAM, cada uno con un nombre diferente (falcon.0, falcon.1, falcon.2).

Piense en un escenario del mundo real, difícilmente veremos nombres como falcon.0–2. Además, esto no es customizable, si algún día quiero crear más de 3 usuarios, debería actualizar el count, esto al menos a mí, no me gusta tanto. Prefiero tener como una lista de nombres y que el count se calcule en base a eso.
Aunque no me lo digas, sabes que esta idea es super "cool".

Ok, mi solución para este problema, es que si combinamos `count.index` con funciones de [interpolación] (https://www.terraform.io/docs/configuration-0-11/interpolation.html) integradas en Terraform, podríamos personalizar cada "iteración" del "bucle" aún más. Para lograr esto, necesitamos dos funciones de interpolación de longitud y elemento:

- element(list, index) —> Devuelve un único elemento de una lista en el índice dado. Si el índice es mayor que el número de elementos, esta función se ajustará utilizando un algoritmo de modificación estándar. 

- length(list) -> Devuelve el número de miembros en una lista o mapa dados, o el número de caracteres en una cadena dada.

```
#variables.tf
variable "username" {
  type = "list"
  default = ["gerardo","lopez","falcon"]
}

#main.tf
resource "aws_iam_user" "example" {
  count = "${length(var.username)}"
  name = "${element(var.username, count.index )}"
}
```

Ahora, cuando ejecutas el `terraform plan`, verás que Terraform quiere crear tres usuarios de IAM, cada uno con un nombre único.

Por ejemplo, si deseas proporcionar el nombre de recurso de Amazon (ARN) de uno de los usuarios de IAM como una variable de salida, podrías hacer lo siguiente:
```
# outputs.tf
output “user_arn” {
 value = “${aws_iam_user.example.0.arn}”
}
```
Si deseas los ARNs de todos los usuarios de IAM creados previamente, debe usar el carácter `splat` ("*"), en lugar del índice:
```
# outputs.tf
output “user_arn” {
 value = “${aws_iam_user.example.*.arn}”
}
```

Ya hemos terminado de crear un usuario de IAM, ahora vamos a adjuntar alguna política con estos usuarios (recuerde que los nuevos usuarios por defecto no tienen permiso alguno).

**IAM policies** Las políticas de IAM son documentos JSON que se utilizan para describir permisos dentro de AWS. Estos se utilizan para otorgar a sus usuarios de AWS acceso a recursos particulares de AWS.

Terraform proporciona un data source útil llamado `aws_iam_policy_document` que nos brinda una forma más concisa de definir la política de IAM:

```
data "aws_iam_policy_document" "example" {
  statement {
    actions = [
      "ec2:Describe*"]
    resources = [
      "*"]
  }
}
```
- La política de IAM consta de una o más declaraciones.
- Cada uno de los cuales especifica un efecto (ya sea "Permitir" o "Denegar"). 
- Una o más acciones (p. Ej., "Ec2: Describe *" permite todas las llamadas API a EC2 que comienzan con el nombre "Describe").
- Uno o más recursos (por ejemplo, "*" significa "todos los recursos").

Para crear una nueva política administrada por IAM a partir de este documento, debemos usar el recurso `aws_iam_policy`:
```
resource “aws_iam_policy” “example” {
 name = “ec2-read-only”
 policy = “${data.aws_iam_policy_document.example.json}”
}
```
El siguiente código usa el parámetro count para "recorrer" sobre cada uno de nuestros usuarios de IAM y la función de interpolación de elementos para seleccionar el ARN de cada usuario de la lista devuelta por aws_iam_user.example. *. Arn.

```
resource “aws_iam_user_policy_attachment” “test-attach” {
 count = “${length(var.username)}”
 user = “${element(aws_iam_user.example.*.name,count.index )}”
 policy_arn = “${aws_iam_policy.example.arn}”
}
```
En el código anterior, hemos adjuntado la nueva política `ec2-read-only` a nuestros usuarios de IAM.

**Los roles de IAM** se utilizan para otorgar acceso a la aplicación a los servicios de AWS sin usar credenciales permanentes.
El rol de IAM es una de las formas más seguras de dar permiso a sus instancias EC2.
Podemos adjuntar roles a una instancia EC2, y eso nos permite dar permiso a la instancia EC2 para usar otros servicios de AWS, por ejemplo: buckets S3.

Voy a crear un nuevo policy usando terrafrom, por ende crearé un nuevo archivo llamado `iam.tf`:

```
resource "aws_iam_role" "test_role" {
  name = "test_role"
  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
  tags = {
      tag-key = "tag-value"
  }
}
```
Esto va a crear el rol de IAM pero no podemos vincularlo a la instancia de AWS directamente y para eso, necesitamos el perfil de instancia EC2:

```
resource "aws_iam_instance_profile" "test_profile" {
  name = "test_profile"
  role = "${aws_iam_role.test_role.name}"
}
```

Ahora, si ejecutamos el código anterior, tenemos el perfil de roles e instancias, pero sin ningún permiso.
El siguiente paso es agregar las Políticas de IAM que permiten que la instancia EC2 ejecute comandos específicos, por ejemplo, acceder a S3 Bucket:

```
resource "aws_iam_role_policy" "test_policy" {
  name = "test_policy"
  role = "${aws_iam_role.test_role.id}"
  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "s3:*"
      ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}
EOF
}
```

Adjuntemos este rol a la instancia EC2:

```
resource "aws_instance" "role-test" {
  ami = "ami-0bbe6b35405ecebdb"
  instance_type = "t2.micro"
  iam_instance_profile = "${aws_iam_instance_profile.test_profile.name}"
}
```
Ok, llegó el momento de la verdad, executemos el código:

1: Esto inicializará el directorio de trabajo de terraform ó descargará complementos para un proveedor (ejemplo: aws)
```
terraform init
```
2: Permite ver qué hará Terraform antes de realizar los cambios reales"
```
terraform plan
```
3: Para crear realmente la instancia, necesitamos ejecutar 
```
terraform apply
```
Pueden ir a la consola de AWS, ver la instancia creada y cuales policies tiene assignado.

Si quieres destruir la infraestrutura, basta con executar el comando
```
terraform destroy
```

Eso sería todo por este primer post. Espero que les sea de utilidad cuando tengan que trabajar con IAM y terraform. No olviden compartir y si les gustó, darle "me gusta", eso me ayuda a seguir creando contenido.