ansible / proposals

Repository for sharing and tracking progress on enhancement proposals for Ansible.
Creative Commons Zero v1.0 Universal
93 stars 19 forks source link

Proposed: Common Cloud Credential Usage #90

Open ryansb opened 6 years ago

ryansb commented 6 years ago

Proposal: Common Cloud Credentials

Author: @ryansb

Date: 2017/12/18

Motivation

Current ways of handling cloud credentials for AWS and other providers is inconsistent, and some of the common usages result in accidentally creating resources in the wrong region/account

Requirements

Configuration

This would (probably) be implemented as a plugin similarly to the hostvars plugin, that can use things that are vaulted.

Alternative

Bless a few magic variable names, so we take advantage of the existing vaulted-vars and such, allowing all plugins of such-and-such cloud to reach into the vars system and demand those variables be passed if they exist. I think this would have to be implemented as a plugin that, when a task is called, checks if the module is registered as one that needs those special variables.

The blessed variables would have to be specific enough to not break existing playbooks, so something like:

cloud_credentials:
  aws:
    secret_key: ....
    key_id: ....
    region: ....
  gcp:
    pem_file: ....
    service_account: ....
    region: ....

Would be specific enough, and extensible to include any new provider.

Precedence (most to least specific)

  1. Module argument
  2. Environment variables
  3. Cloud creds (new)
  4. User-level config files such as ~/.aws/credentials and ~/.aws/config
  5. System-level config such as /etc/boto.cfg
  6. External config such as EC2 Instance Roles

Considered and not-quite-right

Repeated Variables

The most common way in examples & playbooks I see around is to set variables aws_*_key and aws_region and use those throughout:

- hosts: localhost
  vars:
    aws_secret_access_key: sekrit
    aws_access_key_id: AKAI....
    aws_region: us-weast-9
  tasks:
  - ec2:
      region: "{{ aws_region }}"
      aws_secret_access_key: "{{ aws_secret_access_key }}"
      aws_access_key_id: "{{ aws_access_key_id }}"

I also have seen lots of IRC problems where people forget to copy/paste the region on all tasks, and are then surprised their playbook doesn't work right outside us-east-1 (the default region).

Environment Variables

Today, Tower passes AWS credentials in this way. It sets the AWS_ACCESS/SECRET_KEY variables and those env vars are used as normal. Tower provides a nice UI for this so most users of Tower wouldn't need/notice a new way of providing cloud credentials.

For sections of plays, users can do:

- block:
    environment:
      AWS_SECRET_ACCESS_KEY: .....
    tasks:
      - ec2: ....

But that means users have to sometimes put full playbooks inside of blocks, and if they want to have separate error/rescue handling they then have to nest blocks.

YAML Anchors

YAML has a feature that lets you create prototypes based on an item, you declare it with &your_anchor_name and then it can be exploded into any similar type. In some tests we use a YAML anchor to share credentials among all the AWS tasks instead of repeating var-usage (not-quite-right #1)

- set_fact:
    aws_creds: &aws_creds
      aws_secret_access_key: sekrit
      aws_access_key_id: AKAI....
      region: us-weast-9
- ec2:
    <<: *aws_creds
    arg1: val1
    arg2: val2

Other Considerations

Also to consider: MFA/role users. I don't want to reimplement boto profiles because madness lies down that path, but for other cloud providers we're going to have similar requirements and their MFA/SDK situation isn't as nice.

dagwieers commented 6 years ago

This looks similar as the proposal I did at #81, where we have a similar need for REST-based modules.

I would not make the solution cloud-specific, the solution should be generically applicable to any module, or set of modules.

nitzmahone commented 6 years ago

Yeah, we don't have a content-hosted way to do that today in Azure either. We implemented something akin to boto profiles just for the Azure modules, but they live in ~/.azure/credentials, not next to the content.

Microsoft has since come up with "CLI profiles", but IIRC they don't actually let you store the credentials- merely a set of properties like "username/cloud", then they prompt for a password or open a browser window to complete auth and give you a 12h token which can be used transparently by the SDK. I also don't think you can have multiple of those going at a time- IIRC the CLI credentials can only have one set "active" at a time.

I proposed a similar thing a couple years ago for exactly this scenario (before proposals were a thing), and actually even had a sample implementation, but it got shot down. IIRC there were concerns around the security of carving off sensitive hostvars and blindly sending them to modules. In some ways it's a similar problem to allowing modules access to the add_host functionality; we probably need a config-based whitelist or something to enable it, set to some sane defaults. Since that's gotten a lot easier with the new config stuff, maybe people would be more open to it now.

willthames commented 6 years ago

Environment variables (as used with the block/environment scenario) also don't fly when debugging modules. Module arguments get preserved by explode, environment variables are passed outside of python. Presumably this also means they leak at -vvv level.

ryansb commented 6 years ago

I don't think it would be a blind-sending, any more than environment variables are already auto-sent to every executing module. I think it would be, at worst, the same security surface area as the current way that users (and AWX) ship in environment variables.

bcoca commented 6 years ago

@ryansb we don't automatically ship environment variables with modules, those plugins will only get them if they execute locally via normal process inheritance. The environment keyword would not be a change for 'local' execution but it would be very different for 'remote execution'.

bcoca commented 6 years ago

https://github.com/ansible/proposals/issues/24 <= a general version of this, not just cloud but any credentials that get reused