├── examples
├── f5_bigip
│ ├── hosts
│ ├── venafi_ansible_role.png
│ ├── variables.yaml
│ ├── f5_delete_playbook.yaml
│ ├── f5_create_playbook.yaml
│ └── README.md
├── citrix_adc
│ ├── hosts
│ ├── venafi_ansible_role.png
│ ├── variables.yaml
│ ├── citrix_delete_playbook.yaml
│ ├── citrix_create_playbook.yaml
│ └── README.md
├── logo_tile_f5.png
├── logo_tile_citrix.png
└── README.md
├── requirements.txt
├── vars
└── main.yml
├── handlers
└── main.yml
├── ansible.cfg
├── Venafi_logo.png
├── SECURITY.md
├── tests
├── Dockerfile
├── inventory
├── assets
│ ├── valid_ec_key.pem
│ ├── invalid_date_rsa2048_cert.pem
│ ├── valid_rsa2048_key.pem
│ ├── valid_alt_rsa2048_key.pem
│ ├── valid_rsa2048_chain.pem
│ ├── invalid_cert.pem
│ ├── valid_rsa2048_cert.pem
│ ├── invalid_cn_rsa2048_cert.pem
│ └── valid_alt_rsa2048_cert.pem
├── example-vars.yml
├── test_venafi_certificate.py
├── original-ansible-crypto-playbook-example.yml
├── venafi-role-playbook-example.yml
└── venafi-playbook-example.yml
├── molecule
└── default
│ ├── Dockerfile.j2
│ ├── INSTALL.rst
│ ├── molecule.yml
│ └── playbook.yml
├── .gitignore
├── .github
└── ISSUE_TEMPLATE
│ ├── config.yml
│ ├── ask_a_question.md
│ ├── request_a_feature.md
│ └── report_a_bug.md
├── .yamllint
├── tasks
├── main.yml
├── remote-certificate.yml
└── local-certificate.yml
├── defaults
└── main.yml
├── meta
└── main.yml
├── Makefile
├── LICENSE
├── README.md
└── library
└── venafi_certificate.py
/examples/f5_bigip/hosts:
--------------------------------------------------------------------------------
1 | localhost
2 |
--------------------------------------------------------------------------------
/examples/citrix_adc/hosts:
--------------------------------------------------------------------------------
1 | localhost
2 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | vcert~=0.10.0
2 | ansible
3 | cryptography
--------------------------------------------------------------------------------
/vars/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # vars file for venafi-certificate
3 |
--------------------------------------------------------------------------------
/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # handlers file for venafi-certificate
3 |
--------------------------------------------------------------------------------
/ansible.cfg:
--------------------------------------------------------------------------------
1 | [defaults]
2 | roles_path = ../:../tests/roles
3 | library = ./library
4 |
--------------------------------------------------------------------------------
/Venafi_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Venafi/ansible-role-venafi/HEAD/Venafi_logo.png
--------------------------------------------------------------------------------
/examples/logo_tile_f5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Venafi/ansible-role-venafi/HEAD/examples/logo_tile_f5.png
--------------------------------------------------------------------------------
/examples/logo_tile_citrix.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Venafi/ansible-role-venafi/HEAD/examples/logo_tile_citrix.png
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | If you believe you have found a security issue, please report it to opensource@venafi.com. Venafi takes security _very_ seriously.
2 |
--------------------------------------------------------------------------------
/examples/citrix_adc/venafi_ansible_role.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Venafi/ansible-role-venafi/HEAD/examples/citrix_adc/venafi_ansible_role.png
--------------------------------------------------------------------------------
/examples/f5_bigip/venafi_ansible_role.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Venafi/ansible-role-venafi/HEAD/examples/f5_bigip/venafi_ansible_role.png
--------------------------------------------------------------------------------
/tests/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.7.10
2 | RUN apt-get update && apt-get install -y --no-install-recommends python-pip python-setuptools python3-pip python3-setuptools
3 |
--------------------------------------------------------------------------------
/tests/inventory:
--------------------------------------------------------------------------------
1 | [robots]
2 | venafi-cert-python3 image="local-ansible-test"
3 | venafi-cert-python2 image="local-ansible-test" ansible_python_interpreter="/usr/bin/python2.7"
4 |
--------------------------------------------------------------------------------
/molecule/default/Dockerfile.j2:
--------------------------------------------------------------------------------
1 | FROM python:3.7.10
2 | RUN apt-get update && apt-get install -y --no-install-recommends python-pip python-setuptools python3-pip python3-setuptools
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | .idea
3 | venv
4 | venv27
5 | .venv37
6 | venafi_certificate_*.json
7 | *_credentials.yml
8 | credentials.yml
9 | vault-password.txt
10 | tests/library
11 | tests/roles/
12 | *.retry
13 | .cache
14 |
--------------------------------------------------------------------------------
/tests/assets/valid_ec_key.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN EC PRIVATE KEY-----
2 | MHcCAQEEIHxXL1YaMh+1kb8F7U+3PB3jqMGSWZHzrnUrl2T8JG4koAoGCCqGSM49
3 | AwEHoUQDQgAEBXh8ZfUyKSLhJlRBrcJxmHOBc4HGDLLjZe9F+NppFiajSmZooro3
4 | iyv/TSCJZUxG1x8rg3jQOi6We4kf29y68Q==
5 | -----END EC PRIVATE KEY-----
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Join us on Slack!
4 | url: https://join.slack.com/t/venafi-integrations/shared_invite/zt-i8fwc379-kDJlmzU8OiIQOJFSwiA~dg
5 | about: Interact with Venafi and the customer community in real-time!
6 |
--------------------------------------------------------------------------------
/.yamllint:
--------------------------------------------------------------------------------
1 | extends: default
2 |
3 | rules:
4 | braces:
5 | max-spaces-inside: 1
6 | level: error
7 | brackets:
8 | max-spaces-inside: 1
9 | level: error
10 | line-length: disable
11 | # NOTE(retr0h): Templates no longer fail this lint rule.
12 | # Uncomment if running old Molecule templates.
13 | # truthy: disable
14 |
--------------------------------------------------------------------------------
/molecule/default/INSTALL.rst:
--------------------------------------------------------------------------------
1 | ***********************************
2 | Docker driver installation guide
3 | ***********************************
4 |
5 | Requirements
6 | ============
7 |
8 | * General molecule dependencies (see https://molecule.readthedocs.io/en/latest/installation.html)
9 | * Docker Engine
10 | * docker-py
11 | * docker
12 |
13 | Install
14 | =======
15 |
16 | $ sudo pip install docker-py
17 |
--------------------------------------------------------------------------------
/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: "Include vars of {{ credentials_file }} into the venafi variable."
3 | include_vars:
4 | file: "{{ credentials_file }}"
5 | name: venafi
6 |
7 | - name: "show execution mode"
8 | debug:
9 | msg: "certificate_remote_execution is {{ certificate_remote_execution }}"
10 |
11 | - import_tasks: local-certificate.yml
12 | when: not certificate_remote_execution
13 |
14 | - import_tasks: remote-certificate.yml
15 | when: certificate_remote_execution
16 |
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # Venafi Ansible Role examples
2 |
3 | In this section you'll find functional examples you can use to better secure application delivery using the _Venafi Ansible Role_ with F5 BIG-IP or Citrix ADC.
4 |
5 | Adding Venafi enables you to manage certificates more securely as part of the TLS termination process on your application delivery controller (ADC).
6 |
7 | **Examples**:
8 |
9 | [SSL Termination with F5 BIG-IP](f5_bigip)
10 |
11 | [SSL Termination with Citrix ADC](citrix_adc)
12 |
--------------------------------------------------------------------------------
/molecule/default/molecule.yml:
--------------------------------------------------------------------------------
1 | ---
2 | molecule:
3 | ignore_paths:
4 | - ../../.git
5 | - ../../.venv37
6 | - ../../venv27
7 | - ../../venv
8 | - ../../.molecule
9 | dependency:
10 | name: galaxy
11 | driver:
12 | name: docker
13 | lint:
14 | name: yamllint
15 | platforms:
16 | - name: instance
17 | image: python:3.7.10
18 | provisioner:
19 | name: ansible
20 | log: true
21 | lint:
22 | name: ansible-lint
23 | scenario:
24 | name: default
25 | verifier:
26 | name: testinfra
27 | lint:
28 | name: flake8
29 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/ask_a_question.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\U0001F4AC Ask a question"
3 | about: How can we help you?
4 |
5 | ---
6 |
7 | Please send your questions about setup and general usage to opensource@venafi.com rather
8 | than creating GitHub issues.
9 |
10 | The Venafi team is constantly monitoring the opensource@venafi.com inbox so you'll generally
11 | receive a faster response with more detail than if you were to post an issue here on GitHub.
12 | It also allows for you to share information that you might not be comfortable sharing in a
13 | public forum.
14 |
15 | Thanks!
16 |
--------------------------------------------------------------------------------
/examples/citrix_adc/variables.yaml:
--------------------------------------------------------------------------------
1 | adc_address: "192.168.5.188"
2 | adc_username: "youruser"
3 | adc_password: "youtpassword"
4 |
5 | test_site:
6 | name: "demo-citrix"
7 | domain: "venafi.example"
8 |
9 | adc_virtual_ip: "192.168.3.167"
10 | adc_virtual_port: "443"
11 |
12 | http_service: 192.168.6.201
13 | port1: 8001
14 | port2: 8002
15 | port3: 8003
16 |
17 | cert_name: "{{ test_site.name }}.crt"
18 | key_name: "{{ test_site.name }}.key"
19 | chain_name: "{{ test_site.name }}-ca-bundle.crt"
20 |
21 | adc_provider:
22 | nsip: "{{ adc_address }}"
23 | nitro_user: "{{ adc_username }}"
24 | nitro_pass: "{{ adc_password }}"
25 |
--------------------------------------------------------------------------------
/examples/f5_bigip/variables.yaml:
--------------------------------------------------------------------------------
1 | f5_address: "yourf5bigip"
2 | f5_username: "youruser"
3 | f5_password: "yourpassword"
4 |
5 | test_site:
6 | name: "demo-f5"
7 | domain: "venafi.example"
8 |
9 | f5_partition: "Demo"
10 | f5_virtual_ip: "192.168.7.68"
11 | f5_virtual_port: "443"
12 | f5_pool_members:
13 | - host: 192.168.6.201
14 | port: 8001
15 | - host: 192.168.6.201
16 | port: 8002
17 | - host: 192.168.6.201
18 | port: 8003
19 |
20 | cert_name: "{{ test_site.name }}.crt"
21 | key_name: "{{ test_site.name }}.key"
22 | chain_name: "{{ test_site.name }}-ca-bundle.crt"
23 |
24 | f5_provider:
25 | server: "{{ f5_address }}"
26 | server_port: 443
27 | user: "{{ f5_username }}"
28 | password: "{{ f5_password }}"
29 | validate_certs: no
30 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/request_a_feature.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\U0001F680 Request a feature"
3 | about: Tell us about your idea for improvement!
4 | labels: enhancement
5 |
6 | ---
7 |
8 |
14 |
15 | **BUSINESS PROBLEM**
16 |
17 |
18 | **PROPOSED SOLUTION**
19 |
20 |
21 | **CURRENT ALTERNATIVES**
22 |
23 |
24 | **VENAFI EXPERIENCE**
25 |
26 |
--------------------------------------------------------------------------------
/tests/assets/invalid_date_rsa2048_cert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIC9jCCAd6gAwIBAgIJAIH9NNbfhhIoMA0GCSqGSIb3DQEBCwUAMCUxIzAhBgNV
3 | BAMMGnRlc3QxMjMudmVuYWZpLmV4YW1wbGUuY29tMB4XDTE4MDEzMDE4MjMyM1oX
4 | DTE4MDMwMTE4MjMyM1owJTEjMCEGA1UEAwwadGVzdDEyMy52ZW5hZmkuZXhhbXBs
5 | ZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDjQoGFPysXp15K
6 | QTERbffhiLBaWekmN/NFsXrjTU2b/6RVxl8bbQiI2hjXuuUJwEc3HpAoGo/g307W
7 | YyZXRDQUJe6y88+wmrKZ1+f0QkvEFIVUuWGZy4KZvXRjDv+5j1XJUpbbQK/LV2vd
8 | I2BdNAjSw46Sb5Ik+ESIVS5efAxK28GaoVuOnHzp0sKfKbBIMTy+C9ScACTYwXq8
9 | qx/NzWqqKrQ4i9XQUPXawfZRuKrVE0o9iTNs2ffzZ9Us4/f5UGdR7O5LLdV5a4/d
10 | rk+UFMhRpAsfDcsq/QdX14pGwFBqAw42npLxD4bgQB2x2MwmqagkbskxTT9Vjdwh
11 | QUxNmEIDAgMBAAGjKTAnMCUGA1UdEQQeMByCGnRlc3QxMjMudmVuYWZpLmV4YW1w
12 | bGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQDYR6+aWO6aHZ0jHcLQ2t3rxE1rYREM
13 | CrIwM7/0kW2CKxkpF+XEPkz/QKovVoll8TM3xYAtODD31tLvT8WNCzulm6LMqL3K
14 | Q64JYAD5O7GHjn16cDLZJpYOvmsKtWbsZwGG8AjOAlHnG8dhZFHRN3ytiV1NxY7n
15 | czjURe7KNa+QjvISd2Cq0o+Ra+AbS5Z083r0MAEQDeVgWFAfO6QyepHSypbZ1GdR
16 | xlZLa9QIhjOZj2kbC3MsDsv2CARl3ocSiHSnrFTqrXZV8ciYtuiM4WhdxbVd4MaZ
17 | joZPR+GhLd9YWiQq7Zt/A8h9Z4DNsF5gsX+zDn6XTMWEJCzRrEGUWo7y
18 | -----END CERTIFICATE-----
19 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/report_a_bug.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\U0001F41B Report a bug"
3 | about: Let us know what isn't working right!
4 | labels: bug
5 |
6 | ---
7 |
8 |
14 |
15 | **PROBLEM SUMMARY**
16 |
17 |
18 | **STEPS TO REPRODUCE**
19 |
20 |
21 | **EXPECTED RESULTS**
22 |
23 |
24 | **ACTUAL RESULTS**
25 |
26 |
27 | **ENVIRONMENT DETAILS**
28 |
29 |
30 | **COMMENTS/WORKAROUNDS**
31 |
32 |
--------------------------------------------------------------------------------
/tasks/remote-certificate.yml:
--------------------------------------------------------------------------------
1 | # Generates certificates on remote host
2 | ---
3 | - name: "Enroll Venafi certificate on remote host"
4 | venafi_certificate:
5 | url: "{{ venafi.url | default(omit) }}"
6 | token: "{{ venafi.token | default(omit) }}"
7 | zone: "{{ venafi.zone | default(omit) }}"
8 | test_mode: "{{ venafi.test_mode if venafi.test_mode is defined else 'false' }}"
9 | user: "{{ venafi.user | default(omit) }}"
10 | password: "{{ venafi.password | default(omit) }}"
11 | access_token: "{{ venafi.access_token | default(omit) }}"
12 | trust_bundle: "{{ venafi.trust_bundle | default(omit) }}"
13 | cert_path: "{{ certificate_cert_path }}"
14 | chain_path: "{{ certificate_chain_path | default(omit) }}"
15 | privatekey_path: "{{ certificate_privatekey_path | default(omit) }}"
16 | privatekey_type: "{{ certificate_privatekey_type | default(omit) }}"
17 | privatekey_size: "{{ certificate_privatekey_size | default(omit) }}"
18 | common_name: "{{ certificate_common_name }}"
19 | alt_name: "{{ certificate_alt_name | default([]) }}"
20 | before_expired_hours: "{{ certificate_before_expired_hours | default(omit) }}"
21 | force: "{{ certificate_force if certificate_force is defined else false }}"
22 | register: certout
23 | - name: "dump test output"
24 | debug:
25 | msg: "{{ certout }}"
26 |
--------------------------------------------------------------------------------
/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | credentials_file: credentials.yml
3 | # Use Ansible host FQDN for certificate common name
4 | certificate_common_name: "{{ ansible_fqdn }}"
5 |
6 | # Directory where to place certificates
7 | certificate_cert_dir: "/etc/ssl/{{ certificate_common_name }}"
8 | # Paths for certificate and keys
9 | certificate_cert_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.pem"
10 | certificate_chain_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.chain.pem"
11 | certificate_privatekey_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.key"
12 | certificate_csr_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.csr"
13 |
14 | # Where to execute venafi_certificate module. If set to false, certificate will be
15 | # created on ansible master host and then copied to the remote server
16 | certificate_remote_execution: false
17 | # remote location where to place the certificate_
18 | certificate_remote_cert_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.pem"
19 | certificate_remote_chain_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.chain.pem"
20 | certificate_remote_privatekey_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.key"
21 | # Set to false, if you don't want to copy private key to remote location
22 | certificate_copy_private_key_to_remote: true
23 |
--------------------------------------------------------------------------------
/tests/assets/valid_rsa2048_key.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIIEowIBAAKCAQEA0MILue7W4ZcQZbe7wHc1CjTsunsyuWGIU6tNxDH4PW4RyclJ
3 | jQb0jgPTJOxTZ23r8LmnXv7luOyQt09K9bMpEta+925SpILSSVzOXqUKvDxznGd6
4 | LyAP4Md5Yre7g05XJV1pdv7bysbMd2zs8uyYW2n7lacNny9674yXKZ3qcf9FxWEJ
5 | C4RPTd8rDrqg7NXK7wJ3dg9tzVmDKrL6NEbE6cjFNyypWApMvh1Hneed6qeVL1xX
6 | TnQjbJ2vRVI0qJzhm3TgnhaDj1xHFQPDA4UOn1PprAjPiNptmE7OHFB63uoyCSIs
7 | O+yMRQCFDxH77yV8Js3b8IYn3TryahxuDlTlEQIDAQABAoIBADvNOrq61setFL9u
8 | 0NQj0gfofWA2ZqOAcyM00YRApFJEs8fQZ8eElI4SPmw3XvUkIhDFvlKSRpChBBvf
9 | FSQpfLyu0+nhqr8B0ue9NEkckmS2FJBbfD7/uky0F+vVolvNF13W0p2KCMCgDnav
10 | t3knmcWmKLIINvjBC0CQT8VWPZEGAQeBTR14DSefZ5nYAecpJS8ovIgT+9zNkN4R
11 | NWYHv1tJuykEXLC+dnxFrqhDH5KLW6+gCk/SAAPW7n+501SbcEU1NH+XG1OS6Vpr
12 | hDQv+4rs7dgboRNvwwJ9sAPvJvMXurKP5KwMzVz9Rvt/pSTtDZpVAN27y/L1YDVx
13 | /FrT9nkCgYEA8m0L14tm+u3C4OSs2lrxXqL+6xKQFuNNL+73wL6fD2QpE8+xlkna
14 | sbI3w5hUY3wBR2cRb3LE8XBuan+3NTRpbPCwDU7/v1j1dfFPSyQjjQcbJWtK7KDz
15 | KIqr/ja9+KZmB7CR0jrPmC/E9M4era97GiKxEcSxMkSSrTf0Q4om/qMCgYEA3HJm
16 | a7AY0j0x7UJ3pejf+RjMb7cGFw9yDaQLdxqpMKrHxr9wTls9f1sJcnyejFxyWRY2
17 | NRD/E5RLKbyS9y9GV9T+IJ0QdEULFuHp4pdMkLDXek4Vtmk97uz0dPNbG5EH+QHi
18 | 2+2DCiYM9NlBL16I1NarYJ6VF93a3upm+cVFzLsCgYA/BWQXs5Cg2OBZcHkTBqNK
19 | s9rLJ3c3y/1L4bacb7GP4bzf/pUu+aIVobvnaBlAB4OZmUyqCU7zaQP7QZpSBX8G
20 | qAdMXmYTyz98Wq//W7S2O8utzZebrjeoKWyO2JJIKpHJm/g8i7dd28U60r6c7kp1
21 | P+GCOfzKkZMD3tDsIeUDOwKBgQDS5NrZAQHt979Q1Pq0DUJgAvppkbXQz+Tz6dFS
22 | I35i4a73k2O0gpMlyIkULuZbL4Hxek9nmxf7ui3iAtayhVaNZmWr+7anFPpT7NKd
23 | BNOpxJSQHC2nca1fau8/ByVNDQWMkeJfNizbw6U3hLRYxd6vh6MybuQBjiv6gFL9
24 | dl2dSwKBgEGzBxA9Ju1CoA1HMn1yefuIeO5T3BfCHRrcYV9+mngUHuAVyqvXiGKx
25 | ItkNpLzL1XnPirgWpuLLeoJFvgBLEnVmKfe3NjkBauiyHrmonwKzeSXcQH8U/qGT
26 | 2b66skeb3XSNGjsIl3bJ07p9y0uSPFwduOPhi6EzdVPWQpbHiy3v
27 | -----END RSA PRIVATE KEY-----
--------------------------------------------------------------------------------
/tests/assets/valid_alt_rsa2048_key.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIIEpAIBAAKCAQEAwwwRWMAHM58oKfdpKmZDOOKEV2BY6ApyuQGi2EpSX0ErXJj2
3 | bBGdQ3oq25Gpm7qAUH3jxbxq8FOl+47iVq9GLQtYii9D14QrXk6TehBkR7DEyPMO
4 | 26zvcz7kN3tHxIdsL1JsZto6/va1+rukcFGxNBUnASbqPwK9GYDyf9GpOglVewxx
5 | fjmDoxlp+LIzQnT5wO0XB00k2sNC4smvvh58TLjS0VyMmIJpiKz5l1QRk77X6aXA
6 | 7Mkv5wuDz6tDrMsCOOXidAUepSarNjYeA5/K3Q+Yr/M30ZbfDIp3WPcZQ2xLVdS7
7 | VLxTy2Z3WptBiunfr9L/Teb6/IJJM5942jZyUQIDAQABAoIBAFWskFYOi1tGdsrw
8 | TTZ9ktuCzCThQkwygq/3IwOkqcgDhTt4SQ2xMwX3vKmTsw+ffUtd8NXjbInCBG+a
9 | FJuFA+s3R53zIvagrLgRBcChQJso71dhRE6ECO3zWyVvtleCMKgcqVcIVs+1qLvI
10 | /nMDvzWkcOyVnmEtH8j7FgnFG3NM7rVVv5uFmvYWp9t6x7d6kiIRd46aMk4xPq/7
11 | zVKIsUrjKnsc03X9YDcmNGB9CH72g8vzyAX6ob3Gfro2ZIdgPBBlyok7yAK2FAS8
12 | /UaF+UXI8+bPnCs71h+YPQJ0P5abWDxIB8mLB4P3gIw7jCh2ePM43r1K+JvEbnQ4
13 | E3PCP90CgYEA+mltmYc9HGwTFbah990rIQZIyg+Wzu7FpYTAoEnToanf93T27xOD
14 | 5Ksti3k5yrGqTxcoE0hyMNn4DciUgNWVaa2aIcI1YmTPyjaRq7MeHrHv4nbndAJ0
15 | CiyXWKRH+Dthyn4Rw8bRfi6R2Bsn7+TSL5NLci4TWuOIkoYl2tc51wsCgYEAx2ZZ
16 | FpPK6O32yzDHYS3mocYt8H+keHnoWlShmP0ptjSkZPVVmBZr2v0Cr0maY2BC8ttn
17 | xnkbCK/3GNv+KmaMGiTHoaY0EmIz58/sSIWsy8jtKtZrSulX6PyZcoq75hu7AtMn
18 | F3K3LxWK7AgS0Y4qPmaOPTPNfXrVJN0i5YVeRZMCgYEA9uKXAjVJ1QngzxmfGudd
19 | rFOr1DwGbcMP7p6x49al5s+7VxhklVXiRcNXRhmhFuyPgybLhid5Hhzo6X4Gm/b4
20 | NpbITdxSEc5e53lhqa0RVyYL4nVkwQXiLl3EYcqmgmDZi3E8Ro9w4D094Zj0iRpK
21 | +Ej6q3ot7wBCGGRWUiq8hf0CgYBaZbT3vlLcHJ2o6llJXjTTnHPRNxzKHYJQCVQl
22 | dohFeUIaHvsJ8wg8hD2GWBjs+oP/c6ZtXRP7cULVe06TzF+xroDucNnkh66+Zg3Z
23 | pvh6foG+zOxhTr4y+ulZ+zlKDcJPeoibYb9YUizj6pkVdZ0DIx1S87wyKdCdYL9k
24 | TH07jwKBgQDD0kyBbe8T9G/AgJbxdAL1wmwzQaO85m1rwHbBDB0cUfYaU4H+PYII
25 | O7VMsNS7LtwVfQ5oUnD/u5z9wUzFnB6RFwYCwvOa4xESXcRucO06VqLBRqwb3dXW
26 | 3dSjPLXgus9GfbUze3bgBlpSKbqhzr6Uo/45g3zsJeKypMnedvmjnQ==
27 | -----END RSA PRIVATE KEY-----
28 |
--------------------------------------------------------------------------------
/tests/example-vars.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # Credentials.
3 | venafi:
4 | # Venafi Platform connection parameters
5 | user: 'admin'
6 | password: 'secret'
7 | url: 'https://venafi.example.com/vedsdk'
8 | zone: "devops\\vcert"
9 | # Venafi Cloud connection parameters
10 | # token: 'enter-cloud-api-token-here'
11 | # zone: 'Default'
12 | # Test mode parameter
13 | # test_mode: true
14 |
15 | # Certificate parameters. These are just examples.
16 | certificate_common_name: "{{ ansible_fqdn }}"
17 | certificate_alt_name: "IP:192.168.1.1,DNS:www.venafi.example.com,DNS:m.venafi.example.com,email:e@venafi.com,email:e2@venafi.com,IP Address:192.168.2.2"
18 |
19 | certificate_privatekey_type: "RSA"
20 | certificate_privatekey_size: "2048"
21 | certificate_privatekey_curve: "P251"
22 | certificate_privatekey_passphrase: "password"
23 | certificate_chain_option: "last"
24 |
25 | certificate_cert_dir: "/etc/ssl/{{ certificate_common_name }}"
26 | certificate_cert_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.pem"
27 | certificate_chain_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.chain.pem"
28 | certificate_privatekey_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.key"
29 | certificate_csr_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.csr"
30 |
31 | # Where to execute venafi_certificate module. If set to false certificate will be
32 | # created on ansible master host and then copied to the remote server
33 | certificate_remote_execution: false
34 | # remote location where to place the certificate_
35 | certificate_remote_cert_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.pem"
36 | certificate_remote_chain_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.chain.pem"
37 | certificate_remote_privatekey_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.key"
38 | # Set to false if you don't want to copy private key to remote location
39 | certificate_copy_private_key_to_remote: true
40 |
--------------------------------------------------------------------------------
/examples/f5_bigip/f5_delete_playbook.yaml:
--------------------------------------------------------------------------------
1 | - name: Remove F5 Application
2 | hosts: localhost
3 | connection: local
4 |
5 | vars_files:
6 | - variables.yaml
7 |
8 | tasks:
9 | - name: Remove Virtual Server from F5 BIG-IP {{ f5_address }}
10 | bigip_virtual_server:
11 | state: absent
12 | provider: "{{ f5_provider }}"
13 | name: "vs_{{ test_site.name }}"
14 | partition: "{{ f5_partition }}"
15 | delegate_to: localhost
16 |
17 | - name: Remove Pool from F5 BIG-IP {{ f5_address }}
18 | bigip_pool:
19 | state: absent
20 | provider: "{{ f5_provider }}"
21 | name: "pool_{{ test_site.name }}"
22 | partition: "{{ f5_partition }}"
23 | delegate_to: localhost
24 |
25 | - name: Remove Client SSL Profile from F5 BIG-IP {{ f5_address }}
26 | bigip_profile_client_ssl:
27 | state: absent
28 | provider: "{{ f5_provider }}"
29 | name: "clientssl_{{ test_site.name }}"
30 | partition: "{{ f5_partition }}"
31 | delegate_to: localhost
32 |
33 | - name: Remove Private Key from F5 BIG-IP {{ f5_address }}
34 | bigip_ssl_key:
35 | state: absent
36 | provider: "{{ f5_provider }}"
37 | name: "{{ key_name }}"
38 | partition: "{{ f5_partition }}"
39 | delegate_to: localhost
40 |
41 | - name: Remove Certificate from F5 BIG-IP {{ f5_address }}
42 | bigip_ssl_certificate:
43 | state: absent
44 | provider: "{{ f5_provider }}"
45 | name: "{{ cert_name }}"
46 | partition: "{{ f5_partition }}"
47 | delegate_to: localhost
48 |
49 | - name: Remove CA Bundle from F5 BIG-IP {{ f5_address }}
50 | bigip_ssl_certificate:
51 | state: absent
52 | provider: "{{ f5_provider }}"
53 | name: "{{ chain_name }}"
54 | partition: "{{ f5_partition }}"
55 | delegate_to: localhost
56 |
57 | - name: Delete Local Crypto Assets
58 | file:
59 | state: absent
60 | path: "./tmp/"
61 |
--------------------------------------------------------------------------------
/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | galaxy_info:
3 | role_name: ansible_role_venafi
4 | author: venafi
5 | description: Streamline machine identity (certificate and key) acquisition using Venafi vcert.
6 | company: Venafi, Inc.
7 |
8 | license: Apache License 2.0
9 |
10 | min_ansible_version: 2.4
11 |
12 | # If this a Container Enabled role, provide the minimum Ansible Container version.
13 | # min_ansible_container_version:
14 |
15 | # Optionally specify the branch Galaxy will use when accessing the GitHub
16 | # repo for this role. During role install, if no tags are available,
17 | # Galaxy will use this branch. During import, Galaxy will access files on
18 | # this branch. If Travis integration is configured, only notifications for this
19 | # branch will be accepted. Otherwise, in all cases, the repo's default branch
20 | # (usually master) will be used.
21 | # github_branch:
22 |
23 | #
24 | # Provide a list of supported platforms, and for each platform a list of versions.
25 | # If you don't wish to enumerate all versions for a particular platform, use 'all'.
26 | # To view available platforms and versions (or releases), visit:
27 | # https://galaxy.ansible.com/api/v1/platforms/
28 | #
29 | platforms:
30 | - name: EL
31 | versions:
32 | - all
33 | - name: Fedora
34 | versions:
35 | - all
36 | - name: Ubuntu
37 | versions:
38 | - all
39 | - name: Debian
40 | versions:
41 | - all
42 |
43 | galaxy_tags: ['certificates', 'ssl', 'security', 'venafi']
44 | # List tags for your role here, one per line. A tag is a keyword that describes
45 | # and categorizes the role. Users find roles by searching for tags. Be sure to
46 | # remove the '[]' above, if you add tags to this list.
47 | #
48 | # NOTE: A tag is limited to a single word comprised of alphanumeric characters.
49 | # Maximum 20 tags per role.
50 |
51 | dependencies: []
52 | # List your role dependencies here, one per line. Be sure to remove the '[]' above,
53 | # if you add dependencies to this list.
54 |
--------------------------------------------------------------------------------
/tests/assets/valid_rsa2048_chain.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIFpjCCA46gAwIBAgIQPY6aY41C6JxH4BxIUMuftTANBgkqhkiG9w0BAQsFADBb
3 | MRMwEQYKCZImiZPyLGQBGRYDY29tMRYwFAYKCZImiZPyLGQBGRYGdmVuYWZpMRUw
4 | EwYKCZImiZPyLGQBGRYFdmVucWExFTATBgNVBAMTDFFBIFZlbmFmaSBDQTAeFw0x
5 | NjExMjExMzU4NTVaFw0zNjExMjExNDA4NTRaMFsxEzARBgoJkiaJk/IsZAEZFgNj
6 | b20xFjAUBgoJkiaJk/IsZAEZFgZ2ZW5hZmkxFTATBgoJkiaJk/IsZAEZFgV2ZW5x
7 | YTEVMBMGA1UEAxMMUUEgVmVuYWZpIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
8 | MIICCgKCAgEA3M53kteYHPX83uDvxd25NBNfokj4FBGXhrrAuUbiZFu2oolu9W9y
9 | KD/7OwlivxnEkEbUcdaLvKJmlmVUqZqmPATATsvU1RVuv6P2e7BT/C9ErSPQOkUW
10 | XkKBfZtOJufHs0FSwUa+AUm6Kd+bkOEZIbAmMNuip5aC7HDfmN77cSksXRNX/UjU
11 | W5B5y/0aV58p32GGCySr9gBqYwYHX3pPCUl+rnf/+hEMViI1TWlLaVa77uodCfD8
12 | b7hNopVk8KAnNlNhEYNVIQnfKC/OsNGP63FYqDswS0SRr/M6XmoMHZSr6MEXCz9m
13 | MQLeft/nR8llcvB+CnfuzEUWWj2zgBzsCwvBZ6vUrz0ziZmUODqek9oQ+6L9HOJn
14 | nBATIOLMYfDX0kYvfnvVnA2b4ugdrD/PpYOnKHW3twpxVJ2HplRX4dAZ2TXJs7tU
15 | EYgAcYAJzk1rE/yBEgY0Z6Wj8WlBj7PzTxWs8NUhEvrpPNCus2ARz8Xx8IE6A9cI
16 | 87U0BRISiFFtd0BFG0EF4C6vZaBtXK049swsVu+2f2Q9mzxskcUcThxVGHqNLYBY
17 | Zadwjq/+O8/OLG6mpu9d1TpF7TSFmFd2Mc0tqm9ROthtKXRPahVQSmXTYhSrCLRj
18 | /GMCS2+zR6rv3y5K/YBEaskpM0/wZHygFbucjfFizgGxZbvk3NYxLP0CAwEAAaNm
19 | MGQwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF
20 | MAMBAf8wHQYDVR0OBBYEFDysnKYNoTDUVqc9eLwjG+y0e011MBAGCSsGAQQBgjcV
21 | AQQDAgEAMA0GCSqGSIb3DQEBCwUAA4ICAQDb6m9/c694TIo/V2Bowq0iei5f0TKJ
22 | Cc0X4+jUGa3ivkQRB0EgKFbXUHtP52Pribi2OeGeLJibMDcB6sfiOjSmun83Pe8D
23 | pOAAq+YlKRiUTF4qb8SD5iJPPTTL/KaRxisBLcUGxOvhBVJcm5rQ9crowE5RN9qm
24 | YVGVG73T9Y+p9GgLZUz3v1YTZ89LubLfiW6x8Q8jyzjkgfKY49oxGf/DrWp9y6gt
25 | TBFcG4pQNOC7AIVYj5UTPxZqbuuJTkwADdRwElSvzHxceHvICJaSbSNiHhX4XsrQ
26 | FMajGG3AZC879wcPW1pejPN4A2705WPZ/8mMVuYJDadQ6Pt8+PUXJDcmKGtVv+1E
27 | d7AVpYqhgWwze+V+eRgI5rTPr0ijFXX8VGFUcJl5JwUwPLrUNA45UMA7V5qgjb9+
28 | k+GXaoC9l4PyiSdEm/vR0+Vbj/ZB7sgU9XlFe8D8e3c2bdvg2Iwjjx4RBQffnoWl
29 | vc/Ofw9Hbk3LUsn7k4GOrQNpMlz14tpY3pPi6qrZFH/RabZngL5Tog9mszcqgMBv
30 | 9FyPr0ubOaCXBXJzRjVQjHV0YOGwFeLvQAohFIAdMlCVRVx+rIzupEskGgAMnKtG
31 | QXe+VMF9FXaRqDI/cCNsBnR++USinZvwGY6SecfDtHA7x65yJol7Y8YtURNfyDfg
32 | yVzOWlPcu2gJaw==
33 | -----END CERTIFICATE-----
34 |
--------------------------------------------------------------------------------
/tests/assets/invalid_cert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIHBTCCBO2gAwIBAgITbQB2yTTOt8JP17HI7QAAAHbJNDANBgkqhkiG9w0BAQsF
3 | ADBbMRMwEQYKCZImiZPyLGQBGRYDY29tMRYwFAYKCZImiZPyLGQBGRYGdmVuYWZp
4 | MRUwEwYKCZImiZPyLGQBGRYFdmVucWExFTATBgNVBAMTDFFBIFZlbmFmaSBDQTAe
5 | Fw0xOTAxMjgxMjAyMzNaFw0yMTAxMjcxMjAyMzNaMCUxIzAhBgNVBAMTGnRlc3Qx
6 | MTEudmVuYWZpLmV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
7 | CgKCAQEA40KBhT8rF6deSkExEW334YiwWlnpJjfzRbF6401Nm/+kVcZfG20IiNoY
8 | 17rlCcBHNx6QKBqP4N9O1mMmV0Q0FCXusvPPsJqymdfn9EJLxBSFVLlhmcuCmb10
9 | Yw7/uY9VyVKW20Cvy1dr3SNgXTQI0sOOkm+SJPhEiFUuXnwMStvBmqFbjpx86dLC
10 | nymwSDE8vgvUnAAk2MF6vKsfzc1qqiq0OIvV0FD12sH2Ubiq1RNKPYkzbNn382fV
11 | LOP3+VBnUezuSy3VeWuP3a5PlBTIUaQLHw3LKv0HV9eKRsBQagMONp6S8Q+G4EAd
12 | sdjMJqmoJG7JMU0/VY3cIUFMTZhCAwIDAQABo4IC9jCCAvIwCQYDVR0RBAIwADAd
13 | BgNVHQ4EFgQUIFA2UfU9Kn2htZ2nobCp2UlOqWowHwYDVR0jBBgwFoAUPKycpg2h
14 | MNRWpz14vCMb7LR7TXUwggEiBgNVHR8EggEZMIIBFTCCARGgggENoIIBCYZCaHR0
15 | cDovL3FhdmVuYWZpY2EudmVucWEudmVuYWZpLmNvbS9DZXJ0RW5yb2xsL1FBJTIw
16 | VmVuYWZpJTIwQ0EuY3JshoHCbGRhcDovLy9DTj1RQSUyMFZlbmFmaSUyMENBLENO
17 | PXFhdmVuYWZpY2EsQ049Q0RQLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENO
18 | PVNlcnZpY2VzLENOPUNvbmZpZ3VyYXRpb24sREM9dmVucWEsREM9dmVuYWZpLERD
19 | PWNvbT9jZXJ0aWZpY2F0ZVJldm9jYXRpb25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9
20 | AQUFBzAChl5odHRwOi8vcWF2ZW5hZmljYS52ZW5xYS52ZW5hZmkuY29tL0NlcnRF
21 | bnJvbGwvcWF2ZW5hZmljYS52ZW5xYS52ZW5hZmkuY29tX1FBJTIwVmVuYWZpJTIw
22 | Q0EuY3J0MIG3BggrBgEFBQcwAoaBqmxkYXA6Ly8vQ049UUElMjBWZW5hZmklMjBD
23 | QSxDTj1BSUEsQ049UHVibGljJTIwS2V5JTIwU2VydmljZXMsQ049U2VydmljZXMs
24 | Q049Q29uZmlndXJhdGlvbixEQz12ZW5xYSxEQz12ZW5hZmksREM9Y29tP2NBQ2Vy
25 | dGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0aW9uQXV0aG9yaXR5
26 | MCEGCSsGAQQBgjcUAgQUHhIAVwBlAGIAUwBlAHIAdgBlAHIwCwYDVR0PBAQDAgWg
27 | MBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4ICAQBBv4wemLIT
28 | nibcOBnYCKhkJj97i/EVcOWALXi9iVoKtVUuLFTHjLxyzwHobM8ndRJ9Ard/F9Uk
29 | UqEsqf+9SWdZwMWTGmNTdWjywabSP508LdkSz9Ffpmz/Ysw8smbQfFPxbpxqCin8
30 | j5QX2QHeA3+OZvX7/4fc4Xog/19skRrLR/O65IEO+gd5MZjsf3A9mvEtAJB05uiF
31 | L1szUAeKMYFmlVkdcA6C2xxxai049sdkgUwHJ+eqbF43Ko2g6s1kfGtXZI26B5kx
32 | Obqf3KUONRhrUA3NM6LO9A/io5oxweFjvofsFA/QuoU5y4xXzdB8bAS+FzWug/Ve
33 | 6/RT7Xw6RsCDreBFdLd+Xe5vrzq6/duHWWnBTDd23DAlTWrD3RKD16OwnJppaG31
34 | YB0/j+O5dLOqkvh1OTNUAaxOTeE7K/X9s1aIZoeb79W26DMDKJNJJ5/djqNImylU
35 | ZIf77mYIDX+yDCYsrXG5XzecC6HXpCEkJtBEbBuIuW/nwfiuECbiHIqMgStavZzd
36 | 2URasPdBG2vquDxDRgpXYavSgod0a/QjJ+kDXVvHEAyFyruyDcV3DXi+Tk1DcWtV
37 | B2Axcb0wRYYfFFTWrfP74Zgse4hpKA/DHSLxQyzZ7dI2xQjowm6ugAx8K8v/FlhO
38 | 129nilQZ1FsjrhKgFp6OBNc+4ceFTOjDdA==
39 | -----END CERTIFICATE-----
--------------------------------------------------------------------------------
/tests/assets/valid_rsa2048_cert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIHtjCCBZ6gAwIBAgITbQB6PPaudu2pxon+EAAAAHo89jANBgkqhkiG9w0BAQsF
3 | ADBbMRMwEQYKCZImiZPyLGQBGRYDY29tMRYwFAYKCZImiZPyLGQBGRYGdmVuYWZp
4 | MRUwEwYKCZImiZPyLGQBGRYFdmVucWExFTATBgNVBAMTDFFBIFZlbmFmaSBDQTAe
5 | Fw0xOTExMjYxMjQxMDJaFw0yNzExMjQxMjQxMDJaMIGCMQswCQYDVQQGEwJVUzEN
6 | MAsGA1UECBMEVXRhaDESMBAGA1UEBxMJU2FsdCBMYWtlMRQwEgYDVQQKEwtWZW5h
7 | ZmkgSW5jLjEVMBMGA1UECxMMSW50ZWdyYXRpb25zMSMwIQYDVQQDExp0ZXN0MTEx
8 | LnZlbmFmaS5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
9 | ggEBANDCC7nu1uGXEGW3u8B3NQo07Lp7MrlhiFOrTcQx+D1uEcnJSY0G9I4D0yTs
10 | U2dt6/C5p17+5bjskLdPSvWzKRLWvvduUqSC0klczl6lCrw8c5xnei8gD+DHeWK3
11 | u4NOVyVdaXb+28rGzHds7PLsmFtp+5WnDZ8veu+Mlymd6nH/RcVhCQuET03fKw66
12 | oOzVyu8Cd3YPbc1Zgyqy+jRGxOnIxTcsqVgKTL4dR53nneqnlS9cV050I2ydr0VS
13 | NKic4Zt04J4Wg49cRxUDwwOFDp9T6awIz4jabZhOzhxQet7qMgkiLDvsjEUAhQ8R
14 | ++8lfCbN2/CGJ9068mocbg5U5RECAwEAAaOCA0kwggNFMB0GA1UdDgQWBBQjNgT2
15 | sOHXIGWWs1pycaSrqngw2jAfBgNVHSMEGDAWgBQ8rJymDaEw1FanPXi8IxvstHtN
16 | dTCCASIGA1UdHwSCARkwggEVMIIBEaCCAQ2gggEJhkJodHRwOi8vcWF2ZW5hZmlj
17 | YS52ZW5xYS52ZW5hZmkuY29tL0NlcnRFbnJvbGwvUUElMjBWZW5hZmklMjBDQS5j
18 | cmyGgcJsZGFwOi8vL0NOPVFBJTIwVmVuYWZpJTIwQ0EsQ049cWF2ZW5hZmljYSxD
19 | Tj1DRFAsQ049UHVibGljJTIwS2V5JTIwU2VydmljZXMsQ049U2VydmljZXMsQ049
20 | Q29uZmlndXJhdGlvbixEQz12ZW5xYSxEQz12ZW5hZmksREM9Y29tP2NlcnRpZmlj
21 | YXRlUmV2b2NhdGlvbkxpc3Q/YmFzZT9vYmplY3RDbGFzcz1jUkxEaXN0cmlidXRp
22 | b25Qb2ludDCCATgGCCsGAQUFBwEBBIIBKjCCASYwagYIKwYBBQUHMAKGXmh0dHA6
23 | Ly9xYXZlbmFmaWNhLnZlbnFhLnZlbmFmaS5jb20vQ2VydEVucm9sbC9xYXZlbmFm
24 | aWNhLnZlbnFhLnZlbmFmaS5jb21fUUElMjBWZW5hZmklMjBDQS5jcnQwgbcGCCsG
25 | AQUFBzAChoGqbGRhcDovLy9DTj1RQSUyMFZlbmFmaSUyMENBLENOPUFJQSxDTj1Q
26 | dWJsaWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0
27 | aW9uLERDPXZlbnFhLERDPXZlbmFmaSxEQz1jb20/Y0FDZXJ0aWZpY2F0ZT9iYXNl
28 | P29iamVjdENsYXNzPWNlcnRpZmljYXRpb25BdXRob3JpdHkwCwYDVR0PBAQDAgWg
29 | MDsGCSsGAQQBgjcVBwQuMCwGJCsGAQQBgjcVCIGPiXKEhLBq9Z0Qh5b/fMSJFWCE
30 | z5pjhfuuQwIBZAIBBDATBgNVHSUEDDAKBggrBgEFBQcDATAbBgkrBgEEAYI3FQoE
31 | DjAMMAoGCCsGAQUFBwMBMCUGA1UdEQQeMByCGnRlc3QxMTEudmVuYWZpLmV4YW1w
32 | bGUuY29tMA0GCSqGSIb3DQEBCwUAA4ICAQALQbEtiVp/+gVyTS6sS+Ipj54GHJgs
33 | iIsmQCS2YxuUppf7K6kpfMdEgeQZ9UAtPSgTy7fxERjWdrs1ZXHkjeb2LSdP0HdC
34 | /nIwdUkCDzFgOTg/s6T2A/Z200hsp/td+t9JwY1lctZefv+5EgRCU4L4c5zseqpq
35 | /mrLtUBGmFJa2qAeoIAH0wKYcGExcerWlU/e9S7qKSzmoGgUQPsrChJCSClxSTYK
36 | 4HUO05whP6NRbyYhfH3K7O8UEdlFe0H0pKf9v/SrwRTIsG9tnU+P3Rp7o/S15mYz
37 | k61UBmdHa0NJXEFUQy3JZcGW0aC85ovPwBMSqExHuFzGR0rPCRYsPVAOwtWZaLiK
38 | 9IunCh+qFMcdP1nPngnCpV2TnXNyFxCKFwtkTpTcoQvdj5oAaRpcr3RLVd0nwrmj
39 | 8g6Ys8XgTBwziQ2WcEThvp4r5t+CjAlT9n2kIqtBOx3/Q9v3eN+FVA1kdEmWJPig
40 | WJE4dRtxMgHk07Kog4Z5tyvoag8D3gPANNzGF82oIGUyJfD8m0tm9lOBmdgxuJjj
41 | jdVJ7gQSmyszBvJ6vYOOt1rHGfUsCHS01bjO5bgjzProI0k4Rm+8DCVrO2PKnGGx
42 | WgFWkdNhDPzvjx+BD3xTNGDhxlY0ikkNMxs62UeZUp+V0iYSCb6DzrEV/1n6G7yA
43 | Ky4JV+em9M+5mg==
44 | -----END CERTIFICATE-----
45 |
--------------------------------------------------------------------------------
/tests/assets/invalid_cn_rsa2048_cert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIHtjCCBZ6gAwIBAgITbQB6PP8ap5kdHJfdbAAAAHo8/zANBgkqhkiG9w0BAQsF
3 | ADBbMRMwEQYKCZImiZPyLGQBGRYDY29tMRYwFAYKCZImiZPyLGQBGRYGdmVuYWZp
4 | MRUwEwYKCZImiZPyLGQBGRYFdmVucWExFTATBgNVBAMTDFFBIFZlbmFmaSBDQTAe
5 | Fw0xOTExMjYxMjU2MzRaFw0yNzExMjQxMjU2MzRaMIGCMQswCQYDVQQGEwJVUzEN
6 | MAsGA1UECBMEVXRhaDESMBAGA1UEBxMJU2FsdCBMYWtlMRQwEgYDVQQKEwtWZW5h
7 | ZmkgSW5jLjEVMBMGA1UECxMMSW50ZWdyYXRpb25zMSMwIQYDVQQDExp0ZXN0MTIz
8 | LnZlbmFmaS5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
9 | ggEBANvK+i5BjzuqiOOWdoF2FKn+s1ZatlSKSlUOzrN/qvk/6zmTPF7gQbzZDFk+
10 | tAo/3RkrFgy2plYBA8o92uHdUBlw8xel076Ku6wVu5G942Ip4FUfacXGIgWU7dqO
11 | U1rq6XUFU+Gpr93v6VoDiY1lGav4htdtDryfSz4UX40jVgnbTMP+FxLQiV6xSYUo
12 | tYpKWvV9PWshmao3q1mJUMTtOpijhFqk9YF/xUPCSLHym87u7eOD7AEUn2KRDve8
13 | UCBYY2znCwiQrRefmUhDaJeqOy6akrO5A+hUzT2Ue7j6LPQ1cdc9nBEmelFojFXo
14 | RV42zJPSHrtENsBg0EdViw/4VjECAwEAAaOCA0kwggNFMB0GA1UdDgQWBBTOX1Gn
15 | 0XFCQ/cm4ZhV5puMrxEPCDAfBgNVHSMEGDAWgBQ8rJymDaEw1FanPXi8IxvstHtN
16 | dTCCASIGA1UdHwSCARkwggEVMIIBEaCCAQ2gggEJhkJodHRwOi8vcWF2ZW5hZmlj
17 | YS52ZW5xYS52ZW5hZmkuY29tL0NlcnRFbnJvbGwvUUElMjBWZW5hZmklMjBDQS5j
18 | cmyGgcJsZGFwOi8vL0NOPVFBJTIwVmVuYWZpJTIwQ0EsQ049cWF2ZW5hZmljYSxD
19 | Tj1DRFAsQ049UHVibGljJTIwS2V5JTIwU2VydmljZXMsQ049U2VydmljZXMsQ049
20 | Q29uZmlndXJhdGlvbixEQz12ZW5xYSxEQz12ZW5hZmksREM9Y29tP2NlcnRpZmlj
21 | YXRlUmV2b2NhdGlvbkxpc3Q/YmFzZT9vYmplY3RDbGFzcz1jUkxEaXN0cmlidXRp
22 | b25Qb2ludDCCATgGCCsGAQUFBwEBBIIBKjCCASYwagYIKwYBBQUHMAKGXmh0dHA6
23 | Ly9xYXZlbmFmaWNhLnZlbnFhLnZlbmFmaS5jb20vQ2VydEVucm9sbC9xYXZlbmFm
24 | aWNhLnZlbnFhLnZlbmFmaS5jb21fUUElMjBWZW5hZmklMjBDQS5jcnQwgbcGCCsG
25 | AQUFBzAChoGqbGRhcDovLy9DTj1RQSUyMFZlbmFmaSUyMENBLENOPUFJQSxDTj1Q
26 | dWJsaWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0
27 | aW9uLERDPXZlbnFhLERDPXZlbmFmaSxEQz1jb20/Y0FDZXJ0aWZpY2F0ZT9iYXNl
28 | P29iamVjdENsYXNzPWNlcnRpZmljYXRpb25BdXRob3JpdHkwCwYDVR0PBAQDAgWg
29 | MDsGCSsGAQQBgjcVBwQuMCwGJCsGAQQBgjcVCIGPiXKEhLBq9Z0Qh5b/fMSJFWCE
30 | z5pjhfuuQwIBZAIBBDATBgNVHSUEDDAKBggrBgEFBQcDATAbBgkrBgEEAYI3FQoE
31 | DjAMMAoGCCsGAQUFBwMBMCUGA1UdEQQeMByCGnRlc3QxMjMudmVuYWZpLmV4YW1w
32 | bGUuY29tMA0GCSqGSIb3DQEBCwUAA4ICAQCRZFKkhCUTbrM+raD40bNuASrdCViV
33 | Ot9U18PD65IaZSN9l3EQvOjCQRstp9oH4APO7gClb/XEGoGuHUKO8QZPFm9i4SXB
34 | OrCso9dN94x2mvmcyZ/jBEm3Fsfhsm1gZqG41WZVTeUxVOTX4NEep4D/OQbPLs9k
35 | TUXO8mzvh+mFZrA4hkicieIuetGyoNmIZtdC2QgIdjyje7OwUoBQebDcFDVUsqPq
36 | abBNOzJXr+QX8cFeh/tUScHHDbk9vatBEMvZ4pbINKUI2fHEaz82ZzXfhuofagkr
37 | 4CDazfEVXv8EW/Dr+f9fDwrgvc1zZ+nxaQWiKgJYe2vija8Asn6LK1RvauV0i1bH
38 | YegPAUOBhLx6IgM5PmSSoQ+nYokgB28bQ0d/4tvvkBp8vjSbrHxgOesbhJFQALfc
39 | ez/y7ipoUYyECYUn6EAaIVgRDXIdCFrUj0BPYwBzEKcC5qbNo+Ef+ppASTPkaf1V
40 | VL2KgVlXz6Wfk/4SMbBfbn+ZczIaMhPwRWfTulDOEJFSmZ2OqED69/fv64ue5MoB
41 | zoxnz53OQ23Ect2VjjyNjMTrFYM+Kwy+zj6CJn2icsJ/p++GUJ3InWZB74+23Ool
42 | 154qJ8a7X2vVzDXexBMrQaxe0fjhi6+eEKBlcHoIM1YPO0RqsnqIMHm7gx71LPz9
43 | UC6vVjMVQ5/0tg==
44 | -----END CERTIFICATE-----
45 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | pep8:
2 | pycodestyle --first ./library/venafi_certificate.py
3 |
4 | yamllint:
5 | yamllint `git ls-files *.yml | grep -v ISSUE_TEMPLATE`
6 |
7 | lint: yamllint pep8
8 | ansible-lint -x 106,204,504 ./tasks/*
9 | ansible-lint ./meta/*
10 | ansible-lint ./defaults/*
11 |
12 | ansible-molecule:
13 | docker build ./tests --tag local-ansible-test
14 | ANSIBLE_VAULT_PASSWORD_FILE=${PWD}/vault-password.txt molecule converge
15 |
16 | #Testing ansible crypto modules for examples and compability checks
17 | test-crypto-playbook:
18 | ansible-playbook -i tests/inventory tests/original-ansible-crypto-playbook-example.yml
19 |
20 | #test Ansible playbook with venafi certificate module
21 |
22 | test-vcert-playbook-tpp:
23 | # #have to copy library to test our module, otherwise test playbook will not
24 | docker build ./tests --tag local-ansible-test
25 | rm -rvf tests/library
26 | cp -rv library tests/
27 | ansible-playbook -i tests/inventory tests/venafi-playbook-example.yml \
28 | --vault-password-file vault-password.txt \
29 | --extra-vars "credentials_file=../tpp_credentials.yml docker_demo=true"
30 |
31 | #test Ansible role with venafi_Certificate module
32 | test-vcert-role-tpp:
33 | # #have to copy library to test our module, otherwise test playbook will not
34 | docker build ./tests --tag local-ansible-test
35 | rm -rvf tests/library
36 | cp -rv library tests/
37 | ansible-playbook -i tests/inventory tests/venafi-role-playbook-example.yml \
38 | --vault-password-file vault-password.txt \
39 | --extra-vars "credentials_file=tpp_credentials.yml docker_demo=true"
40 |
41 | test-vcert-role-cloud:
42 | # #have to copy library to test our module, otherwise test playbook will not
43 | docker build ./tests --tag local-ansible-test
44 | rm -rvf tests/library
45 | cp -rv library tests/
46 | ansible-playbook -i tests/inventory tests/venafi-role-playbook-example.yml \
47 | --vault-password-file vault-password.txt \
48 | --extra-vars "credentials_file=cloud_credentials.yml docker_demo=true"
49 |
50 | test-vcert-role-fake:
51 | # #have to copy library to test our module, otherwise test playbook will not
52 | docker build ./tests --tag local-ansible-test
53 | rm -rvf tests/library
54 | cp -rv library tests/
55 | ansible-playbook -i tests/inventory tests/venafi-role-playbook-example.yml \
56 | --vault-password-file vault-password.txt \
57 | --extra-vars "credentials_file=fake_credentials.yml docker_demo=true"
58 |
59 | #test module with python using json for args
60 | test-python-module: test-python-module-fake test-python-module-tpp test-python-module-cloud
61 |
62 | test-python-module-tpp:
63 | python3 library/venafi_certificate.py venafi_certificate_tpp.json
64 |
65 | test-python-module-fake:
66 | python3 ./library/venafi_certificate.py venafi_certificate_fake.json
67 |
68 | test-python-module-cloud:
69 | python3 ./library/venafi_certificate.py venafi_certificate_cloud.json
70 |
71 | unit-test:
72 | rm -rvf tests/library
73 | cp -rv library tests/
74 | PYTHONPATH=./:$PYTHONPATH pytest tests/test_venafi_certificate.py
75 |
--------------------------------------------------------------------------------
/tasks/local-certificate.yml:
--------------------------------------------------------------------------------
1 | # Locally generate certificate
2 | # Copy files to remote host from inventory
3 | # Generates certificates on remote host
4 | # TODO: maybe rewrite to delegate to be able to register certificate from any host?
5 | ---
6 | - name: "Create directory {{ certificate_cert_dir }}"
7 | file:
8 | path: "{{ certificate_cert_dir }}"
9 | state: directory
10 | mode: 0755
11 | delegate_to: localhost
12 |
13 | - name: "Enroll Venafi certificate on local host"
14 | venafi_certificate:
15 | url: "{{ venafi.url | default(omit) }}"
16 | token: "{{ venafi.token | default(omit) }}"
17 | zone: "{{ venafi.zone | default(omit) }}"
18 | test_mode: "{{ venafi.test_mode if venafi.test_mode is defined else 'false' }}"
19 | user: "{{ venafi.user | default(omit) }}"
20 | password: "{{ venafi.password | default(omit) }}"
21 | access_token: "{{ venafi.access_token | default(omit) }}"
22 | trust_bundle: "{{ venafi.trust_bundle | default(omit) }}"
23 | cert_path: "{{ certificate_cert_path }}"
24 | chain_path: "{{ certificate_chain_path | default(omit) }}"
25 | privatekey_path: "{{ certificate_privatekey_path | default(omit) }}"
26 | privatekey_type: "{{ certificate_privatekey_type | default(omit) }}"
27 | privatekey_size: "{{ certificate_privatekey_size | default(omit) }}"
28 | privatekey_curve: "{{ certificate_privatekey_curve | default(omit) }}"
29 | common_name: "{{ certificate_common_name }}"
30 | alt_name: "{{ certificate_alt_name | default([]) }}"
31 | before_expired_hours: "{{ certificate_before_expired_hours if certificate_before_expired_hours is defined else 72 }}"
32 | force: "{{ certificate_force if certificate_force is defined else false }}"
33 | delegate_to: localhost
34 | register: certout
35 | - name: "dump test output"
36 | debug:
37 | msg: "{{ certout }}"
38 |
39 | - name: "Copy Venafi certificate file to remote location {{ certificate_remote_cert_path if certificate_remote_cert_path is defined else certificate_cert_path }}"
40 | copy:
41 | src: "{{ certificate_cert_path }}"
42 | dest: "{{ certificate_remote_cert_path if certificate_remote_cert_path is defined else certificate_cert_path }}"
43 | mode: 0644
44 |
45 | - name: "Copy Venafi private key file to remote location {{ certificate_remote_privatekey_path if certificate_remote_privatekey_path else certificate_privatekey_path }}"
46 | copy:
47 | src: "{{ certificate_privatekey_path }}"
48 | dest: "{{ certificate_remote_privatekey_path if certificate_remote_privatekey_path else certificate_privatekey_path }}"
49 | mode: 0600
50 | when: certificate_copy_private_key_to_remote
51 |
52 | - name: "Copy Venafi certificate chain file to remote location {{ certificate_remote_chain_path if certificate_remote_chain_path else certificate_chain_path }}"
53 | copy:
54 | src: "{{ certificate_chain_path }}"
55 | dest: "{{ certificate_remote_chain_path if certificate_remote_chain_path else certificate_chain_path }}"
56 | mode: 0644
57 | when: certificate_chain_path is defined
58 |
--------------------------------------------------------------------------------
/tests/assets/valid_alt_rsa2048_cert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIIDzCCBfegAwIBAgITbQB6PP3/whfpBt6RngAAAHo8/TANBgkqhkiG9w0BAQsF
3 | ADBbMRMwEQYKCZImiZPyLGQBGRYDY29tMRYwFAYKCZImiZPyLGQBGRYGdmVuYWZp
4 | MRUwEwYKCZImiZPyLGQBGRYFdmVucWExFTATBgNVBAMTDFFBIFZlbmFmaSBDQTAe
5 | Fw0xOTExMjYxMjU0NDNaFw0yNzExMjQxMjU0NDNaMIGCMQswCQYDVQQGEwJVUzEN
6 | MAsGA1UECBMEVXRhaDESMBAGA1UEBxMJU2FsdCBMYWtlMRQwEgYDVQQKEwtWZW5h
7 | ZmkgSW5jLjEVMBMGA1UECxMMSW50ZWdyYXRpb25zMSMwIQYDVQQDExp0ZXN0MTIz
8 | LnZlbmFmaS5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
9 | ggEBAMMMEVjABzOfKCn3aSpmQzjihFdgWOgKcrkBothKUl9BK1yY9mwRnUN6KtuR
10 | qZu6gFB948W8avBTpfuO4lavRi0LWIovQ9eEK15Ok3oQZEewxMjzDtus73M+5Dd7
11 | R8SHbC9SbGbaOv72tfq7pHBRsTQVJwEm6j8CvRmA8n/RqToJVXsMcX45g6MZafiy
12 | M0J0+cDtFwdNJNrDQuLJr74efEy40tFcjJiCaYis+ZdUEZO+1+mlwOzJL+cLg8+r
13 | Q6zLAjjl4nQFHqUmqzY2HgOfyt0PmK/zN9GW3wyKd1j3GUNsS1XUu1S8U8tmd1qb
14 | QYrp36/S/03m+vyCSTOfeNo2clECAwEAAaOCA6IwggOeMH4GA1UdEQR3MHWBDWVl
15 | QHZlbmFmaS5jb22BDmVlMkB2ZW5hZmkuY29tghZ3d3cudmVuYWZpLmV4YW1wbGUu
16 | Y29tghRtLnZlbmFmaS5leGFtcGxlLmNvbYIadGVzdDEyMy52ZW5hZmkuZXhhbXBs
17 | ZS5jb22HBMCoAQGHBMCoAgIwHQYDVR0OBBYEFIQvRi2dz7DrUDnzYJKGSQvsHNMB
18 | MB8GA1UdIwQYMBaAFDysnKYNoTDUVqc9eLwjG+y0e011MIIBIgYDVR0fBIIBGTCC
19 | ARUwggERoIIBDaCCAQmGQmh0dHA6Ly9xYXZlbmFmaWNhLnZlbnFhLnZlbmFmaS5j
20 | b20vQ2VydEVucm9sbC9RQSUyMFZlbmFmaSUyMENBLmNybIaBwmxkYXA6Ly8vQ049
21 | UUElMjBWZW5hZmklMjBDQSxDTj1xYXZlbmFmaWNhLENOPUNEUCxDTj1QdWJsaWMl
22 | MjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0aW9uLERD
23 | PXZlbnFhLERDPXZlbmFmaSxEQz1jb20/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlz
24 | dD9iYXNlP29iamVjdENsYXNzPWNSTERpc3RyaWJ1dGlvblBvaW50MIIBOAYIKwYB
25 | BQUHAQEEggEqMIIBJjBqBggrBgEFBQcwAoZeaHR0cDovL3FhdmVuYWZpY2EudmVu
26 | cWEudmVuYWZpLmNvbS9DZXJ0RW5yb2xsL3FhdmVuYWZpY2EudmVucWEudmVuYWZp
27 | LmNvbV9RQSUyMFZlbmFmaSUyMENBLmNydDCBtwYIKwYBBQUHMAKGgapsZGFwOi8v
28 | L0NOPVFBJTIwVmVuYWZpJTIwQ0EsQ049QUlBLENOPVB1YmxpYyUyMEtleSUyMFNl
29 | cnZpY2VzLENOPVNlcnZpY2VzLENOPUNvbmZpZ3VyYXRpb24sREM9dmVucWEsREM9
30 | dmVuYWZpLERDPWNvbT9jQUNlcnRpZmljYXRlP2Jhc2U/b2JqZWN0Q2xhc3M9Y2Vy
31 | dGlmaWNhdGlvbkF1dGhvcml0eTALBgNVHQ8EBAMCBaAwOwYJKwYBBAGCNxUHBC4w
32 | LAYkKwYBBAGCNxUIgY+JcoSEsGr1nRCHlv98xIkVYITPmmOF+65DAgFkAgEEMBMG
33 | A1UdJQQMMAoGCCsGAQUFBwMBMBsGCSsGAQQBgjcVCgQOMAwwCgYIKwYBBQUHAwEw
34 | DQYJKoZIhvcNAQELBQADggIBABq6Jp+bURVxNqIQ3TikvxCut3Hj6D0LrDolU/c5
35 | SVz21I0gONdvJiwqSQmYVkiVHIvFvbaWBEhoJa5UuUY12wcTT7IE+VFzM6w6lyud
36 | PPy14q9PX6RRNUzzvRRlW0TTsB6OyRHF+TqlNuxiCcLub3ZPaDlQGaa5lA1o9t0g
37 | CNOEbiYicSCKYADNal03uH/j3GG1Uwc7Y44vage616sJdreogT7m1XR0aJvwFzVo
38 | ftZ1nu7qcPGhXEvQ4T3g8tBabAYS+EoNBVb738D8togNPB8MUCiyZ5GL5fFITpLL
39 | qDKZW2yVoFz0fVqjy6cdxw6N46nCaIkrLbOs5KenlWo95RIZNCQYqbw/ScVmZCQB
40 | LWpHdVrIq44O80mNLF6GZmLVTqhOaNKhxelx15+6WiBJJThhHO5i5DM5ipfH3Sbc
41 | 0sijw5fHdEqQzfK8X00u4XE5jTqoTFJN1hq5YFKcrxOB9vcpB49aQeCe1Uel9Tog
42 | 1nPCfKfKu6jqyciT8kB7CdT/gSyr5DeQ2BCV/X5WG+7cCYktP19F3LbS61JhwDkR
43 | RYWy1JMWopKvJzDvfJ1jsBEzwJAnz1eHTJMuHcSlxUemQm+DDAHV8Bz4z5sye5m4
44 | qXHfNeNyU3Ldus8YTeayNZIPf5ChdPZ5l5Pr5WDzs4QW8yHq9480dLWH3mMHNJku
45 | dbrs
46 | -----END CERTIFICATE-----
47 |
--------------------------------------------------------------------------------
/examples/f5_bigip/f5_create_playbook.yaml:
--------------------------------------------------------------------------------
1 | - name: Create F5 Application
2 | hosts: localhost
3 | connection: local
4 |
5 | vars_files:
6 | - variables.yaml
7 |
8 | roles:
9 | - role: venafi.ansible_role_venafi
10 |
11 | certificate_common_name: "{{ test_site.name }}.{{ test_site.domain }}"
12 | certificate_alt_name: "DNS:{{ test_site.name }}.{{ test_site.domain }}"
13 | certificate_privatekey_type: "RSA"
14 | certificate_privatekey_size: "2048"
15 | certificate_chain_option: "last"
16 |
17 | certificate_cert_dir: "./tmp"
18 | certificate_cert_path: "./tmp/{{ cert_name }}"
19 | certificate_chain_path: "./tmp/{{ chain_name }}"
20 | certificate_privatekey_path: "./tmp/{{ key_name }}"
21 | certificate_copy_private_key_to_remote: false
22 |
23 | certificate_remote_execution: false
24 | certificate_remote_privatekey_path: "./tmp/{{ key_name }}.remote"
25 | certificate_remote_cert_path: "./tmp/{{ cert_name }}.remote"
26 | certificate_remote_chain_path: "./tmp/{{ chain_name }}.remote"
27 |
28 | tasks:
29 | - name: Create Private Key on F5 BIG-IP {{ f5_address }}
30 | bigip_ssl_key:
31 | state: present
32 | provider: "{{ f5_provider }}"
33 | name: "{{ key_name }}"
34 | partition: "{{ f5_partition }}"
35 | content: "{{ lookup('file', './tmp/' + key_name) }}"
36 | delegate_to: localhost
37 |
38 | - name: Create Certificate on F5 BIG-IP {{ f5_address }}
39 | bigip_ssl_certificate:
40 | state: present
41 | provider: "{{ f5_provider }}"
42 | name: "{{ cert_name }}"
43 | partition: "{{ f5_partition }}"
44 | content: "{{ lookup('file', './tmp/' + cert_name + '.remote') }}"
45 | delegate_to: localhost
46 |
47 | - name: Create CA Bundle on F5 BIG-IP {{ f5_address }}
48 | bigip_ssl_certificate:
49 | state: present
50 | provider: "{{ f5_provider }}"
51 | name: "{{ chain_name }}"
52 | partition: "{{ f5_partition }}"
53 | content: "{{ lookup('file', './tmp/' + chain_name + '.remote') }}"
54 | delegate_to: localhost
55 |
56 | - name: Create Client SSL Profile on F5 BIG-IP {{ f5_address }}
57 | bigip_profile_client_ssl:
58 | state: present
59 | provider: "{{ f5_provider }}"
60 | name: "clientssl_{{ test_site.name }}"
61 | partition: "{{ f5_partition }}"
62 | parent: "clientssl"
63 | cert_key_chain:
64 | - cert: "{{ cert_name }}"
65 | key: "{{ key_name }}"
66 | chain: "{{ chain_name }}"
67 | delegate_to: localhost
68 |
69 | - name: Create Pool on F5 BIG-IP {{ f5_address }}
70 | bigip_pool:
71 | state: present
72 | provider: "{{ f5_provider }}"
73 | name: "pool_{{ test_site.name }}"
74 | partition: "{{ f5_partition }}"
75 | lb_method: round-robin
76 | delegate_to: localhost
77 |
78 | - name: Add Pool Members on F5 BIG-IP {{ f5_address }}
79 | bigip_pool_member:
80 | state: present
81 | provider: "{{ f5_provider }}"
82 | partition: "{{ f5_partition }}"
83 | host: "{{ item.host }}"
84 | port: "{{ item.port }}"
85 | pool: "pool_{{ test_site.name }}"
86 | with_items: "{{ f5_pool_members }}"
87 | delegate_to: localhost
88 |
89 | - name: Create Virtual Server on F5 BIG-IP {{ f5_address }}
90 | bigip_virtual_server:
91 | state: present
92 | provider: "{{ f5_provider }}"
93 | name: "vs_{{ test_site.name }}"
94 | partition: "{{ f5_partition }}"
95 | description: "Provisioned by Ansible"
96 | destination: "{{ f5_virtual_ip }}"
97 | port: "{{ f5_virtual_port }}"
98 | snat: Automap
99 | pool: "pool_{{ test_site.name }}"
100 | profiles:
101 | - "clientssl_{{ test_site.name }}"
102 | delegate_to: localhost
103 |
--------------------------------------------------------------------------------
/tests/test_venafi_certificate.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import shutil
3 | import os
4 | from collections import namedtuple, defaultdict
5 | from library.venafi_certificate import VCertificate
6 |
7 | CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
8 |
9 |
10 | testAsset = namedtuple("testAssert", "is_valid cert chain private_key password common_name alt_name id")
11 |
12 | CERT_PATH = "/tmp/cert.pem"
13 | CHAIN_PATH = "/tmp/chain.pem"
14 | PRIV_PATH = "/tmp/priv.pem"
15 |
16 | class Fail(Exception):
17 | pass
18 |
19 | class FakeModule(object):
20 | def __init__(self, asset):
21 | self.fail_code = None
22 | self.exit_code = None
23 | self.warn = str
24 | self.params = defaultdict(lambda: None)
25 | self.params["cert_path"] = CERT_PATH
26 | self.params["chain_path"] = CHAIN_PATH
27 | self.params["privatekey_path"] = PRIV_PATH
28 | self.params["common_name"] = asset.common_name
29 | self.params["before_expired_hours"] = 72
30 | if asset.alt_name:
31 | self.params["alt_name"] = [x.strip() for x in asset.alt_name.split(',')]
32 | self.params["test_mode"] = True
33 |
34 | def exit_json(self, **kwargs):
35 | self.exit_code = kwargs
36 |
37 | def fail_json(self, **kwargs):
38 | self.fail_code = kwargs
39 | raise Fail(self.fail_code['msg'])
40 |
41 |
42 | class TestVcertificate(unittest.TestCase):
43 | def test_validate(self):
44 | for asset in TEST_ASSETS:
45 | print("testing asset id %s" % asset.id)
46 | create_testfiles(asset)
47 | module = FakeModule(asset)
48 | vcert = VCertificate(module)
49 | if asset.is_valid:
50 | vcert.validate()
51 | self.assertIsNone(module.fail_code)
52 | else:
53 | self.assertRaises(Fail, vcert.validate)
54 |
55 |
56 | def create_testfiles(asset):
57 | """
58 | :param testAsset asset:
59 | """
60 | for p, v in ((CERT_PATH, asset.cert), (CHAIN_PATH, asset.chain), (PRIV_PATH, asset.private_key)):
61 |
62 | shutil.copy(CURRENT_DIR + "/assets/" + v, p)
63 |
64 |
65 | TEST_ASSETS = [
66 | # TODO check error message, not just valid\invalid
67 | #simple valid
68 | testAsset(is_valid=True, cert="valid_rsa2048_cert.pem", chain="valid_rsa2048_chain.pem",
69 | private_key="valid_rsa2048_key.pem", password=None, common_name="test111.venafi.example.com",
70 | alt_name=None,id=1),
71 | #another cn
72 | testAsset(is_valid=False, cert="valid_rsa2048_cert.pem", chain="valid_rsa2048_chain.pem",
73 | private_key="valid_rsa2048_key.pem", password=None, common_name="test1111.venafi.example.com", alt_name=None,id=2),
74 | #corrupted file
75 | testAsset(is_valid=False, cert="invalid_cert.pem", chain="valid_rsa2048_chain.pem",
76 | private_key="valid_rsa2048_key.pem", password=None, common_name="test111.venafi.example.com", alt_name=None,id=3),
77 | #unmactched cn
78 | testAsset(is_valid=False, cert="invalid_cn_rsa2048_cert.pem", chain="valid_rsa2048_chain.pem",
79 | private_key="valid_rsa2048_key.pem", password=None, common_name="test111.venafi.example.com", alt_name=None,id=4),
80 | # unmatched key type
81 | testAsset(is_valid=False, cert="valid_rsa2048_cert.pem", chain="valid_rsa2048_chain.pem",
82 | private_key="valid_ec_key.pem", password=None, common_name="test1111.venafi.example.com", alt_name=None,id=5),
83 | #valid with dns
84 | testAsset(is_valid=True, cert="valid_alt_rsa2048_cert.pem", chain="valid_rsa2048_chain.pem",
85 | private_key="valid_alt_rsa2048_key.pem", password=None, common_name="test123.venafi.example.com",
86 | alt_name="IP:192.168.1.1,DNS:www.venafi.example.com,DNS:m.venafi.example.com,email:e@venafi.com,"
87 | "email:e2@venafi.com,IP Address:192.168.2.2",id=6),
88 | #invalid with dns
89 | testAsset(is_valid=False, cert="valid_alt_rsa2048_cert.pem", chain="valid_rsa2048_chain.pem",
90 | private_key="valid_alt_rsa2048_key.pem", password=None, common_name="test123.venafi.example.com",
91 | alt_name="IP:192.168.1.1,DNS:www.venafi.example.com,DNS:m.venafi.example.com,email:e@venafi.com,"
92 | "email:e2@venafi.com",id=7),
93 | #expired
94 | testAsset(is_valid=False, cert="invalid_date_rsa2048_cert.pem", chain="valid_rsa2048_chain.pem",
95 | private_key="valid_rsa2048_key.pem", password=None, common_name="test123.venafi.example.com",
96 | alt_name=None,id=8)
97 | ]
98 |
99 |
--------------------------------------------------------------------------------
/tests/original-ansible-crypto-playbook-example.yml:
--------------------------------------------------------------------------------
1 | # Clone https://github.com/chrismeyersfsu/provision_docker.git to /etc/ansible/roles before run
2 | ---
3 | - name: "Bring up Docker containers for Docker connection inventory"
4 | hosts: localhost
5 | roles:
6 | - role: provision_docker
7 | provision_docker_privileged: true,
8 | provision_docker_inventory_group: "{{ groups['robots'] }}"
9 | provision_docker_use_docker_connection: true
10 |
11 | - hosts: robots
12 | vars:
13 | ca:
14 | local_dir: /tmp/ansible-ca
15 | remote_dir: /etc/ssl
16 | cert_file: ca.pem
17 | key_file: ca.key
18 | csr_file: ca.csr
19 | country_name: US
20 | organization_name: Example
21 | email_address: ca@example.com
22 | common_name: ca
23 | cert:
24 | dir: /etc/ssl
25 | cert_file: example.com.pem
26 | key_file: example.com.key
27 | csr_file: example.com.csr
28 | country_name: US
29 | organization_name: Example
30 | email_address: ca@example.com
31 | common_name: cert.example.com
32 |
33 | tasks:
34 | - name: "Say hello to my new containers"
35 | ping:
36 |
37 | - name: "Create directories for local CA files"
38 | local_action:
39 | module: file
40 | path: "{{ ca.local_dir }}"
41 | state: directory
42 | mode: 0755
43 |
44 | - name: "Generate an OpenSSL private key with the default values (4096 bits, RSA) for local CA"
45 | local_action:
46 | module: openssl_privatekey
47 | path: "{{ ca.local_dir }}/{{ ca.key_file }}"
48 |
49 | - name: "Generate an OpenSSL Certificate Signing Request with Subject information for local CA"
50 | local_action:
51 | module: openssl_csr
52 | path: "{{ ca.local_dir }}/{{ ca.csr_file }}"
53 | privatekey_path: "{{ ca.local_dir }}/{{ ca.key_file }}"
54 | country_name: "{{ ca.country_name }}"
55 | organization_name: "{{ ca.organization_name }}"
56 | email_address: "{{ ca.email_address }}"
57 | common_name: "{{ ca.common_name }}"
58 | basic_constraints: "CA:true"
59 |
60 | - name: "Generate a Self Signed local CA certificate"
61 | local_action:
62 | module: openssl_certificate
63 | path: "{{ ca.local_dir }}/{{ ca.cert_file }}"
64 | privatekey_path: "{{ ca.local_dir }}/{{ ca.key_file }}"
65 | csr_path: "{{ ca.local_dir }}/{{ ca.csr_file }}"
66 | provider: selfsigned
67 |
68 | - name: "Copy CA file to remote location"
69 | copy:
70 | src: "{{ ca.local_dir }}/{{ item.file }}"
71 | dest: "{{ cert.dir }}"
72 | with_items:
73 | - { file: "{{ ca.cert_file }}" }
74 | - { file: "{{ ca.key_file }}" }
75 |
76 | - name: "Install required pip packages"
77 | pip:
78 | name:
79 | - pyOpenSSL
80 |
81 | - name: "Create directories"
82 | file:
83 | path: "{{ cert.dir }}/{{ item.dir }}"
84 | state: directory
85 | mode: 0755
86 | with_items:
87 | - { dir: 'csr' }
88 | - { dir: 'crt' }
89 | - { dir: 'private' }
90 |
91 |
92 | # Testing original crypto modules
93 |
94 | - name: "Generate an OpenSSL private key with the default values (4096 bits, RSA)"
95 | openssl_privatekey:
96 | path: "{{ cert.dir }}/private/{{ cert.key_file }}"
97 |
98 | - name: "Generate an OpenSSL Certificate Signing Request with Subject information"
99 | openssl_csr:
100 | path: "{{ cert.dir }}/csr/{{ cert.csr_file }}"
101 | privatekey_path: "{{ cert.dir }}/private/{{ cert.key_file }}"
102 | country_name: "{{ ca.country_name }}"
103 | organization_name: "{{ ca.organization_name }}"
104 | email_address: "{{ cert.email_address }}"
105 | common_name: "{{ cert.common_name }}"
106 |
107 | - name: "Sign certificate with local CA"
108 | openssl_certificate:
109 | path: "{{ cert.dir }}/crt/{{ cert.cert_file }}"
110 | privatekey_path: "{{ cert.dir }}/private/{{ cert.key_file }}"
111 | csr_path: "{{ cert.dir }}/csr/{{ cert.csr_file }}"
112 | ownca_path: "{{ cert.dir }}/{{ ca.cert_file }}"
113 | ownca_privatekey_path: "{{ cert.dir }}/{{ ca.key_file }}"
114 | provider: ownca
115 |
116 | - name: "Verify certificate"
117 | openssl_certificate:
118 | path: "{{ cert.dir }}/crt/{{ cert.cert_file }}"
119 | privatekey_path: "{{ cert.dir }}/private/{{ cert.key_file }}"
120 | issuer:
121 | CN: ca
122 | has_expired: false
123 | provider: assertonly
124 |
--------------------------------------------------------------------------------
/examples/citrix_adc/citrix_delete_playbook.yaml:
--------------------------------------------------------------------------------
1 | - name: Remove Citrix ADC Application
2 | hosts: localhost
3 | connection: local
4 | collections: citrix.adc
5 |
6 | vars_files:
7 | - variables.yaml
8 |
9 | tasks:
10 | - name: Remove ssl binding from Citrix ADC {{ adc_address }}
11 | citrix_adc_nitro_resource:
12 | nsip: "{{ adc_address }}"
13 | nitro_user: "{{ adc_username }}"
14 | nitro_pass: "{{ adc_password }}"
15 | nitro_protocol: http
16 | validate_certs: false
17 | state: absent
18 | workflow:
19 | lifecycle: binding
20 | endpoint: sslvserver_sslcertkey_binding
21 | bound_resource_missing_errorcode: 461
22 | primary_id_attribute: vservername
23 | delete_id_attributes:
24 | - certkeyname
25 | - crlcheck
26 | - ocspcheck
27 | - ca
28 | - snicert
29 | resource:
30 | vservername: "vs-{{ test_site.name }}.{{ test_site.domain }}"
31 | certkeyname: "{{ test_site.name }}.{{ test_site.domain }}_certkey"
32 | snicert: true
33 | delegate_to: localhost
34 |
35 | - name: Remove lb vserver from Citrix ADC {{ adc_address }}
36 | citrix_adc_service:
37 | nsip: "{{ adc_address }}"
38 | nitro_user: "{{ adc_username }}"
39 | nitro_pass: "{{ adc_password }}"
40 | nitro_protocol: http
41 | validate_certs: false
42 | state: absent
43 | name: "vs-{{ test_site.name }}.{{ test_site.domain }}"
44 | delegate_to: localhost
45 |
46 | - name: Remove service-http-1 from Citrix ADC {{ adc_address }}
47 | citrix_adc_service:
48 | nsip: "{{ adc_address }}"
49 | nitro_user: "{{ adc_username }}"
50 | nitro_pass: "{{ adc_password }}"
51 | nitro_protocol: http
52 | validate_certs: false
53 | state: absent
54 | name: service-http-1
55 | delegate_to: localhost
56 |
57 | - name: Remove service-http-2 from Citrix ADC {{ adc_address }}
58 | citrix_adc_service:
59 | nsip: "{{ adc_address }}"
60 | nitro_user: "{{ adc_username }}"
61 | nitro_pass: "{{ adc_password }}"
62 | nitro_protocol: http
63 | validate_certs: false
64 | state: absent
65 | name: service-http-2
66 | delegate_to: localhost
67 |
68 | - name: Remove service-http-3 from Citrix ADC {{ adc_address }}
69 | citrix_adc_service:
70 | nsip: "{{ adc_address }}"
71 | nitro_user: "{{ adc_username }}"
72 | nitro_pass: "{{ adc_password }}"
73 | nitro_protocol: http
74 | validate_certs: false
75 | state: absent
76 | name: service-http-3
77 | delegate_to: localhost
78 |
79 | - name: Remove Certkey from Citrix ADC {{ adc_address }}
80 | citrix_adc_ssl_certkey:
81 | nsip: "{{ adc_address }}"
82 | nitro_user: "{{ adc_username }}"
83 | nitro_pass: "{{ adc_password }}"
84 | nitro_protocol: http
85 | validate_certs: false
86 | state: absent
87 | certkey: "{{ test_site.name }}.{{ test_site.domain }}_certkey"
88 | delegate_to: localhost
89 |
90 | - name: Remove Private Key from Citrix ADC {{ adc_address }}
91 | citrix_adc_system_file:
92 | nsip: "{{ adc_address }}"
93 | nitro_user: "{{ adc_username }}"
94 | nitro_pass: "{{ adc_password }}"
95 | nitro_protocol: http
96 | validate_certs: false
97 | state: absent
98 | filelocation: "/nsconfig/ssl"
99 | filename: "{{ key_name }}"
100 | delegate_to: localhost
101 |
102 | - name: Remove Certificate from Citrix ADC {{ adc_address }}
103 | citrix_adc_system_file:
104 | nsip: "{{ adc_address }}"
105 | nitro_user: "{{ adc_username }}"
106 | nitro_pass: "{{ adc_password }}"
107 | nitro_protocol: http
108 | validate_certs: false
109 | state: absent
110 | filelocation: "/nsconfig/ssl"
111 | filename: "{{ cert_name }}"
112 | delegate_to: localhost
113 |
114 | - name: Remove CA Bundle from Citrix ADC {{ adc_address }}
115 | citrix_adc_system_file:
116 | nsip: "{{ adc_address }}"
117 | nitro_user: "{{ adc_username }}"
118 | nitro_pass: "{{ adc_password }}"
119 | nitro_protocol: http
120 | validate_certs: false
121 | state: absent
122 | filelocation: "/nsconfig/ssl"
123 | filename: "{{ chain_name }}"
124 |
125 | - name: Delete Local Crypto Assets
126 | file:
127 | state: absent
128 | path: "./tmp/"
129 |
--------------------------------------------------------------------------------
/examples/citrix_adc/citrix_create_playbook.yaml:
--------------------------------------------------------------------------------
1 | - name: Create Critx ADC Application
2 | hosts: localhost
3 | connection: local
4 | collections: citrix.adc
5 |
6 | vars_files:
7 | - variables.yaml
8 |
9 | roles:
10 | - role: venafi.ansible_role_venafi
11 |
12 | certificate_common_name: "{{ test_site.name }}.{{ test_site.domain }}"
13 | certificate_alt_name: "DNS:{{ test_site.name }}.{{ test_site.domain }}"
14 | certificate_privatekey_type: "RSA"
15 | certificate_privatekey_size: "2048"
16 | certificate_chain_option: "last"
17 |
18 | certificate_cert_dir: "./tmp"
19 | certificate_cert_path: "./tmp/{{ cert_name }}"
20 | certificate_chain_path: "./tmp/{{ chain_name }}"
21 | certificate_privatekey_path: "./tmp/{{ key_name }}"
22 | certificate_copy_private_key_to_remote: false
23 |
24 | certificate_remote_execution: false
25 | certificate_remote_privatekey_path: "./tmp/{{ key_name }}.remote"
26 | certificate_remote_cert_path: "./tmp/{{ cert_name }}.remote"
27 | certificate_remote_chain_path: "./tmp/{{ chain_name }}.remote"
28 |
29 | tasks:
30 | - name: Copy Private Key to Citrix ADC {{ adc_address }}
31 | citrix_adc_system_file:
32 | nsip: "{{ adc_address }}"
33 | nitro_user: "{{ adc_username }}"
34 | nitro_pass: "{{ adc_password }}"
35 | nitro_protocol: http
36 | validate_certs: false
37 | state: present
38 | filename: "{{ key_name }}"
39 | filelocation: "/nsconfig/ssl/"
40 | filecontent: "{{ lookup('file', './tmp/' + key_name) }}"
41 | delegate_to: localhost
42 |
43 | - name: Copy Certificate to Citrix ADC {{ adc_address }}
44 | citrix_adc_system_file:
45 | nsip: "{{ adc_address }}"
46 | nitro_user: "{{ adc_username }}"
47 | nitro_pass: "{{ adc_password }}"
48 | nitro_protocol: http
49 | validate_certs: false
50 | state: present
51 | filename: "{{ cert_name }}"
52 | filelocation: "/nsconfig/ssl/"
53 | filecontent: "{{ lookup('file', './tmp/' + cert_name + '.remote') }}"
54 | delegate_to: localhost
55 |
56 | - name: Copy CA Bundle to Citrix ADC {{ adc_address }}
57 | citrix_adc_system_file:
58 | nsip: "{{ adc_address }}"
59 | nitro_user: "{{ adc_username }}"
60 | nitro_pass: "{{ adc_password }}"
61 | nitro_protocol: http
62 | validate_certs: false
63 | state: present
64 | filename: "{{ chain_name }}"
65 | filelocation: "/nsconfig/ssl"
66 | filecontent: "{{ lookup('file', './tmp/' + chain_name + '.remote') }}"
67 | delegate_to: localhost
68 |
69 | - name: Create Certkey on Citrix ADC {{ adc_address }}
70 | citrix_adc_ssl_certkey:
71 | nsip: "{{ adc_address }}"
72 | nitro_user: "{{ adc_username }}"
73 | nitro_pass: "{{ adc_password }}"
74 | nitro_protocol: http
75 | validate_certs: false
76 | state: present
77 | certkey: "{{ test_site.name }}.{{ test_site.domain }}_certkey"
78 | cert: "/nsconfig/ssl/{{ cert_name }}"
79 | key: "/nsconfig/ssl/{{ key_name }}"
80 |
81 | - name: Create service-http-1 on Citrix ADC {{ adc_address }}
82 | citrix_adc_service:
83 | nsip: "{{ adc_address }}"
84 | nitro_user: "{{ adc_username }}"
85 | nitro_pass: "{{ adc_password }}"
86 | nitro_protocol: http
87 | validate_certs: false
88 | state: present
89 | name: service-http-1
90 | servicetype: HTTP
91 | ip: "{{ http_service }}"
92 | ipaddress: "{{ http_service }}"
93 | port: "{{ port1 }}"
94 | delegate_to: localhost
95 |
96 | - name: Create service-http-2 on Citrix ADC {{ adc_address }}
97 | citrix_adc_service:
98 | nsip: "{{ adc_address }}"
99 | nitro_user: "{{ adc_username }}"
100 | nitro_pass: "{{ adc_password }}"
101 | nitro_protocol: http
102 | validate_certs: false
103 | state: present
104 | name: service-http-2
105 | servicetype: HTTP
106 | ip: "{{ http_service }}"
107 | ipaddress: "{{ http_service }}"
108 | port: "{{ port2 }}"
109 | delegate_to: localhost
110 |
111 | - name: Create service-http-3 on Citrix ADC {{ adc_address }}
112 | citrix_adc_service:
113 | nsip: "{{ adc_address }}"
114 | nitro_user: "{{ adc_username }}"
115 | nitro_pass: "{{ adc_password }}"
116 | nitro_protocol: http
117 | validate_certs: false
118 | state: present
119 | name: service-http-3
120 | servicetype: HTTP
121 | ip: "{{ http_service }}"
122 | ipaddress: "{{ http_service }}"
123 | port: "{{ port3 }}"
124 | delegate_to: localhost
125 |
126 | - name: Create lb vserver on Citrix ADC {{ adc_address }}
127 | citrix_adc_lb_vserver:
128 | nsip: "{{ adc_address }}"
129 | nitro_user: "{{ adc_username }}"
130 | nitro_pass: "{{ adc_password }}"
131 | nitro_protocol: http
132 | validate_certs: false
133 | state: present
134 | name: "vs-{{ test_site.name }}.{{ test_site.domain }}"
135 | servicetype: SSL
136 | timeout: 2
137 | ipv46: "{{ adc_virtual_ip }}"
138 | port: "{{ adc_virtual_port }}"
139 | lbmethod: ROUNDROBIN
140 | ssl_certkey: "{{ test_site.name }}.{{ test_site.domain }}_certkey"
141 | servicebindings:
142 | - servicename: service-http-1
143 | weight: 80
144 | - servicename: service-http-2
145 | weight: 60
146 | - servicename: service-http-3
147 | weight: 40
148 | disabled: no
149 | delegate_to: localhost
150 |
151 |
152 |
--------------------------------------------------------------------------------
/tests/venafi-role-playbook-example.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # We need Docker provisioning only for demo purpose
3 | - name: "Bring up Docker containers for Docker connection inventory iface"
4 | hosts: localhost
5 | roles:
6 | - role: provision_docker
7 | provision_docker_privileged: true
8 | provision_docker_inventory_group: "{{ groups['robots'] }}"
9 | provision_docker_use_docker_connection: true
10 | when: docker_demo is defined
11 |
12 | - name: Prepare
13 | hosts: all
14 | gather_facts: false
15 | tasks:
16 | - name: "Set CN fact"
17 | set_fact:
18 | cn: "{{ 10000|random }}"
19 |
20 | - hosts: robots
21 | vars:
22 | certificate_common_name: "{{ ansible_fqdn }}-{{ cn }}.venafi.example.com"
23 | certificate_alt_name: "IP:{{ansible_default_ipv4.address}},DNS:{{ ansible_fqdn }}-{{ cn }}-alt.venafi.example.com"
24 | certificate_cert_dir: "/tmp/ansible/etc/ssl/{{ certificate_common_name }}"
25 | certificate_cert_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.pem"
26 | certificate_chain_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.chain.pem"
27 | certificate_privatekey_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.key"
28 | certificate_csr_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.csr"
29 |
30 | # Where to execute venafi_certificate module. If set to false certificate will be
31 | # created on Ansible master host and then copied to the remote server
32 | certificate_remote_execution: false
33 | # remote location where to place the certificate.
34 | certificate_remote_cert_dir: "/etc/ssl"
35 | certificate_remote_cert_path: "{{ certificate_remote_cert_dir }}/{{ certificate_common_name }}.pem"
36 | certificate_remote_chain_path: "{{ certificate_remote_cert_dir }}/{{ certificate_common_name }}.chain.pem"
37 | certificate_remote_privatekey_path: "{{ certificate_remote_cert_dir }}/{{ certificate_common_name }}.key"
38 | # Set to false if you don't want to copy private key to remote location
39 | certificate_copy_private_key_to_remote: true
40 |
41 | roles:
42 | - role: "ansible-role-venafi"
43 |
44 | # This tasks needed only for certificate verification
45 | tasks:
46 | - name: "Install vcert for verification"
47 | pip:
48 | name:
49 | - vcert
50 | - name: "Verify Venafi certificate on remote host"
51 | venafi_certificate:
52 | url: "{{ venafi.url | default(omit) }}"
53 | token: "{{ venafi.token | default(omit) }}"
54 | zone: "{{ venafi.zone | default(omit) }}"
55 | test_mode: "{{ venafi.test_mode if venafi.test_mode is defined else 'false' }}"
56 | user: "{{ venafi.user | default(omit) }}"
57 | password: "{{ venafi.password | default(omit) }}"
58 | trust_bundle: "{{ venafi.trust_bundle | default(omit) }}"
59 | cert_path: "{{ certificate_remote_cert_path if certificate_remote_cert_path is defined else certificate_cert_path }}"
60 | chain_path: "{{ certificate_remote_chain_path if certificate_remote_chain_path else certificate_chain_path }}"
61 | privatekey_path: "{{ certificate_remote_privatekey_path if certificate_remote_privatekey_path else certificate_privatekey_path }}"
62 | common_name: "{{ certificate_common_name }}"
63 | alt_name: "{{ certificate_alt_name }}"
64 | check_mode: true
65 | register: cert_validation
66 |
67 | - debug:
68 | msg: "Certificate {{ certificate_common_name }} is not in valid state: {{ cert_validation.changed_msg }}"
69 | when: cert_validation is changed
70 |
71 | - name: "Example verification which will always fail with debug message"
72 | venafi_certificate:
73 | url: "{{ venafi.url | default(omit) }}"
74 | token: "{{ venafi.token | default(omit) }}"
75 | zone: "{{ venafi.zone | default(omit) }}"
76 | test_mode: "{{ venafi.test_mode if venafi.test_mode is defined else 'false' }}"
77 | user: "{{ venafi.user | default(omit) }}"
78 | password: "{{ venafi.password | default(omit) }}"
79 | trust_bundle: "{{ venafi.trust_bundle | default(omit) }}"
80 | cert_path: "{{ certificate_remote_cert_path if certificate_remote_cert_path is defined else certificate_cert_path }}"
81 | chain_path: "{{ certificate_remote_chain_path if certificate_remote_chain_path else certificate_chain_path }}"
82 | privatekey_path: "{{ certificate_remote_privatekey_path if certificate_remote_privatekey_path else certificate_privatekey_path }}"
83 | common_name: "{{ certificate_common_name }}-fail-check"
84 | alt_name: "{{ certificate_alt_name }}"
85 | check_mode: true
86 | register: cert_validation_failed
87 |
88 | - debug:
89 | msg: "Certificate {{ certificate_common_name }} is not in valid state: {{ cert_validation_failed.changed_msg }}"
90 | when: cert_validation_failed is changed
91 |
92 | - name: "This one shouldn't enroll new Venafi certificate on remote host because it's valid"
93 | venafi_certificate:
94 | url: "{{ venafi.url | default(omit) }}"
95 | token: "{{ venafi.token | default(omit) }}"
96 | zone: "{{ venafi.zone | default(omit) }}"
97 | test_mode: "{{ venafi.test_mode if venafi.test_mode is defined else 'false' }}"
98 | user: "{{ venafi.user | default(omit) }}"
99 | password: "{{ venafi.password | default(omit) }}"
100 | trust_bundle: "{{ venafi.trust_bundle | default(omit) }}"
101 | cert_path: "{{ certificate_remote_cert_path if certificate_remote_cert_path is defined else certificate_cert_path }}"
102 | chain_path: "{{ certificate_remote_chain_path if certificate_remote_chain_path else certificate_chain_path }}"
103 | privatekey_path: "{{ certificate_remote_privatekey_path if certificate_remote_privatekey_path else certificate_privatekey_path }}"
104 | common_name: "{{ certificate_common_name }}"
105 | register: result
106 |
107 | - name: "Certificate is in following state:"
108 | debug:
109 | msg: "{{ result }}"
110 |
--------------------------------------------------------------------------------
/molecule/default/playbook.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Prepare
3 | hosts: all
4 | gather_facts: false
5 | tasks:
6 | - name: "Set CN fact"
7 | set_fact:
8 | cn: "{{ 10000|random }}"
9 | # TODO: make test cases of ECDSA, minimum variables, maximum variables
10 | - name: Converge
11 | hosts: all
12 | vars:
13 | tpp_alt_names: "email:e@venafi.com,IP:192.168.0.15,DNS:{{ ansible_fqdn }}-{{ cn }}-alt.venafi.example.com"
14 | cloud_alt_names: "DNS:{{ ansible_fqdn }}-{{ cn }}-alt.venafi.example.com"
15 | certificate_common_name: "{{ ansible_fqdn }}-{{ cn }}.venafi.example.com"
16 | certificate_alt_name: "{{ cloud_alt_names if venafi.token is defined else tpp_alt_names }}"
17 | certificate_cert_dir: "/tmp/ansible/etc/ssl/{{ certificate_common_name }}"
18 | certificate_cert_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.pem"
19 | certificate_chain_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.chain.pem"
20 | certificate_privatekey_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.key"
21 | certificate_privatekey_type: "RSA"
22 | certificate_privatekey_size: 4096
23 | certificate_csr_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.csr"
24 |
25 | # Where to execute venafi_certificate module. If set to false certificate will be
26 | # created on Ansible master host and then copied to the remote server
27 | certificate_remote_execution: false
28 | # remote location where to place the certificate.
29 | certificate_remote_cert_dir: "/etc/ssl"
30 | certificate_remote_cert_path: "{{ certificate_remote_cert_dir }}/{{ certificate_common_name }}.pem"
31 | certificate_remote_chain_path: "{{ certificate_remote_cert_dir }}/{{ certificate_common_name }}.chain.pem"
32 | certificate_remote_privatekey_path: "{{ certificate_remote_cert_dir }}/{{ certificate_common_name }}.key"
33 | # Set to false if you don't want to copy private key to remote location
34 | certificate_copy_private_key_to_remote: true
35 |
36 | roles:
37 | - role: ansible-role-venafi
38 |
39 | # This tasks needed only for certificate verification
40 | tasks:
41 | - name: "Install future library"
42 | pip:
43 | name:
44 | - future
45 | - name: "Install vcert for verification"
46 | pip:
47 | name:
48 | - vcert==0.10.0
49 |
50 | - name: "Verify Venafi certificate on remote host"
51 | venafi_certificate:
52 | url: "{{ venafi.url | default(omit) }}"
53 | token: "{{ venafi.token | default(omit) }}"
54 | zone: "{{ venafi.zone | default(omit) }}"
55 | test_mode: "{{ venafi.test_mode if venafi.test_mode is defined else 'false' }}"
56 | user: "{{ venafi.user | default(omit) }}"
57 | password: "{{ venafi.password | default(omit) }}"
58 | access_token: "{{ venafi.access_token | default(omit) }}"
59 | trust_bundle: "{{ venafi.trust_bundle | default(omit) }}"
60 | cert_path: "{{ certificate_remote_cert_path if certificate_remote_cert_path is defined else certificate_cert_path }}"
61 | chain_path: "{{ certificate_remote_chain_path if certificate_remote_chain_path else certificate_chain_path }}"
62 | privatekey_path: "{{ certificate_remote_privatekey_path if certificate_remote_privatekey_path else certificate_privatekey_path }}"
63 | common_name: "{{ certificate_common_name }}"
64 | alt_name: "{{ certificate_alt_name }}"
65 | check_mode: true
66 | register: cert_validation
67 |
68 | - debug:
69 | msg: "Certificate {{ certificate_common_name }} is not in valid state: {{ cert_validation.changed_msg }}"
70 | when: cert_validation is changed
71 |
72 | - name: "Example verification which will always fail with debug message"
73 | venafi_certificate:
74 | url: "{{ venafi.url | default(omit) }}"
75 | token: "{{ venafi.token | default(omit) }}"
76 | zone: "{{ venafi.zone | default(omit) }}"
77 | test_mode: "{{ venafi.test_mode if venafi.test_mode is defined else 'false' }}"
78 | user: "{{ venafi.user | default(omit) }}"
79 | password: "{{ venafi.password | default(omit) }}"
80 | access_token: "{{ venafi.access_token | default(omit) }}"
81 | trust_bundle: "{{ venafi.trust_bundle | default(omit) }}"
82 | cert_path: "{{ certificate_remote_cert_path if certificate_remote_cert_path is defined else certificate_cert_path }}"
83 | chain_path: "{{ certificate_remote_chain_path if certificate_remote_chain_path else certificate_chain_path }}"
84 | privatekey_path: "{{ certificate_remote_privatekey_path if certificate_remote_privatekey_path else certificate_privatekey_path }}"
85 | common_name: "{{ certificate_common_name }}-fail-check"
86 | alt_name: "{{ certificate_alt_name }}"
87 | check_mode: true
88 | register: cert_validation_failed
89 |
90 | - debug:
91 | msg: "Certificate {{ certificate_common_name }} is not in valid state: {{ cert_validation_failed.changed_msg }}"
92 | when: cert_validation_failed is changed
93 |
94 | - name: "Fail playbook if cert_validation_failed is not in changed state"
95 | fail:
96 | msg: "Certificate should be in changed stage but it is not"
97 | when: cert_validation_failed is not changed
98 |
99 | - name: "This one shouldn't enroll new Venafi certificate on remote host because it's valid"
100 | venafi_certificate:
101 | url: "{{ venafi.url | default(omit) }}"
102 | token: "{{ venafi.token | default(omit) }}"
103 | zone: "{{ venafi.zone | default(omit) }}"
104 | test_mode: "{{ venafi.test_mode if venafi.test_mode is defined else 'false' }}"
105 | user: "{{ venafi.user | default(omit) }}"
106 | password: "{{ venafi.password | default(omit) }}"
107 | access_token: "{{ venafi.access_token | default(omit) }}"
108 | trust_bundle: "{{ venafi.trust_bundle | default(omit) }}"
109 | cert_path: "{{ certificate_remote_cert_path if certificate_remote_cert_path is defined else certificate_cert_path }}"
110 | chain_path: "{{ certificate_remote_chain_path if certificate_remote_chain_path else certificate_chain_path }}"
111 | privatekey_path: "{{ certificate_remote_privatekey_path if certificate_remote_privatekey_path else certificate_privatekey_path }}"
112 | common_name: "{{ certificate_common_name }}"
113 | alt_name: "{{ certificate_alt_name }}"
114 | register: cert_is_valid
115 |
116 | - name: "Certificate is in following state:"
117 | debug:
118 | msg: "{{ cert_is_valid }}"
119 |
120 | - name: "Fail playbook if cert_is_valid is in changed state"
121 | fail:
122 | msg: "Certificate should not be in changed stage but it is"
123 | when: cert_is_valid is changed
124 |
--------------------------------------------------------------------------------
/tests/venafi-playbook-example.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # We need Docker provisioning only for demo purpose
3 | - name: "Bring up Docker containers for Docker connection inventory iface"
4 | hosts: localhost
5 | roles:
6 | - role: provision_docker
7 | provision_docker_privileged: true
8 | provision_docker_inventory_group: "{{ groups['robots'] }}"
9 | provision_docker_use_docker_connection: true
10 | when: docker_demo is defined
11 |
12 | - name: Prepare
13 | hosts: all
14 | gather_facts: false
15 | tasks:
16 | - name: "Set random CN fact"
17 | set_fact:
18 | cn: "{{ 10000|random }}"
19 |
20 | - hosts: robots
21 | vars:
22 | credentials_file: "../credentials.yml"
23 | # Use Ansible host FQDN for certificate common name
24 | certificate_common_name: "{{ ansible_fqdn }}-{{ cn }}.venafi.example.com"
25 | # Use ansible default IP for DNS
26 | certificate_alt_name: "IP:{{ansible_default_ipv4.address}},DNS:{{ ansible_fqdn }}-{{ cn }}-alt.venafi.example.com"
27 |
28 | # Directory where to place certificates
29 | certificate_cert_dir: "/tmp/ansible/etc/ssl/{{ certificate_common_name }}"
30 | # Paths for certficaite and keys
31 | certificate_cert_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.pem"
32 | certificate_chain_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.chain.pem"
33 | certificate_privatekey_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.key"
34 | certificate_csr_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.csr"
35 |
36 | # Where to execute venafi_certificate module. If set to false, certificate will be
37 | # created on ansible master host and then copied to the remote server
38 | certificate_remote_execution: false
39 | # remote location where to place the certificate_
40 | certificate_remote_cert_path: "/etc/ssl/{{ certificate_common_name }}.pem"
41 | certificate_remote_chain_path: "/etc/ssl/{{ certificate_common_name }}.chain.pem"
42 | certificate_remote_privatekey_path: "/etc/ssl/{{ certificate_common_name }}.key"
43 | # Set to false, if you don't want to copy private key to remote location
44 | certificate_copy_private_key_to_remote: true
45 |
46 | # Modify default before expire hours variable
47 | certificate_before_expired_hours: 100
48 |
49 | # Set to true if you want forcly renew certificate
50 | certificate_force: false
51 |
52 | tasks:
53 | - name: "Include vars of {{ credentials_file }} into the venafi variable."
54 | include_vars:
55 | file: "{{ credentials_file }}"
56 | name: venafi
57 |
58 | - name: "Create directory {{ certificate_cert_dir }}"
59 | local_action:
60 | module: file
61 | path: "{{ certificate_cert_dir }}"
62 | state: directory
63 |
64 | - name: "Enroll Venafi certificate on local host"
65 | local_action:
66 | module: venafi_certificate
67 | url: "{{ venafi.url | default(omit) }}"
68 | token: "{{ venafi.token | default(omit) }}"
69 | zone: "{{ venafi.zone | default(omit) }}"
70 | test_mode: "{{ venafi.test_mode if venafi.test_mode is defined else 'false' }}"
71 | user: "{{ venafi.user | default(omit) }}"
72 | password: "{{ venafi.password | default(omit) }}"
73 | trust_bundle: "{{ venafi.trust_bundle | default(omit) }}"
74 | cert_path: "{{ certificate_cert_path }}"
75 | chain_path: "{{ certificate_chain_path | default(omit) }}"
76 | privatekey_path: "{{ certificate_privatekey_path | default(omit) }}"
77 | privatekey_size: "{{ certificate_privatekey_size | default(omit) }}"
78 | common_name: "{{ certificate_common_name }}"
79 | register: certout
80 | - name: "Certificate is in following state:"
81 | debug:
82 | msg: "{{ certout }}"
83 |
84 |
85 | - name: "Copy Venafi certificate file to remote location {{ certificate_remote_cert_path if certificate_remote_cert_path is defined else certificate_cert_path }}"
86 | copy:
87 | src: "{{ certificate_cert_path }}"
88 | dest: "{{ certificate_remote_cert_path if certificate_remote_cert_path is defined else certificate_cert_path }}"
89 |
90 | - name: "Copy Venafi private key file to remote location {{ certificate_remote_privatekey_path if certificate_remote_privatekey_path else certificate_privatekey_path }}"
91 | copy:
92 | src: "{{ certificate_privatekey_path }}"
93 | dest: "{{ certificate_remote_privatekey_path if certificate_remote_privatekey_path else certificate_privatekey_path }}"
94 | when: certificate_copy_private_key_to_remote
95 |
96 | - name: "Copy Venafi certificate chain file to remote location {{ certificate_remote_chain_path if certificate_remote_chain_path else certificate_chain_path }}"
97 | copy:
98 | src: "{{ certificate_chain_path }}"
99 | dest: "{{ certificate_remote_chain_path if certificate_remote_chain_path else certificate_chain_path }}"
100 | when: certificate_chain_path is defined
101 |
102 | - name: "Install vcert for verification"
103 | pip:
104 | name:
105 | - vcert
106 |
107 | - name: "Verify Venafi certificate on remote host"
108 | venafi_certificate:
109 | url: "{{ venafi.url | default(omit) }}"
110 | token: "{{ venafi.token | default(omit) }}"
111 | zone: "{{ venafi.zone | default(omit) }}"
112 | test_mode: "{{ venafi.test_mode if venafi.test_mode is defined else 'false' }}"
113 | user: "{{ venafi.user | default(omit) }}"
114 | password: "{{ venafi.password | default(omit) }}"
115 | trust_bundle: "{{ venafi.trust_bundle | default(omit) }}"
116 | cert_path: "{{ certificate_remote_cert_path if certificate_remote_cert_path is defined else certificate_cert_path }}"
117 | chain_path: "{{ certificate_remote_chain_path if certificate_remote_chain_path else certificate_chain_path }}"
118 | privatekey_path: "{{ certificate_remote_privatekey_path if certificate_remote_privatekey_path else certificate_privatekey_path }}"
119 | common_name: "{{ certificate_common_name }}"
120 | check_mode: true
121 | register: cert_validation
122 |
123 | - debug:
124 | msg: "Certificate {{ certificate_common_name }} is not in valid state: {{ cert_validation.changed_msg }}"
125 | when: cert_validation is changed
126 |
127 | - name: "Example verification which will always fail with debug message"
128 | venafi_certificate:
129 | url: "{{ venafi.url | default(omit) }}"
130 | token: "{{ venafi.token | default(omit) }}"
131 | zone: "{{ venafi.zone | default(omit) }}"
132 | test_mode: "{{ venafi.test_mode if venafi.test_mode is defined else 'false' }}"
133 | user: "{{ venafi.user | default(omit) }}"
134 | password: "{{ venafi.password | default(omit) }}"
135 | trust_bundle: "{{ venafi.trust_bundle | default(omit) }}"
136 | cert_path: "{{ certificate_remote_cert_path if certificate_remote_cert_path is defined else certificate_cert_path }}"
137 | chain_path: "{{ certificate_remote_chain_path if certificate_remote_chain_path else certificate_chain_path }}"
138 | privatekey_path: "{{ certificate_remote_privatekey_path if certificate_remote_privatekey_path else certificate_privatekey_path }}"
139 | common_name: "{{ certificate_common_name }}-fail-check"
140 | check_mode: true
141 | register: cert_validation_failed
142 |
143 | - debug:
144 | msg: "Certificate {{ certificate_common_name }} is not in valid state: {{ cert_validation_failed.changed_msg }}"
145 | when: cert_validation_failed is changed
146 |
147 | - name: "Fail playbook if cert_validation_failed is not in changed state"
148 | fail:
149 | msg: "Certificate should be in changed stage but it is not"
150 | when: cert_validation_failed is not changed
151 |
152 | - name: "This one shouldn't enroll new Venafi certificate on remote host because it's valid"
153 | venafi_certificate:
154 | url: "{{ venafi.url | default(omit) }}"
155 | token: "{{ venafi.token | default(omit) }}"
156 | zone: "{{ venafi.zone | default(omit) }}"
157 | test_mode: "{{ venafi.test_mode if venafi.test_mode is defined else 'false' }}"
158 | user: "{{ venafi.user | default(omit) }}"
159 | password: "{{ venafi.password | default(omit) }}"
160 | trust_bundle: "{{ venafi.trust_bundle | default(omit) }}"
161 | cert_path: "{{ certificate_remote_cert_path if certificate_remote_cert_path is defined else certificate_cert_path }}"
162 | chain_path: "{{ certificate_remote_chain_path if certificate_remote_chain_path else certificate_chain_path }}"
163 | privatekey_path: "{{ certificate_remote_privatekey_path if certificate_remote_privatekey_path else certificate_privatekey_path }}"
164 | common_name: "{{ certificate_common_name }}"
165 | register: result
166 |
167 | - name: "Certificate is in following state:"
168 | debug:
169 | msg: "{{ result }}"
170 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2019 Venafi Inc.
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | [](https://opensource.org/licenses/Apache-2.0)
3 | 
4 | 
5 | _:warning: **This community-supported open source project has reached its END-OF-LIFE, and as of May 30th 2025, this project is deprecated and will no longer be maintained**. Please use **[Ansible Collection for Venafi](https://github.com/Venafi/ansible-collection-venafi)** instead. Switching over is easy, simply install the `venafi.machine_identity` collection using Ansible Galaxy and replace `role: venafi.ansible_role_venafi` with `role: venafi.machine_identity.certificate` in your playbooks._
6 |
7 | # Venafi Role for Ansible
8 |
9 | This solution adds certificate enrollment capabilities to [Red Hat Ansible](https://www.ansible.com/) by seamlessly
10 | integrating with the [Venafi Trust Protection Platform](https://www.venafi.com/platform/trust-protection-platform)
11 | or [Venafi as a Service](https://www.venafi.com/venaficloud) in a manner that ensures compliance with corporate
12 | security policy and provides visibility into certificate issuance enterprise wide.
13 |
14 | >:red_car: **Test drive our integration examples today**
15 | >
16 | >Let us show you _step-by-step_ how to add certificates to your _Infrastucture as Code_ automation with Ansible.
17 | >
18 | >Products | Available integration examples...
19 | >:------: | --------
20 | >[
](examples/f5_bigip/README.md) | [How to configure secure application delivery using F5 BIG-IP and the Venafi Ansible role](examples/f5_bigip/README.md)
21 | >[
](examples/citrix_adc/README.md) | [How to configure secure application delivery using Citrix ADC and the Venafi Ansible role](examples/citrix_adc/README.md)
22 | >
23 | >**NOTE** If you don't see an example for a product you use, check back later. We're working hard to add more integration examples.
24 |
25 | ## Requirements
26 |
27 | Review the [Venafi](https://github.com/Venafi/vcert-python#prerequisites-for-using-with-trust-protection-platform)
28 | prerequisites, then install Ansible and [VCert-Python](https://github.com/Venafi/vcert-python) (v0.10.0 or higher) using `pip`:
29 | ```sh
30 | pip install ansible vcert --upgrade
31 | ```
32 |
33 | ## Using with Ansible Galaxy
34 |
35 | For more information about Ansible Galaxy, go to https://galaxy.ansible.com/docs/using/installing.html
36 |
37 | 1. Install the [Venafi Role for Ansible](https://galaxy.ansible.com/venafi/ansible_role_venafi) from Ansible Galaxy:
38 |
39 | ```sh
40 | ansible-galaxy install venafi.ansible_role_venafi
41 | ```
42 |
43 | 1. Create the `credentials.yml` and populate it with connection parameters:
44 |
45 | **Trust Protection Platform**:
46 |
47 | ```sh
48 | cat <>credentials.yml
49 | access_token: 'p0WTt3sDPbzm2BDIkoJROQ=='
50 | url: 'https://tpp.venafi.example'
51 | zone: "DevOps\\Ansible"
52 | trust_bundle: "/path/to/bundle.pem"
53 | EOF
54 | ```
55 |
56 | **Venafi as a Service**:
57 |
58 | ```sh
59 | cat <>credentials.yml
60 | token: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
61 | zone: 'Business App\\Enterprise CIT'
62 | EOF
63 | ```
64 |
65 | The Venafi Role for Ansible supports the following connection and credential settings:
66 |
67 | | Variable Name | Description |
68 | | -------------- | ------------------------------------------------------------ |
69 | | `access_token` | Trust Protection Platform access token for the "ansible-by-venafi" API Application |
70 | | `password` | **[DEPRECATED]** Trust Protection Platform WebSDK password, use `access_token` if possible |
71 | | `test_mode` | When "true", the role operates without connecting to Trust Protection Platform or Venafi as a Service |
72 | | `token` | Venafi as a Service API key |
73 | | `trust_bundle` | Text file containing trust anchor certificates in PEM (text) format, generally required for Trust Protection Platform |
74 | | `url` | Venafi service URL (e.g. "https://tpp.venafi.example"), generally only applicable to Trust Protection Platform |
75 | | `user` | **[DEPRECATED]** Trust Protection Platform WebSDK username, use `access_token` if possible |
76 | | `zone` | Policy folder for TPP or Application name and Issuing Template API Alias for VaaS (e.g. "Business App\Enterprise CIT") |
77 |
78 | 1. Use `ansible-vault` to encrypt the `credentials.yml` file using a password. This is optional but highly recommended.
79 | As long as you know the password you can always decrypt the file to make changes and then re-encrypt it.
80 | Go to https://docs.ansible.com/ansible/latest/user_guide/vault.html for more information.
81 |
82 | ```sh
83 | ansible-vault encrypt credentials.yml
84 | ```
85 |
86 | 1. Write a simple playbook called, for example, `sample.yml`.
87 |
88 | ```yaml
89 | - hosts: localhost
90 | roles:
91 | - role: venafi.ansible_role_venafi
92 | certificate_cert_dir: "/tmp/etc/ssl/{{ certificate_common_name }}"
93 | ```
94 |
95 | 1. Run the playbook.
96 |
97 | ```sh
98 | ansible-playbook sample.yml --ask-vault-pass
99 | ```
100 |
101 | Running the playbook will generate a certificate and place it into folder in /tmp/etc/ssl/ directory.
102 | The `--ask-vault-pass` parameter is needed if you encrypted the `credentials.yml` file. Additional
103 | playbook variables can be added to specify properties of the certificate and key pair, file locations,
104 | and to override default behaviors.
105 |
106 | | Variable Name | Description |
107 | | ---------------------------------------- | ------------------------------------------------------------ |
108 | | `credentials_file` | Name of the file containing Venafi credentials and connection settings
Default: `credentials.yml` |
109 | | `certificate_common_name` | *Common Name* to request for the certificate.
Default: `"{{ ansible_fqdn }}"` |
110 | | `certificate_alt_name` | Comma separated list of *Subject Alternative Names* to request for the certificate. Prefix each value with the SAN type.
Example: `"DNS:host.example.com,IP:10.20.30.40,email:me@example.com"` | |
111 | | `certificate_privatekey_type` | Key algorithm, "RSA" or "ECDSA"
Default: `"RSA"` (from VCert) |
112 | | `certificate_privatekey_size` | Key size in bits for RSA keys
Default: `"2048"` (from VCert) |
113 | | `certificate_privatekey_curve` | Elliptic Curve for ECDSA keys
Default: `"P251"` (from VCert) |
114 | | `certificate_privatekey_passphrase` | Password to use for encrypting the private key |
115 | | `certificate_chain_option` | Specifies whether the root CA certificate appears `"last"` (default) or `"first"` in the chain file |
116 | | `certificate_cert_dir` | Local parent directory where the cryptographic assets will be stored
Default: `"/etc/ssl/{{ certificate_common_name }}"` |
117 | | `certificate_cert_path` | Local directory where certificate files will be stored
Default: `{{ certificate_cert_dir }}/{{ certificate_common_name }}.pem"` |
118 | | `certificate_chain_path` | Local directory where certificate chain files will be stored
Default: `"{{ certificate_cert_dir }}/{{ certificate_common_name }}.chain.pem"` |
119 | | `certificate_privatekey_path` | Local directory where private key files will be stored
Default: `"{{ certificate_cert_dir }}/{{ certificate_common_name }}.key"` |
120 | | `certificate_csr_path` | Local directory where certificate signing request files will be stored
Default: `"{{ certificate_cert_dir }}/{{ certificate_common_name }}.csr"` |
121 | | `certificate_remote_execution` | Specifies whether cryptographic assets will be generated remotely, or locally and then provisioned to the remote host
Default: `false` |
122 | | `certificate_remote_cert_path` | Directory on remote host where certificate files will be stored
Default: `"{{ certificate_cert_dir }}/{{ certificate_common_name }}.pem"` |
123 | | `certificate_remote_chain_path` | Directory on remote host where certificate chain files will be stored
Default: `"{{ certificate_cert_dir }}/{{ certificate_common_name }}.chain.pem"` |
124 | | `certificate_remote_privatekey_path` | Directory on remote host where private key files will be stored
Default: `"{{ certificate_cert_dir }}/{{ certificate_common_name }}.key"` |
125 | | `certificate_copy_private_key_to_remote` | Specifies whether to copy the private key file to the remote host
Default: `true` |
126 | | `certificate_before_expired_hours` | Number of hours prior to the expiration of the certificate before it can be renewed
Default: `72` |
127 | | `certificate_renew` | Specifies whether to renew the certificate if it is within the "before_expired_hours" window when the playbook is run
Default: `true` |
128 | | `certificate_force` | Specifies whether to request a new certificate every time the playbook is run
Default: `false` |
129 |
130 | Defaults are defined in the [defaults/main.yml](defaults/main.yml) file.
131 |
132 | ## Preparing a Docker demo environment for running Ansible
133 |
134 | 1. (Optional) Prepare the demo environment. If you want to use your own inventory, update the tests/inventory file.
135 |
136 | 1. To run our test/demo playbook you'll need the Docker provisioning role.
137 | Download it into the `tests/roles/provision_docker` directory:
138 |
139 | ```sh
140 | git clone https://github.com/chrismeyersfsu/provision_docker.git tests/roles/provision_docker
141 | ```
142 |
143 | 1. Then build the Docker images needed for the demo playbook:
144 |
145 | ```sh
146 | docker build ./tests --tag local-ansible-test
147 | ```
148 |
149 | Demo certificates will be placed in the `/tmp/ansible/etc/ssl` directory on the Ansible host.
150 | From there they will be distributed to the `/etc/ssl/` directory of remote hosts.
151 |
152 | 1. Generate a credentials file for either Trust Protection Platform or Venafi as a Service as described in the above section.
153 |
154 | 1. Run the Ansible playbook (remove `docker_demo=true` if you want to use your own inventory).
155 | The contents of `credentials.yml` will be used to decide whether Trust Protection Platform or Venafi as a Service is used.
156 | If you set the `token` parameter, the playbook assumes you are using Venafi as a Service. If you set the `access_token` or
157 | `password` parameters, the playbook assumes you are using Trust Protection Platform.
158 |
159 | ```sh
160 | ansible-playbook -i tests/inventory \
161 | tests/venafi-playbook-example.yml \
162 | --extra-vars "credentials_file=credentials.yml docker_demo=true" \
163 | --ask-vault-pass
164 | ```
165 |
166 | You will be prompted for the password for decrypting the `credentials.yml` as before. The source file for the
167 | credentials can be overridden using the *credentials_file* variable and this can be specified on the command line
168 | using the `--extra-vars` parameter as shown.
169 |
170 | ## Sample Playbook
171 |
172 | ```yaml
173 | - hosts: servers
174 | roles:
175 | - role: "ansible-role-venafi"
176 | certificate_common_name: "{{ ansible_fqdn }}.venafi.example.com"
177 | certificate_cert_dir: "/tmp/ansible/etc/ssl/{{ certificate_common_name }}"
178 | certificate_cert_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.pem"
179 | certificate_chain_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.chain.pem"
180 | certificate_privatekey_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.key"
181 | certificate_csr_path: "{{ certificate_cert_dir }}/{{ certificate_common_name }}.csr"
182 |
183 | # Where to execute venafi_certificate module. If set to false, certificate will be
184 | # created on ansible master host and then copied to the remote server.
185 | certificate_remote_execution: false
186 | # Remote location where to place the certificate.
187 | certificate_remote_cert_dir: "/etc/ssl"
188 | certificate_remote_cert_path: "{{ certificate_remote_cert_dir }}/{{ certificate_common_name }}.pem"
189 | certificate_remote_chain_path: "{{ certificate_remote_cert_dir }}/{{ certificate_common_name }}.chain.pem"
190 | certificate_remote_privatekey_path: "{{ certificate_remote_cert_dir }}/{{ certificate_common_name }}.key"
191 | # Set to false if you don't want to copy private key to remote location.
192 | certificate_copy_private_key_to_remote: true
193 | ```
194 |
195 | For playbook examples look into [venafi-playbook-example.yml](tests/venafi-playbook-example.yml) file.
196 | For role examples look into [venafi-role-playbook-example.yml](tests/venafi-role-playbook-example.yml) file
197 |
198 | For more information about using roles go to https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html
199 |
200 | ## License
201 |
202 | Copyright © Venafi, Inc. All rights reserved.
203 |
204 | This solution is licensed under the Apache License, Version 2.0. See `LICENSE` for the full license text.
205 |
206 | Please direct questions/comments to opensource@venafi.com.
207 |
--------------------------------------------------------------------------------
/examples/f5_bigip/README.md:
--------------------------------------------------------------------------------
1 | # Configuring secure application delivery using F5 BIG-IP and the Venafi Ansible Role
2 |
3 | In this example, we'll show you how to better secure application delivery using the Venafi Ansible Role with your [F5 BIG-IP](https://www.f5.com/products/big-ip-services) instance.
4 | Adding Venafi enables you to manage certificates more securely as part of the [TLS termination](https://www.f5.com/services/resources/glossary/ssl-termination) process on your load balancer.
5 |
6 | ## Who should use this example?
7 |
8 | The steps described in this document are typically performed by a _DevOps engineers_ or a _system administrators_. Generally, you'll need a basic undestanding of F5 BIG-IP, Venafi Trust Protection Platform or Venafi Cloud, and the required permissions for completing the tasks described in the example.
9 |
10 | ## About this example
11 |
12 | An _application delivery controller_ (ADC) is used to increase capacity and reliability of applications. ADC improves the performance of applications by decreasing the load on associated servers while managing and maintaining application and network sessions. But ADC configuration can become a lengthy process. However, you can actually automate the process by using a configuration management tool.
13 |
14 | In this example we use [RedHat Ansible](https://www.ansible.com/) with the _Venafi Ansible Role_ to automate the process of requesting, retrieving and installing a certificate as part of SSL termination on an ADC (specifically, F5 BIG-IP) for load balancing web traffic. We'll also utilize three HTTP servers contained in a cluster as the endpoints that are sending and receiving web traffic and being managed by F5 BIG-IP.
15 |
16 | Later in this example, you'll generate a certificate for the `demo-f5.venafi.example` domain using the Venafi Ansible Role to request and retrieve it from either _Venafi Trust Protection Platform_ or _Venafi Cloud_ services. Then you'll copy the certificate files (certificate, private key, chain bundle) to the F5 BIG-IP. Finally, you'll configure F5 BIG-IP to distribute the traffic between three HTTP servers using the round-robin load balancing method. Take a look at the diagram below for an overview of what we're going to create.
17 |
18 | > **NOTE** In our example, we suggest that you use the round-robin balancing method. But keep in mind that there are [other methods](https://www.f5.com/services/resources/glossary/load-balancer) that might be more suitable for your specific use case.
19 |
20 | 
21 |
22 | ## Prerequisites
23 |
24 | To perform the tasks described in this example, you'll need:
25 |
26 | - The Venafi Ansible Role installed on your machine, you can install it using `ansible-galaxy` [as described here](https://github.com/Venafi/ansible-role-venafi#using-with-ansible-galaxy)
27 | - Access to either **Venafi Trust Protection Platform** or **Venafi Cloud** services (the `credentials.yml` [file](https://github.com/Venafi/ansible-role-venafi#using-with-ansible-galaxy) is used in this example).
28 | - If you are working with **Venafi Trust Protection Platform** obtain the `access_token` and `refresh_token` using the [VCert CLI](https://github.com/Venafi/vcert/blob/master/README-CLI-PLATFORM.md#obtaining-an-authorization-token).
29 | - Administration access to the F5 BIG-IP instance.
30 | - A set of 3 HTTP servers running your application.
31 |
32 | ## Getting started
33 |
34 | Here are the steps we'll take as we go trough this example:
35 |
36 | 1. Retrieve a certificate using the Venafi Ansible Role
37 | 2. Copy the retrieved certificate files to F5 BIG-IP
38 | 3. Create a Client SSL Profile on F5 BIG-IP
39 | 4. Create Pool on F5 BIG-IP
40 | 5. Add pool members on F5 BIG-IP
41 | 6. Create a virtual server on F5 BIG-IP
42 | 7. Execute the playbook
43 |
44 | > **NOTE** Credentials used in this example are for demonstration purposes only. You should use stronger credentials.
45 |
46 | > **BEST PRACTICES** In general, be careful when using self-signed certificates because of the inherent risks of no identity verification or trust control. The public and private keys are both held by the same entity. Also, self-signed certificates cannot be revoked; they can only be replaced. If an attacker has already gained access to a system, the attacker can spoof the identity of the subject. Of course, CAs can revoke a certificate only when they discover the compromise.
47 |
48 | ## Step 1: Retrieve a certificate using Venafi Ansible Role
49 |
50 | ### Step 1a: Creating variables file
51 |
52 | The first step is to create the `variables.yaml` file. This file defines the variables used during the execution of the playbook, which include:
53 |
54 | - The F5 BIG-IP management IP address
55 | - The credentials used to manage the F5 BIG-IP
56 | - The CN needed to generate the certificate
57 | - The partition in which all the information will be stored
58 | - The Virtual IP and port on which all the HTTPS traffic will be handled
59 | - The pool members (the NGINX servers running the application)
60 | - The name for the certificate files that will be copied to the F5 BIG-IP
61 | - To facilitate the connection with the device, the connection parameters can be also specified in this file.
62 | - The pattern used for this is called a _provider_; the provider is a dictionary that includes sub-keys such as *password*, *server*, etc.
63 | - In the following steps, the provider dictionary will be passed as a parameter to the tasks so that they can connect to the F5 BIG-IP.
64 |
65 | ```yaml
66 | f5_address: "yourf5bigip"
67 | f5_username: "youruser"
68 | f5_password: "yourpassword"
69 |
70 | test_site:
71 | name: "demo-f5"
72 | domain: "venafi.example"
73 |
74 | f5_partition: "Demo"
75 | f5_virtual_ip: "192.168.7.68"
76 | f5_virtual_port: "443"
77 | f5_pool_members:
78 | - host: 192.168.6.201
79 | port: 8001
80 | - host: 192.168.6.201
81 | port: 8002
82 | - host: 192.168.6.201
83 | port: 8003
84 |
85 | cert_name: "{{ test_site.name }}.crt"
86 | key_name: "{{ test_site.name }}.key"
87 | chain_name: "{{ test_site.name }}-ca-bundle.crt"
88 |
89 | f5_provider:
90 | server: "{{ f5_address }}"
91 | server_port: 443
92 | user: "{{ f5_username }}"
93 | password: "{{ f5_password }}"
94 | validate_certs: no
95 | ```
96 |
97 | ### Step 1b: Creating the playbook
98 |
99 | Start by creating a YAML file named `f5_create_playbook.yaml`, inside, define a name for the playbook, the hosts in which the tasks will be executed, the type of connection to use and specify the variables file created in the previous step :
100 |
101 | ```yaml
102 | - name: Create F5 Application
103 | hosts: localhost
104 | connection: local
105 |
106 | vars_files:
107 | - variables.yaml
108 | ```
109 |
110 | ### Step 1c: Requesting and retrieving the certificate using Venafi Role
111 |
112 | In the following block of instructions the Venafi Ansible Role is being specified along with the variables it needs to request and retrieve the certificate from the Venafi services, by adding these instructions the Ansible will:
113 |
114 | - Request and retrieve a certificate which common and alternate names are `demo-f5.venafi.example`.
115 | - Create a RSA private key of a size of 2048 bits.
116 | - Generate a chain bundle file where the CA certificate will be place at the end of the file.
117 | - Create a `tmp` directory on the current working directory which will store the retrieved certificate files.
118 | - 3 files will be retrieved and stored using the names on the variables file (*demonstration.{crt,key,-ca-bundle.crt}*).
119 | - Simulate the copy of the retrieved files to the remote host by generating a duplicate of them adding the `.remote` extension (the certificate files retrieved are going to be copied to F5 BIG-IP using the F5 Ansible modules that's the reason why the options `certificate_copy_private_key_to_remote` and `certificate_remote_execution` are set to `false`).
120 |
121 |
122 | ```yaml
123 | ---
124 |
125 | roles:
126 | - role: venafi.ansible_role_venafi
127 |
128 | certificate_common_name: "{{ test_site.name }}.{{ test_site.domain }}"
129 | certificate_alt_name: "DNS:{{ test_site.name }}.{{ test_site.domain }}"
130 | certificate_privatekey_type: "RSA"
131 | certificate_privatekey_size: "2048"
132 | certificate_chain_option: "last"
133 |
134 | certificate_cert_dir: "./tmp"
135 | certificate_cert_path: "./tmp/{{ cert_name }}"
136 | certificate_chain_path: "./tmp/{{ chain_name }}"
137 | certificate_privatekey_path: "./tmp/{{ key_name }}"
138 | certificate_copy_private_key_to_remote: false
139 |
140 | certificate_remote_execution: false
141 | certificate_remote_privatekey_path: "/tmp/{{ key_name }}.remote"
142 | certificate_remote_cert_path: "/tmp/{{ cert_name }}.remote"
143 | certificate_remote_chain_path: "/tmp/{{ chan_name }}.remote"
144 | ```
145 |
146 | ## Step 2: Copy the retrieved certificate files to F5 BIG-IP
147 |
148 | By adding the instructions below to the playbook, we specify the actions the playbook will execute. Ansible will connect to the F5 BIG-IP (using the credentials specified in the provider dictionary) and then it will create the key, CA bundle and certificate using the local files retrieved in the previous step.
149 |
150 |
151 | ```yaml
152 | ---
153 |
154 | tasks:
155 | - name: Create Private Key on F5 BIG-IP {{ f5_address }}
156 | bigip_ssl_key:
157 | state: present
158 | provider: "{{ f5_provider }}"
159 | name: "{{ key_name }}"
160 | partition: "{{ f5_partition }}"
161 | content: "{{ lookup('file', './tmp/' + key_name) }}"
162 | delegate_to: localhost
163 |
164 | - name: Create Certificate on F5 BIG-IP {{ f5_address }}
165 | bigip_ssl_certificate:
166 | state: present
167 | provider: "{{ f5_provider }}"
168 | name: "{{ cert_name }}"
169 | partition: "{{ f5_partition }}"
170 | content: "{{ lookup('file', './tmp/' + cert_name + '.remote') }}"
171 | delegate_to: localhost
172 |
173 | - name: Create CA Bundle on F5 BIG-IP {{ f5_address }}
174 | bigip_ssl_certificate:
175 | state: present
176 | provider: "{{ f5_provider }}"
177 | name: "{{ chain_name }}"
178 | partition: "{{ f5_partition }}"
179 | content: "{{ lookup('file', './tmp/' + chain_name + '.remote') }}"
180 | delegate_to: localhost
181 |
182 | ```
183 |
184 | ## Step 3: Create a Client SSL Profile on F5 BIG-IP
185 |
186 | After copying the certificate files to the F5 BIG-IP, we need to specify where those files will be used. You can do this by adding `Client SSL profile`, which enables the F5 BIG-IP system to accept and terminate client requests that are using SSL. And once again, we're specifying the credentials used to execute this task on the F5 instance, as well as specifying the certificate files to use.
187 |
188 | ```yaml
189 | ---
190 | - name: Create Client SSL Profile on F5 BIG-IP {{ f5_address }}
191 | bigip_profile_client_ssl:
192 | state: present
193 | provider: "{{ f5_provider }}"
194 | name: "clientssl_{{ test_site.name }}"
195 | partition: "{{ f5_partition }}"
196 | parent: "clientssl"
197 | cert_key_chain:
198 | - cert: "{{ cert_name }}"
199 | key: "{{ key_name }}"
200 | chain: "{{ chain_name }}"
201 | delegate_to: localhost
202 | ```
203 |
204 | ## Step 4: Create a Pool on F5 BIG-IP
205 |
206 | The next step is to add a pool, which is a collection of resources to which F5 will distribute the requests. This provides load balancing [functionality](https://www.f5.com/services/resources/glossary/load-balancer) by using the [round-robin](https://en.wikipedia.org/wiki/Round-robin_scheduling) method. In this case the members of the pool are the NGINX servers defined in the variables file.
207 |
208 | ```yaml
209 | ---
210 |
211 | - name: Create Pool on F5 BIG-IP {{ f5_address }}
212 | bigip_pool:
213 | state: present
214 | provider: "{{ f5_provider }}"
215 | name: "pool_{{ test_site.name }}"
216 | partition: "{{ f5_partition }}"
217 | lb_method: round-robin
218 | delegate_to: localhost
219 | ```
220 |
221 | ## Step 5: Add Pool members on F5 BIG-IP
222 |
223 | Once the pool is created, Ansible needs to create the pool member in the F5 BIG-IP instance. The members actually serve the requests (NGINX servers hosting the application). Ansible will use the host and port variables defined in the variables file for each one of the pool members defined in the `f5_pool_members` dictionary.
224 |
225 | ```yaml
226 | ---
227 |
228 | - name: Add Pool Members on F5 BIG-IP {{ f5_address }}
229 | bigip_pool_member:
230 | state: present
231 | provider: "{{ f5_provider }}"
232 | partition: "{{ f5_partition }}"
233 | host: "{{ item.host }}"
234 | port: "{{ item.port }}"
235 | pool: "pool_{{ test_site.name }}"
236 | with_items: "{{ f5_pool_members }}"
237 | delegate_to: localhost
238 | ```
239 |
240 | ## Step 6: Create a virtual server on F5 BIG-IP
241 |
242 | Now that the pool and the nodes are members of the pool, Ansible has to create a virtual IP address in order to send the external requests to pool members. The following task creates the virtual server and assigns it the virtual IP defined in the variables files, as well as the port and Client SSL profile created previously.
243 |
244 | ```yaml
245 | ---
246 |
247 | - name: Create Virtual Server on F5 BIG-IP {{ f5_address }}
248 | bigip_virtual_server:
249 | state: present
250 | provider: "{{ f5_provider }}"
251 | name: "vs_{{ test_site.name }}"
252 | partition: "{{ f5_partition }}"
253 | description: "Provisioned by Ansible"
254 | destination: "{{ f5_virtual_ip }}"
255 | port: "{{ f5_virtual_port }}"
256 | snat: Automap
257 | pool: "pool_{{ test_site.name }}"
258 | profiles:
259 | - "clientssl_{{ test_site.name }}"
260 | delegate_to: localhost
261 | ```
262 |
263 | ## Step 7: Execute the playbook
264 |
265 | After you finish the [playbook](f5_create_playbook.yaml), use the following command to run it:
266 |
267 | ```bash
268 | ansible-playbook f5_create_playbook.yaml --ask-vault-pass
269 | ```
270 |
271 | If done correctly, you should see output similar to the following:
272 |
273 | [](https://asciinema.org/a/ff3Ulbvvr6XdP8XTn4gbCFLyy)
274 |
275 | ## Reversing the changes performed
276 |
277 | In this example, we include a [playbook that lets you revert the changes made by running f5_create_playbook.yaml](f5_delete_playbook.yaml). Use the following command to run it:
278 |
279 | ```bash
280 | ansible-playbook f5_delete_playbook.yaml
281 | ```
282 |
--------------------------------------------------------------------------------
/examples/citrix_adc/README.md:
--------------------------------------------------------------------------------
1 | # Configuring secure application delivery using Citrix ADC and the Venafi Ansible Role
2 |
3 | In this example, we'll show you how to better secure application delivery using the Venafi Ansible Role with your [Citrix ADC](https://www.citrix.com/products/citrix-adc/) instance.
4 | Adding Venafi enables you to manage certificates more securely as part of the [TLS Termination](https://en.wikipedia.org/wiki/TLS_termination_proxy) process on your load balancer.
5 |
6 | ## Who should use this example?
7 |
8 | The steps described in this document are typically performed by _DevOps engineers_ or _system administrators_. Generally, you'll need a basic undestanding of Citrix ADC, Venafi Trust Protection Platform or Venafi Cloud, and the required permissions for completing the tastks described in the example.
9 |
10 | ## About this example
11 |
12 | An _application delivery controller_ (ADC) is used to increase the capacity and reliability of applications. ADC improves the performance of applications by decreasing the load on associated servers while managing and maintaining application and network sessions. But ADC configuration can become a long process. However, you can actually automate the process by using a configuration management tool.
13 |
14 | In this example, we use [RedHat Ansible](https://www.ansible.com/) with the _Venafi Ansible Role_ to automate the process of requesting, retrieving and installing a certificate as part of SSL termination on an ADC (specifically, Citrix ADC) for load balancing web traffic. We'll also utilize three HTTP servers contained in a cluster as the endpoints that are sending and receiving web traffic and being managed by Citrix ADC.
15 |
16 | Later in this example, you'll generate a certificate for the `demo-citrix.venafi.example` domain using the Venafi Ansible Role to request and retrieve it from either _Venafi Trust Protection Platform_ or _Venafi Cloud_ services. Then you'll copy the certificate files (certificate, private key, chain Bundle) to the Citrix ADC. Finally, you'll configure Citrix ADC to distribute the traffic between three NGINX servers using the round-robin load balancing method. Here below you can find a diagram of what we are trying to accomplish.
17 |
18 | > **NOTE** In our example, we suggest that you use the round-robin balancing method. But keep in mind that there are [other methods](https://docs.citrix.com/en-us/citrix-adc/current-release/load-balancing/load-balancing-customizing-algorithms.html) that might be more suitable for your specific use case.
19 |
20 | 
21 |
22 | ## Prerequisites
23 |
24 | > **BEST PRACTICES** In general, be careful when using self-signed certificates because of the inherent risks of no identity verification or trust control. The public and private keys are both held by the same entity. Also, self-signed certificates cannot be revoked; they can only be replaced. If an attacker has already gained access to a system, the attacker can spoof the identity of the subject. Of course, CAs can revoke a certificate only when they discover the compromise.
25 |
26 | To perform the tasks described in this example, you'll need:
27 |
28 | - The Venafi Ansible Role installed on your machine; you can install it using `ansible-galaxy` [as described here](https://github.com/Venafi/ansible-role-venafi#using-with-ansible-galaxy)
29 | - Access to either Venafi Trust Protection Platform or Venafi Cloud services (the `credentials.yml` [file](https://github.com/Venafi/ansible-role-venafi#using-with-ansible-galaxy) is used in this example)
30 | - If you are working with Trust Protection Platform, obtain the `access_token` and `refresh_token` using the [VCert CLI](https://github.com/Venafi/vcert/blob/master/README-CLI-PLATFORM.md#obtaining-an-authorization-token).
31 | - Administration access to a Citrix ADC instance
32 | - Nitro Python SDK (available from https://www.citrix.com/downloads/netscaler-adc or from the _Downloads_ tab of the Citrix ADC GUI)
33 | - Citrix ADC modules for Ansible installed from `ansible-galaxy` (for installation instructions, see [this guide](https://github.com/citrix/citrix-adc-ansible-modules#installation))
34 | - A set of three (3) NGINX servers running your application
35 |
36 | ## Getting started
37 |
38 | Here are the steps we'll complete as we go through this example:
39 |
40 | 1. Retrieve a certificate using the Venafi Ansible Role
41 | 2. Copy the retrieved certificate files to Citrix ADC
42 | 3. Create a certificate-key pair on Citrix ADC
43 | 4. Create HTTP back-end services on Citrix ADC
44 | 5. Create a virtual server on Citrix ADC
45 | 6. Execute the playbook
46 |
47 | ## Step 1: Retrieve a certificate using the Venafi Ansible Role
48 |
49 | ### Step 1a: Creating variables file
50 |
51 | The first step is to create the `variables.yaml` file, in this file are defined the variables used during the execution of the playbook such as:
52 |
53 | - The Citrix ADC management IP address.
54 | - The credentials used to manage the Citrix ADC.
55 | - The CN needed to generate the certificate.
56 | - The Virtual IP and port on which all the HTTPS traffic will be handled.
57 | - The http services (the NGINX servers running the application).
58 | - The name for the certificate files which will be copied to the Citrix ADC.
59 |
60 | ```yaml
61 |
62 | adc_address: "192.168.5.188"
63 | adc_username: "youruser"
64 | adc_password: "yourpassword"
65 |
66 | test_site:
67 | name: "demo-citrix"
68 | domain: "venafi.example"
69 |
70 | adc_virtual_ip: "192.168.3.167"
71 | adc_virtual_port: "443"
72 |
73 | http_service: 192.168.6.201
74 | port1: 8001
75 | port2: 8002
76 | port3: 8003
77 |
78 | cert_name: "{{ test_site.name }}.crt"
79 | key_name: "{{ test_site.name }}.key"
80 | chain_name: "{{ test_site.name }}-ca-bundle.crt"
81 | ```
82 |
83 | ### Step 1b: Creating the playbook
84 |
85 | Start by creating a YAML file named `citrix_create_playbook.yaml`, inside, define a name for the playbook, the hosts in which the tasks will be executed, the type of connection to use, the Citrix ADC collection and specify the variables file created in the previous step :
86 |
87 | ```yaml
88 | - name: Create Critx ADC Application
89 | hosts: localhost
90 | connection: local
91 | collections: citrix.adc
92 |
93 | vars_files:
94 | - variables.yaml
95 | ```
96 |
97 |
98 | ### Step 1c: Requesting and retrieving the certificate using Venafi Role
99 |
100 | In the following block of instructions the Venafi Ansible Role is being specified along with the variables it needs to request and retrieve the certificate from the Venafi services, by adding these instructions the Ansible Role will:
101 |
102 | - Request and retrieve a certificate which common and alternate names are `demo-citrix.venafi.example`.
103 | - Create a RSA private key of a size of 2048 bits.
104 | - Generate a chain bundle file where the CA certificate will be place at the end of the file.
105 | - Create a `tmp` directory on the current working directory which will store the retrieved certificate files.
106 | - 3 files will be retrieved and stored using the names on the variables file (*demo-citrix.{crt,key,-ca-bundle.crt}*).
107 | - Simulate the copy of the retrieved files to the remote host by generating a duplicate of them adding the `.remote` extension (the certificate files retrieved are going to be copied to the Citrix ADC using the Citrix ADC Ansible modules that's the reason why the options `certificate_copy_private_key_to_remote` and `certificate_remote_execution` are set to `false`).
108 |
109 |
110 | ```yaml
111 | ---
112 |
113 | roles:
114 | - role: venafi.ansible_role_venafi
115 |
116 | certificate_common_name: "{{ test_site.name }}.{{ test_site.domain }}"
117 | certificate_alt_name: "DNS:{{ test_site.name }}.{{ test_site.domain }}"
118 | certificate_privatekey_type: "RSA"
119 | certificate_privatekey_size: "2048"
120 | certificate_chain_option: "last"
121 |
122 | certificate_cert_dir: "./tmp"
123 | certificate_cert_path: "./tmp/{{ cert_name }}"
124 | certificate_chain_path: "./tmp/{{ chain_name }}"
125 | certificate_privatekey_path: "./tmp/{{ key_name }}"
126 | certificate_copy_private_key_to_remote: false
127 |
128 | certificate_remote_execution: false
129 | certificate_remote_privatekey_path: "./tmp/{{ key_name }}.remote"
130 | certificate_remote_cert_path: "./tmp/{{ cert_name }}.remote"
131 | certificate_remote_chain_path: "./tmp/{{ chain_name }}.remote"
132 | ```
133 |
134 | ## Step 2: Copy the retrieved certificate files to Citrix ADC
135 |
136 | By adding the following instructions to the playbook, we specify the actions the playbook will execute. Ansible will connect to the Citrix ADC (using the credentials specified in the variables file) and then it will create the key, CA bundle and certificate using the local files retrieved in the previous step.
137 |
138 | ```yaml
139 | ---
140 |
141 | tasks:
142 | - name: Copy Private Key to Citrix ADC {{ adc_address }}
143 | citrix_adc_system_file:
144 | nsip: "{{ adc_address }}"
145 | nitro_user: "{{ adc_username }}"
146 | nitro_pass: "{{ adc_password }}"
147 | nitro_protocol: http
148 | validate_certs: false
149 | state: present
150 | filename: "{{ key_name }}"
151 | filelocation: "/nsconfig/ssl/"
152 | filecontent: "{{ lookup('file', './tmp/' + key_name) }}"
153 | delegate_to: localhost
154 |
155 | - name: Copy Certificate to Citrix ADC {{ adc_address }}
156 | citrix_adc_system_file:
157 | nsip: "{{ adc_address }}"
158 | nitro_user: "{{ adc_username }}"
159 | nitro_pass: "{{ adc_password }}"
160 | nitro_protocol: http
161 | validate_certs: false
162 | state: present
163 | filename: "{{ cert_name }}"
164 | filelocation: "/nsconfig/ssl/"
165 | filecontent: "{{ lookup('file', './tmp/' + cert_name + '.remote') }}"
166 | delegate_to: localhost
167 |
168 | - name: Copy CA Bundle to Citrix ADC {{ adc_address }}
169 | citrix_adc_system_file:
170 | nsip: "{{ adc_address }}"
171 | nitro_user: "{{ adc_username }}"
172 | nitro_pass: "{{ adc_password }}"
173 | nitro_protocol: http
174 | validate_certs: false
175 | state: present
176 | filename: "{{ chain_name }}"
177 | filelocation: "/nsconfig/ssl"
178 | filecontent: "{{ lookup('file', './tmp/' + chain_name + '.remote') }}"
179 | delegate_to: localhost
180 |
181 | ```
182 |
183 | ## Step 3: Create a certificate-key pair on Citrix ADC
184 |
185 | To be able to handle the HTTPS requests, Ansible needs to create a cert-key pair on the Citrix ADC using the files you copied in the previous step.
186 | ```yaml
187 | ---
188 |
189 | - name: Create Certkey on Citrix ADC {{ adc_address }}
190 | citrix_adc_ssl_certkey:
191 | nsip: "{{ adc_address }}"
192 | nitro_user: "{{ adc_username }}"
193 | nitro_pass: "{{ adc_password }}"
194 | nitro_protocol: http
195 | validate_certs: false
196 | state: present
197 | certkey: "{{ test_site.name }}.{{ test_site.domain }}_certkey"
198 | cert: "/nsconfig/ssl/{{ cert_name }}"
199 | key: "/nsconfig/ssl/{{ key_name }}"
200 | ```
201 |
202 | ## Step 4: Create HTTP back-end services on Citrix ADC
203 |
204 | After you create the cert-key pair on the Citrix ADC, Ansible needs to create the HTTP services on the Citric ADC instance. These services are the ones that will actually serve the requests (NGINX servers hosting the application). Ansible will use the host and port variables defined in the variables file for each service.
205 |
206 | ```yaml
207 | ---
208 |
209 | - name: Create service-http-1 on Citrix ADC {{ adc_address }}
210 | citrix_adc_service:
211 | nsip: "{{ adc_address }}"
212 | nitro_user: "{{ adc_username }}"
213 | nitro_pass: "{{ adc_password }}"
214 | nitro_protocol: http
215 | validate_certs: false
216 | state: present
217 | name: service-http-1
218 | servicetype: HTTP
219 | ip: "{{ http_service }}"
220 | ipaddress: "{{ http_service }}"
221 | port: "{{ port1 }}"
222 | delegate_to: localhost
223 |
224 | - name: Create service-http-2 on Citrix ADC {{ adc_address }}
225 | citrix_adc_service:
226 | nsip: "{{ adc_address }}"
227 | nitro_user: "{{ adc_username }}"
228 | nitro_pass: "{{ adc_password }}"
229 | nitro_protocol: http
230 | validate_certs: false
231 | state: present
232 | name: service-http-2
233 | servicetype: HTTP
234 | ip: "{{ http_service }}"
235 | ipaddress: "{{ http_service }}"
236 | port: "{{ port2 }}"
237 | delegate_to: localhost
238 |
239 | - name: Create service-http-3 on Citrix ADC {{ adc_address }}
240 | citrix_adc_service:
241 | nsip: "{{ adc_address }}"
242 | nitro_user: "{{ adc_username }}"
243 | nitro_pass: "{{ adc_password }}"
244 | nitro_protocol: http
245 | validate_certs: false
246 | state: present
247 | name: service-http-3
248 | servicetype: HTTP
249 | ip: "{{ http_service }}"
250 | ipaddress: "{{ http_service }}"
251 | port: "{{ port3 }}"
252 | delegate_to: localhost
253 | ```
254 |
255 | ## Step 5: Create a virtual server on Citrix ADC
256 |
257 | Now that HTTP services have been created, Ansible must create a virtual IP address in order to send the external requests to the HTTP back-end services. The following task creates the virtual server and assigns it the virtual IP defined in the variables file, the port, the certificate-key pair and the HTTP services previously created, as well as the round-robin load balancing [method](https://docs.citrix.com/en-us/citrix-adc/current-release/load-balancing/load-balancing-customizing-algorithms.html) which will allow the virtual server to distribute the load between the NGINX servers hosting the application.
258 |
259 | ```yaml
260 | ---
261 |
262 | - name: Create lb vserver on Citrix ADC {{ adc_address }}
263 | citrix_adc_lb_vserver:
264 | nsip: "{{ adc_address }}"
265 | nitro_user: "{{ adc_username }}"
266 | nitro_pass: "{{ adc_password }}"
267 | nitro_protocol: http
268 | validate_certs: false
269 | state: present
270 | name: "vs-{{ test_site.name }}.{{ test_site.domain }}"
271 | servicetype: SSL
272 | timeout: 2
273 | ipv46: "{{ adc_virtual_ip }}"
274 | port: "{{ adc_virtual_port }}"
275 | lbmethod: ROUNDROBIN
276 | ssl_certkey: "{{ test_site.name }}.{{ test_site.domain }}_certkey"
277 | servicebindings:
278 | - servicename: service-http-1
279 | weight: 80
280 | - servicename: service-http-2
281 | weight: 60
282 | - servicename: service-http-3
283 | weight: 40
284 | disabled: no
285 | delegate_to: localhost
286 |
287 | ```
288 |
289 | ## Step 6: Executing the playbook
290 |
291 | After you finish the [playbook](citrix_create_playbook.yaml), use the following command to run it:
292 |
293 | ```bash
294 | ansible-playbook citrix_create_playbook.yaml --ask-vault-pass
295 | ```
296 |
297 | If done correctly, you should see output similar to the following:
298 |
299 | [](https://asciinema.org/a/d9BlbbXWGNFcSzQUkIAeJ0Qcr)
300 |
301 | ## Reversing the changes performed
302 |
303 | In this example, we include a [playbook that lets you revert the changes made by running citrix_create_playbook.yaml](citrix_delete_playbook.yaml). Use the following command to run it:
304 |
305 | ```bash
306 | ansible-playbook citrix_delete_playbook.yaml
307 | ```
308 |
--------------------------------------------------------------------------------
/library/venafi_certificate.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | #
3 | # Copyright 2019 Venafi, Inc.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | #
17 |
18 |
19 | from __future__ import absolute_import, print_function, unicode_literals
20 | import time
21 | import datetime
22 | import os.path
23 | import random
24 | from ansible.module_utils.basic import AnsibleModule
25 | from ansible.module_utils._text import to_bytes, to_text
26 |
27 | HAS_VCERT = HAS_CRYPTOGRAPHY = True
28 | try:
29 | from vcert import CertificateRequest, Connection, KeyType,\
30 | venafi_connection
31 | except ImportError:
32 | HAS_VCERT = False
33 | try:
34 | from cryptography import x509
35 | from cryptography.hazmat.backends import default_backend
36 | from cryptography.x509.oid import NameOID, ExtensionOID
37 | from cryptography.hazmat.primitives import serialization, hashes
38 | except ImportError:
39 | HAS_CRYPTOGRAPHY = False
40 |
41 | ANSIBLE_METADATA = {
42 | 'metadata_version': '1.1',
43 | 'status': ['preview'],
44 | 'supported_by': 'community'
45 | }
46 |
47 | DOCUMENTATION = '''
48 | ---
49 | module: venafi_certificate_module
50 |
51 | short_description: This is Venafi certificate module for working with
52 | Venafi Cloud or Venafi Trusted Platform
53 |
54 | version_added: "2.7"
55 |
56 | description:
57 | - This is Venafi certificate module for working with Venafi Cloud or
58 | Venafi Trust Platform"
59 |
60 | options:
61 | force:
62 | default: False
63 | type: bool
64 | description:
65 | - Generate the certificate, even if it already exists.
66 |
67 | state:
68 | default: "present"
69 | choices: [ present, absent ]
70 | description:
71 | - > Whether the certificate should exist or not,
72 | taking action if the state is different from what is stated.
73 |
74 | renew:
75 | default: True
76 | type: bool
77 | description:
78 | - Try to renew certificate if is existing but not valid.
79 |
80 | cert_path:
81 | required: true
82 | description:
83 | - Remote absolute path where the generated certificate file should
84 | be created or is already located.
85 |
86 | chain_path:
87 | required: false
88 | description:
89 | - > Remote absolute path where the generated certificate chain file
90 | should
91 | be created or is already located. If set certificate and chain will
92 | be in separated files.
93 |
94 | chain_option:
95 | required: false
96 | default: "last"
97 | description:
98 | - > Specify ordering certificates in chain. Root can be "first" or
99 | "last"
100 |
101 | common_name:
102 | required: false
103 | aliases: [ 'CN', 'commonName' ]
104 | description:
105 | - commonName field of the certificate signing request subject
106 |
107 | alt_name:
108 | required: false
109 | aliases: [ 'alt_name' ]
110 | description:
111 | - SAN extension to attach to the certificate signing request
112 | - This can either be a 'comma separated string' or a YAML list.
113 | - Values should be prefixed by their options. (IP:,email:,DNS:)
114 |
115 | privatekey_path:
116 | required: false
117 | description:
118 | - > Path to the private key to use when signing the certificate
119 | signing request. If not set will be placed
120 | near certificate with key suffix.
121 |
122 | privatekey_type:
123 | default: "RSA"
124 | required: false
125 | description:
126 | - Type of private key. RSA or ECDSA
127 |
128 | privatekey_size:
129 | required: false
130 | default: 2048
131 | description:
132 | - Size (in bits) of the TLS/SSL key to generate. Used only for RSA.
133 |
134 | privatekey_curve:
135 | required: false
136 | default: "P521"
137 | description:
138 | - | Curves name for ECDSA algorithm. Choices are "P224", "P256",
139 | "P384", "P521".
140 |
141 | privatekey_passphrase:
142 | required: false
143 | description:
144 | - The passphrase for the privatekey.
145 |
146 | privatekey_reuse:
147 | required: false
148 | type: bool
149 | description:
150 | - If set to false new key won't be generated
151 |
152 | before_expired_hours:
153 | required: false
154 | type: int
155 | default: 72
156 | description:
157 | - | If certificate will expire in less hours than this value
158 | module will try to renew it.
159 | extends_documentation_fragment:
160 | - files
161 |
162 | author:
163 | - Alexander Rykalin (@arykalin)
164 | '''
165 |
166 | EXAMPLES = '''
167 | # Enroll fake certificate for testing purposes
168 | - name: venafi_certificate_fake
169 | connection: local
170 | hosts: localhost
171 | tags:
172 | - fake
173 | tasks:
174 | - name: venafi_certificate
175 | venafi_certificate:
176 | test_mode: true
177 | common_name: 'testcert-fake-{{ 99999999 | random }}.example.com'
178 | alt_name: 'DNS:www.venafi.example,DNS:m.venafi.example'
179 | cert_path: '/tmp'
180 | register: certout
181 | - name: dump test output
182 | debug:
183 | msg: '{{ certout }}'
184 |
185 | # Enroll Platform certificate with a lot of alt names
186 | - name: venafi_certificate_tpp
187 | connection: local
188 | hosts: localhost
189 | tags:
190 | - tpp
191 | tasks:
192 | - name: venafi_certificate
193 | venafi_certificate:
194 | url: 'https://venafi.example.com/vedsdk'
195 | user: 'admin'
196 | password: !vault |
197 | $ANSIBLE_VAULT;1.1;AES256
198 | zone: 'example\\\\policy'
199 | cert_path: '/tmp'
200 | common_name: 'testcert-tpp-{{ 99999999 | random }}.example.com'
201 | alt_name: |
202 | IP:192.168.1.1,DNS:www.venafi.example.com,
203 | DNS:m.venafi.example.com,email:test@venafi.com,IP Address:192.168.2.2
204 | register: certout
205 | - name: dump test output
206 | debug:
207 | msg: '{{ certout }}'
208 |
209 | # Enroll Cloud certificate
210 | - name: venafi_certificate_cloud
211 | connection: local
212 | hosts: localhost
213 | tags:
214 | - cloud
215 | tasks:
216 | - name: venafi_certificate
217 | venafi_certificate:
218 | token: !vault |
219 | $ANSIBLE_VAULT;1.1;AES256
220 | zone: 'Default'
221 | cert_path: '/tmp'
222 | common_name: 'testcert-cloud.example.com'
223 | register: certout
224 | - name: dump test output
225 | debug:
226 | msg: '{{ certout }}'
227 | '''
228 |
229 | RETURN = '''
230 | privatekey_filename:
231 | description: Path to the TLS/SSL private key the CSR was generated for
232 | returned: changed or success
233 | type: string
234 | sample: /etc/ssl/private/venafi.example.pem
235 |
236 | privatekey_size:
237 | description: Size (in bits) of the TLS/SSL private key
238 | returned: changed or success
239 | type: int
240 | sample: 4096
241 |
242 | privatekey_curve:
243 | description: > ECDSA curve of generated private key. Variants are "P521",
244 | "P384", "P256", "P224".
245 |
246 | returned: changed or success
247 | type: string
248 | sample: "P521"
249 |
250 | privatekey_type:
251 | description: > Algorithm used to generate the TLS/SSL private key.
252 | Variants are RSA or ECDSA
253 |
254 | returned: changed or success
255 | type: string
256 | sample: RSA
257 |
258 | certificate_filename:
259 | description: Path to the signed certificate
260 | returned: changed or success
261 | type: string
262 | sample: /etc/ssl/www.venafi.example.pem
263 |
264 | chain_filename:
265 | description: > Path to the chain of CA certificates that link
266 | the certificate to a trust anchor
267 |
268 | returned: changed or success
269 | type: string
270 | sample: /etc/ssl/www.venafi.example_chain.pem
271 | '''
272 | # Some strings variables
273 | STRING_FAILED_TO_CHECK_CERT_VALIDITY = "Certificate is not yet valid, " \
274 | "has expired, or has CN or SANs " \
275 | "that differ from the request"
276 | STRING_PKEY_NOT_MATCHED = "Private key does not match certificate public key"
277 | STRING_BAD_PKEY = "Private key file does not contain a valid private key"
278 | STRING_CERT_FILE_NOT_EXISTS = "Certificate file does not exist"
279 | STRING_BAD_PERMISSIONS = "Insufficient file permissions"
280 |
281 |
282 | class VCertificate:
283 | def __init__(self, module):
284 | """
285 | :param AnsibleModule module:
286 | """
287 | self.common_name = module.params['common_name']
288 |
289 | self.test_mode = module.params['test_mode']
290 | self.url = module.params['url']
291 | self.password = module.params['password']
292 | self.access_token = module.params['access_token']
293 | self.token = module.params['token']
294 | self.user = module.params['user']
295 | self.zone = module.params['zone']
296 | self.privatekey_filename = module.params['privatekey_path']
297 | self.certificate_filename = module.params['cert_path']
298 | self.privatekey_type = module.params['privatekey_type']
299 |
300 | if self.user != "":
301 | module.warn("User is deprecated use access token instead")
302 | if self.password != "":
303 | module.warn("Password is deprecated use access token instead")
304 |
305 | if module.params['privatekey_curve']:
306 | if not module.params['privatekey_type']:
307 | module.fail_json(
308 | msg="privatekey_type should be "
309 | "set if privatekey_curve configured")
310 | self.privatekey_curve = module.params['privatekey_curve']
311 | if module.params['privatekey_size']:
312 | if not module.params['privatekey_type']:
313 | module.fail_json(
314 | msg="privatekey_type should be set if "
315 | "privatekey_size configured")
316 | self.privatekey_size = module.params['privatekey_size']
317 | self.privatekey_passphrase = module.params['privatekey_passphrase']
318 | self.privatekey_reuse = module.params['privatekey_reuse']
319 | self.chain_filename = module.params['chain_path']
320 | self.csr_path = module.params['csr_path']
321 | self.args = ""
322 | self.changed = False
323 | self.module = module
324 | self.ip_addresses = []
325 | self.email_addresses = []
326 | self.san_dns = []
327 | self.changed_message = []
328 | if module.params['alt_name']:
329 | for n in module.params['alt_name']:
330 | if n.startswith(("IP:", "IP Address:")):
331 | ip = n.split(":", 1)[1]
332 | self.ip_addresses.append(ip)
333 | elif n.startswith("DNS:"):
334 | ns = n.split(":", 1)[1]
335 | self.san_dns.append(ns)
336 | elif n.startswith("email:"):
337 | mail = n.split(":", 1)[1]
338 | self.email_addresses.append(mail)
339 | else:
340 | self.module.fail_json(
341 | msg="Failed to determine extension type: %s" % n)
342 | trust_bundle = module.params['trust_bundle']
343 | if trust_bundle:
344 | if self.access_token and self.access_token != "":
345 | self.conn = venafi_connection(
346 | url=self.url, user=None, password=None,
347 | access_token=self.access_token,
348 | refresh_token=None,
349 | http_request_kwargs={"verify": trust_bundle},
350 | api_key=None, fake=self.test_mode)
351 | else:
352 | self.conn = Connection(
353 | url=self.url, token=self.token, password=self.password,
354 | user=self.user, fake=self.test_mode,
355 | http_request_kwargs={"verify": trust_bundle})
356 | else:
357 | if self.access_token and self.access_token != "":
358 | self.conn = venafi_connection(
359 | url=self.url, access_token=self.access_token,
360 | user=None, password=None, api_key=None, fake=self.test_mode)
361 | else:
362 | self.conn = Connection(
363 | url=self.url, token=self.token, fake=self.test_mode,
364 | user=self.user, password=self.password)
365 | self.before_expired_hours = module.params['before_expired_hours']
366 |
367 | def check_dirs_existed(self):
368 | cert_dir = os.path.dirname(self.certificate_filename or "/a")
369 | key_dir = os.path.dirname(self.privatekey_filename or "/a")
370 | chain_dir = os.path.dirname(self.chain_filename or "/a")
371 | ok = True
372 | for p in set((cert_dir, key_dir, chain_dir)):
373 | if os.path.isdir(p):
374 | continue
375 | elif os.path.exists(p):
376 | self.module.fail_json(
377 | msg="Path %s already exists but this is not directory" % p)
378 | elif not os.path.exists(p):
379 | self.module.fail_json(msg="Directory %s does not exists" % p)
380 | ok = False
381 | return ok
382 |
383 | def _check_private_key_correct(self):
384 | if not self.privatekey_filename:
385 | return None
386 | if not os.path.exists(self.privatekey_filename):
387 | return False
388 | private_key = to_text(open(self.privatekey_filename, "rb").read())
389 |
390 | r = CertificateRequest(private_key=private_key,
391 | key_password=self.privatekey_passphrase)
392 | key_type = {"RSA": "rsa", "ECDSA": "ec", "EC": "ec"}. \
393 | get(self.privatekey_type)
394 | if key_type and key_type != r.key_type.key_type:
395 | return False
396 | if key_type == "rsa" and self.privatekey_size:
397 | if self.privatekey_size != r.key_type.option:
398 | return False
399 | if key_type == "ec" and self.privatekey_curve:
400 | if self.privatekey_curve != r.key_type.option:
401 | return False
402 | return True
403 |
404 | def enroll(self):
405 | request = CertificateRequest(
406 | common_name=self.common_name,
407 | key_password=self.privatekey_passphrase,
408 | origin="Red Hat Ansible"
409 | )
410 | zone_config = self.conn.read_zone_conf(self.zone)
411 | request.update_from_zone_config(zone_config)
412 |
413 | use_existed_key = False
414 | if self._check_private_key_correct() and not self.privatekey_reuse:
415 | private_key = to_text(open(self.privatekey_filename, "rb").read())
416 | request.private_key = private_key
417 | use_existed_key = True
418 | elif self.privatekey_type:
419 | key_type = {"RSA": "rsa", "ECDSA": "ec", "EC": "ec"}. \
420 | get(self.privatekey_type)
421 | if not key_type:
422 | self.module.fail_json(msg=(
423 | "Failed to determine key type: %s."
424 | "Must be RSA or ECDSA" % self.privatekey_type))
425 | if key_type == "rsa":
426 | request.key_type = KeyType(KeyType.RSA,
427 | self.privatekey_size)
428 | elif key_type == "ecdsa" or "ec":
429 | request.key_type = KeyType(KeyType.ECDSA,
430 | self.privatekey_curve)
431 | else:
432 | self.module.fail_json(msg=(
433 | "Failed to determine key type: %s."
434 | "Must be RSA or ECDSA" % self.privatekey_type))
435 |
436 | request.ip_addresses = self.ip_addresses
437 | request.san_dns = self.san_dns
438 | request.email_addresses = self.email_addresses
439 |
440 | request.chain_option = self.module.params['chain_option']
441 | try:
442 | csr = open(self.csr_path, "rb").read()
443 | request.csr = csr
444 | except Exception as e:
445 | self.module.log(msg=str(e))
446 | pass
447 |
448 | self.conn.request_cert(request, self.zone)
449 | print(request.csr)
450 | while True:
451 | cert = self.conn.retrieve_cert(request) # vcert.Certificate
452 | if cert:
453 | break
454 | else:
455 | time.sleep(5)
456 | if self.chain_filename:
457 | self._atomic_write(self.chain_filename, "\n".join(cert.chain))
458 | self._atomic_write(self.certificate_filename, cert.cert)
459 | else:
460 | self._atomic_write(self.certificate_filename, cert.full_chain)
461 | if not use_existed_key:
462 | self._atomic_write(self.privatekey_filename,
463 | request.private_key_pem)
464 | # todo: server generated private key
465 |
466 | def _atomic_write(self, path, content):
467 | suffix = ".atomic_%s" % random.randint(100, 100000)
468 | try:
469 | with open(path + suffix, "wb") as f:
470 | f.write(to_bytes(content))
471 | except OSError as e:
472 | self.module.fail_json(msg="Failed to write file %s: %s" % (
473 | path + suffix, e))
474 |
475 | self.module.atomic_move(path + suffix, path)
476 | self.changed = True
477 | self._check_and_update_permissions(path)
478 |
479 | def _check_and_update_permissions(self, path):
480 | file_args = self.module.load_file_common_arguments(self.module.params)
481 | file_args['path'] = path
482 | if self.module.set_fs_attributes_if_different(file_args, False):
483 | self.changed = True
484 |
485 | def _check_dns_sans_correct(self, actual, required, optional):
486 | if len(optional) == 0 and len(actual) != len(required):
487 | return False
488 | for i in required:
489 | found = False
490 | for j in actual:
491 | if i == j:
492 | found = True
493 | break
494 | if not found:
495 | return False
496 | combined = required + optional
497 | for i in actual:
498 | found = False
499 | for j in combined:
500 | if i == j:
501 | found = True
502 | break
503 | if not found:
504 | return False
505 | return True
506 |
507 | def _check_certificate_validity(self, cert, validate):
508 | cn = cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value
509 | if cn != self.common_name:
510 | self.changed_message.append(
511 | 'Certificate CN %s not matched to expected %s'
512 | % (cn, self.common_name)
513 | )
514 | return False
515 | # Check if certificate not already expired
516 | if cert.not_valid_after < datetime.datetime.now():
517 | self.changed_message.append(
518 | 'Certificate expiration date %s '
519 | 'is less than current time %s (certificate expired)'
520 | % (cert.not_valid_after, self.before_expired_hours)
521 | )
522 | return False
523 | # Check if certificate expiring time is greater than
524 | # before_expired_hours (only for creating new certificate)
525 | if not validate:
526 | if cert.not_valid_after - datetime.timedelta(
527 | hours=self.before_expired_hours) < datetime.datetime.now():
528 | self.changed_message.append(
529 | 'Hours before certificate expiration date %s '
530 | 'is less than before_expired_hours value %s'
531 | % (cert.not_valid_after, self.before_expired_hours)
532 | )
533 | return False
534 | if cert.not_valid_before - datetime.timedelta(
535 | hours=24) > datetime.datetime.now():
536 | self.changed_message.append(
537 | "Certificate expiration date %s "
538 | "is set to future from server time %s."
539 | % (cert.not_valid_before -
540 | datetime.timedelta(hours=24),
541 | (datetime.datetime.now()))
542 | )
543 | return False
544 | ips = []
545 | dns = []
546 | alternative_names = cert.extensions.get_extension_for_oid(
547 | ExtensionOID.SUBJECT_ALTERNATIVE_NAME).value
548 | for e in alternative_names:
549 | if isinstance(e, x509.general_name.DNSName):
550 | dns.append(e.value)
551 | elif isinstance(e, x509.general_name.IPAddress):
552 | ips.append(e.value.exploded)
553 | if self.ip_addresses and sorted(self.ip_addresses) != sorted(ips):
554 | self.changed_message.append("IP address in request: %s and in"
555 | "certificate: %s are different"
556 | % (sorted(self.ip_addresses), ips))
557 | self.changed_message.append("CN is %s" % cn)
558 | return False
559 | if self.san_dns and not self._check_dns_sans_correct(
560 | dns, self.san_dns, [self.common_name]):
561 | self.changed_message.append("DNS addresses in request: %s and in "
562 | "certificate: %s are different"
563 | % (sorted(self.san_dns), sorted(dns)))
564 | return False
565 | return True
566 |
567 | def _check_public_key_matched_to_private_key(self, cert):
568 | if not self.privatekey_filename:
569 | return True
570 | if not os.path.exists(self.privatekey_filename):
571 | return False
572 | try:
573 | with open(self.privatekey_filename, 'rb') as key_data:
574 | password = self.privatekey_passphrase.encode() if \
575 | self.privatekey_passphrase else None
576 | pkey = serialization.load_pem_private_key(
577 | key_data.read(), password=password,
578 | backend=default_backend())
579 |
580 | except OSError as exc:
581 | self.module.fail_json(
582 | msg="Failed to read private key file: %s" % exc)
583 |
584 | cert_public_key_pem = cert.public_key().public_bytes(
585 | encoding=serialization.Encoding.PEM,
586 | format=serialization.PublicFormat.SubjectPublicKeyInfo
587 | ).decode()
588 |
589 | private_key_public_key_pem = pkey.public_key().public_bytes(
590 | encoding=serialization.Encoding.PEM,
591 | format=serialization.PublicFormat.SubjectPublicKeyInfo
592 | ).decode()
593 |
594 | if cert_public_key_pem != private_key_public_key_pem:
595 | return False
596 | return True
597 |
598 | def _check_files_permissions(self):
599 | files = (self.privatekey_filename, self.certificate_filename,
600 | self.chain_filename)
601 | return all([self._check_file_permissions(x) for x in files])
602 |
603 | def _check_file_permissions(self, path, update=False):
604 | return True # todo: write
605 |
606 | def check(self, validate):
607 | """Return true if running will change anything"""
608 | result = {
609 | 'cert_file_exists': True,
610 | 'changed': False,
611 | }
612 | if not os.path.exists(self.certificate_filename):
613 | result = {
614 | 'cert_file_exists': False,
615 | 'changed': True,
616 | 'changed_msg':
617 | self.changed_message.append(STRING_CERT_FILE_NOT_EXISTS),
618 | }
619 | else:
620 | try:
621 | with open(self.certificate_filename, 'rb') as cert_data:
622 | try:
623 | cert = x509.load_pem_x509_certificate(
624 | cert_data.read(), default_backend())
625 | except Exception:
626 | self.module.fail_json(
627 | msg="Failed to load certificate from file: %s"
628 | % self.certificate_filename)
629 | except OSError as exc:
630 | self.module.fail_json(
631 | msg="Failed to read certificate file: %s" % exc)
632 |
633 | if not self._check_public_key_matched_to_private_key(cert):
634 | result['changed'] = True
635 | self.changed_message.append(STRING_PKEY_NOT_MATCHED)
636 |
637 | if not self._check_certificate_validity(cert, validate):
638 | result['changed'] = True
639 | self.changed_message.append(
640 | STRING_FAILED_TO_CHECK_CERT_VALIDITY)
641 |
642 | if self._check_private_key_correct() is False: # may be None
643 | result['changed'] = True
644 | self.changed_message.append(STRING_BAD_PKEY)
645 |
646 | if not self._check_files_permissions():
647 | result['changed'] = True
648 | self.changed_message.append(STRING_BAD_PERMISSIONS)
649 |
650 | result['changed_msg'] = ' | '.join(self.changed_message)
651 | return result
652 |
653 | def validate(self):
654 | """Ensure the resource is in its desired state."""
655 | result = self.check(validate=True)
656 | if result['changed']:
657 | self.module.fail_json(
658 | msg=result['changed_msg']
659 | )
660 |
661 | def dump(self):
662 |
663 | result = {
664 | 'changed': self.changed,
665 | 'privatekey_filename': self.privatekey_filename,
666 | 'privatekey_size': self.privatekey_size,
667 | 'privatekey_curve': self.privatekey_curve,
668 | 'privatekey_type': self.privatekey_type,
669 | 'certificate_filename': self.certificate_filename,
670 | 'chain_filename': self.chain_filename,
671 | }
672 |
673 | return result
674 |
675 |
676 | def main():
677 | # the AnsibleModule object will be our abstraction working with Ansible
678 | # this includes instantiation, a couple of common attr would be the
679 | # args/params passed to the execution, as well as if the module
680 | # supports check mode
681 | module = AnsibleModule(
682 | # define the available arguments/parameters that a user can pass to
683 | # the module
684 | argument_spec=dict(
685 | state=dict(type='str', choices=['present', 'absent'],
686 | default='present'),
687 | force=dict(type='bool', default=False, ),
688 |
689 | # Endpoint
690 | test_mode=dict(type='bool', required=False, default=False),
691 | url=dict(type='str', required=False, default=''),
692 | password=dict(type='str', required=False, default='', no_log=True),
693 | token=dict(type='str', required=False, default='', no_log=True),
694 | access_token=dict(type='str', required=False,
695 | default='', no_log=True),
696 | user=dict(type='str', required=False, default='', no_log=True),
697 | zone=dict(type='str', required=False, default=''),
698 | log_verbose=dict(type='str', required=False, default=''),
699 | config_file=dict(type='str', required=False, default=''),
700 | config_section=dict(type='str', required=False, default=''),
701 | trust_bundle=dict(type='str', required=False),
702 |
703 | # General properties of a certificate
704 | path=dict(type='path', aliases=['cert_path'], require=True),
705 | chain_path=dict(type='path', require=False),
706 | privatekey_path=dict(type='path', required=False),
707 | privatekey_type=dict(type='str', required=False),
708 | privatekey_size=dict(type='int', required=False),
709 | privatekey_curve=dict(type='str', required=False),
710 | privatekey_passphrase=dict(type='str', no_log=True),
711 | privatekey_reuse=dict(type='bool', required=False, default=True),
712 | alt_name=dict(type='list', aliases=['subjectAltName'],
713 | elements='str'),
714 | common_name=dict(aliases=['CN', 'commonName', 'common_name'],
715 | type='str', required=True),
716 | chain_option=dict(type='str', required=False, default='last'),
717 | csr_path=dict(type='path', require=False),
718 |
719 | # Role config
720 | before_expired_hours=dict(type='int', required=False, default=72),
721 | renew=dict(type='bool', required=False, default=True)
722 | ),
723 | supports_check_mode=True,
724 | add_file_common_args=True,
725 | )
726 | if not HAS_VCERT:
727 | module.fail_json(msg='"vcert" python library is required')
728 | if not HAS_CRYPTOGRAPHY:
729 | module.fail_json(msg='"cryptography" python library is required')
730 | vcert = VCertificate(module)
731 | change_dump = vcert.check(validate=False)
732 | if module.check_mode:
733 | module.exit_json(**change_dump)
734 |
735 | if not vcert.check_dirs_existed():
736 | module.fail_json(msg="Dirs not existed")
737 | if change_dump['changed']:
738 | # TODO: Cover it by tests
739 | """
740 | make a following choice:
741 | 1. If certificate is present and renew is true validate it
742 | 2. If certificate not present renew it
743 | 3. If it present and renew is false just keep it.
744 | """
745 | if change_dump['cert_file_exists']:
746 | if module.params['renew']:
747 | vcert.enroll()
748 | else:
749 | module.exit_json(**change_dump)
750 | else:
751 | vcert.enroll()
752 | elif module.params['force']:
753 | vcert.enroll()
754 | vcert.validate()
755 | result = vcert.dump()
756 | module.exit_json(**result)
757 |
758 |
759 | if __name__ == '__main__':
760 | main()
761 |
--------------------------------------------------------------------------------