Amazon EFSのマウントターゲットのカスタムドメインにEFSマウントヘルパーを使ってマウントできるのか確認してみた

EFSマウントヘルパーの沼にハマってみました
2023.07.04

カスタムドメインでマウントする時もEFSマウントヘルパーを使いたいな

こんにちは、のんピ(@non____97)です。

皆さんはAmazon EFSのカスタムドメインに対してEFSマウントヘルパーでマウントしたいなと思ったことはありますか? 私はあります。

EFSマウントヘルパーを使うことで得られる主なメリットは以下の4つ考えています。

  1. IAM認証が使用できる
  2. EFS用に最適化されたマウントオプションを自動で使用してくれる
  3. 転送中の暗号化を簡単に実装できる
  4. マウント時のログが出力される

EFSマウントヘルパーを使える環境であれば、EFSマウントヘルパーを使わない手はないですね。

EFSマウントヘルパーを使ってマウントする際は以下の3つの方法でEFSを指定します。

  • ファイルシステム DNS 名 — ファイルシステム DNS 名を使用しても、マウントヘルパーがそれを解決できない場合、たとえばファイルシステムを別の VPC にマウントする場合、マウントターゲット IP アドレスの使用にフォールバックします。詳細については、「別の AWS アカウント または VPC から EFS ファイルシステムをマウントする」を参照してください。
  • ファイルシステム ID — ファイルシステム ID を使用する場合、マウントヘルパーは、外部リソースを呼び出さずに、マウントターゲットの elastic network interface (ENI) のローカル IP アドレスを解決します。
  • マウントターゲット IP アドレス – ファイルシステムのマウントターゲットのいずれかの IP アドレスを使用できます。

EFS マウントヘルパーを使用して EFS ファイルシステムをマウントする - Amazon Elastic File System

ここで、ふとカスタムドメインでも接続できるのかが気になりました。

EFSファイルシステムのDNS名は、そのEFSファイルシステムのVPC内のRoute 53 Resolverでなければ名前解決できません。

そのため、別VPCからEFSファイルシステムのDNS名を指定してマウントしようとしたり、オンプレのDNSサーバーを参照している状態でマウントしようとしたりすると名前解決に失敗し、接続できません。

AWS公式ドキュメントには「手動でマウントターゲットの名前解決をしろ」と記載があります。

EFS マウントヘルパーを使用し、NFS クライアントおよび EFS アクセスポイントに対して IAM 認証を使用することで、Amazon EFS ファイルシステムをマウントできます。既定では、EFS マウントヘルパーは、ドメインネームサービス (DNS) を使用して、EFS マウントターゲットの IP アドレスを解決します。別のアカウントまたは Virtual Private Cloud (VPC) からファイルシステムをマウントする場合は、EFS マウントターゲットを手動で解決する必要があります。

EFS マウントヘルパーを使用して EFS ファイルシステムをマウントする - Amazon Elastic File System

では、DNSサーバーのレコードとしてマウントターゲットのIPアドレスを登録し、そのカスタムドメインを指定してEFSマウントヘルパーを使う場合は正常に接続できるのでしょうか。

気になって朝4時半に起きたので検証します。

いきなりまとめ

  • Amazon EFSのカスタムドメインにEFSマウントヘルパーを使ってマウントできる
    • ただし、ファイルシステムIDを指定する必要がある
  • 特段カスタムドメインを使うことによるメリットは薄い
  • 存在しないマウントターゲットのIPアドレスを指定する場合、存在しないファイルシステムIDを指定しても接続できる
    • マウントターゲットのIPアドレスとファイルシステムIDの整合性チェックは行われない
    • ただし、存在しないファイルシステムのIDであるためIAM認証はできない
    • TLS接続はできる

検証環境

検証環境は以下の通りです。

Amazon EFSのカスタムドメインにEFSマウントヘルパーを使ってマウントできるのか確認してみた検証環境構成図

VPCが2つあり、VPC間はVPCピアリングで接続しています。

それぞれのEC2インスタンスからEFSファイルシステムに対してEFSマウントヘルパーでマウントしにいきます。

Route 53 Private Hosted Zoneのレコードは以下の通りです。

Route 53 Private Hosted Zone

EFSファイルシステムのマウントターゲットのIPアドレス(10.0.0.206)をAレコードとして登録したものと、EFSファイルシステムのDNS名をCNAMEレコードとして登録したものの2つ用意しました。

EFSマウントヘルパーはdnfでインストールしました。

$ sudo dnf install amazon-efs-utils -y
Last metadata expiration check: 0:13:38 ago on Mon Jul  3 09:06:53 2023.
Dependencies resolved.
==============================================================================================================================================================
 Package                                 Architecture                  Version                                       Repository                          Size
==============================================================================================================================================================
Installing:
 amazon-efs-utils                        noarch                        1.35.0-1.amzn2023                             amazonlinux                         56 k
Installing dependencies:
 stunnel                                 x86_64                        5.58-1.amzn2023.0.2                           amazonlinux                        156 k

Transaction Summary
==============================================================================================================================================================
Install  2 Packages

Total download size: 212 k
Installed size: 556 k
Downloading Packages:
(1/2): amazon-efs-utils-1.35.0-1.amzn2023.noarch.rpm                                                                          734 kB/s |  56 kB     00:00
(2/2): stunnel-5.58-1.amzn2023.0.2.x86_64.rpm                                                                                 1.1 MB/s | 156 kB     00:00
--------------------------------------------------------------------------------------------------------------------------------------------------------------
Total                                                                                                                         1.1 MB/s | 212 kB     00:00
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
  Preparing        :                                                                                                                                      1/1
  Installing       : stunnel-5.58-1.amzn2023.0.2.x86_64                                                                                                   1/2
  Running scriptlet: stunnel-5.58-1.amzn2023.0.2.x86_64                                                                                                   1/2
  Installing       : amazon-efs-utils-1.35.0-1.amzn2023.noarch                                                                                            2/2
  Running scriptlet: amazon-efs-utils-1.35.0-1.amzn2023.noarch                                                                                            2/2
  Verifying        : stunnel-5.58-1.amzn2023.0.2.x86_64                                                                                                   1/2
  Verifying        : amazon-efs-utils-1.35.0-1.amzn2023.noarch                                                                                            2/2

Installed:
  amazon-efs-utils-1.35.0-1.amzn2023.noarch                                         stunnel-5.58-1.amzn2023.0.2.x86_64

Complete!

VPC AのEC2インスタンスからマウント

ファイルシステムIDを指定してEFSマウントヘルパーでマウント

まずは、ファイルシステムIDを指定してEFSマウントヘルパーでマウントをしてみます。

# マウントポイントの作成
$ sudo mkdir -p /mount/efs

# ファイルシステムIDを指定してEFSマウントヘルパーでマウント
$ sudo mount -t efs -o tls,iam fs-0e211df86678573e6 /mount/efs

# マウントできているか確認
$ df -hT -t nfs4
Filesystem     Type  Size  Used Avail Use% Mounted on
127.0.0.1:/    nfs4  8.0E     0  8.0E   0% /mount/efs

マウントできましたね。

ついでに書き込みができることも確認します。

$ sudo touch /mount/efs/test

$ ls -l /mount/efs/
total 4
-rw-r--r--. 1 root root 0 Jul  3 09:23 test

問題なくできますね。

ファイルシステムのDNS名を指定してEFSマウントヘルパーでマウント

続いて、ファイルシステムのDNS名を指定してEFSマウントヘルパーでマウントします。

# ファイルシステムのDNS名を指定してEFSマウントヘルパーでマウント
$ sudo mount -t efs -o tls,iam fs-0e211df86678573e6.efs.us-east-1.amazonaws.com /mount/efs

$ df -hT -t nfs4
Filesystem     Type  Size  Used Avail Use% Mounted on
127.0.0.1:/    nfs4  8.0E     0  8.0E   0% /mount/efs

$ ls -l /mount/efs/
total 4
-rw-r--r--. 1 root root 0 Jul  3 09:23 test

こちらも何事もなくマウントできましたね。

マウントターゲットのIPアドレスを指定してEFSマウントヘルパーでマウント

次に、マウントターゲットのIPアドレスを指定してEFSマウントヘルパーでマウントします。

まず、マウントターゲットのIPアドレスを確認します。

$ dig fs-0e211df86678573e6.efs.us-east-1.amazonaws.com +short
10.0.0.206

10.0.0.206でした。

こちらのIPアドレスをマウントします。

