Los permisos y el AWS CDK - AWS Cloud Development Kit (AWS CDK) v2

Esta es la segunda versión de la Guía para desarrolladores de AWS CDK. La primera versión del CDK pasó a la etapa de mantenimiento el 1.° de junio de 2022 y no cuenta con soporte desde el 1.° de junio de 2023.

Los permisos y el AWS CDK

La AWS Construct Library utiliza algunas expresiones idiomáticas comunes y ampliamente usadas para administrar el acceso y los permisos. El módulo de IAM le proporciona las herramientas que necesita para utilizar estas expresiones idiomáticas.

AWS CDK utiliza AWS CloudFormation para implementar cambios. Cada implementación involucra a un actor (ya sea un desarrollador o un sistema automatizado) que inicia una implementación de AWS CloudFormation. En este proceso, el actor asumirá una o más identidades de IAM (usuario o roles) y, si lo desea, le transferirá un rol a AWS CloudFormation.

Si se utiliza AWS IAM Identity Center para autenticarse como usuario, el proveedor de inicio de sesión único proporciona credenciales a corto plazo para iniciar sesión que lo autorizan a actuar como un rol de IAM predefinido. Para obtener información sobre cómo AWS CDK obtiene las credenciales de AWS de la autenticación de IAM Identity Center, consulte Comprender la autenticación de IAM Identity Center en la Guía de referencia para los SDK y las herramientas de AWS.

Entidades principales

Las entidades principales de IAM son entidades de AWS autenticadas que representan a un usuario, un servicio o una aplicación que puede llamar a las API de AWS. La Biblioteca de constructos de AWS permite especificar las entidades principales de varias formas flexibles para concederles acceso a sus recursos de AWS.

En contextos de seguridad, el término “entidad principal” se refiere específicamente a las entidades autenticadas, como los usuarios. Los objetos, como los grupos y los roles, no representan a los usuarios (ni a otras entidades autenticadas), sino que los identifican indirectamente con el fin de concederles permisos.

Por ejemplo, si crea un grupo de IAM, puede conceder al grupo (y, por lo tanto, a sus miembros) acceso para escribir en una tabla de Amazon RDS. Sin embargo, el grupo en sí no es una entidad principal porque no representa a una sola entidad (además, no puede iniciar sesión en un grupo).

En la biblioteca de IAM del CDK, las clases que identifican directa o indirectamente a las entidades principales implementan la interfaz de IPrincipal, lo que permite que estos objetos se usen de manera indistinta en las políticas de acceso. Sin embargo, no todos son entidades principales desde el punto de vista de la seguridad. Entre estos objetos, se incluyen:

  1. Recursos de IAM, como Role, User y Group

  2. Entidades principales de servicio (new iam.ServicePrincipal('service.amazonaws.com'))

  3. Entidades principales federadas (new iam.FederatedPrincipal('cognito-identity.amazonaws.com'))

  4. Entidades principales de cuenta () (new iam.AccountPrincipal('0123456789012'))

  5. Entidades principales de usuario canónico (new iam.CanonicalUserPrincipal('79a59d[…​]7ef2be'))

  6. Organizaciones principales de AWS (new iam.OrganizationPrincipal('org-id'))

  7. Entidades principales de ARN arbitrarios (new iam.ArnPrincipal(res.arn))

  8. iam.CompositePrincipal(principal1, principal2, …​) para confiar en varias entidades principales

Concesiones

Cada constructo que representa un recurso al que se puede acceder, como un bucket de Amazon S3 o una tabla de Amazon DynamoDB, tiene métodos que conceden acceso a otra entidad. Todos estos métodos tienen nombres que comienzan por grant.

Por ejemplo, los buckets de Amazon S3 tienen los métodos grantRead y grantReadWrite (Python: grant_read, grant_read_write) para permitir el acceso de lectura y lectura/escritura, respectivamente, de una entidad en el bucket. La entidad no necesita saber exactamente qué permisos de IAM de Amazon S3 son necesarios para realizar estas operaciones.

El primer argumento de un método de concesión es siempre del tipo IGrantable. Esta interfaz representa a las entidades a las que se les pueden conceder permisos. Es decir, representa los recursos con roles, como los objetos de IAM Role, User y Group.

