Allow credentials from ${HOME}/.aws/credentials

This matches the configuration from popular tools like AWS CLI and
allows multiple profile names via -o profile=name.  The existing
credential mechanisms continue to work.  Fixes #822.
This commit is contained in:
Andrew Gaul 2018-11-04 11:41:49 -08:00
parent e8d76a6f58
commit 9e530c86ae
3 changed files with 81 additions and 0 deletions

View File

@ -87,6 +87,10 @@ Keep in mind using the pre-built packages when available.
Examples Examples
-------- --------
s3fs supports the standard
[AWS credentials file](https://docs.aws.amazon.com/cli/latest/userguide/cli-config-files.html)
stored in `${HOME}/.aws/credentials`. Alternatively, s3fs supports a custom passwd file.
The default location for the s3fs password file can be created: The default location for the s3fs password file can be created:
* using a .passwd-s3fs file in the users home directory (i.e. ~/.passwd-s3fs) * using a .passwd-s3fs file in the users home directory (i.e. ~/.passwd-s3fs)

View File

@ -20,6 +20,8 @@ For unprivileged user.
.SH DESCRIPTION .SH DESCRIPTION
s3fs is a FUSE filesystem that allows you to mount an Amazon S3 bucket as a local filesystem. It stores files natively and transparently in S3 (i.e., you can use other programs to access the same files). s3fs is a FUSE filesystem that allows you to mount an Amazon S3 bucket as a local filesystem. It stores files natively and transparently in S3 (i.e., you can use other programs to access the same files).
.SH AUTHENTICATION .SH AUTHENTICATION
s3fs supports the standard AWS credentials (filehttps://docs.aws.amazon.com/cli/latest/userguide/cli-config-files.html) stored in `${HOME}/.aws/credentials`.
Alternatively, s3fs supports a custom passwd file.
The s3fs password file has this format (use this format if you have only one set of credentials): The s3fs password file has this format (use this format if you have only one set of credentials):
.RS 4 .RS 4
\fBaccessKeyId\fP:\fBsecretAccessKey\fP \fBaccessKeyId\fP:\fBsecretAccessKey\fP
@ -133,6 +135,10 @@ This option specifies the configuration file path which file is the additional H
A sample configuration file is uploaded in "test" directory. A sample configuration file is uploaded in "test" directory.
If you specify this option for set "Content-Encoding" HTTP header, please take care for RFC 2616. If you specify this option for set "Content-Encoding" HTTP header, please take care for RFC 2616.
.TP .TP
\fB\-o\fR profile (default="default")
Choose a profile from ${HOME}/.aws/credentials to authenticate against S3.
Note that this format matches the AWS CLI format and differs from the s3fs passwd format.
.TP
\fB\-o\fR public_bucket (default="" which means disabled) \fB\-o\fR public_bucket (default="" which means disabled)
anonymously mount a public bucket when set to 1, ignores the $HOME/.passwd-s3fs and /etc/passwd-s3fs files. anonymously mount a public bucket when set to 1, ignores the $HOME/.passwd-s3fs and /etc/passwd-s3fs files.
S3 does not allow copy object api for anonymous users, then s3fs sets nocopyapi option automatically when public_bucket=1 option is specified. S3 does not allow copy object api for anonymous users, then s3fs sets nocopyapi option automatically when public_bucket=1 option is specified.

View File

@ -103,6 +103,7 @@ std::string cipher_suites = "";
std::string instance_name = ""; std::string instance_name = "";
s3fs_log_level debug_level = S3FS_LOG_CRIT; s3fs_log_level debug_level = S3FS_LOG_CRIT;
const char* s3fs_log_nest[S3FS_LOG_NEST_MAX] = {"", " ", " ", " "}; const char* s3fs_log_nest[S3FS_LOG_NEST_MAX] = {"", " ", " ", " "};
std::string aws_profile = "default";
//------------------------------------------------------------------- //-------------------------------------------------------------------
// Static variables // Static variables
@ -194,6 +195,7 @@ static int s3fs_check_service(void);
static int parse_passwd_file(bucketkvmap_t& resmap); static int parse_passwd_file(bucketkvmap_t& resmap);
static int check_for_aws_format(const kvmap_t& kvmap); static int check_for_aws_format(const kvmap_t& kvmap);
static int check_passwd_file_perms(void); static int check_passwd_file_perms(void);
static int read_aws_credentials_file(const std::string &filename);
static int read_passwd_file(void); static int read_passwd_file(void);
static int get_access_keys(void); static int get_access_keys(void);
static int set_mountpoint_attribute(struct stat& mpst); static int set_mountpoint_attribute(struct stat& mpst);
@ -3990,6 +3992,61 @@ static int check_passwd_file_perms(void)
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
static int read_aws_credentials_file(const std::string &filename)
{
// open passwd file
ifstream PF(filename.c_str());
if(!PF.good()){
return -1;
}
string profile;
string accesskey;
string secret;
// read each line
string line;
while(getline(PF, line)){
line = trim(line);
if(0 == line.size()){
continue;
}
if('#' == line[0]){
continue;
}
if(line.size() > 2 && line[0] == '[' && line[line.size() - 1] == ']') {
if(profile == aws_profile){
break;
}
profile = line.substr(1, line.size() - 2);
accesskey.clear();
secret.clear();
}
size_t pos = line.find_first_of('=');
if(pos == string::npos){
continue;
}
string key = trim(line.substr(0, pos));
string value = trim(line.substr(pos + 1, string::npos));
if(key == "aws_access_key_id"){
accesskey = value;
}else if(key == "aws_secret_access_key"){
secret = value;
}
}
if(profile != aws_profile){
return EXIT_FAILURE;
}
if(!S3fsCurl::SetAccessKey(accesskey.c_str(), secret.c_str())){
S3FS_PRN_EXIT("failed to set internal data for access key/secret key from aws credential file.");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
// //
// read_passwd_file // read_passwd_file
// //
@ -4071,6 +4128,7 @@ static int read_passwd_file(void)
// keys: // keys:
// //
// 1 - from the command line (security risk) // 1 - from the command line (security risk)
// 1a - from ${HOME}/.aws/credentials
// 2 - from a password file specified on the command line // 2 - from a password file specified on the command line
// 3 - from environment variables // 3 - from environment variables
// 4 - from the users ~/.passwd-s3fs // 4 - from the users ~/.passwd-s3fs
@ -4093,6 +4151,15 @@ static int get_access_keys(void)
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
// 1a - check ${HOME}/.aws/credentials
std::string aws_credentials = std::string(getpwuid(getuid())->pw_dir) + "/.aws/credentials";
if(read_aws_credentials_file(aws_credentials) == EXIT_SUCCESS) {
return EXIT_SUCCESS;
}else if(aws_profile != "default"){
S3FS_PRN_EXIT("Could not find profile: %s in file: %s", aws_profile.c_str(), aws_credentials.c_str());
return EXIT_FAILURE;
}
// 2 - was specified on the command line // 2 - was specified on the command line
if(passwd_file.size() > 0){ if(passwd_file.size() > 0){
ifstream PF(passwd_file.c_str()); ifstream PF(passwd_file.c_str());
@ -4571,6 +4638,10 @@ static int my_fuse_opt_proc(void* data, const char* arg, int key, struct fuse_ar
return 0; return 0;
} }
} }
if(0 == STR2NCMP(arg, "profile=")){
aws_profile = strchr(arg, '=') + sizeof(char);
return 0;
}
if(0 == STR2NCMP(arg, "public_bucket=")){ if(0 == STR2NCMP(arg, "public_bucket=")){
off_t pubbucket = s3fs_strtoofft(strchr(arg, '=') + sizeof(char)); off_t pubbucket = s3fs_strtoofft(strchr(arg, '=') + sizeof(char));
if(1 == pubbucket){ if(1 == pubbucket){