From 9e530c86ae7ada4598f8de9e77bcca59d5292c82 Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Sun, 4 Nov 2018 11:41:49 -0800 Subject: [PATCH] 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. --- README.md | 4 +++ doc/man/s3fs.1 | 6 +++++ src/s3fs.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) diff --git a/README.md b/README.md index 5a7c83e..f6de81a 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,10 @@ Keep in mind using the pre-built packages when available. 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: * using a .passwd-s3fs file in the users home directory (i.e. ~/.passwd-s3fs) diff --git a/doc/man/s3fs.1 b/doc/man/s3fs.1 index 89d81de..6a01ad0 100644 --- a/doc/man/s3fs.1 +++ b/doc/man/s3fs.1 @@ -20,6 +20,8 @@ For unprivileged user. .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). .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): .RS 4 \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. If you specify this option for set "Content-Encoding" HTTP header, please take care for RFC 2616. .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) 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. diff --git a/src/s3fs.cpp b/src/s3fs.cpp index 56b3dda..3a37986 100644 --- a/src/s3fs.cpp +++ b/src/s3fs.cpp @@ -103,6 +103,7 @@ std::string cipher_suites = ""; std::string instance_name = ""; s3fs_log_level debug_level = S3FS_LOG_CRIT; const char* s3fs_log_nest[S3FS_LOG_NEST_MAX] = {"", " ", " ", " "}; +std::string aws_profile = "default"; //------------------------------------------------------------------- // Static variables @@ -194,6 +195,7 @@ static int s3fs_check_service(void); static int parse_passwd_file(bucketkvmap_t& resmap); static int check_for_aws_format(const kvmap_t& kvmap); 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 get_access_keys(void); static int set_mountpoint_attribute(struct stat& mpst); @@ -3990,6 +3992,61 @@ static int check_passwd_file_perms(void) 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 // @@ -4071,6 +4128,7 @@ static int read_passwd_file(void) // keys: // // 1 - from the command line (security risk) +// 1a - from ${HOME}/.aws/credentials // 2 - from a password file specified on the command line // 3 - from environment variables // 4 - from the users ~/.passwd-s3fs @@ -4093,6 +4151,15 @@ static int get_access_keys(void) 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 if(passwd_file.size() > 0){ 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; } } + if(0 == STR2NCMP(arg, "profile=")){ + aws_profile = strchr(arg, '=') + sizeof(char); + return 0; + } if(0 == STR2NCMP(arg, "public_bucket=")){ off_t pubbucket = s3fs_strtoofft(strchr(arg, '=') + sizeof(char)); if(1 == pubbucket){