También se pueden conceder permisos a otras entidades. Por ejemplo, más adelante en este tema, mostraremos cómo conceder acceso a un bucket de Amazon S3 a un proyecto de CodeBuild. Por lo general, el rol asociado se obtiene a través de una propiedad role de la entidad a la que se concede el acceso.

Los recursos que utilizan roles de ejecución, como lambda.Function, también implementan IGrantable, por lo que puede concederles acceso directamente, en lugar de concederle acceso a su rol. Por ejemplo, si bucket es un bucket de Amazon S3 y function es una función de Lambda, el siguiente código concede a la función acceso de lectura al bucket.

TypeScript
bucket.grantRead(function);
JavaScript
bucket.grantRead(function);
Python
bucket.grant_read(function)
Java
bucket.grantRead(function);
C#
bucket.GrantRead(function);

A veces, los permisos se deben aplicar mientras se implementa la pila. Uno de estos casos se produce cuando concede a un recurso personalizado de AWS CloudFormation acceso a otro recurso. El recurso personalizado se invocará durante la implementación, por lo que debe tener los permisos especificados en el momento de la implementación.

Otro caso es cuando un servicio verifica que el rol que le transfiere tiene aplicadas las políticas correctas. (Varios servicios de AWS lo hacen para asegurarse de que no se olvide de configurar las políticas). En esos casos, la implementación podría fallar si los permisos se aplican demasiado tarde.

Para forzar la aplicación de los permisos de la concesión antes de que se cree otro recurso, puede agregar una dependencia a la propia concesión, como se muestra aquí. Si bien el valor devuelto por los métodos de concesión suele descartarse, de hecho, todos los métodos de concesión devuelven un objeto iam.Grant.

TypeScript
const grant = bucket.grantRead(lambda); const custom = new CustomResource(...); custom.node.addDependency(grant);
JavaScript
const grant = bucket.grantRead(lambda); const custom = new CustomResource(...); custom.node.addDependency(grant);
Python
grant = bucket.grant_read(function) custom = CustomResource(...) custom.node.add_dependency(grant)
Java
Grant grant = bucket.grantRead(function); CustomResource custom = new CustomResource(...); custom.node.addDependency(grant);
C#
var grant = bucket.GrantRead(function); var custom = new CustomResource(...); custom.node.AddDependency(grant);

Roles

El paquete de IAM contiene un constructo Role que representa los roles de IAM. El siguiente código crea un nuevo rol que confía en el servicio Amazon EC2.

