Crear un servidor SFTP usando AWS Transfer Family, S3 y Route53

Hello World 🙂
En el artículo de hoy vamos a poner en práctica un caso de uso muy interesante que he implementado en varias ocasiones: la creación de un servidor SFTP en AWS. Este tipo de servicio es común en las empresas para compartir archivos, ya sea internamente o con clientes. Para ello, utilizaremos AWS Transfer Family, un servicio totalmente administrado por AWS. Esto significa que solo debemos encargarnos de su configuración inicial y del control de acceso, ya que AWS se ocupa del mantenimiento y escalabilidad cuando sea necesario.
AWS Transfer Family es un servicio de transferencia seguro que le permite transferir archivos dentro y fuera de los servicios de almacenamiento de AWS. AWS Transfer Family ofrece soporte totalmente administrado para la transferencia de archivos a través de SFTP, AS2, FTPS y FTP directamente dentro y fuera de Amazon S3 o Amazon EFS.
Tomado de AWS
Lo interesante de este servicio es que, como almacenamiento, utiliza S3 o EFS. En esta entrada nos enfocaremos en S3, un servicio altamente escalable, duradero y rentable, ideal para almacenar grandes cantidades de datos de manera segura y con acceso sencillo desde cualquier parte del mundo. En el repositorio de GitHub les dejo todo lo necesario para configurarlo también con EFS como backend de almacenamiento. Además, utilizaremos Route53 para crear una Zona Privada y agregar algunos registros necesarios para el SFTP, con el objetivo de que podamos acceder a él a través de una URL amigable, como por ejemplo sftps3.example.com. Como siempre, usaremos una VPC como base, ya que es un recurso indispensable en la mayoría de nuestros casos de uso.
Dado que nuestra zona en Route53 será privada y solo accesible desde nuestra VPC, también desplegaremos una instancia EC2 con Linux donde instalaremos todo lo necesario para hacer copiar algún fichero desde la instancia al SFTP.
Aunque todo esto puede configurarse desde la consola de AWS, en este blog preferimos hacer uso de Terraform siempre que sea posible, ya que nos permite replicar y mejorar la infraestructura de manera sencilla y eficiente.
¡Así que, sin más preámbulos, comencemos!
Requisitos:
- Tener una cuenta de AWS.
- Tener instalado Terraform.
El proyecto estará organizado en una única carpeta, dentro de la cual definiremos todos los archivos de Terraform necesarios para crear los recursos.
1- Primero necesitamos exportar nuestras credenciales de AWS en nuestra consola local;
export AWS_ACCESS_KEY_ID="XXXXXXXXXX"
export AWS_SECRET_ACCESS_KEY="XXXXXXXXXX"- Comprobamos que estemos conectados de forma correcta
2- En el archivo 0-provider.tf, definiremos los proveedores que utilizaremos en este proyecto. Estos proveedores son esenciales, ya que permiten a Terraform interactuar con los distintos servicios de infraestructura en la nube necesarios para desplegar los recursos correctamente.
-
provider "aws" {region = local.regiondefault_tags {tags = local.tags}}
3- En este archivo 01-vpc.tf, definiremos todo lo necesario para crear nuestra VPC, que servirá como la base de la infraestructura del proyecto. Aquí, definiremos subredes y demás componentes claves que permitirán una red segura y eficiente para los recursos que desplegaremos más adelante.
-
module "vpc" {source = "terraform-aws-modules/vpc/aws"name = "main-module"cidr = "10.0.0.0/16"azs = ["us-west-1a", "us-west-1b"]private_subnets = ["10.0.0.0/19", "10.0.32.0/19"]public_subnets = ["10.0.64.0/19", "10.0.96.0/19"]enable_dns_hostnames = trueenable_dns_support = trueenable_nat_gateway = truesingle_nat_gateway = trueone_nat_gateway_per_az = falsepublic_subnet_tags = {"Name" = "public-subnet"}private_subnet_tags = {"Name" = "private-subnet"}}
4- En el archivo 02-security.tf, configuraremos detalladamente los grupos de seguridad necesarios para el proyecto.
-
resource "aws_security_group" "ec2" {name = "SG-EC2"description = "SG-EC2"vpc_id = module.vpc.vpc_idingress {from_port = 0to_port = 0protocol = "-1"cidr_blocks = ["0.0.0.0/0"]}egress {from_port = 0to_port = 0protocol = "-1"cidr_blocks = ["0.0.0.0/0"]}tags = {Name = "SG-EC2"}}
5- En el archivo 03-ec2.tf, configuraremos todo lo necesario para crear nuestra instancia EC2.
-
resource "aws_instance" "linuxinstance" {count = 1# Cambiar el AMI ID si cambias de regiónami = "ami-ID"instance_type = "t2.medium"iam_instance_profile = "${aws_iam_instance_profile.poc_profile.name}"subnet_id = module.vpc.public_subnets[0]associate_public_ip_address= truevpc_security_group_ids = [aws_security_group.ec2.id]key_name="demo" # Cambiartags= {Name = "demo-linux-awstf"}}
6- En el archivo 04-iam.tf, definiremos los roles y políticas necesarias para otorgar los permisos adecuados a nuestra instancia EC2 y al usuario que se conectará al EFS. En el caso de la política AWSTransferSFTPS3AccessPolicy, es importante recordar actualizar el ARN del bucket, asegurándonos de que los permisos sean aplicados correctamente y se ajusten a nuestro entorno.
-
resource "aws_iam_role" "AWSTransferSFTPS3AccessRole" {name = "AWSTransferSFTPS3AccessRole"assume_role_policy = <<EOF{"Version": "2012-10-17","Statement": [{"Action": "sts:AssumeRole","Principal": {"Service": "transfer.amazonaws.com"},"Effect": "Allow","Sid": ""}]}EOF} -
Nota: Si creamos un Transfer Family que va a usar un EFS como backed de almacenamiento, tenemos que adjuntarle las siguientes políticas al rol ‘AmazonElasticFileSystemClientFullAccess‘ y ‘AWSTransferConsoleFullAccess‘. Si no adjuntamos estas políticas cuando intentemos conectarnos, obtendremos un error de acceso denegado.
# Actualizar el ARN del Bucketresource "aws_iam_role_policy" "AWSTransferSFTPS3AccessPolicy" {name = "AWSTransferSFTPS3AccessPolicy"role = "${aws_iam_role.AWSTransferSFTPS3AccessRole.id}"policy = <<EOF{"Version": "2012-10-17","Statement": [{"Effect": "Allow","Action": "s3:*","Resource": ["arn:aws:s3:::poc-us-west-1-eks-pod-log","arn:aws:s3:::poc-us-west-1-eks-pod-log/*"]}]}EOF}resource "aws_iam_role" "poc_role" {name = "poc_role"assume_role_policy = <<EOF{"Version": "2012-10-17","Statement": [{"Action": "sts:AssumeRole","Principal": {"Service": "ec2.amazonaws.com"},"Effect": "Allow","Sid": ""}]}EOF}resource "aws_iam_role_policy" "demo_policy" {name = "demo_policy"role = "${aws_iam_role.poc_role.id}"policy = <<EOF{"Version": "2012-10-17","Statement": [{"Effect": "Allow","Action": "ec2:*","Resource": "*"}]}EOF}resource "aws_iam_policy_attachment" "attach_amazon_ssm_full_access-poc_role" {name = "attach-amazon-ssm-full-access-poc_role"roles = [aws_iam_role.poc_role.name]policy_arn = "arn:aws:iam::aws:policy/AmazonSSMFullAccess"}resource "aws_iam_policy_attachment" "attach_amazon_ec2_role_for_ssm-poc_role" {name = "attach-amazon-ec2-role-for-ssm-poc_role"roles = [aws_iam_role.poc_role.name]policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM"}resource "aws_iam_instance_profile" "poc_profile" {name = "poc_profile"role = "${aws_iam_role.poc_role.name}"}
7- En el archivo 05-locals.tf, definiremos las variables locales del proyecto. Estas variables ayudan a centralizar valores que se utilizarán en múltiples recursos, simplificando la configuración y facilitando el mantenimiento del código.
-
locals {region = "us-west-1"environment = "poc"bucket_name = "${local.environment}-${local.region}-sftp"tags = {Environment = "POC",Terraform = "True"}}
8- En el archivo 06-rt53.tf, vamos a definir nuestra zona privada local en Route53 y vamos a crear el registro llamado sftps3.example.com.
-
esource "aws_route53_zone" "private" {name = "example.com"vpc {vpc_id = module.vpc.vpc_id}}resource "aws_route53_record" "sftp_dns_alias" {zone_id = aws_route53_zone.private.zone_idname = "sftps3.example.com"type = "CNAME"ttl = 300records = [aws_transfer_server.sftp_transfer_server.endpoint]}
9- En el archivo 07-s3.tf, vamos a definir lo necesario para crear nuestro bucket de S3
-
resource "aws_s3_bucket" "demo" {bucket = local.bucket_nametags = {Name = local.bucket_name}}
10- En el archivo 08-sftp-with-s3.tf, definiremos todos los recursos necesarios para configurar el servidor SFTP, incluyendo el grupo de logs en CloudWatch (cloudwatch_log_group) que registrará la actividad del SFTP, permitiéndonos monitorear eventos y posibles problemas, entre otros.
-
resource "aws_transfer_server" "sftp_transfer_server" {identity_provider_type = "SERVICE_MANAGED"endpoint_type = "PUBLIC"protocols = ["SFTP"] # Especificamos el protocolo SFTPdomain = "S3" # Especificamos S3 como el sistema de almacenamientosecurity_policy_name = "TransferSecurityPolicy-2024-01"post_authentication_login_banner = "Bienvenido al SFTP usando S3"logging_role = aws_iam_role.iam_for_transfer.arnstructured_log_destinations = ["${aws_cloudwatch_log_group.transfer.arn}:*"]tags = {Name = "SFTP-S3-Server"}}resource "aws_transfer_tag" "hostname" {resource_arn = aws_transfer_server.sftp_transfer_server.arnkey = "aws:transfer:customHostname"value = "sftps3.example.com"}resource "aws_cloudwatch_log_group" "transfer" {name_prefix = "transfer_test_"}data "aws_iam_policy_document" "transfer_assume_role" {statement {effect = "Allow"principals {type = "Service"identifiers = ["transfer.amazonaws.com"]}actions = ["sts:AssumeRole"]}}resource "aws_iam_role" "iam_for_transfer" {name_prefix = "iam_for_transfer_"assume_role_policy = data.aws_iam_policy_document.transfer_assume_role.jsonmanaged_policy_arns = ["arn:aws:iam::aws:policy/service-role/AWSTransferLoggingAccess"]}
11- En el archivo 09-aws_tranfer_user-s3.tf, vamos a definir lo necesario para crear nuestro usuario que se va a conectar al EFS.
-
resource "aws_transfer_user" "sftp_s3_user" {server_id = aws_transfer_server.sftp_transfer_server.iduser_name = "s3_user"role = aws_iam_role.AWSTransferSFTPS3AccessRole.arnhome_directory = "/${aws_s3_bucket.demo.bucket}/s3_user"}
Con todos los archivos configurados, ya estamos listos para ejecutar Terraform y desplegar la infraestructura.
terraform initterraform planterraform apply
Vamos a comprobar que todos recursos fueron generados correctamente, conectarnos a la instancia Linux, hacer algunos ajustes y finalmente copiar algún fichero hacia el EFS.
- Verificamos que el servicio de SFTP este activo y que el usuario esté creado.
- Zona privada y registro creados en
Route53. - Revisamos que la politica AWSTransferSFTPS3AccessPolicy tenga el ARN del bucket correcto.
- Nos conectamos a la instancia EC2 y generamos una llave SSH.
- Nota: Copiar esa llave y actualizar el parámetro
bodydentro deaws_transfer_ssh_key_s3.- Copiar el siguiente código en el fichero 09-aws_tranfer_user-s3.tf y volver a ejecutar
terraform apply, de esta forma nos aseguramos de que el usuario que generemos tenga la llave SSH correcta.-
resource "aws_transfer_ssh_key" "aws_transfer_ssh_key_s3" {server_id = aws_transfer_server.sftp_transfer_server.iduser_name = aws_transfer_user.sftp_s3_user.user_name# salida del comando cat .ssh/id_rsa.pubbody = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDYMy...'"}
-
- Copiar el siguiente código en el fichero 09-aws_tranfer_user-s3.tf y volver a ejecutar
- Después de eso ya podemos establecer conexión con el servidor SFTP usando el usuario creado.
- Pero primero vamos a ejecutar
nslookupytelnetpara ver si la respuesta es correcta, es una forma sencilla de validar que llegamos al SFTP bien. - Ahora que todo parece estar bien, vamos a conectarnos usando el comando sftp s3_user@sftps3.example.com, aunque también podemos usar el comando
scppara conectarnos y copiar cualquier fichero. - Usando el comando
getpodemos descargar ficheros del SFTP hacia nuestra instancia. - Podemos ver todas las opciones ejecutando el comando
help. - Nota: Todo este proceso de verificación también se puede realizar desde una instancia con SO Windows. Solo necesitas instalar FileZilla, generar una clave SSH usando PuTTYgen e importarla en FileZilla. Este método también es sencillo e intuitivo, y de hecho, diría que es más utilizado que el procedimiento que describí usando la instancia con Linux.
- Pero primero vamos a ejecutar
- Así podemos ver los ficheros en el bucket
S3. - Podemos ver las traza en el
Log groupdeCloudWatchcreado.
Terraform destroy
- Como siempre, al finalizar tus pruebas, recuerda eliminar todos los recursos creados ejecutando
terraform destroy --auto-approve. Este paso es crucial para evitar sorpresas desagradables en tu factura a final del mes. No subestimes la importancia de esta tarea, ya que la acumulación de recursos no utilizados puede generar costos innecesarios. Tómate el tiempo para limpiar tu entorno, eso te ahorrará dolores de cabeza en el futuro 🙂
Conclusiones
Hemos llegado al final de esta entrada, en la cual vimos cómo crear un servidor SFTP en AWS, aprovechando las grandes ventajas que ofrece la plataforma. Fuimos capaces de combinar varios servicios clave para construir una infraestructura pequeña pero robusta, obteniendo un servicio para la transferencia de archivos, altamente demandado tanto para el trabajo interno de muchas organizaciones como para el intercambio de datos con sus clientes.
En esta ocasión, nos enfocamos en el uso de S3 como almacenamiento, destacando su escalabilidad y bajo costo, pero como mencioné al inicio, también es posible utilizar EFS, lo que puede ser más adecuado para ciertos escenarios. Ambos servicios son excelentes opciones, y la decisión entre uno u otro dependerá del caso de uso específico.
¡Nos vemos en la próxima entrada!
Nota: Todo el código lo pueden encontrar en el siguiente repositorio.



















