🌍

Terraform + EC2でFTPサーバーを構築

2022/12/19に公開約5,900字

概要

EC2で検証用にFTPサーバーを構築する機会があり、Terraformでの構築記事が無かったのでメモを残しておきます。
Terraformで構築を行いますが、後述するように全自動では構築が出来なかったので、半自動で構築を行います。

なお今回はVPCは作成されているものとして、作成されているVPCの値を利用します。

環境

  • Mac OS X 12.6
  • Terraform 1.2.1

以下のようなディレクトリ構成で進めていきますので、あらかじめ作成しておいてください

sample
  - key_pair/
  - main.tf
  - local.tf
  - parameter_store.tf
  - security_group.tf
  - ec2.tf

手順

  1. キーペアの作成
  2. main.tfの作成
  3. local.tf
  4. parameter_store.tfの作成
  5. security_group.tfの作成
  6. ec2.tfの作成
  7. terraform apply
  8. vsftpd.confを修正し、vsftpdを再起動
  9. 接続確認

1. キーペアの作成

FTPサーバーにログインするためのキーペアを作成します

$ cd /path/to/sample

# 4096ビットでキーペアを作成
$ ssh-keygen -t rsa -b 4096 -f ./key_pair/id_rsa

2. main.tfの作成

本来はS3 or Terraform Cloudでstateファイルを管理しますが、今回はサンプルなので、ローカルでstateファイルを管理します。

main.tf
terraform {
  required_version = "~> 1.2.1"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "4.22.0"
    }
  }
}

3. local.tfの作成

ローカル変数の値を管理します

local.tf
locals {
  vpc_id = ""
  public_subnet_id = ""
  allow_ips = ["", ""] # FTP接続を許可するIPの配列を設定
}

4. parameter_store.tfの作成

FTPプロトコルでログインをする際に利用するパスワードをterraformで生成します。

parameter_store.tf
resource "random_password" "this" {
  length  = 16
  special = false
}

resource "aws_ssm_parameter" "this" {
  name  = "/ftp/user-password"
  type  = "SecureString"
  value = random_password.this.result
}

5. security_group.tfの作成

security_group.tfでは以下のことを許可するように構築

  • 許可したIPからのFTP接続を許可
  • 許可したIPからのSSHでの接続を許可
security_group.tf
resource "aws_security_group" "this" {
  name   = "ftp-sg"
  vpc_id = local.vpc_id

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_security_group_rule" "range" {
  security_group_id = aws_security_group.this.id

  from_port   = 60001
  to_port     = 60010
  protocol    = "tcp"
  type        = "ingress"
  cidr_blocks = local.allow_ips
}

resource "aws_security_group_rule" "ssh" {
  security_group_id = aws_security_group.this.id

  from_port   = 22
  to_port     = 22
  protocol    = "tcp"
  type        = "ingress"
  cidr_blocks = local.allow_ips
}

resource "aws_security_group_rule" "ftp" {
  security_group_id = aws_security_group.this.id

  from_port   = 21
  to_port     = 21
  protocol    = "tcp"
  type        = "ingress"
  cidr_blocks = local.allow_ips
}

6. ec2.tfの作成

FTPサーバーの構築を行います。
ここでのポイントは、user_dataにスクリプトを投げてある程度のことは自動化しているのと、後の作業でElastic IPの値を利用するため、outputでElastic IPを出力出来るようにしています

ec2.tf
resource "aws_key_pair" "this" {
  key_name   = "ftp-rsa"
  public_key = file("${path.module}/key_pair/id_rsa.pub")
}

data "aws_ssm_parameter" "amzn2_ami" {
  name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
}