$ sudo mount -t efs -o tls,iam 10.0.0.206 /mount/efs
The specified CNAME "10.0.0.206" did not resolve to a valid DNS name for an EFS mount target. Please refer to the EFS documentation for mounting with DNS names for examples: https://docs.aws.amazon.com/efs/latest/ug/mounting-fs-mount-cmd-dns-name.html

$ sudo mount -t efs -o tls 10.0.0.206 /mount/efs
The specified CNAME "10.0.0.206" did not resolve to a valid DNS name for an EFS mount target. Please refer to the EFS documentation for mounting with DNS names for examples: https://docs.aws.amazon.com/efs/latest/ug/mounting-fs-mount-cmd-dns-name.html

「そんなCNAMEはない」と怒られてしまいました。

ログを確認してみましょう。

$ cat /var/log/amazon/efs/mount.log
2023-07-03 09:21:08 UTC - INFO - version=1.35.0 options={'rw': None, 'tls': None, 'iam': None}
2023-07-03 09:21:09 UTC - INFO - binding 20326
2023-07-03 09:21:09 UTC - WARNING - stunnel does not support "b'libwrap'"
2023-07-03 09:21:09 UTC - INFO - Starting TLS tunnel: "/usr/bin/stunnel /var/run/efs/stunnel-config.fs-0e211df86678573e6.mount.efs.20326"
2023-07-03 09:21:09 UTC - INFO - Started TLS tunnel, pid: 25419
2023-07-03 09:21:09 UTC - INFO - Executing: "/sbin/mount.nfs4 127.0.0.1:/ /mount/efs -o rw,nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,port=20326" with 15 sec time limit.
2023-07-03 09:21:24 UTC - ERROR - Mounting fs-0e211df86678573e6.efs.us-east-1.amazonaws.com to /mount/efs failed due to timeout after 15 sec, mount attempt 1/3, wait 0 sec before next attempt.
2023-07-03 09:21:24 UTC - INFO - Executing: "/sbin/mount.nfs4 127.0.0.1:/ /mount/efs -o rw,nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,port=20326" with 15 sec time limit.
2023-07-03 09:21:39 UTC - ERROR - Mounting fs-0e211df86678573e6.efs.us-east-1.amazonaws.com to /mount/efs failed due to timeout after 15 sec, mount attempt 2/3, wait 0 sec before next attempt.
2023-07-03 09:21:39 UTC - INFO - Executing: "/sbin/mount.nfs4 127.0.0.1:/ /mount/efs -o rw,nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,port=20326"
2023-07-03 09:21:53 UTC - INFO - Successfully mounted fs-0e211df86678573e6.efs.us-east-1.amazonaws.com at /mount/efs
2023-07-03 09:26:28 UTC - INFO - version=1.35.0 options={'rw': None, 'tls': None, 'iam': None}
2023-07-03 09:26:28 UTC - INFO - binding 21008
2023-07-03 09:26:28 UTC - WARNING - stunnel does not support "b'libwrap'"
2023-07-03 09:26:28 UTC - INFO - Starting TLS tunnel: "/usr/bin/stunnel /var/run/efs/stunnel-config.fs-0e211df86678573e6.mount.efs.21008"
2023-07-03 09:26:28 UTC - INFO - Started TLS tunnel, pid: 26031
2023-07-03 09:26:28 UTC - INFO - Executing: "/sbin/mount.nfs4 127.0.0.1:/ /mount/efs -o rw,nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,port=21008" with 15 sec time limit.
2023-07-03 09:26:28 UTC - INFO - Successfully mounted fs-0e211df86678573e6.efs.us-east-1.amazonaws.com at /mount/efs
2023-07-03 09:28:12 UTC - ERROR - The specified CNAME "10.0.0.206" did not resolve to a valid DNS name for an EFS mount target. Please refer to the EFS documentation for mounting with DNS names for examples: https://docs.aws.amazon.com/efs/latest/ug/mounting-fs-mount-cmd-dns-name.html
2023-07-03 09:32:34 UTC - ERROR - The specified CNAME "10.0.0.206" did not resolve to a valid DNS name for an EFS mount target. Please refer to the EFS documentation for mounting with DNS names for examples: https://docs.aws.amazon.com/efs/latest/ug/mounting-fs-mount-cmd-dns-name.html

特に目新しい情報はないですね。

EFSマウントヘルパーのマニュアルを見てみましょう。

# マニュアルをファイルに出力
$ man mount.efs  | col -bfx > mount.efs.txt

# EFSマウントヘルパーのマニュアル確認
$ cat mount.efs.txt
EFS(8)                                                            System Manager's Manual                                                           EFS(8)

NAME
       mount.efs - Mount helper for using Amazon EFS file systems.

SYNOPSIS
       mount.efs fs-id-or-dns-name mount-point [-o options]

DESCRIPTION
       mount.efs is part of the amazon-efs-utils package, which simplifies using EFS file systems.

       mount.efs is meant to be used through the mount(8) command for mounting EFS file systems.

       fs-id-or-dns-name has to be of one of the following two forms:

       •      An EFS filesystem ID in the form of "fs-abcd1234", generated when the file system is created.

       •      A  domain  name  that  has  a  resolvable  DNS-CNAME  record,  which  in  turn  points  to  a  fully-qualified  EFS  DNS name in the form of
              "fs-abcd1234.efs.us-east-1.amazonaws.com" or "us-east-1a.fs-abcd1234.efs.us-east-1.amazonaws.com".

       mount-point is the local directory on which the file system will be mounted.

       mount.efs automatically applies the following NFS options:

           nfsvers=4.1
           rsize=1048576
           wsize=1048576
           hard
           timeo=600
           retrans=2
           noresvport
           tls (for Mac distributions)

       By default, when using the Amazon EFS mount helper with Transport Layer Security (TLS), the mount helper enforces the certificate hostname checking
       and  disables  the  use  of  Online  Certificate  Status  Protocol  (OCSP). These options can be configured in the config file located at /etc/ama‐
       zon/efs/efs-utils.conf.

       Additionally, the Amazon EFS mount helper has built-in logging for troubleshooting purposes. These logs are located at /var/log/amazon/efs.

       It is possible to configure your Amazon EC2 instance to automatically remount your Amazon EFS file system when it reboots.  For  more  information,
       see the online documentation at: https://docs.aws.amazon.com/efs/latest/ug/mount-fs-auto-mount-onreboot.html.

OPTIONS
       -o,  Options are specified with a -o flag followed by a comma separated string of options. All of the options specified in nfs(5) are available, in
       addition to the following EFS-specific options:

           tls    Mounts the EFS file system over TLS. For EC2 instances using Mac distributions, this option is by default passed and the EFS file system
                  is mounted over TLS.

           notls  Mounts the EFS file system without TLS, applies for Mac distributions only.

           tlsport=n
                  Configure  the  TLS  relay  to listen on the specified port. By default, the tlsport is choosing randomly from port range defined in the
                  config file located at /etc/amazon/efs/efs-utils.conf.

           verify=n
                  Verify TLS certificates using the specified stunnel verify level. For more information, see stunnel(8).

           ocsp / noocsp
                  Selects whether to perform OCSP validation on TLS certificates, overriding /etc/amazon/efs/efs-utils.conf. By default OCSP is  disabled.
                  For more information, see stunnel(8).

           iam    Use the system's IAM identity to authenticate with EFS. The mount helper will try to retrieve the required IAM credentials from the fol‐
                  lowing locations: the aws credentials URI passed by mount option, the AWS CLI credentials file (~/.aws/credentials),  and  the  AWS  CLI
                  config file (~/.aws/config), the AWS_CONTAINER_CREDENTIALS_RELATIVE_URI environment variable, the AssumeRoleWithWebIdentity, the EC2 in‐
                  stance profile.  The first location that has credentials will be used. This option requires the tls option.

           rolearn
                  Role ARN for IAM authentication with AssumeRoleWithWebIdentity API.

           jwtpath
                  Identity token for IAM authentication with AssumeRoleWithWebIdentity API.

           accesspoint
                  Mount the EFS file system using the specified access point. This option requires the tls option. The access point must be in the "avail‐
                  able" state before it can be used to mount EFS.

           awsprofile
                  Use  the  named  profile  used  to  lookup  IAM  credentials in the AWS CLI credentials file (~/.aws/credentials) or AWS CLI config file
                  (~/.aws/config). If botocore is installed, assume the named profile and use the credentials of the assumed profile. If  "awsprofile"  is
                  not specified, the "default" profile is used.

           awscredsuri
                  Use the relative uri to lookup IAM credentials from ecs task metadata endpoint.

           cafile Use the cafile as the stunnel certificate authority file.

           netns  Mount the EFS file system to the specified network namespace.

           az     Mount the EFS file system to the specified availability zone mount target.

           mountport
                  Use the port 2049 to bypass portmapper daemon on EC2 Mac instances running macOS Big Sur.

           mounttargetip
                  Mount the EFS file system to the specified mount target ip address.

