El servicio RDS (Relational Database Service) de Amazon Web Services aloja bases de datos de MySQL en el Cloud de AWS por ti. Los SQLs alojados son en su mayoría como cualquier MySQL estándar que quieras instalar en una instancia de EC2, solo que con un par de trucos bajo la manga. Uno de esos trucos es que permite autenticarte en la base de datos con credenciales de IAM (las Access Keys y Secret Keys que usas para autenticarte en las APIs) en vez de usar usuarios tradicionales y contraseñas de MySQL.

Por qué usar credenciales de IAM para bases de datos

Las bases de datos de RDS empiezan con un nombre de usuario y contraseña inicial que tienes configurados. Ese usuario es el usuario root administrativo y tiene mucho poder. ¡No deberías usar esas credenciales en tus aplicaciones!

Yo también quiero hacer eso

Si te gusta la idea de usar credenciales de IAM para conectarte a tu instancia de RDS y lees su manual sobre la herramienta, tal vez pienses “esto no es para mí”, ya que el manual solamente explica cómo conectarse con el cliente de línea de comando de MySQL y Java.

Aurora database with IAM authentication - CAPSiDE

Explícame cómo funciona

Básicamente, necesitas crear un RDS con la herramienta «IAM database authentication» habilitada y un usuario de IAM que te permita

{ "Version":"2012-10-17",
"Statement":[{
"Effect": "Allow",
"Action": ["rds-db:connect"],
"Resource": ["arn:aws:rds-db:eu-west-1:AWS_ACCOUNT_ID:dbuser:RDS_INSTANCE_IDENTIFIER/DATABASEUSER"]
}]
}

AWS_ACCOUNT_ID es tu identificador de cuenta AWS, de 12 dígitos, que puedes encontrar en la parte superior derecha de la consola de AWS (o en cualquier ARN para tus recursos. Fíjate en el ejemplo de más abajo para saber cómo obtenerlo).

RDS_INSTANCE_ID es el «Resource Id» de RDS (¡cuidado! ¡No es el nombre de la instancia de RDS!) de una instancia de RDS, o * para otorgar permiso para autenticarse en una instancia de RDS.

DATABASEUSER es el nombre de un usuario creado en la base de datos de MySQL con una declaración de CREATE USER y más adelante un GRANT, para poder acceder a algunas bases de datos.

También tienes que configurar un usuario de base de datos debidamente para poder autenticarte con el nuevo esquema (ten en cuenta que todavía puedes crear y usar usuarios de MySQL no autenticados con IAM, ya que los autenticados con IAM deben estar habilitados explícitamente).

CREATE USER DATABASEUSER IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS';
GRANT ALL ON mydb.* TO DATABASEUSER\@'%';

Tal vez hayas pensado que te ibas a conectar a la instancia de RDS con la Access Key como usuario y la Secret Key como contraseña de tu base de datos. Un detalle poco conocido sobre cómo AWS autentica las solicitudes de API es que la Secret Key nunca abandona la máquina del cliente (nunca se transmite a los servidores). En su lugar, la Secret Key se utiliza para firmar una solicitud, de modo que AWS puede verificar que se haya firmado con la Secret Key. Esto significa que las solicitudes pueden ser interceptadas, y eso no es suficiente para que el interceptor genere nuevas solicitudes. Por lo tanto, la autenticación IAM de RDS utiliza el mismo esquema de firma que utilizan las APIs.

Ya que el esquema de autenticación tiene que transmitir una firma, tenemos que usar una capacidad poco conocida del cliente de MySQL para transmitir contraseñas de manera clara. El protocolo por defecto de MySQL envía la contraseña que especifiques al cliente de MySQL que tiene aplicada la función hash al servidor. Al tener una firma que tiene que llegar intacta al servidor de MySQL, no debería manipularse (si se le aplica una función hash, el servidor no puede encontrarle sentido). Y esa es la razón por la que usamos la capacidad de los clientes de MySQL de enviar cualquier cosa que especifiques como contraseña a través de la red.

Para una mayor protección, encripta la conexión de MySQL para usar el esquema de autenticación de IAM (si no, la base de datos no autenticará el usuario).

¡Enséñame cómo hacerlo!

Estas instrucciones están hechas de tal manera que puedas copiarlas y pegarlas en tu sesión de consola. Necesitas el cliente de línea de comando de aws instalado y configurado.

Primero creamos una instancia de RDS con MySQL

