Commit 2abd13cf authored by Yuan Gao's avatar Yuan Gao
Browse files

Support fallback to mount with mount target ip address when DNS resolution fails

* Support using DescribeMountTargets+DescribeAvailabilityZones calls to resolve mount target IP when DNS resolution fails (requires botocore and IAM permissions for DescribeMountTargets and DescribeAvailabilityZones)
* Support mount to specific mount target ip address with mounttargetip option
* Support mount with IAM identify by assuming aws profile (requires botocore)
* Use IMDSv2 to fetch ec2 metadata by default, fallback to IMDSv1 when user disable the token fetch in config file or token fetch timeout
* Fix an issue where stunnel cannot be established in pod after efs-utils being updated to use python3 on efs-csi-driver
parent 41e5c417
......@@ -42,6 +42,10 @@ The `efs-utils` package has been verified against the following MacOS distributi
* Python 3.4+
* `stunnel` 4.56+
## Optional
* `botocore` 1.12.0+
## Installation
### On Amazon Linux distributions
......@@ -165,6 +169,12 @@ To mount with the recommended default options, simply run:
$ sudo mount -t efs file-system-id efs-mount-point/
```
To mount file system to a specific mount target of the file system, run:
```
$ sudo mount -t efs -o mounttargetip=mount-target-ip-address file-system-id efs-mount-point/
```
To mount file system within a given network namespace, run:
```
......@@ -256,13 +266,12 @@ The installation installs latest stunnel available in brew repository. You can a
brew upgrade stunnel
```
## Install botocore
## Enable mount success/failure notification via CloudWatch log
`efs-utils` now support publishing mount success/failure logs to CloudWatch log. By default, this feature is disabled. There are three
steps you must follow to enable and use this feature:
### Step 1. Install botocore
`efs-utils` uses botocore to interact with CloudWatch log service . Please note the package type from the above table.
`efs-utils` uses botocore to interact with other AWS services. Please note the package type from the above table and install
botocore based on that info. If botocore is already installed and does not meet the minimum required version,
you can upgrade the botocore by following the [upgrade botocore section](#Upgrade-botocore).
- Download the `get-pip.py` script
#### RPM
```bash
......@@ -316,6 +325,20 @@ sudo pip3 install --target /usr/lib/python3/dist-packages botocore || sudo /usr/
sudo pip3 install botocore
```
## Upgrade botocore
Pass `--upgrade` to the corresponding installation scripts above based on system platform and distribution
```bash
sudo pip3 install botocore --upgrade
```
## Enable mount success/failure notification via CloudWatch log
`efs-utils` now support publishing mount success/failure logs to CloudWatch log. By default, this feature is disabled. There are three
steps you must follow to enable and use this feature:
### Step 1. Install botocore
Follow [install botocore section](#Install-botocore)
### Step 2. Enable CloudWatch log feature in efs-utils config file `/etc/amazon/efs/efs-utils.conf`
```bash
sudo sed -i -e '/\[cloudwatch-log\]/{N;s/# enabled = true/enabled = true/}' /etc/amazon/efs/efs-utils.conf
......@@ -363,7 +386,86 @@ You can also manually chose a value of read_ahead_kb to optimize read throughput
$ sudo bash -c "echo read-ahead-value-in-kb > /sys/class/bdi/0:$(stat -c '%d' efs-mount-point)/read_ahead_kb"
```
## Using botocore to retrieve mount target ip address when dns name cannot be resolved
`efs-utils` now supports using botocore to retrieve mount target ip address when dns name cannot be resolved, e.g.
when user is mounting a file system in another VPC. There are two prerequisites to use this feature:
### Step 1. Install botocore
Follow [install botocore section](#Install-botocore)
### Step 2. Allow DescribeMountTargets and DescribeAvailabilityZones action in the IAM policy
Allow the `elasticfilesystem:DescribeMountTargets` and `ec2:DescribeAvailabilityZones` action in your policy attached to
the iam role you attached to the instance, or the aws credentials configured on your instance. We recommend you attach
AWS managed policy `AmazonElasticFileSystemsUtils`.
This feature will be enabled by default. To disable this feature:
```bash
sed -i "s/fall_back_to_mount_target_ip_address_enabled = true/fall_back_to_mount_target_ip_address_enabled = false/" /etc/amazon/efs/efs-utils.conf
```
If you decide that you do not want to use this feature, but need to mount a cross-VPC file system, you can use the mounttargetip
option to do so, using the desired mount target ip address in the mount command.
## The way to access instance metadata
`efs-utils` by default uses IMDSv2, which is a session-oriented method used to access instance metadata. If you don't want to use
IMDSv2, you can disable the token fetching feature by running the following command:
```bash
sed -i "s/disable_fetch_ec2_metadata_token = false/disable_fetch_ec2_metadata_token = true/" /etc/amazon/efs/efs-utils.conf
```
## Use the assumed profile credentials for IAM
To authenticate with EFS using the system’s IAM identity of an awsprofile, add the `iam` option and pass the profile name to
`awsprofile` option. These options require the `tls` option.
```
$ sudo mount -t efs -o tls,iam,aws-profile=test-profile file-system-id efs-mount-point/
```
To configure the named profile, see the [Named Profiles doc](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html)
and [Support Config File Settings doc](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html#cli-configure-files-settings)
for more details. If the credentials (e.g. aws_access_key_id) are not configured in `/root/.aws/credentials` or `/root/.aws/config`
(note that the path prefix may vary based on the root path of sudo), efs-utils will use botocore to assume the named profile.
This will require botocore is pre-installed, please follow [install botocore section](#Install-botocore) to install botocore first.
Normally you will need to configure your profile IAM policy to make the assume works. For example, if you want to perform a
cross-account mounting, suppose you have established
[vpc-peering-connections](https://docs.aws.amazon.com/vpc/latest/peering/create-vpc-peering-connection.html) between your vpcs,
next step you need to do is giving permission to account B so that it can assume a role in account A and then mount the file system
that belongs to account A. You can see
[IAM doc](https://docs.aws.amazon.com/IAM/latest/UserGuide/tutorial_cross-account-with-roles.html) for more details.
After the IAM identity is setup, you can configure your awsprofile credentials or config. You can refer to
[sdk settings](https://docs.aws.amazon.com/sdkref/latest/guide/settings-global.html). For example you can define
the profile to use the credentials of profile `default` to assume role in account A by defining the `source_profile`.
```bash
# /root/.aws/credentials
[default]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key_id =wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
# /root/.aws/config
[default]
...
[profile test-profile]
role_arn = <role-arn-in-account-A>
source_profile = default
```
Or you can use the credentials from IAM role attached to instance to assume the named profile, e.g.
```bash
# /root/.aws/config
[profile test-profile]
role_arn = <role-arn-in-account-A>
credential_source = Ec2InstanceMetadata
```
## License Summary
This code is made available under the MIT license.
......@@ -31,7 +31,7 @@
%endif
Name : amazon-efs-utils
Version : 1.30.2
Version : 1.31.1
Release : 1%{platform}
Summary : This package provides utilities for simplifying the use of EFS file systems
......@@ -131,6 +131,11 @@ fi
%clean
%changelog
* Thu May 06 2021 Yuan Gao <ygaochn@amazon.com> - 1.31.1
- Support new option: mounttargetip, enable mount file system to specific mount target ip address
- Support using botocore to retrieve and mount via file system mount target ip address when DNS resolution fails
- Use IMDSv2 by default to access instance metadata service
* Thu Apr 15 2021 Yue Wang <wangnyue@amazon.com> - 1.30.2
- Fix the throughput regression due to read_ahead configuration change on Linux distribution with kernel version 5.4.x and above
......
......@@ -11,7 +11,7 @@ set -ex
BASE_DIR=$(pwd)
BUILD_ROOT=${BASE_DIR}/build/debbuild
VERSION=1.30.2
VERSION=1.31.1
RELEASE=1
DEB_SYSTEM_RELEASE_PATH=/etc/os-release
......
......@@ -7,5 +7,5 @@
#
[global]
version=1.30.2
version=1.31.1
release=1
Package: amazon-efs-utils
Architecture: all
Version: 1.30.2
Version: 1.31.1
Section: utils
Depends: python3, nfs-common, stunnel4 (>= 4.56), openssl (>= 1.0.2), util-linux
Priority: optional
......
......@@ -36,6 +36,12 @@ port_range_upper_bound = 20449
# Optimize read_ahead_kb for Linux 5.4+
optimize_readahead = true
# By default, we enable the feature to fallback to mount with mount target ip address when dns name cannot be resolved
fall_back_to_mount_target_ip_address_enabled = true
# By default, we use IMDSv2 to get the instance metadata, set this to true if you want to disable IMDSv2 usage
disable_fetch_ec2_metadata_token = false
[mount.cn-north-1]
dns_name_suffix = amazonaws.com.cn
......
......@@ -101,8 +101,9 @@ can be used to mount EFS\&.
.TP
\fBawsprofile\fR
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 "awsprofile" is not \
specified, the "default" profile is used\&.
(~/.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\&.
.TP
\fBawscredsuri\fR
Use the relative uri to lookup IAM credentials from ecs task metadata endpoint\&.
......@@ -118,6 +119,9 @@ Mount the EFS file system to the specified availability zone mount target\&.
.TP
\fBmountport\fR
Use the port 2049 to bypass portmapper daemon on EC2 Mac instances running macOS Big Sur\&.
.TP
\fBmounttargetip\fR
Mount the EFS file system to the specified mount target ip address\&.
.if n \{\
.RE
.\}
......@@ -127,6 +131,11 @@ 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\&.
.TP
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\&.
.TP
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 \
......@@ -173,6 +182,16 @@ Mount an EFS file system with file system ID "fs-abcd1234" at mount point "/mnt/
with encryption of data in transit. The mount helper will authenticate with EFS using \
the system's IAM identity\&.
.TP
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)\&.
.TP
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 \
......
This diff is collapsed.
......@@ -18,6 +18,7 @@ import os
import pwd
import re
import shutil
import socket
import subprocess
import sys
import time
......@@ -48,15 +49,19 @@ except ImportError:
from urllib import urlencode
VERSION = '1.30.2'
VERSION = '1.31.1'
SERVICE = 'elasticfilesystem'
CONFIG_FILE = '/etc/amazon/efs/efs-utils.conf'
CONFIG_SECTION = 'mount-watchdog'
MOUNT_CONFIG_SECTION = 'mount'
CLIENT_INFO_SECTION = 'client-info'
CLIENT_SOURCE_STR_LEN_LIMIT = 100
DISABLE_FETCH_EC2_METADATA_TOKEN_ITEM = 'disable_fetch_ec2_metadata_token'
DEFAULT_UNKNOWN_VALUE = 'unknown'
DEFAULT_MACOS_VALUE = 'macos'
# 50ms
DEFAULT_TIMEOUT = 0.05
LOG_DIR = '/var/log/amazon/efs'
LOG_FILE = 'mount-watchdog.log'
......@@ -132,6 +137,9 @@ INSTANCE_METADATA_TOKEN_URL = 'http://169.254.169.254/latest/api/token'
SECURITY_CREDS_ECS_URI_HELP_URL = 'https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html'
SECURITY_CREDS_WEBIDENTITY_HELP_URL = 'https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html'
SECURITY_CREDS_IAM_ROLE_HELP_URL = 'https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html'
NAMED_PROFILE_HELP_URL = 'https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html'
CONFIG_FILE_SETTINGS_HELP_URL \
= 'https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html#cli-configure-files-settings'
Mount = namedtuple('Mount', ['server', 'mountpoint', 'type', 'options', 'freq', 'passno'])
......@@ -147,7 +155,7 @@ def fatal_error(user_message, log_message=None):
sys.exit(1)
def get_aws_security_credentials(credentials_source, region):
def get_aws_security_credentials(config, credentials_source, region):
"""
Lookup AWS security credentials (access key ID and secret access key). Adapted credentials provider chain from:
https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html and
......@@ -157,32 +165,64 @@ def get_aws_security_credentials(credentials_source, region):
if method == 'credentials':
return get_aws_security_credentials_from_file('credentials', value)
elif method == 'named_profile':
return get_aws_security_credentials_from_assumed_profile(value)
elif method == 'config':
return get_aws_security_credentials_from_file('config', value)
elif method == 'ecs':
return get_aws_security_credentials_from_ecs(value)
return get_aws_security_credentials_from_ecs(config, value)
elif method == 'webidentity':
return get_aws_security_credentials_from_webidentity(*(value.split(',')), region=region)
return get_aws_security_credentials_from_webidentity(config, *(value.split(',')), region=region)
elif method == 'metadata':
return get_aws_security_credentials_from_instance_metadata()
return get_aws_security_credentials_from_instance_metadata(config)
else:
logging.error('Improper credentials source string "%s" found from mount state file', credentials_source)
return None
def get_aws_ec2_metadata_token():
def get_boolean_config_item_value(config, config_section, config_item, default_value, emit_warning_message=True):
warning_message = None
if not config.has_section(config_section):
warning_message = 'Warning: config file does not have section %s.' % config_section
elif not config.has_option(config_section, config_item):
warning_message = 'Warning: config file does not have %s item in section %s.' % (config_item, config_section)
if warning_message:
if emit_warning_message:
sys.stdout.write('%s. You should be able to find a new config file in the same folder as current config file %s. '
'Consider update the new config file to latest config file. Use the default value [%s = %s].'
% (warning_message, CONFIG_FILE, config_item, default_value))
return default_value
return config.getboolean(config_section, config_item)
def fetch_ec2_metadata_token_disabled(config):
return get_boolean_config_item_value(config, MOUNT_CONFIG_SECTION, DISABLE_FETCH_EC2_METADATA_TOKEN_ITEM, default_value=False)
def get_aws_ec2_metadata_token(timeout=DEFAULT_TIMEOUT):
# Normally the session token is fetched within 10ms, setting a timeout of 50ms here to abort the request
# and return None if the token has not returned within 50ms
try:
opener = build_opener(HTTPHandler)
request = Request(INSTANCE_METADATA_TOKEN_URL)
request.add_header('X-aws-ec2-metadata-token-ttl-seconds', 21600)
request.add_header('X-aws-ec2-metadata-token-ttl-seconds', '21600')
request.get_method = lambda: 'PUT'
res = opener.open(request)
return res.read()
try:
res = opener.open(request, timeout=timeout)
return res.read()
except socket.timeout:
logging.debug('Timeout when get the aws ec2 metadata token')
return None
except NameError:
headers = {'X-aws-ec2-metadata-token-ttl-seconds': 21600}
headers = {'X-aws-ec2-metadata-token-ttl-seconds': '21600'}
req = Request(INSTANCE_METADATA_TOKEN_URL, headers=headers, method='PUT')
res = urlopen(req)
return res.read()
try:
res = urlopen(req, timeout=timeout)
return res.read()
except socket.timeout:
logging.debug('Timeout when get the aws ec2 metadata token')
return None
def get_aws_security_credentials_from_file(file_name, awsprofile):
......@@ -197,14 +237,49 @@ def get_aws_security_credentials_from_file(file_name, awsprofile):
return None
def get_aws_security_credentials_from_ecs(uri):
def get_aws_security_credentials_from_assumed_profile(awsprofile):
credentials = botocore_credentials_helper(awsprofile)
if credentials['AccessKeyId']:
return credentials
logging.error('AWS security credentials not found via assuming named profile [%s] using botocore', awsprofile)
return None
def botocore_credentials_helper(awsprofile):
credentials = {'AccessKeyId': None, 'SecretAccessKey': None, 'Token': None}
try:
import botocore.session
from botocore.exceptions import ProfileNotFound
except ImportError:
logging.error('Named profile credentials cannot be retrieved without botocore, please install botocore first.')
return credentials
session = botocore.session.get_session()
session.set_config_variable('profile', awsprofile)
try:
frozen_credentials = session.get_credentials().get_frozen_credentials()
except ProfileNotFound as e:
logging.error('%s, please add the [profile %s] section in the aws config file following %s and %s.'
% (e, awsprofile, NAMED_PROFILE_HELP_URL, CONFIG_FILE_SETTINGS_HELP_URL))
return credentials
credentials['AccessKeyId'] = frozen_credentials.access_key
credentials['SecretAccessKey'] = frozen_credentials.secret_key
credentials['Token'] = frozen_credentials.token
return credentials
def get_aws_security_credentials_from_ecs(config, uri):
# through ECS security credentials uri found in AWS_CONTAINER_CREDENTIALS_RELATIVE_URI environment variable
dict_keys = ['AccessKeyId', 'SecretAccessKey', 'Token']
ecs_uri = ECS_TASK_METADATA_API + uri
ecs_unsuccessful_resp = 'Unsuccessful retrieval of AWS security credentials at %s.' % ecs_uri
ecs_url_error_msg = 'Unable to reach %s to retrieve AWS security credentials. See %s for more info.' % \
(ecs_uri, SECURITY_CREDS_ECS_URI_HELP_URL)
ecs_security_dict = url_request_helper(ecs_uri, ecs_unsuccessful_resp, ecs_url_error_msg)
ecs_security_dict = url_request_helper(config, ecs_uri, ecs_unsuccessful_resp, ecs_url_error_msg)
if ecs_security_dict and all(k in ecs_security_dict for k in dict_keys):
return ecs_security_dict
......@@ -212,7 +287,7 @@ def get_aws_security_credentials_from_ecs(uri):
return None
def get_aws_security_credentials_from_webidentity(role_arn, token_file, region):
def get_aws_security_credentials_from_webidentity(config, role_arn, token_file, region):
try:
with open(token_file, 'r') as f:
token = f.read()
......@@ -232,7 +307,7 @@ def get_aws_security_credentials_from_webidentity(role_arn, token_file, region):
unsuccessful_resp = 'Unsuccessful retrieval of AWS security credentials at %s.' % STS_ENDPOINT_URL
url_error_msg = 'Unable to reach %s to retrieve AWS security credentials. See %s for more info.' % \
(STS_ENDPOINT_URL, SECURITY_CREDS_WEBIDENTITY_HELP_URL)
resp = url_request_helper(webidentity_url, unsuccessful_resp, url_error_msg, headers={'Accept': 'application/json'})
resp = url_request_helper(config, webidentity_url, unsuccessful_resp, url_error_msg, headers={'Accept': 'application/json'})
if resp:
creds = resp \
......@@ -249,21 +324,19 @@ def get_aws_security_credentials_from_webidentity(role_arn, token_file, region):
return None
def get_aws_security_credentials_from_instance_metadata():
def get_aws_security_credentials_from_instance_metadata(config):
# through IAM role name security credentials lookup uri (after lookup for IAM role name attached to instance)
dict_keys = ['AccessKeyId', 'SecretAccessKey', 'Token']
iam_role_unsuccessful_resp = 'Unsuccessful retrieval of IAM role name at %s.' % INSTANCE_IAM_URL
iam_role_url_error_msg = 'Unable to reach %s to retrieve IAM role name. See %s for more info.' % \
(INSTANCE_IAM_URL, SECURITY_CREDS_IAM_ROLE_HELP_URL)
iam_role_name = url_request_helper(INSTANCE_IAM_URL, iam_role_unsuccessful_resp,
iam_role_url_error_msg, retry_with_new_header_token=True)
iam_role_name = url_request_helper(config, INSTANCE_IAM_URL, iam_role_unsuccessful_resp, iam_role_url_error_msg)
if iam_role_name:
security_creds_lookup_url = INSTANCE_IAM_URL + iam_role_name
unsuccessful_resp = 'Unsuccessful retrieval of AWS security credentials at %s.' % security_creds_lookup_url
url_error_msg = 'Unable to reach %s to retrieve AWS security credentials. See %s for more info.' % \
(security_creds_lookup_url, SECURITY_CREDS_IAM_ROLE_HELP_URL)
iam_security_dict = url_request_helper(security_creds_lookup_url, unsuccessful_resp,
url_error_msg, retry_with_new_header_token=True)
iam_security_dict = url_request_helper(config, security_creds_lookup_url, unsuccessful_resp, url_error_msg)
if iam_security_dict and all(k in iam_security_dict for k in dict_keys):
return iam_security_dict
......@@ -297,22 +370,37 @@ def credentials_file_helper(file_path, awsprofile):
return credentials
def url_request_helper(url, unsuccessful_resp, url_error_msg, headers={}, retry_with_new_header_token=False):
def is_instance_metadata_url(url):
return url.startswith('http://169.254.169.254')
def url_request_helper(config, url, unsuccessful_resp, url_error_msg, headers={}):
try:
req = Request(url)
for k, v in headers.items():
req.add_header(k, v)
if not fetch_ec2_metadata_token_disabled(config) and is_instance_metadata_url(url):
# https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html
# IMDSv1 is a request/response method to access instance metadata
# IMDSv2 is a session-oriented method to access instance metadata
# We expect the token retrieve will fail in bridge networking environment (e.g. container) since the default hop
# limit for getting the token is 1. If the token retrieve does timeout, we fallback to use IMDSv1 instead
token = get_aws_ec2_metadata_token()
if token:
req.add_header('X-aws-ec2-metadata-token', token)
request_resp = urlopen(req, timeout=1)
return get_resp_obj(request_resp, url, unsuccessful_resp)
except socket.timeout:
err_msg = 'Request timeout'
except HTTPError as e:
# For instance enable with IMDSv2, Unauthorized 401 error will be thrown,
# to retrieve metadata, the header should embeded with metadata token
if e.code == 401 and retry_with_new_header_token:
token = get_aws_ec2_metadata_token()
req.add_header('X-aws-ec2-metadata-token', token)
request_resp = urlopen(req, timeout=1)
return get_resp_obj(request_resp, url, unsuccessful_resp)
# For instance enable with IMDSv2 and fetch token disabled, Unauthorized 401 error will be thrown
if e.code == 401 and fetch_ec2_metadata_token_disabled(config) and is_instance_metadata_url(url):
logging.warning('Unauthorized request to instance metadata url %s, IMDSv2 is enabled on the instance, while fetching '
'ec2 metadata token is disabled. Please set the value of config item '
'"%s" to "false" in config file %s.' % (url, DISABLE_FETCH_EC2_METADATA_TOKEN_ITEM, CONFIG_FILE))
err_msg = 'Unable to reach the url at %s: status=%d, reason is %s' % (url, e.code, e.reason)
except URLError as e:
err_msg = 'Unable to reach the url at %s, reason is %s' % (url, e.reason)
......@@ -496,7 +584,7 @@ def is_pid_running(pid):
def start_tls_tunnel(child_procs, state_file, command):
# launch the tunnel in a process group so if it has any child processes, they can be killed easily
logging.info('Starting TLS tunnel: "%s"', ' '.join(command))
tunnel = subprocess.Popen(command, preexec_fn=os.setsid, close_fds=True)
tunnel = subprocess.Popen(command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, preexec_fn=os.setsid, close_fds=True)
if not is_pid_running(tunnel.pid):
fatal_error('Failed to initialize TLS tunnel for %s' % state_file, 'Failed to start TLS tunnel.')
......@@ -747,8 +835,8 @@ def recreate_certificate(config, mount_name, common_name, fs_id, credentials_sou
create_public_key(private_key, public_key)
client_info = get_client_info(config)
config_body = create_ca_conf(certificate_config, common_name, tls_paths['mount_dir'], private_key, current_time, region,
fs_id, credentials_source, ap_id=ap_id, client_info=client_info)
config_body = create_ca_conf(config, certificate_config, common_name, tls_paths['mount_dir'], private_key, current_time,
region, fs_id, credentials_source, ap_id=ap_id, client_info=client_info)
if not config_body:
logging.error('Cannot recreate self-signed certificate')
......@@ -797,8 +885,8 @@ def check_and_create_private_key(base_path=STATE_FILE_DIR):
return function()
except OSError as e:
if e.errno == errno.EEXIST:
logging.info('Failed to take out private key creation lock, sleeping 50 ms')
time.sleep(0.05)
logging.info('Failed to take out private key creation lock, sleeping %s (s)' % DEFAULT_TIMEOUT)
time.sleep(DEFAULT_TIMEOUT)
else:
raise
......@@ -820,11 +908,11 @@ def create_certificate_signing_request(config_path, key_path, csr_path):
subprocess_call(cmd, 'Failed to create certificate signing request (csr)')
def create_ca_conf(config_path, common_name, directory, private_key, date, region, fs_id, credentials_source,
def create_ca_conf(config, config_path, common_name, directory, private_key, date, region, fs_id, credentials_source,
ap_id=None, client_info=None):
"""Populate ca/req configuration file with fresh configurations at every mount since SigV4 signature can change"""
public_key_path = os.path.join(directory, 'publicKey.pem')
security_credentials = get_aws_security_credentials(credentials_source, region) if credentials_source else ''
security_credentials = get_aws_security_credentials(config, credentials_source, region) if credentials_source else ''
if credentials_source and security_credentials is None:
logging.error('Failed to retrieve AWS security credentials using lookup method: %s', credentials_source)
......@@ -1175,7 +1263,7 @@ def main():
child_procs = []
if config.getboolean(CONFIG_SECTION, 'enabled'):
if get_boolean_config_item_value(config, CONFIG_SECTION, 'enabled', default_value=True):
logging.info('amazon-efs-mount-watchdog, version %s, is enabled and started', VERSION)
poll_interval_sec = config.getint(CONFIG_SECTION, 'poll_interval_sec')
unmount_grace_period_sec = config.getint(CONFIG_SECTION, 'unmount_grace_period_sec')
......
......@@ -33,7 +33,7 @@ def setup_mocks(mocker):
mocker.patch('mount_efs.start_watchdog')
mocker.patch('mount_efs.get_tls_port_range', return_value=(DEFAULT_TLS_PORT, DEFAULT_TLS_PORT + 10))
mocker.patch('socket.socket', return_value=MagicMock())
mocker.patch('mount_efs.get_dns_name', return_value=DNS_NAME)
mocker.patch('mount_efs.get_dns_name_and_fallback_mount_target_ip_address', return_value=(DNS_NAME, None))
mocker.patch('mount_efs.get_target_region', return_value=REGION)
mocker.patch('mount_efs.write_tls_tunnel_state_file', return_value='~mocktempfile')
mocker.patch('mount_efs.create_certificate')
......@@ -53,7 +53,7 @@ def setup_mocks_without_popen(mocker):
mocker.patch('mount_efs.start_watchdog')
mocker.patch('mount_efs.get_tls_port_range', return_value=(DEFAULT_TLS_PORT, DEFAULT_TLS_PORT + 10))
mocker.patch('socket.gethostname', return_value=DNS_NAME)
mocker.patch('mount_efs.get_dns_name', return_value=DNS_NAME)
mocker.patch('mount_efs.get_dns_name_and_fallback_mount_target_ip_address', return_value=(DNS_NAME, None))
mocker.patch('mount_efs.write_tls_tunnel_state_file', return_value='~mocktempfile')
mocker.patch('os.kill')
......
#
# Copyright 2017-2018 Amazon.com, Inc. and its affiliates. All Rights Reserved.
#
# Licensed under the MIT License. See the LICENSE accompanying this file
# for the specific language governing permissions and limitations under
# the License.
#
import mount_efs
import pytest
from botocore.exceptions import ClientError, NoCredentialsError, EndpointConnectionError
from .. import utils
MOCK_EC2_AGENT = 'fake-client'
AZ_NAME = 'us-east-2b'
AZ_ID = 'use2-az2'
OPERATION_NAME = 'DescribeAvailabilityZones'
def _test_describe_availability_zones_response(mocker, dryrun_effect, response, expected_describe_time, desired_az_id=None,
desired_exception=None, desired_message=None):
describe_mock = mocker.patch('mount_efs.ec2_describe_availability_zones_helper', side_effect=[dryrun_effect, response])