EXAMPLES
       sudo mount -t efs fs-abcd1234 /mnt/efs
              Mount an EFS file system with file system ID "fs-abcd1234" at mount point "/mnt/efs" without encryption of data in transit.

       sudo mount -t efs -o mounttargetip=192.0.0.1 /mnt/efs
              Mount  an  EFS  file  system  with file system ID "fs-abcd1234" on the mount target that belongs to the file system with address "192.0.0.1"
              without encryption of data in transit.

       sudo mount -t efs -o netns=/proc/1/net/ns fs-abcd1234 /mnt/efs
              Mount an EFS file system with file system ID "fs-abcd1234" at mount point "/mnt/efs" without encryption of data in transit in given  network
              namespace '/proc/1/net/ns'

       sudo mount -t efs -o az=us-east-1a fs-abcd1234 /mnt/efs
              Mount an EFS file system with file system ID "fs-abcd1234" at mount point "/mnt/efs" to the mount target in availability zone us-east-1a

       sudo mount -t efs fs-abcd1234:/child /mnt/efs
              Mount  a  non-root directory of an EFS file system with file system ID "fs-abcd1234" at mount point "/mnt/efs" without encryption of data in
              transit.

       sudo mount -t efs -o tls fs-abcd1234 /mnt/efs
              Mount an EFS file system with file system ID "fs-abcd1234" at mount point "/mnt/efs" using encryption of data in transit.

       sudo mount -t efs -o tls,verify=0 fs-abcd1234 /mnt/efs
              Mount an EFS file system with file system ID "fs-abcd1234" at mount point "/mnt/efs" using encryption of data in transit and a verify  level
              of 0.

       sudo mount -t efs -o tls,ocsp fs-abcd1234 /mnt/efs
              Mount an EFS file system with file system ID "fs-abcd1234" at mount point "/mnt/efs" using encryption of data in transit and with OCSP vali‐
              dation enabled.

       sudo mount -t efs custom-cname.example.com /mnt/efs
              Mount an EFS file system using the custom DNS name "custom-cname.example.com" — which has to resolve to a fully-qualified EFS DNS name  such
              as "fs-abcd1234.efs.us-east-1.amazonaws.com" — at mount point "/mnt/efs" without encryption of data in transit.

       sudo mount -t efs -o tls custom-cname.example.com /mnt/efs
              Mount  an EFS file system using the custom DNS name "custom-cname.example.com" — which has to resolve to a fully-qualified EFS DNS name such
              as "fs-abcd1234.efs.us-east-1.amazonaws.com" — at mount point "/mnt/efs" using encryption of data in transit.

       sudo mount -t efs -o tls,iam fs-abcd1234 /mnt/efs
              Mount an EFS file system with file system ID "fs-abcd1234" at mount point "/mnt/efs" with encryption of data in transit.  The  mount  helper
              will authenticate with EFS using the system's IAM identity.

       sudo mount -t efs -o tls,iam,rolearn="ROLE_ARN",jwtpath="PATH/JWT_TOKEN_FILE" fs-abcd1234 /mnt/efs
              Mount  an  EFS  file system with file system ID "fs-abcd1234" at mount point "/mnt/efs" with encryption of data in transit. The mount helper
              will assume the role "ROLE_ARN" by calling the AssumeRoleWithWebIdentity API with the identity token at "PATH/JWT_TOKEN_FILE".

       sudo mount -t efs -o tls,iam,awsprofile=test-profile fs-abcd1234 /mnt/efs
              Mount an EFS file system with file system ID "fs-abcd1234" at mount point "/mnt/efs" with encryption of data in transit.  The  mount  helper
              will authenticate with EFS using the system's IAM identity named profile "test profile", for which the credentials are retrieved either from
              /root/.aws/credentials or /root/.aws/config. If the credentials are not present in the credentials or config files, and there is a "[profile
              test-profile]"  section  in  the  /root/.aws/config file, the mount helper will assume the named profile "test-profile" based on the profile
              section configuration in root/.aws/config and use the credentials retrieved with botocore to mount (botocore must be pre-installed).

       sudo mount -t efs -o tls,accesspoint=fsap-12345678 fs-abcd1234 /mnt/efs
              Mount an EFS file system with file system ID "fs-abcd1234" at mount point "/mnt/efs" with encryption of data in transit. The file system  is
              mounted using the access point "fsap-12345678".

FILES
       /sbin/mount.efs
              The executable for the Amazon EFS mount helper.

       /usr/bin/amazon-efs-mount-watchdog
              The executable for the supervisor process that monitors the network relay.

       /etc/amazon/efs/efs-utils.conf
              The configuration file for the Amazon EFS mount helper.

       /etc/amazon/efs/efs-utils.crt
              The default Certificate Authority file used by the Amazon EFS mount helper.

       /etc/init/amazon-efs-mount-watchdog.conf
              The configuration file for the supervisor process.

       /var/log/amazon/efs/
              The directory where logs for the Amazon EFS mount helper, the stunnel network relay, and the supervisor process are stored.

       /usr/share/man/man8/mount.efs.8
              The man page for the Amazon EFS mount helper.

NOTES
       For more information on using the amazon-efs-utils package, see https://docs.aws.amazon.com/efs/latest/ug/using-amazon-efs-utils.html in the Amazon
       EFS User Guide.

       The paths on EC2 MacOS instances are relocated under /usr/local/Cellar/amazon-efs-utils/<version>/libexec directory.

SEE ALSO
       nfs(8), stunnel(8), fstab(5)

COPYING
       Copyright 2017-2018 Amazon.com, Inc. and its affiliates. All Rights Reserved.

例にsudo mount -t efs -o mounttargetip=192.0.0.1 /mnt/efsとある通り、mounttargetipをオプションとして指定する必要があるようですね。

試してみましょう。

$ sudo mount -t efs -o tls,iam,mounttargetip=10.0.0.206 /mount/efs
mount: /mount/efs: can't find in /etc/fstab.

$ sudo mount -t efs -o mounttargetip=10.0.0.206 /mount/efs
mount: /mount/efs: can't find in /etc/fstab.

マウントできないですね。

もう少し調べてみると、どうやらマウントターゲットのIPアドレスを指定する際はファイルシステムIDも一緒に指定する必要があるようです。

  • マウントターゲット IP アドレスを使用してマウントするには:
sudo mount -t efs -o tls,mounttargetip=mount-target-ip file-system-id efs-mount-point/
sudo mount -t efs -o tls,mounttargetip=192.0.2.0 fs-abcd123456789ef0 efs/

EFS マウントヘルパーを使用した Amazon EC2 Linux インスタンスをマウントする - Amazon Elastic File System

実際に試してみましょう。

$ sudo mount -t efs -o tls,iam,mounttargetip=10.0.0.206 fs-0e211df86678573e6 /mount/efs

$ df -hT -t nfs4
Filesystem     Type  Size  Used Avail Use% Mounted on
127.0.0.1:/    nfs4  8.0E     0  8.0E   0% /mount/efs

$ ls -l /mount/efs/
total 4
-rw-r--r--. 1 root root 0 Jul  3 09:23 test

マウントできましたね。

ログも確認してみましょう。

$ cat /var/log/amazon/efs/mount.log
.
.
(中略)
.
.
2023-07-03 09:51:17 UTC - INFO - version=1.35.0 options={'rw': None, 'tls': None, 'iam': None, 'mounttargetip': '10.0.0.206'}
2023-07-03 09:51:17 UTC - INFO - Use the mount target ip address 10.0.0.206 provided in the mount options to mount.
2023-07-03 09:51:17 UTC - INFO - binding 20597
2023-07-03 09:51:17 UTC - WARNING - stunnel does not support "b'libwrap'"
2023-07-03 09:51:17 UTC - INFO - Starting TLS tunnel: "/usr/bin/stunnel /var/run/efs/stunnel-config.fs-0e211df86678573e6.mount.efs.20597"
2023-07-03 09:51:17 UTC - INFO - Started TLS tunnel, pid: 27191
2023-07-03 09:51:17 UTC - INFO - Executing: "/sbin/mount.nfs4 127.0.0.1:/ /mount/efs -o rw,nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,port=20597" with 15 sec time limit.
2023-07-03 09:51:17 UTC - INFO - Successfully mounted fs-0e211df86678573e6.efs.us-east-1.amazonaws.com at /mount/efs

