ansible / ansible-modules-extras

Ansible extra modules - these modules ship with ansible
948 stars 1.46k forks source link

lxd_container does not validate HTTPS server certificate #3663

Closed candlerb closed 7 years ago

candlerb commented 7 years ago
ISSUE TYPE
COMPONENT NAME

lxd_container

ANSIBLE VERSION
ansible 2.2.0.0
  config file = /home/brian/ansible-configs/auth-servers/ansible.cfg
  configured module search path = Default w/o overrides
CONFIGURATION

N/A

OS / ENVIRONMENT

N/A

SUMMARY

lxd_container does not appear to validate server certificate when connecting via REST API to a remote host using HTTPS, and therefore seems to be using it insecurely.

STEPS TO REPRODUCE
- hosts: localhost
  tasks:
    - name: create container
      lxd_container:
        url: https://192.168.5.82:8443
        trust_password: XXXXXXXX
        name: foo
        state: started
        source:
          type: image
          mode: pull
          server: https://cloud-images.ubuntu.com/releases
          protocol: simplestreams
          alias: '16.04'

... where 192.168.5.82 is some remote lxd server which has remote access enabled using

lxc config set core.https_address "[::]"
lxc config set core.trust_password XXXXXXXX

Remove trust password and repeat. ansible is happy to talk to the host again (it is caching its own client certificate). However it doesn't appear to have cached the server certificate anywhere, so has no way to confirm it is still talking to the same host.

EXPECTED RESULTS

The 'lxc' command line tool does exactly this:

error: Get https://192.168.5.82:8443/1.0/containers?recursion=1: x509: certificate signed by unknown authority

(Aside: until recently, lxd created certificates with "DNS:192.168.5.82" in the SubjectAltName. This has now been fixed so it has "IP:192.168.5.82". But you still need to accept the certificate since it's self-signed)

ACTUAL RESULTS

No server certificate checking or caching appears to be taking place

NOTES

In lib/ansible/module_utils/lxd.py the ssl context is created using:

            ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)

However according to the documentation Purpose.CLIENT_AUTH is used for when you are building an SSL server. If you are connecting to an SSL server you are supposed to use Purpose.SERVER_AUTH.

(But it's not entirely clear to me what the 'purpose' is doing. It does say that "Passing SERVER_AUTH as purpose sets verify_mode to CERT_REQUIRED" and loads CA certificates. But in this case we may wish to validate the server certificate and present a client certificate to the server)

ansibot commented 7 years ago

@hnakamur ping, this issue is waiting for your response. click here for bot help

hnakamur commented 7 years ago

I think it may be easier to letting the lxc remote add (which is supposed to be called from the new lxd_remote module) to take care of certificates and remove certificated related code from lib/ansible/module_utils/lxd.py and cloud/lxd/lxd_container.py.

However according to the documentation Purpose.CLIENT_AUTH is used for when you are building an SSL server. If you are connecting to an SSL server you are supposed to use Purpose.SERVER_AUTH.

I read the documentation and you are correct. This is my bug. Sorry.

Well, I admit I have not enough knowledge and experience on the LXD on a remote host. I should create an environment to test the LXD on a remote host after all. I'll try it in my spare time.

If you hurry and if you can create a pull request yourself, please go ahead. Thanks.

candlerb commented 7 years ago

The obvious starting point would be to add a server_cert option to the module:

lxd_container:
  url: https://1.2.3.4:8443
  server_cert_file: '{{"{}/.config/lxc/servercerts/foo.yml".format(os.environ["HOME"])}}'
  name: bar

Problems are:

  1. This is no good when you connect for the first time, unless you've already fetched the certificate (which you could do using ansible, connecting to the remote host over ssh and downloading /var/lib/lxd/server.crt)
  2. You have to explicitly point to the right cert file for each URL you use

Or you could match the certificate fingerprint, but again you'd have to copy it from the remote host (parsing lxc info output).

But ultimately this may be another reason to move to named remotes. Suppose you had:

lxd_container:
  remote: foo
  name: bar

or (more simply, and consistent with lxd connection plugin)

lxd_container:
  name: 'foo:bar'

Then when connecting to foo you'd pick up the target URL from ~/.config/lxc/config.yml and use the server certificate from ~/.config/lxc/servercerts/foo.yml

Another advantage of named remotes is that you don't have to provide the trust_key parameter to lxd_container - you'd only have to do this when adding the remote.

ansibot commented 7 years ago

This repository has been locked. All new issues and pull requests should be filed in https://github.com/ansible/ansible

Please read through the repomerge page in the dev guide. The guide contains links to tools which automatically move your issue or pull request to the ansible/ansible repo.

candlerb commented 7 years ago

This issue was moved to ansible/ansible#18882