resource "aws_instance" "this" {
  depends_on                  = [random_password.this, aws_ssm_parameter.this]
  ami                         = data.aws_ssm_parameter.amzn2_ami.value
  instance_type               = "t2.small"
  monitoring                  = true
  key_name                    = aws_key_pair.this.id
  vpc_security_group_ids      = [aws_security_group.this.id]
  associate_public_ip_address = true
  subnet_id                   = local.public_subnet_id
  user_data                   = <<EOF
#!/bin/bash
sudo yum update -y
sudo yum install vsftpd -y
sudo systemctl start vsftpd
sudo systemctl enable vsftpd
sudo adduser --home /home/ftp-user ftp-user
echo ${random_password.this.result} | sudo passwd --stdin ftp-user
sudo chown ftp-user:ftp-user -R /home/ftp-user/
sudo mv /etc/vsftpd/vsftpd.conf /etc/vsftpd/vsftpd.bk
sudo touch /etc/vsftpd/vsftpd.conf
sudo sh -c "cat << EOF >> /etc/vsftpd/vsftpd.conf
anonymous_enable=NO
local_enable=YES
write_enable=YES
local_umask=022
dirmessage_enable=NO
xferlog_enable=YES
connect_from_port_20=NO
xferlog_file=/var/log/vsftpd.log
xferlog_std_format=NO
ascii_upload_enable=YES
ascii_download_enable=YES
ls_recurse_enable=YES
listen=NO
listen_ipv6=YES
pam_service_name=vsftpd
userlist_enable=YES
tcp_wrappers=YES
pasv_enable=YES
pasv_addr_resolve=YES
# pasv_address=Elastic Terraform Apply後、固定IPに変更をする
pasv_min_port=60001
pasv_max_port=60010
use_localtime=YES
force_dot_files=YES
seccomp_sandbox=NO
allow_writeable_chroot=YES
EOF"
sudo systemctl restart vsftpd
EOF
  tags = {
    Name      = "ftp-server"
  }
}

resource "aws_eip" "this" {
  vpc      = true
  instance = aws_instance.this.id
}

output "eip" {
  value = aws_eip.this.public_ip
}

7. terraform apply

以下を実行して、問題ないことを確認していきましょう

$ terraform init

$ terraform fmt

$ terraform validate

# ここで出力されるリソースを問題ないか確認する
$ terraform plan

# planで問題なければ、apply
$ terraform apply

8. vsftpd.confを修正し、vsftpdを再起動

一部EC2内で手動で作業をする必要があり、以下のように変更します。

ec2.tf
- # pasv_address=Elastic Terraform Apply後、固定IPに変更をする
+ pasv_address=ElasticIPのIPを設定

なぜこの部分だけ手動で作業をするかというと、Terraformのec2.tfのコードでelastic ipはec2 instanceを以下のように参照しています

ec2.tf
resource "aws_eip" "this" {
  vpc      = true
  instance = aws_instance.this.id
}

そして、aws_instanceのスコープで以下のようにしてしまうと、双方で参照となり、循環参照となってしまいエラーとなってしまうため、vsftpd.confは手動で書き換える必要があります。

ec2.tf
pasv_address=aws_eip.this.public_ip
$ /path/to/sample

# 固定IPを確認
$ terraform output

$ ssh -i ./key_pair/id_rsa ec2-user@上で確認した固定IP

上記でFTPサーバーにSSH接続をしたら、vsftpd.confを編集します

$ sudo vi /etc/vsftpd/vsftpd.conf

pasv_addressをコメントインして、値に固定IPを設定します

ec2.tf
- # pasv_address=Elastic Terraform Apply後、固定IPに変更をする
+ pasv_address=ElasticIPのIPを設定

設定したら、保存して、vsftpdを再起動します

$ sudo systemctl restart vsftpd

上記でFTPサーバーの構築作業は完了です。

9. 接続確認

Macでftpコマンドで接続確認を行っていきます

ftpコマンドが使えない場合は、brewでインストールしてください
$ brew install tnftp

ftpへのログインパスワードは、AWS Web Console > System Manager > Parameter Storeで/ftp/user-passwordと検索をして、パラメータを確認が出来ます

# 以下でエンターしたらパスワードを聞かれるので、上記で取得したパスワードでログインを行う
$ ftp ftp-user@固定IP

これで接続が出来たかと思います

Discussion

ログインするとコメントできます