マウントターゲットのIPアドレスが確かに指定されていることが分かります。

EFSファイルシステムのDNS名のCNAMEレコードであるカスタムドメインを指定してEFSマウントヘルパーでマウント

次に、EFSファイルシステムのDNS名のCNAMEレコードであるカスタムドメインを指定してEFSマウントヘルパーでマウントします。

まず、名前解決できることを確認します。

$ dig efs-cname.corp.non-97 +short
fs-0e211df86678573e6.efs.us-east-1.amazonaws.com.
10.0.0.206

名前解決できますね。

# EFSファイルシステムのDNS名のCNAMEレコードであるカスタムドメインを指定してEFSマウントヘルパーでマウント
$ sudo mount -t efs -o tls,iam efs-cname.corp.non-97 /mount/efs

$ df -hT -t nfs4
Filesystem     Type  Size  Used Avail Use% Mounted on
127.0.0.1:/    nfs4  8.0E     0  8.0E   0% /mount/efs

$ ls -l /mount/efs/
total 4
-rw-r--r--. 1 root root 0 Jul  3 09:23 test

# マウント時のログ確認
$ tail /var/log/amazon/efs/mount.log
2023-07-03 09:51:17 UTC - INFO - Started TLS tunnel, pid: 27191
2023-07-03 09:51:17 UTC - INFO - Executing: "/sbin/mount.nfs4 127.0.0.1:/ /mount/efs -o rw,nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,port=20597" with 15 sec time limit.
2023-07-03 09:51:17 UTC - INFO - Successfully mounted fs-0e211df86678573e6.efs.us-east-1.amazonaws.com at /mount/efs
2023-07-03 10:00:42 UTC - INFO - version=1.35.0 options={'rw': None, 'tls': None, 'iam': None}
2023-07-03 10:00:42 UTC - INFO - binding 20752
2023-07-03 10:00:42 UTC - WARNING - stunnel does not support "b'libwrap'"
2023-07-03 10:00:42 UTC - INFO - Starting TLS tunnel: "/usr/bin/stunnel /var/run/efs/stunnel-config.fs-0e211df86678573e6.mount.efs.20752"
2023-07-03 10:00:42 UTC - INFO - Started TLS tunnel, pid: 27786
2023-07-03 10:00:42 UTC - INFO - Executing: "/sbin/mount.nfs4 127.0.0.1:/ /mount/efs -o rw,nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,port=20752" with 15 sec time limit.
2023-07-03 10:00:42 UTC - INFO - Successfully mounted fs-0e211df86678573e6.efs.us-east-1.amazonaws.com at /mount/efs

問題なくマウントできました。

ログを確認したところ、ファイルシステムのDNS名を認識しているようですね。

マウントターゲットのIPアドレスのAレコードであるカスタムドメインを指定してEFSマウントヘルパーでマウント

続いて、マウントターゲットのIPアドレスのAレコードであるカスタムドメインを指定してEFSマウントヘルパーでマウントします。

まず、名前解決できることを確認します。

$ dig efs-a.corp.non-97 +short
10.0.0.206

名前解決できますね。

# マウントターゲットのIPアドレスのAレコードであるカスタムドメインを指定してEFSマウントヘルパーでマウント
$ sudo mount -t efs -o tls,iam efs-cname.corp.non-97 /mount/efs

$ sudo mount -t efs -o tls,iam efs-a.corp.non-97 /mount/efs
The specified CNAME "efs-a.corp.non-97" did not resolve to a valid DNS name for an EFS mount target. Please refer to the EFS documentation for mounting with DNS namesfor examples: https://docs.aws.amazon.com/efs/latest/ug/mounting-fs-mount-cmd-dns-name.html

「そんなCNAMEは名前解決できない」と怒られてしまいました。

ファイルシステムのDNS名以外のAレコードをDNS名として指定することはできないようですね。

ちなみに、EFSマウントヘルパーを使わずに普通にNFSv4でマウントする場合は、問題なくマウントできます。

$ sudo mount -t nfs4 efs-a.corp.non-97:/ /mount/efs

$ df -hT -t nfs4
Filesystem          Type  Size  Used Avail Use% Mounted on
efs-a.corp.non-97:/ nfs4  8.0E     0  8.0E   0% /mount/efs

$ ls -l /mount/efs
total 4
-rw-r--r--. 1 root root 0 Jul  3 09:23 test

マウントターゲットのIPアドレスのAレコードであるカスタムドメインとファイルシステムIDを指定してEFSマウントヘルパーでマウント

どうにかして対応する方法はないものかと思ったので、EFSマウントヘルパーのソースコードを覗いてみます。

The specified CNAME xxx did not resolve to a valid DNS name for an EFS mount targetというメッセージを出力している箇所を探すとmatch_device()というEFSファイルシステムのIDとパス、AZを返す関数がありました。

efs-utils/src/mount_efs/__init__.py

def match_device(config, device, options):
    """Return the EFS id, the remote path, and the az to mount"""

    try:
        remote, path = device.split(":", 1)
    except ValueError:
        remote = device
        path = "/"

    if FS_ID_RE.match(remote):
        return remote, path, None

    try:
        primary, secondaries, _ = socket.gethostbyname_ex(remote)
        hostnames = list(filter(lambda e: e is not None, [primary] + secondaries))
    except socket.gaierror:
        create_default_cloudwatchlog_agent_if_not_exist(config, options)
        fatal_error(
            'Failed to resolve "%s" - check that the specified DNS name is a CNAME record resolving to a valid EFS DNS '
            "name" % remote,
            'Failed to resolve "%s"' % remote,
        )

    if not hostnames:
        create_default_cloudwatchlog_agent_if_not_exist(config, options)
        fatal_error(
            'The specified domain name "%s" did not resolve to an EFS mount target'
            % remote
        )

    for hostname in hostnames:
        efs_fqdn_match = EFS_FQDN_RE.match(hostname)

        if efs_fqdn_match:
            az = efs_fqdn_match.group("az")
            fs_id = efs_fqdn_match.group("fs_id")

            if az and "az" in options and az != options["az"]:
                fatal_error(
                    'The hostname "%s" resolved by the specified domain name "%s" does not match the az provided in the '
                    "mount options, expected = %s, given = %s"
                    % (hostname, remote, options["az"], az)
                )

            expected_dns_name, _ = get_dns_name_and_fallback_mount_target_ip_address(
                config, fs_id, add_field_in_options(options, "az", az)
            )

            # check that the DNS name of the mount target matches exactly the DNS name the CNAME resolves to
            if hostname == expected_dns_name:
                return fs_id, path, az
    else:
        create_default_cloudwatchlog_agent_if_not_exist(config, options)
        fatal_error(
            'The specified CNAME "%s" did not resolve to a valid DNS name for an EFS mount target. '
            "Please refer to the EFS documentation for mounting with DNS names for examples: %s"
            % (
                remote,
                "https://docs.aws.amazon.com/efs/latest/ug/mounting-fs-mount-cmd-dns-name.html",
            )
        )

DNS名がEFS_FQDN_REとマッチしない場合は上述のエラー分のステートに遷移するようですね。

ちなみにFS_ID_REEFS_FQDN_REは以下の通り、ファイルシステムのIDやファイルシステムのDNS名の正規表現です。

efs-utils/src/mount_efs/__init__.py

FS_ID_RE = re.compile("^(?P<fs_id>fs-[0-9a-f]+)$")
EFS_FQDN_RE = re.compile(
    r"^((?P<az>[a-z0-9-]+)\.)?(?P<fs_id>fs-[0-9a-f]+)\.efs\."
    r"(?P<region>[a-z0-9-]+)\.(?P<dns_name_suffix>[a-z0-9.]+)$"
)

match_device()を呼び出しているのはparse_arguments()です。

efs-utils/src/mount_efs/__init__.py

def parse_arguments(config, args=None):
    """Parse arguments, return (fsid, path, mountpoint, options)"""
    if args is None:
        args = sys.argv

    fsname = None
    mountpoint = None
    options = {}

    if not check_if_platform_is_mac():
        if len(args) > 1:
            fsname = args[1]
        if len(args) > 2:
            mountpoint = args[2]
        if len(args) > 4 and "-o" in args[:-1]:
            options_index = args.index("-o") + 1
            options = parse_options(args[options_index])
    else:
        if len(args) > 1:
            fsname = args[-2]
        if len(args) > 2:
            mountpoint = args[-1]
        if len(args) > 4 and "-o" in args[:-2]:
            for arg in args[1:-2]:
                if arg != "-o":
                    options.update(parse_options(arg))

    if not fsname or not mountpoint:
        usage(out=sys.stderr)

    # We treat az as an option when customer is using dns name of az mount target to mount,
    # even if they don't provide az with option, we update the options with that info
    fs_id, path, az = match_device(config, fsname, options)

    return fs_id, path, mountpoint, add_field_in_options(options, "az", az)

