AnsibleでFTPSサーバ(vsftpd)を立ててみた
AWS事業本部 梶原@福岡オフィスです。
FTPSサーバを立てる機会があり、ちょっと証明書やらポート設定やらでつまづいてしまったので、次回一発でたてれるようにAnsible Playbookを作成しましたので共有します。
といっても、0からプレイブックを作成したわけでなく、vsFTPdを使用し、vsFTPdの設定はAnsible Galaxyに登録されている(https://galaxy.ansible.com/weareinteractive/vsftpd)を使用して、証明書の作成とvsftpdの設定を行っています。
では、Ansibleでもサクサクいきます。
事前準備
- ansibleのインストール(pip等でインストールを実施してください)
- AWS Configure等を実施してAWSの認証設定を行います。
- weareinteractive/vsftpd をAnsible Galaxyからインストール
$ ansible-galaxy install weareinteractive.vsftpd
roles(環境変数:roles_path)の配下にweareinteractive.vsftpd
また、依存しているweareinteractive.openssl
がダウンロードされます
EC2をAnsibleで立てる
せっかくなのでAnsibleでAWS上にEC2を立ててみようと思います。
すでに構築してあるEC2に対して実施する場合は本項はスキップしてください
構成はシンプルにパブリックサブネットにAmazon Linux2のインスタンスを立て、EIPを割り当てます。
Ansible Playbookでは
- 鍵作成
- VPC
- サブネット
- ルートテーブル
- インターネットゲートウェイ
- Elastic IP
- セキュリティグループ
- EC2(Amazon Linux2)
を行います。 セキュリティグループはSSH用の22番ポート、またFTPSように21番ポート, 50000-50010を開けています。 プレイブックは下記になります。 パラメータとして、秘密鍵またアクセス元のIPをvarsで定義しているので適時変更してください
- 秘密鍵:key_name
- アクセス元のIPアドレス:ipaddress
PlayBook(EC2作成)
# ansible-playbook aws-ftps.yml - hosts: localhost connection: local gather_facts: False become: False vars: region: ap-northeast-1 ipaddress: XXX.XXX.XXX.XXX/32 key_name: ec2-ftps tasks: - ec2_key: region: "{{ region }}" name: "{{ key_name }}" register: _key - copy: content: "{{ _key.key.private_key }}" dest: "~/.ssh/{{ _key.key.name }}.pem" mode: 0600 when: _key.changed - ec2_vpc_net: region: "{{ region }}" name: vpc-ftps cidr_block: 10.0.0.0/16 register: _vpc - ec2_vpc_igw: region: "{{ region }}" vpc_id: "{{ _vpc.vpc.id }}" register: _igw - ec2_vpc_subnet: region: "{{ region }}" vpc_id: "{{ _vpc.vpc.id }}" cidr: 10.0.0.0/24 register: _subnet - ec2_vpc_route_table: region: "{{ region }}" vpc_id: "{{ _vpc.vpc.id }}" tags: Name: Public subnets: - "{{ _subnet.subnet.id }}" routes: - dest: 0.0.0.0/0 gateway_id: "{{ _igw.gateway_id }}" register: _public_route_table # Security Group - ec2_group: region: "{{ region }}" vpc_id: "{{ _vpc.vpc.id }}" name: sg_ssh description: sg-ssh rules: - proto: tcp from_port: 22 to_port: 22 cidr_ip: "{{ ipaddress }}" register: _group_ssh - ec2_group: region: "{{ region }}" vpc_id: "{{ _vpc.vpc.id }}" name: sg_ftps description: sg-ftps rules: - proto: tcp from_port: 21 to_port: 21 cidr_ip: "{{ ipaddress }}" - proto: tcp from_port: 50000 to_port: 50010 cidr_ip: "{{ ipaddress }}" register: _group_ftps - ec2_instance: name: "ftps" key_name: "{{ key_name }}" vpc_subnet_id: "{{ _subnet.subnet.id }}" instance_type: t3.micro image_id: ami-0c3fd0f5d33134a76 security_groups: - "{{ _group_ssh.group_id}}" - "{{ _group_ftps.group_id}}" network: assign_public_ip: true register: _instance - ec2_eip: region: "{{ region }}" device_id: "{{ item }}" loop: "{{ _instance.instance_ids }}" register: _eip - debug: msg: "Allocated IP is {{ _eip.results[0].public_ip }}"
実行結果
ansible-playbook aws-ftps.yml <<略>> TASK [debug] ********************************************************************************************************************************************************************************************************************ok: [localhost] => { "msg": "Allocated IP is AAA.AAA.AAA.AAA" }
正常にAnsible Playbookが実行されますとEIPが割り当てられているので、(ここではAAA.AAA.AAA.AAA)こちらをhostsに設定します。
ssh_config
Host ftps Hostname AAA.AAA.AAA.AAA User ec2-user
hosts
[all] ftps
接続確認
$ ssh -F ssh_config ftps __| __|_ ) _| ( / Amazon Linux 2 AMI ___|\___|___| https://aws.amazon.com/amazon-linux-2/ No packages needed for security; 6 packages available Run "sudo yum update" to apply all updates. [ec2-user@ip-10-0-0-140 ~]$ logout
vsftpd-ftps.ymlを証明書の各値を環境、設定するドメインに合わせて書き換えてください。 各値の設定内容はOpenssl、またvsFTPDの設定項目に準拠しています。
主な設定内容としては
- openssl_self_signed: 部分の作成したい証明書の内容
- vsftpd_users: 接続するユーザ/パスワード
- vsftpd_config: vsftpd.confの内容そのままです。プロトコル等を変更してください
- pasv_addressは作成したEIPを設定します
になるかと思いますので、サーバ構成にあわせて変更お願いします。
vsftpd-ftps.yml
# ansible-playbook vsftpd-ftps.yml - hosts: all vars: # 自己証明書作成 openssl_keys_path: /etc/pki/tls/private openssl_certs_path: /etc/pki/tls/private openssl_csrs_path: /etc/pki/tls/private openssl_default_key_owner: root openssl_default_key_group: root openssl_default_cert_owner: root openssl_default_cert_group: root openssl_generate_csr: yes openssl_self_signed: - name: ftp.classmethod.info subject: C: JP ST: Tokyo L: Chiyoda-ku O: Classmethod,Inc. CN: ftp.classmethod.info emailAddress: hogefuga@classmethod.jp # FTP用 User 追加 vsftpd_users: - username: ftpuser name: FTP User password: "{{ 'ftpuser' | password_hash('sha256', 'mysecretsalt') }}" # パスワード変更 # vsFTPD設定 vsftpd_config: anonymous_enable: NO local_enable: YES write_enable: YES allow_writeable_chroot: YES dirmessage_enable: NO ascii_upload_enable: YES ascii_download_enable: YES chroot_local_user: YES tcp_wrappers: NO connect_from_port_20: NO xferlog_std_format: NO pasv_enable: YES pasv_addr_resolve: YES pasv_address: AAA.AAA.AAA.AAA # 割り当てられたElasticIpアドレスに変更 pasv_min_port: 50000 pasv_max_port: 50010 use_localtime: YES force_dot_files: YES # userlist_deny: NO listen: YES listen_ipv6: NO # FTP(s) 設定 ssl_enable: YES ssl_sslv2: NO ssl_sslv3: NO ssl_tlsv1: YES ssl_tlsv1_1: YES ssl_tlsv1_2: YES force_local_data_ssl: YES force_local_logins_ssl: YES # log_ftp_protocol: YES # ssl key vsftpd_key_file: "ftp.classmethod.info.key" # ssl cert vsftpd_cert_file: "ftp.classmethod.info.crt" roles: - weareinteractive.openssl - weareinteractive.vsftpd
Ansible Playbookの実行
各項目を変更してAnsible Playbookをながします。
ansible-playbook vsftpd-ftps.yml PLAY [all] ********************************************************************************************************************************************************************************************************************** TASK [Gathering Facts] **********************************************************************************************************************************************************************************************************ok: [ftps] TASK [weareinteractive.openssl : Including variables] ***************************************************************************************************************************************************************************ok: [ftps] => TASK [weareinteractive.openssl : Installing packages] ***************************************************************************************************************************************************************************changed: [ftps] TASK [weareinteractive.openssl : Installing python packages] ********************************************************************************************************************************************************************changed: [ftps] TASK [weareinteractive.openssl : Checking dir exists] ***************************************************************************************************************************************************************************ok: [ftps] TASK [weareinteractive.openssl : Creating directory] ****************************************************************************************************************************************************************************skipping: [ftps] TASK [weareinteractive.openssl : Checking dir exists] ***************************************************************************************************************************************************************************ok: [ftps] TASK [weareinteractive.openssl : Creating directory] ****************************************************************************************************************************************************************************skipping: [ftps] TASK [weareinteractive.openssl : Checking dir exists] ***************************************************************************************************************************************************************************ok: [ftps] TASK [weareinteractive.openssl : Creating directory] ****************************************************************************************************************************************************************************skipping: [ftps] TASK [weareinteractive.openssl : Configuring] ***********************************************************************************************************************************************************************************changed: [ftps] TASK [weareinteractive.openssl : Copying keys] ********************************************************************************************************************************************************************************** TASK [weareinteractive.openssl : Copying certs] ********************************************************************************************************************************************************************************* TASK [weareinteractive.openssl : Creating self-signed private keys] *************************************************************************************************************************************************************changed: [ftps] => (item={'name': 'ftp.classmethod.info', 'subject': {'C': 'JP', 'ST': 'Tokyo', 'L': 'Chiyoda-ku', 'O': 'Classmethod,Inc.', 'CN': 'ftp.classmethod.info', 'emailAddress': 'hogefuga@classmethod.jp'}}) TASK [weareinteractive.openssl : Creating self-signed CSRs] *********************************************************************************************************************************************************************changed: [ftps] => (item={'name': 'ftp.classmethod.info', 'subject': {'C': 'JP', 'ST': 'Tokyo', 'L': 'Chiyoda-ku', 'O': 'Classmethod,Inc.', 'CN': 'ftp.classmethod.info', 'emailAddress': 'hogefuga@classmethod.jp'}}) TASK [weareinteractive.openssl : Generate a Self Signed OpenSSL certificate.] ***************************************************************************************************************************************************changed: [ftps] => (item={'name': 'ftp.classmethod.info', 'subject': {'C': 'JP', 'ST': 'Tokyo', 'L': 'Chiyoda-ku', 'O': 'Classmethod,Inc.', 'CN': 'ftp.classmethod.info', 'emailAddress': 'hogefuga@classmethod.jp'}}) TASK [weareinteractive.openssl : Create directory for CAcert certificates] ******************************************************************************************************************************************************skipping: [ftps] TASK [weareinteractive.openssl : Download CAcert Class 1 PKI key] ***************************************************************************************************************************************************************skipping: [ftps] TASK [weareinteractive.openssl : Download CAcert Class 3 PKI key] ***************************************************************************************************************************************************************skipping: [ftps] TASK [weareinteractive.openssl : Update ca certificates to enable CAcert] *******************************************************************************************************************************************************skipping: [ftps] TASK [weareinteractive.vsftpd : Includeing OS specific variables] ***************************************************************************************************************************************************************ok: [ftps] TASK [weareinteractive.vsftpd : Installing packages] ****************************************************************************************************************************************************************************changed: [ftps] TASK [weareinteractive.vsftpd : Configuring vsftp] ******************************************************************************************************************************************************************************changed: [ftps] TASK [weareinteractive.vsftpd : Adding nologin shell] ***************************************************************************************************************************************************************************changed: [ftps] TASK [weareinteractive.vsftpd : Setting SELinux booleans] *********************************************************************************************************************************************************************** TASK [weareinteractive.vsftpd : Managing users] *********************************************************************************************************************************************************************************changed: [ftps] => (item={'username': 'ftpuser', 'name': 'FTP User', 'password': '$5$mysecretsalt$xxxxxxxxxxxxxxxxx'}) TASK [weareinteractive.vsftpd : Configuring service] ****************************************************************************************************************************************************************************changed: [ftps] RUNNING HANDLER [weareinteractive.vsftpd : restart vsftpd] **********************************************************************************************************************************************************************changed: [ftps] PLAY RECAP **********************************************************************************************************************************************************************************************************************ftps : ok=18 changed=12 unreachable=0 failed=0
接続確認
各FTPクライアントから接続確認を実施します。 とくに特別なクライアントからじゃないと接続できないとかいうことはないので割愛します。 注意事項としては
- クライアントがサポートしているプロトコルの確認
- 自己証明を使用しているためクライアント側で証明書のチェックを無効
- passiveモードでの接続
などをご注意ください。
まとめ
SFTPについては、AWSにはマネージドサービスのAWS Transfer for SFTP(https://aws.amazon.com/jp/sftp/) があるのですが、FTPまたFTPSサーバを立てる機会もまだあるかと思います。 また 【AWS】AWSでFTPサーバー立てる時に気をつけるべき2つのこと+α に沿って気をつけても、結構設定もれしていたりして子ハマりするので、次回からはサクッと終わらせれそうです。
参考
weareinteractive/ansible-vsftpd https://github.com/weareinteractive/ansible-vsftpd