TypeScript
import * as iam from 'aws-cdk-lib/aws-iam'; const role = new iam.Role(this, 'Role', { assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'), // required });
JavaScript
const iam = require('aws-cdk-lib/aws-iam'); const role = new iam.Role(this, 'Role', { assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com') // required });
Python
import aws_cdk.aws_iam as iam role = iam.Role(self, "Role", assumed_by=iam.ServicePrincipal("ec2.amazonaws.com")) # required
Java
import software.amazon.awscdk.services.iam.Role; import software.amazon.awscdk.services.iam.ServicePrincipal; Role role = Role.Builder.create(this, "Role") .assumedBy(new ServicePrincipal("ec2.amazonaws.com")).build();
C#
using Amazon.CDK.AWS.IAM; var role = new Role(this, "Role", new RoleProps { AssumedBy = new ServicePrincipal("ec2.amazonaws.com"), // required });

Puede agregar permisos para un rol llamando al método addToPolicy del rol (Python: add_to_policy) y presentar una PolicyStatement que defina la regla que se va a agregar. La declaración se agrega a la política predeterminada del rol; si no tiene ninguna, se crea una.

El siguiente ejemplo agrega una declaración de política Deny al rol para las acciones ec2:SomeAction y s3:AnotherAction en los recursos bucket y otherRole (Python: other_role), con la condición de que el servicio autorizado sea AWS CodeBuild.

TypeScript
role.addToPolicy(new iam.PolicyStatement({ effect: iam.Effect.DENY, resources: [bucket.bucketArn, otherRole.roleArn], actions: ['ec2:SomeAction', 's3:AnotherAction'], conditions: {StringEquals: { 'ec2:AuthorizedService': 'codebuild.amazonaws.com', }}}));
JavaScript
role.addToPolicy(new iam.PolicyStatement({ effect: iam.Effect.DENY, resources: [bucket.bucketArn, otherRole.roleArn], actions: ['ec2:SomeAction', 's3:AnotherAction'], conditions: {StringEquals: { 'ec2:AuthorizedService': 'codebuild.amazonaws.com' }}}));
Python
role.add_to_policy(iam.PolicyStatement( effect=iam.Effect.DENY, resources=[bucket.bucket_arn, other_role.role_arn], actions=["ec2:SomeAction", "s3:AnotherAction"], conditions={"StringEquals": { "ec2:AuthorizedService": "codebuild.amazonaws.com"}} ))
Java
role.addToPolicy(PolicyStatement.Builder.create() .effect(Effect.DENY) .resources(Arrays.asList(bucket.getBucketArn(), otherRole.getRoleArn())) .actions(Arrays.asList("ec2:SomeAction", "s3:AnotherAction")) .conditions(java.util.Map.of( // Map.of requires Java 9 or later "StringEquals", java.util.Map.of( "ec2:AuthorizedService", "codebuild.amazonaws.com"))) .build());
C#
role.AddToPolicy(new PolicyStatement(new PolicyStatementProps { Effect = Effect.DENY, Resources = new string[] { bucket.BucketArn, otherRole.RoleArn }, Actions = new string[] { "ec2:SomeAction", "s3:AnotherAction" }, Conditions = new Dictionary<string, object> { ["StringEquals"] = new Dictionary<string, string> { ["ec2:AuthorizedService"] = "codebuild.amazonaws.com" } } }));

En el ejemplo anterior, hemos creado una nueva PolicyStatement insertada con la llamada a addToPolicy (Python: add_to_policy). También puede presentar una declaración de política existente o una que haya modificado. El objeto PolicyStatement tiene numerosos métodos para agregar entidades principales, recursos, condiciones y acciones.

Si está utilizando un constructo que requiere un rol para funcionar correctamente, puede optar por una de las siguientes opciones:

  • Presenta un rol existente al crear una instancia del constructo.

  • Deje que el constructo cree un nuevo rol para usted, que confíe en la entidad principal de servicio apropiada. El siguiente ejemplo utiliza un constructo de este tipo: un proyecto de CodeBuild.

TypeScript
import * as codebuild from 'aws-cdk-lib/aws-codebuild'; // imagine roleOrUndefined is a function that might return a Role object // under some conditions, and undefined under other conditions const someRole: iam.IRole | undefined = roleOrUndefined(); const project = new codebuild.Project(this, 'Project', { // if someRole is undefined, the Project creates a new default role, // trusting the codebuild.amazonaws.com service principal role: someRole, });
JavaScript
const codebuild = require('aws-cdk-lib/aws-codebuild'); // imagine roleOrUndefined is a function that might return a Role object // under some conditions, and undefined under other conditions const someRole = roleOrUndefined(); const project = new codebuild.Project(this, 'Project', { // if someRole is undefined, the Project creates a new default role, // trusting the codebuild.amazonaws.com service principal role: someRole });
Python
import aws_cdk.aws_codebuild as codebuild # imagine role_or_none is a function that might return a Role object # under some conditions, and None under other conditions some_role = role_or_none(); project = codebuild.Project(self, "Project", # if role is None, the Project creates a new default role, # trusting the codebuild.amazonaws.com service principal role=some_role)
Java
import software.amazon.awscdk.services.iam.Role; import software.amazon.awscdk.services.codebuild.Project; // imagine roleOrNull is a function that might return a Role object // under some conditions, and null under other conditions Role someRole = roleOrNull(); // if someRole is null, the Project creates a new default role, // trusting the codebuild.amazonaws.com service principal Project project = Project.Builder.create(this, "Project") .role(someRole).build();
C#
using Amazon.CDK.AWS.CodeBuild; // imagine roleOrNull is a function that might return a Role object // under some conditions, and null under other conditions var someRole = roleOrNull(); // if someRole is null, the Project creates a new default role, // trusting the codebuild.amazonaws.com service principal var project = new Project(this, "Project", new ProjectProps { Role = someRole });

Una vez creado el objeto, el rol (ya sea el rol presentado o el predeterminado que crea el constructo) está disponible como la propiedad de role. Sin embargo, esta propiedad no está disponible en los recursos externos. Por lo tanto, estos constructos tienen un método addToRolePolicy (Python: add_to_role_policy).

El método no hace nada si el constructo es un recurso externo y, de lo contrario, llama al método addToPolicy (Python: add_to_policy) de la propiedad role. Esto le ahorrará la molestia de tratar el caso indefinido de forma explícita.

El siguiente ejemplo lo demuestra:

TypeScript
// project is imported into the CDK application const project = codebuild.Project.fromProjectName(this, 'Project', 'ProjectName'); // project is imported, so project.role is undefined, and this call has no effect project.addToRolePolicy(new iam.PolicyStatement({ effect: iam.Effect.ALLOW, // ... and so on defining the policy }));
JavaScript
// project is imported into the CDK application const project = codebuild.Project.fromProjectName(this, 'Project', 'ProjectName'); // project is imported, so project.role is undefined, and this call has no effect project.addToRolePolicy(new iam.PolicyStatement({ effect: iam.Effect.ALLOW // ... and so on defining the policy }));
Python
# project is imported into the CDK application project = codebuild.Project.from_project_name(self, 'Project', 'ProjectName') # project is imported, so project.role is undefined, and this call has no effect project.add_to_role_policy(iam.PolicyStatement( effect=iam.Effect.ALLOW, # ... and so on defining the policy ) )
Java
// project is imported into the CDK application Project project = Project.fromProjectName(this, "Project", "ProjectName"); // project is imported, so project.getRole() is null, and this call has no effect project.addToRolePolicy(PolicyStatement.Builder.create() .effect(Effect.ALLOW) // .. and so on defining the policy .build(); )
C#
// project is imported into the CDK application var project = Project.FromProjectName(this, "Project", "ProjectName"); // project is imported, so project.role is null, and this call has no effect project.AddToRolePolicy(new PolicyStatement(new PolicyStatementProps { Effect = Effect.ALLOW, // ... and so on defining the policy }));

Políticas de recursos

Algunos recursos en AWS, como los buckets de Amazon S3 y los roles de IAM, también tienen una política de recursos. Estos constructos tienen un método addToResourcePolicy (Python: add_to_resource_policy), que toma una PolicyStatement como argumento. Cada declaración de política agregada a una política de recursos debe especificar al menos una entidad principal.

En el siguiente ejemplo, el bucket de Amazon S3 bucket otorga un rol con el permiso s3:SomeAction para sí mismo.

TypeScript
bucket.addToResourcePolicy(new iam.PolicyStatement({ effect: iam.Effect.ALLOW, actions: ['s3:SomeAction'], resources: [bucket.bucketArn], principals: [role] }));
JavaScript
bucket.addToResourcePolicy(new iam.PolicyStatement({ effect: iam.Effect.ALLOW, actions: ['s3:SomeAction'], resources: [bucket.bucketArn], principals: [role] }));
Python
bucket.add_to_resource_policy(iam.PolicyStatement( effect=iam.Effect.ALLOW, actions=["s3:SomeAction"], resources=[bucket.bucket_arn], principals=role))
Java
bucket.addToResourcePolicy(PolicyStatement.Builder.create() .effect(Effect.ALLOW) .actions(Arrays.asList("s3:SomeAction")) .resources(Arrays.asList(bucket.getBucketArn())) .principals(Arrays.asList(role)) .build());
C#
bucket.AddToResourcePolicy(new PolicyStatement(new PolicyStatementProps { Effect = Effect.ALLOW, Actions = new string[] { "s3:SomeAction" }, Resources = new string[] { bucket.BucketArn }, Principals = new IPrincipal[] { role } }));

Uso de objetos de IAM externos

Si ha definido un usuario, una entidad principal, un grupo o un rol de IAM fuera de su aplicación de AWS CDK, puede usar ese objeto de IAM en su aplicación de AWS CDK. Para ello, cree una referencia a él con su ARN o su nombre. (Utilice el nombre para los usuarios, los grupos y los roles). A continuación, la referencia que se obtiene se puede utilizar para conceder permisos o para elaborar declaraciones de políticas, como se ha explicado anteriormente.

Las políticas (incluidas las políticas administradas) se pueden utilizar de forma similar mediante los siguientes métodos. Puede utilizar las referencias a estos objetos en cualquier lugar que se requiera una política de IAM.

nota

Como ocurre con todas las referencias a recursos de AWS externos, no puede modificar los objetos de IAM externos en su aplicación de CDK.