そして、parse_arguments()main()で呼び出されています。

efs-utils/src/mount_efs/__init__.py

def main():
    parse_arguments_early_exit()

    assert_root()

    config = read_config()
    bootstrap_logging(config)

    if check_if_platform_is_mac() and not check_if_mac_version_is_supported():
        fatal_error(
            "We do not support EFS on MacOS Kernel version " + platform.release()
        )

    fs_id, path, mountpoint, options = parse_arguments(config)

    logging.info("version=%s options=%s", VERSION, options)

    global CLOUDWATCHLOG_AGENT
    CLOUDWATCHLOG_AGENT = bootstrap_cloudwatch_logging(config, options, fs_id)

    check_unsupported_options(options)
    check_options_validity(options)

    init_system = get_init_system()
    check_network_status(fs_id, init_system)

    dns_name, fallback_ip_address = get_dns_name_and_fallback_mount_target_ip_address(
        config, fs_id, options
    )

    if check_if_platform_is_mac() and "notls" not in options:
        options["tls"] = None

    if "tls" in options:
        mount_tls(
            config,
            init_system,
            dns_name,
            path,
            fs_id,
            mountpoint,
            options,
            fallback_ip_address=fallback_ip_address,
        )
    else:
        mount_nfs(
            config,
            dns_name,
            path,
            mountpoint,
            options,
            fallback_ip_address=fallback_ip_address,
        )


if "__main__" == __name__:
    main()

では、マウントターゲットのIPアドレスが指定された場合の検証はどこでしているのかというと、get_dns_name_and_fallback_mount_target_ip_address()で行われています。

efs-utils/src/mount_efs/__init__.py

def get_dns_name_and_fallback_mount_target_ip_address(config, fs_id, options):
    def _validate_replacement_field_count(format_str, expected_ct):
        if format_str.count("{") != expected_ct or format_str.count("}") != expected_ct:
            raise ValueError(
                "DNS name format has an incorrect number of replacement fields"
            )

    dns_name_format = config.get(CONFIG_SECTION, "dns_name_format")

    if "{fs_id}" not in dns_name_format:
        raise ValueError("DNS name format must include {fs_id}")

    format_args = {"fs_id": fs_id}

    expected_replacement_field_ct = 1

    if "{az}" in dns_name_format:
        az = options.get("az")
        if az:
            expected_replacement_field_ct += 1
            format_args["az"] = az
        else:
            dns_name_format = dns_name_format.replace("{az}.", "")

    if "{region}" in dns_name_format:
        expected_replacement_field_ct += 1
        format_args["region"] = get_target_region(config)

    if "{dns_name_suffix}" in dns_name_format:
        expected_replacement_field_ct += 1
        config_section = CONFIG_SECTION
        region = format_args.get("region")

        if region:
            config_section = get_config_section(config, region)

        format_args["dns_name_suffix"] = config.get(config_section, "dns_name_suffix")

        logging.debug(
            "Using dns_name_suffix %s in config section [%s]",
            format_args.get("dns_name_suffix"),
            config_section,
        )

    _validate_replacement_field_count(dns_name_format, expected_replacement_field_ct)

    dns_name = dns_name_format.format(**format_args)

    if "mounttargetip" in options:
        ip_address = options.get("mounttargetip")
        logging.info(
            "Use the mount target ip address %s provided in the mount options to mount."
            % ip_address
        )
        try:
            mount_target_ip_address_can_be_resolved(
                ip_address,
                passed_via_options=True,
                network_namespace=options.get("netns") if "netns" in options else None,
            )
            return dns_name, options.get("mounttargetip")
        except FallbackException as e:
            fallback_message = e.message
            throw_ip_address_connect_failure_with_fallback_message(
                ip_address=ip_address, fallback_message=fallback_message
            )

    if dns_name_can_be_resolved(dns_name):
        return dns_name, None

    logging.info(
        "Failed to resolve %s, attempting to lookup mount target ip address using botocore.",
        dns_name,
    )

    try:
        fallback_mount_target_ip_address = get_fallback_mount_target_ip_address(
            config, options, fs_id, dns_name
        )
        logging.info(
            "Found fall back mount target ip address %s for file system %s",
            fallback_mount_target_ip_address,
            fs_id,
        )
        return dns_name, fallback_mount_target_ip_address
    except FallbackException as e:
        fallback_message = e.message

    throw_dns_resolve_failure_with_fallback_message(dns_name, fallback_message)

mount_target_ip_address_can_be_resolved()で解決できた場合、マウントターゲットのIPアドレスをDNS名のフォールバック用のIPアドレスとして返すようですね。

mount_target_ip_address_can_be_resolved()では、mount_target_ip_addressに対して到達可能かどうかチェックしているようです。

efs-utils/src/mount_efs/__init__.py

def mount_target_ip_address_can_be_resolved(
    mount_target_ip_address, passed_via_options=False, network_namespace=None
):
    tries = 3
    for attempt in range(tries):
        try:
            # Open a socket connection to mount target nfs port to verify that the mount target can be connected
            if not network_namespace:
                s = socket.create_connection((mount_target_ip_address, 2049), timeout=2)
            else:
                with NetNS(nspath=network_namespace):
                    s = socket.create_connection(
                        (mount_target_ip_address, 2049), timeout=2
                    )
            s.close()
            return True
        except socket.timeout:
            if attempt < tries - 1:
                message = (
                    "The ip address %s cannot be connected yet, sleep 0.5s, %s retry time(s) left"
                    % (mount_target_ip_address, tries - attempt - 1)
                )
                logging.warning(message)
                time.sleep(0.5)
                continue
            else:
                raise FallbackException(
                    "Connection to the mount target IP address %s timeout. Please retry in 5 minutes if the "
                    "mount target is newly created. Otherwise check your VPC and security group "
                    "configuration to ensure your file system is reachable via TCP port 2049 from your "
                    "instance." % mount_target_ip_address
                )
        except Exception as e:
            hint_message = (
                " Please check if the mount target ip address passed via mount option is correct."
                if passed_via_options
                else ""
            )
            raise FallbackException(
                "Unknown error when connecting to mount target IP address %s, %s.%s"
                % (mount_target_ip_address, e, hint_message)
            )

実際にマウント処理はmount_tls()mount_nfs()で行われます。これら関数の引数にはDNS名を必ず指定する必要があります。そのため、mounttargetip=mount-target-ipのみを指定するのはよろしくないということですね。

ちなみに、フォールバック用のIPアドレスと言いつつ、fallback_ip_addressが存在する場合はDNS名ではなく、fallback_ip_addressに対してTLSのトンネルを掘ったり、接続をしたりしています。

efs-utils/src/mount_efs/__init__.py

def write_stunnel_config_file(
    config,
    state_file_dir,
    fs_id,
    mountpoint,
    tls_port,
    dns_name,
    verify_level,
    ocsp_enabled,
    options,
    region,
    log_dir=LOG_DIR,
    cert_details=None,
    fallback_ip_address=None,
):
    """
    Serializes stunnel configuration to a file. Unfortunately this does not conform to Python's config file format, so we have to
    hand-serialize it.
    """

    stunnel_options = get_stunnel_options()
    mount_filename = get_mount_specific_filename(fs_id, mountpoint, tls_port)

    system_release_version = get_system_release_version()
    global_config = dict(STUNNEL_GLOBAL_CONFIG)

    if is_stunnel_option_supported(
        stunnel_options, b"foreground", b"quiet", emit_warning_log=False
    ):
        # Do not log to stderr of subprocess in addition to the destinations specified with syslog and output.
        # Only support in stunnel version 5.25+.
        global_config["foreground"] = "quiet"
    if any(
        release in system_release_version
        for release in SKIP_NO_SO_BINDTODEVICE_RELEASES
    ):
        global_config["socket"].remove("a:SO_BINDTODEVICE=lo")

    if get_boolean_config_item_value(
        config, CONFIG_SECTION, "stunnel_debug_enabled", default_value=False
    ):
        global_config["debug"] = "debug"
        # If the stunnel debug is enabled, we also redirect stunnel log to stderr to capture any error while launching
        # the stunnel.
        global_config["foreground"] = "yes"
        if config.has_option(CONFIG_SECTION, "stunnel_logs_file"):
            global_config["output"] = config.get(
                CONFIG_SECTION, "stunnel_logs_file"
            ).replace("{fs_id}", fs_id)
        else:
            global_config["output"] = os.path.join(
                log_dir, "%s.stunnel.log" % mount_filename
            )
    global_config["pid"] = os.path.join(
        state_file_dir, mount_filename + "+", "stunnel.pid"
    )
    if get_fips_config(config):
        global_config["fips"] = "yes"

    efs_config = dict(STUNNEL_EFS_CONFIG)
    efs_config["accept"] = efs_config["accept"] % tls_port

    if fallback_ip_address:
        efs_config["connect"] = efs_config["connect"] % fallback_ip_address
    else:
        efs_config["connect"] = efs_config["connect"] % dns_name