RDS_NAME=RDSIAMTest
RDS_REGION=eu-west-1
DB_NAME=ExampleDB
DB_ROOT_USER=master
DB_ROOT_PASS=master123
aws rds create-db-instance \
--region $RDS_REGION \
--db-instance-identifier $RDS_NAME \
--db-instance-class db.t2.micro \
--engine MySQL \
--port 3306 \
--allocated-storage 5 \
--db-name $DB_NAME \
--master-username $DB_ROOT_USER \
--master-user-password $DB_ROOT_PASS \
--backup-retention-period 0 \
--enable-iam-database-authentication \
--publicly-accessible \
--no-multi-az

Abre la instancia de RDS para que puedas conectarte a ella

RDS_SG=`aws rds describe-db-instances --region $RDS_REGION --db-instance-identifier $RDS_NAME --query DBInstances[0].VpcSecurityGroups[0].VpcSecurityGroupId --output text`
MY_IP=`curl -s http://checkip.amazonaws.com/`
aws ec2 authorize-security-group-ingress --region $RDS_REGION --group-id $RDS_SG --protocol all --port 3306 --cidr $MY_IP/32

Crea un usuario de IAM con una política que nos permita conectarnos a la base de datos

IAM_USER=rdsiamtest
aws iam create-user --user-name $IAM_USER
AWS_ACCOUNT_ID=`aws iam get-user --user-name $IAM_USER --query User.Arn --output text | cut -d: -f5`
RDS_RESOURCE_ID=`aws rds describe-db-instances --region $RDS_REGION --db-instance-identifier $RDS_NAME --query DBInstances[0].DbiResourceId --output text`
DB_CONNECT_USER=dbiamuser

IAM_POLICY_ARN=`aws iam create-policy --policy-name ${IAM_USER} --policy-document "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":[\"rds-db:connect\"],\"Resource\":[\"arn:aws:rds-db:eu-west-1:$AWS_ACCOUNT_ID:dbuser:$RDS_RESOURCE_ID/$DB_CONNECT_USER\"]}]}" --query Policy.Arn --output text`
aws iam attach-user-policy --user-name $IAM_USER --policy-arn $IAM_POLICY_ARN
AWS_SECRET_KEY=`aws iam create-access-key --user-name $IAM_USER --output text --query AccessKey.SecretAccessKey`
AWS_ACCESS_KEY=`aws iam list-access-keys --user-name $IAM_USER --output text --query AccessKeyMetadata[0].AccessKeyId`

Ahora vamos a crear un usuario de MySQL que permita conectarte a la base de datos con dbiamuser. Espera a que la instancia de RDS esté completamente creada antes de ejecutar los siguientes comandos:

DB_HOST=`aws rds describe-db-instances --region $RDS_REGION --db-instance-identifier $RDS_NAME --query DBInstances[0].Endpoint.Address --output text`

echo "CREATE USER $DB_CONNECT_USER IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS'" | mysql -h $DB_HOST -u $DB_ROOT_USER -p$DB_ROOT_PASS
echo "GRANT ALL ON $DB_NAME.* TO [email protected]'%'" | mysql -h $DB_HOST -u $DB_ROOT_USER -p$DB_ROOT_PASS

¡Y ahora «la piece de résistance«! ¡Vamos a conectarnos a la instancia de RDS!

He escogido hacer esto con Perl, ya que tiene librerías estándar para conectarse a MySQL, y quiero sentir la experiencia de hacer funcionar algo que no está documentado. En este repositorio hay un script que te conectará a la instancia de RDS, calculando la firma antes de tiempo y estableciendo las opciones de cliente apropiadas para conectarse. Las librerías en otras lenguas obviamente tendrán opciones similares, seguro que ver cómo se hace en Perl te será de ayuda.

./rds_iam_connect.pl $DB_HOST $DB_CONNECT_USER $AWS_ACCESS_KEY $AWS_SECRET_KEY

Nota: si te aparece un error que dice “can’t use DBI” o “can’t find the MySQL driver”, tal vez necesites instalar la interfaz de base de datos de Perl DataBase Interface de Perl. Si estás en Debian o Ubuntu. apt-get install -y libdbd-mysql-perl hará toda la magia por ti.

Aurora database with IAM authentication - CAPSiDE

Algunas observaciones

Más información

La fuente canónica y actualizada está en GitHub. Tus contribuciones de vuelta serán bienvenidas.

 

TAGS: Aurora, Database, IAM, IAM Authentication, RDS

speech-bubble-13-icon Created with Sketch.
Comentarios
Enrique Alonso | julio 25, 2020 12:25 pm

Cuando dices que la primera conexión Tarda un poco te refieres a la primera vez que se conecta por primera vez o a todas las primera veces que se establece la conexión o también cuando el token expira la conexión con un nuevo token también tardo un poco?

Reply

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

*
*