.
.
(以下略)
.
.

efs-utils/src/mount_efs/__init__.py

def mount_nfs(config, dns_name, path, mountpoint, options, fallback_ip_address=None):

    if "tls" in options:
        mount_path = "127.0.0.1:%s" % path
    elif fallback_ip_address:
        mount_path = "%s:%s" % (fallback_ip_address, path)
    else:
        mount_path = "%s:%s" % (dns_name, path)
.
.
(以下略)
.
.

試しにマウントターゲットのIPアドレスのAレコードであるカスタムドメインとファイルシステムIDを指定してEFSマウントヘルパーでマウントできるか試してみましょう。

$ sudo mount -t efs -o tls,iam,mounttargetip=efs-a.corp.non-97 fs-0e211df86678573e6 /mount/efs

$ df -hT -t nfs4
Filesystem     Type  Size  Used Avail Use% Mounted on
127.0.0.1:/    nfs4  8.0E     0  8.0E   0% /mount/efs

$ ls -l /mount/efs
total 4

$ tail /var/log/amazon/efs/mount.log
2023-07-03 10:07:11 UTC - INFO - Successfully mounted fs-0e211df86678573e6.efs.us-east-1.amazonaws.com at /mount/efs
2023-07-03 20:42:12 UTC - ERROR - Failed to resolve "mounttargetip=efs-a.corp.non-97"
2023-07-03 20:43:30 UTC - INFO - version=1.35.0 options={'rw': None, 'tls': None, 'iam': None, 'mounttargetip': 'efs-a.corp.non-97'}
2023-07-03 20:43:30 UTC - INFO - Use the mount target ip address efs-a.corp.non-97 provided in the mount options to mount.
2023-07-03 20:43:30 UTC - INFO - binding 20675
2023-07-03 20:43:30 UTC - WARNING - stunnel does not support "b'libwrap'"
2023-07-03 20:43:30 UTC - INFO - Starting TLS tunnel: "/usr/bin/stunnel /var/run/efs/stunnel-config.fs-0e211df86678573e6.mount.efs.20675"
2023-07-03 20:43:30 UTC - INFO - Started TLS tunnel, pid: 1933
2023-07-03 20:43:30 UTC - INFO - Executing: "/sbin/mount.nfs4 127.0.0.1:/ /mount/efs -o rw,nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,port=20675" with 15 sec time limit.
2023-07-03 20:43:31 UTC - INFO - Successfully mounted fs-0e211df86678573e6.efs.us-east-1.amazonaws.com at /mount/efs

マウントできましたね。

自身のIPアドレスをマウントターゲットIPアドレスとして指定した際もマウントできるのか確認しましょう。

下準備としてNFSサーバーの設定をして、起動させます。

# エクスポート設定が存在しないことを確認
$ ls -l /etc/exports.d/
total 0

# エクスポート設定の追加
$ sudo vi /etc/exports.d/export-nfs.exports

# / をエクスポートする設定が追加されたことを確認
$ cat /etc/exports.d/export-nfs.exports
/  *(rw,no_root_squash)

# エクスポート設定の反映
$ sudo exportfs -ar

# エクスポート設定が反映されたことを確認
$ sudo exportfs -v
/               <world>(sync,wdelay,hide,no_subtree_check,sec=sys,rw,secure,no_root_squash,no_all_squash)

# NFSサーバーのサービス起動
$ sudo systemctl start nfs-server

# NFSサーバーが起動できていることを確認
$ systemctl status nfs-server
● nfs-server.service - NFS server and services
     Loaded: loaded (/usr/lib/systemd/system/nfs-server.service; disabled; preset: disabled)
     Active: active (exited) since Tue 2023-07-04 01:35:22 UTC; 12s ago
    Process: 3577 ExecStartPre=/usr/sbin/exportfs -r (code=exited, status=0/SUCCESS)
    Process: 3578 ExecStart=/usr/sbin/rpc.nfsd (code=exited, status=0/SUCCESS)
    Process: 3595 ExecStart=/bin/sh -c if systemctl -q is-active gssproxy; then systemctl reload gssproxy ; fi (code=exited, status=0/SUCCESS)
   Main PID: 3595 (code=exited, status=0/SUCCESS)
        CPU: 28ms

Jul 04 01:35:22 ip-10-0-0-23.ec2.internal systemd[1]: Starting nfs-server.service - NFS server and services...
Jul 04 01:35:22 ip-10-0-0-23.ec2.internal rpc.nfsd[3578]: rpc.nfsd: Unable to request RDMA services: Protocol not supported
Jul 04 01:35:22 ip-10-0-0-23.ec2.internal systemd[1]: Finished nfs-server.service - NFS server and services.

起動後、NFSでマウントできることを確認します。

$ sudo mount -t nfs4 127.0.0.1:/ /mount/efs

$ df -hT -t nfs4
Filesystem     Type  Size  Used Avail Use% Mounted on
127.0.0.1:/    nfs4  8.0G  2.2G  5.8G  28% /mount/efs

$ ls -l /mount/efs/
total 32
lrwxrwxrwx.  1 root root     7 Jan 30 02:31 bin -> usr/bin
dr-xr-xr-x.  5 root root 16384 Jun 28 15:33 boot
drwxr-xr-x.  3 root root   136 Jun 28 15:33 dev
drwxr-xr-x. 79 root root 16384 Jul  3 09:20 etc
drwxr-xr-x.  3 root root    22 Jul  3 09:06 home
lrwxrwxrwx.  1 root root     7 Jan 30 02:31 lib -> usr/lib
lrwxrwxrwx.  1 root root     9 Jan 30 02:31 lib64 -> usr/lib64
drwxr-xr-x.  2 root root     6 Jun 28 15:31 local
drwxr-xr-x.  2 root root     6 Jan 30 02:31 media
drwxr-xr-x.  2 root root     6 Jan 30 02:31 mnt
drwxr-xr-x.  3 root root    17 Jul  3 09:20 mount
drwxr-xr-x.  3 root root    17 Jun 28 15:32 opt
drwxr-xr-x.  2 root root     6 Jun 28 15:31 proc
dr-xr-x---.  3 root root   119 Jul  4 01:40 root
drwxr-xr-x.  2 root root     6 Jun 28 15:33 run
lrwxrwxrwx.  1 root root     8 Jan 30 02:31 sbin -> usr/sbin
drwxr-xr-x.  2 root root     6 Jan 30 02:31 srv
drwxr-xr-x.  2 root root     6 Jun 28 15:31 sys
drwxrwxrwt.  2 root root     6 Jun 28 15:31 tmp
drwxr-xr-x. 12 root root   144 Jun 28 15:32 usr
drwxr-xr-x. 19 root root   266 Jul  3 09:06 var

NFSで自身の/をマウントできることを確認できました。

それでは、IPアドレスをマウントターゲットIPアドレスとしてEFSマウントヘルパーでマウントしてみます。

$ sudo mount -t efs -o tls,iam,mounttargetip=127.0.0.1 fs-0e211df86678573e6 /mount/efs
Mount attempt 1/3 failed due to timeout after 15 sec, wait 0 sec before next attempt.
Mount attempt 2/3 failed due to timeout after 15 sec, wait 0 sec before next attempt.
^CTraceback (most recent call last):
  File "/sbin/mount.efs", line 3811, in <module>

[ec2-user@ip-10-0-0-23 ~]$     main()
  File "/sbin/mount.efs", line 3789, in main
    mount_tls(
  File "/sbin/mount.efs", line 2935, in mount_tls
    mount_nfs(config, dns_name, path, mountpoint, options)
  File "/sbin/mount.efs", line 1801, in mount_nfs
    out, err = proc.communicate()
  File "/usr/lib64/python3.9/subprocess.py", line 1134, in communicate
    stdout, stderr = self._communicate(input, endtime, timeout)
  File "/usr/lib64/python3.9/subprocess.py", line 1979, in _communicate
    ready = selector.select(timeout)
  File "/usr/lib64/python3.9/selectors.py", line 416, in select
    fd_event_list = self._selector.poll(timeout)
KeyboardInterrupt
^C

$ tail -n11 /var/log/amazon/efs/mount.log
2023-07-04 02:04:43 UTC - INFO - version=1.35.0 options={'rw': None, 'tls': None, 'iam': None, 'mounttargetip': '127.0.0.1'}
2023-07-04 02:04:43 UTC - INFO - Use the mount target ip address 127.0.0.1 provided in the mount options to mount.
2023-07-04 02:04:43 UTC - INFO - binding 20715
2023-07-04 02:04:43 UTC - WARNING - stunnel does not support "b'libwrap'"
2023-07-04 02:04:43 UTC - INFO - Starting TLS tunnel: "/usr/bin/stunnel /var/run/efs/stunnel-config.fs-0e211df86678573e6.mount.efs.20715"
2023-07-04 02:04:43 UTC - INFO - Started TLS tunnel, pid: 623531
2023-07-04 02:04:44 UTC - INFO - Executing: "/sbin/mount.nfs4 127.0.0.1:/ /mount/efs -o rw,nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,port=20715" with 15 sectime limit.
2023-07-04 02:04:59 UTC - ERROR - Mounting fs-0e211df86678573e6.efs.us-east-1.amazonaws.com to /mount/efs failed due to timeout after 15 sec, mount attempt 1/3, wait 0 sec before next attempt.
2023-07-04 02:04:59 UTC - INFO - Executing: "/sbin/mount.nfs4 127.0.0.1:/ /mount/efs -o rw,nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,port=20715" with 15 sectime limit.
2023-07-04 02:05:14 UTC - ERROR - Mounting fs-0e211df86678573e6.efs.us-east-1.amazonaws.com to /mount/efs failed due to timeout after 15 sec, mount attempt 2/3, wait 0 sec before next attempt.
2023-07-04 02:05:14 UTC - INFO - Executing: "/sbin/mount.nfs4 127.0.0.1:/ /mount/efs -o rw,nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,port=20715"

なかなかマウントが進まなかったので、途中でCtrl + Cでキャンセルしてしまいましたが、ファイルシステムIDfs-0e211df86678573e6のマウントターゲットのIPアドレスとして127.0.0.1に接続しようとしたのがログから確認できます。

続いて、ファイルシステムのIDを適当なものにした場合もマウントできるか確認します。

$ sudo mount -t efs -o tls,iam,mounttargetip=efs-a.corp.non-97 fs-0e211df86678573ex /mount/efs
Failed to resolve "fs-0e211df86678573ex" - check that the specified DNS name is a CNAME record resolving to a valid EFS DNS name

ファイルシステムIDを使った名前解決ができず、怒られてしまいました。

ファイルシステムIDの正規表現は^(?Pfs-[0-9a-f]+)$xは含まれないようです。正規表現に当てはまるようなファイルシステムIDに修正して接続します。

$ sudo mount -t efs -o tls,iam,mounttargetip=efs-a.corp.non-97 fs-0e211df86678573ea /mount/efs
b'mount.nfs4: access denied by server while mounting 127.0.0.1:/'

$ tail /var/log/amazon/efs/mount.log
2023-07-04 02:08:59 UTC - INFO - Successfully mounted fs-0e211df86678573e6.efs.us-east-1.amazonaws.com at /mount/efs
2023-07-04 02:09:15 UTC - ERROR - Failed to resolve "fs-0e211df86678573ex"
2023-07-04 04:43:57 UTC - INFO - version=1.35.0 options={'rw': None, 'tls': None, 'iam': None, 'mounttargetip': 'efs-a.corp.non-97'}
2023-07-04 04:43:57 UTC - INFO - Use the mount target ip address efs-a.corp.non-97 provided in the mount options to mount.
2023-07-04 04:43:57 UTC - INFO - binding 20473
2023-07-04 04:43:57 UTC - WARNING - stunnel does not support "b'libwrap'"
2023-07-04 04:43:57 UTC - INFO - Starting TLS tunnel: "/usr/bin/stunnel /var/run/efs/stunnel-config.fs-0e211df86678573ea.mount.efs.20473"
2023-07-04 04:43:57 UTC - INFO - Started TLS tunnel, pid: 2113
2023-07-04 04:43:58 UTC - INFO - Executing: "/sbin/mount.nfs4 127.0.0.1:/ /mount/efs -o rw,nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,port=20473" with 15 sectime limit.
2023-07-04 04:43:58 UTC - ERROR - Failed to mount fs-0e211df86678573ea.efs.us-east-1.amazonaws.com at /mount/efs: returncode=32, stderr="b'mount.nfs4: access denied by server while mounting 127.0.0.1:/'"

エラーメッセージが変わりましたね。

おそらく、IAM認証だと認証先のEFSファイルシステムがそもそも存在しないため接続のだと考えます。

IAM認証を行わず接続します。

$ sudo mount -t efs -o tls,mounttargetip=efs-a.corp.non-97 fs-0e211df86678573ea /mount/efs

$ tail /var/log/amazon/efs/mount.log
2023-07-04 04:43:58 UTC - INFO - Executing: "/sbin/mount.nfs4 127.0.0.1:/ /mount/efs -o rw,nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,port=20473" with 15 sectime limit.
2023-07-04 04:43:58 UTC - ERROR - Failed to mount fs-0e211df86678573ea.efs.us-east-1.amazonaws.com at /mount/efs: returncode=32, stderr="b'mount.nfs4: access denied by server while mounting 127.0.0.1:/'"
2023-07-04 04:44:27 UTC - INFO - version=1.35.0 options={'rw': None, 'tls': None, 'mounttargetip': 'efs-a.corp.non-97'}
2023-07-04 04:44:27 UTC - INFO - Use the mount target ip address efs-a.corp.non-97 provided in the mount options to mount.
2023-07-04 04:44:27 UTC - INFO - binding 20372
2023-07-04 04:44:27 UTC - WARNING - stunnel does not support "b'libwrap'"
2023-07-04 04:44:27 UTC - INFO - Starting TLS tunnel: "/usr/bin/stunnel /var/run/efs/stunnel-config.fs-0e211df86678573ea.mount.efs.20372"
2023-07-04 04:44:27 UTC - INFO - Started TLS tunnel, pid: 2170
2023-07-04 04:44:27 UTC - INFO - Executing: "/sbin/mount.nfs4 127.0.0.1:/ /mount/efs -o rw,nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,port=20372" with 15 sectime limit.
2023-07-04 04:44:27 UTC - INFO - Successfully mounted fs-0e211df86678573ea.efs.us-east-1.amazonaws.com at /mount/efs

はい、マウントできました。

ファイルシステムIDの正規表現さえ合っていれば、存在しないファイルシステムIDであってもTLS接続はできることを確認できました。

VPC BのEC2インスタンスからマウント

ファイルシステムIDを指定してEFSマウントヘルパーでマウント

続いて、EFSファイルシステムとは別のVPC上のEC2インスタンスからマウントします。

まず、ファイルシステムIDを指定してEFSマウントヘルパーでマウントしてみます。

# マウントポイントの作成
$ sudo mkdir -p /mount/efs

$ sudo mount -t efs -o tls,iam fs-0e211df86678573e6 /mount/efs
Failed to resolve "fs-0e211df86678573e6.efs.us-east-1.amazonaws.com" - check that your file system ID is correct, and ensure that the VPC has an EFS mount target for this file system ID.
See https://docs.aws.amazon.com/console/efs/mount-dns-name for more detail.
Attempting to lookup mount target ip address using botocore. Failed to import necessary dependency botocore, please install botocore first.

はい、怒られました。

ファイルシステムのIDからDNS名を生成し、そのDNS名を名前解決しようとしたが失敗したようです。

マウントターゲットのIPアドレスを指定してEFSマウントヘルパーでマウント

マウントターゲットのIPアドレスを指定してEFSマウントヘルパーでマウントします。

$ sudo mount -t efs -o tls,iam,mounttargetip=10.0.0.206 fs-0e211df86678573e6 /mount/efs

$ df -hT -t nfs4
Filesystem     Type  Size  Used Avail Use% Mounted on
127.0.0.1:/    nfs4  8.0E     0  8.0E   0% /mount/efs

$ ls -l /mount/efs
total 4

$ cat /var/log/amazon/efs/mount.log
2023-07-03 10:13:48 UTC - INFO - version=1.35.0 options={'rw': None, 'tls': None, 'iam': None}
2023-07-03 10:13:48 UTC - INFO - Failed to resolve fs-0e211df86678573e6.efs.us-east-1.amazonaws.com, attempting to lookup mount target ip address using botocore.
2023-07-03 10:13:48 UTC - ERROR - Failed to resolve "fs-0e211df86678573e6.efs.us-east-1.amazonaws.com" - check that your file system ID is correct, and ensure that the VPC has an EFS mount target for this file system ID.
See https://docs.aws.amazon.com/console/efs/mount-dns-name for more detail.
Attempting to lookup mount target ip address using botocore. Failed to import necessary dependency botocore, please install botocore first.
2023-07-03 10:15:38 UTC - INFO - version=1.35.0 options={'rw': None, 'tls': None, 'iam': None, 'mounttargetip': '10.0.0.206'}
2023-07-03 10:15:38 UTC - INFO - Use the mount target ip address 10.0.0.206 provided in the mount options to mount.
2023-07-03 10:15:38 UTC - INFO - binding 20528
2023-07-03 10:15:39 UTC - WARNING - stunnel does not support "b'libwrap'"
2023-07-03 10:15:39 UTC - INFO - Starting TLS tunnel: "/usr/bin/stunnel /var/run/efs/stunnel-config.fs-0e211df86678573e6.mount.efs.20528"
2023-07-03 10:15:39 UTC - INFO - Started TLS tunnel, pid: 26810
2023-07-03 10:15:39 UTC - INFO - Executing: "/sbin/mount.nfs4 127.0.0.1:/ /mount/efs -o rw,nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,port=20528" with 15 sec time limit.
2023-07-03 10:15:39 UTC - INFO - Successfully mounted fs-0e211df86678573e6.efs.us-east-1.amazonaws.com at /mount/efs

マウントできましたね。

マウントターゲットのIPアドレスとファイルシステムIDが分かっていれば別VPCからでもEFSマウントヘルパーでマウントできることが分かりました。

EFSファイルシステムのDNS名のCNAMEレコードであるカスタムドメインを指定してEFSマウントヘルパーでマウント

EFSファイルシステムのDNS名のCNAMEレコードであるカスタムドメインを指定してEFSマウントヘルパーでマウントします。

以下のようにそもそも名前解決ができないので、望み薄ですが念のため確認します。

$ dig efs-cname.corp.non-97 +short
fs-0e211df86678573e6.efs.us-east-1.amazonaws.com.

$ dig fs-0e211df86678573e6.efs.us-east-1.amazonaws.com. +short

実行結果は以下の通りです。

$ sudo mount -t efs -o tls,iam efs-cname.corp.non-97 /mount/efs
Failed to resolve "efs-cname.corp.non-97" - check that the specified DNS name is a CNAME record resolving to a valid EFS DNS name

やはり名前解決できないと怒られました。

マウントターゲットのIPアドレスのAレコードであるカスタムドメインとファイルシステムIDを指定してEFSマウントヘルパーでマウント

最後にマウントターゲットのIPアドレスのAレコードであるカスタムドメインとファイルシステムIDを指定してEFSマウントヘルパーでマウントしてみます。

$ sudo mount -t efs -o tls,iam,mounttargetip=efs-a.corp.non-97 fs-0e211df86678573e6 /mount/efs

$ df -hT -t nfs4
Filesystem     Type  Size  Used Avail Use% Mounted on
127.0.0.1:/    nfs4  8.0E     0  8.0E   0% /mount/efs

$ ls -l /mount/efs
total 4
-rw-r--r--. 1 root root 0 Jul  3 09:23 test

$ tail /var/log/amazon/efs/mount.log
2023-07-03 20:49:34 UTC - INFO - Executing: "/sbin/mount.nfs4 127.0.0.1:/ /mount/efs -o rw,nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,port=20468" with 15 sectime limit.
2023-07-03 20:49:35 UTC - INFO - Successfully mounted fs-0e211df86678573e6.efs.us-east-1.amazonaws.com at /mount/efs
2023-07-04 02:59:54 UTC - INFO - version=1.35.0 options={'rw': None, 'tls': None, 'iam': None, 'mounttargetip': 'efs-a.corp.non-97'}
2023-07-04 02:59:54 UTC - INFO - Use the mount target ip address efs-a.corp.non-97 provided in the mount options to mount.
2023-07-04 02:59:54 UTC - INFO - binding 20389
2023-07-04 02:59:54 UTC - WARNING - stunnel does not support "b'libwrap'"
2023-07-04 02:59:54 UTC - INFO - Starting TLS tunnel: "/usr/bin/stunnel /var/run/efs/stunnel-config.fs-0e211df86678573e6.mount.efs.20389"
2023-07-04 02:59:54 UTC - INFO - Started TLS tunnel, pid: 1822
2023-07-04 02:59:54 UTC - INFO - Executing: "/sbin/mount.nfs4 127.0.0.1:/ /mount/efs -o rw,nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,port=20389" with 15 sectime limit.
2023-07-04 02:59:54 UTC - INFO - Successfully mounted fs-0e211df86678573e6.efs.us-east-1.amazonaws.com at /mount/efs

マウントできました。

おまけでファイルシステムのIDを適当なものにした場合をマウントできるか試します。

$ sudo mount -t efs -o tls,iam,mounttargetip=efs-a.corp.non-97 fs-0e211df86678573ea /mount/efs
b'mount.nfs4: access denied by server while mounting 127.0.0.1:/'

$ tail /var/log/amazon/efs/mount.log
2023-07-04 04:38:34 UTC - INFO - Use the mount target ip address efs-a.corp.non-97 provided in the mount options to mount.
2023-07-04 04:38:34 UTC - WARNING - /mount/efs is already mounted, mount aborted
2023-07-04 04:38:43 UTC - INFO - version=1.35.0 options={'rw': None, 'tls': None, 'iam': None, 'mounttargetip': 'efs-a.corp.non-97'}
2023-07-04 04:38:43 UTC - INFO - Use the mount target ip address efs-a.corp.non-97 provided in the mount options to mount.
2023-07-04 04:38:43 UTC - INFO - binding 20656
2023-07-04 04:38:43 UTC - WARNING - stunnel does not support "b'libwrap'"
2023-07-04 04:38:43 UTC - INFO - Starting TLS tunnel: "/usr/bin/stunnel /var/run/efs/stunnel-config.fs-0e211df86678573ea.mount.efs.20656"
2023-07-04 04:38:43 UTC - INFO - Started TLS tunnel, pid: 2430
2023-07-04 04:38:43 UTC - INFO - Executing: "/sbin/mount.nfs4 127.0.0.1:/ /mount/efs -o rw,nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,port=20656" with 15 sectime limit.
2023-07-04 04:38:43 UTC - ERROR - Failed to mount fs-0e211df86678573ea.efs.us-east-1.amazonaws.com at /mount/efs: returncode=32, stderr="b'mount.nfs4: access denied by server while mounting 127.0.0.1:/'"

やはりIAM認証だと認証先のEFSファイルシステムがそもそも存在しないので接続できなそうですね。

IAM認証を行わずに接続してみます。

$ sudo mount -t efs -o tls,mounttargetip=efs-a.corp.non-97 fs-0e211df86678573ea /mount/efs

$ df -hT -t nfs4
Filesystem     Type  Size  Used Avail Use% Mounted on
127.0.0.1:/    nfs4  8.0E     0  8.0E   0% /mount/efs

$ ls -l /mount/efs
total 4
-rw-r--r--. 1 root root 0 Jul  3 09:23 test

はい、こちらはマウントできました。

カスタムドメインで接続できるけれども

Amazon EFSのカスタムドメインにEFSマウントヘルパーを使ってマウントできるのか確認してみました。

カスタムドメインを使ってEFSマウントヘルパーを使ってマウントは可能ですが、結局のところファイルシステムIDを指定する必要があるというのがネックですね。

移行などで、「DNSレコードを変更するだけで違うEFSファイルシステムに振り分けるようにする」といったことは出来ないと考えます。併せてファイルシステムIDの変更も行う必要があるためです。

加えて、カスタムドメインを使う場合 = マウントターゲットを指定しまうことになるため、マウント元のAZと同じAZのマウントターゲットを自動で判別して接続してれなくなります。

もし、「カスタムドメインを使いたい。でも、AZを跨ぐレイテンシーや料金が気になる。」のであれば、マウントターゲット毎にカスタムドメインを用意する必要があると考えます。

カスタムドメインを使ってEFSマウントヘルパーを使いたい場面としては、別VPCからアクセスする時でしょうか。ただし、わざわざカスタムドメインを用意せずにマウントターゲットのIPアドレスを指定する形でも良いかなと考えます。

この記事が誰かの助けになれば幸いです。

以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!