diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 12:04:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 12:04:41 +0000 |
commit | 975f66f2eebe9dadba04f275774d4ab83f74cf25 (patch) | |
tree | 89bd26a93aaae6a25749145b7e4bca4a1e75b2be /ansible_collections/cyberark | |
parent | Initial commit. (diff) | |
download | ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.tar.xz ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.zip |
Adding upstream version 7.7.0+dfsg.upstream/7.7.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
192 files changed, 11359 insertions, 0 deletions
diff --git a/ansible_collections/cyberark/conjur/.codeclimate.yml b/ansible_collections/cyberark/conjur/.codeclimate.yml new file mode 100644 index 000000000..40b46a5fd --- /dev/null +++ b/ansible_collections/cyberark/conjur/.codeclimate.yml @@ -0,0 +1,162 @@ +# This is our default .CodeClimate.yml, broken out by language. Uncomment the +# sections at the bottom that apply to your project. ACTION comments indicate +# places where config might need to be tweaked. + +version: "2" + +plugins: + +# --------------- +# Cross-language plugins. Should always be on. + + duplication: # Looks for similar and identical code blocks + enabled: true + config: + languages: + go: + java: + javascript: + php: + python: + python_version: 3 # ACTION Comment this out if using Python 2 + ruby: + swift: + typescript: + + fixme: # Flags any FIXME, TODO, BUG, XXX, HACK comments so they can be fixed + enabled: true + config: + strings: + - FIXME + - TODO + - HACK + - XXX + - BUG + +# --------------- +# Commonly-used languages - run time is minimal and all of these will work +# whether files of that language are found or not. In general, leave uncommented + + # Markdown + markdownlint: + enabled: true + + # Go + gofmt: + enabled: true + golint: + enabled: true + govet: + enabled: true + + # Ruby + flog: + enabled: true + reek: + enabled: true + rubocop: + enabled: true + channel: rubocop-0-79 # As of March 10, 2020, rubocop 0.80.1 is the latest + # However, it does not work with CodeClimate - throws + # an Invalid JSON error. + # ACTION uncomment bundler-audit below if using Gemfile/Gemfile.lock + # ACTION uncomment brakeman below if using Rails + + # Shell scripts + shellcheck: + enabled: true + +# --------------- +# Other languages - will work with or without language files present. Again, +# runtime is minimal, so OK to leave uncommented. + + # CoffeeScript + coffeelint: + enabled: true + + # CSS + csslint: + enabled: true + + # Groovy + codenarc: + enabled: true + + # Java + pmd: + enabled: true + sonar-java: + enabled: true + config: + sonar.java.source: "7" # ACTION set this to the major version of Java used + # ACTION uncomment checkstyle below if Java code exists in repo + + # Node.js + nodesecurity: + enabled: true + # ACTION uncomment eslint below if JavaScript already exists and .eslintrc + # file exists in repo + + # PHP + phan: + enabled: true + config: + file_extensions: "php" + phpcodesniffer: + enabled: true + config: + file_extensions: "php,inc,lib" + # Using Wordpress standards as our one PHP repo is a Wordpress theme + standards: "PSR1,PSR2,WordPress,WordPress-Core,WordPress-Extra" + phpmd: + enabled: true + config: + file_extensions: "php,inc,lib" + rulesets: "cleancode,codesize,controversial,naming,unusedcode" + sonar-php: + enabled: true + + # Python + bandit: + enabled: true + pep8: + enabled: true + radon: + enabled: true + # config: + # python_version: 2 # ACTION Uncomment these 2 lines if using Python 2 + sonar-python: + enabled: true + +# --------------- +# Configuration Required Language specific - these will error and abort the +# codeclimate run if they are turned on and certain files or configuration are +# missing. Should be commented out unless the project already includes the +# necessary files that the linter looks at + + # Ruby - requires presence of Gemfile and Gemfile.lock + # bundler-audit: + # enabled: true + + # Rails - requires detecting a Rails application + # brakeman: + # enabled: true + + # Chef - requires detecting a cookbook + # foodcritic: + # enabled: true + + # Java - might require Java code? Errored when run without + # checkstyle: + # enabled: true + + # JavaScript - requires an eslintrc to be created and added to project + # eslint: + # enabled: true + # channel: "eslint-6" + +# --------------- +# List any files/folders to exclude from checking. Wildcards accepted. Leave +# commented if no files to exclude as an empty array will error +exclude_patterns: + - ".gitignore" diff --git a/ansible_collections/cyberark/conjur/.github/CODEOWNERS b/ansible_collections/cyberark/conjur/.github/CODEOWNERS new file mode 100644 index 000000000..7e099ff54 --- /dev/null +++ b/ansible_collections/cyberark/conjur/.github/CODEOWNERS @@ -0,0 +1,10 @@ +* @cyberark/community-and-integrations-team @conjurinc/community-and-integrations-team @conjurdemos/community-and-integrations-team + +# Changes to .trivyignore require Security Architect approval +.trivyignore @cyberark/security-architects @conjurinc/security-architects @conjurdemos/security-architects + +# Changes to .codeclimate.yml require Quality Architect approval +.codeclimate.yml @cyberark/quality-architects @conjurinc/quality-architects @conjurdemos/quality-architects + +# Changes to SECURITY.md require Security Architect approval +SECURITY.md @cyberark/security-architects @conjurinc/security-architects @conjurdemos/security-architects diff --git a/ansible_collections/cyberark/conjur/.github/workflows/ansible-test.yml b/ansible_collections/cyberark/conjur/.github/workflows/ansible-test.yml new file mode 100644 index 000000000..27d352352 --- /dev/null +++ b/ansible_collections/cyberark/conjur/.github/workflows/ansible-test.yml @@ -0,0 +1,91 @@ +# This file implements a Github action to run Ansible collection sanity tests +# on the Conjur Ansible Collection. The Ansible collection sanity tests are +# run across the following matrices: +# +#Ansible versions: +# - stable-2.10 +# - stable-2.11 +# - stable-2.12 +# - devel +# +#Python versions: +# - Python 3.9 + +name: CI +on: +# Run CI against all pushes (direct commits) and Pull Requests +- push +- pull_request + +jobs: + +### +# Sanity tests (REQUIRED) +# +# https://docs.ansible.com/ansible/latest/dev_guide/testing_sanity.html + + sanity: + name: Sanity (${{ matrix.ansible }}+py${{ matrix.python }}) + strategy: + matrix: + ansible: + # It's important that Sanity is tested against all stable-X.Y branches + # Testing against `devel` may fail as new tests are added. + - stable-2.10 + - stable-2.11 + - stable-2.12 + - devel + python: + - 3.9 + runs-on: ubuntu-latest + steps: + + # ansible-test requires the collection to be in a directory in the form + # .../ansible_collections/cyberark/conjur/ + + - name: Check out code + uses: actions/checkout@v2 + with: + path: ansible_collections/cyberark/conjur + + - name: Set up Python ${{ matrix.ansible }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + + # Install the head of the given branch (devel, stable-2.10) + - name: Install ansible-base (${{ matrix.ansible }}) + run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible }}.tar.gz --disable-pip-version-check + + # run ansible-test sanity inside of Docker. + # The docker container has all the pinned dependencies that are required. + # Explicity specify the version of Python we want to test + - name: Run sanity tests + run: ansible-test sanity --docker -v --color --python ${{ matrix.python }} + working-directory: ./ansible_collections/cyberark/conjur + +# Unit tests (OPTIONAL) + +# https://docs.ansible.com/ansible/latest/dev_guide/testing_units.html + + units: + name: Units (â’¶${{ matrix.ansible }}+py${{ matrix.python }}) + strategy: + matrix: + ansible: + - stable-2.10 + - stable-2.11 + - stable-2.12 + - devel + python: + - 3.9 + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v2 + with: + path: ansible_collections/cyberark/conjur + + - name: Run unit tests + run: ./dev/test_unit.sh -a ${{ matrix.ansible }} -p ${{ matrix.python }} + working-directory: ./ansible_collections/cyberark/conjur diff --git a/ansible_collections/cyberark/conjur/.gitignore b/ansible_collections/cyberark/conjur/.gitignore new file mode 100644 index 000000000..2be2e7467 --- /dev/null +++ b/ansible_collections/cyberark/conjur/.gitignore @@ -0,0 +1,39 @@ +# System directories files +.DS_Store +.idea/ + +# Test output +tests/*/bot/ +tests/*/coverage/ +tests/*/junit/ +tests/*/reports/ + +tests/*/conjur.pem +tests/*/conjur-enterprise.pem +tests/*/access_token +**/test-files/output +**/conjur-intro/ +bot +coverage +junit +reports + +*.js +*.html +output/ + +# Pycache directories and files +**/__pycache__/ +.pytest_cache +*.pyc + +# Distributable directories files +*.tar.gz +vendor/ + +# Temporary files +*.log +.cache +*.retry +*.tmp +conjur.pem diff --git a/ansible_collections/cyberark/conjur/CHANGELOG.md b/ansible_collections/cyberark/conjur/CHANGELOG.md new file mode 100644 index 000000000..8fa9162e4 --- /dev/null +++ b/ansible_collections/cyberark/conjur/CHANGELOG.md @@ -0,0 +1,84 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [1.2.0] - 2020-09-01 + +### Added +- Add `state` variable to Conjur Ansible role, which can be used to cleanup + configuration and identity artifacts created on managed nodes. + [cyberark/ansible-conjur-collection#176](https://github.com/cyberark/ansible-conjur-collection/pull/176) + +### Changed +- Lookup plugin now retries variable retrieval 5 times before accepting a + failure response. + [cyberark/ansible-conjur-collection#60](https://github.com/cyberark/ansible-conjur-collection/pull/60) + +### Removed +- End support for Python 2. + [cyberark/ansible-conjur-collection#69](https://github.com/cyberark/ansible-conjur-collection/pull/69) + +## [1.1.0] - 2020-12-29 + +### Added +- The [Conjur Ansible role](https://galaxy.ansible.com/cyberark/conjur-host-identity) has been + migrated to this collection, where it will be maintained moving forward. + At current, the role in the collection is aligned with the v0.3.2 release of + the standalone role. + [cyberark/ansible-conjur-host-identity#30](https://github.com/cyberark/ansible-conjur-host-identity/issues/30) +- Add `as_file` boolean option to the lookup plugin which stores the secret as + a temporary file and returns its path. This enables users to use the + `ansible_ssh_private_key_file` parameter to define an SSH private key using a + variable stored in Conjur; previously, users couldn't set this parameter via + a direct call to the lookup plugin because the parameter does not accept + inline SSH keys, and the lookup plugin could only return a string. + [cyberark/ansible-conjur-collection#52](https://github.com/cyberark/ansible-conjur-collection/issues/52), + [Cyberark Commons post #1070](https://discuss.cyberarkcommons.org/t/conjur-ansible-lookup-plugin-and-ssh-key-file/1070) + +## [1.0.7] - 2020-08-20 + +### Changed +- Various improvements to code quality, documentation, and adherence to Ansible standards + in preparation for including this collection in the release of Ansible 2.10. + [cyberark/ansible-conjur-collection#30](https://github.com/cyberark/ansible-conjur-collection/issues/30) + +## [1.0.6] - 2020-07-01 + +### Added +- Plugin supports authenticating with Conjur access token (for example, if provided by authn-k8s). + [cyberark/ansible-conjur-collection#23](https://github.com/cyberark/ansible-conjur-collection/issues/23) + +## [1.0.5] - 2020-06-18 + +### Added +- Plugin supports validation of self-signed certificates provided in `CONJUR_CERT_FILE` + or Conjur config file + ([cyberark/ansible-conjur-collection#4](https://github.com/cyberark/ansible-conjur-collection/issues/4)) + +### Fixed +- Encode spaces to "%20" instead of "+". This encoding fixes an issue where Conjur + variables that have spaces were not encoded correctly + ([cyberark/ansible-conjur-collection#12](https://github.com/cyberark/ansible-conjur-collection/issues/12)) +- Allow users to set `validate_certs` to `false` without setting a value to `cert_file` + ([cyberark/ansible-conjur-collection#13](https://github.com/cyberark/ansible-conjur-collection/issues/13)) + +## [1.0.3] - 2020-04-18 +### Changed +- Updated documentation section to comply with sanity checks + +## [1.0.2] - 2020-04-01 +### Added +- Migrated code from Ansible conjur_variable lookup plugin +- Added support to configure the use of the plugin via environment variables + +[Unreleased]: https://github.com/cyberark/ansible-conjur-collection/compare/v1.2.0...HEAD +[1.2.0]: https://github.com/cyberark/ansible-conjur-collection/compare/v1.1.0...v1.2.0 +[1.1.0]: https://github.com/cyberark/ansible-conjur-collection/compare/v1.0.7...v1.1.0 +[1.0.7]: https://github.com/cyberark/ansible-conjur-collection/compare/v1.0.6...v1.0.7 +[1.0.6]: https://github.com/cyberark/ansible-conjur-collection/compare/v1.0.5...v1.0.6 +[1.0.5]: https://github.com/cyberark/ansible-conjur-collection/compare/v1.0.3...v1.0.5 +[1.0.3]: https://github.com/cyberark/ansible-conjur-collection/compare/v1.0.2...v1.0.3 diff --git a/ansible_collections/cyberark/conjur/CONTRIBUTING.md b/ansible_collections/cyberark/conjur/CONTRIBUTING.md new file mode 100644 index 000000000..82319a287 --- /dev/null +++ b/ansible_collections/cyberark/conjur/CONTRIBUTING.md @@ -0,0 +1,251 @@ +# Contributing to the Ansible Conjur Collection + +Thanks for your interest in Conjur. Before contributing, please take a moment to +read and sign our <a href="https://github.com/cyberark/community/blob/master/documents/CyberArk_Open_Source_Contributor_Agreement.pdf" download="conjur_contributor_agreement">Contributor Agreement</a>. +This provides patent protection for all Conjur users and allows CyberArk to enforce +its license terms. Please email a signed copy to <a href="oss@cyberark.com">oss@cyberark.com</a>. +For general contribution and community guidelines, please see the [community repo](https://github.com/cyberark/community). + +- [Contributing to the Ansible Conjur Collection](#contributing-to-the-ansible-conjur-collection) + * [Prerequisites](#prerequisites) + * [Set up a development environment](#set-up-a-development-environment) + + [Verification](#verification) + + [Useful links](#useful-links) + * [Testing](#testing) + + [Unit tests](#unit-tests) + + [Integration tests](#integration-tests) + * [Releasing](#releasing) +- [Ansible Conjur Collection Quick Start](#ansible-conjur-collection-quick-start) + * [Setup a conjur OSS Environment](#setup-a-conjur-oss-environment) + * [Load policy to set up Conjur Ansible integration](#load-policy-to-set-up-conjur-ansible-integration) + * [Create Ansible managed nodes](#create-ansible-managed-nodes) + * [Use Conjur Ansible Role to set up identity on managed nodes](#use-conjur-ansible-role-to-set-up-identity-on-managed-nodes) + * [Use Conjur Lookup Plugin to provide secrets to Ansible Playbooks](#use-conjur-lookup-plugin-to-provide-secrets-to-ansible-playbooks) + +<small><i><a href='http://ecotrust-canada.github.io/markdown-toc/'>Table of contents generated with markdown-toc</a></i></small> + +## Prerequisites + +Before getting started, the following tools need to be installed: + +1. [Git][get-git] to manage source code +2. [Docker][get-docker] to manage dependencies and runtime environments +3. [Docker Compose][get-docker-compose] to orchestrate Docker environments + +[get-docker]: https://docs.docker.com/engine/installation +[get-docker-compose]: https://docs.docker.com/compose/install +[get-git]: https://git-scm.com/downloads + +## Set up a development environment + +The `dev` directory contains a `docker-compose` file which creates a development +environment : +- A Conjur Open Source instance +- An Ansible control node +- Managed nodes to push tasks to + +To use it: + +1. Install dependencies (as above) + +1. To use the dev environment, clone the + [Collection repository](https://github.com/cyberark/ansible-conjur-collection) + and run the setup script: + + ```sh-session + $ git clone https://github.com/cyberark/ansible-conjur-collection.git + $ cd ansible-conjur-collection/dev + $ ./start.sh + ``` + +### Verification + +When the Conjur and Ansible containers have been successfully setup, the +terminal prints the following: + +```sh-session + ... + PLAY RECAP ********************************************************************* + ansibleplugingtestingconjurhostidentity-test_app_centos-1 : ok=17 ... + ansibleplugingtestingconjurhostidentity-test_app_centos-2 : ok=17 ... + ansibleplugingtestingconjurhostidentity-test_app_ubuntu-1 : ok=16 ... + ansibleplugingtestingconjurhostidentity-test_app_ubuntu-2 : ok=16 ... + ``` + +Your Conjur instance will be configured with the following: +* Account: `cucumber` +* User: `admin` +* Password: Run `conjurctl role retrieve-key cucumber:user:admin` inside the + Conjur container shell to retrieve the admin user API key + +### Useful links + +- [Official documentation for Conjur's Ansible integration](https://docs.conjur.org/Latest/en/Content/Integrations/ansible.html) +- [Conjur Collection on Ansible Galaxy](https://galaxy.ansible.com/cyberark/conjur) +- [Ansible documentation for the Conjur collection](https://docs.ansible.com/ansible/latest/collections/cyberark/conjur/index.html) + +## Testing + +### Unit tests + +Unit tests are only available for the Conjur Variable Lookup plugin. To run +these tests: +``` +./dev/test_unit.sh +``` + +### Integration tests + +The collection has integration tests for both the Variable Lookup plugin and the +Host Identity role that will validate each against live Conjur and Ansible +containers. + +To run all tests: +``` +./ci/test.sh -a +``` + +To run the tests for a particular module: +``` +./ci/test.sh -d <role or plugin name> +``` + +Integration tests can be run against Conjur Enterprise by adding the `-e` flag: +``` +./ci/test/sh -e -a +``` + +## Releasing + +From a clean instance of main, perform the following actions to release a new version +of this plugin: + +- Update the version number in [`galaxy.yml`](galaxy.yml) and [`CHANGELOG.md`](CHANGELOG.md) + - Verify that all changes for this version in `CHANGELOG.md` are clear and accurate, + and are followed by a link to their respective issue + - Create a PR with these changes + +- Create an annotated tag with the new version, formatted as `v##.##.##` + - This will kick off an automated script which publish the release to + [Ansible Galaxy](https://galaxy.ansible.com/cyberark/conjur) + +- Create the release on GitHub for that tag + - Build the release package with `./ci/build_release` + - Attach package to Github Release + + +# Ansible Conjur Collection Quick Start + +## Setup a conjur OSS Environment + +Generate the master key, which will be used to encrypt Conjur's database. Store +this value as an environment variable. + +```sh-session +docker-compose run --no-deps --rm conjur data-key generate > data_key +export CONJUR_DATA_KEY="$(< data_key)" +``` + +Start the Conjur OSS environment. An account, named `cucumber`, will be +automatically created. + +```sh-session +docker-compose up -d conjur +``` + +Retrieve the admin user's API key, and store the value in an environment variable. + +```sh-session +export CLI_CONJUR_AUTHN_API_KEY="$(docker-compose exec conjur conjurctl role retrieve-key cucumber:user:admin)" +``` + +Start the Conjur CLI container. The CLI will be automatically authenticated as +the user `cucumber:user:admin`. + +```sh-session +docker-compose up -d conjur_cli +``` + +## Load policy to set up Conjur Ansible integration + +Policy defines Conjur entities and the relationships between them. An entity can +be a policy, a host, a user, a layer, a group, or a variable. + +Check out the policy file, and load it into Conjur: + +```sh-session +docker-compose exec conjur_cli cat /policy/root.yml +docker-compose exec conjur_cli conjur policy load root /policy/root.yml +``` + +Also, load a dummy secret value into the `ansible/target-password` variable. +This is a variable required by remote nodes in order to complete their workloads. + +```sh-session +docker-compose exec conjur_cli conjur variable values add ansible/target-password S3cretV@lue +``` + +## Create Ansible managed nodes + +The Ansible environment will include a control node and a number of managed +nodes. First, retrieve the API key for the Conjur host representing the control +node, then create it: + +```sh-session +export ANSIBLE_CONJUR_AUTHN_API_KEY="$(docker-compose exec conjur conjurctl role retrieve-key cucumber:host:ansible/ansible-master)" +docker-compose up -d ansible +``` + +Next, create two instances of each managed node: + +```sh-session +docker-compose up -d --scale test_app_ubuntu=2 test_app_ubuntu +docker-compose up -d --scale test_app_centos=2 test_app_centos +``` + +## Use Conjur Ansible Role to set up identity on managed nodes + +To grant your Ansible host a Conjur identity, first install the Conjur +Collection on your Ansible control node: + +```sh-session +docker-compose exec ansible ansible-galaxy collection install cyberark.conjur +``` + +Set up the host factory token in the HFTOKEN env var + +```sh-session +export HFTOKEN="$(docker-compose exec conjur_cli conjur hostfactory tokens create ansible/ansible-factory | jq -r '.[0].token')" +``` + +Once you've done this, you can configure each Ansible node with a Conjur +identity by including a section like the example below in your Ansible playbook: + +```yaml +--- +- hosts: testapp + roles: + - role: cyberark.conjur.conjur_host_identity + conjur_appliance_url: 'https://conjur.myorg.com', + conjur_account: 'cucumber', + conjur_host_factory_token: "{{lookup('env', 'HFTOKEN')}}", + conjur_host_name: "{{inventory_hostname}}" +``` + +First we register the host with Conjur, adding it into the layer specific to the +provided host factory token, and then install Summon with the Summon Conjur +provider for secret retrieval from Conjur. + +## Use Conjur Lookup Plugin to provide secrets to Ansible Playbooks + +The Conjur lookup plugin can inject secret data directly into an Ansible +playbook, like it the following example: + +```yaml +--- +- hosts: testapp + tasks: + - name: Provide secret with Lookup plugin + debug: + msg: "{{ lookup('cyberark.conjur.conjur_variable', '/ansible/target-password') }}" +``` diff --git a/ansible_collections/cyberark/conjur/FILES.json b/ansible_collections/cyberark/conjur/FILES.json new file mode 100644 index 000000000..75f988f2c --- /dev/null +++ b/ansible_collections/cyberark/conjur/FILES.json @@ -0,0 +1,1419 @@ +{ + "files": [ + { + "name": ".", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "README.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4f950d62ff31f5dd42adccedf87aee3526c4d297843eef14db49452c802a9445", + "format": 1 + }, + { + "name": ".github", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": ".github/workflows", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": ".github/workflows/ansible-test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3ea436b9dad8d9ea9ec8a5d07d60187cc85cabcbf455feaa1f3a21db10aa9fa1", + "format": 1 + }, + { + "name": ".github/CODEOWNERS", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f1dee7b6ae693cebe88547d034b17710489e515c3def06dad75252c8b19bfc51", + "format": 1 + }, + { + "name": "secrets.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "de372b010bf7a57b35af4bc901681a75cabe48b2e182d0691ff48d95a9d2b96f", + "format": 1 + }, + { + "name": "ci", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "ci/publish_to_galaxy", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "901e3836a10feba6a35d1c3b16e64a53e9069f87fed951edc2f292acc0fc1438", + "format": 1 + }, + { + "name": "ci/parse-changelog.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "21337ea8fac801781f8299af7e0cb7818fbc851bab26cbfff6224a0e5cf8dbb8", + "format": 1 + }, + { + "name": "ci/test.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "29d385e809a0a8c5e56a245244e1787d201c0c685182972ed39a5d3d07d5be0e", + "format": 1 + }, + { + "name": "ci/build_release", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9fd1048e1e1af19e2eb301a04c18fe207cc9fff663abc6485352aad2ed2225ef", + "format": 1 + }, + { + "name": ".codeclimate.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6e0a09af4e5f5f6fe2b16a8a49230170d53f0a495c6405d962be66a15a9774bd", + "format": 1 + }, + { + "name": "plugins", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/lookup", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/lookup/conjur_variable.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fb3d6620f57c577a966cbbeb1f01d234cf43cafb81dc3edb44f0e3cd5b6d9679", + "format": 1 + }, + { + "name": "roles", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/conjur_host_identity", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/conjur_host_identity/README.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a712c1e5c56fb9a3917ebfd4a55109801da9e57dff304d1941aaf2cf2ab471ca", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/conjur_host_identity/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c7f30d049a541e0fd98faeb5dcd8d83839e00db1f68ece8b14431630fb779e13", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tasks/uninstall.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d40f3bd7fdddbd8eddeb983e28ba959685e086d7382789b717e54a7f469d1f77", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tasks/identity.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e4a249b32c8b6d90b37e9787c6a22f6683820cf03af4f1038b8d73dbabf3fd1b", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tasks/install.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "723de78f2785f8cb7737c2e0e0801d674eba41636a7b1c5915dd41cec7083fed", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5f2195992211d603a73dead76e8e60e511ae0da2293a8a3167836d129ffe9bbf", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tasks/summon.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "40ad1a387e7f7a0a7c342614d9cdd5cd7ad4334634f4da733929e8e3b0a7ab4f", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tasks/identity_check.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "681aa9e68a95921df2701bd96baf8c9335845863d75395293a3002b037ce2b94", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tasks/summon-conjur.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b75ef3c1b8db527e7469e50d3b1f4f13ce09cc17ccec05ec598273f2afb79f34", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/templates", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/conjur_host_identity/templates/conjur.conf.j2", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a658eba375110e14ba752f82ccbcca9012351ca1c51a18419d870917bd807202", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/templates/conjur.identity.j2", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b53dbe8b97fc87d0c0c168799a14d91862a010bc0472f982587a47326a2312", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/conjur_host_identity/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0103f8e65603e7da7511224456dacf19c6a0e92c6e6f1fbf152f171d33741f80", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/inventory.j2", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f1474fb6e66c0fbcdd6129332de95feab1e1222f64f1be45325f4eac5101614f", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/.dockerignore", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1a0dc73ccd02fc377cf4cb1d99237e42335beeaf8c0bdb012dd4ecfb54790d57", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/ansible.cfg", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "73a428e3f22914440bb8072e66b3e36b3f930e513623a0a46a23bbd644995776", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/junit", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/junit/configure-conjur-identity", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c8c91315bc4679968e1f14101ae2ecc8606b82b32ab19e6aca4fb4905ee1e998", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/junit/cleanup-conjur-identity", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "24ac5dadeb28955ca2b4766e57bf27268a2fb10a186f425f06ff94200fc9e270", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/test_app_centos", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/test_app_centos/Dockerfile", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0de86cd21721bcf47192e1cb659d6edf4b20ac7ebf8ce1b667fc2dec14b4fbdf", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/test_app_ubuntu", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/test_app_ubuntu/Dockerfile", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2f381951ae812a2521539c06206945f2105f02beef04ab55ae76c5cf7e86406c", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/inventory-v2.j2", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2e1c0d5018358dbe5bce37280e9d8c8fd6fc9f0ff85f4b8fefbfa4aa04defb37", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/inventory-playbook.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "af542c07e6677cb819d35eb71e56587d3b4d97721578bed0775df4100695c79b", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/policy", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/policy/root.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2cf74b90f4eabbb9a9be67afeb2cf63c35b447579cef5b70f6daf270509d4309", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/Dockerfile", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d3e9a9f4bfff5c34b2078aa879faf017a494613a1ca5be5791ad4a8a08694c2e", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/test_cases", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/test_cases/configure-conjur-identity", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/test_cases/configure-conjur-identity/playbook.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e5608c4f7fbc040647b99f2137e58df82807f8054fabbcc10bd4c830df64e80c", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/test_cases/configure-conjur-identity/tests", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/test_cases/configure-conjur-identity/tests/test_default.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b23eaacaae61ccdaf6d79e911606c8b964301cb2e1d661a1328171c1ea080bdf", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/test_cases/cleanup-conjur-identity", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/test_cases/cleanup-conjur-identity/playbook.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c1d3a10ce6d52166877a318bee957e9f394a87f29b5732652c9b2ea9f3d456b4", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/test_cases/cleanup-conjur-identity/tests", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/test_cases/cleanup-conjur-identity/tests/test_default.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9425b40cb335c7dc2d2d2cce0f9f1bfd64ce6c3833032b38522f8f709a157953", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/test_cases/misconfig-conjur-identity", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/test_cases/misconfig-conjur-identity/playbook.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b6af952b247d510012d4d2ed2bd067f2f989345d391c28a83c0d865da10a2a2d", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/docker-compose.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "dec4fc80511912594c6c7dd0b0d0721de13c66a0bfe4fbf92ba7bc9a5ff6cb85", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/test.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e6d2aadcf5611e12181425d176b67fbe95740d374565535ece1be9181b78aabe", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/.pytest_cache", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/.pytest_cache/README.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "73fd6fccdd802c419a6b2d983d6c3173b7da97558ac4b589edec2dfe443db9ad", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/.pytest_cache/.gitignore", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3ed731b65d06150c138e2dadb0be0697550888a6b47eb8c45ecc9adba8b8e9bd", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/.pytest_cache/CACHEDIR.TAG", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "37dc88ef9a0abeddbe81053a6dd8fdfb13afb613045ea1eb4a5c815a74a3bde4", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/.pytest_cache/v", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/.pytest_cache/v/cache", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/.pytest_cache/v/cache/nodeids", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "49897170c9d77d5454d0f6550ba0c931b1dc295434317ac0ca3544c67dc53dfc", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/.pytest_cache/v/cache/stepwise", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/inventory-playbook-v2.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6045114154ca1faa2de0fa327a5524d44a76354023bd461a5b0f69c889bf1fe7", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/Dockerfile_nginx", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "513be93943c1c05ff4ea2df90d9e439f41d51059ec4513ce0098f463f0c96822", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/proxy", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/proxy/ssl.conf", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0a4a35d53f3dc60c2c4931e50136c580ed1eeb8850c93eea12e0965248990e41", + "format": 1 + }, + { + "name": "roles/conjur_host_identity/tests/proxy/default.conf", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2bf362d9b43aac8f3271e92af5d180ab2e5e9621a91bb49d9819f458e89c8e74", + "format": 1 + }, + { + "name": "dev", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "dev/ansible.cfg", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a9af189b8c8d89c9abbde117964844485f11a1f39a571c4222054e28a289aac6", + "format": 1 + }, + { + "name": "dev/test_app_centos", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "dev/test_app_centos/Dockerfile", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0de86cd21721bcf47192e1cb659d6edf4b20ac7ebf8ce1b667fc2dec14b4fbdf", + "format": 1 + }, + { + "name": "dev/test_unit.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2d7fd0d7f339f326c47c3bf29fb371e6f90748de5904cce4ef93a5c2868b31b7", + "format": 1 + }, + { + "name": "dev/test_app_ubuntu", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "dev/test_app_ubuntu/Dockerfile", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a589a57581799c71a9a0179d20cafac119423bf476b1283ffe64dee0dbb1327d", + "format": 1 + }, + { + "name": "dev/start.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "65c0f0415e06b601b8bce9f41b4ef1b50bfc5f6e88f24eb91ee1fe2cd9665bf3", + "format": 1 + }, + { + "name": "dev/policy", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "dev/policy/root.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2cf74b90f4eabbb9a9be67afeb2cf63c35b447579cef5b70f6daf270509d4309", + "format": 1 + }, + { + "name": "dev/Dockerfile", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "04decc424cd79115cd13f014ed57bb9c053b97132cdb1983dce73d20dfb20e86", + "format": 1 + }, + { + "name": "dev/docker-compose.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ca35276332fda1a0029c08c4993856c65db0aac39b23f171c9cbed99fa1f7c48", + "format": 1 + }, + { + "name": "dev/playbooks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "dev/playbooks/inventory-setup", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "dev/playbooks/inventory-setup/inventory.j2", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f1474fb6e66c0fbcdd6129332de95feab1e1222f64f1be45325f4eac5101614f", + "format": 1 + }, + { + "name": "dev/playbooks/inventory-setup/inventory-v2.j2", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2e1c0d5018358dbe5bce37280e9d8c8fd6fc9f0ff85f4b8fefbfa4aa04defb37", + "format": 1 + }, + { + "name": "dev/playbooks/inventory-setup/inventory-playbook.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5889c8007cb9273c2415a399b4099cc49ed12e41599409e79ee11183a66c6d33", + "format": 1 + }, + { + "name": "dev/playbooks/inventory-setup/inventory-playbook-v2.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4acbd2fe2d992b72af5085c1f74f1154b5843c679d723662529cd6f9b365ce5e", + "format": 1 + }, + { + "name": "dev/playbooks/conjur-identity-setup", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "dev/playbooks/conjur-identity-setup/conjur_role_playbook.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bcd9272359c7694b8c02240ed1596f47d237b099bf00ee3b64d08295d0c7478c", + "format": 1 + }, + { + "name": "dev/Dockerfile_nginx", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "513be93943c1c05ff4ea2df90d9e439f41d51059ec4513ce0098f463f0c96822", + "format": 1 + }, + { + "name": "dev/proxy", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "dev/proxy/ssl.conf", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0a4a35d53f3dc60c2c4931e50136c580ed1eeb8850c93eea12e0965248990e41", + "format": 1 + }, + { + "name": "dev/proxy/default.conf", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2bf362d9b43aac8f3271e92af5d180ab2e5e9621a91bb49d9819f458e89c8e74", + "format": 1 + }, + { + "name": "CONTRIBUTING.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "51665e44857e861e04b64120a17ea6f6960a1ca496d9ef6e2fec24bc01f585b6", + "format": 1 + }, + { + "name": ".gitignore", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b38eece6dfedd018ef0a4a5aafc02ec38b1fd2c26b6046876238b814b5ac6a13", + "format": 1 + }, + { + "name": "SECURITY.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1e913fcef04d2f2652839b896dd875dd3268b67d4669105e0e4b1a0249ef843a", + "format": 1 + }, + { + "name": "requirements.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0a2f8c8f38472c11f47c93fe7ac69bb6e08b2d09dcc5a2b9fdd7054366822a21", + "format": 1 + }, + { + "name": "examples", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "examples/test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "94fcc880e144e39d06fb0fd4957ca4aac77578627b509a110f4ec5b2ffc7f9ff", + "format": 1 + }, + { + "name": "Jenkinsfile", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "86a001994fd11e82fb1065a0448e4aca1ce8f3bb4d92b86d3b91285c97bd98c7", + "format": 1 + }, + { + "name": "meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "meta/runtime.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5d87483c5cc5fa8efe932acb8b6d203610070eaecf3eaf89244828331affdc59", + "format": 1 + }, + { + "name": "tests", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/plugins", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/plugins/lookup", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/plugins/lookup/test_conjur_variable.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1094276de30412849f76efef2aa091a3b40f9491bb581a4190070ca33e65d7c1", + "format": 1 + }, + { + "name": "tests/unit/plugins/lookup/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/unit/Dockerfile", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f3ddc9e66be63de38a509c3de747685d82f0ddf4d921260bcb9afe4a33c8f25b", + "format": 1 + }, + { + "name": "tests/unit/requirements.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d77bc26186f3301ae88c08e272549546ce9b746b881b9cc18873b71592ef9c39", + "format": 1 + }, + { + "name": "tests/sanity", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/sanity/ignore-2.10.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ae307769453795ad4a44b902d7b12b4c8fbaf0e14860dde658cd78a0dc4ae851", + "format": 1 + }, + { + "name": "tests/sanity/ignore-2.12.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "afec9c66a2cb034dbdbde017b83c2396fbd5522ca70176fe3bdb7c9890fffcaa", + "format": 1 + }, + { + "name": "tests/sanity/ignore-2.9.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b08ae705c45bd09b8e38926a50ba7f41b099bcd7eb954b9275398932fb5ef3af", + "format": 1 + }, + { + "name": "tests/sanity/ignore-2.13.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "afec9c66a2cb034dbdbde017b83c2396fbd5522ca70176fe3bdb7c9890fffcaa", + "format": 1 + }, + { + "name": "tests/sanity/ignore-2.14.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "15187a140306a1008f16190fbd19869ae04780436f3c424de932fc9e727e6c8c", + "format": 1 + }, + { + "name": "tests/sanity/ignore-2.11.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f14e70571eaed048c38d12d5d3ece414d8bd8b21ab2036b26dc5515e6ebf0b17", + "format": 1 + }, + { + "name": "tests/conjur_variable", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/.dockerignore", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1a0dc73ccd02fc377cf4cb1d99237e42335beeaf8c0bdb012dd4ecfb54790d57", + "format": 1 + }, + { + "name": "tests/conjur_variable/junit", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/junit/retrieve-variable-with-spaces-secret", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "450814057d9ff23fe172d1ab4261370e8c49e93429dea0ec5a5e54a90402044b", + "format": 1 + }, + { + "name": "tests/conjur_variable/junit/retrieve-variable-with-authn-token-bad-cert", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "22ee749d5bbcc57247124f120ff952c4b120a8fb31b383c57c8d237f9dce6756", + "format": 1 + }, + { + "name": "tests/conjur_variable/junit/retrieve-variable-bad-cert-path", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8aaab3b0c4135a32feb6e27b25c0a56c07e210554e52c8b7d5b0ff96e96f382a", + "format": 1 + }, + { + "name": "tests/conjur_variable/junit/retrieve-variable-no-cert-provided", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "02fe519032146f888ee4917ab2e4e4634430e3228eb5e1b7e1b8d4a0cd1c72c9", + "format": 1 + }, + { + "name": "tests/conjur_variable/junit/retrieve-variable", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "da21a70004df7d5ede5b6de8f22309be77a2a2e2d63edeb87a9f8c788d3f0055", + "format": 1 + }, + { + "name": "tests/conjur_variable/junit/retrieve-variable-into-file", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ff27dbeb102cd3c925f79f5783cacf6e461586af768c2cc6a051252d90eb8bd7", + "format": 1 + }, + { + "name": "tests/conjur_variable/junit/retrieve-variable-with-authn-token", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2b78a9aeaaf0226879ccb95a40995425c212789dcd6ed8effb1e99e8442776ee", + "format": 1 + }, + { + "name": "tests/conjur_variable/junit/retrieve-variable-bad-certs", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "78f002c0d5330875c83f68fa51aed9ad489351031f146093f83576a7434d11bd", + "format": 1 + }, + { + "name": "tests/conjur_variable/junit/retrieve-variable-disable-verify-certs", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "cff63e56362085e36639c36c9e4d5cb8717051b4b8f201be203b10d6a6d4875b", + "format": 1 + }, + { + "name": "tests/conjur_variable/policy", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/policy/root.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a14765439458912cff269b174d8d1630abbc8ff3ce634fb84ef3a3050d36e6dc", + "format": 1 + }, + { + "name": "tests/conjur_variable/Dockerfile", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "55f203234d81c41d08b6e13bca720243846b06ce1ab971da6b3535bcbef6623b", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-with-spaces-secret", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-with-spaces-secret/env", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e4e591758bdfdbe673f40afd9e81b4ccdd749a484d843b46f63e77d1e33c8108", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-with-spaces-secret/playbook.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e1a6f8f4cded9369fbf9c6bc55f725cd0aa8da7ff1fba59e91b338511ed20736", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-with-spaces-secret/tests", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-with-spaces-secret/tests/test_default.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "afd74fa0ebdb6d7cbb79e10760b644c33850a03daaf9b5960f0b6a7030c666c0", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-with-authn-token-bad-cert", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-with-authn-token-bad-cert/env", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4d4140d2408d4370136030335f326ae051bbff50998d45f7ec5db6b6249d1168", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-with-authn-token-bad-cert/bad-cert.pem", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7529afc89345f5dde282fb51014d158769d3fb22ddf20744d093eb8fa820b8d3", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-with-authn-token-bad-cert/playbook.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "09328157b6fca097da34a7f2deec84b639c46476d8d9deefc445c55097603d58", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-with-authn-token-bad-cert/tests", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-with-authn-token-bad-cert/tests/test_default.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6f82d730f6f13a368ee69640beb3907fdddfb243e5b6e600c127b6154e7d67b5", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-bad-cert-path", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-bad-cert-path/env", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9f5febf65f45e537c0666df07ea12f0568f1ee5afa7bc9eef5a36370e6b5dfa4", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-bad-cert-path/playbook.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bb4764e18fcc10f83c16d10cbc4b7eac8c0abd2668f1b158649fc1a0d47df2c8", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-bad-cert-path/tests", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-bad-cert-path/tests/test_default.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7aacd0b2b04e7b1850906a76dbda5df814c59a78f8b5290eed154cac9655ed68", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-no-cert-provided", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-no-cert-provided/playbook.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bb4764e18fcc10f83c16d10cbc4b7eac8c0abd2668f1b158649fc1a0d47df2c8", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-no-cert-provided/tests", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-no-cert-provided/tests/test_default.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7aacd0b2b04e7b1850906a76dbda5df814c59a78f8b5290eed154cac9655ed68", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable/env", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e4e591758bdfdbe673f40afd9e81b4ccdd749a484d843b46f63e77d1e33c8108", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable/playbook.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6bce626301d8259174bfa57b2bc7ff543b267f2f93be4a1a77df69b8cf515801", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable/tests", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable/tests/test_default.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "af47f2a134c9a33e2682f66cefe03002704c71557350674d5b5a58963251f6e3", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-into-file", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-into-file/env", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e4e591758bdfdbe673f40afd9e81b4ccdd749a484d843b46f63e77d1e33c8108", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-into-file/playbook.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b35ead7d25b79bf07011729173d288accd5a997296f4760373bd51cfeb3e6873", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-into-file/tests", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-into-file/tests/test_default.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8040a8b67b23b63706f3b4f79e6c080f26fbb1472906cab4a7146373b6dfd1b7", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-with-authn-token", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-with-authn-token/env", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8fa8ee6b4fb30d49d52e64a5fb70167ac0bdc7d968f18fe53219e1db475fcff9", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-with-authn-token/playbook.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a57510d743d358c0405b3de73cbae3e5945c492fb8ca64be0d3777f1e4de811e", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-with-authn-token/tests", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-with-authn-token/tests/test_default.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "af47f2a134c9a33e2682f66cefe03002704c71557350674d5b5a58963251f6e3", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-bad-certs", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-bad-certs/env", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e1d90dec2be9f840d4f27dcfd2bf1c67be44c5d801ee57ea45c94ff6895ddf62", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-bad-certs/bad-cert.pem", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7529afc89345f5dde282fb51014d158769d3fb22ddf20744d093eb8fa820b8d3", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-bad-certs/playbook.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bb4764e18fcc10f83c16d10cbc4b7eac8c0abd2668f1b158649fc1a0d47df2c8", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-bad-certs/tests", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-bad-certs/tests/test_default.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7aacd0b2b04e7b1850906a76dbda5df814c59a78f8b5290eed154cac9655ed68", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-disable-verify-certs", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-disable-verify-certs/playbook.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d94bc0b090fc07e738c0bfc05b3b6747850b8f06e5e290771d200efeb3044e35", + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-disable-verify-certs/tests", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/test_cases/retrieve-variable-disable-verify-certs/tests/test_default.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "af47f2a134c9a33e2682f66cefe03002704c71557350674d5b5a58963251f6e3", + "format": 1 + }, + { + "name": "tests/conjur_variable/docker-compose.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c433a8af1060d174e8c03e99fa26da51092a24c007e5486a83de9e507854df9c", + "format": 1 + }, + { + "name": "tests/conjur_variable/pytest.ini", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4b8dce177a1820e68b4a821d858669035b849f9c5fbc4cfd6d4718325a7e69c8", + "format": 1 + }, + { + "name": "tests/conjur_variable/test.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "40af14dc3993251c72b4a0d11075e999a1b8ef9076e41b15614e762e6dd94879", + "format": 1 + }, + { + "name": "tests/conjur_variable/.pytest_cache", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/.pytest_cache/README.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "73fd6fccdd802c419a6b2d983d6c3173b7da97558ac4b589edec2dfe443db9ad", + "format": 1 + }, + { + "name": "tests/conjur_variable/.pytest_cache/.gitignore", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3ed731b65d06150c138e2dadb0be0697550888a6b47eb8c45ecc9adba8b8e9bd", + "format": 1 + }, + { + "name": "tests/conjur_variable/.pytest_cache/CACHEDIR.TAG", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "37dc88ef9a0abeddbe81053a6dd8fdfb13afb613045ea1eb4a5c815a74a3bde4", + "format": 1 + }, + { + "name": "tests/conjur_variable/.pytest_cache/v", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/.pytest_cache/v/cache", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/.pytest_cache/v/cache/nodeids", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d58a2892e1f89b35c10fd58d24d7811dd7537e762c65d3b32decd33681206cee", + "format": 1 + }, + { + "name": "tests/conjur_variable/.pytest_cache/v/cache/stepwise", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945", + "format": 1 + }, + { + "name": "tests/conjur_variable/Dockerfile_nginx", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "53cbc9253079dd1a19afb896e3839d9bd8b812d9473d769438c44eb10e03858c", + "format": 1 + }, + { + "name": "tests/conjur_variable/proxy", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/conjur_variable/proxy/ssl.conf", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "11bd8688bb233d0d366f16673a9892bf14aea34aa0d2fe40811ad5ca5028b490", + "format": 1 + }, + { + "name": "tests/conjur_variable/proxy/default.conf", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ffa77f3f6db4da0916c4c666fc6e5582a3584dc302a4852048a0b84c889ab7ea", + "format": 1 + }, + { + "name": "LICENSE", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e092618211b1d864e3caf325abbd567f997e6ffb98d9fb97188d4fa280334bbe", + "format": 1 + }, + { + "name": "CHANGELOG.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "cfc80a8bef07ed8c3f72a7c27774095d8033d59077a165f688efae49fb8ada77", + "format": 1 + } + ], + "format": 1 +}
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/Jenkinsfile b/ansible_collections/cyberark/conjur/Jenkinsfile new file mode 100644 index 000000000..01242da42 --- /dev/null +++ b/ansible_collections/cyberark/conjur/Jenkinsfile @@ -0,0 +1,127 @@ +#!/usr/bin/env groovy + +pipeline { + agent { label 'executor-v2' } + + options { + timestamps() + buildDiscarder(logRotator(numToKeepStr: '30')) + } + + stages { + stage('Validate') { + parallel { + stage('Changelog') { + steps { sh './ci/parse-changelog.sh' } + } + } + } + + stage('Run conjur_variable unit tests') { + steps { + sh './dev/test_unit.sh -r' + publishHTML (target : [allowMissing: false, + alwaysLinkToLastBuild: false, + keepAll: true, + reportDir: 'tests/output/reports/coverage=units/', + reportFiles: 'index.html', + reportName: 'Ansible Coverage Report', + reportTitles: 'Conjur Ansible Collection report']) + } + } + + stage('Run integration tests with Conjur Open Source') { + stages { + stage('Ansible v6 - latest') { + parallel { + stage('Testing conjur_variable lookup plugin') { + steps { + sh './ci/test.sh -d conjur_variable' + junit 'tests/conjur_variable/junit/*' + } + } + + stage('Testing conjur_host_identity role') { + steps { + sh './ci/test.sh -d conjur_host_identity' + junit 'roles/conjur_host_identity/tests/junit/*' + } + } + } + } + + stage('Ansible v5') { + when { + anyOf { + branch 'main' + buildingTag() + } + } + parallel { + stage('Testing conjur_variable lookup plugin') { + steps { + sh './ci/test.sh -v 5 -d conjur_variable' + junit 'tests/conjur_variable/junit/*' + } + } + + stage('Testing conjur_host_identity role') { + steps { + sh './ci/test.sh -v 5 -d conjur_host_identity' + junit 'roles/conjur_host_identity/tests/junit/*' + } + } + } + } + } + } + + stage('Run integration tests with Conjur Enterprise') { + stages { + stage("Testing conjur_variable lookup plugin") { + steps { + sh './ci/test.sh -e -d conjur_variable' + junit 'tests/conjur_variable/junit/*' + } + } + + stage("Testing conjur_host_identity role") { + steps { + sh './ci/test.sh -e -d conjur_host_identity' + junit 'roles/conjur_host_identity/tests/junit/*' + } + } + } + } + + stage('Build Release Artifacts') { + when { + anyOf { + branch 'main' + buildingTag() + } + } + + steps { + sh './ci/build_release' + archiveArtifacts 'cyberark-conjur-*.tar.gz' + } + } + + stage('Publish to Ansible Galaxy') { + when { + buildingTag() + } + + steps { + sh 'summon ./ci/publish_to_galaxy' + } + } + } + + post { + always { + cleanupAndNotify(currentBuild.currentResult) + } + } +}
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/LICENSE b/ansible_collections/cyberark/conjur/LICENSE new file mode 100644 index 000000000..af8afbaf4 --- /dev/null +++ b/ansible_collections/cyberark/conjur/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) 2020 CyberArk Software Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ansible_collections/cyberark/conjur/MANIFEST.json b/ansible_collections/cyberark/conjur/MANIFEST.json new file mode 100644 index 000000000..e3ecbed80 --- /dev/null +++ b/ansible_collections/cyberark/conjur/MANIFEST.json @@ -0,0 +1,43 @@ +{ + "collection_info": { + "namespace": "cyberark", + "name": "conjur", + "version": "1.2.0", + "authors": [ + "CyberArk Business Development (@cyberark-bizdev)", + "(@cyberark/community-and-integrations-team)" + ], + "readme": "README.md", + "tags": [ + "cyberark", + "conjur", + "access", + "security", + "account", + "vault", + "identity", + "credential", + "secret", + "privileged", + "devops" + ], + "description": "This is a Collection of the CyberArk Conjur/DAP toolkit.", + "license": [ + "Apache-2.0" + ], + "license_file": null, + "dependencies": {}, + "repository": "https://github.com/cyberark/ansible-conjur-collection", + "documentation": null, + "homepage": null, + "issues": "https://github.com/cyberark/ansible-conjur-collection/issues" + }, + "file_manifest_file": { + "name": "FILES.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4c7843e25d53f8c2c8b96576286bfc6b138b4d24784289888c832f761992aadf", + "format": 1 + }, + "format": 1 +}
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/README.md b/ansible_collections/cyberark/conjur/README.md new file mode 100644 index 000000000..7b9b35471 --- /dev/null +++ b/ansible_collections/cyberark/conjur/README.md @@ -0,0 +1,228 @@ +# CyberArk Ansible Conjur Collection + +This collection contains components to be used with CyberArk Conjur & Conjur Enterprise +hosted in [Ansible Galaxy](https://galaxy.ansible.com/cyberark/conjur). + +## Table of Contents + +* [Certification Level](#certification-level) +* [Requirements](#requirements) +* [Installation](#installation) +* [Conjur Ansible Role](#conjur-ansible-role) + + [Usage](#usage) + + [Role Variables](#role-variables) + + [Example Playbook](#example-playbook) + + [Summon & Service Managers](#summon---service-managers) + + [Recommendations](#recommendations) +* [Conjur Ansible Lookup Plugin](#conjur-ansible-lookup-plugin) + + [Environment variables](#environment-variables) + + [Role Variables](#role-variables-1) + + [Examples](#examples) + - [Retrieve a secret in a Playbook](#retrieve-a-secret-in-a-playbook) + - [Retrieve a private key in an Inventory file](#retrieve-a-private-key-in-an-inventory-file) +* [Contributing](#contributing) +* [License](#license) + +<!-- Table of contents generated with markdown-toc +http://ecotrust-canada.github.io/markdown-toc/ --> + +## Certification Level + +![](https://img.shields.io/badge/Certification%20Level-Certified-6C757D?link=https://github.com/cyberark/community/blob/main/Conjur/conventions/certification-levels.md) + +This repo is a **Certified** level project. It's been reviewed by CyberArk to +verify that it will securely work with CyberArk Enterprise as documented. In +addition, CyberArk offers Enterprise-level support for these features. For more +detailed information on our certification levels, see +[our community guidelines](https://github.com/cyberark/community/blob/main/Conjur/conventions/certification-levels.md#community). + +## Requirements + +- An instance of [CyberArk Conjur Open Source](https://www.conjur.org) v1.x+ or [CyberArk + Conjur Enterprise](https://docs.cyberark.com/Product-Doc/OnlineHelp/AAM-DAP/Latest/en/Content/Resources/_TopNav/cc_Home.htm) + (formerly DAP) v10.x+ accessible from the target node +- Ansible >= 2.9 + +## Using ansible-conjur-collection with Conjur Open Source + +Are you using this project with [Conjur Open Source](https://github.com/cyberark/conjur)? Then we +**strongly** recommend choosing the version of this project to use from the latest [Conjur OSS +suite release](https://docs.conjur.org/Latest/en/Content/Overview/Conjur-OSS-Suite-Overview.html). +Conjur maintainers perform additional testing on the suite release versions to ensure +compatibility. When possible, upgrade your Conjur version to match the +[latest suite release](https://docs.conjur.org/Latest/en/Content/ReleaseNotes/ConjurOSS-suite-RN.htm); +when using integrations, choose the latest suite release that matches your Conjur version. For any +questions, please contact us on [Discourse](https://discuss.cyberarkcommons.org/c/conjur/5). + +## Installation + +From terminal, run the following command: +```sh +ansible-galaxy collection install cyberark.conjur +``` + +## Conjur Ansible Role + +This Ansible role provides the ability to grant Conjur machine identity to a host. Based on that +identity, secrets can then be retrieved securely using the [Conjur Lookup +Plugin](#conjur-ansible-lookup-plugin) or using the [Summon](https://github.com/cyberark/summon) +tool (installed on hosts with identities created by this role). + +### Usage + +The Conjur role provides a method to establish the Conjur identity of a remote node with Ansible. +The node can then be granted least-privilege access to retrieve the secrets it needs in a secure +manner. + +### Role Variables + +* `conjur_appliance_url` _(Required)_: URL of the running Conjur service +* `conjur_account` _(Required)_: Conjur account name +* `conjur_host_factory_token` _(Required)_: [Host + Factory](https://developer.conjur.net/reference/services/host_factory/) token for layer + enrollment. This should be specified in the environment on the Ansible controlling host. +* `conjur_host_name` _(Required)_: Name of the host to be created. +* `conjur_ssl_certificate`: Public SSL certificate of the Conjur endpoint +* `conjur_validate_certs`: Boolean value to indicate if the Conjur endpoint should validate + certificates +* `state`: Specifies whether to install of uninstall the Role on the specified nodes +* `summon.version`: version of Summon to install. Default is `0.8.2`. +* `summon_conjur.version`: version of Summon-Conjur provider to install. Default is `0.5.3`. + +The variables not marked _`(Required)`_ are required for running with an HTTPS Conjur endpoint. + +### Example Playbook + +Configure a remote node with a Conjur identity and Summon: +```yml +- hosts: servers + roles: + - role: cyberark.conjur.conjur_host_identity + conjur_appliance_url: 'https://conjur.myorg.com' + conjur_account: 'myorg' + conjur_host_factory_token: "{{ lookup('env', 'HFTOKEN') }}" + conjur_host_name: "{{ inventory_hostname }}" + conjur_ssl_certificate: "{{ lookup('file', '/path/to/conjur.pem') }}" + conjur_validate_certs: yes +``` + +This example: +- Registers the host `{{ inventory_hostname }}` with Conjur, adding it into the Conjur policy layer + defined for the provided host factory token. +- Installs Summon with the Summon Conjur provider for secret retrieval from Conjur. + +### Role Cleanup + +Executing the following playbook will clean up configuration and identity files +written to the specified remote nodes, as well as uninstalling Summon and the +Summon Conjur provider: +```yml +- hosts: servers + roles: + - role: cyberark.conjur.conjur_host_identity + state: absent +``` + +### Summon & Service Managers + +With Summon installed, using Conjur with a Service Manager (like systemd) becomes a snap. Here's a +simple example of a `systemd` file connecting to Conjur: + +```ini +[Unit] +Description=DemoApp +After=network-online.target + +[Service] +User=DemoUser +#Environment=CONJUR_MAJOR_VERSION=4 +ExecStart=/usr/local/bin/summon --yaml 'DB_PASSWORD: !var staging/demoapp/database/password' /usr/local/bin/myapp +``` + +> Note: When connecting to Conjur 4 (Conjur Enterprise), Summon requires the environment variable +`CONJUR_MAJOR_VERSION` set to `4`. You can provide it by uncommenting the relevant line above. + +The above example uses Summon to retrieve the password stored in `staging/myapp/database/password`, +set it to an environment variable `DB_PASSWORD`, and provide it to the demo application process. +Using Summon, the secret is kept off disk. If the service is restarted, Summon retrieves the +password as the application is started. + +### Recommendations + +- Add `no_log: true` to each play that uses sensitive data, otherwise that data can be printed to + the logs. + +- Set the Ansible files to minimum permissions. Ansible uses the permissions of the user that runs + it. + +## Conjur Ansible Lookup Plugin + +Fetch credentials from CyberArk Conjur using the controlling host's Conjur identity or environment +variables. + +The controlling host running Ansible must have a Conjur identity, provided for example by the +[ConjurAnsible role](#conjur-ansible-role). + +### Environment variables + +The following environment variables will be used by the lookup plugin to authenticate with the +Conjur host, if they are present on the system running the lookup plugin. + +- `CONJUR_ACCOUNT` : The Conjur account name +- `CONJUR_APPLIANCE_URL` : URL of the running Conjur service +- `CONJUR_CERT_FILE` : Path to the Conjur certificate file +- `CONJUR_AUTHN_LOGIN` : A valid Conjur host username +- `CONJUR_AUTHN_API_KEY` : The api key that corresponds to the Conjur host username +- `CONJUR_AUTHN_TOKEN_FILE` : Path to a file containing a valid Conjur auth token + +### Role Variables + +None. + +### Examples + +#### Retrieve a secret in a Playbook + +```yaml +--- +- hosts: localhost + tasks: + - name: Lookup variable in Conjur + debug: + msg: "{{ lookup('cyberark.conjur.conjur_variable', '/path/to/secret') }}" +``` + +#### Retrieve a private key in an Inventory file + +```yaml +--- +ansible_host: <host> +ansible_ssh_private_key_file: "{{ lookup('cyberark.conjur.conjur_variable', 'path/to/secret-id', as_file=True) }}" +``` + +**Note:** Using the `as_file=True` condition, the private key is stored in a temporary file and its path is written +in `ansible_ssh_private_key_file`. + +## Contributing + +We welcome contributions of all kinds to this repository. For instructions on how to get started and +descriptions of our development workflows, please see our [contributing guide][contrib]. + +[contrib]: https://github.com/cyberark/ansible-conjur-collection/blob/main/CONTRIBUTING.md + +## License + +Copyright (c) 2020 CyberArk Software Ltd. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is +distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied. See the License for the specific language governing permissions and limitations under the +License. + +For the full license text see [`LICENSE`](LICENSE). diff --git a/ansible_collections/cyberark/conjur/SECURITY.md b/ansible_collections/cyberark/conjur/SECURITY.md new file mode 100644 index 000000000..5315a3953 --- /dev/null +++ b/ansible_collections/cyberark/conjur/SECURITY.md @@ -0,0 +1,42 @@ +# Security Policies and Procedures + +This document outlines security procedures and general policies for the CyberArk Conjur +suite of tools and products. + + * [Reporting a Bug](#reporting-a-bug) + * [Disclosure Policy](#disclosure-policy) + * [Comments on this Policy](#comments-on-this-policy) + +## Reporting a Bug + +The CyberArk Conjur team and community take all security bugs in the Conjur suite seriously. +Thank you for improving the security of the Conjur suite. We appreciate your efforts and +responsible disclosure and will make every effort to acknowledge your +contributions. + +Report security bugs by emailing the lead maintainers at security@conjur.org. + +The maintainers will acknowledge your email within 2 business days. Subsequently, we will +send a more detailed response within 2 business days of our acknowledgement indicating +the next steps in handling your report. After the initial reply to your report, the security +team will endeavor to keep you informed of the progress towards a fix and full +announcement, and may ask for additional information or guidance. + +Report security bugs in third-party modules to the person or team maintaining +the module. + +## Disclosure Policy + +When the security team receives a security bug report, they will assign it to a +primary handler. This person will coordinate the fix and release process, +involving the following steps: + + * Confirm the problem and determine the affected versions. + * Audit code to find any potential similar problems. + * Prepare fixes for all releases still under maintenance. These fixes will be + released as fast as possible. + +## Comments on this Policy + +If you have suggestions on how this process could be improved please submit a +pull request. diff --git a/ansible_collections/cyberark/conjur/ci/build_release b/ansible_collections/cyberark/conjur/ci/build_release new file mode 100755 index 000000000..07baf8898 --- /dev/null +++ b/ansible_collections/cyberark/conjur/ci/build_release @@ -0,0 +1,14 @@ +#!/bin/bash + +set -euo pipefail + +TOP_LEVEL_DIR="$(cd "$(dirname "$BASH_SOURCE")"; pwd)/.." + +pushd "$TOP_LEVEL_DIR" >/dev/null + docker run --rm -t \ + -v "$TOP_LEVEL_DIR:/collection" \ + python:3 /bin/bash -c " + pip install ansible + ansible-galaxy collection build --force --output /collection/. /collection + " +popd >/dev/null diff --git a/ansible_collections/cyberark/conjur/ci/parse-changelog.sh b/ansible_collections/cyberark/conjur/ci/parse-changelog.sh new file mode 100755 index 000000000..be7d8270e --- /dev/null +++ b/ansible_collections/cyberark/conjur/ci/parse-changelog.sh @@ -0,0 +1,6 @@ +#!/bin/bash -ex + +docker run \ + --rm \ + --volume "${PWD}/CHANGELOG.md":/CHANGELOG.md \ + cyberark/parse-a-changelog
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/ci/publish_to_galaxy b/ansible_collections/cyberark/conjur/ci/publish_to_galaxy new file mode 100755 index 000000000..44a2aa16d --- /dev/null +++ b/ansible_collections/cyberark/conjur/ci/publish_to_galaxy @@ -0,0 +1,18 @@ +#!/bin/bash + +set -euo pipefail + +# Strip the 'v' from the Tag Name +TAG=${TAG_NAME//"v"} + +TOP_LEVEL_DIR="$(cd "$(dirname "$BASH_SOURCE")"; pwd)/.." + +pushd "$TOP_LEVEL_DIR" >/dev/null + docker run --rm -t \ + -e GALAXY_API_KEY \ + -v "$TOP_LEVEL_DIR:/collection" \ + python:3 /bin/bash -c " + pip install ansible + ansible-galaxy collection publish --api-key \${GALAXY_API_KEY} /collection/cyberark-conjur-${TAG}.tar.gz + " +popd >/dev/null diff --git a/ansible_collections/cyberark/conjur/ci/test.sh b/ansible_collections/cyberark/conjur/ci/test.sh new file mode 100755 index 000000000..2a535d750 --- /dev/null +++ b/ansible_collections/cyberark/conjur/ci/test.sh @@ -0,0 +1,107 @@ +#!/bin/bash -ex + +# Test runner for Ansible Conjur Collection + +# Test subdirectors containing a `test.sh` file +test_directories=("conjur_variable") + +# Roles containing a test subdirectory +role_directories=("conjur_host_identity") + +# Target directory that can be manually set by passing a value to the `-d` flag +target="" + +# Flags to be applied to testing scripts +flags="" + +declare -x ANSIBLE_VERSION="${ANSIBLE_VERSION:-6}" + +# Print usage instructions +function help { + echo "Test runner for Ansible Conjur Collection" + + echo "-a Run all test files in default test directories" + echo "-v <ver> Run tests against the given Ansible major version" + echo "-d <arg> Run test file in given directory. Valid options are: ${test_directories[*]} all" + echo "-e Run tests against Conjur Enterprise. Default: Conjur Open Source" + echo " This option is currently only available when testing against the conjur_variable plugin" + echo "-h View help and available commands" + exit 1 +} + +# Run a `test.sh` file in a given subdirectory of the top-level `tests` directory +# Expected directory structure is "tests/<plugin>/test.sh" +function run_test { + pushd "${PWD}/tests/${1}" + echo "Running ${1} tests..." + ./test.sh "$flags" + popd +} + +# Run a `test.sh` file for a given role +# Expected directory structure is "roles/<role>/tests/test.sh" +function run_role_test { + pushd "${PWD}/roles/${1}/tests" + echo "Running ${1} tests..." + ./test.sh "$flags" + popd +} + +# Handles input to dictate wether all tests should be ran, or just one set +function handle_input { + if [[ -n ${target} ]]; then + for test_dir in "${test_directories[@]}"; do + if [[ ${target} == "${test_dir}" ]]; then + run_test ${target} + exit 0 + fi + done + for test_dir in "${role_directories[@]}"; do + if [[ ${target} == "${test_dir}" ]]; then + run_role_test ${target} + exit 0 + fi + done + echo "Error: unrecognized test directory given: ${target}" + echo "" + help + else + echo "Running all tests..." + for test_dir in "${test_directories[@]}"; do + run_test "${test_dir}" + done + for test_dir in "${role_directories[@]}"; do + run_role_test "${test_dir}" + done + exit 0 + fi +} + +# Exit if no input given +if [[ $# -eq 0 ]] ; then + echo "Error: No test directory or flag given" + echo "" + help +fi + +while getopts ad:ehv: option; do + case "$option" in + a) handle_input + ;; + d) target=${OPTARG} + handle_input + ;; + e) flags="-e" + ;; + h) help + ;; + v) ANSIBLE_VERSION="${OPTARG}" + ;; + * ) + echo "$1 is not a valid option" + help + exit 1 + ;; + esac +done + diff --git a/ansible_collections/cyberark/conjur/dev/Dockerfile b/ansible_collections/cyberark/conjur/dev/Dockerfile new file mode 100644 index 000000000..f8ebcaccb --- /dev/null +++ b/ansible_collections/cyberark/conjur/dev/Dockerfile @@ -0,0 +1,38 @@ +FROM ubuntu:20.04 + +ENV DEBIAN_FRONTEND=noninteractive + +WORKDIR /cyberark + +# install python 3 +RUN apt-get update && \ + apt-get install -y python3-pip && \ + pip3 install --upgrade pip + +# install ansible and its test tool +RUN pip3 install ansible pytest-testinfra + +# install docker installation requirements +RUN apt-get update && \ + apt-get install -y apt-transport-https \ + ca-certificates \ + curl \ + software-properties-common + +# install docker +RUN curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - +RUN add-apt-repository \ + "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ + $(lsb_release -cs) \ + stable" + +RUN apt-get update && \ + apt-get -y install docker-ce + +# NOTE: Everything above is copied from REPO_ROOT/tests/conjur_variable/Dockerfile. It defines a +# standard container image for running ansible tests + +# install ruby +RUN apt-get update && apt-get install -y gcc build-essential +RUN apt-add-repository -y ppa:brightbox/ruby-ng && apt-get update && apt-get install -y ruby2.7 ruby2.7-dev +RUN gem install conjur-cli diff --git a/ansible_collections/cyberark/conjur/dev/Dockerfile_nginx b/ansible_collections/cyberark/conjur/dev/Dockerfile_nginx new file mode 100644 index 000000000..d9f18c7bd --- /dev/null +++ b/ansible_collections/cyberark/conjur/dev/Dockerfile_nginx @@ -0,0 +1,16 @@ +FROM nginx:1.13.3 + +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y iputils-ping procps openssl && \ + rm -rf /var/lib/apt/lists/* + +WORKDIR /etc/nginx/ + +COPY proxy/ssl.conf /etc/ssl/openssl.cnf + +RUN openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -config /etc/ssl/openssl.cnf -extensions v3_ca \ + -keyout cert.key -out cert.crt + +COPY proxy/default.conf /etc/nginx/conf.d/default.conf diff --git a/ansible_collections/cyberark/conjur/dev/ansible.cfg b/ansible_collections/cyberark/conjur/dev/ansible.cfg new file mode 100644 index 000000000..5b1d8246b --- /dev/null +++ b/ansible_collections/cyberark/conjur/dev/ansible.cfg @@ -0,0 +1,7 @@ +[defaults] +host_key_checking = False +error_on_undefined_vars = True +timeout = 60 +inventory = inventory.tmp +roles_path = /cyberark +remote_tmp = /tmp diff --git a/ansible_collections/cyberark/conjur/dev/docker-compose.yml b/ansible_collections/cyberark/conjur/dev/docker-compose.yml new file mode 100644 index 000000000..8e1d2dd9f --- /dev/null +++ b/ansible_collections/cyberark/conjur/dev/docker-compose.yml @@ -0,0 +1,74 @@ +version: '3' +services: + ansible: + build: + context: . + dockerfile: Dockerfile + command: /bin/sleep 1d + environment: + CONJUR_APPLIANCE_URL: http://conjur:3000 + CONJUR_ACCOUNT: cucumber + CONJUR_AUTHN_LOGIN: host/ansible/ansible-master + CONJUR_AUTHN_API_KEY: ${ANSIBLE_CONJUR_AUTHN_API_KEY} + CONJUR_CUSTOM_AUTHN_API_KEY: ${CUSTOM_CONJUR_AUTHN_API_KEY} + COMPOSE_PROJECT_NAME: ${COMPOSE_PROJECT_NAME} + # NOTE: Explicitly setting the ANSIBLE_CONFIG envvar avoids Ansible ignoring + # the configuration because it is in a world-writable working directory, + # see https://docs.ansible.com/ansible/latest/reference_appendices/config.html#avoiding-security-risks-with-ansible-cfg-in-the-current-directory. + ANSIBLE_CONFIG: ./ansible.cfg + volumes: + - ../roles/conjur_host_identity:/cyberark/cyberark.conjur.conjur-host-identity/ + - .:/cyberark/dev/ + - /var/run/docker.sock:/var/run/docker.sock + + pg: + image: postgres:9.3 + + conjur: + image: cyberark/conjur + command: server -a cucumber -p 3000 + environment: + CONJUR_APPLIANCE_URL: http://localhost:3000 + DATABASE_URL: postgres://postgres@pg/postgres + CONJUR_DATA_KEY: "W0BuL8iTr/7QvtjIluJbrb5LDAnmXzmcpxkqihO3dXA=" + networks: + - default + links: + - pg + + conjur_cli: + image: cyberark/conjur-cli:5-latest + entrypoint: [] + command: sleep infinity + environment: + CONJUR_APPLIANCE_URL: http://conjur:3000 + CONJUR_ACCOUNT: cucumber + CONJUR_AUTHN_LOGIN: admin + CONJUR_AUTHN_API_KEY: ${CLI_CONJUR_AUTHN_API_KEY} + volumes: + - ./policy:/policy + networks: + - default + links: + - conjur + + test_app_ubuntu: + build: ./test_app_ubuntu + entrypoint: sleep + command: infinity + + test_app_centos: + build: ./test_app_centos + entrypoint: sleep + command: infinity + + conjur-proxy-nginx: + build: + context: . + dockerfile: Dockerfile_nginx + entrypoint: nginx-debug -g 'daemon off;' + environment: + TERM: xterm + depends_on: + - conjur + - conjur_cli diff --git a/ansible_collections/cyberark/conjur/dev/playbooks/conjur-identity-setup/conjur_role_playbook.yml b/ansible_collections/cyberark/conjur/dev/playbooks/conjur-identity-setup/conjur_role_playbook.yml new file mode 100644 index 000000000..6972b50ed --- /dev/null +++ b/ansible_collections/cyberark/conjur/dev/playbooks/conjur-identity-setup/conjur_role_playbook.yml @@ -0,0 +1,11 @@ +--- +- name: Configuring conjur identity on remote hosts + hosts: testapp + roles: + - role: "cyberark.conjur.conjur-host-identity" + conjur_account: cucumber + conjur_appliance_url: "https://conjur-proxy-nginx" + conjur_host_factory_token: "{{lookup('env', 'HFTOKEN')}}" + conjur_host_name: "conjur_{{ ansible_hostname }}" + conjur_ssl_certificate: "{{lookup('file', '../../conjur.pem')}}" + conjur_validate_certs: yes diff --git a/ansible_collections/cyberark/conjur/dev/playbooks/inventory-setup/inventory-playbook-v2.yml b/ansible_collections/cyberark/conjur/dev/playbooks/inventory-setup/inventory-playbook-v2.yml new file mode 100644 index 000000000..3bf92eaf8 --- /dev/null +++ b/ansible_collections/cyberark/conjur/dev/playbooks/inventory-setup/inventory-playbook-v2.yml @@ -0,0 +1,6 @@ +--- +- name: Compile inventory template locally + hosts: localhost + tasks: + - name: compile inventory template + template: src=inventory-v2.j2 dest=/cyberark/dev/inventory.tmp diff --git a/ansible_collections/cyberark/conjur/dev/playbooks/inventory-setup/inventory-playbook.yml b/ansible_collections/cyberark/conjur/dev/playbooks/inventory-setup/inventory-playbook.yml new file mode 100644 index 000000000..e61ee8053 --- /dev/null +++ b/ansible_collections/cyberark/conjur/dev/playbooks/inventory-setup/inventory-playbook.yml @@ -0,0 +1,6 @@ +--- +- name: Compile inventory template locally + hosts: localhost + tasks: + - name: compile inventory template + template: src=inventory.j2 dest=/cyberark/dev/inventory.tmp diff --git a/ansible_collections/cyberark/conjur/dev/playbooks/inventory-setup/inventory-v2.j2 b/ansible_collections/cyberark/conjur/dev/playbooks/inventory-setup/inventory-v2.j2 new file mode 100644 index 000000000..4a004fb13 --- /dev/null +++ b/ansible_collections/cyberark/conjur/dev/playbooks/inventory-setup/inventory-v2.j2 @@ -0,0 +1,6 @@ +[testapp] +{{ lookup('env','COMPOSE_PROJECT_NAME') }}-test_app_ubuntu-[1:2] ansible_connection=docker +{{ lookup('env','COMPOSE_PROJECT_NAME') }}-test_app_centos-[1:2] ansible_connection=docker + +[ansible] +{{ lookup('env','COMPOSE_PROJECT_NAME') }}-ansible-1 ansible_connection=docker diff --git a/ansible_collections/cyberark/conjur/dev/playbooks/inventory-setup/inventory.j2 b/ansible_collections/cyberark/conjur/dev/playbooks/inventory-setup/inventory.j2 new file mode 100644 index 000000000..62d48ef82 --- /dev/null +++ b/ansible_collections/cyberark/conjur/dev/playbooks/inventory-setup/inventory.j2 @@ -0,0 +1,6 @@ +[testapp] +{{ lookup('env','COMPOSE_PROJECT_NAME') }}_test_app_ubuntu_[1:2] ansible_connection=docker +{{ lookup('env','COMPOSE_PROJECT_NAME') }}_test_app_centos_[1:2] ansible_connection=docker + +[ansible] +{{ lookup('env','COMPOSE_PROJECT_NAME') }}_ansible_1 ansible_connection=docker diff --git a/ansible_collections/cyberark/conjur/dev/policy/root.yml b/ansible_collections/cyberark/conjur/dev/policy/root.yml new file mode 100644 index 000000000..0309cf702 --- /dev/null +++ b/ansible_collections/cyberark/conjur/dev/policy/root.yml @@ -0,0 +1,32 @@ +--- +- !policy + id: ansible + annotations: + description: Policy for Ansible master and remote hosts + body: + + - !host + id: ansible-master + annotations: + description: Host for running Ansible on remote targets + + - !layer &remote_hosts_layer + id: remote_hosts + annotations: + description: Layer for Ansible remote hosts + + - !host-factory + id: ansible-factory + annotations: + description: Factory to create new hosts for ansible + layer: [ *remote_hosts_layer ] + + - !variable + id: target-password + annotations: + description: Password needed by the Ansible remote machine + + - !permit + role: *remote_hosts_layer + privileges: [ execute ] + resources: [ !variable target-password ] diff --git a/ansible_collections/cyberark/conjur/dev/proxy/default.conf b/ansible_collections/cyberark/conjur/dev/proxy/default.conf new file mode 100644 index 000000000..db2153a71 --- /dev/null +++ b/ansible_collections/cyberark/conjur/dev/proxy/default.conf @@ -0,0 +1,33 @@ +server { + listen 80; + return 301 https://conjur$request_uri; +} + +server { + listen 443; + server_name localhost; + ssl_certificate /etc/nginx/cert.crt; + ssl_certificate_key /etc/nginx/cert.key; + + ssl on; + ssl_session_cache builtin:1000 shared:SSL:10m; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4; + ssl_prefer_server_ciphers on; + + access_log /var/log/nginx/access.log; + + location / { + proxy_pass http://conjur:3000; + } + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + +} diff --git a/ansible_collections/cyberark/conjur/dev/proxy/ssl.conf b/ansible_collections/cyberark/conjur/dev/proxy/ssl.conf new file mode 100644 index 000000000..e78716b27 --- /dev/null +++ b/ansible_collections/cyberark/conjur/dev/proxy/ssl.conf @@ -0,0 +1,39 @@ +[req] +default_bits = 2048 +prompt = no +default_md = sha256 +req_extensions = req_ext +distinguished_name = dn +x509_extensions = v3_ca # The extentions to add to the self signed cert +req_extensions = v3_req +x509_extensions = usr_cert + +[ dn ] +C=IL +ST=Israel +L=TLV +O=Onyx +OU=CyberArk +CN=conjur-proxy-nginx + +[ usr_cert ] +basicConstraints=CA:FALSE +nsCertType = client, server, email +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = serverAuth, clientAuth, codeSigning, emailProtection +nsComment = "OpenSSL Generated Certificate" +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +[ v3_req ] +extendedKeyUsage = serverAuth, clientAuth, codeSigning, emailProtection +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +[ v3_ca ] +subjectAltName = @alt_names + +[ alt_names ] +DNS.1 = localhost +DNS.2 = conjur-proxy-nginx +IP.1 = 127.0.0.1 diff --git a/ansible_collections/cyberark/conjur/dev/start.sh b/ansible_collections/cyberark/conjur/dev/start.sh new file mode 100755 index 000000000..f9ba8b525 --- /dev/null +++ b/ansible_collections/cyberark/conjur/dev/start.sh @@ -0,0 +1,113 @@ +#!/bin/bash +set -ex + + +declare -x ANSIBLE_CONJUR_AUTHN_API_KEY='' +declare -x CLI_CONJUR_AUTHN_API_KEY='' +declare cli_cid='' +declare conjur_cid='' +declare ansible_cid='' +# normalises project name by filtering non alphanumeric characters and transforming to lowercase +declare -x COMPOSE_PROJECT_NAME + +COMPOSE_PROJECT_NAME=$(echo "${BUILD_TAG:-ansible-pluging-testing}-conjur-host-identity" | sed -e 's/[^[:alnum:]]//g' | tr '[:upper:]' '[:lower:]') +export COMPOSE_PROJECT_NAME + +# get conjur client auth api key +function api_key_for { + local role_id=$1 + if [ -n "$role_id" ] + then + docker exec "${conjur_cid}" rails r "print Credentials['${role_id}'].api_key" + else + echo ERROR: api_key_for called with no argument 1>&2 + exit 1 + fi +} + +function hf_token { + docker exec "${cli_cid}" bash -c 'conjur hostfactory tokens create --duration-days=5 ansible/ansible-factory | jq -r ".[0].token"' +} + +function setup_conjur { + echo "---- setting up conjur ----" + # run policy + docker exec "${cli_cid}" conjur policy load root /policy/root.yml + # set secret values + docker exec "${cli_cid}" bash -ec 'conjur variable values add ansible/target-password target_secret_password' +} + +function setup_conjur_identities { + echo "---scale up inventory nodes and setup the conjur identity there---" + teardown_and_setup + docker exec "${ansible_cid}" env HFTOKEN="$(hf_token)" bash -ec " + cd dev + ansible-playbook playbooks/conjur-identity-setup/conjur_role_playbook.yml" +} + + # Scale up inventory nodes +function teardown_and_setup { + docker-compose up -d --force-recreate --scale test_app_ubuntu=2 test_app_ubuntu + docker-compose up -d --force-recreate --scale test_app_centos=2 test_app_centos +} + +function wait_for_server { + # shellcheck disable=SC2016 + docker exec "${cli_cid}" bash -ec ' + for i in $( seq 20 ); do + curl -o /dev/null -fs -X OPTIONS ${CONJUR_APPLIANCE_URL} > /dev/null && echo "server is up" && break + echo "." + sleep 2 + done + ' +} + +function fetch_ssl_cert { + (docker-compose exec -T conjur-proxy-nginx cat cert.crt) > conjur.pem +} + +function generate_inventory { + # Use a different inventory file for docker-compose v1 and v2 or later + playbook_file="inventory-playbook-v2.yml" + compose_ver=$(docker-compose version --short) + if [[ $compose_ver == "1"* ]]; then + playbook_file="inventory-playbook.yml" + fi + + # uses .j2 template to generate inventory prepended with COMPOSE_PROJECT_NAME + docker-compose exec -T ansible bash -ec " + cd dev + ansible-playbook playbooks/inventory-setup/$playbook_file + " +} + +function clean { + echo 'Removing dev environment' + echo '---' + docker-compose down -v + rm -rf inventory.tmp +} + +function main() { + clean + docker-compose up -d --build + generate_inventory + + conjur_cid=$(docker-compose ps -q conjur) + cli_cid=$(docker-compose ps -q conjur_cli) + fetch_ssl_cert + wait_for_server + + CLI_CONJUR_AUTHN_API_KEY=$(api_key_for 'cucumber:user:admin') + docker-compose up -d conjur_cli + + cli_cid=$(docker-compose ps -q conjur_cli) + setup_conjur + + ANSIBLE_CONJUR_AUTHN_API_KEY=$(api_key_for 'cucumber:host:ansible/ansible-master') + docker-compose up -d ansible + + ansible_cid=$(docker-compose ps -q ansible) + setup_conjur_identities +} + main
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/dev/test_app_centos/Dockerfile b/ansible_collections/cyberark/conjur/dev/test_app_centos/Dockerfile new file mode 100644 index 000000000..ee474e7bf --- /dev/null +++ b/ansible_collections/cyberark/conjur/dev/test_app_centos/Dockerfile @@ -0,0 +1,4 @@ +FROM centos:7 + +# Install Python so Ansible can run against node +RUN yum update -y && yum install -y python3 diff --git a/ansible_collections/cyberark/conjur/dev/test_app_ubuntu/Dockerfile b/ansible_collections/cyberark/conjur/dev/test_app_ubuntu/Dockerfile new file mode 100644 index 000000000..1721241ba --- /dev/null +++ b/ansible_collections/cyberark/conjur/dev/test_app_ubuntu/Dockerfile @@ -0,0 +1,5 @@ +FROM ubuntu:20.04 + +# Install Python so Ansible can run against node +RUN apt-get update -y && apt-get install -y python3-minimal + diff --git a/ansible_collections/cyberark/conjur/dev/test_unit.sh b/ansible_collections/cyberark/conjur/dev/test_unit.sh new file mode 100755 index 000000000..0a00da865 --- /dev/null +++ b/ansible_collections/cyberark/conjur/dev/test_unit.sh @@ -0,0 +1,47 @@ +#!/bin/bash -eu + +ansible_version="stable-2.10" +python_version="3.9" +gen_report="false" + +cd "$(dirname "$0")"/.. + +function print_usage() { + cat << EOF +Run unit tests for Conjur Variable Lookup plugin. + +./ansibletest.sh [options] + +-a <version> Run tests against specified Ansible version (Default: stable-2.10) +-p <version> Run tests against specified Python version (Default: 3.9) +-r Generate test coverage report +EOF +} + +while getopts 'a:p:r' flag; do + case "${flag}" in + a) ansible_version="${OPTARG}" ;; + p) python_version="${OPTARG}" ;; + r) gen_report="true" ;; + *) print_usage + exit 1 ;; + esac +done + +test_cmd="ansible-test units -v --python $python_version" +if [[ "$gen_report" == "true" ]]; then + test_cmd="ansible-test coverage erase; + $test_cmd --coverage; + ansible-test coverage html --requirements --group-by command; + " +fi + +docker build \ + --build-arg PYTHON_VERSION="${python_version}" \ + --build-arg ANSIBLE_VERSION="${ansible_version}" \ + -t pytest-tools:latest \ + -f tests/unit/Dockerfile . +docker run --rm \ + -v "${PWD}/":/ansible_collections/cyberark/conjur/ \ + -w /ansible_collections/cyberark/conjur/tests/unit/ \ + pytest-tools:latest /bin/bash -c "$test_cmd" diff --git a/ansible_collections/cyberark/conjur/examples/test.yml b/ansible_collections/cyberark/conjur/examples/test.yml new file mode 100644 index 000000000..9f9ad15ad --- /dev/null +++ b/ansible_collections/cyberark/conjur/examples/test.yml @@ -0,0 +1,11 @@ +--- + - hosts: localhost + + tasks: + - name: Lookup variable in Conjur + debug: + msg: "{{ lookup('cyberark.conjur.conjur_variable', '/path/to/secret') }}" + + - name: Lookup variable in Conjur to not validate certs (in case of self-signed) + debug: + msg: "{{ lookup('cyberark.conjur.conjur_variable', '/path/to/secret', validate_certs=false) }}" diff --git a/ansible_collections/cyberark/conjur/meta/runtime.yml b/ansible_collections/cyberark/conjur/meta/runtime.yml new file mode 100644 index 000000000..58bc85789 --- /dev/null +++ b/ansible_collections/cyberark/conjur/meta/runtime.yml @@ -0,0 +1,2 @@ +--- + requires_ansible: '>=2.9' diff --git a/ansible_collections/cyberark/conjur/plugins/lookup/conjur_variable.py b/ansible_collections/cyberark/conjur/plugins/lookup/conjur_variable.py new file mode 100644 index 000000000..8b523c6c5 --- /dev/null +++ b/ansible_collections/cyberark/conjur/plugins/lookup/conjur_variable.py @@ -0,0 +1,367 @@ +# (c) 2020 CyberArk Software Ltd. All rights reserved. +# (c) 2018 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = """ + name: conjur_variable + version_added: "1.0.2" + short_description: Fetch credentials from CyberArk Conjur. + author: + - CyberArk BizDev (@cyberark-bizdev) + description: + Retrieves credentials from Conjur using the controlling host's Conjur identity + or environment variables. + Environment variables could be CONJUR_ACCOUNT, CONJUR_APPLIANCE_URL, CONJUR_CERT_FILE, CONJUR_AUTHN_LOGIN, CONJUR_AUTHN_API_KEY, CONJUR_AUTHN_TOKEN_FILE + Conjur info - U(https://www.conjur.org/). + requirements: + - 'The controlling host running Ansible has a Conjur identity. + (More: U(https://docs.conjur.org/latest/en/Content/Get%20Started/key_concepts/machine_identity.html))' + options: + _terms: + description: Variable path + required: True + validate_certs: + description: Flag to control SSL certificate validation + type: boolean + default: True + as_file: + description: > + Store lookup result in a temporary file and returns the file path. Thus allowing it to be consumed as an ansible file parameter + (eg ansible_ssh_private_key_file). + type: boolean + default: False + identity_file: + description: Path to the Conjur identity file. The identity file follows the netrc file format convention. + type: path + default: /etc/conjur.identity + required: False + ini: + - section: conjur, + key: identity_file_path + env: + - name: CONJUR_IDENTITY_FILE + authn_token_file: + description: Path to the access token file. + type: path + default: /var/run/conjur/access-token + required: False + ini: + - section: conjur, + key: authn_token_file + env: + - name: CONJUR_AUTHN_TOKEN_FILE + config_file: + description: Path to the Conjur configuration file. The configuration file is a YAML file. + type: path + default: /etc/conjur.conf + required: False + ini: + - section: conjur, + key: config_file_path + env: + - name: CONJUR_CONFIG_FILE +""" + +EXAMPLES = """ +--- + - hosts: localhost + collections: + - cyberark.conjur + tasks: + - name: Lookup variable in Conjur + debug: + msg: "{{ lookup('cyberark.conjur.conjur_variable', '/path/to/secret') }}" +""" + +RETURN = """ + _raw: + description: + - Value stored in Conjur. +""" + +import os.path +import socket +from ansible.errors import AnsibleError +from ansible.plugins.lookup import LookupBase +from base64 import b64encode +from netrc import netrc +from os import environ +from time import time, sleep +from ansible.module_utils.six.moves.urllib.parse import quote +from ansible.module_utils.urls import urllib_error +from stat import S_IRUSR, S_IWUSR +from tempfile import gettempdir, NamedTemporaryFile +import yaml + +from ansible.module_utils.urls import open_url +from ansible.utils.display import Display +import ssl + +display = Display() + + +# Load configuration and return as dictionary if file is present on file system +def _load_conf_from_file(conf_path): + display.vvv('conf file: {0}'.format(conf_path)) + + if not os.path.exists(conf_path): + return {} + # raise AnsibleError('Conjur configuration file `{0}` was not found on the controlling host' + # .format(conf_path)) + + display.vvvv('Loading configuration from: {0}'.format(conf_path)) + with open(conf_path) as f: + config = yaml.safe_load(f.read()) + return config + + +# Load identity and return as dictionary if file is present on file system +def _load_identity_from_file(identity_path, appliance_url): + display.vvvv('identity file: {0}'.format(identity_path)) + + if not os.path.exists(identity_path): + return {} + # raise AnsibleError('Conjur identity file `{0}` was not found on the controlling host' + # .format(identity_path)) + + display.vvvv('Loading identity from: {0} for {1}'.format(identity_path, appliance_url)) + + conjur_authn_url = '{0}/authn'.format(appliance_url) + identity = netrc(identity_path) + + if identity.authenticators(conjur_authn_url) is None: + raise AnsibleError('The netrc file on the controlling host does not contain an entry for: {0}' + .format(conjur_authn_url)) + + id, account, api_key = identity.authenticators(conjur_authn_url) + if not id or not api_key: + return {} + + return {'id': id, 'api_key': api_key} + + +# Merge multiple dictionaries by using dict.update mechanism +def _merge_dictionaries(*arg): + ret = {} + for item in arg: + ret.update(item) + return ret + + +# The `quote` method's default value for `safe` is '/' so it doesn't encode slashes +# into "%2F" which is what the Conjur server expects. Thus, we need to use this +# method with no safe characters. We can't use the method `quote_plus` (which encodes +# slashes correctly) because it encodes spaces into the character '+' instead of "%20" +# as expected by the Conjur server +def _encode_str(input_str): + return quote(input_str, safe='') + + +# Use credentials to retrieve temporary authorization token +def _fetch_conjur_token(conjur_url, account, username, api_key, validate_certs, cert_file): + conjur_url = '{0}/authn/{1}/{2}/authenticate'.format(conjur_url, account, _encode_str(username)) + display.vvvv('Authentication request to Conjur at: {0}, with user: {1}'.format( + conjur_url, + _encode_str(username))) + + response = open_url(conjur_url, + data=api_key, + method='POST', + validate_certs=validate_certs, + ca_path=cert_file) + code = response.getcode() + if code != 200: + raise AnsibleError('Failed to authenticate as \'{0}\' (got {1} response)' + .format(username, code)) + + return response.read() + + +def retry(retries, retry_interval): + """ + Custom retry decorator + + Args: + retries (int, optional): Number of retries. Defaults to 5. + retry_interval (int, optional): Time to wait between intervals. Defaults to 10. + """ + def parameters_wrapper(target): + def decorator(*args, **kwargs): + retry_count = 0 + while True: + retry_count += 1 + try: + return_value = target(*args, **kwargs) + return return_value + except urllib_error.HTTPError as e: + if retry_count >= retries: + raise e + display.v('Error encountered. Retrying..') + except socket.timeout: + if retry_count >= retries: + raise e + display.v('Socket timeout encountered. Retrying..') + sleep(retry_interval) + return decorator + return parameters_wrapper + + +@retry(retries=5, retry_interval=10) +def _repeat_open_url(url, headers=None, method=None, validate_certs=True, ca_path=None): + return open_url(url, + headers=headers, + method=method, + validate_certs=validate_certs, + ca_path=ca_path) + + +# Retrieve Conjur variable using the temporary token +def _fetch_conjur_variable(conjur_variable, token, conjur_url, account, validate_certs, cert_file): + token = b64encode(token) + headers = {'Authorization': 'Token token="{0}"'.format(token.decode("utf-8"))} + + url = '{0}/secrets/{1}/variable/{2}'.format(conjur_url, account, _encode_str(conjur_variable)) + display.vvvv('Conjur Variable URL: {0}'.format(url)) + + response = _repeat_open_url(url, + headers=headers, + method='GET', + validate_certs=validate_certs, + ca_path=cert_file) + + if response.getcode() == 200: + display.vvvv('Conjur variable {0} was successfully retrieved'.format(conjur_variable)) + value = response.read().decode("utf-8") + return [value] + if response.getcode() == 401: + raise AnsibleError('Conjur request has invalid authorization credentials') + if response.getcode() == 403: + raise AnsibleError('The controlling host\'s Conjur identity does not have authorization to retrieve {0}' + .format(conjur_variable)) + if response.getcode() == 404: + raise AnsibleError('The variable {0} does not exist'.format(conjur_variable)) + + return {} + + +def _default_tmp_path(): + if os.access("/dev/shm", os.W_OK): + return "/dev/shm" + + return gettempdir() + + +def _store_secret_in_file(value): + secrets_file = NamedTemporaryFile(mode='w', dir=_default_tmp_path(), delete=False) + os.chmod(secrets_file.name, S_IRUSR | S_IWUSR) + secrets_file.write(value[0]) + + return [secrets_file.name] + + +class LookupModule(LookupBase): + + def run(self, terms, variables=None, **kwargs): + if terms == []: + raise AnsibleError("Invalid secret path: no secret path provided.") + elif not terms[0] or terms[0].isspace(): + raise AnsibleError("Invalid secret path: empty secret path not accepted.") + + self.set_options(direct=kwargs) + validate_certs = self.get_option('validate_certs') + conf_file = self.get_option('config_file') + as_file = self.get_option('as_file') + + if validate_certs is False: + display.warning('Certificate validation has been disabled. Please enable with validate_certs option.') + + if 'http://' in str(environ.get("CONJUR_APPLIANCE_URL")): + raise AnsibleError(('[WARNING]: Conjur URL uses insecure connection. Please consider using HTTPS.')) + + conf = _merge_dictionaries( + _load_conf_from_file(conf_file), + { + "account": environ.get('CONJUR_ACCOUNT'), + "appliance_url": environ.get("CONJUR_APPLIANCE_URL") + } if ( + environ.get('CONJUR_ACCOUNT') is not None + and environ.get('CONJUR_APPLIANCE_URL') is not None + ) + else {}, + { + "cert_file": environ.get('CONJUR_CERT_FILE') + } if (environ.get('CONJUR_CERT_FILE') is not None) + else {}, + { + "authn_token_file": environ.get('CONJUR_AUTHN_TOKEN_FILE') + } if (environ.get('CONJUR_AUTHN_TOKEN_FILE') is not None) + else {} + ) + + if 'authn_token_file' not in conf: + identity_file = self.get_option('identity_file') + identity = _merge_dictionaries( + _load_identity_from_file(identity_file, conf['appliance_url']), + { + "id": environ.get('CONJUR_AUTHN_LOGIN'), + "api_key": environ.get('CONJUR_AUTHN_API_KEY') + } if (environ.get('CONJUR_AUTHN_LOGIN') is not None + and environ.get('CONJUR_AUTHN_API_KEY') is not None) + else {} + ) + + if 'account' not in conf or 'appliance_url' not in conf: + raise AnsibleError( + ("Configuration file on the controlling host must " + "define `account` and `appliance_url`" + "entries or they should be environment variables") + ) + + if 'id' not in identity or 'api_key' not in identity: + raise AnsibleError( + ("Identity file on the controlling host must contain " + "`login` and `password` entries for Conjur appliance" + " URL or they should be environment variables") + ) + + cert_file = None + if 'cert_file' in conf: + display.vvv("Using cert file path {0}".format(conf['cert_file'])) + cert_file = conf['cert_file'] + + token = None + if 'authn_token_file' not in conf: + token = _fetch_conjur_token( + conf['appliance_url'], + conf['account'], + identity['id'], + identity['api_key'], + validate_certs, + cert_file + ) + else: + if not os.path.exists(conf['authn_token_file']): + raise AnsibleError('Conjur authn token file `{0}` was not found on the host' + .format(conf['authn_token_file'])) + with open(conf['authn_token_file'], 'rb') as f: + token = f.read() + + conjur_variable = _fetch_conjur_variable( + terms[0], + token, + conf['appliance_url'], + conf['account'], + validate_certs, + cert_file + ) + + if as_file: + return _store_secret_in_file(conjur_variable) + + return conjur_variable diff --git a/ansible_collections/cyberark/conjur/requirements.txt b/ansible_collections/cyberark/conjur/requirements.txt new file mode 100644 index 000000000..40d19fd88 --- /dev/null +++ b/ansible_collections/cyberark/conjur/requirements.txt @@ -0,0 +1 @@ +ansible>=2.9 diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/README.md b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/README.md new file mode 100644 index 000000000..138d549da --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/README.md @@ -0,0 +1,8 @@ +# Conjur Ansible Role + +This Ansible role provides the ability to grant Conjur machine identity to a host. +Once a host has an identity created by this role, secrets can be retrieved securely +using the [Summon](https://github.com/cyberark/summon) tool. + +For full usage and installation instructions, please see our +[collection documentation](https://github.com/cyberark/ansible-conjur-collection#installation). diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/defaults/main.yml b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/defaults/main.yml new file mode 100644 index 000000000..d04410d8b --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/defaults/main.yml @@ -0,0 +1,6 @@ +summon: + version: 0.8.2 + os: linux-amd64 +summon_conjur: + version: 0.5.3 + os: linux-amd64 diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/meta/main.yml b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/meta/main.yml new file mode 100644 index 000000000..1fc12ef18 --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/meta/main.yml @@ -0,0 +1,26 @@ +dependencies: [] + +galaxy_info: + short_description: Grants Conjur machine identity + description: Grants Conjur machine identity to hosts + company: CyberArk + license: Apache + author: + - Cyberark Community and Integrations Team (@cyberark/community-and-integrations-team) + + min_ansible_version: '2.9' + + platforms: + - name: Ubuntu + versions: + - trusty + - xenial + - name: EL + versions: + - 7 + + galaxy_tags: + - identity + - cyberark + - conjur + - security diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tasks/identity.yml b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tasks/identity.yml new file mode 100644 index 000000000..c87f6e37b --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tasks/identity.yml @@ -0,0 +1,73 @@ +--- +- name: Create group conjur + group: + name: conjur + state: present + +- block: + - name: Install "ca-certificates" + package: + name: ca-certificates + retries: 10 + delay: 2 + + - name: Place Conjur public SSL certificate + copy: + dest: "{{ conjur_ssl_certificate_path }}" + content: "{{ conjur_ssl_certificate }}" + mode: 0644 + + - name: Symlink Conjur public SSL certificate into /etc/ssl/certs + file: + src: "{{ conjur_ssl_certificate_path }}" + dest: /etc/ssl/certs/conjur.crt + state: link + register: cert_symlink + + - name: Install openssl-perl Package + yum: + name: openssl-perl + when: + ansible_os_family == 'RedHat' + retries: 10 + delay: 2 + + - name: Rehash certs + command: 'c_rehash' + when: cert_symlink.changed + when: ssl_configuration + +- name: Render /etc/conjur.conf + template: + src: templates/conjur.conf.j2 + dest: /etc/conjur.conf + mode: 0644 + +- block: + - name: Warn against disabling cert validation + debug: + msg: "[WARNING]: Certificate validation has been disabled. Please enable with conjur_validate_certs variable." + when: not conjur_validate_certs + + - name: Request identity from Conjur + uri: + url: "{{ conjur_appliance_url }}/host_factories/hosts" + method: POST + body: "id={{ conjur_host_name }}" + headers: + Authorization: Token token="{{ conjur_host_factory_token }}" + Content-Type: "application/x-www-form-urlencoded" + status_code: 201 + validate_certs: "{{ conjur_validate_certs }}" + register: host_factory_response + retries: 3 + delay: 10 + until: host_factory_response.status == 201 + + - name: Place identity file /etc/conjur.identity + template: + src: templates/conjur.identity.j2 + dest: /etc/conjur.identity + mode: 0640 + group: conjur + when: not conjurized diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tasks/identity_check.yml b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tasks/identity_check.yml new file mode 100644 index 000000000..8661daf70 --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tasks/identity_check.yml @@ -0,0 +1,53 @@ +--- +- name: Check if /etc/conjur.identity already exists + stat: + path: /etc/conjur.identity + register: identity_file + +- name: Set fact "conjurized" + set_fact: + conjurized: "{{ identity_file.stat.exists|bool }}" + +- name: Ensure all required variables are set + fail: msg="Variable '{{ item }}' is not set!" + when: item is undefined + with_items: + - "{{ conjur_account }}" + - "{{ conjur_appliance_url }}" + - "{{ conjur_host_name }}" + +- name: Set fact "ssl_configuration" + set_fact: + ssl_configuration: "{{ 'https' in conjur_appliance_url }}" + +- block: + - name: Ensure all required ssl variables are set + fail: msg="Variable '{{ item }}' is not set!" + when: item is undefined + with_items: + - "{{ conjur_ssl_certificate }}" + - "{{ conjur_validate_certs }}" + + - name: Set fact "ssl file path" + set_fact: + conjur_ssl_certificate_path: "/etc/conjur.pem" + when: ssl_configuration + +- block: + - name: Set fact "non ssl configuration" + set_fact: + conjur_ssl_certificate_path: "" + conjur_validate_certs: no + + - name: Warn against using insecure connection schemes + debug: + msg: "[WARNING]: Provided Conjur URL uses insecure connection scheme. Please consider using HTTPS." + when: not ssl_configuration + +- block: + - name: Ensure "conjur_host_factory_token" is set (if node is not already conjurized) + fail: msg="Variable '{{ item }}' is not set!" + when: item is undefined + with_items: + - "{{ conjur_host_factory_token }}" + when: not conjurized diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tasks/install.yml b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tasks/install.yml new file mode 100644 index 000000000..a2fc9993f --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tasks/install.yml @@ -0,0 +1,5 @@ +--- +- import_tasks: identity_check.yml # registers variable 'conjurized' +- import_tasks: identity.yml +- import_tasks: summon.yml +- import_tasks: summon-conjur.yml diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tasks/main.yml b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tasks/main.yml new file mode 100644 index 000000000..d7b94878f --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tasks/main.yml @@ -0,0 +1,6 @@ +--- +- import_tasks: install.yml + when: state|default('present') == "present" + +- import_tasks: uninstall.yml + when: state|default('present') == "absent" diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tasks/summon-conjur.yml b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tasks/summon-conjur.yml new file mode 100644 index 000000000..2e003cd45 --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tasks/summon-conjur.yml @@ -0,0 +1,13 @@ +--- +- name: Create folder for Summon-Conjur to be installed into + file: + path: /usr/local/lib/summon + state: directory + recurse: yes + +- name: Download and unpack Summon-Conjur + unarchive: + src: https://github.com/cyberark/summon-conjur/releases/download/v{{ summon_conjur.version }}/summon-conjur-{{ summon_conjur.os }}.tar.gz + dest: /usr/local/lib/summon + remote_src: yes + creates: /usr/local/lib/summon/summon-conjur diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tasks/summon.yml b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tasks/summon.yml new file mode 100644 index 000000000..98ae0b82a --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tasks/summon.yml @@ -0,0 +1,7 @@ +--- +- name: Download and unpack Summon + unarchive: + src: https://github.com/cyberark/summon/releases/download/v{{ summon.version }}/summon-{{ summon.os }}.tar.gz + dest: /usr/local/bin + remote_src: yes + creates: /usr/local/bin/summon diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tasks/uninstall.yml b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tasks/uninstall.yml new file mode 100644 index 000000000..17f7e3b05 --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tasks/uninstall.yml @@ -0,0 +1,35 @@ +--- +- block: + - name: Clean Summon binary + file: + path: /usr/local/bin/summon + state: absent + - name: Clean Summon library + file: + path: /usr/local/lib/summon/ + state: absent + +- name: Clean conjur.identity + file: + path: /etc/conjur.identity + state: absent + +- name: Clean conjur.conf + file: + path: /etc/conjur.conf + state: absent + +- name: Clean Conjur SSL certificate Symlink + file: + path: /etc/ssl/certs/conjur.crt + state: absent + +- name: Clean Conjur SSL certificate + file: + path: /etc/conjur.pem + state: absent + +- name: Remove group conjur + group: + name: conjur + state: absent diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/templates/conjur.conf.j2 b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/templates/conjur.conf.j2 new file mode 100644 index 000000000..cd1403ce7 --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/templates/conjur.conf.j2 @@ -0,0 +1,5 @@ +account: {{conjur_account}} +appliance_url: {{conjur_appliance_url}} +cert_file: {{conjur_ssl_certificate_path}} +netrc_path: /etc/conjur.identity +plugins: [] diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/templates/conjur.identity.j2 b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/templates/conjur.identity.j2 new file mode 100644 index 000000000..7bde0ff2e --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/templates/conjur.identity.j2 @@ -0,0 +1,3 @@ +machine {{conjur_appliance_url}}/authn + login host/{{conjur_host_name}} + password {{host_factory_response.json.api_key}} diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/.dockerignore b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/.dockerignore new file mode 100644 index 000000000..5ed3ebd29 --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/.dockerignore @@ -0,0 +1 @@ +conjur-intro/
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/.pytest_cache/.gitignore b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/.pytest_cache/.gitignore new file mode 100644 index 000000000..bc1a1f616 --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/.pytest_cache/.gitignore @@ -0,0 +1,2 @@ +# Created by pytest automatically. +* diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/.pytest_cache/CACHEDIR.TAG b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/.pytest_cache/CACHEDIR.TAG new file mode 100644 index 000000000..fce15ad7e --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/.pytest_cache/CACHEDIR.TAG @@ -0,0 +1,4 @@ +Signature: 8a477f597d28d172789f06886806bc55 +# This file is a cache directory tag created by pytest. +# For information about cache directory tags, see: +# https://bford.info/cachedir/spec.html diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/.pytest_cache/README.md b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/.pytest_cache/README.md new file mode 100644 index 000000000..b89018ced --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/.pytest_cache/README.md @@ -0,0 +1,8 @@ +# pytest cache directory # + +This directory contains data from the pytest's cache plugin, +which provides the `--lf` and `--ff` options, as well as the `cache` fixture. + +**Do not** commit this to version control. + +See [the docs](https://docs.pytest.org/en/stable/how-to/cache.html) for more information. diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/.pytest_cache/v/cache/nodeids b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/.pytest_cache/v/cache/nodeids new file mode 100644 index 000000000..ffadcb1be --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/.pytest_cache/v/cache/nodeids @@ -0,0 +1,22 @@ +[ + "test_cases/cleanup-conjur-identity/tests/test_default.py::test_hosts_file[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_centos_1]", + "test_cases/cleanup-conjur-identity/tests/test_default.py::test_hosts_file[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_centos_2]", + "test_cases/cleanup-conjur-identity/tests/test_default.py::test_hosts_file[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_ubuntu_1]", + "test_cases/cleanup-conjur-identity/tests/test_default.py::test_hosts_file[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_ubuntu_2]", + "test_cases/cleanup-conjur-identity/tests/test_default.py::test_is_not_conjurized[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_centos_1]", + "test_cases/cleanup-conjur-identity/tests/test_default.py::test_is_not_conjurized[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_centos_2]", + "test_cases/cleanup-conjur-identity/tests/test_default.py::test_is_not_conjurized[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_ubuntu_1]", + "test_cases/cleanup-conjur-identity/tests/test_default.py::test_is_not_conjurized[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_ubuntu_2]", + "test_cases/configure-conjur-identity/tests/test_default.py::test_hosts_file[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_centos_1]", + "test_cases/configure-conjur-identity/tests/test_default.py::test_hosts_file[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_centos_2]", + "test_cases/configure-conjur-identity/tests/test_default.py::test_hosts_file[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_ubuntu_1]", + "test_cases/configure-conjur-identity/tests/test_default.py::test_hosts_file[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_ubuntu_2]", + "test_cases/configure-conjur-identity/tests/test_default.py::test_is_conjurized[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_centos_1]", + "test_cases/configure-conjur-identity/tests/test_default.py::test_is_conjurized[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_centos_2]", + "test_cases/configure-conjur-identity/tests/test_default.py::test_is_conjurized[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_ubuntu_1]", + "test_cases/configure-conjur-identity/tests/test_default.py::test_is_conjurized[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_ubuntu_2]", + "test_cases/configure-conjur-identity/tests/test_default.py::test_retrieve_secret_with_summon[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_centos_1]", + "test_cases/configure-conjur-identity/tests/test_default.py::test_retrieve_secret_with_summon[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_centos_2]", + "test_cases/configure-conjur-identity/tests/test_default.py::test_retrieve_secret_with_summon[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_ubuntu_1]", + "test_cases/configure-conjur-identity/tests/test_default.py::test_retrieve_secret_with_summon[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_ubuntu_2]" +]
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/.pytest_cache/v/cache/stepwise b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/.pytest_cache/v/cache/stepwise new file mode 100644 index 000000000..0637a088a --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/.pytest_cache/v/cache/stepwise @@ -0,0 +1 @@ +[]
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/Dockerfile b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/Dockerfile new file mode 100644 index 000000000..3985b12ad --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/Dockerfile @@ -0,0 +1,39 @@ +FROM ubuntu:20.04 + +ENV DEBIAN_FRONTEND=noninteractive + +WORKDIR /cyberark + +# install python 3 +RUN apt-get update && \ + apt-get install -y python3-pip && \ + pip3 install --upgrade pip + +ARG ANSIBLE_VERSION +# install ansible and its test tool +RUN pip3 install ansible==${ANSIBLE_VERSION}.* pytest-testinfra + +# install docker installation requirements +RUN apt-get update && \ + apt-get install -y apt-transport-https \ + ca-certificates \ + curl \ + software-properties-common + +# install docker +RUN curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - +RUN add-apt-repository \ + "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ + $(lsb_release -cs) \ + stable" + +RUN apt-get update && \ + apt-get -y install docker-ce + +# NOTE: Everything above is copied from REPO_ROOT/tests/conjur_variable/Dockerfile. It defines a +# standard container image for running ansible tests + +# install ruby +RUN apt-get update && apt-get install -y gcc build-essential +RUN apt-add-repository -y ppa:brightbox/ruby-ng && apt-get update && apt-get install -y ruby2.7 ruby2.7-dev +RUN gem install conjur-cli
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/Dockerfile_nginx b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/Dockerfile_nginx new file mode 100644 index 000000000..d9f18c7bd --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/Dockerfile_nginx @@ -0,0 +1,16 @@ +FROM nginx:1.13.3 + +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y iputils-ping procps openssl && \ + rm -rf /var/lib/apt/lists/* + +WORKDIR /etc/nginx/ + +COPY proxy/ssl.conf /etc/ssl/openssl.cnf + +RUN openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -config /etc/ssl/openssl.cnf -extensions v3_ca \ + -keyout cert.key -out cert.crt + +COPY proxy/default.conf /etc/nginx/conf.d/default.conf diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/ansible.cfg b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/ansible.cfg new file mode 100644 index 000000000..56026b775 --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/ansible.cfg @@ -0,0 +1,8 @@ +[defaults] +display_skipped_hosts = False +host_key_checking = False +error_on_undefined_vars = True +timeout = 60 +inventory = inventory.tmp +roles_path = /cyberark +remote_tmp = /tmp diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/docker-compose.yml b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/docker-compose.yml new file mode 100644 index 000000000..832655687 --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/docker-compose.yml @@ -0,0 +1,84 @@ +version: '3' +services: + ansible: + build: + context: . + dockerfile: Dockerfile + args: + ANSIBLE_VERSION: ${ANSIBLE_VERSION} + command: /bin/sleep 1d + environment: + CONJUR_APPLIANCE_URL: ${CONJUR_APPLIANCE_URL} + CONJUR_ACCOUNT: ${CONJUR_ACCOUNT} + CONJUR_AUTHN_LOGIN: ${CONJUR_AUTHN_LOGIN} + CONJUR_AUTHN_API_KEY: ${ANSIBLE_CONJUR_AUTHN_API_KEY} + CONJUR_CUSTOM_AUTHN_API_KEY: ${CUSTOM_CONJUR_AUTHN_API_KEY} + COMPOSE_PROJECT_NAME: ${COMPOSE_PROJECT_NAME} + # NOTE: Explicitly setting the ANSIBLE_CONFIG envvar avoids Ansible ignoring + # the configuration because it is in a world-writable working directory, + # see https://docs.ansible.com/ansible/latest/reference_appendices/config.html#avoiding-security-risks-with-ansible-cfg-in-the-current-directory. + ANSIBLE_CONFIG: ./ansible.cfg + networks: + - "${DOCKER_NETWORK}" + volumes: + - ..:/cyberark/cyberark.conjur.conjur-host-identity/ + - .:/cyberark/tests/ + - /var/run/docker.sock:/var/run/docker.sock + pg: + image: postgres:9.3 + + conjur: + image: cyberark/conjur + command: server -a cucumber -p 3000 + environment: + CONJUR_APPLIANCE_URL: http://localhost:3000 + DATABASE_URL: postgres://postgres@pg/postgres + CONJUR_DATA_KEY: "W0BuL8iTr/7QvtjIluJbrb5LDAnmXzmcpxkqihO3dXA=" + networks: + - default + links: + - pg + + conjur_cli: + image: cyberark/conjur-cli:5-latest + entrypoint: [] + command: sleep infinity + environment: + CONJUR_APPLIANCE_URL: http://conjur:3000 + CONJUR_ACCOUNT: cucumber + CONJUR_AUTHN_LOGIN: admin + CONJUR_AUTHN_API_KEY: ${CLI_CONJUR_AUTHN_API_KEY} + volumes: + - ./policy:/policy + links: + - conjur + + test_app_ubuntu: + build: ./test_app_ubuntu + entrypoint: sleep + command: infinity + networks: + - "${DOCKER_NETWORK}" + + test_app_centos: + build: ./test_app_centos + entrypoint: sleep + command: infinity + networks: + - "${DOCKER_NETWORK}" + + conjur-proxy-nginx: + build: + context: . + dockerfile: Dockerfile_nginx + entrypoint: nginx-debug -g 'daemon off;' + environment: + TERM: xterm + depends_on: + - conjur + - conjur_cli + +networks: + dap_net: + name: dap_net + external: true diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/inventory-playbook-v2.yml b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/inventory-playbook-v2.yml new file mode 100644 index 000000000..d47081cbe --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/inventory-playbook-v2.yml @@ -0,0 +1,6 @@ +--- +- name: Compile inventory template locally + hosts: localhost + tasks: + - name: compile inventory template + template: src=inventory-v2.j2 dest=/cyberark/tests/inventory.tmp diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/inventory-playbook.yml b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/inventory-playbook.yml new file mode 100644 index 000000000..91d44fcb8 --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/inventory-playbook.yml @@ -0,0 +1,6 @@ +--- +- name: Compile inventory template locally + hosts: localhost + tasks: + - name: compile inventory template + template: src=inventory.j2 dest=/cyberark/tests/inventory.tmp diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/inventory-v2.j2 b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/inventory-v2.j2 new file mode 100644 index 000000000..4a004fb13 --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/inventory-v2.j2 @@ -0,0 +1,6 @@ +[testapp] +{{ lookup('env','COMPOSE_PROJECT_NAME') }}-test_app_ubuntu-[1:2] ansible_connection=docker +{{ lookup('env','COMPOSE_PROJECT_NAME') }}-test_app_centos-[1:2] ansible_connection=docker + +[ansible] +{{ lookup('env','COMPOSE_PROJECT_NAME') }}-ansible-1 ansible_connection=docker diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/inventory.j2 b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/inventory.j2 new file mode 100644 index 000000000..62d48ef82 --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/inventory.j2 @@ -0,0 +1,6 @@ +[testapp] +{{ lookup('env','COMPOSE_PROJECT_NAME') }}_test_app_ubuntu_[1:2] ansible_connection=docker +{{ lookup('env','COMPOSE_PROJECT_NAME') }}_test_app_centos_[1:2] ansible_connection=docker + +[ansible] +{{ lookup('env','COMPOSE_PROJECT_NAME') }}_ansible_1 ansible_connection=docker diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/junit/cleanup-conjur-identity b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/junit/cleanup-conjur-identity new file mode 100644 index 000000000..d1372eaea --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/junit/cleanup-conjur-identity @@ -0,0 +1 @@ +<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="8" time="4.102" timestamp="2022-09-09T15:12:54.260298" hostname="3ef34ba116db"><testcase classname="test_cases.cleanup-conjur-identity.tests.test_default" name="test_hosts_file[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_centos_1]" time="0.681" /><testcase classname="test_cases.cleanup-conjur-identity.tests.test_default" name="test_is_not_conjurized[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_centos_1]" time="0.198" /><testcase classname="test_cases.cleanup-conjur-identity.tests.test_default" name="test_hosts_file[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_centos_2]" time="0.687" /><testcase classname="test_cases.cleanup-conjur-identity.tests.test_default" name="test_is_not_conjurized[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_centos_2]" time="0.201" /><testcase classname="test_cases.cleanup-conjur-identity.tests.test_default" name="test_hosts_file[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_ubuntu_1]" time="0.698" /><testcase classname="test_cases.cleanup-conjur-identity.tests.test_default" name="test_is_not_conjurized[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_ubuntu_1]" time="0.213" /><testcase classname="test_cases.cleanup-conjur-identity.tests.test_default" name="test_hosts_file[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_ubuntu_2]" time="0.702" /><testcase classname="test_cases.cleanup-conjur-identity.tests.test_default" name="test_is_not_conjurized[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_ubuntu_2]" time="0.190" /></testsuite></testsuites>
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/junit/configure-conjur-identity b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/junit/configure-conjur-identity new file mode 100644 index 000000000..d982cbaa2 --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/junit/configure-conjur-identity @@ -0,0 +1 @@ +<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="12" time="5.511" timestamp="2022-09-09T15:14:31.194874" hostname="3ef34ba116db"><testcase classname="test_cases.configure-conjur-identity.tests.test_default" name="test_hosts_file[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_centos_1]" time="0.691" /><testcase classname="test_cases.configure-conjur-identity.tests.test_default" name="test_is_conjurized[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_centos_1]" time="0.412" /><testcase classname="test_cases.configure-conjur-identity.tests.test_default" name="test_retrieve_secret_with_summon[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_centos_1]" time="0.133" /><testcase classname="test_cases.configure-conjur-identity.tests.test_default" name="test_hosts_file[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_centos_2]" time="0.710" /><testcase classname="test_cases.configure-conjur-identity.tests.test_default" name="test_is_conjurized[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_centos_2]" time="0.384" /><testcase classname="test_cases.configure-conjur-identity.tests.test_default" name="test_retrieve_secret_with_summon[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_centos_2]" time="0.126" /><testcase classname="test_cases.configure-conjur-identity.tests.test_default" name="test_hosts_file[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_ubuntu_1]" time="0.727" /><testcase classname="test_cases.configure-conjur-identity.tests.test_default" name="test_is_conjurized[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_ubuntu_1]" time="0.383" /><testcase classname="test_cases.configure-conjur-identity.tests.test_default" name="test_retrieve_secret_with_summon[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_ubuntu_1]" time="0.155" /><testcase classname="test_cases.configure-conjur-identity.tests.test_default" name="test_hosts_file[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_ubuntu_2]" time="0.719" /><testcase classname="test_cases.configure-conjur-identity.tests.test_default" name="test_is_conjurized[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_ubuntu_2]" time="0.387" /><testcase classname="test_cases.configure-conjur-identity.tests.test_default" name="test_retrieve_secret_with_summon[docker://jenkinscyberarkansibleconjurcollectionv1201conjurhostidentity_test_app_ubuntu_2]" time="0.143" /></testsuite></testsuites>
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/policy/root.yml b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/policy/root.yml new file mode 100644 index 000000000..0309cf702 --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/policy/root.yml @@ -0,0 +1,32 @@ +--- +- !policy + id: ansible + annotations: + description: Policy for Ansible master and remote hosts + body: + + - !host + id: ansible-master + annotations: + description: Host for running Ansible on remote targets + + - !layer &remote_hosts_layer + id: remote_hosts + annotations: + description: Layer for Ansible remote hosts + + - !host-factory + id: ansible-factory + annotations: + description: Factory to create new hosts for ansible + layer: [ *remote_hosts_layer ] + + - !variable + id: target-password + annotations: + description: Password needed by the Ansible remote machine + + - !permit + role: *remote_hosts_layer + privileges: [ execute ] + resources: [ !variable target-password ] diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/proxy/default.conf b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/proxy/default.conf new file mode 100644 index 000000000..db2153a71 --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/proxy/default.conf @@ -0,0 +1,33 @@ +server { + listen 80; + return 301 https://conjur$request_uri; +} + +server { + listen 443; + server_name localhost; + ssl_certificate /etc/nginx/cert.crt; + ssl_certificate_key /etc/nginx/cert.key; + + ssl on; + ssl_session_cache builtin:1000 shared:SSL:10m; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4; + ssl_prefer_server_ciphers on; + + access_log /var/log/nginx/access.log; + + location / { + proxy_pass http://conjur:3000; + } + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + +} diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/proxy/ssl.conf b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/proxy/ssl.conf new file mode 100644 index 000000000..e78716b27 --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/proxy/ssl.conf @@ -0,0 +1,39 @@ +[req] +default_bits = 2048 +prompt = no +default_md = sha256 +req_extensions = req_ext +distinguished_name = dn +x509_extensions = v3_ca # The extentions to add to the self signed cert +req_extensions = v3_req +x509_extensions = usr_cert + +[ dn ] +C=IL +ST=Israel +L=TLV +O=Onyx +OU=CyberArk +CN=conjur-proxy-nginx + +[ usr_cert ] +basicConstraints=CA:FALSE +nsCertType = client, server, email +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = serverAuth, clientAuth, codeSigning, emailProtection +nsComment = "OpenSSL Generated Certificate" +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +[ v3_req ] +extendedKeyUsage = serverAuth, clientAuth, codeSigning, emailProtection +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +[ v3_ca ] +subjectAltName = @alt_names + +[ alt_names ] +DNS.1 = localhost +DNS.2 = conjur-proxy-nginx +IP.1 = 127.0.0.1 diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test.sh b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test.sh new file mode 100755 index 000000000..9a54cb8b9 --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test.sh @@ -0,0 +1,249 @@ +#!/bin/bash -eu + +set -o pipefail + +# normalises project name by filtering non alphanumeric characters and transforming to lowercase +declare -x COMPOSE_PROJECT_NAME='' +declare -x ENTERPRISE_PROJECT='conjur-intro-host' +declare -x ANSIBLE_PROJECT='' + +declare -x ANSIBLE_CONJUR_AUTHN_API_KEY='' +declare -x CLI_CONJUR_AUTHN_API_KEY='' +declare -x DOCKER_NETWORK="default" +declare -x ANSIBLE_VERSION="${ANSIBLE_VERSION:-6}" + +declare cli_cid='' +declare ansible_cid='' +declare enterprise='false' +declare test_dir='' + + ANSIBLE_PROJECT=$(echo "${BUILD_TAG:-ansible-plugin-testing}-conjur-host-identity" | sed -e 's/[^[:alnum:]]//g' | tr '[:upper:]' '[:lower:]') + test_dir="$(pwd)" + +function clean { + echo 'Removing test environment' + echo '---' + + # Escape conjur-intro dir if Enterprise setup fails + cd "${test_dir}" + + if [[ -d conjur-intro ]]; then + pushd conjur-intro + COMPOSE_PROJECT_NAME="${ENTERPRISE_PROJECT}" + ./bin/dap --stop + popd + rm -rf conjur-intro + fi + + COMPOSE_PROJECT_NAME="${ANSIBLE_PROJECT}" + docker-compose down -v + rm -rf inventory.tmp \ + conjur.pem +} +function finish { + rv=$? + clean || true + exit $rv +} +trap finish EXIT + +while getopts 'e' flag; do + case "${flag}" in + e) enterprise="true" ;; + *) exit 1 ;; + esac +done + +clean + +function setup_admin_api_key { + if [[ "$enterprise" == "true" ]]; then + docker exec "${cli_cid}" \ + conjur user rotate_api_key + else + docker-compose exec -T conjur \ + conjurctl role retrieve-key "${CONJUR_ACCOUNT}:user:admin" + fi +} + +function setup_ansible_api_key { + docker exec "${cli_cid}" \ + conjur host rotate_api_key --host ansible/ansible-master +} + +function hf_token { + docker exec "${cli_cid}" bash -c "conjur hostfactory tokens create --duration-days=5 ansible/ansible-factory | jq -r '.[0].token'" +} + +function setup_conjur_resources { + echo "---- setting up conjur ----" + policy_path="root.yml" + if [[ "${enterprise}" == "false" ]]; then + policy_path="/policy/${policy_path}" + fi + + docker exec "${cli_cid}" bash -ec " + conjur policy load root ${policy_path} + conjur variable values add ansible/target-password target_secret_password + " +} + +function run_test_cases { + for test_case in test_cases/*; do + teardown_and_setup + run_test_case "$(basename -- "$test_case")" + done +} + +function run_test_case { + echo "---- testing ${test_case} ----" + local test_case=$1 + if [ -n "$test_case" ]; then + docker exec "${ansible_cid}" \ + env HFTOKEN="$(hf_token)" \ + env CONJUR_ACCOUNT="${CONJUR_ACCOUNT}" \ + env CONJUR_APPLIANCE_URL="${CONJUR_APPLIANCE_URL}" \ + bash -ec " + cd tests + ansible-playbook test_cases/${test_case}/playbook.yml + " + if [ -d "${test_dir}/test_cases/${test_case}/tests/" ]; then + docker exec "${ansible_cid}" bash -ec " + cd tests + py.test --junitxml=./junit/${test_case} --connection docker -v test_cases/${test_case}/tests/test_default.py + " + fi + else + echo ERROR: run_test called with no argument 1>&2 + exit 1 + fi +} + +function teardown_and_setup { + docker-compose up -d --force-recreate --scale test_app_ubuntu=2 test_app_ubuntu + docker-compose up -d --force-recreate --scale test_app_centos=2 test_app_centos +} + +function wait_for_server { + # shellcheck disable=SC2016 + docker exec "${cli_cid}" bash -ec ' + for i in $( seq 20 ); do + curl -o /dev/null -fs -X OPTIONS ${CONJUR_APPLIANCE_URL} > /dev/null && echo "server is up" && break + echo "." + sleep 2 + done + ' +} + +function fetch_ssl_cert { + echo "Fetching SSL certs" + service_id="conjur-proxy-nginx" + cert_path="cert.crt" + if [[ "${enterprise}" == "true" ]]; then + service_id="conjur-master.mycompany.local" + cert_path="/etc/ssl/certs/ca.pem" + fi + + (docker-compose exec -T "${service_id}" cat "${cert_path}") > conjur.pem +} + +function generate_inventory { + # Use a different inventory file for docker-compose v1 and v2 or later + playbook_file="inventory-playbook-v2.yml" + compose_ver=$(docker-compose version --short) + if [[ $compose_ver == "1"* ]]; then + playbook_file="inventory-playbook.yml" + fi + + # uses .j2 template to generate inventory prepended with COMPOSE_PROJECT_NAME + docker-compose exec -T ansible bash -ec " + cd tests + ansible-playbook $playbook_file + " + + cat inventory.tmp +} + +function setup_conjur_open_source() { + docker-compose up -d --build + + cli_cid="$(docker-compose ps -q conjur_cli)" + + fetch_ssl_cert + wait_for_server + + echo "Recreating Conjur CLI with admin credentials" + CLI_CONJUR_AUTHN_API_KEY=$(setup_admin_api_key) + docker-compose up -d conjur_cli + cli_cid=$(docker-compose ps -q conjur_cli) + + setup_conjur_resources +} + +function setup_conjur_enterprise() { + git clone --single-branch --branch main https://github.com/conjurdemos/conjur-intro.git + pushd ./conjur-intro + + echo "Provisioning Enterprise leader and follower" + ./bin/dap --provision-master + ./bin/dap --provision-follower + + cp ../policy/root.yml . + + # Run 'sleep infinity' in the CLI container, so the scripts + # have access to an alive and authenticated CLI until the script terminates + cli_cid="$(docker-compose run -d \ + -w /src/cli \ + --entrypoint sleep client infinity)" + + echo "Authenticate Conjur CLI container" + docker exec "${cli_cid}" \ + /bin/bash -c " + if [ ! -e /root/conjur-demo.pem ]; then + yes 'yes' | conjur init -u ${CONJUR_APPLIANCE_URL} -a ${CONJUR_ACCOUNT} + fi + conjur authn login -u admin -p MySecretP@ss1 + hostname -I + " + + fetch_ssl_cert + setup_conjur_resources + + echo "Relocate credential files" + mv conjur.pem ../. + popd +} + +function main() { + if [[ "${enterprise}" == "true" ]]; then + echo "Deploying Conjur Enterprise" + + export DOCKER_NETWORK="dap_net" + export CONJUR_APPLIANCE_URL="https://conjur-master.mycompany.local" + export CONJUR_ACCOUNT="demo" + COMPOSE_PROJECT_NAME="${ENTERPRISE_PROJECT}" + DOCKER_NETWORK="dap_net" + + setup_conjur_enterprise + else + echo "Deploying Conjur Open Source" + + export CONJUR_APPLIANCE_URL="https://conjur-proxy-nginx" + export CONJUR_ACCOUNT="cucumber" + COMPOSE_PROJECT_NAME="${ANSIBLE_PROJECT}" + + setup_conjur_open_source + fi + + echo "Preparing Ansible for test run" + COMPOSE_PROJECT_NAME="${ANSIBLE_PROJECT}" + ANSIBLE_CONJUR_AUTHN_API_KEY=$(setup_ansible_api_key) + docker-compose up -d ansible + ansible_cid=$(docker-compose ps -q ansible) + generate_inventory + + echo "Running tests" + run_test_cases +} + +main
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test_app_centos/Dockerfile b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test_app_centos/Dockerfile new file mode 100644 index 000000000..ee474e7bf --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test_app_centos/Dockerfile @@ -0,0 +1,4 @@ +FROM centos:7 + +# Install Python so Ansible can run against node +RUN yum update -y && yum install -y python3 diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test_app_ubuntu/Dockerfile b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test_app_ubuntu/Dockerfile new file mode 100644 index 000000000..ce919aa57 --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test_app_ubuntu/Dockerfile @@ -0,0 +1,4 @@ +FROM ubuntu:20.04 + +# Install Python so Ansible can run against node +RUN apt-get update -y && apt-get install -y python3-minimal diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test_cases/cleanup-conjur-identity/playbook.yml b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test_cases/cleanup-conjur-identity/playbook.yml new file mode 100644 index 000000000..dfdbe2804 --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test_cases/cleanup-conjur-identity/playbook.yml @@ -0,0 +1,17 @@ +--- +- name: Configuring Conjur identity on remote hosts + hosts: testapp + roles: + - role: "cyberark.conjur.conjur-host-identity" + conjur_account: "{{lookup('env', 'CONJUR_ACCOUNT')}}" + conjur_appliance_url: "{{lookup('env', 'CONJUR_APPLIANCE_URL')}}" + conjur_host_factory_token: "{{lookup('env', 'HFTOKEN')}}" + conjur_host_name: "conjur_{{ ansible_hostname }}" + conjur_ssl_certificate: "{{lookup('file', '../../conjur.pem')}}" + conjur_validate_certs: yes + +- name: Revoke Conjur identity from remote hosts + hosts: testapp + roles: + - role: "cyberark.conjur.conjur-host-identity" + state: absent diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test_cases/cleanup-conjur-identity/tests/test_default.py b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test_cases/cleanup-conjur-identity/tests/test_default.py new file mode 100644 index 000000000..9139ea87a --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test_cases/cleanup-conjur-identity/tests/test_default.py @@ -0,0 +1,23 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import testinfra.utils.ansible_runner + +testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( + '/cyberark/tests/inventory.tmp').get_hosts('testapp') + + +def test_hosts_file(host): + f = host.file('/etc/hosts') + + assert f.exists + assert f.user == 'root' + assert f.group == 'root' + + +def test_is_not_conjurized(host): + identity_file = host.file('/etc/conjur.identity') + assert not identity_file.exists + + conf_file = host.file('/etc/conjur.conf') + assert not conf_file.exists diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test_cases/configure-conjur-identity/playbook.yml b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test_cases/configure-conjur-identity/playbook.yml new file mode 100644 index 000000000..2f1ec4556 --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test_cases/configure-conjur-identity/playbook.yml @@ -0,0 +1,11 @@ +--- +- name: Configuring Conjur identity on remote hosts + hosts: testapp + roles: + - role: "cyberark.conjur.conjur-host-identity" + conjur_account: "{{lookup('env', 'CONJUR_ACCOUNT')}}" + conjur_appliance_url: "{{lookup('env', 'CONJUR_APPLIANCE_URL')}}" + conjur_host_factory_token: "{{lookup('env', 'HFTOKEN')}}" + conjur_host_name: "conjur_{{ ansible_hostname }}" + conjur_ssl_certificate: "{{lookup('file', '../../conjur.pem')}}" + conjur_validate_certs: yes diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test_cases/configure-conjur-identity/tests/test_default.py b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test_cases/configure-conjur-identity/tests/test_default.py new file mode 100644 index 000000000..80513230a --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test_cases/configure-conjur-identity/tests/test_default.py @@ -0,0 +1,33 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import testinfra.utils.ansible_runner + +testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( + '/cyberark/tests/inventory.tmp').get_hosts('testapp') + + +def test_hosts_file(host): + f = host.file('/etc/hosts') + + assert f.exists + assert f.user == 'root' + assert f.group == 'root' + + +def test_is_conjurized(host): + identity_file = host.file('/etc/conjur.identity') + + assert identity_file.exists + assert identity_file.user == 'root' + + conf_file = host.file('/etc/conjur.conf') + + assert conf_file.exists + assert conf_file.user == 'root' + + +def test_retrieve_secret_with_summon(host): + result = host.check_output("summon --yaml 'DB_USERNAME: !var ansible/target-password' bash -c 'printenv DB_USERNAME'", shell=True) + + assert result == "target_secret_password" diff --git a/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test_cases/misconfig-conjur-identity/playbook.yml b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test_cases/misconfig-conjur-identity/playbook.yml new file mode 100644 index 000000000..2ae9ec144 --- /dev/null +++ b/ansible_collections/cyberark/conjur/roles/conjur_host_identity/tests/test_cases/misconfig-conjur-identity/playbook.yml @@ -0,0 +1,22 @@ +--- +- name: Configuring Conjur identity on remote hosts fails when missing required variable + hosts: testapp + tasks: + - name: Attempt to configure Conjur identity + block: + - import_role: + name: "cyberark.conjur.conjur-host-identity" + vars: + conjur_account: cucumber + # conjur_appliance_url: "https://conjur-proxy-nginx" + conjur_host_factory_token: "{{lookup('env', 'HFTOKEN')}}" + conjur_host_name: "conjur_{{ ansible_hostname }}" + conjur_ssl_certificate: "{{lookup('file', '../../conjur.pem')}}" + conjur_validate_certs: yes + rescue: + - name: Confirm Role setup fails + assert: + that: ansible_failed_result.failed == true + - name: Confirm error message + assert: + that: ansible_failed_result.msg == "'conjur_appliance_url' is undefined" diff --git a/ansible_collections/cyberark/conjur/secrets.yml b/ansible_collections/cyberark/conjur/secrets.yml new file mode 100644 index 000000000..87c9771bf --- /dev/null +++ b/ansible_collections/cyberark/conjur/secrets.yml @@ -0,0 +1,2 @@ +--- +GALAXY_API_KEY: !var ecosystems/ansible/galaxy/api-key diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/.dockerignore b/ansible_collections/cyberark/conjur/tests/conjur_variable/.dockerignore new file mode 100644 index 000000000..5ed3ebd29 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/.dockerignore @@ -0,0 +1 @@ +conjur-intro/
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/.pytest_cache/.gitignore b/ansible_collections/cyberark/conjur/tests/conjur_variable/.pytest_cache/.gitignore new file mode 100644 index 000000000..bc1a1f616 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/.pytest_cache/.gitignore @@ -0,0 +1,2 @@ +# Created by pytest automatically. +* diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/.pytest_cache/CACHEDIR.TAG b/ansible_collections/cyberark/conjur/tests/conjur_variable/.pytest_cache/CACHEDIR.TAG new file mode 100644 index 000000000..fce15ad7e --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/.pytest_cache/CACHEDIR.TAG @@ -0,0 +1,4 @@ +Signature: 8a477f597d28d172789f06886806bc55 +# This file is a cache directory tag created by pytest. +# For information about cache directory tags, see: +# https://bford.info/cachedir/spec.html diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/.pytest_cache/README.md b/ansible_collections/cyberark/conjur/tests/conjur_variable/.pytest_cache/README.md new file mode 100644 index 000000000..b89018ced --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/.pytest_cache/README.md @@ -0,0 +1,8 @@ +# pytest cache directory # + +This directory contains data from the pytest's cache plugin, +which provides the `--lf` and `--ff` options, as well as the `cache` fixture. + +**Do not** commit this to version control. + +See [the docs](https://docs.pytest.org/en/stable/how-to/cache.html) for more information. diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/.pytest_cache/v/cache/nodeids b/ansible_collections/cyberark/conjur/tests/conjur_variable/.pytest_cache/v/cache/nodeids new file mode 100644 index 000000000..c6b2f5e13 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/.pytest_cache/v/cache/nodeids @@ -0,0 +1,11 @@ +[ + "test_cases/retrieve-variable-bad-cert-path/tests/test_default.py::test_retrieval_failed[docker://jenkinscyberarkansibleconjurcollectionv1201conjurvariable-ansible]", + "test_cases/retrieve-variable-bad-certs/tests/test_default.py::test_retrieval_failed[docker://jenkinscyberarkansibleconjurcollectionv1201conjurvariable-ansible]", + "test_cases/retrieve-variable-disable-verify-certs/tests/test_default.py::test_retrieved_secret[docker://jenkinscyberarkansibleconjurcollectionv1201conjurvariable-ansible]", + "test_cases/retrieve-variable-into-file/tests/test_default.py::test_retrieved_secret[docker://jenkinscyberarkansibleconjurcollectionv1201conjurvariable-ansible]", + "test_cases/retrieve-variable-no-cert-provided/tests/test_default.py::test_retrieval_failed[docker://jenkinscyberarkansibleconjurcollectionv1201conjurvariable-ansible]", + "test_cases/retrieve-variable-with-authn-token-bad-cert/tests/test_default.py::test_retrieve_secret_failed[docker://jenkinscyberarkansibleconjurcollectionv1201conjurvariable-ansible]", + "test_cases/retrieve-variable-with-authn-token/tests/test_default.py::test_retrieved_secret[docker://jenkinscyberarkansibleconjurcollectionv1201conjurvariable-ansible]", + "test_cases/retrieve-variable-with-spaces-secret/tests/test_default.py::test_retrieved_secret[docker://jenkinscyberarkansibleconjurcollectionv1201conjurvariable-ansible]", + "test_cases/retrieve-variable/tests/test_default.py::test_retrieved_secret[docker://jenkinscyberarkansibleconjurcollectionv1201conjurvariable-ansible]" +]
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/.pytest_cache/v/cache/stepwise b/ansible_collections/cyberark/conjur/tests/conjur_variable/.pytest_cache/v/cache/stepwise new file mode 100644 index 000000000..0637a088a --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/.pytest_cache/v/cache/stepwise @@ -0,0 +1 @@ +[]
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/Dockerfile b/ansible_collections/cyberark/conjur/tests/conjur_variable/Dockerfile new file mode 100644 index 000000000..293ccdca0 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/Dockerfile @@ -0,0 +1,31 @@ +FROM ubuntu:20.04 + +ENV DEBIAN_FRONTEND=noninteractive + +WORKDIR /cyberark + +# install python 3 +RUN apt-get update && \ + apt-get install -y python3-pip && \ + pip3 install --upgrade pip + +ARG ANSIBLE_VERSION +# install ansible and its test tool +RUN pip3 install ansible==${ANSIBLE_VERSION}.* pytest-testinfra + +# install docker installation requirements +RUN apt-get update && \ + apt-get install -y apt-transport-https \ + ca-certificates \ + curl \ + software-properties-common + +# install docker +RUN curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - +RUN add-apt-repository \ + "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ + $(lsb_release -cs) \ + stable" + +RUN apt-get update && \ + apt-get -y install docker-ce diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/Dockerfile_nginx b/ansible_collections/cyberark/conjur/tests/conjur_variable/Dockerfile_nginx new file mode 100644 index 000000000..6f1e28107 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/Dockerfile_nginx @@ -0,0 +1,17 @@ +FROM nginx:1.13.3 + +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y iputils-ping \ + procps \ + openssl && \ + rm -rf /var/lib/apt/lists/* + +WORKDIR /etc/nginx/ + +COPY proxy/ssl.conf /etc/ssl/openssl.cnf +COPY proxy/default.conf /etc/nginx/conf.d/default.conf + +RUN openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -config /etc/ssl/openssl.cnf -extensions v3_ca \ + -keyout cert.key -out cert.crt diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/docker-compose.yml b/ansible_collections/cyberark/conjur/tests/conjur_variable/docker-compose.yml new file mode 100644 index 000000000..01294d94b --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/docker-compose.yml @@ -0,0 +1,67 @@ +version: '3' +services: + ansible: + image: ansiblecontainername + container_name: ${COMPOSE_PROJECT_NAME}-ansible + build: + context: . + dockerfile: Dockerfile + args: + ANSIBLE_VERSION: ${ANSIBLE_VERSION} + entrypoint: sleep + command: infinity + environment: + CONJUR_APPLIANCE_URL: ${CONJUR_APPLIANCE_URL} + CONJUR_ACCOUNT: ${CONJUR_ACCOUNT} + CONJUR_AUTHN_LOGIN: ${CONJUR_AUTHN_LOGIN} + CONJUR_AUTHN_API_KEY: ${ANSIBLE_MASTER_AUTHN_API_KEY} + COMPOSE_PROJECT_NAME: ${COMPOSE_PROJECT_NAME} + networks: + - "${DOCKER_NETWORK}" + volumes: + - ../../plugins:/root/.ansible/plugins + - ../..:/cyberark + - /var/run/docker.sock:/var/run/docker.sock + + pg: + image: postgres:9.4 + environment: + POSTGRES_HOST_AUTH_METHOD: password + POSTGRES_PASSWORD: StrongPass + + conjur: + image: cyberark/conjur + command: server -a cucumber -p 3000 + environment: + DATABASE_URL: postgres://postgres:StrongPass@pg/postgres + CONJUR_DATA_KEY: "W0BuL8iTr/7QvtjIluJbrb5LDAnmXzmcpxkqihO3dXA=" + depends_on: + - pg + + conjur_https: + hostname: conjur-https + build: + context: . + dockerfile: Dockerfile_nginx + entrypoint: nginx-debug -g 'daemon off;' + environment: + TERM: xterm + depends_on: + - conjur + + conjur_cli: + image: cyberark/conjur-cli:5 + entrypoint: sleep + command: infinity + environment: + CONJUR_APPLIANCE_URL: http://conjur:3000 + CONJUR_ACCOUNT: cucumber + CONJUR_AUTHN_LOGIN: admin + CONJUR_AUTHN_API_KEY: ${CONJUR_ADMIN_AUTHN_API_KEY} + volumes: + - ./policy:/policy + +networks: + dap_net: + name: dap_net + external: true diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable b/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable new file mode 100644 index 000000000..08c9ccb5e --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable @@ -0,0 +1 @@ +<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="1" time="0.575" timestamp="2022-09-09T15:07:48.307449" hostname="96989ca8092d"><testcase classname="test_cases.retrieve-variable.tests.test_default" name="test_retrieved_secret[docker://jenkinscyberarkansibleconjurcollectionv1201conjurvariable-ansible]" time="0.530" /></testsuite></testsuites>
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-bad-cert-path b/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-bad-cert-path new file mode 100644 index 000000000..0ed22fad7 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-bad-cert-path @@ -0,0 +1 @@ +<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="1" time="0.458" timestamp="2022-09-09T15:07:51.763243" hostname="96989ca8092d"><testcase classname="test_cases.retrieve-variable-bad-cert-path.tests.test_default" name="test_retrieval_failed[docker://jenkinscyberarkansibleconjurcollectionv1201conjurvariable-ansible]" time="0.422" /></testsuite></testsuites>
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-bad-certs b/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-bad-certs new file mode 100644 index 000000000..6b0b865c1 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-bad-certs @@ -0,0 +1 @@ +<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="1" time="0.459" timestamp="2022-09-09T15:07:55.122204" hostname="96989ca8092d"><testcase classname="test_cases.retrieve-variable-bad-certs.tests.test_default" name="test_retrieval_failed[docker://jenkinscyberarkansibleconjurcollectionv1201conjurvariable-ansible]" time="0.423" /></testsuite></testsuites>
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-disable-verify-certs b/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-disable-verify-certs new file mode 100644 index 000000000..9f1fc6494 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-disable-verify-certs @@ -0,0 +1 @@ +<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="1" time="0.568" timestamp="2022-09-09T15:07:58.851346" hostname="96989ca8092d"><testcase classname="test_cases.retrieve-variable-disable-verify-certs.tests.test_default" name="test_retrieved_secret[docker://jenkinscyberarkansibleconjurcollectionv1201conjurvariable-ansible]" time="0.532" /></testsuite></testsuites>
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-into-file b/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-into-file new file mode 100644 index 000000000..5fcc68f80 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-into-file @@ -0,0 +1 @@ +<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="1" time="0.863" timestamp="2022-09-09T15:08:02.658511" hostname="96989ca8092d"><testcase classname="test_cases.retrieve-variable-into-file.tests.test_default" name="test_retrieved_secret[docker://jenkinscyberarkansibleconjurcollectionv1201conjurvariable-ansible]" time="0.828" /></testsuite></testsuites>
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-no-cert-provided b/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-no-cert-provided new file mode 100644 index 000000000..f1c9029a8 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-no-cert-provided @@ -0,0 +1 @@ +<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="1" time="0.464" timestamp="2022-09-09T15:08:06.406130" hostname="96989ca8092d"><testcase classname="test_cases.retrieve-variable-no-cert-provided.tests.test_default" name="test_retrieval_failed[docker://jenkinscyberarkansibleconjurcollectionv1201conjurvariable-ansible]" time="0.429" /></testsuite></testsuites>
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-with-authn-token b/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-with-authn-token new file mode 100644 index 000000000..407145017 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-with-authn-token @@ -0,0 +1 @@ +<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="1" time="0.551" timestamp="2022-09-09T15:08:10.115226" hostname="96989ca8092d"><testcase classname="test_cases.retrieve-variable-with-authn-token.tests.test_default" name="test_retrieved_secret[docker://jenkinscyberarkansibleconjurcollectionv1201conjurvariable-ansible]" time="0.516" /></testsuite></testsuites>
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-with-authn-token-bad-cert b/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-with-authn-token-bad-cert new file mode 100644 index 000000000..680f3913f --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-with-authn-token-bad-cert @@ -0,0 +1 @@ +<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="1" time="0.460" timestamp="2022-09-09T15:08:13.541799" hostname="96989ca8092d"><testcase classname="test_cases.retrieve-variable-with-authn-token-bad-cert.tests.test_default" name="test_retrieve_secret_failed[docker://jenkinscyberarkansibleconjurcollectionv1201conjurvariable-ansible]" time="0.425" /></testsuite></testsuites>
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-with-spaces-secret b/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-with-spaces-secret new file mode 100644 index 000000000..65e72fecb --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/junit/retrieve-variable-with-spaces-secret @@ -0,0 +1 @@ +<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="1" time="0.571" timestamp="2022-09-09T15:08:17.207877" hostname="96989ca8092d"><testcase classname="test_cases.retrieve-variable-with-spaces-secret.tests.test_default" name="test_retrieved_secret[docker://jenkinscyberarkansibleconjurcollectionv1201conjurvariable-ansible]" time="0.536" /></testsuite></testsuites>
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/policy/root.yml b/ansible_collections/cyberark/conjur/tests/conjur_variable/policy/root.yml new file mode 100644 index 000000000..dbaea73fa --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/policy/root.yml @@ -0,0 +1,21 @@ +--- +- !policy + id: ansible + annotations: + description: Policy for Ansible master + body: + + - !host + id: ansible-master + annotations: + description: Host for running Ansible on remote targets + + - &variables + - !variable test-secret + - !variable test-secret-in-file + - !variable var with spaces + + - !permit + role: !host ansible-master + privileges: [ read, execute ] + resource: *variables diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/proxy/default.conf b/ansible_collections/cyberark/conjur/tests/conjur_variable/proxy/default.conf new file mode 100644 index 000000000..578b3c5f8 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/proxy/default.conf @@ -0,0 +1,29 @@ +server { + listen 80; + return 301 https://conjur$request_uri; +} + +server { + listen 443; + server_name localhost; + ssl_certificate /etc/nginx/cert.crt; + ssl_certificate_key /etc/nginx/cert.key; + + ssl on; + ssl_session_cache builtin:1000 shared:SSL:10m; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4; + ssl_prefer_server_ciphers on; + + access_log /var/log/nginx/access.log; + + location / { + proxy_pass http://conjur:3000; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + +} diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/proxy/ssl.conf b/ansible_collections/cyberark/conjur/tests/conjur_variable/proxy/ssl.conf new file mode 100644 index 000000000..1b11cd755 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/proxy/ssl.conf @@ -0,0 +1,39 @@ +[req] +default_bits = 2048 +prompt = no +default_md = sha256 +req_extensions = req_ext +distinguished_name = dn +x509_extensions = v3_ca # The extentions to add to the self signed cert +req_extensions = v3_req +x509_extensions = usr_cert + +[ dn ] +C=IL +ST=Israel +L=TLV +O=Onyx +OU=CyberArk +CN=conjur-https + +[ usr_cert ] +basicConstraints=CA:FALSE +nsCertType = client, server, email +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = serverAuth, clientAuth, codeSigning, emailProtection +nsComment = "OpenSSL Generated Certificate" +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +[ v3_req ] +extendedKeyUsage = serverAuth, clientAuth, codeSigning, emailProtection +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +[ v3_ca ] +subjectAltName = @alt_names + +[ alt_names ] +DNS.1 = localhost +DNS.2 = conjur-https +IP.1 = 127.0.0.1 diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/pytest.ini b/ansible_collections/cyberark/conjur/tests/conjur_variable/pytest.ini new file mode 100644 index 000000000..fe55d2ed6 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +junit_family=xunit2 diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test.sh b/ansible_collections/cyberark/conjur/tests/conjur_variable/test.sh new file mode 100755 index 000000000..464921b81 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test.sh @@ -0,0 +1,225 @@ +#!/bin/bash -eu + +set -o pipefail + +# normalises project name by filtering non alphanumeric characters and transforming to lowercase +declare -x COMPOSE_PROJECT_NAME='' +declare -x ENTERPRISE_PROJECT='conjur-intro-variable' +declare -x ANSIBLE_PROJECT='' + +declare -x ANSIBLE_MASTER_AUTHN_API_KEY='' +declare -x CONJUR_ADMIN_AUTHN_API_KEY='' +declare -x DOCKER_NETWORK="default" +declare -x ANSIBLE_VERSION="${ANSIBLE_VERSION:-6}" + +ANSIBLE_PROJECT=$(echo "${BUILD_TAG:-ansible-plugin-testing}-conjur-variable" | sed -e 's/[^[:alnum:]]//g' | tr '[:upper:]' '[:lower:]') + +enterprise="false" +cli_cid="" +test_dir="$(pwd)" + +function cleanup { + echo 'Removing test environment' + echo '---' + + # Escape conjur-intro dir if Enterprise setup fails + cd "${test_dir}" + + if [[ -d conjur-intro ]]; then + pushd conjur-intro + COMPOSE_PROJECT_NAME="${ENTERPRISE_PROJECT}" + ./bin/dap --stop + popd + rm -rf conjur-intro + fi + + COMPOSE_PROJECT_NAME="${ANSIBLE_PROJECT}" + docker-compose down -v + rm -f conjur.pem \ + access_token +} +trap cleanup EXIT + +while getopts 'e' flag; do + case "${flag}" in + e) enterprise="true" ;; + *) exit 1 ;; + esac +done + +cleanup + +function wait_for_conjur { + echo "Waiting for Conjur server to come up" + docker-compose exec -T conjur conjurctl wait -r 30 -p 3000 +} + +function fetch_ssl_certs { + echo "Fetching SSL certs" + service_id="conjur_https" + cert_path="cert.crt" + if [[ "${enterprise}" == "true" ]]; then + service_id="conjur-master.mycompany.local" + cert_path="/etc/ssl/certs/ca.pem" + fi + + (docker-compose exec -T "${service_id}" cat "${cert_path}") > conjur.pem +} + +function setup_conjur_resources { + echo "Configuring Conjur via CLI" + + policy_path="root.yml" + if [[ "${enterprise}" == "false" ]]; then + policy_path="/policy/${policy_path}" + fi + + docker exec "${cli_cid}" bash -c " + conjur policy load root ${policy_path} + conjur variable values add ansible/test-secret test_secret_password + conjur variable values add ansible/test-secret-in-file test_secret_in_file_password + conjur variable values add 'ansible/var with spaces' var_with_spaces_secret_password + " +} + +function setup_admin_api_key { + echo "Fetching admin API key" + if [[ "$enterprise" == "true" ]]; then + CONJUR_ADMIN_AUTHN_API_KEY="$(docker exec "${cli_cid}" conjur user rotate_api_key)" + else + CONJUR_ADMIN_AUTHN_API_KEY="$(docker-compose exec -T conjur conjurctl role retrieve-key "${CONJUR_ACCOUNT}":user:admin)" + fi +} + +function setup_ansible_api_key { + echo "Fetching Ansible master host credentials" + ANSIBLE_MASTER_AUTHN_API_KEY="$(docker exec "${cli_cid}" conjur host rotate_api_key --host ansible/ansible-master)" +} + +function setup_access_token { + echo "Get Access Token" + docker exec "${cli_cid}" bash -c " + export CONJUR_AUTHN_LOGIN=host/ansible/ansible-master + export CONJUR_AUTHN_API_KEY=\"$ANSIBLE_MASTER_AUTHN_API_KEY\" + conjur authn authenticate + " > access_token +} + +function setup_conjur_open_source() { + docker-compose up -d --build conjur \ + conjur_https + + wait_for_conjur + fetch_ssl_certs + setup_admin_api_key + + echo "Creating Conjur CLI with admin credentials" + docker-compose up -d conjur_cli + cli_cid="$(docker-compose ps -q conjur_cli)" + + setup_conjur_resources + setup_ansible_api_key + setup_access_token +} + +function setup_conjur_enterprise() { + git clone --single-branch --branch main https://github.com/conjurdemos/conjur-intro.git + pushd ./conjur-intro + + echo "Provisioning Enterprise leader and follower" + ./bin/dap --provision-master + ./bin/dap --provision-follower + + cp ../policy/root.yml . + + # Run 'sleep infinity' in the CLI container, so the scripts + # have access to an alive and authenticated CLI until the script terminates + cli_cid="$(docker-compose run -d \ + -w /src/cli \ + --entrypoint sleep client infinity)" + + echo "Authenticate Conjur CLI container" + docker exec "${cli_cid}" \ + /bin/bash -c " + if [ ! -e /root/conjur-demo.pem ]; then + yes 'yes' | conjur init -u ${CONJUR_APPLIANCE_URL} -a ${CONJUR_ACCOUNT} + fi + conjur authn login -u admin -p MySecretP@ss1 + hostname -I + " + + fetch_ssl_certs + setup_conjur_resources + setup_admin_api_key + setup_ansible_api_key + setup_access_token + + echo "Relocate credential files" + mv conjur.pem ../. + mv access_token ../. + popd +} + +function run_test_cases { + for test_case in test_cases/*; do + run_test_case "$(basename -- "$test_case")" + done +} + +function run_test_case { + local test_case=$1 + echo "---- testing ${test_case} ----" + + if [ -z "$test_case" ]; then + echo ERROR: run_test called with no argument 1>&2 + exit 1 + fi + + docker-compose exec -T ansible bash -exc " + cd tests/conjur_variable + + # If env vars were provided, load them + if [ -e 'test_cases/${test_case}/env' ]; then + . ./test_cases/${test_case}/env + fi + + # You can add -vvvv here for debugging + ansible-playbook 'test_cases/${test_case}/playbook.yml' + + py.test --junitxml='./junit/${test_case}' \ + --connection docker \ + -v 'test_cases/${test_case}/tests/test_default.py' + " +} + +function main() { + if [[ "$enterprise" == "true" ]]; then + echo "Deploying Conjur Enterprise" + + export CONJUR_APPLIANCE_URL="https://conjur-master.mycompany.local" + export CONJUR_ACCOUNT="demo" + COMPOSE_PROJECT_NAME="${ENTERPRISE_PROJECT}" + DOCKER_NETWORK="dap_net" + + setup_conjur_enterprise + else + echo "Deploying Conjur Open Source" + + export CONJUR_APPLIANCE_URL="https://conjur-https" + export CONJUR_ACCOUNT="cucumber" + COMPOSE_PROJECT_NAME="${ANSIBLE_PROJECT}" + + setup_conjur_open_source + fi + + COMPOSE_PROJECT_NAME="${ANSIBLE_PROJECT}" + export CONJUR_AUTHN_LOGIN="host/ansible/ansible-master" + + echo "Preparing Ansible for test run" + docker-compose up -d --build ansible + + echo "Running tests" + run_test_cases +} + +main diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-bad-cert-path/env b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-bad-cert-path/env new file mode 100644 index 000000000..07d7632c0 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-bad-cert-path/env @@ -0,0 +1 @@ +export CONJUR_CERT_FILE=./bad/cert/path diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-bad-cert-path/playbook.yml b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-bad-cert-path/playbook.yml new file mode 100644 index 000000000..516faec41 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-bad-cert-path/playbook.yml @@ -0,0 +1,15 @@ +--- +- name: Retrieve Conjur variable fails with bad cert + hosts: localhost + connection: local + tasks: + - name: Clean artifact path + file: + state: absent + path: /conjur_secrets.txt + + - name: Retrieve Conjur variable with bad cert + vars: + super_secret_key: "{{lookup('conjur_variable', 'ansible/test-secret')}}" + shell: echo "{{super_secret_key}}" > /conjur_secrets.txt + ignore_errors: True diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-bad-cert-path/tests/test_default.py b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-bad-cert-path/tests/test_default.py new file mode 100644 index 000000000..a3f2bbdf3 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-bad-cert-path/tests/test_default.py @@ -0,0 +1,13 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import testinfra.utils.ansible_runner + +testinfra_hosts = [os.environ['COMPOSE_PROJECT_NAME'] + '-ansible'] + + +def test_retrieval_failed(host): + secrets_file = host.file('/conjur_secrets.txt') + + assert not secrets_file.exists diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-bad-certs/bad-cert.pem b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-bad-certs/bad-cert.pem new file mode 100644 index 000000000..a3831e0ce --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-bad-certs/bad-cert.pem @@ -0,0 +1,41 @@ +-----BEGIN CERTIFICATE----- +MIIDqTCCApGgAwIBAgIQN/xr5EKbXSqdyhGyGBWCizANBgkqhkiG9w0BAQsFADAY +MRYwFAYDVQQDEw1jb25qdXItb3NzLWNhMB4XDTIwMDYxMjIwNTYzOVoXDTIxMDYx +MjIwNTYzOVowGzEZMBcGA1UEAxMQY29uanVyLm15b3JnLmNvbTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAOewPeFNcuLuE1tgpINSm4EajDXC+C6qsPhn +Jf3PhDFCpAEHckx1BVK01kFUdC6FOI6JEuI6pFnV1Tb9+WFYTX6wcGBAy90i+K9u +Xcjqb3sz5O3p6MjKL4xVvT4TxllT8cslNJix8gFrTYSvBFaUqCioGJAgyQyJ4SV+ +Tm/bXu/KdtfJQaLie+J5Xz/28220hC5NYlIjhMg5YgtRB7JjCj5bPe3PYykN5m8i +INWEw20EW+54YKNs5RDFNKzXOqF5h6Mrc/RcE1rbCGNOqveQ43DTFUgS4rVF8T8S +juO+LGfso2w6YvO7pT+ob9GxZytZXQSxrOXe8LpU4jSDS5g0+cECAwEAAaOB6zCB +6DAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC +MAwGA1UdEwEB/wQCMAAwgagGA1UdEQSBoDCBnYIQY29uanVyLm15b3JnLmNvbYIK +Y29uanVyLW9zc4IhY29uanVyLW9zcy50ZXN0LWRlcGxveS1kZWZhdWx0cy0xgiVj +b25qdXItb3NzLnRlc3QtZGVwbG95LWRlZmF1bHRzLTEuc3ZjgjNjb25qdXItb3Nz +LnRlc3QtZGVwbG95LWRlZmF1bHRzLTEuc3ZjLmNsdXN0ZXIubG9jYWwwDQYJKoZI +hvcNAQELBQADggEBAIdtOYlIdnojqVBbHGKPAJS8ZWDUm97W1KajZ6QGy6X7fD66 +tOb0QNhzLwbi4HQsuNR0rpWa48Z/sN1E6V5WlfG8pTrNRjqAc/sdobERMMS+rhtu +RfLu1UbYCRLXYIAQFIQFtGDVNXnuvhkCwDz6PV4rniml24qhnGeL3+8ZkW6m5+8u +XIt4Wq9otR3qj/Jx8eUg9eY/7RKgqCClP5Eg4szO0LaqQNblSR+3OgcGxmdTaJQt +BlXRIH0CPpX+3p3mO4zfKIDRHZ0tueWLqOVRARyx9n9qBbhlpBJnecS19PNUAGVa +10H1XkmpVXdadcV/8vBhjEpeq4cVgguS5oozG7Y= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIC/TCCAeWgAwIBAgIRAMUGxDd4cpSs7m9hvzTHoi4wDQYJKoZIhvcNAQELBQAw +GDEWMBQGA1UEAxMNY29uanVyLW9zcy1jYTAeFw0yMDA2MTIyMDU2MzhaFw0yMTA2 +MTIyMDU2MzhaMBgxFjAUBgNVBAMTDWNvbmp1ci1vc3MtY2EwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC111bZxfhVHPerwHXvc+ycgCcI02VtfsrH9Tyk +cmMsbUO6jF5aKBQj4fKSwbOCdXcqYoGwaQHQzOVMCGGruOYrOwfusOg2t2EQ7KIE +KWdP0BpAvASvkKwk/GGfaBqtzy1DvVyl0B8b/4C7tnta21Zs5HFOKo0CE+iX/FUQ +RDjpncE9Zhg2E2f1eCef4D+h2JJLtZPLOUZIUs0IMBPqQiL7iNSfY+e8dy6dRC9v +AhyFLULpK34aPm1DqwX3rHDPoLJl24sZFo8q9UvpCwj3sqVoABagS32yL/8//LvU +DaR4pgwuyd9sWf/CQkT0OHsHup5CH7xbYB0DSdmRJPeb07lHAgMBAAGjQjBAMA4G +A1UdDwEB/wQEAwICpDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYD +VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEANrAiuLkZGmSQlURpWtvm +PgfPFPKPt1PAqjNwWhzMjHZg2P3UNBUTLeUdo52yJai6/iajlYTRPVvNUqiVnTay +/X9LwWH5EXTKCHagfQh4fYtTSFa12BUBlSP7at3S25pMDOpylb3CxdGe/Oh8S0HZ +gu7MMayFhcGCSJnT+F+JIqwnWkbWPYgHn0VCbBXN+5s7GJWFWwZljQzMCIa/xvwr +xuSX6Lsgai1Abqo1pDJA8RNyxMtn5V8RHgwjQ/BdeodptqZc/kULVDOZ0dkAKxyH +UYfqxxk4Ywc2JSSJYRs/RJpjngGnnLIOHgnruEIDtdOHw2yxAJZ/e7p8y9ThSxRo +5Q== +-----END CERTIFICATE----- diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-bad-certs/env b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-bad-certs/env new file mode 100644 index 000000000..73e6e980a --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-bad-certs/env @@ -0,0 +1 @@ +export CONJUR_CERT_FILE=./test_cases/retrieve-variable-bad-certs/bad_cert.pem diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-bad-certs/playbook.yml b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-bad-certs/playbook.yml new file mode 100644 index 000000000..516faec41 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-bad-certs/playbook.yml @@ -0,0 +1,15 @@ +--- +- name: Retrieve Conjur variable fails with bad cert + hosts: localhost + connection: local + tasks: + - name: Clean artifact path + file: + state: absent + path: /conjur_secrets.txt + + - name: Retrieve Conjur variable with bad cert + vars: + super_secret_key: "{{lookup('conjur_variable', 'ansible/test-secret')}}" + shell: echo "{{super_secret_key}}" > /conjur_secrets.txt + ignore_errors: True diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-bad-certs/tests/test_default.py b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-bad-certs/tests/test_default.py new file mode 100644 index 000000000..a3f2bbdf3 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-bad-certs/tests/test_default.py @@ -0,0 +1,13 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import testinfra.utils.ansible_runner + +testinfra_hosts = [os.environ['COMPOSE_PROJECT_NAME'] + '-ansible'] + + +def test_retrieval_failed(host): + secrets_file = host.file('/conjur_secrets.txt') + + assert not secrets_file.exists diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-disable-verify-certs/playbook.yml b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-disable-verify-certs/playbook.yml new file mode 100644 index 000000000..f1085642f --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-disable-verify-certs/playbook.yml @@ -0,0 +1,14 @@ +--- +- name: Retrieve Conjur variable with disabled cert verification + hosts: localhost + connection: local + tasks: + - name: Clean artifact path + file: + state: absent + path: /conjur_secrets.txt + + - name: Retrieve Conjur variable with disabled cert verification + vars: + super_secret_key: "{{lookup('conjur_variable', 'ansible/test-secret', validate_certs=False)}}" + shell: echo "{{super_secret_key}}" > /conjur_secrets.txt diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-disable-verify-certs/tests/test_default.py b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-disable-verify-certs/tests/test_default.py new file mode 100644 index 000000000..a98ce29e9 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-disable-verify-certs/tests/test_default.py @@ -0,0 +1,17 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import testinfra.utils.ansible_runner + +testinfra_hosts = [os.environ['COMPOSE_PROJECT_NAME'] + '-ansible'] + + +def test_retrieved_secret(host): + secrets_file = host.file('/conjur_secrets.txt') + + assert secrets_file.exists + + result = host.check_output("cat /conjur_secrets.txt", shell=True) + + assert result == "test_secret_password" diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-into-file/env b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-into-file/env new file mode 100644 index 000000000..2363951d1 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-into-file/env @@ -0,0 +1 @@ +export CONJUR_CERT_FILE=./conjur.pem diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-into-file/playbook.yml b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-into-file/playbook.yml new file mode 100644 index 000000000..ef982db4f --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-into-file/playbook.yml @@ -0,0 +1,14 @@ +--- +- name: Retrieve Conjur variable into file + hosts: localhost + connection: local + tasks: + - name: Clean artifact path + file: + state: absent + path: /conjur_secret_path.txt + + - name: Retrieve Conjur variable into file using as_file option + vars: + secret_path: "{{lookup('conjur_variable', 'ansible/test-secret-in-file', as_file=True)}}" + shell: echo -n "{{secret_path}}" > /lookup_output.txt diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-into-file/tests/test_default.py b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-into-file/tests/test_default.py new file mode 100644 index 000000000..5d05f950b --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-into-file/tests/test_default.py @@ -0,0 +1,22 @@ +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +import os +import testinfra.utils.ansible_runner + +testinfra_hosts = [os.environ['COMPOSE_PROJECT_NAME'] + '-ansible'] + + +def test_retrieved_secret(host): + """ + Verify that the as_file parameter makes the lookup plugin return the path to a temporary file + containing the secret. + """ + lookup_output_file = host.file('/lookup_output.txt') + assert lookup_output_file.exists + + secret_file = host.file(lookup_output_file.content_string) + assert secret_file.exists + assert secret_file.mode == 0o600 + assert secret_file.content_string == "test_secret_in_file_password" diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-no-cert-provided/playbook.yml b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-no-cert-provided/playbook.yml new file mode 100644 index 000000000..516faec41 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-no-cert-provided/playbook.yml @@ -0,0 +1,15 @@ +--- +- name: Retrieve Conjur variable fails with bad cert + hosts: localhost + connection: local + tasks: + - name: Clean artifact path + file: + state: absent + path: /conjur_secrets.txt + + - name: Retrieve Conjur variable with bad cert + vars: + super_secret_key: "{{lookup('conjur_variable', 'ansible/test-secret')}}" + shell: echo "{{super_secret_key}}" > /conjur_secrets.txt + ignore_errors: True diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-no-cert-provided/tests/test_default.py b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-no-cert-provided/tests/test_default.py new file mode 100644 index 000000000..a3f2bbdf3 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-no-cert-provided/tests/test_default.py @@ -0,0 +1,13 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import testinfra.utils.ansible_runner + +testinfra_hosts = [os.environ['COMPOSE_PROJECT_NAME'] + '-ansible'] + + +def test_retrieval_failed(host): + secrets_file = host.file('/conjur_secrets.txt') + + assert not secrets_file.exists diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-authn-token-bad-cert/bad-cert.pem b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-authn-token-bad-cert/bad-cert.pem new file mode 100644 index 000000000..a3831e0ce --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-authn-token-bad-cert/bad-cert.pem @@ -0,0 +1,41 @@ +-----BEGIN CERTIFICATE----- +MIIDqTCCApGgAwIBAgIQN/xr5EKbXSqdyhGyGBWCizANBgkqhkiG9w0BAQsFADAY +MRYwFAYDVQQDEw1jb25qdXItb3NzLWNhMB4XDTIwMDYxMjIwNTYzOVoXDTIxMDYx +MjIwNTYzOVowGzEZMBcGA1UEAxMQY29uanVyLm15b3JnLmNvbTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAOewPeFNcuLuE1tgpINSm4EajDXC+C6qsPhn +Jf3PhDFCpAEHckx1BVK01kFUdC6FOI6JEuI6pFnV1Tb9+WFYTX6wcGBAy90i+K9u +Xcjqb3sz5O3p6MjKL4xVvT4TxllT8cslNJix8gFrTYSvBFaUqCioGJAgyQyJ4SV+ +Tm/bXu/KdtfJQaLie+J5Xz/28220hC5NYlIjhMg5YgtRB7JjCj5bPe3PYykN5m8i +INWEw20EW+54YKNs5RDFNKzXOqF5h6Mrc/RcE1rbCGNOqveQ43DTFUgS4rVF8T8S +juO+LGfso2w6YvO7pT+ob9GxZytZXQSxrOXe8LpU4jSDS5g0+cECAwEAAaOB6zCB +6DAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC +MAwGA1UdEwEB/wQCMAAwgagGA1UdEQSBoDCBnYIQY29uanVyLm15b3JnLmNvbYIK +Y29uanVyLW9zc4IhY29uanVyLW9zcy50ZXN0LWRlcGxveS1kZWZhdWx0cy0xgiVj +b25qdXItb3NzLnRlc3QtZGVwbG95LWRlZmF1bHRzLTEuc3ZjgjNjb25qdXItb3Nz +LnRlc3QtZGVwbG95LWRlZmF1bHRzLTEuc3ZjLmNsdXN0ZXIubG9jYWwwDQYJKoZI +hvcNAQELBQADggEBAIdtOYlIdnojqVBbHGKPAJS8ZWDUm97W1KajZ6QGy6X7fD66 +tOb0QNhzLwbi4HQsuNR0rpWa48Z/sN1E6V5WlfG8pTrNRjqAc/sdobERMMS+rhtu +RfLu1UbYCRLXYIAQFIQFtGDVNXnuvhkCwDz6PV4rniml24qhnGeL3+8ZkW6m5+8u +XIt4Wq9otR3qj/Jx8eUg9eY/7RKgqCClP5Eg4szO0LaqQNblSR+3OgcGxmdTaJQt +BlXRIH0CPpX+3p3mO4zfKIDRHZ0tueWLqOVRARyx9n9qBbhlpBJnecS19PNUAGVa +10H1XkmpVXdadcV/8vBhjEpeq4cVgguS5oozG7Y= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIC/TCCAeWgAwIBAgIRAMUGxDd4cpSs7m9hvzTHoi4wDQYJKoZIhvcNAQELBQAw +GDEWMBQGA1UEAxMNY29uanVyLW9zcy1jYTAeFw0yMDA2MTIyMDU2MzhaFw0yMTA2 +MTIyMDU2MzhaMBgxFjAUBgNVBAMTDWNvbmp1ci1vc3MtY2EwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC111bZxfhVHPerwHXvc+ycgCcI02VtfsrH9Tyk +cmMsbUO6jF5aKBQj4fKSwbOCdXcqYoGwaQHQzOVMCGGruOYrOwfusOg2t2EQ7KIE +KWdP0BpAvASvkKwk/GGfaBqtzy1DvVyl0B8b/4C7tnta21Zs5HFOKo0CE+iX/FUQ +RDjpncE9Zhg2E2f1eCef4D+h2JJLtZPLOUZIUs0IMBPqQiL7iNSfY+e8dy6dRC9v +AhyFLULpK34aPm1DqwX3rHDPoLJl24sZFo8q9UvpCwj3sqVoABagS32yL/8//LvU +DaR4pgwuyd9sWf/CQkT0OHsHup5CH7xbYB0DSdmRJPeb07lHAgMBAAGjQjBAMA4G +A1UdDwEB/wQEAwICpDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYD +VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEANrAiuLkZGmSQlURpWtvm +PgfPFPKPt1PAqjNwWhzMjHZg2P3UNBUTLeUdo52yJai6/iajlYTRPVvNUqiVnTay +/X9LwWH5EXTKCHagfQh4fYtTSFa12BUBlSP7at3S25pMDOpylb3CxdGe/Oh8S0HZ +gu7MMayFhcGCSJnT+F+JIqwnWkbWPYgHn0VCbBXN+5s7GJWFWwZljQzMCIa/xvwr +xuSX6Lsgai1Abqo1pDJA8RNyxMtn5V8RHgwjQ/BdeodptqZc/kULVDOZ0dkAKxyH +UYfqxxk4Ywc2JSSJYRs/RJpjngGnnLIOHgnruEIDtdOHw2yxAJZ/e7p8y9ThSxRo +5Q== +-----END CERTIFICATE----- diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-authn-token-bad-cert/env b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-authn-token-bad-cert/env new file mode 100644 index 000000000..b93328faf --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-authn-token-bad-cert/env @@ -0,0 +1,4 @@ +unset CONJUR_AUTHN_API_KEY +unset CONJUR_AUTHN_LOGIN +export CONJUR_AUTHN_TOKEN_FILE=./access_token +export CONJUR_CERT_FILE=./test_cases/retrieve-variable-with-authn-token-bad-cert/bad-cert.pem diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-authn-token-bad-cert/playbook.yml b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-authn-token-bad-cert/playbook.yml new file mode 100644 index 000000000..8423a2c13 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-authn-token-bad-cert/playbook.yml @@ -0,0 +1,15 @@ +--- +- name: Retrieve Conjur variable with authn-token fails with bad cert + hosts: localhost + connection: local + tasks: + - name: Clean artifact path + file: + state: absent + path: /conjur_secrets.txt + + - name: Retrieve Conjur variable with bad cert + vars: + super_secret_key: "{{lookup('conjur_variable', 'ansible/test-secret')}}" + shell: echo "{{super_secret_key}}" > /conjur_secrets.txt + ignore_errors: True diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-authn-token-bad-cert/tests/test_default.py b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-authn-token-bad-cert/tests/test_default.py new file mode 100644 index 000000000..c87b160f4 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-authn-token-bad-cert/tests/test_default.py @@ -0,0 +1,13 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import testinfra.utils.ansible_runner + +testinfra_hosts = [os.environ['COMPOSE_PROJECT_NAME'] + '-ansible'] + + +def test_retrieve_secret_failed(host): + secrets_file = host.file('/conjur_secrets.txt') + + assert not secrets_file.exists diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-authn-token/env b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-authn-token/env new file mode 100644 index 000000000..f4e4155ea --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-authn-token/env @@ -0,0 +1,4 @@ +export CONJUR_CERT_FILE=./conjur.pem +unset CONJUR_AUTHN_API_KEY +unset CONJUR_AUTHN_LOGIN +export CONJUR_AUTHN_TOKEN_FILE=./access_token diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-authn-token/playbook.yml b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-authn-token/playbook.yml new file mode 100644 index 000000000..e515b0f11 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-authn-token/playbook.yml @@ -0,0 +1,14 @@ +--- +- name: Retrieve Conjur variable with authn-token + hosts: localhost + connection: local + tasks: + - name: Clean artifact path + file: + state: absent + path: /conjur_secrets.txt + + - name: Retrieve Conjur variable + vars: + super_secret_key: "{{lookup('conjur_variable', 'ansible/test-secret')}}" + shell: echo "{{super_secret_key}}" > /conjur_secrets.txt diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-authn-token/tests/test_default.py b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-authn-token/tests/test_default.py new file mode 100644 index 000000000..a98ce29e9 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-authn-token/tests/test_default.py @@ -0,0 +1,17 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import testinfra.utils.ansible_runner + +testinfra_hosts = [os.environ['COMPOSE_PROJECT_NAME'] + '-ansible'] + + +def test_retrieved_secret(host): + secrets_file = host.file('/conjur_secrets.txt') + + assert secrets_file.exists + + result = host.check_output("cat /conjur_secrets.txt", shell=True) + + assert result == "test_secret_password" diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-spaces-secret/env b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-spaces-secret/env new file mode 100644 index 000000000..2363951d1 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-spaces-secret/env @@ -0,0 +1 @@ +export CONJUR_CERT_FILE=./conjur.pem diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-spaces-secret/playbook.yml b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-spaces-secret/playbook.yml new file mode 100644 index 000000000..103d7e082 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-spaces-secret/playbook.yml @@ -0,0 +1,14 @@ +--- +- name: Retrieve Conjur variable with spaces in the variable name + hosts: localhost + connection: local + tasks: + - name: Clean artifact path + file: + state: absent + path: /conjur_secrets.txt + + - name: Retrieve Conjur variable with spaces in the variable name + vars: + super_secret_key: "{{lookup('conjur_variable', 'ansible/var with spaces')}}" + shell: echo "{{super_secret_key}}" > /conjur_secrets.txt diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-spaces-secret/tests/test_default.py b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-spaces-secret/tests/test_default.py new file mode 100644 index 000000000..145cbb2eb --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable-with-spaces-secret/tests/test_default.py @@ -0,0 +1,17 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import testinfra.utils.ansible_runner + +testinfra_hosts = [os.environ['COMPOSE_PROJECT_NAME'] + '-ansible'] + + +def test_retrieved_secret(host): + secrets_file = host.file('/conjur_secrets.txt') + + assert secrets_file.exists + + result = host.check_output("cat /conjur_secrets.txt", shell=True) + + assert result == "var_with_spaces_secret_password" diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable/env b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable/env new file mode 100644 index 000000000..2363951d1 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable/env @@ -0,0 +1 @@ +export CONJUR_CERT_FILE=./conjur.pem diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable/playbook.yml b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable/playbook.yml new file mode 100644 index 000000000..44e993f97 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable/playbook.yml @@ -0,0 +1,14 @@ +--- +- name: Retrieve Conjur variable + hosts: localhost + connection: local + tasks: + - name: Clean artifact path + file: + state: absent + path: /conjur_secrets.txt + + - name: Retrieve Conjur variable + vars: + super_secret_key: "{{lookup('conjur_variable', 'ansible/test-secret')}}" + shell: echo "{{super_secret_key}}" > /conjur_secrets.txt diff --git a/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable/tests/test_default.py b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable/tests/test_default.py new file mode 100644 index 000000000..a98ce29e9 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/conjur_variable/test_cases/retrieve-variable/tests/test_default.py @@ -0,0 +1,17 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import testinfra.utils.ansible_runner + +testinfra_hosts = [os.environ['COMPOSE_PROJECT_NAME'] + '-ansible'] + + +def test_retrieved_secret(host): + secrets_file = host.file('/conjur_secrets.txt') + + assert secrets_file.exists + + result = host.check_output("cat /conjur_secrets.txt", shell=True) + + assert result == "test_secret_password" diff --git a/ansible_collections/cyberark/conjur/tests/sanity/ignore-2.10.txt b/ansible_collections/cyberark/conjur/tests/sanity/ignore-2.10.txt new file mode 100644 index 000000000..92bf04480 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/sanity/ignore-2.10.txt @@ -0,0 +1,10 @@ +dev/start.sh shebang +Jenkinsfile shebang +tests/conjur_variable/policy/root.yml yamllint:unparsable-with-libyaml +roles/conjur_host_identity/tests/policy/root.yml yamllint:unparsable-with-libyaml # File loaded by summon utility (in Jenkinsfile), not via Python +ci/build_release shebang +ci/parse-changelog.sh shebang +ci/publish_to_galaxy shebang +ci/test.sh shebang +secrets.yml yamllint:unparsable-with-libyaml # File loaded by Conjur server, not via Python +dev/policy/root.yml yamllint:unparsable-with-libyaml
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/tests/sanity/ignore-2.11.txt b/ansible_collections/cyberark/conjur/tests/sanity/ignore-2.11.txt new file mode 100644 index 000000000..6049963fb --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/sanity/ignore-2.11.txt @@ -0,0 +1,10 @@ +Jenkinsfile shebang +dev/start.sh shebang +tests/conjur_variable/policy/root.yml yamllint:unparsable-with-libyaml +roles/conjur_host_identity/tests/policy/root.yml yamllint:unparsable-with-libyaml # File loaded by summon utility (in Jenkinsfile), not via Python +ci/build_release shebang +ci/parse-changelog.sh shebang +ci/publish_to_galaxy shebang +ci/test.sh shebang +secrets.yml yamllint:unparsable-with-libyaml # File loaded by Conjur server, not via Python +dev/policy/root.yml yamllint:unparsable-with-libyaml
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/tests/sanity/ignore-2.12.txt b/ansible_collections/cyberark/conjur/tests/sanity/ignore-2.12.txt new file mode 100644 index 000000000..5d750b26c --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/sanity/ignore-2.12.txt @@ -0,0 +1,10 @@ +Jenkinsfile shebang +dev/start.sh shebang +tests/conjur_variable/policy/root.yml yamllint:unparsable-with-libyaml # File loaded by Conjur server, not via Python +roles/conjur_host_identity/tests/policy/root.yml yamllint:unparsable-with-libyaml # File loaded by Conjur server, not via Python +ci/build_release shebang +ci/parse-changelog.sh shebang +ci/publish_to_galaxy shebang +ci/test.sh shebang +secrets.yml yamllint:unparsable-with-libyaml # File loaded by Summon utility (in Jenkinsfile), not via Python +dev/policy/root.yml yamllint:unparsable-with-libyaml
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/tests/sanity/ignore-2.13.txt b/ansible_collections/cyberark/conjur/tests/sanity/ignore-2.13.txt new file mode 100644 index 000000000..5d750b26c --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/sanity/ignore-2.13.txt @@ -0,0 +1,10 @@ +Jenkinsfile shebang +dev/start.sh shebang +tests/conjur_variable/policy/root.yml yamllint:unparsable-with-libyaml # File loaded by Conjur server, not via Python +roles/conjur_host_identity/tests/policy/root.yml yamllint:unparsable-with-libyaml # File loaded by Conjur server, not via Python +ci/build_release shebang +ci/parse-changelog.sh shebang +ci/publish_to_galaxy shebang +ci/test.sh shebang +secrets.yml yamllint:unparsable-with-libyaml # File loaded by Summon utility (in Jenkinsfile), not via Python +dev/policy/root.yml yamllint:unparsable-with-libyaml
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/tests/sanity/ignore-2.14.txt b/ansible_collections/cyberark/conjur/tests/sanity/ignore-2.14.txt new file mode 100644 index 000000000..00a2d8432 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/sanity/ignore-2.14.txt @@ -0,0 +1,11 @@ +Jenkinsfile shebang +dev/start.sh shebang +tests/conjur_variable/policy/root.yml yamllint:unparsable-with-libyaml # File loaded by Conjur server, not via Python +roles/conjur_host_identity/tests/policy/root.yml yamllint:unparsable-with-libyaml # File loaded by Conjur server, not via Python +ci/build_release shebang +ci/parse-changelog.sh shebang +ci/publish_to_galaxy shebang +ci/test.sh shebang +secrets.yml yamllint:unparsable-with-libyaml # File loaded by Summon utility (in Jenkinsfile), not via Python +dev/policy/root.yml yamllint:unparsable-with-libyaml +plugins/lookup/conjur_variable.py validate-modules:version-added-must-be-major-or-minor # Lookup plugin added in v1.0.2
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/tests/sanity/ignore-2.9.txt b/ansible_collections/cyberark/conjur/tests/sanity/ignore-2.9.txt new file mode 100644 index 000000000..45c7c7e97 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/sanity/ignore-2.9.txt @@ -0,0 +1,8 @@ +Jenkinsfile shebang +dev/start.sh shebang +tests/conjur_variable/test.sh shebang +roles/conjur_host_identity/tests/test.sh shebang +ci/build_release shebang +ci/parse-changelog.sh shebang +ci/publish_to_galaxy shebang +ci/test.sh shebang
\ No newline at end of file diff --git a/ansible_collections/cyberark/conjur/tests/unit/Dockerfile b/ansible_collections/cyberark/conjur/tests/unit/Dockerfile new file mode 100644 index 000000000..66e584669 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/unit/Dockerfile @@ -0,0 +1,8 @@ +ARG PYTHON_VERSION +FROM python:${PYTHON_VERSION} + +ARG ANSIBLE_VERSION +RUN pip install https://github.com/ansible/ansible/archive/${ANSIBLE_VERSION}.tar.gz --disable-pip-version-check + +COPY tests/unit/requirements.txt /tmp/requirements.txt +RUN pip install -r /tmp/requirements.txt diff --git a/ansible_collections/cyberark/conjur/tests/unit/plugins/lookup/__init__.py b/ansible_collections/cyberark/conjur/tests/unit/plugins/lookup/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/unit/plugins/lookup/__init__.py diff --git a/ansible_collections/cyberark/conjur/tests/unit/plugins/lookup/test_conjur_variable.py b/ansible_collections/cyberark/conjur/tests/unit/plugins/lookup/test_conjur_variable.py new file mode 100644 index 000000000..7a0db1e12 --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/unit/plugins/lookup/test_conjur_variable.py @@ -0,0 +1,159 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from unittest import TestCase +from unittest.mock import call, MagicMock, patch +from ansible.errors import AnsibleError +from ansible.plugins.loader import lookup_loader + +from ansible_collections.cyberark.conjur.plugins.lookup.conjur_variable import _merge_dictionaries, _fetch_conjur_token, _fetch_conjur_variable +from ansible_collections.cyberark.conjur.plugins.lookup.conjur_variable import _load_identity_from_file, _load_conf_from_file + + +class MockMergeDictionaries(MagicMock): + RESPONSE = {'id': 'host/ansible/ansible-fake', 'api_key': 'fakekey'} + + +class MockFileload(MagicMock): + RESPONSE = {} + + +class TestConjurLookup(TestCase): + def setUp(self): + self.lookup = lookup_loader.get("conjur_variable") + + def test_merge_dictionaries(self): + functionOutput = _merge_dictionaries( + {}, + {'id': 'host/ansible/ansible-fake', 'api_key': 'fakekey'} + ) + self.assertEquals(MockMergeDictionaries.RESPONSE, functionOutput) + + def test_load_identity_from_file(self): + load_identity = _load_identity_from_file("/etc/conjur.identity", "https://conjur-fake") + self.assertEquals(MockFileload.RESPONSE, load_identity) + + def test_load_conf_from_file(self): + load_conf = _load_conf_from_file("/etc/conjur.conf") + self.assertEquals(MockFileload.RESPONSE, load_conf) + + @patch('ansible_collections.cyberark.conjur.plugins.lookup.conjur_variable.open_url') + def test_fetch_conjur_token(self, mock_open_url): + mock_response = MagicMock() + mock_response.getcode.return_value = 200 + mock_response.read.return_value = "response body" + mock_open_url.return_value = mock_response + result = _fetch_conjur_token("url", "account", "username", "api_key", True, "cert_file") + mock_open_url.assert_called_with("url/authn/account/username/authenticate", + data="api_key", + method="POST", + validate_certs=True, + ca_path="cert_file") + self.assertEquals("response body", result) + + @patch('ansible_collections.cyberark.conjur.plugins.lookup.conjur_variable._repeat_open_url') + def test_fetch_conjur_variable(self, mock_repeat_open_url): + mock_response = MagicMock() + mock_response.getcode.return_value = 200 + mock_response.read.return_value = "response body".encode("utf-8") + mock_repeat_open_url.return_value = mock_response + result = _fetch_conjur_variable("variable", b'{"protected":"fakeid"}', "url", "account", True, "cert_file") + mock_repeat_open_url.assert_called_with("url/secrets/account/variable/variable", + headers={'Authorization': 'Token token="eyJwcm90ZWN0ZWQiOiJmYWtlaWQifQ=="'}, + method="GET", + validate_certs=True, + ca_path="cert_file") + self.assertEquals(['response body'], result) + + @patch('ansible_collections.cyberark.conjur.plugins.lookup.conjur_variable._fetch_conjur_variable') + @patch('ansible_collections.cyberark.conjur.plugins.lookup.conjur_variable._fetch_conjur_token') + @patch('ansible_collections.cyberark.conjur.plugins.lookup.conjur_variable._merge_dictionaries') + def test_run(self, mock_merge_dictionaries, mock_fetch_conjur_token, mock_fetch_conjur_variable): + mock_fetch_conjur_token.return_value = "token" + mock_fetch_conjur_variable.return_value = ["conjur_variable"] + mock_merge_dictionaries.side_effect = [ + {'account': 'fakeaccount', 'appliance_url': 'https://conjur-fake', 'cert_file': './conjurfake.pem'}, + {'id': 'host/ansible/ansible-fake', 'api_key': 'fakekey'} + ] + + terms = ['ansible/fake-secret'] + kwargs = {'as_file': False, 'conf_file': 'conf_file', 'validate_certs': False} + result = self.lookup.run(terms, **kwargs) + + self.assertEquals(result, ["conjur_variable"]) + + @patch('ansible_collections.cyberark.conjur.plugins.lookup.conjur_variable._fetch_conjur_variable') + @patch('ansible_collections.cyberark.conjur.plugins.lookup.conjur_variable._fetch_conjur_token') + @patch('ansible_collections.cyberark.conjur.plugins.lookup.conjur_variable._merge_dictionaries') + def test_retrieve_to_file(self, mock_merge_dictionaries, mock_fetch_conjur_token, mock_fetch_conjur_variable): + mock_fetch_conjur_token.return_value = "token" + mock_fetch_conjur_variable.return_value = ["conjur_variable"] + mock_merge_dictionaries.side_effect = [ + {'account': 'fakeaccount', 'appliance_url': 'https://conjur-fake', 'cert_file': './conjurfake.pem'}, + {'id': 'host/ansible/ansible-fake', 'api_key': 'fakekey'} + ] + + terms = ['ansible/fake-secret'] + kwargs = {'as_file': True, 'conf_file': 'conf_file', 'validate_certs': False} + filepaths = self.lookup.run(terms, **kwargs) + self.assertRegex(filepaths[0], '/dev/shm/.*') + + with open(filepaths[0], "r") as file: + content = file.read() + self.assertEqual(content, "conjur_variable") + + # Negative test cases + + @patch('ansible_collections.cyberark.conjur.plugins.lookup.conjur_variable._merge_dictionaries') + def test_run_bad_config(self, mock_merge_dictionaries): + # Withhold 'account' field + mock_merge_dictionaries.side_effect = [ + {'appliance_url': 'https://conjur-fake', 'cert_file': './conjurfake.pem'}, + {'id': 'host/ansible/ansible-fake', 'api_key': 'fakekey'} + ] + + terms = ['ansible/fake-secret'] + kwargs = {'as_file': False, 'conf_file': 'conf_file', 'validate_certs': True} + with self.assertRaises(AnsibleError) as context: + self.lookup.run(terms, **kwargs) + self.assertEqual( + context.exception.message, + "Configuration file on the controlling host must define `account` and `appliance_url` entries or they should be environment variables" + ) + + # Withhold 'id' and 'api_key' fields + mock_merge_dictionaries.side_effect = [ + {'account': 'fakeaccount', 'appliance_url': 'https://conjur-fake', 'cert_file': './conjurfake.pem'}, + {} + ] + + with self.assertRaises(AnsibleError) as context: + self.lookup.run(terms, **kwargs) + self.assertEqual( + context.exception.message, + ("Identity file on the controlling host must contain `login` and `password` " + "entries for Conjur appliance URL or they should be environment variables") + ) + + @patch('ansible_collections.cyberark.conjur.plugins.lookup.conjur_variable._merge_dictionaries') + def test_run_bad_cert_path(self, mock_merge_dictionaries): + mock_merge_dictionaries.side_effect = [ + {'account': 'fakeaccount', 'appliance_url': 'https://conjur-fake', 'cert_file': './conjurfake.pem'}, + {'id': 'host/ansible/ansible-fake', 'api_key': 'fakekey'} + ] + + terms = ['ansible/fake-secret'] + kwargs = {'as_file': False, 'conf_file': 'conf_file', 'validate_certs': True} + with self.assertRaises(FileNotFoundError): + self.lookup.run(terms, **kwargs) + + def test_run_no_variable_path(self): + kwargs = {'as_file': False, 'conf_file': 'conf_file', 'validate_certs': True} + + with self.assertRaises(AnsibleError) as context: + self.lookup.run([], **kwargs) + self.assertEqual(context.exception.message, "Invalid secret path: no secret path provided.") + + with self.assertRaises(AnsibleError) as context: + self.lookup.run([''], **kwargs) + self.assertEqual(context.exception.message, "Invalid secret path: empty secret path not accepted.") diff --git a/ansible_collections/cyberark/conjur/tests/unit/requirements.txt b/ansible_collections/cyberark/conjur/tests/unit/requirements.txt new file mode 100644 index 000000000..9b481ce1d --- /dev/null +++ b/ansible_collections/cyberark/conjur/tests/unit/requirements.txt @@ -0,0 +1,14 @@ +mock +pytest +pytest-mock +pytest-xdist +pytest-forked +pyyaml # required by the collection loader (only needed for collections) +coverage==4.5.4 + +bcrypt ; python_version >= '3.8' # controller only +passlib ; python_version >= '3.8' # controller only +pexpect ; python_version >= '3.8' # controller only +pytz +pywinrm ; python_version >= '3.8' # controller only +unittest2 ; python_version < '2.7' diff --git a/ansible_collections/cyberark/pas/.DS_Store b/ansible_collections/cyberark/pas/.DS_Store Binary files differnew file mode 100644 index 000000000..c21215883 --- /dev/null +++ b/ansible_collections/cyberark/pas/.DS_Store diff --git a/ansible_collections/cyberark/pas/CONTRIBUTING.md b/ansible_collections/cyberark/pas/CONTRIBUTING.md new file mode 100644 index 000000000..97d53cb0a --- /dev/null +++ b/ansible_collections/cyberark/pas/CONTRIBUTING.md @@ -0,0 +1,9 @@ +# Contributing to the Ansible Security Automation Collection + +Thanks for your interest in the Ansible Security Automation collection. + +For general community guidelines, please see the [community repo](https://github.com/cyberark/community). + +## Pull Request Workflow + +Currently, this repository is source-available and not open to contributions. Please continue to follow this repository for updates and open-source availability diff --git a/ansible_collections/cyberark/pas/FILES.json b/ansible_collections/cyberark/pas/FILES.json new file mode 100644 index 000000000..bef13dac3 --- /dev/null +++ b/ansible_collections/cyberark/pas/FILES.json @@ -0,0 +1,488 @@ +{ + "files": [ + { + "name": ".", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": ".DS_Store", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "06521a08299939cac5d58311a8e54a50f370e264ce599fce1b24c155db49dad0", + "format": 1 + }, + { + "name": "LICENSE", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f717ec697e01e78397a7c8a6f13b78070ea4503346ba65202ccaa055f34f0261", + "format": 1 + }, + { + "name": "plugins", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/.DS_Store", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9fb1a681718842e1c421f5e0b8c3ac05f77e4717115b99beee920a4282d479a9", + "format": 1 + }, + { + "name": "plugins/event_source", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/event_source/cyberark-eda-json-v1.0.xsl", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "14cc4b68daaf5955cf35432331f720a2ab038d05dc3db2e9828895bf2cf9a921", + "format": 1 + }, + { + "name": "plugins/event_source/syslog.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7f2476298fc3942811e865f00c8d8099981c7353c5156bb3a15c0c4d5304819e", + "format": 1 + }, + { + "name": "plugins/modules", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/modules/cyberark_account.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "116db33283c897cbc2d3a4d3f3e0b3d16021e41b2060b2b265a30e5c59ef365e", + "format": 1 + }, + { + "name": "plugins/modules/cyberark_user.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "88346fd9883d77e1f6631e23fdf88bed4b659f1f5ce2542f3afa4814193c0469", + "format": 1 + }, + { + "name": "plugins/modules/cyberark_credential.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "63a79fd9eeda9f571a4a4020bca8c5ea836431df433212c01995133bb713705c", + "format": 1 + }, + { + "name": "plugins/modules/cyberark_authentication.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7e7451bc50d5a6a5d44f01bee1581e0b30ed72da2e410b508ead9c75c02f2af5", + "format": 1 + }, + { + "name": "tests", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/change_test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c5de68ecfc9f53db7c766c42ca5984624ba8859ac8dd0d3326a0ea92e9ba4641", + "format": 1 + }, + { + "name": "tests/provision_user.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "886b820d493fe19407c14522b66fc74c456d1f30df27116482acb4480e6d620b", + "format": 1 + }, + { + "name": "tests/test.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "49c4dab3f755c0c0fd3f4010097bc8af0c31c34caee162d8a980f10e72ec4d1d", + "format": 1 + }, + { + "name": "tests/provision_account.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "885a0748fa873047be277636fc265bc0a986ffbf2e33cc7deda9a317cf7ff87b", + "format": 1 + }, + { + "name": "tests/enable_user.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "91f1863e86bd2c890732285cf101dab9fc78c6deac91e9a8964135444f74ee36", + "format": 1 + }, + { + "name": "tests/deprovision_user.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "77be4201572460a644b0298698123089f7d526feb09ddfad51fa839766c736d6", + "format": 1 + }, + { + "name": "tests/changepolicy.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "756ac18b033fc4a7e208e31c9e3da6a33acadc2f8b07ed8adb18e1daa9da9915", + "format": 1 + }, + { + "name": "tests/reset_user_password.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "179c40338463a55bf504034de04c58b62525a696667adde36fc8f3d79b8b81fb", + "format": 1 + }, + { + "name": "tests/disable_user.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "cd03538b91445bba1f80b3390f857a54afe55bc8b6f7f056881f62a51e506cad", + "format": 1 + }, + { + "name": "tests/deprovision_account.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "679689e8c958366a772b3cca71e3e43873d18583b12b81bf788af7c2a14d1d02", + "format": 1 + }, + { + "name": "meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "meta/runtime.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "db1dfe098d05c998eb62c32cf99d3b7bbf93afc0a1f0b4704b024eaa7c7b6848", + "format": 1 + }, + { + "name": "roles", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/aimprovider", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/aimprovider/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/aimprovider/tasks/uninstallAIMProvider.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2a3dce24d9d2f05edbee3ad2ee14b733fdcfdc6494e2fb9550c5dd02fbe1f734", + "format": 1 + }, + { + "name": "roles/aimprovider/tasks/installAIMProvider.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f334faee54008e6101414771409a9aa7f98b62e4996269003dca84441d4f6db5", + "format": 1 + }, + { + "name": "roles/aimprovider/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "224814783560fc317b2df277f2542273747f533abf493c891b03a0e496d8feab", + "format": 1 + }, + { + "name": "roles/aimprovider/README.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f1d69c30a1131171ac7acad895d588ab08b20896751d1db2fcb510103b37bc5b", + "format": 1 + }, + { + "name": "roles/aimprovider/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/aimprovider/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "56a96c0b8c7e35aed626b90d3b3020b0aa6b34ccd08cf1fdab08778b5c2f5839", + "format": 1 + }, + { + "name": "docs", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "docs/cyberark_account.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "95508e0e44993426f59af04652c7dcaeacc3aad7279573f570d1fd705ff0a755", + "format": 1 + }, + { + "name": "docs/images", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "docs/images/platform_account_properties.JPG", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "82e22cb050df050bd5af8680d341f61d48beafbf9c30cb40fe994c684151d8d4", + "format": 1 + }, + { + "name": "docs/images/eda-pta-syslog.png", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b83470678d39e86b460a4e6b21c618b8015090e2dbd2d8296766e8b4f64073d1", + "format": 1 + }, + { + "name": "docs/images/eda_disableuser_kafka.png", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a265629b84ea81402306ea137ce3669608177bd5bfc6463d0144156b08d57830", + "format": 1 + }, + { + "name": "docs/images/eda_disableuser_webhook.png", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a968cb0de979628ac398870f908be350cb7baa0ef589876949cade89e69a2ae9", + "format": 1 + }, + { + "name": "docs/images/rsyslog-webhook.png", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0d828047be1cdf5608223fd8fb95830bd122b55273b3d9d91afb2b13ee931c5d", + "format": 1 + }, + { + "name": "docs/images/full-cyberark-logo.jpg", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5d910c2159e7caded5286414d0329d7e0f084676fcb80610efffa5be7214452b", + "format": 1 + }, + { + "name": "docs/images/eda-syslog.png", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "53494d3c2029136e10675b9a1063e7e516b87da70445ba7fcb2c70801635e9c8", + "format": 1 + }, + { + "name": "docs/images/rsyslog-kafka.png", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8112d7af4f66ceeb8665199a269a3817ee81268116d0205921492aa636dfb8f3", + "format": 1 + }, + { + "name": "docs/images/eda_pta_disable_user_syslog.png", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6a258913dea76f7b6a4525ca5fa20818a9e2585e32c5d988f71deb3c724384eb", + "format": 1 + }, + { + "name": "docs/images/cyberark_logo.jpg", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "535e69685a6e58c210d685abe0fb4f8990bdca029ef41f30c130000f9a596631", + "format": 1 + }, + { + "name": "docs/images/eda_disable_user_syslog.png", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8754056835abf11bf5ca125e80414a3d78a572bea4952d0c5daf97346f55c745", + "format": 1 + }, + { + "name": "docs/cyberark_credential.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "903afbe64bbbbd94f8c2571eee2890a9380c4ebc0d5e94184ba3111672a708bf", + "format": 1 + }, + { + "name": "docs/aimprovider.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f1d69c30a1131171ac7acad895d588ab08b20896751d1db2fcb510103b37bc5b", + "format": 1 + }, + { + "name": "docs/cyberark_authentication.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c8c121019f265ba222a968a2fe94d1724e02490d3f637b7bc5645c97caa2f423", + "format": 1 + }, + { + "name": "docs/cyberark_user.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1b1e3a133afead30cc3c7ae40fd2d26e8f0032e188b55b6d4d0f06e015b9701d", + "format": 1 + }, + { + "name": "docs/cyberark_eda.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3f75ebf09e764a54338e13f8315fa5628034f84a1ca60a322d606a1c96e769fd", + "format": 1 + }, + { + "name": "README.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8f163f0bfa5d572f89fb02db1acf39330d9e5d56e5eb3a87500a9c6b07fc0291", + "format": 1 + }, + { + "name": "rulebooks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "rulebooks/disable_pas_user_webhook.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "aaf528f7dc4f268793df315d4e5b78ccf2d1f14ff2f312e0c089d6014b59b708", + "format": 1 + }, + { + "name": "rulebooks/pta_notify.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fce3b40b7c3ddd6b3be7ddfda3369584e3bb6e8ac84e0ef40454022472955830", + "format": 1 + }, + { + "name": "rulebooks/cyberark_test_rule.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ac03ca12b8957fff3a352641886ab24a2f0eb31a2888602f9e3557583f3a0736", + "format": 1 + }, + { + "name": "rulebooks/disable_pas_user_kafka.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4a8fd969fbf4244bfa1de34afce843b0e5bac4c8d444f413a2d479ffb49e87e2", + "format": 1 + }, + { + "name": "rulebooks/pta_disable_notify.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "199aef6aa3bf71d6aa7b261778a5545103a292c33f4bd85122ea7010a722c0bd", + "format": 1 + }, + { + "name": "rulebooks/disable_user.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d6cd1b632262eb0c6eac20213fedd1dc2274ca6df1a1a27feaeac9fd3a2f169d", + "format": 1 + }, + { + "name": "rulebooks/inventory.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c0ce0d7e6d03184e9cd4202f377174d36736b4dd9fb1ab8b79cf909fb0b70ffc", + "format": 1 + }, + { + "name": "CONTRIBUTING.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "27344573191496c7506f67129c6b88d8a05ad8c99e069fa7c27f8f17f61bda57", + "format": 1 + }, + { + "name": "custom-cred-types", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "custom-cred-types/cyberark-pas-restapi", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "custom-cred-types/cyberark-pas-restapi/input.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f4adbb5b1da168dea98a8fd85d5a4b109c346b3deed95cfd06f160c5db4ce927", + "format": 1 + }, + { + "name": "custom-cred-types/cyberark-pas-restapi/injector.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f61c7965c5570860c7345b15f5ce4eea14b7ea4e51548060e023f6dd75879b4c", + "format": 1 + }, + { + "name": "custom-cred-types/cyberark-pas-restapi/README.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a07b281e2ab653ee3b61a36a7cced24051b2c2a8a7b14fdc8632b2c34f31e2c5", + "format": 1 + } + ], + "format": 1 +}
\ No newline at end of file diff --git a/ansible_collections/cyberark/pas/LICENSE b/ansible_collections/cyberark/pas/LICENSE new file mode 100644 index 000000000..d6296a340 --- /dev/null +++ b/ansible_collections/cyberark/pas/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 CyberArk + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ansible_collections/cyberark/pas/MANIFEST.json b/ansible_collections/cyberark/pas/MANIFEST.json new file mode 100644 index 000000000..73d64bb7a --- /dev/null +++ b/ansible_collections/cyberark/pas/MANIFEST.json @@ -0,0 +1,43 @@ +{ + "collection_info": { + "namespace": "cyberark", + "name": "pas", + "version": "1.0.19", + "authors": [ + "CyberArk Business Development (@cyberark-bizdev)", + "Edward Nunez (@enunez-cyberark)", + "Joe Garcia (@infamousjoeg)" + ], + "readme": "README.md", + "tags": [ + "cyberark", + "access", + "security", + "account", + "epv", + "vault", + "identity", + "credential", + "secret", + "privileged" + ], + "description": "This is a Collection of the CyberArk Ansible Security Automation toolkit.", + "license": [ + "MIT" + ], + "license_file": null, + "dependencies": {}, + "repository": "https://github.com/cyberark/ansible-security-automation-collection", + "documentation": null, + "homepage": null, + "issues": "https://github.com/cyberark/ansible-security-automation-collection/issues" + }, + "file_manifest_file": { + "name": "FILES.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "963cff01c982a389719c2948de6d0c8e18531926af68e1f238789385e49cec71", + "format": 1 + }, + "format": 1 +}
\ No newline at end of file diff --git a/ansible_collections/cyberark/pas/README.md b/ansible_collections/cyberark/pas/README.md new file mode 100644 index 000000000..43842e7dc --- /dev/null +++ b/ansible_collections/cyberark/pas/README.md @@ -0,0 +1,73 @@ +<!-- please note this has to be a absolute URL since otherwise it will not show up on galaxy.ansible.com --> +![cyberark logo|](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/images/full-cyberark-logo.jpg?raw=true) + +## CyberArk Ansible Security Automation Collection + +************* + +## Collection + +#### cyberark.pas + +This collection is the CyberArk Ansible Security Automation project and can be found on [ansible galaxy](https://galaxy.ansible.com/cyberark/pas). This is aimed to enable the automation of securing privileged access by storing privileged accounts in the Enterprise Password Vault (EPV), controlling user's access to privileged accounts in EPV, and securely retreiving secrets using Application Access Manager (AAM). +The collection includes [support for Event-Driven Ansible](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/cyberark_eda.md) by providing an event-source plugin for syslog and also guidance on how to use it. + + +The following modules will allow CyberArk administrators to automate the following tasks: + +#### Requirements + +- CyberArk Privileged Account Security Web Services SDK +- CyberArk AAM Central Credential Provider (**Only required for cyberark_credential**) + +#### Role Variables + +None. +<br> +<br> + +## Modules + +#### cyberark_authentication + +- Using the CyberArk Web Services SDK, authenticate and obtain an auth token to be passed as a variable in playbooks +- Logoff of an authenticated REST API session<br> +[Playbooks and Module Info](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/cyberark_authentication.md) + +#### cyberark_user + +- Add a CyberArk User +- Delete a CyberArk User +- Update a CyberArk User's account parameters + - Enable/Disable, change password, mark for change at next login, etc +<br>[Playbooks and Module Info](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/cyberark_user.md)<br/> + +#### cyberark_account + +- Add Privileged Account to the EPV +- Delete account objects +- Modify account properties +- Rotatate privileged credentials<br> +[Playbooks and Module Info](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/cyberark_account.md) + +#### cyberark_credential + +- Using AAM Central Credential Provider (CCP), to securely retreive secrets and account properties from EPV to be registered for use in playbooks<br> +[Playbooks and Module Info](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/cyberark_credential.md) + +## Roles + +#### aimprovider + +- Install agent-based Credential Provider (AIM) on Linux hosts +[Playbooks and Module Info](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/aimprovider.md) + +#### Python3 + +- The modules will work with either python2 or python3. + +#### Author Information +- CyberArk Business Development Technical Team + - @enunez-cyberark + - @cyberark-bizdev + diff --git a/ansible_collections/cyberark/pas/custom-cred-types/cyberark-pas-restapi/README.md b/ansible_collections/cyberark/pas/custom-cred-types/cyberark-pas-restapi/README.md new file mode 100644 index 000000000..852ff7db4 --- /dev/null +++ b/ansible_collections/cyberark/pas/custom-cred-types/cyberark-pas-restapi/README.md @@ -0,0 +1,32 @@ +# CyberArk PAS REST API + +Custom Credential Type for Ansible Tower + +## Installation + +1. Login to Ansible Tower as a Tower Administrator. +2. Under Administration, click on Credential Type. +3. Click the green [ + ] in the top right-hand corner to create a new Custom Credential Type. +4. Set the name of your Credential Type. e.g. `CyberArk PAS REST API` +5. Under `Input Configuration` select `YAML`. +6. Copy and paste the [input.yml](input.yml) into the text field for `Input Configuration`. +7. Under `Injector Configuration` select `YAML`. +8. Copy and paste the [injector.yml](injector.yml) into the text field for `Injector Configuration`. +9. Click `Save` at the bottom to save the Custom Credential Type. + +## Usage + +Reference the following environment variables within your Ansible Playbook when using this Credential Type: + +* `CYBERARK_API_URL` \ +This is the Base URI of your CyberArk Password Vault Web Access (PVWA). _e.g. `https://pvwa.cyberark.com`_ + +* `CYBERARK_API_USERNAME` \ +This is the username to use when logging into the CyberArk PAS Web Services SDK (REST API). + +* `CYBERARK_API_PASSWORD` \ +This is the password associated with the username provided for login. + +## Maintainer + +Joe Garcia, CISSP - DevOps Security Engineer, CyberArk - [@infamousjoeg](https://github.com/infamousjoeg) diff --git a/ansible_collections/cyberark/pas/custom-cred-types/cyberark-pas-restapi/injector.yml b/ansible_collections/cyberark/pas/custom-cred-types/cyberark-pas-restapi/injector.yml new file mode 100644 index 000000000..4d6edc5fe --- /dev/null +++ b/ansible_collections/cyberark/pas/custom-cred-types/cyberark-pas-restapi/injector.yml @@ -0,0 +1,4 @@ +extra_vars: + CYBERARK_API_PASSWORD: '{{ password }}' + CYBERARK_API_URL: '{{ pvwa_url }}' + CYBERARK_API_USERNAME: '{{ username }}' diff --git a/ansible_collections/cyberark/pas/custom-cred-types/cyberark-pas-restapi/input.yml b/ansible_collections/cyberark/pas/custom-cred-types/cyberark-pas-restapi/input.yml new file mode 100644 index 000000000..59a8441e0 --- /dev/null +++ b/ansible_collections/cyberark/pas/custom-cred-types/cyberark-pas-restapi/input.yml @@ -0,0 +1,18 @@ +fields: + - id: username + type: string + label: Username + help_text: CyberArk REST API Username + - id: password + type: string + label: Password + secret: true + help_text: CyberArk REST API Password + - id: pvwa_url + type: string + label: PVWA URL + help_text: 'CyberArk PVWA URL e.g. https://pvwa.cyberark.com' +required: + - username + - password + - pvwa_url diff --git a/ansible_collections/cyberark/pas/docs/aimprovider.md b/ansible_collections/cyberark/pas/docs/aimprovider.md new file mode 100644 index 000000000..e39863539 --- /dev/null +++ b/ansible_collections/cyberark/pas/docs/aimprovider.md @@ -0,0 +1,91 @@ +cyberark.pas.aimprovider +==================== + +Role to install/uninstall CyberArk's AIM Credential Provider. + +Requirements +------------ + +- CyberArk Privileged Account Security Web Services SDK. +- `cyberark.pas` Collection from Ansible Galaxy or Automation Hub + +Role Variables +-------------- +``` +# CyberArk's Privileged Account Security Web Services SDK api base URL (example: https://components.cyberark.local) +rest_api_url: "" + +# Whether to validate certificates for REST api calls. If false, SSL certificates will not be validated. This should only be used on personally controlled sites using self-signed certificates. +validate_certs: true + +# Zip file with distribution of AIM Provider (example: /tmp/binaries/RHELinux x64-Rls-v9.8.zip); this file is located in the Ansible server, and it will be copied to the Ansible nodes. It should point to the current version of AIM distribution to be used when delivering to the nodes in a central folder within the Ansible server. +zip_file_name: "" + +# Folder name within the ZIP file that will be used. By default, it's taken from zip file name, for example: "RHELinux x64" +folder_name: '{{zip_file_name.split("/")[-1].split("-Rls")[0]}}' + +# CyberArk location for App Provider user to be created +app_provider_user_location: "\\Applications" + +# CyberArk Vault Address +vault_address: "" + +# Whether to use shared logon authentication. If true, it will use the "Shared Logon Authentication" as described in the CyberArk's document "Privileged Account Security Web Services SDK Implementation Guide" +use_shared_logon_authentication: false + +# State - can be "present"/"absent" for install/uninstall. +state: "present" +``` + + +Additionally: +- **app_provider_user_group**: The name of the group the Provider user will be added to. + +Dependencies +------------ + +None. + + +Example Playbook +---------------- + +1) Install CyberArk AIM Provider. + +``` +--- +- hosts: all + + roles: + + - role: cyberark.pas.aimprovider + api_base_url: "https://components.cyberark.local" + validate_certs: false + zip_file_name: "/tmp/binaries/RHELinux x64-Rls-v9.8.zip" + vault_address: "10.0.1.10" + use_shared_logon_authentication: true +``` + +2) Uninstall CyberArk AIM Provider. +``` +--- +- hosts: all + + roles: + + - role: cyberark.pas.aimprovider + api_base_url: "https://components.cyberark.local" + use_shared_logon_authentication: true + state: "absent" + validate_certs: false +``` + +License +------- + +MIT + +Author Information +------------------ + +- Edward Nunez (edward.nunez@cyberark.com) diff --git a/ansible_collections/cyberark/pas/docs/cyberark_account.md b/ansible_collections/cyberark/pas/docs/cyberark_account.md new file mode 100644 index 000000000..7ddb4e4d5 --- /dev/null +++ b/ansible_collections/cyberark/pas/docs/cyberark_account.md @@ -0,0 +1,278 @@ +# cyberark_account + +Allows for adding, deleting, modifying a privileged credential within the Cyberark Vault. The request uses the Privileged Account Security Web Services SDK.<br> + +The ability to modify consists of the following: + +* Password (see secret_management) +* Safe +* Platform +* Address +* Object Name +* Username +* Platform Account Properties + * These are the parameters listed in the Platform under `UI & Workflows -> Properties` and are unique to each Platform (see image below) +* Remote Machines Access + +![Platform Account Properties](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/images/platform_account_properties.JPG?raw=true) + +### secret_management +The `secret_management` dictionary provides the capability to set a CPM password rotation flag on an existing account. + +The available options are as follows:<br> + +`automatic_management_enabled`: bool<br> +`manual_management_reason`: This is a string value that populates the Reason field is you have set an account to not be managed by the CPM. This value is only necessary if `automatic_management_enabled` is set to false.<br> +`management_action`: This value indicates what type CPM management flag will be placed on the account +* change - <br> +* change_immediately - <br> +* reconcile - <br> + +`new_secret`: This parameter is available to set the value of the new password<br> +`perform_secret_management`: This parameter was allows the option to place a CPM management flag on an account upon creation of an account object. +* always - All `secret_management` actions will follow the table below at all times. +* on_create - Will place a CPM management flag according to the table below ONLY on creation of an account object. + +#### Secret Management Action Table +| management_action | new_secret | Action | +| :---------: | :----: | :----- | +| change | populated | change password to set value at next scheduled rotation | +| change | NULL | rotate password at next scheduled rotation | +| change_immediately | populated | change immediately to the set value | +| change_immediately | NULL | rotate immediately | +| reconcile | populated | reconcile immediately NOT to set value | +| reconcile | NULL | reconcile immediately | +| NULL | populated | set value in Vault ONLY | + + +### identified_by +This property allows for the module to confidently identify the account object needing to be identified. If multiple accounts are returned from the modules initial `Get Accounts` it will use the value(s) set in the `identified_by` parameter to direct which account is selected from the list. + +**EXAMPLE:** +``` +-Playbook Parameters- + +cyberark_account: + identified_by: "address,username,platform_id" + safe: "testSafe" + address: "dev.local" + username: "admin" + platform_id: WinDomain + + -This is the query sent to CyberArk Web SDK: +/api/Accounts?filter=safeName eq testSafe&search= admin dev.local + +**This could return multiple accounts in the testSafe** + +RETURNED: +account1 + username: administrator + address: cyberark.dev.local + safe: testSafe + policyID: WinDomain + +account2 + username: admin + address: dev.local + safe: testSafe + policyID: WinDomain +``` +With the `identified_by` parameter set the `cyberark_account` module will select the account2 object becauses the values of the `address`, `username` and `platform_id` parameters are identical matches to the values of account2 properties. + +#### Limitations +**Idempotency** - All actions taken in the module adhere to the Ansible idempotency guidelines _except_ for password change. If you have the playbook set to modify a password it will send a password change request every time the playbook is run, even if you are defining the next password value and it is the same password that is set in other runs.<br> +**Remote Machines Access** - When modifying the values in the `remote_machines_access` dictionary be mindful of the `platform_id` value. Remote Machines Access values are stored at the Vault database level and not stored as File Categories. It is a function that is only available with the `WinDomain` platform and if you attempt to assign these values to another platform it will cause errors in the PSM functionality. + + +#### Available Fields +``` +options: + state: + description: + - Assert the desired state of the account C(present) to creat or update and account object. Set to C(absent) for deletion of an account object + required: true + default: present + choices: [present, absent] + type: str + logging_level: + description: + - Parameter used to define the level of troubleshooting output to the C(logging_file) value + required: true + choices: [NOTSET, DEBUG, INFO] + type: str + logging_file: + description: + - Setting the log file name and location for troubleshooting logs + required: false + default: /tmp/ansible_cyberark.log + type: str + api_base_url: + description: + - A string containing the base URL of the server hosting CyberArk's Privileged Account Security Web Services SDK + - Example: U(https://<IIS_Server_Ip>/PasswordVault/api/) + required: true + type: str + validate_certs: + description: + - If C(false), SSL certificate chain will not be validated. This should only set to C(true) if you have a root CA certificate installed on each node. + required: false + default: true + type: bool + cyberark_session: + description: + - Dictionary set by a CyberArk authentication containing the different values to perform actions on a logged-on CyberArk session, please see M(cyberark_authentication) module for an example of cyberark_session. + required: true + type: dict + identified_by: + description: + - When an API call is made to Get Accounts, often times the default parameters passed will identify more than one account. This parameter is used to confidently identify a single account when the default query can return multiple results. + required: false + default: username,address,platform_id + type: str + safe: + description: + - The safe in the Vault where the privileged account is to be located + required: true + type: str + platform_id: + description: + - The PolicyID of the Platform that is to be managing the account + required: false + type: str + address: + description: + - The adress of the endpoint where the privileged account is located + required: false + type: str + name: + description: + - The ObjectID of the account + required: false + type: str + secret_type: + description: + - The value that identifies what type of account it will be. + required: false + default: password + choices: [password, key] + type: str + secret: + description: + - The initial password for the creation of the account + required: false + type: str + username: + description: + - The username associated with the account + required: false + type: str + secret_management + description: + - Set of parameters associated with the management of the credential + required: false + suboptions: + automatic_management_enabled: + description: + - Parameter that indicates whether the CPM will manage the password or not + default: true + type: bool + manual_management_reason: + description: + - String value indicating why the CPM will NOT manage the password + type: str + management_action: + description: + - CPM action flag to be placed on the account object for credential rotation + choices: [change, change_immediately, reconcile] + type: str + new_secret: + description: + - The actual password value that will be assigned for the CPM action to be taken + type: str + perform_management_action: + description: + - C(always) will perform the management action in every action + - C(on_create) will only perform the management action right after the account is created + choices: [always, on_create] + default: always + type: str + remote_machines_access: + description: + - Set of parameters for defining PSM endpoint access targets + required: false + type: dict + suboptions: + remote_machines: + description: + - List of targets allowed for this account + type: str + access_restricted_to_remote_machines: + description: + - Whether or not to restrict access only to specified remote machines + type: bool + platform_account_properties: + description: + - Object containing key-value pairs to associate with the account, as defined by the account platform. These properties are validated against the mandatory and optional properties of the specified platform's definition. Optional properties that do not exist on the account will not be returned here. Internal properties are not returned. + required: false + type: dict + suboptions: + KEY: + description: + - Freeform key value associated to the mandatory or optional property assigned to the specified Platform's definition. + aliases: [Port, ExtrPass1Name, database] + type: str +``` + +## Example Playbooks + + +```yaml + tasks: + + - name: Logon to CyberArk Vault using PAS Web Services SDK + cyberark.pas.cyberark_authentication: + api_base_url: "http://components.cyberark.local" + validate_certs: false + username: "bizdev" + password: "Cyberark1" + + - name: Creating an Account using the PAS WebServices SDK + cyberark.pas.cyberark_account: + logging_level: DEBUG + identified_by: "address,username" + safe: "Test" + address: "cyberark.local" + username: "administrator-x" + platform_id: WinServerLocal + secret: "@N&Ibl3!" + platform_account_properties: + LogonDomain: "cyberark" + OwnerName: "ansible_user" + secret_management: + automatic_management_enabled: true + state: present + cyberark_session: "{{ cyberark_session }}" + register: cyberarkaction + + - name: Rotate credential via reconcile and providing the password to be changed to + cyberark.pas.cyberark_account: + identified_by: "address,username" + safe: "Domain_Admins" + address: "prod.cyberark.local" + username: "admin" + platform_id: WinDomain + platform_account_properties: + LogonDomain: "PROD" + secret_management: + new_secret: "Ama123ah12@#!Xaamdjbdkl@#112" + management_action: "reconcile" + automatic_management_enabled: true + state: present + cyberark_session: "{{ cyberark_session }}" + register: reconcileaccount + + - name: Logoff from CyberArk Vault + cyberark.pas.cyberark_authentication: + state: absent + cyberark_session: "{{ cyberark_session }}" +``` diff --git a/ansible_collections/cyberark/pas/docs/cyberark_authentication.md b/ansible_collections/cyberark/pas/docs/cyberark_authentication.md new file mode 100644 index 000000000..7ea60d591 --- /dev/null +++ b/ansible_collections/cyberark/pas/docs/cyberark_authentication.md @@ -0,0 +1,104 @@ +# cyberark_authentication + + +Authenticates to CyberArk Vault using Privileged Account Security Web Services SDK and creates a session fact that can be used by other modules. It returns an Ansible fact called `cyberark_session`. Every module can use this fact as `cyberark_session` parameter. + + +#### Available Fields +``` +options: + state: + default: present + choices: [present, absent] + description: + - Specifies if an authentication logon/logoff and a cyberark_session should be added/removed. + username: + description: + - The name of the user who will logon to the Vault. + password: + description: + - The password of the user. + new_password: + description: + - The new password of the user. This parameter is optional, and enables you to change a password. + api_base_url: + description: + - A string containing the base URL of the server hosting CyberArk's Privileged Account Security Web Services SDK. + validate_certs: + type: bool + default: 'true' + description: + - If C(false), SSL certificates will not be validated. This should only + set to C(false) used on personally controlled sites using self-signed + certificates. + use_shared_logon_authentication: + type: bool + default: 'false' + description: + - Whether or not Shared Logon Authentication will be used. + use_radius_authentication: + type: bool + default: 'false' + description: + - Whether or not users will be authenticated via a RADIUS server. Valid values are true/false. + cyberark_session: + description: + - Dictionary set by a CyberArk authentication containing the different values to perform actions on a logged-on CyberArk session. + timeout: + type: int + default: 10 + description: + - Allows you set a timeout for when your authenticating to Cyberark +``` +## Example Playbooks + +**Shared Logon Authentication.**<br/> +Shared authentication is based on a user credential file that is stored in the PVWA web server. During shared authentication, only the user defined in the credential file can log on to the PVWA, but multiple users can use the logon token. + +This type of authentication requires the playbook to manage the users as the Vault can't identify which specific user performs each action. + +Multiple concurrent connections can be created using the same token, without affecting each other. + +The shared user is defined in a user credential file, whose location is specified in the WSCredentialFile parameter, in the appsettings section of the PVWAweb.config file: + +```xml +<add key="WSCredentialFile" value="C:\CyberArk\Password Vault Web Access\CredFiles\WSUser.ini"/> +``` +> Make sure that this user can access the PVWA interface.<br/> +> Make sure the user only has the permissions in the Vault that they require. + +It is recommended to secure connections between Ansible and the REST Web Services when using Shared Logon Authentication, using Client Authentication. + +In addition to SSL, use Client Authentication to authenticate Ansible using a client certificate. + +[Configuring client authentication via certificates](https://docs.cyberark.com/Product-Doc/OnlineHelp/PAS/Latest/en/Content/SDK/Configuring%20Client%20Authentication%20via%20Client%20Certificates.htm) + +```yaml +- name: Logon to CyberArk Vault using PAS Web Services SDK - use_shared_logon_authentication + cyberark_authentication: + api_base_url: "{{ web_services_base_url }}" + use_shared_logon_authentication: true +``` + +**CyberArk Authentication**<br/> +This method authenticates a user to the Vault and returns a token that can be used in subsequent web services calls. In addition, this method allows you to set a new password. + +Users can authenticate using **CyberArk**, **LDAP** or **RADIUS** authentication. + +```yaml +- name: Logon to CyberArk Vault using PAS Web Services SDK - Not use_shared_logon_authentication + cyberark_authentication: + api_base_url: "{{ web_services_base_url }}" + username: "{{ password_object.password }}" + password: "{{ password_object.passprops.username }}" + use_shared_logon_authentication: false +``` +**Logoff**<br/> +This method logs off the user and removes the Vault session. + +```yaml +- name: Logoff from CyberArk Vault + cyberark_authentication: + state: absent + cyberark_session: "{{ cyberark_session }} +``` diff --git a/ansible_collections/cyberark/pas/docs/cyberark_credential.md b/ansible_collections/cyberark/pas/docs/cyberark_credential.md new file mode 100644 index 000000000..046d8b2a7 --- /dev/null +++ b/ansible_collections/cyberark/pas/docs/cyberark_credential.md @@ -0,0 +1,127 @@ +# cyberark_credential + +Creates a URI for retrieving a credential from a password object stored in the Cyberark Vault. The request uses the Privileged Account Security Web Services SDK through the Central Credential Provider by requesting access with an Application ID. + +**Requirements:** +- CyberArk AAM Central Credential Provider +- ApplicationID with the following permissions on the safe containing the credential being requested: + - List Accounts + - Retrieve Accounts +> **NOTE:** The CCP's Provider user (Prov_hostaname) needs to have the following permissions on the safe containing the credential being requested: +>> List Accounts<br> +>> Retrieve Accounts<br> +>> View Safe Members<br> + +## Query +This field is semicolon delimited value that is the exact syntax that goes in the URI<br> +If you use the `object` parameter then there is no need to use any other parameter as the ObjectID is a unique value.<br> +**Example:** +``` + query: "Safe=test;UserName=admin" + OR + query: "Object=OperatingSystem-administrator-dev.local" +``` + +## Available Fields + +``` +options: + api_base_url: + description: + - A string containing the base URL of the server hosting the Central Credential Provider + required: true + type: string + validate_certs: + description: + - If C(false), SSL certificate chain will not be validated. This should only set to C(true) if you have a root CA certificate installed on each node. + type: bool + required: false + default: false + type: bool + app_id: + description: + - A string containing the Application ID authorized for retrieving the credential + required: true + type: string + query: + description: + - A string containing details of the object being queried + required: true + parameters: + Safe=<safe name> + Folder=<folder name within safe> + Object=<object name> + UserName=<username of object> + Address=<address listed for object> + Database=<optional file category for database objects> + PolicyID=<platform id managing object> + connection_timeout: + description: + - An integer value of the allowed time before the request returns failed + required: false + default: '30' + type: integer + query_format: + description: + - The format for which your Query will be received by the CCP + required: false + default: 'Exact' + choices: [Exact, Regexp] + type: choice + fail_request_on_password_change: + description: + - A boolean parameter for completing the request in the middle of a password change of the requested credential + required: false + default: false + type: bool + client_cert: + description: + - A string containing the file location and name of the client certificate used for authentication + required: false + type: string + client_key: + description: + - A string containing the file location and name of the private key of the client certificate used for authentication + required: false + type: string + reason: + description: + - Reason for requesting credential if required by policy + required: false + type: string +``` + + + +## Example Playbooks + +```yaml +- name: credential retrieval basic + cyberark_credential: + api_base_url: "http://10.10.0.1" + app_id: "TestID" + query: "Safe=test;UserName=admin" + register: result + + result: + { api_base_url }"/AIMWebService/api/Accounts?AppId="{ app_id }"&Query="{ query } + + +- name: credential retrieval advanced + cyberark_credential: + api_base_url: "https://components.cyberark.local" + validate_certs: true + client_cert: /etc/pki/ca-trust/source/client.pem + client_key: /etc/pki/ca-trust/source/priv-key.pem + app_id: "TestID" + query: "Safe=test;UserName=admin" + connection_timeout: 60 + query_format: Exact + fail_request_on_password_change: true + reason: "requesting credential for Ansible deployment" + register: result + + result: + { api_base_url }"/AIMWebService/api/Accounts?AppId="{ app_id }"&Query="{ query }"&ConnectionTimeout="{ connection_timeout }"&QueryFormat="{ query_format }"&FailRequestOnPasswordChange="{ fail_request_on_password_change } + +``` diff --git a/ansible_collections/cyberark/pas/docs/cyberark_eda.md b/ansible_collections/cyberark/pas/docs/cyberark_eda.md new file mode 100644 index 000000000..d58b8f4b4 --- /dev/null +++ b/ansible_collections/cyberark/pas/docs/cyberark_eda.md @@ -0,0 +1,161 @@ +# Event-Driven Ansible + +The CyberArk PAM Self-Hosted solution can be configured to export syslogs to Ansible as an event source. Once Ansible EDA ingests the event it will act on rule-based criteria contained in an Ansible Rulebook and will call Ansible Playbooks to initiate action when the conditions contained in the rulebook are met. + +The following options will be available to configure CyberArk as Event Source: + +**VAULT** +* CyberArk to Rsyslog to EDA Webhook +* CyberArk to Rsyslog to EDA Kafka Topic +* CyberArk Syslog as EDA event source (UDP Protocol) + +**PTA** +* CyberArk PTA Syslog to EDA event source (UDP Protocol) + + +**NOTE**: For Rsyslog work, it was tested successfully with rsyslogd 8.2306.0.master (aka 2023.06) running on Ubuntu + + +## CyberArk to Rsyslog to EDA Webhook + +![CyberArk to Rsyslog to EDA Webhook](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/images/rsyslog-webhook.png?raw=true) + +### Vault Configuration +Follow the steps under [Security Information and Event Management (SIEM) Applications](https://docs.cyberark.com/Product-Doc/OnlineHelp/PAS/Latest/en/Content/PASIMP/DV-Integrating-with-SIEM-Applications.htm) documentation to setup the integration: + +* Copy the [cyberark-eda-json-v1.0.xsl](https://github.com/cyberark/ansible-security-automation-collection/blob/master/plugins/event_source/cyberark-eda-json-v1.0.xsl) XSL Translator file to the Server\Syslog folder. +* See sample syslog configuration for DBPARM.ini below +* Recommended to use TCP and port 514 as it's default rsyslog port. + +``` +[SYSLOG] +UseLegacySyslogFormat=Yes +SyslogTranslatorFile=Syslog\cyberark-eda-json-v1.0.xsl +SyslogServerIP=<INSERT RSYSLOG IP HERE> +SyslogServerPort=514 +SyslogServerProtocol=TCP +``` + +### Rsyslog server configuration to forward to event to webhook +Create a conf file in /etc/rsyslog.d/webhook.conf with the following content: +``` +template(name="CyberarkFormat" type="string" + string="%syslogtag%%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\n") +module(load="omprog") +if ($hostname == 'VAULT') then +{ + action(type="omprog" + binary="/tmp/run_curl.py" + output="/tmp/output.log" + template="CyberarkFormat") +} +``` + +Hostname will need be changed accordingly to your environment. If the syslog message are from a vault server, it will forward the event using the python script to the EDA webhook. + +Here is an example of a python script to forward the information using the webhook: +``` +#!/usr/bin/python +import urllib2 +import sys +value = sys.stdin.readline() +file = open('/tmp/debug.txt', 'a') +file.write(value) +file.close +#data = '{"nw_src": "10.0.0.1/32", "nw_dst": "10.0.0.2/32", "nw_proto": "ICMP", "actions": "ALLOW", "priority": "10"}' #test data +data = value +url = 'http://ubuntu:5000/endpoint' +req = urllib2.Request(url, data, {'Content-Type': 'application/json'}) +f = urllib2.urlopen(req) +for x in f: + print(x) +f.close() +``` + +![Sample run of disable_pas_user_webhook.yml](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/images/eda_disableuser_webhook.png?raw=true) + +## CyberArk to Rsyslog to EDA Kafka Topic + +![CyberArk to Rsyslog to EDA Kafka Topic](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/images/rsyslog-kafka.png?raw=true) + +### Vault Configuration +Follow the steps under [Security Information and Event Management (SIEM) Applications](https://docs.cyberark.com/Product-Doc/OnlineHelp/PAS/Latest/en/Content/PASIMP/DV-Integrating-with-SIEM-Applications.htm) documentation to setup the integration: + +* Copy the [cyberark-eda-json-v1.0.xsl](https://github.com/cyberark/ansible-security-automation-collection/blob/master/plugins/event_source/cyberark-eda-json-v1.0.xsl) XSL Translator file to the Server\Syslog folder. +* See sample syslog configuration for DBPARM.ini below +* Recommended to use TCP and port 514 as it's default rsyslog port. + +``` +[SYSLOG] +UseLegacySyslogFormat=Yes +SyslogTranslatorFile=Syslog\ansible-json-v1.0.xsl +SyslogServerIP=<INSERT RSYSLOG IP HERE> +SyslogServerPort=514 +SyslogServerProtocol=TCP +``` + +### Rsyslog server configuration to forward to Kafka topic +Create a conf file in /etc/rsyslog.d/ansible_kafka.conf with the following content: + +``` +$EscapeControlCharactersOnReceive off +template(name="CyberarkFormat" type="string" + string="%syslogtag%%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\n") +if ($hostname == 'VAULT') then +{ + action(type="omkafka" broker=["ubuntu:9092"] topic="ansible" confParam="compression.codec=snappy" template="CyberarkFormat") +} +``` + +Hostname will need be changed accordingly to your environment. If the syslog message are from a vault server, it will forward to a Kafka topic on Kafka server in ubuntu on port 9092. + +### Kafka Topic +Rsyslog use the omkafka plugin to forward the vault rsyslog to the Kafka topic. This plugin may need to be install by your admin as it's not enabled by default. +``` +yum install rsyslog-kafka +``` + +Refer to Kafka documentation: https://kafka.apache.org/quickstart how to stand up a Kafka server and view topic. + +![Sample rulebook](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/images/eda_disableuser_kafka.png?raw=true) + +The rulebook above listens on a Kafka ansible topic for suspended user and will disable the user and email admin via email. + +## CyberArk Syslog as EDA event source (UDP Protocol) +This EDA plugin acts as a syslog listener on specific port using UDP bypassing the need of an existing rsyslog server. + +![CyberArk Syslog as EDA event source (UDP Protocol)](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/images/eda-syslog.png?raw=true) + +Follow the steps under [Security Information and Event Management (SIEM) Applications](https://docs.cyberark.com/Product-Doc/OnlineHelp/PAS/Latest/en/Content/PASIMP/DV-Integrating-with-SIEM-Applications.htm) documentation to setup the integration: + + +* Copy the [cyberark-eda-json-v1.0.xsl](https://github.com/cyberark/ansible-security-automation-collection/blob/master/plugins/event_source/cyberark-eda-json-v1.0.xsl) XSL Translator file to the Server\Syslog folder. +* See sample syslog configuration for DBPARM.ini below +* • Currently only UDP is supported for the syslog event-source plugin + +``` +[SYSLOG] +UseLegacySyslogFormat=Yes +SyslogTranslatorFile=Syslog\ansible-json-v1.0.xsl +SyslogServerIP=<INSERT RSYSLOG IP HERE> +SyslogServerPort=1514 +SyslogServerProtocol=UDP +``` + +![Sample rulebook](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/images/eda_disable_user_syslog.png?raw=true) + + +## CyberArk PTA Syslog to EDA event source (UDP Protocol) + +![CyberArk PTA Syslog to EDA event source (UDP Protocol)](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/images/eda-pta-syslog.png?raw=true) + +Please refer to the following documentation for instructions on how to setup PTA to sent data to SIEM: +[Send PTA syslog Records to SIEM](https://docs.cyberark.com/Product-Doc/OnlineHelp/PAS/Latest/en/Content/PTA/Outbound-Sending-%20PTA-syslog-Records-to-SIEM.htm?tocpath=Administrator%7CComponents%7CPrivileged%20Threat%20Analytics%7CConfigure%20Privileged%20Threat%20Analytics%7CSend%20PTA%20Data%7CSend%20PTA%20syslog%20Records%20to%20SIEM%7C_____0) + +In the PTA server's local systemparm.properties file have a line with: + +``` +syslog_outbound=[{\"siem\": \"SIEM\", \"format\": \"CEF\", \"host\": \"ANSIBLE_EDA_SERVER\", \"port\": << PORT FOR THE ANSIBLE EVENT-SOURCE EDA PLUGIN >>, \"protocol\": \"UDP\"}] +``` + +![Sample rulebook](https://github.com/cyberark/ansible-security-automation-collection/blob/master/docs/images/eda_pta_disable_user_syslog.png?raw=true)
\ No newline at end of file diff --git a/ansible_collections/cyberark/pas/docs/cyberark_user.md b/ansible_collections/cyberark/pas/docs/cyberark_user.md new file mode 100644 index 000000000..c61e173ac --- /dev/null +++ b/ansible_collections/cyberark/pas/docs/cyberark_user.md @@ -0,0 +1,163 @@ +# cyberark_user + +This module allows admins to Add, Delete, and Modify CyberArk Vault Users. The ability to modify consists of the following: + +* Enable User<br> +* Disable User<br> +* Add/Remove Group<br> +* Set New Password<br> +* Force "change password at next login"<br> +* Modify User Information Fields<br> + * Email<br> + * First Name<br> + * Last Name<br> + * Expiry Date<br> + * User Type<br> + * Location<br> + +#### Limitations +**Idempotency** - All actions taken in the playbook adhere to the Ansible idempotency guidelines _except_ for password change. If you have the playbook set to modify a password it will "modify" the password every time the playbook is run, even if it is the same password.<br> +**Group Creation** - If the value for `group_name` does not exist in the Vault it will not create that group, the user action that was expected will fail. + +#### Available Fields + +``` +options: + username: + description: + - The name of the user who will be queried (for details), added, updated or deleted. + type: str + required: true + state: + description: + - Specifies the state needed for the user present for create user, absent for delete user. + type: str + choices: [ absent, present ] + default: present + cyberark_session: + description: + - Dictionary set by a CyberArk authentication containing the different values to perform actions on a logged-on CyberArk session, + please see M(cyberark_authentication) module for an example of cyberark_session. + type: dict + required: true + initial_password: + description: + - The password that the new user will use to log on the first time. + - This password must meet the password policy requirements. + - This parameter is required when state is present -- Add User. + type: str + new_password: + description: + - The user updated password. Make sure that this password meets the password policy requirements. + type: str + email: + description: + - The user email address. + type: str + first_name: + description: + - The user first name. + type: str + last_name: + description: + - The user last name. + type: str + change_password_on_the_next_logon: + description: + - Whether or not the user must change their password in their next logon. + type: bool + default: false + expiry_date: + description: + - The date and time when the user account will expire and become disabled. + type: str + user_type_name: + description: + - The type of user. + - The parameter defaults to C(EPVUser). + type: str + disabled: + description: + - Whether or not the user will be disabled. + type: bool + default: false + location: + description: + - The Vault Location for the user. + type: str + group_name: + description: + - The name of the group the user will be added to. + type: str +``` +## Example Playbooks + +This playbook will check if username `admin` exists, if it does not, it will provision the user in the Vault, add it to the `Auditors` group and set the account to be changed at first logon. + +```yaml +- name: Logon to CyberArk Vault using PAS Web Services SDK + cyberark_authentication: + api_base_url: https://components.cyberark.local + use_shared_logon_authentication: true + +- name: Create user, add to Group + cyberark_user: + username: admin + first_name: "Cyber" + last_name: "Admin" + email: "cyber.admin@ansibledev.com" + initial_password: PA$$Word123 + user_type_name: EPVUser + change_password_on_the_next_logon: true + group_name: Auditors + state: present + cyberark_session: '{{ cyberark_session }}' + register: cyberarkaction + +- name: Logoff from CyberArk Vault + cyberark_authentication: + state: absent + cyberark_session: '{{ cyberark_session }}' +``` + +This playbook will identify the user and delete it from the CyberArk Vault based on the `state: absent` parameter. + +```yaml +- name: Logon to CyberArk Vault using PAS Web Services SDK - use_shared_logon_authentication + cyberark_authentication: + api_base_url: "{{ web_services_base_url }}" + use_shared_logon_authentication: true + +- name: Removing a CyberArk User + cyberark_user: + username: "ansibleuser" + state: absent + cyberark_session: "{{ cyberark_session }}" + register: cyberarkaction + +- name: Logoff from CyberArk Vault + cyberark_authentication: + state: absent + cyberark_session: "{{ cyberark_session }}" +``` +This playbook is an example of disabling a user based on the `disabled: true` value with that authentication using the credential set in Tower. +```yaml +- name: Logon to CyberArk Vault using PAS Web Services SDK - Not use_shared_logon_authentication + cyberark_authentication: + api_base_url: "{{ web_services_base_url }}" + username: "{{ password_object.password }}" + password: "{{ password_object.passprops.username }}" + use_shared_logon_authentication: false + +- name: Disabling a CyberArk User + cyberark_user: + username: "ansibleuser" + disabled: true + cyberark_session: "{{ cyberark_session }}" + register: cyberarkaction + +- name: Logoff from CyberArk Vault + cyberark_authentication: + state: absent + cyberark_session: "{{ cyberark_session }}" +``` diff --git a/ansible_collections/cyberark/pas/docs/images/cyberark_logo.jpg b/ansible_collections/cyberark/pas/docs/images/cyberark_logo.jpg Binary files differnew file mode 100644 index 000000000..e24741c3f --- /dev/null +++ b/ansible_collections/cyberark/pas/docs/images/cyberark_logo.jpg diff --git a/ansible_collections/cyberark/pas/docs/images/eda-pta-syslog.png b/ansible_collections/cyberark/pas/docs/images/eda-pta-syslog.png Binary files differnew file mode 100644 index 000000000..213498341 --- /dev/null +++ b/ansible_collections/cyberark/pas/docs/images/eda-pta-syslog.png diff --git a/ansible_collections/cyberark/pas/docs/images/eda-syslog.png b/ansible_collections/cyberark/pas/docs/images/eda-syslog.png Binary files differnew file mode 100644 index 000000000..298f38ccb --- /dev/null +++ b/ansible_collections/cyberark/pas/docs/images/eda-syslog.png diff --git a/ansible_collections/cyberark/pas/docs/images/eda_disable_user_syslog.png b/ansible_collections/cyberark/pas/docs/images/eda_disable_user_syslog.png Binary files differnew file mode 100644 index 000000000..768aac4b0 --- /dev/null +++ b/ansible_collections/cyberark/pas/docs/images/eda_disable_user_syslog.png diff --git a/ansible_collections/cyberark/pas/docs/images/eda_disableuser_kafka.png b/ansible_collections/cyberark/pas/docs/images/eda_disableuser_kafka.png Binary files differnew file mode 100644 index 000000000..0d2c49f88 --- /dev/null +++ b/ansible_collections/cyberark/pas/docs/images/eda_disableuser_kafka.png diff --git a/ansible_collections/cyberark/pas/docs/images/eda_disableuser_webhook.png b/ansible_collections/cyberark/pas/docs/images/eda_disableuser_webhook.png Binary files differnew file mode 100644 index 000000000..9c3f54c30 --- /dev/null +++ b/ansible_collections/cyberark/pas/docs/images/eda_disableuser_webhook.png diff --git a/ansible_collections/cyberark/pas/docs/images/eda_pta_disable_user_syslog.png b/ansible_collections/cyberark/pas/docs/images/eda_pta_disable_user_syslog.png Binary files differnew file mode 100644 index 000000000..ed9c0d73c --- /dev/null +++ b/ansible_collections/cyberark/pas/docs/images/eda_pta_disable_user_syslog.png diff --git a/ansible_collections/cyberark/pas/docs/images/full-cyberark-logo.jpg b/ansible_collections/cyberark/pas/docs/images/full-cyberark-logo.jpg Binary files differnew file mode 100644 index 000000000..f44fdcdf0 --- /dev/null +++ b/ansible_collections/cyberark/pas/docs/images/full-cyberark-logo.jpg diff --git a/ansible_collections/cyberark/pas/docs/images/platform_account_properties.JPG b/ansible_collections/cyberark/pas/docs/images/platform_account_properties.JPG Binary files differnew file mode 100644 index 000000000..4ae11e3b6 --- /dev/null +++ b/ansible_collections/cyberark/pas/docs/images/platform_account_properties.JPG diff --git a/ansible_collections/cyberark/pas/docs/images/rsyslog-kafka.png b/ansible_collections/cyberark/pas/docs/images/rsyslog-kafka.png Binary files differnew file mode 100644 index 000000000..657923023 --- /dev/null +++ b/ansible_collections/cyberark/pas/docs/images/rsyslog-kafka.png diff --git a/ansible_collections/cyberark/pas/docs/images/rsyslog-webhook.png b/ansible_collections/cyberark/pas/docs/images/rsyslog-webhook.png Binary files differnew file mode 100644 index 000000000..4e713aafb --- /dev/null +++ b/ansible_collections/cyberark/pas/docs/images/rsyslog-webhook.png diff --git a/ansible_collections/cyberark/pas/meta/runtime.yml b/ansible_collections/cyberark/pas/meta/runtime.yml new file mode 100644 index 000000000..1b754c9c2 --- /dev/null +++ b/ansible_collections/cyberark/pas/meta/runtime.yml @@ -0,0 +1,2 @@ +--- + requires_ansible: '>=2.9'
\ No newline at end of file diff --git a/ansible_collections/cyberark/pas/plugins/.DS_Store b/ansible_collections/cyberark/pas/plugins/.DS_Store Binary files differnew file mode 100644 index 000000000..e707c03aa --- /dev/null +++ b/ansible_collections/cyberark/pas/plugins/.DS_Store diff --git a/ansible_collections/cyberark/pas/plugins/event_source/cyberark-eda-json-v1.0.xsl b/ansible_collections/cyberark/pas/plugins/event_source/cyberark-eda-json-v1.0.xsl new file mode 100644 index 000000000..d9ee36467 --- /dev/null +++ b/ansible_collections/cyberark/pas/plugins/event_source/cyberark-eda-json-v1.0.xsl @@ -0,0 +1,175 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> +<xsl:import href="./Syslog/RFC5424Changes.xsl"/> +<xsl:output method='text' version='1.0' encoding='UTF-8' indent='no'/> + +<!-- version control variables --> +<xsl:variable name="format" select="'cyberark-eda-json'"/> +<xsl:variable name="version" select="'1.0'"/> + +<!-- configuration --> +<xsl:variable name="include_raw" select="0"/> <!-- save a "raw" key with the original XML --> + +<!-- main object with header info --> +<xsl:template match="/"> + <xsl:apply-imports/> + <xsl:text>{"format":"</xsl:text><xsl:value-of select="$format"/> + <xsl:text>","version":"</xsl:text><xsl:value-of select="$version"/> + <xsl:text>"</xsl:text> + <xsl:choose> + <xsl:when test="$include_raw=1"> + <xsl:text>,"raw":</xsl:text> + <xsl:call-template name="json-string"> + <xsl:with-param name="text"> + <xsl:apply-templates select="*" mode="raw"/> + </xsl:with-param> + </xsl:call-template> + </xsl:when> + </xsl:choose> + <xsl:text>,</xsl:text> + <xsl:apply-templates select="*" mode="object"/> + <!-- this text below includes the terminating newline --> + <xsl:text>}
</xsl:text> +</xsl:template> + +<xsl:template match="*" mode="raw"> + <xsl:value-of select="concat('<', name())" /> + <xsl:for-each select="@*"> + <xsl:value-of select="concat(' ', name(), '="', ., '"')"/> + </xsl:for-each> + <xsl:text>></xsl:text> + <xsl:apply-templates mode="raw"/> + <xsl:value-of select="concat('</', name(), '>')" /> + </xsl:template> + +<!-- serialize objects --> +<xsl:template match="*" mode="object"> + <xsl:text>"</xsl:text> + <xsl:value-of select="name()"/><xsl:text>":</xsl:text><xsl:call-template name="value"> + <xsl:with-param name="parent" select="1"/> + </xsl:call-template> +</xsl:template> + +<!-- serialize array elements --> +<xsl:template match="*" mode="array"> + <xsl:call-template name="value"/> +</xsl:template> + +<!-- value of node serializer --> +<xsl:template name="value"> + <xsl:param name="parent"/> + <xsl:variable name="childName" select="name(*[1])"/> + <xsl:choose> + <xsl:when test="not(*|@*)"> + <xsl:choose> + <xsl:when test="$parent=1"> + <xsl:call-template name="json-string"> + <xsl:with-param name="text" select="."/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <xsl:call-template name="json-string"> + <xsl:with-param name="text" select="name()"/> + </xsl:call-template> + <xsl:text>:</xsl:text> + <xsl:call-template name="json-string"> + <xsl:with-param name="text" select="."/> + </xsl:call-template> + </xsl:otherwise> + </xsl:choose> + </xsl:when> + <xsl:when test="count(*[name()=$childName]) > 1"> + <xsl:text>{</xsl:text> + <xsl:call-template name="json-string"> + <xsl:with-param name="text" select="$childName"/> + </xsl:call-template> + <xsl:text>:[</xsl:text> + <xsl:apply-templates select="*" mode="array"/> + <xsl:text>]}</xsl:text> + </xsl:when> + <xsl:otherwise> + <xsl:text>{</xsl:text> + <xsl:apply-templates select="@*" mode="attrs"/> + <xsl:if test='count(@*)>0 and count(*)>0'>,</xsl:if> + <xsl:apply-templates select="*" mode="object"/> + <xsl:text>}</xsl:text> + </xsl:otherwise> + </xsl:choose> + <xsl:if test="following-sibling::*"><xsl:text>,</xsl:text></xsl:if> +</xsl:template> + +<!-- serialize attributes --> +<xsl:template match="@*" mode="attrs"> + <xsl:call-template name="json-string"> + <xsl:with-param name="text" select="name()"/> + </xsl:call-template> + <xsl:text>:</xsl:text> + <xsl:call-template name="json-string"> + <xsl:with-param name="text" select="."/> + </xsl:call-template> + <xsl:if test="position()!=last()"><xsl:text>,</xsl:text></xsl:if> +</xsl:template> + +<!-- json-string converts a text to a quoted and escaped JSON string --> +<xsl:template name="json-string"> + <xsl:param name="text"/> + <xsl:variable name="tmp"> + <xsl:call-template name="string-replace"> + <xsl:with-param name="string" select="$text"/> + <xsl:with-param name="from" select="'\'"/> + <xsl:with-param name="to" select="'\\'"/> + </xsl:call-template> + </xsl:variable> + <xsl:variable name="tmp2"> + <xsl:call-template name="string-replace"> + <xsl:with-param name="string" select="$tmp"/> + <xsl:with-param name="from" select="'
'"/> + <xsl:with-param name="to" select="'\n'"/> + </xsl:call-template> + </xsl:variable> + <xsl:variable name="tmp3"> + <xsl:call-template name="string-replace"> + <xsl:with-param name="string" select="$tmp2"/> + <xsl:with-param name="from" select="'
'"/> + <xsl:with-param name="to" select="'\r'"/> + </xsl:call-template> + </xsl:variable> + <xsl:variable name="tmp4"> + <xsl:call-template name="string-replace"> + <xsl:with-param name="string" select="$tmp3"/> + <xsl:with-param name="from" select="'	'"/> + <xsl:with-param name="to" select="'\t'"/> + </xsl:call-template> + </xsl:variable> + <xsl:text>"</xsl:text> + <xsl:call-template name="string-replace"> + <xsl:with-param name="string" select="$tmp4"/> + <xsl:with-param name="from" select="'"'"/> + <xsl:with-param name="to" select="'\"'"/> + </xsl:call-template> + <xsl:text>"</xsl:text> +</xsl:template> + +<!-- replace all occurences of the character(s) `from' + by the string `to' in the string `string'.--> +<xsl:template name="string-replace"> + <xsl:param name="string"/> + <xsl:param name="from"/> + <xsl:param name="to"/> + <xsl:choose> + <xsl:when test="contains($string,$from)"> + <xsl:value-of select="substring-before($string,$from)"/> + <xsl:value-of select="$to"/> + <xsl:call-template name="string-replace"> + <xsl:with-param name="string" select="substring-after($string,$from)"/> + <xsl:with-param name="from" select="$from"/> + <xsl:with-param name="to" select="$to"/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="$string"/> + </xsl:otherwise> + </xsl:choose> +</xsl:template> + +</xsl:stylesheet> diff --git a/ansible_collections/cyberark/pas/plugins/event_source/syslog.py b/ansible_collections/cyberark/pas/plugins/event_source/syslog.py new file mode 100644 index 000000000..2fbe1916c --- /dev/null +++ b/ansible_collections/cyberark/pas/plugins/event_source/syslog.py @@ -0,0 +1,173 @@ +#!/usr/bin/python +# Copyright: (c) 2017, Ansible Project +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import (absolute_import, division, print_function) + + +__metaclass__ = type + +""" +syslog.py + +An ansible-rulebook event source module for receiving events via a syslog. + +Arguments: + host: The hostname to listen to. Set to 0.0.0.0 to listen on all + interfaces. Defaults to 127.0.0.1 + port: The TCP port to listen to. Defaults to 1514 + +""" + +import asyncio +import json +import logging +import socketserver +from typing import Any, Dict +import re + +def parse(str_input): + """ + Parse a string in CEF format and return a dict with the header values + and the extension data. + """ + + logger = logging.getLogger() + # Create the empty dict we'll return later + values = dict() + + # This regex separates the string into the CEF header and the extension + # data. Once we do this, it's easier to use other regexes to parse each + # part. + header_re = r'((CEF:\d+)([^=\\]+\|){,7})(.*)' + + res = re.search(header_re, str_input) + + if res: + header = res.group(1) + extension = res.group(4) + + # Split the header on the "|" char. Uses a negative lookbehind + # assertion to ensure we don't accidentally split on escaped chars, + # though. + spl = re.split(r'(?<!\\)\|', header) + + # If the input entry had any blanks in the required headers, that's wrong + # and we should return. Note we explicitly don't check the last item in the + # split list becuase the header ends in a '|' which means the last item + # will always be an empty string (it doesn't exist, but the delimiter does). + if "" in spl[0:-1]: + logger.warning(f'Blank field(s) in CEF header. Is it valid CEF format?') + return None + + # Since these values are set by their position in the header, it's + # easy to know which is which. + values["DeviceVendor"] = spl[1] + values["DeviceProduct"] = spl[2] + values["DeviceVersion"] = spl[3] + values["DeviceEventClassID"] = spl[4] + values["Name"] = spl[5] + values["DeviceName"] = spl[5] + if len(spl) > 6: + values["Severity"] = spl[6] + values["DeviceSeverity"] = spl[6] + + # The first value is actually the CEF version, formatted like + # "CEF:#". Ignore anything before that (like a date from a syslog message). + # We then split on the colon and use the second value as the + # version number. + cef_start = spl[0].find('CEF') + if cef_start == -1: + return None + (cef, version) = spl[0][cef_start:].split(':') + values["CEFVersion"] = version + + # The ugly, gnarly regex here finds a single key=value pair, + # taking into account multiple whitespaces, escaped '=' and '|' + # chars. It returns an iterator of tuples. + spl = re.findall(r'([^=\s]+)=((?:[\\]=|[^=])+)(?:\s|$)', extension) + for i in spl: + # Split the tuples and put them into the dictionary + values[i[0]] = i[1] + + # Process custom field labels + for key in list(values.keys()): + # If the key string ends with Label, replace it in the appropriate + # custom field + if key[-5:] == "Label": + customlabel = key[:-5] + # Find the corresponding customfield and replace with the label + for customfield in list(values.keys()): + if customfield == customlabel: + values[values[key]] = values[customfield] + del values[customfield] + del values[key] + else: + # return None if our regex had now output + # logger.warning('Could not parse record. Is it valid CEF format?') + return None + + # Now we're done! + logger.debug('Returning values: ' + str(values)) + return values + + +class SyslogProtocol(asyncio.DatagramProtocol): + def __init__(self, edaQueue): + super().__init__() + self.edaQueue = edaQueue + def connection_made(self, transport) -> "Used by asyncio": + self.transport = transport + + def datagram_received(self, data, addr): + asyncio.get_event_loop().create_task(self.datagram_received_async( data, addr)) + + async def datagram_received_async(self, indata, addr) -> "Main entrypoint for processing message": + # Syslog event data received, and processed for EDA + logger = logging.getLogger() + rcvdata = indata.decode() + logger.info(f"Received Syslog message: {rcvdata}") + data = parse(rcvdata) + + if data is None: + # if not CEF, we will try JSON load of the text from first curly brace + try: + value = rcvdata[rcvdata.index("{"):len(rcvdata)] + #logger.info("value after encoding:%s", value1) + data = json.loads(value) + #logger.info("json:%s", data) + except json.decoder.JSONDecodeError as jerror: + logger.error(jerror) + data = rcvdata + except UnicodeError as e: + logger.error(e) + + if data: + #logger.info("json data:%s", data) + queue = self.edaQueue + await queue.put({"cyberark": data}) + +async def main(queue: asyncio.Queue, args: Dict[str, Any]): + logger = logging.getLogger() + + loop = asyncio.get_event_loop() + host = args.get("host") or '0.0.0.0' + port = args.get("port") or 1514 + transport, protocol = await asyncio.get_running_loop().create_datagram_endpoint( + lambda: SyslogProtocol(queue), + local_addr=((host, port))) + logger.info(f"Starting cyberark.pas.syslog [Host={host}, port={port}]") + try: + while True: + await asyncio.sleep(3600) # Serve for 1 hour. + finally: + transport.close() + + +if __name__ == "__main__": + + class MockQueue: + async def put(self, event): + pass #print(event) + + asyncio.run(main(MockQueue(), {})) diff --git a/ansible_collections/cyberark/pas/plugins/modules/cyberark_account.py b/ansible_collections/cyberark/pas/plugins/modules/cyberark_account.py new file mode 100644 index 000000000..e23b493bf --- /dev/null +++ b/ansible_collections/cyberark/pas/plugins/modules/cyberark_account.py @@ -0,0 +1,1309 @@ +#!/usr/bin/python +# Copyright: (c) 2017, Ansible Project +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import (absolute_import, division, print_function) + + +__metaclass__ = type + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "community", +} + +DOCUMENTATION = """ +--- +module: cyberark_account +short_description: Module for CyberArk Account object creation, deletion, and + modification using PAS Web Services SDK. +author: + - CyberArk BizDev (@cyberark-bizdev) + - Edward Nunez (@enunez-cyberark) + - James Stutes (@jimmyjamcabd) +version_added: '1.0.0' +description: + - Creates a URI for adding, deleting, modifying a privileged credential + within the Cyberark Vault. The request uses the Privileged Account + Security Web Services SDK. + + +options: + state: + description: + - Assert the desired state of the account C(present) to creat or + update and account object. Set to C(absent) for deletion of an + account object. + required: false + default: present + choices: [present, absent] + type: str + logging_level: + description: + - Parameter used to define the level of troubleshooting output to + the C(logging_file) value. + required: false + choices: [NOTSET, DEBUG, INFO] + type: str + logging_file: + description: + - Setting the log file name and location for troubleshooting logs. + required: false + default: /tmp/ansible_cyberark.log + type: str + api_base_url: + description: + - A string containing the base URL of the server hosting CyberArk's + Privileged Account Security Web Services SDK. + - Example U(https://<IIS_Server_Ip>/PasswordVault/api/) + required: false + type: str + validate_certs: + description: + - If C(false), SSL certificate chain will not be validated. This + should only set to C(true) if you have a root CA certificate + installed on each node. + required: false + default: true + type: bool + cyberark_session: + description: + - Dictionary set by a CyberArk authentication containing the + different values to perform actions on a logged-on CyberArk + session, please see M(cyberark.pas.cyberark_authentication) module for an + example of cyberark_session. + required: true + type: dict + identified_by: + description: + - When an API call is made to Get Accounts, often times the default + parameters passed will identify more than one account. This + parameter is used to confidently identify a single account when + the default query can return multiple results. + required: false + default: username,address,platform_id + type: str + safe: + description: + - The safe in the Vault where the privileged account is to be + located. + required: true + type: str + platform_id: + description: + - The PolicyID of the Platform that is to be managing the account + required: false + type: str + address: + description: + - The address of the endpoint where the privileged account is + located. + required: false + type: str + name: + description: + - The ObjectID of the account + required: false + type: str + secret_type: + description: + - The value that identifies what type of account it will be. + required: false + default: password + choices: [password, key] + type: str + secret: + description: + - The initial password for the creation of the account + required: false + type: str + new_secret: + description: + - The new secret/password to be stored in CyberArk Vault. + type: str + username: + description: + - The username associated with the account. + required: false + type: str + secret_management: + description: + - Set of parameters associated with the management of the + credential. + required: false + type: dict + suboptions: + automatic_management_enabled: + description: + - Parameter that indicates whether the CPM will manage + the password or not. + default: false + type: bool + manual_management_reason: + description: + - String value indicating why the CPM will NOT manage + the password. + type: str + management_action: + description: + - CPM action flag to be placed on the account object + for credential rotation. + choices: [change, change_immediately, reconcile] + type: str + new_secret: + description: + - The actual password value that will be assigned for + the CPM action to be taken. + type: str + perform_management_action: + description: + - C(always) will perform the management action in + every action. + - C(on_create) will only perform the management action + right after the account is created. + choices: [always, on_create] + default: always + type: str + remote_machines_access: + description: + - Set of parameters for defining PSM endpoint access targets. + required: false + type: dict + suboptions: + remote_machines: + description: + - List of targets allowed for this account. + type: str + access_restricted_to_remote_machines: + description: + - Whether or not to restrict access only to specified + remote machines. + type: bool + platform_account_properties: + description: + - Object containing key-value pairs to associate with the account, + as defined by the account platform. These properties are + validated against the mandatory and optional properties of the + specified platform's definition. Optional properties that do not + exist on the account will not be returned here. Internal + properties are not returned. + required: false + type: dict + suboptions: + KEY: + description: + - Freeform key value associated to the mandatory or + optional property assigned to the specified + Platform's definition. + aliases: [Port, ExtrPass1Name, database] + type: str +""" + +EXAMPLES = """ + collections: + - cyberark.pas + + tasks: + + - name: Logon to CyberArk Vault using PAS Web Services SDK + cyberark_authentication: + api_base_url: "http://components.cyberark.local" + validate_certs: false + username: "bizdev" + password: "Cyberark1" + + - name: Creating an Account using the PAS WebServices SDK + cyberark_account: + logging_level: DEBUG + identified_by: "address,username" + safe: "Test" + address: "cyberark.local" + username: "administrator-x" + platform_id: WinServerLocal + secret: "@N&Ibl3!" + platform_account_properties: + LogonDomain: "cyberark" + OwnerName: "ansible_user" + secret_management: + automatic_management_enabled: true + state: present + cyberark_session: "{{ cyberark_session }}" + register: cyberarkaction + + - name: + - Rotate credential via reconcile and providing the password to + bechanged to. + cyberark_account: + identified_by: "address,username" + safe: "Domain_Admins" + address: "prod.cyberark.local" + username: "admin" + platform_id: WinDomain + platform_account_properties: + LogonDomain: "PROD" + secret_management: + new_secret: "Ama123ah12@#!Xaamdjbdkl@#112" + management_action: "reconcile" + automatic_management_enabled: true + state: present + cyberark_session: "{{ cyberark_session }}" + register: reconcileaccount + + - name: Logoff from CyberArk Vault + cyberark_authentication: + state: absent + cyberark_session: "{{ cyberark_session }}" + +""" +RETURN = """ +changed: + description: + - Identify if the playbook run resulted in a change to the account in + any way. + returned: always + type: bool +failed: + description: Whether playbook run resulted in a failure of any kind. + returned: always + type: bool +status_code: + description: Result HTTP Status code. + returned: success + type: int + sample: "200, 201, -1, 204" +result: + description: A json dump of the resulting action. + returned: success + type: complex + contains: + address: + description: + - The adress of the endpoint where the privileged account is + located. + returned: successful addition and modification + type: str + sample: dev.local + createdTime: + description: + - Timeframe calculation of the timestamp of account creation. + returned: successful addition and modification + type: int + sample: "1567824520" + id: + description: Internal ObjectID for the account object identified + returned: successful addition and modification + type: int + sample: "25_21" + name: + description: The external ObjectID of the account + returned: successful addition and modification + type: str + sample: + - Operating System-WinServerLocal-cyberark.local-administrator + platformAccountProperties: + description: + - Object containing key-value pairs to associate with the + account, as defined by the account platform. + returned: successful addition and modification + type: complex + contains: + KEY VALUE: + description: + - Object containing key-value pairs to associate with the + account, as defined by the account platform. + returned: successful addition and modification + type: str + sample: + - "LogonDomain": "cyberark" + - "Port": "22" + platformId: + description: + - The PolicyID of the Platform that is to be managing the + account. + returned: successful addition and modification + type: str + sample: WinServerLocal + safeName: + description: + - The safe in the Vault where the privileged account is to + be located. + returned: successful addition and modification + type: str + sample: Domain_Admins + secretManagement: + description: + - Set of parameters associated with the management of + the credential. + returned: successful addition and modification + type: complex + contains: + automaticManagementEnabled: + description: + - Parameter that indicates whether the CPM will manage + the password or not. + returned: successful addition and modification + type: bool + lastModifiedTime: + description: + - Timeframe calculation of the timestamp of account + modification. + returned: successful addition and modification + type: int + sample: "1567824520" + manualManagementReason: + description: + - Reason for disabling automatic management of the account + returned: if C(automaticManagementEnabled) is set to false + type: str + sample: This is a static account + secretType: + description: + - The value that identifies what type of account it will be + returned: successful addition and modification + type: list + sample: + - key + - password + userName: + description: The username associated with the account + returned: successful addition and modification + type: str + sample: administrator +""" + + +from ansible.module_utils._text import to_text +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.urls import open_url +from ansible.module_utils.six.moves.urllib.error import HTTPError +from ansible.module_utils.six.moves.urllib.parse import quote +from ansible.module_utils.six.moves.http_client import HTTPException +import json +import logging + +_empty = object() + +ansible_specific_parameters = [ + "state", + "api_base_url", + "validate_certs", + "cyberark_session", + "identified_by", + "logging_level", + "logging_file", + "new_secret", + "secret_management.management_action", + "secret_management.new_secret", + "management_action", + "secret_management.perform_management_action", +] + +cyberark_fixed_properties = [ + "createdTime", + "id", + "name", + "lastModifiedTime", + "safeName", + "secretType", + "secret", +] + +removal_value = "NO_VALUE" + +cyberark_reference_fieldnames = { + "username": "userName", + "safe": "safeName", + "platform_id": "platformId", + "secret_type": "secretType", + "platform_account_properties": "platformAccountProperties", + "secret_management": "secretManagement", + "manual_management_reason": "manualManagementReason", + "automatic_management_enabled": "automaticManagementEnabled", + "remote_machines_access": "remoteMachinesAccess", + "access_restricted_to_remote_machines": "accessRestrictedToRemoteMachines", + "remote_machines": "remoteMachines", +} + +ansible_reference_fieldnames = { + "userName": "username", + "safeName": "safe", + "platformId": "platform_id", + "secretType": "secret_type", + "platformAccountProperties": "platform_account_properties", + "secretManagement": "secret_management", + "manualManagementReason": "manual_management_reason", + "automaticManagementEnabled": "automatic_management_enabled", + "remoteMachinesAccess": "remote_machines_access", + "accessRestrictedToRemoteMachines": "access_testricted_to_remoteMachines", + "remoteMachines": "remote_machines", +} + + +def equal_value(existing, parameter): + if isinstance(existing, str): + return existing == str(parameter) + elif isinstance(parameter, str): + return str(existing) == parameter + else: + return existing == parameter + + +def update_account(module, existing_account): + + logging.debug("Updating Account") + + cyberark_session = module.params["cyberark_session"] + api_base_url = cyberark_session["api_base_url"] + validate_certs = cyberark_session["validate_certs"] + + # Prepare result, end_point, and headers + result = {"result": existing_account} + changed = False + last_status_code = -1 + + HTTPMethod = "PATCH" + end_point = "/PasswordVault/api/Accounts/%s" % existing_account["id"] + + headers = { + "Content-Type": "application/json", + "Authorization": cyberark_session["token"], + "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" + } + + payload = {"Operations": []} + + # Determining whether to add or update properties + for parameter_name in list(module.params.keys()): + if ( + parameter_name not in ansible_specific_parameters + and module.params[parameter_name] is not None + ): + module_parm_value = module.params[parameter_name] + cyberark_property_name = referenced_value( + parameter_name, cyberark_reference_fieldnames, default=parameter_name + ) + existing_account_value = referenced_value( + cyberark_property_name, + existing_account, + keys=list(existing_account.keys()), + ) + if cyberark_property_name not in cyberark_fixed_properties: + if module_parm_value is not None and isinstance( + module_parm_value, dict + ): + # Internal child values + replacing = {} + adding = {} + removing = {} + for child_parm_name in list(module_parm_value.keys()): + nested_parm_name = "%s.%s" % (parameter_name, child_parm_name) + if nested_parm_name not in ansible_specific_parameters: + child_module_parm_value = module_parm_value[child_parm_name] + child_cyberark_property_name = referenced_value( + child_parm_name, + cyberark_reference_fieldnames, + default=child_parm_name, + ) + child_existing_account_value = referenced_value( + child_cyberark_property_name, + existing_account_value, + list(existing_account_value.keys()) + if existing_account_value is not None + else {}, + ) + path_value = "/%s/%s" % ( + cyberark_property_name, + child_cyberark_property_name, + ) + if child_existing_account_value is not None: + logging.debug( + ( + "child_module_parm_value: %s " + "child_existing_account_value=%s path=%s" + ), + child_module_parm_value, + child_existing_account_value, + path_value + ) + if child_module_parm_value == removal_value: + removing.update( + { + child_cyberark_property_name: child_existing_account_value + } + ) + elif ( + child_module_parm_value is not None + and not equal_value( + child_existing_account_value, + child_module_parm_value, + ) + ): + # Updating a property + replacing.update( + { + child_cyberark_property_name: child_module_parm_value + } + ) + elif ( + child_module_parm_value is not None + and child_module_parm_value != removal_value + ): + # Adding a property value + adding.update( + { + child_cyberark_property_name: child_module_parm_value + } + ) + logging.debug( + "parameter_name=%s value=%s existing=%s", + path_value, + child_module_parm_value, + child_existing_account_value + ) + # Processing child operations + if len(list(adding.keys())) > 0: + payload["Operations"].append( + { + "op": "add", + "path": "/%s" % cyberark_property_name, + "value": adding, + } + ) + if len(list(replacing.keys())) > 0: + payload["Operations"].append( + { + "op": "replace", + "path": "/%s" % cyberark_property_name, + "value": replacing, + } + ) + if len(removing) > 0: + payload["Operations"].append( + { + "op": "remove", + "path": "/%s" % cyberark_property_name, + "value": removing, + } + ) + else: + if existing_account_value is not None: + if module_parm_value == removal_value: + payload["Operations"].append( + {"op": "remove", "path": "/%s" % cyberark_property_name} + ) + elif not equal_value(existing_account_value, module_parm_value): + # Updating a property + payload["Operations"].append( + { + "op": "replace", + "value": module_parm_value, + "path": "/%s" % cyberark_property_name, + } + ) + elif module_parm_value != removal_value: + # Adding a property value + payload["Operations"].append( + { + "op": "add", + "value": module_parm_value, + "path": "/%s" % cyberark_property_name, + } + ) + logging.debug( + "parameter_name=%s value=%s existing=%s", + parameter_name, module_parm_value, existing_account_value + ) + + if len(payload["Operations"]) != 0: + if module.check_mode: + logging.debug("Proceeding with Update Account (CHECK_MODE)") + logging.debug("Operations => %s", json.dumps(payload)) + result = {"result": existing_account} + changed = True + last_status_code = -1 + else: + logging.debug("Proceeding with Update Account") + + logging.debug( + "Processing invidual operations (%d) => %s", + len(payload["Operations"]), + json.dumps(payload), + ) + for operation in payload["Operations"]: + individual_payload = [operation] + try: + logging.debug(" ==> %s", json.dumps([operation])) + response = open_url( + api_base_url + end_point, + method=HTTPMethod, + headers=headers, + data=json.dumps(individual_payload), + validate_certs=validate_certs, + ) + + result = {"result": json.loads(response.read())} + changed = True + last_status_code = response.getcode() + + # return (True, result, response.getcode()) + + except (HTTPError, HTTPException) as http_exception: + + if isinstance(http_exception, HTTPError): + res = json.load(http_exception) + else: + res = to_text(http_exception) + + module.fail_json( + msg=( + "Error while performing update_account." + "Please validate parameters provided." + "\n*** end_point=%s%s\n ==> %s" + % (api_base_url, end_point, res) + ), + payload=individual_payload, + headers=headers, + status_code=http_exception.code, + ) + + except Exception as unknown_exception: + + module.fail_json( + msg=( + "Unknown error while performing update_account." + "\n*** end_point=%s%s\n%s" + % (api_base_url, end_point, to_text(unknown_exception)) + ), + payload=individual_payload, + headers=headers, + status_code=-1, + ) + + return (changed, result, last_status_code) + + +def add_account(module): + + logging.debug("Adding Account") + + cyberark_session = module.params["cyberark_session"] + api_base_url = cyberark_session["api_base_url"] + validate_certs = cyberark_session["validate_certs"] + + # Prepare result, end_point, and headers + result = {} + HTTPMethod = "POST" + end_point = "/PasswordVault/api/Accounts" + + headers = { + "Content-Type": "application/json", + "Authorization": cyberark_session["token"], + "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" + } + + payload = {"safeName": module.params["safe"]} + + for parameter_name in list(module.params.keys()): + if ( + parameter_name not in ansible_specific_parameters + and module.params[parameter_name] is not None + ): + cyberark_property_name = referenced_value( + parameter_name, cyberark_reference_fieldnames, default=parameter_name + ) + if isinstance(module.params[parameter_name], dict): + payload[cyberark_property_name] = {} + for dict_key in list(module.params[parameter_name].keys()): + cyberark_child_property_name = referenced_value( + dict_key, cyberark_reference_fieldnames, default=dict_key + ) + logging.debug( + ( + "parameter_name =%s.%s cyberark_property_name=%s " + "cyberark_child_property_name=%s" + ), + parameter_name, + dict_key, + cyberark_property_name, + cyberark_child_property_name, + ) + if ( + parameter_name + "." + dict_key + not in ansible_specific_parameters + and module.params[parameter_name][dict_key] is not None + ): + payload[cyberark_property_name][ + cyberark_child_property_name + ] = deep_get( + module.params[parameter_name], dict_key, _empty, False + ) + else: + if parameter_name not in cyberark_reference_fieldnames: + module_parm_value = deep_get( + module.params, parameter_name, _empty, False + ) + if ( + module_parm_value is not None + and module_parm_value != removal_value + ): + payload[ + parameter_name + ] = module_parm_value # module.params[parameter_name] + else: + module_parm_value = deep_get( + module.params, parameter_name, _empty, True + ) + if ( + module_parm_value is not None + and module_parm_value != removal_value + ): + payload[ + cyberark_reference_fieldnames[parameter_name] + ] = module_parm_value # module.params[parameter_name] + logging.debug("parameter_name =%s", parameter_name) + + logging.debug("Add Account Payload => %s", json.dumps(payload)) + + try: + + if module.check_mode: + logging.debug("Proceeding with Add Account (CHECK_MODE)") + return (True, {"result": None}, -1) + else: + logging.debug("Proceeding with Add Account") + response = open_url( + api_base_url + end_point, + method=HTTPMethod, + headers=headers, + data=json.dumps(payload), + validate_certs=validate_certs, + ) + + result = {"result": json.loads(response.read())} + + return (True, result, response.getcode()) + + except (HTTPError, HTTPException) as http_exception: + + if isinstance(http_exception, HTTPError): + res = json.load(http_exception) + else: + res = to_text(http_exception) + + module.fail_json( + msg=( + "Error while performing add_account." + "Please validate parameters provided." + "\n*** end_point=%s%s\n ==> %s" % (api_base_url, end_point, res) + ), + payload=payload, + headers=headers, + status_code=http_exception.code, + ) + + except Exception as unknown_exception: + + module.fail_json( + msg=( + "Unknown error while performing add_account." + "\n*** end_point=%s%s\n%s" + % (api_base_url, end_point, to_text(unknown_exception)) + ), + payload=payload, + headers=headers, + status_code=-1, + ) + + +def delete_account(module, existing_account): + + if module.check_mode: + logging.debug("Deleting Account (CHECK_MODE)") + return (True, {"result": None}, -1) + else: + logging.debug("Deleting Account") + + cyberark_session = module.params["cyberark_session"] + api_base_url = cyberark_session["api_base_url"] + validate_certs = cyberark_session["validate_certs"] + + # Prepare result, end_point, and headers + result = {} + HTTPMethod = "DELETE" + end_point = "/PasswordVault/api/Accounts/%s" % existing_account["id"] + + headers = { + "Content-Type": "application/json", + "Authorization": cyberark_session["token"], + "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" + } + + try: + + response = open_url( + api_base_url + end_point, + method=HTTPMethod, + headers=headers, + validate_certs=validate_certs, + ) + + result = {"result": None} + + return (True, result, response.getcode()) + + except (HTTPError, HTTPException) as http_exception: + + if isinstance(http_exception, HTTPError): + res = json.load(http_exception) + else: + res = to_text(http_exception) + + module.fail_json( + msg=( + "Error while performing delete_account." + "Please validate parameters provided." + "\n*** end_point=%s%s\n ==> %s" % (api_base_url, end_point, res) + ), + headers=headers, + status_code=http_exception.code, + ) + + except Exception as unknown_exception: + + module.fail_json( + msg=( + "Unknown error while performing delete_account." + "\n*** end_point=%s%s\n%s" + % (api_base_url, end_point, to_text(unknown_exception)) + ), + headers=headers, + status_code=-1, + ) + + +def reset_account_if_needed(module, existing_account): + + cyberark_session = module.params["cyberark_session"] + api_base_url = cyberark_session["api_base_url"] + validate_certs = cyberark_session["validate_certs"] + + # Credential changes + management_action = deep_get( + module.params, "secret_management.management_action", "NOT_FOUND", False + ) + cpm_new_secret = deep_get( + module.params, "secret_management.new_secret", "NOT_FOUND", False + ) + logging.debug( + "management_action: %s cpm_new_secret: %s", management_action, cpm_new_secret + ) + + # Prepare result, end_point, and headers + result = {} + end_point = None + payload = {} + existing_account_id = None + if existing_account is not None: + existing_account_id = existing_account["id"] + elif module.check_mode: + existing_account_id = 9999 + + if ( + management_action == "change" + and cpm_new_secret is not None + and cpm_new_secret != "NOT_FOUND" + ): + logging.debug("CPM change secret for next CPM cycle") + end_point = ( + "/PasswordVault/API/Accounts/%s/SetNextPassword" + ) % existing_account_id + payload["ChangeImmediately"] = False + payload["NewCredentials"] = cpm_new_secret + elif management_action == "change_immediately" and ( + cpm_new_secret == "NOT_FOUND" or cpm_new_secret is None + ): + logging.debug("CPM change_immediately with random secret") + end_point = ("/PasswordVault/API/Accounts/%s/Change") % existing_account_id + payload["ChangeEntireGroup"] = True + elif management_action == "change_immediately" and ( + cpm_new_secret is not None and cpm_new_secret != "NOT_FOUND" + ): + logging.debug("CPM change immediately secret for next CPM cycle") + end_point = ( + "/PasswordVault/API/Accounts/%s/SetNextPassword" + ) % existing_account_id + payload["ChangeImmediately"] = True + payload["NewCredentials"] = cpm_new_secret + elif management_action == "reconcile": + logging.debug("CPM reconcile secret") + end_point = ("/PasswordVault/API/Accounts/%s/Reconcile") % existing_account_id + elif ( + "new_secret" in list(module.params.keys()) + and module.params["new_secret"] is not None + ): + logging.debug("Change Credential in Vault") + end_point = ( + "/PasswordVault/API/Accounts/%s/Password/Update" + ) % existing_account_id + payload["ChangeEntireGroup"] = True + payload["NewCredentials"] = module.params["new_secret"] + + if end_point is not None: + + if module.check_mode: + logging.debug("Proceeding with Credential Rotation (CHECK_MODE)") + return (True, result, -1) + else: + logging.debug("Proceeding with Credential Rotation") + + result = {"result": None} + headers = { + "Content-Type": "application/json", + "Authorization": cyberark_session["token"], + "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" + } + HTTPMethod = "POST" + try: + + response = open_url( + api_base_url + end_point, + method=HTTPMethod, + headers=headers, + data=json.dumps(payload), + validate_certs=validate_certs, + ) + + return (True, result, response.getcode()) + + except (HTTPError, HTTPException) as http_exception: + + if isinstance(http_exception, HTTPError): + res = json.load(http_exception) + else: + res = to_text(http_exception) + + module.fail_json( + msg=( + "Error while performing reset_account." + "Please validate parameters provided." + "\n*** end_point=%s%s\n ==> %s" + ) + % (api_base_url, end_point, res), + headers=headers, + payload=payload, + status_code=http_exception.code, + ) + + except Exception as unknown_exception: + + module.fail_json( + msg=( + "Unknown error while performing delete_account." + "\n*** end_point=%s%s\n%s" + % (api_base_url, end_point, to_text(unknown_exception)) + ), + headers=headers, + payload=payload, + status_code=-1, + ) + + else: + return (False, result, -1) + + +def referenced_value(field, dct, keys=None, default=None): + return dct[field] if field in (keys if keys is not None else dct) else default + + +def deep_get(dct, dotted_path, default=_empty, use_reference_table=True): + result_dct = {} + for key in dotted_path.split("."): + try: + key_field = key + if use_reference_table: + key_field = referenced_value( + key, cyberark_reference_fieldnames, default=key + ) + + if len(list(result_dct.keys())) == 0: # No result_dct set yet + result_dct = dct + + logging.debug( + "keys=%s key_field=>%s key=>%s", + ",".join(list(result_dct.keys())), + key_field, + key, + ) + result_dct = ( + result_dct[key_field] + if key_field in list(result_dct.keys()) + else result_dct[key] + ) + if result_dct is None: + return default + + except KeyError as e: + logging.debug("KeyError %s", to_text(e)) + if default is _empty: + raise + return default + return result_dct + + +def get_account(module): + + logging.debug("Finding Account") + + identified_by_fields = module.params["identified_by"].split(",") + logging.debug("Identified_by: %s", json.dumps(identified_by_fields)) + safe_filter = ( + quote("safeName eq ") + quote(module.params["safe"]) + if "safe" in module.params and module.params["safe"] is not None + else None + ) + search_string = None + for field in identified_by_fields: + if field not in ansible_specific_parameters: + search_string = "%s%s" % ( + search_string + " " if search_string is not None else "", + deep_get(module.params, field, "NOT FOUND", False), + ) + + logging.debug("Search_String => %s", search_string) + logging.debug("Safe Filter => %s", safe_filter) + + cyberark_session = module.params["cyberark_session"] + api_base_url = cyberark_session["api_base_url"] + validate_certs = cyberark_session["validate_certs"] + + end_point = None + if search_string is not None and safe_filter is not None: + end_point = "/PasswordVault/api/accounts?filter=%s&search=%s" % ( + safe_filter, + quote(search_string.lstrip()), + ) + elif search_string is not None: + end_point = ("/PasswordVault/api/accounts?search=%s") % (search_string.lstrip()) + else: + end_point = "/PasswordVault/api/accounts?filter=%s" % (safe_filter) + + logging.debug("End Point => %s", end_point) + + headers = { + "Content-Type": "application/json", + "Authorization": cyberark_session["token"], + "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" + } + + try: + + logging.debug("Executing: " + api_base_url + end_point) + response = open_url( + api_base_url + end_point, + method="GET", + headers=headers, + validate_certs=validate_certs, + ) + + result_string = response.read() + accounts_data = json.loads(result_string) + + logging.debug("RESULT => %s", json.dumps(accounts_data)) + + if accounts_data["count"] == 0: + return (False, None, response.getcode()) + else: + how_many = 0 + first_record_found = None + for account_record in accounts_data["value"]: + logging.debug("Acct Record => %s", json.dumps(account_record)) + found = False + for field in identified_by_fields: + record_field_value = deep_get(account_record, field, "NOT FOUND") + logging.debug( + ( + "Comparing field %s | record_field_name=%s " + "record_field_value=%s module.params_value=%s" + ), + field, + field, + record_field_value, + deep_get(module.params, field, "NOT FOUND"), + ) + if record_field_value != "NOT FOUND" and ( + record_field_value + == deep_get(module.params, field, "NOT FOUND", False) + ): + found = True + else: + found = False + break + if found: + how_many = how_many + 1 + if first_record_found is None: + first_record_found = account_record + + logging.debug( + "How Many: %d First Record Found => %s", + how_many, + json.dumps(first_record_found), + ) + if how_many > 1: # too many records found + module.fail_json( + msg=( + "Error while performing get_account. " + "Too many rows (%d) found matching your criteria!" + ) + % how_many + ) + else: + return (how_many == 1, first_record_found, response.getcode()) + + except (HTTPError, HTTPException) as http_exception: + + if http_exception.code == 404: + return (False, None, http_exception.code) + else: + module.fail_json( + msg=( + "Error while performing get_account." + "Please validate parameters provided." + "\n*** end_point=%s%s\n ==> %s" + % (api_base_url, end_point, to_text(http_exception)) + ), + headers=headers, + status_code=http_exception.code, + ) + + except Exception as unknown_exception: + + module.fail_json( + msg=( + "Unknown error while performing get_account." + "\n*** end_point=%s%s\n%s" + % (api_base_url, end_point, to_text(unknown_exception)) + ), + headers=headers, + status_code=-1, + ) + + +def main(): + + fields = { + "state": { + "type": "str", + "choices": ["present", "absent"], + "default": "present", + }, + "logging_level": {"type": "str", "choices": ["NOTSET", "DEBUG", "INFO"]}, + "logging_file": {"type": "str", "default": "/tmp/ansible_cyberark.log"}, + "api_base_url": {"type": "str"}, + "validate_certs": {"type": "bool", "default": "true"}, + "cyberark_session": {"required": True, "type": "dict", "no_log": True}, + "identified_by": { + "required": False, + "type": "str", + "default": "username,address,platform_id", + }, + "safe": {"required": True, "type": "str"}, + "platform_id": {"required": False, "type": "str"}, + "address": {"required": False, "type": "str"}, + "name": {"required": False, "type": "str"}, + "secret_type": { + "required": False, + "type": "str", + "choices": ["password", "key"], + "default": "password", + }, + "secret": {"required": False, "type": "str", "no_log": True}, + "new_secret": {"required": False, "type": "str", "no_log": True}, + "username": {"required": False, "type": "str"}, + "secret_management": { + "required": False, + "type": "dict", + "options": { + "automatic_management_enabled": { + "type": "bool", + "default": False, + }, + "manual_management_reason": {"type": "str"}, + "management_action": { + "type": "str", + "choices": ["change", "change_immediately", "reconcile"], + }, + "new_secret": {"type": "str", "no_log": True}, + "perform_management_action": { + "type": "str", + "choices": ["on_create", "always"], + "default": "always", + }, + }, + "no_log": False, + }, + "remote_machines_access": { + "required": False, + "type": "dict", + "options": { + "remote_machines": {"type": "str"}, + "access_restricted_to_remote_machines": {"type": "bool"}, + }, + }, + "platform_account_properties": {"required": False, "type": "dict"}, + } + + module = AnsibleModule(argument_spec=fields, supports_check_mode=True) + + if module.params["logging_level"] is not None: + logging.basicConfig( + filename=module.params["logging_file"], level=module.params["logging_level"] + ) + + logging.info("Starting Module") + + state = module.params["state"] + + (found, account_record, status_code) = get_account(module) + logging.debug( + "Account was %s, status_code=%s", "FOUND" if found else "NOT FOUND", status_code + ) + + changed = False + result = {"result": account_record} + + if state == "present": + + if found: # Account already exists + (changed, result, status_code) = update_account(module, account_record) + else: # Account does not exist + (changed, result, status_code) = add_account(module) + + perform_management_action = "always" + if "secret_management" in list(module.params.keys()): + secret_management = module.params["secret_management"] + if secret_management is not None and "perform_management_action" in list( + secret_management.keys() + ): + perform_management_action = secret_management[ + "perform_management_action" + ] + + logging.debug("Result=>%s", json.dumps(result)) + if perform_management_action == "always" or ( + perform_management_action == "on_create" and not found + ): + (account_reset, no_result, no_status_code) = reset_account_if_needed( + module, result["result"] + ) + if account_reset: + changed = True + + elif found and state == "absent": + (changed, result, status_code) = delete_account(module, account_record) + + module.exit_json(changed=changed, result=result, status_code=status_code) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cyberark/pas/plugins/modules/cyberark_authentication.py b/ansible_collections/cyberark/pas/plugins/modules/cyberark_authentication.py new file mode 100644 index 000000000..c8efae3ed --- /dev/null +++ b/ansible_collections/cyberark/pas/plugins/modules/cyberark_authentication.py @@ -0,0 +1,392 @@ +#!/usr/bin/python +# Copyright: (c) 2017, Ansible Project +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import (absolute_import, division, print_function) + + +__metaclass__ = type + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "certified", +} + +DOCUMENTATION = """ +--- +module: cyberark_authentication +short_description: CyberArk Authentication using PAS Web Services SDK. +author: + - Edward Nunez (@enunez-cyberark) + - Cyberark Bizdev (@cyberark-bizdev) +version_added: '1.0.0' +description: + - Authenticates to CyberArk Vault using Privileged Account Security + Web Services SDK and creates a session fact that can be used by other + modules. It returns an Ansible fact called I(cyberark_session). Every + module can use this fact as C(cyberark_session) parameter. +options: + state: + default: present + choices: [present, absent] + description: + - Specifies if an authentication logon/logoff and a + cyberark_session should be added/removed. + type: str + username: + description: + - The name of the user who will logon to the Vault. + type: str + password: + description: + - The password of the user. + type: str + new_password: + description: + - The new password of the user. This parameter is optional, + and enables you to change a password. + type: str + api_base_url: + description: + - A string containing the base URL of the server hosting + CyberArk's Privileged Account Security Web Services SDK. + type: str + validate_certs: + type: bool + default: 'true' + description: + - If C(false), SSL certificates will not be validated. This + should only set to C(false) used on personally controlled + sites using self-signed certificates. + use_ldap_authentication: + type: bool + default: 'false' + description: + - Whether or not LDAP will be used. + use_windows_authentication: + type: bool + default: 'false' + description: + - Whether or not Windows will be used. + use_cyberark_authentication: + type: bool + default: 'false' + description: + - Whether or not LDAP will be used. + use_radius_authentication: + type: bool + default: 'false' + description: + - Whether or not users will be authenticated via a RADIUS + server. Valid values are true/false. + connection_number: + type: int + description: + - To support multiple connections for same user specify + - different value for this parameter. + concurrentSession: + type: bool + default: false + description: + - Whether or not to allow concurrent sessions for the same user. + cyberark_session: + description: + - Dictionary set by a CyberArk authentication containing the + different values to perform actions on a logged-on CyberArk + session. + type: dict + timeout: + description: + - Allows you set a timeout for when your authenticating to Cyberark + default: 10 + type: int +""" + +EXAMPLES = """ +- name: Logon - use_shared_logon_authentication + cyberark_authentication: + api_base_url: "{{ web_services_base_url }}" + use_shared_logon_authentication: true + +- name: Logon - Not use_shared_logon_authentication + cyberark_authentication: + api_base_url: "{{ web_services_base_url }}" + username: "{{ password_object.password }}" + password: "{{ password_object.passprops.username }}" + use_shared_logon_authentication: false + +- name: Logoff from CyberArk Vault + cyberark_authentication: + state: absent + cyberark_session: "{{ cyberark_session }}" +""" + +RETURN = """ +cyberark_session: + description: Authentication facts. + returned: success + type: complex + contains: + api_base_url: + description: + - Base URL for API calls. Returned in the cyberark_session, + so it can be used in subsequent calls. + type: str + returned: always + token: + description: + - The token that identifies the session, encoded in BASE 64. + type: str + returned: always + use_shared_logon_authentication: + description: + - Whether or not Shared Logon Authentication was used to + establish the session. + type: bool + returned: always + validate_certs: + description: Whether or not SSL certificates should be validated. + type: bool + returned: always +""" + +from ansible.module_utils._text import to_text +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.urls import open_url +from ansible.module_utils.six.moves.urllib.error import HTTPError +from ansible.module_utils.six.moves.http_client import HTTPException +import json + + +def processAuthentication(module): + + # Getting parameters from module + + api_base_url = module.params["api_base_url"] + validate_certs = module.params["validate_certs"] + username = module.params["username"] + password = module.params["password"] + new_password = module.params["new_password"] + + use_radius = module.params["use_radius_authentication"] + use_ldap = module.params["use_ldap_authentication"] + use_windows = module.params["use_windows_authentication"] + use_cyberark = module.params["use_cyberark_authentication"] + + # connection_number = module.params["connection_number"] + state = module.params["state"] + cyberark_session = module.params["cyberark_session"] + + concurrentSession = module.params["concurrentSession"] + + timeout = module.params["timeout"] + + # if in check mode it will not perform password changes + if module.check_mode and new_password is not None: + new_password = None + + # Defining initial values for open_url call + headers = { + "Content-Type": "application/json", + "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" + } + + payload = "" + + if state == "present": # Logon Action + + # Different end_points based on the use of desired method of auth + + if use_ldap: + end_point = "/PasswordVault/API/Auth/LDAP/Logon" + payload_dict = {"username": username, "password": password} + + elif use_radius: + end_point = "/PasswordVault/API/Auth/radius/Logon" + + elif use_windows: + end_point = "/PasswordVault/API/auth/Windows/Logon" + + else: + use_cyberark = True + end_point = "/PasswordVault/API/Auth/CyberArk/Logon" + + # The payload will contain username, password + # and optionally use_radius_authentication and new_password + payload_dict = {"username": username, "password": password} + + if new_password is not None and use_cyberark: + payload_dict["newPassword"] = new_password + + # COMMENT: I dont know what this is for and the old api seems like it didnt have this field + # if connection_number is not None: + # payload_dict["connectionNumber"] = connection_number + + if concurrentSession: + payload_dict["concurrentSession"] = True + + payload = json.dumps(payload_dict) + + else: # Logoff Action + + # Get values from cyberark_session already established + api_base_url = cyberark_session["api_base_url"] + validate_certs = cyberark_session["validate_certs"] + + headers["Authorization"] = cyberark_session["token"] + + # All off the logoff with the same endpoint + end_point = "/PasswordVault/API/Auth/Logoff" + + result = None + changed = False + response = None + + try: + + response = open_url( + api_base_url + end_point, + method="POST", + headers=headers, + data=payload, + validate_certs=validate_certs, + timeout=timeout, + ) + + except (HTTPError, HTTPException) as http_exception: + + module.fail_json( + msg=( + "Error while performing authentication." + "Please validate parameters provided, and ability to logon to " + "CyberArk.\n*** end_point=%s%s\n ==> %s" + ) + % (api_base_url, end_point, to_text(http_exception)), + payload=payload, + headers=headers, + status_code=http_exception.code, + ) + + except Exception as unknown_exception: + + module.fail_json( + msg=( + "Unknown error while performing authentication." + "\n*** end_point=%s%s\n%s" + % (api_base_url, end_point, to_text(unknown_exception)) + ), + payload=payload, + headers=headers, + status_code=-1, + ) + + if response.getcode() == 200: # Success + + if state == "present": # Logon Action + + # Result token from REST Api uses a different key based + # the use of shared logon authentication + token = "" + try: + token = str(json.loads(response.read())) + + # the new one just returns a token + # if use: + # token = json.loads(response.read())["LogonResult"] + # else: + # token = json.loads(response.read())["CyberArkLogonResult"] + except Exception as e: + module.fail_json( + msg="Error obtaining token\n%s" % (to_text(e)), + payload=payload, + headers=headers, + status_code=-1, + ) + + # Preparing result of the module + result = { + "cyberark_session": { + "token": token, + "api_base_url": api_base_url, + "validate_certs": validate_certs, + } + } + + if new_password is not None: + # Only marks change if new_password was received resulting + # in a password change + changed = True + + else: # Logoff Action clears cyberark_session + + result = {"cyberark_session": {}} + + return (changed, result, response.getcode()) + + else: + module.fail_json(msg="error in end_point=>" + end_point, headers=headers) + + +def main(): + + fields = { + "api_base_url": {"type": "str"}, + "validate_certs": {"type": "bool", "default": "true"}, + "username": {"type": "str"}, + "password": {"type": "str", "no_log": True}, + "new_password": {"type": "str", "no_log": True}, + "use_radius_authentication": {"default": False, "type": "bool"}, + "use_windows_authentication": {"default": False, "type": "bool"}, + "use_ldap_authentication": {"default": False, "type": "bool"}, + "use_cyberark_authentication": {"default": False, "type": "bool"}, + "concurrentSession": {"default": False, "type": "bool"}, + "connection_number": {"type": "int"}, + "state": { + "type": "str", + "choices": ["present", "absent"], + "default": "present", + }, + "cyberark_session": {"type": "dict"}, + "timeout": {"default": 10, "type": "int"}, + } + + # cyberark and radius -> mutually_exclusive is cyberark and ldap + # ldap and radius + # windows has to be by itself + + mutually_exclusive = [ + [ + "use_windows_authentication", + "use_ldap_authentication", + "use_cyberark_authentication", + "use_radius_authentication", + ], + ["use_radius_authentication", "new_password"], + ["use_windows_authentication", "new_password"], + ["use_ldap_authentication", "new_password"], + ["api_base_url", "cyberark_session"], + ] + + required_if = [ + ("state", "present", ["api_base_url"]), + ("state", "absent", ["cyberark_session"]), + ] + + required_together = [["username", "password"]] + + module = AnsibleModule( + argument_spec=fields, + mutually_exclusive=mutually_exclusive, + required_if=required_if, + required_together=required_together, + supports_check_mode=True, + ) + + (changed, result, status_code) = processAuthentication(module) + + module.exit_json(changed=changed, ansible_facts=result, status_code=status_code) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cyberark/pas/plugins/modules/cyberark_credential.py b/ansible_collections/cyberark/pas/plugins/modules/cyberark_credential.py new file mode 100644 index 000000000..128d03b32 --- /dev/null +++ b/ansible_collections/cyberark/pas/plugins/modules/cyberark_credential.py @@ -0,0 +1,337 @@ +#!/usr/bin/python +# Copyright: (c) 2017, Ansible Project +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import (absolute_import, division, print_function) + + +__metaclass__ = type + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "community", +} + +DOCUMENTATION = """ +--- +module: cyberark_credential +short_description: Credential retrieval using AAM Central Credential Provider. +author: + - Edward Nunez (@enunez-cyberark) + - CyberArk BizDev (@cyberark-bizdev) + - Erasmo Acosta (@erasmix) + - James Stutes (@JimmyJamCABD) +version_added: '1.0.0' +description: + - Creates a URI for retrieving a credential from a password object stored + in the Cyberark Vault. The request uses the Privileged Account Security + Web Services SDK through the Central Credential Provider by requesting + access with an Application ID. + +options: + api_base_url: + type: str + required: true + description: + - A string containing the base URL of the server hosting the + Central Credential Provider. + validate_certs: + type: bool + required: false + default: true + description: + - If C(false), SSL certificate chain will not be validated. This + should only set to C(true) if you have a root CA certificate + installed on each node. + app_id: + type: str + required: true + description: + - A string containing the Application ID authorized for retrieving + the credential. + query: + type: str + required: true + description: + - A string containing details of the object being queried; + - Possible parameters could be Safe, Folder, Object + - (internal account name), UserName, Address, Database, + - PolicyID. + connection_timeout: + type: int + required: false + default: '30' + description: + - An integer value of the allowed time before the request returns + failed. + query_format: + type: str + required: false + default: Exact + choices: [Exact, Regexp] + description: + - The format for which your Query will be received by the CCP. + fail_request_on_password_change: + type: bool + required: false + default: false + description: + - A boolean parameter for completing the request in the middle of + a password change of the requested credential. + client_cert: + type: str + required: false + description: + - A string containing the file location and name of the client + certificate used for authentication. + client_key: + type: str + required: false + description: + - A string containing the file location and name of the private + key of the client certificate used for authentication. + reason: + type: str + required: false + description: + - Reason for requesting credential if required by policy; + - It must be specified if the Policy managing the object + - requires it. +""" + +EXAMPLES = """ + tasks: + - name: credential retrieval basic + cyberark_credential: + api_base_url: "http://10.10.0.1" + app_id: "TestID" + query: "Safe=test;UserName=admin" + register: result + + - name: credential retrieval advanced + cyberark_credential: + api_base_url: "https://components.cyberark.local" + validate_certs: true + client_cert: /etc/pki/ca-trust/source/client.pem + client_key: /etc/pki/ca-trust/source/priv-key.pem + app_id: "TestID" + query: "Safe=test;UserName=admin" + connection_timeout: 60 + query_format: Exact + fail_request_on_password_change: true + reason: "requesting credential for Ansible deployment" + register: result + +""" + +RETURN = """ +changed: + description: + - Identify if the playbook run resulted in a change to the account in + any way. + returned: always + type: bool +failed: + description: Whether playbook run resulted in a failure of any kind. + returned: always + type: bool +status_code: + description: Result HTTP Status code. + returned: success + type: int + sample: "200, 201, -1, 204" +result: + description: A json dump of the resulting action. + returned: success + type: complex + contains: + Address: + description: The target address of the credential being queried + type: str + returned: if required + Content: + description: The password for the object being queried + type: str + returned: always + CreationMethod: + description: This is how the object was created in the Vault + type: str + returned: always + DeviceType: + description: + - An internal File Category for more granular management of + Platforms. + type: str + returned: always + Folder: + description: + - The folder within the Safe where the credential is stored. + type: str + returned: always + Name: + description: + - The Cyberark unique object ID of the credential being + queried. + type: str + returned: always + PasswordChangeInProcess: + description: If the password has a change flag placed by the CPM + type: bool + returned: always + PolicyID: + description: Whether or not SSL certificates should be validated. + type: str + returned: if assigned to a policy + Safe: + description: The safe where the queried credential is stored + type: str + returned: always + Username: + description: The username of the credential being queried + type: str + returned: if required + LogonDomain: + description: The Address friendly name resolved by the CPM + type: str + returned: if populated + CPMDisabled: + description: + - A description of why this vaulted credential is not being + managed by the CPM. + type: str + returned: if CPM management is disabled and a reason is given +""" + +from ansible.module_utils._text import to_text +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.urls import open_url +from ansible.module_utils.six.moves.urllib.error import HTTPError +from ansible.module_utils.six.moves.urllib.parse import quote +from ansible.module_utils.six.moves.http_client import HTTPException +import json + + +def retrieve_credential(module): + + # Getting parameters from module + + api_base_url = module.params["api_base_url"] + validate_certs = module.params["validate_certs"] + app_id = module.params["app_id"] + query = module.params["query"] + connection_timeout = module.params["connection_timeout"] + query_format = module.params["query_format"] + fail_request_on_password_change = module.params["fail_request_on_password_change"] + client_cert = None + client_key = None + + if "client_cert" in module.params: + client_cert = module.params["client_cert"] + if "client_key" in module.params: + client_key = module.params["client_key"] + + end_point = ( + "/AIMWebService/api/Accounts?AppId=%s&Query=%s&" + "ConnectionTimeout=%s&QueryFormat=%s" + "&FailRequestOnPasswordChange=%s" + ) % ( + quote(app_id), + quote(query), + connection_timeout, + query_format, + fail_request_on_password_change, + ) + + if "reason" in module.params and module.params["reason"] is not None: + reason = quote(module.params["reason"]) + end_point = end_point + "&reason=%s" % reason + + result = None + response = None + + try: + + response = open_url( + api_base_url + end_point, + method="GET", + validate_certs=validate_certs, + client_cert=client_cert, + client_key=client_key, + ) + + except (HTTPError, HTTPException) as http_exception: + + module.fail_json( + msg=( + "Error while retrieving credential." + "Please validate parameters provided, and permissions for " + "the application and provider in CyberArk." + "\n*** end_point=%s%s\n ==> %s" + % (api_base_url, end_point, to_text(http_exception)) + ), + status_code=http_exception.code, + ) + + except Exception as unknown_exception: + + module.fail_json( + msg=( + "Unknown error while retrieving credential." + "\n*** end_point=%s%s\n%s" + % (api_base_url, end_point, to_text(unknown_exception)) + ), + status_code=-1, + ) + + if response.getcode() == 200: # Success + + # Result token from REST Api uses a different key based + try: + result = json.loads(response.read()) + except Exception as exc: + module.fail_json( + msg=("Error obtain cyberark credential result " "from http body\n%s") + % (to_text(exc)), + status_code=-1, + ) + + return (result, response.getcode()) + + else: + module.fail_json(msg="error in end_point=>" + end_point) + + +def main(): + + fields = { + "api_base_url": {"required": True, "type": "str"}, + "app_id": {"required": True, "type": "str"}, + "query": {"required": True, "type": "str"}, + "reason": {"required": False, "type": "str"}, + "connection_timeout": {"required": False, "type": "int", "default": 30}, + "query_format": { + "required": False, + "type": "str", + "choices": ["Exact", "Regexp"], + "default": "Exact", + }, + "fail_request_on_password_change": { + "required": False, + "type": "bool", + "default": False, + }, + "validate_certs": {"type": "bool", "default": True}, + "client_cert": {"type": "str", "required": False}, + "client_key": {"type": "str", "required": False, "no_log": True}, + } + + module = AnsibleModule(argument_spec=fields, supports_check_mode=True) + + (result, status_code) = retrieve_credential(module) + + module.exit_json(changed=False, result=result, status_code=status_code) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cyberark/pas/plugins/modules/cyberark_user.py b/ansible_collections/cyberark/pas/plugins/modules/cyberark_user.py new file mode 100644 index 000000000..2e4bedbe9 --- /dev/null +++ b/ansible_collections/cyberark/pas/plugins/modules/cyberark_user.py @@ -0,0 +1,782 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Ansible Project +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import (absolute_import, division, print_function) + + +__metaclass__ = type + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "certified", +} + +DOCUMENTATION = r""" +--- +module: cyberark_user +short_description: CyberArk User Management using PAS Web Services SDK. +author: + - Edward Nunez (@enunez-cyberark) + - Cyberark Bizdev (@cyberark-bizdev) + - Erasmo Acosta (@erasmix) + - James Stutes (@jimmyjamcabd) +version_added: '1.0.0' +description: + - CyberArk User Management using PAS Web Services SDK, + It currently supports the following actions Get User Details, Add User, + Update User, Delete User. + +options: + username: + description: + - The name of the user who will be queried (for details), added, + updated or deleted. + type: str + required: true + state: + description: + - Specifies the state needed for the user present for create user, + absent for delete user. + type: str + choices: [ absent, present ] + default: present + logging_level: + description: + - Parameter used to define the level of troubleshooting output to + the C(logging_file) value. + required: false + choices: [NOTSET, DEBUG, INFO] + default: NOTSET + type: str + logging_file: + description: + - Setting the log file name and location for troubleshooting logs. + required: false + default: /tmp/ansible_cyberark.log + type: str + cyberark_session: + description: + - Dictionary set by a CyberArk authentication containing the + different values to perform actions on a logged-on CyberArk + session, please see M(cyberark.pas.cyberark_authentication) module for an + example of cyberark_session. + type: dict + required: true + initial_password: + description: + - The password that the new user will use to log on the first time. + - This password must meet the password policy requirements. + - This parameter is required when state is present -- Add User. + type: str + new_password: + description: + - The user updated password. Make sure that this password meets + the password policy requirements. + type: str + email: + description: + - The user email address. + type: str + first_name: + description: + - The user first name. + type: str + last_name: + description: + - The user last name. + type: str + change_password_on_the_next_logon: + description: + - Whether or not the user must change their password in their + next logon. + type: bool + default: false + domain_name: + description: + - The name of the user domain. + type: str + member_type: + description: + - The type of member. + type: str + expiry_date: + description: + - The date and time when the user account will expire and become + disabled. + type: str + user_type_name: + description: + - The type of user. + - The parameter defaults to C(EPVUser). + type: str + disabled: + description: + - Whether or not the user will be disabled. + type: bool + default: false + location: + description: + - The Vault Location for the user. + type: str + group_name: + description: + - The name of the group the user will be added to. + - Causes an additional lookup in cyberark + - Will be ignored if vault_id is used + - Will cause a failure if group is missing or more than one group with that name exists + type: str + timeout: + description: + - How long to wait for the server to send data before giving up + type: float + default: 10 + vault_id: + description: + - The ID of the user group to add the user to + - Prefered over group_name + type: int + authorization: + description: + - A list of authorization options for this user. + - Options can include AddSafes and AuditUsers + - The default provides backwards compatability with older versions of the collection + type: list + elements: str + default: + - AddSafes + - AuditUsers +""" + +EXAMPLES = r""" +- name: Logon to CyberArk Vault using PAS Web Services SDK + cyberark_authentication: + api_base_url: https://components.cyberark.local + use_shared_logon_authentication: true + +- name: Create user & immediately add it to a group + cyberark_user: + username: username + initial_password: password + user_type_name: EPVUser + change_password_on_the_next_logon: false + group_name: GroupOfUser + state: present + cyberark_session: '{{ cyberark_session }}' + +- name: Make sure user is present and reset user credential if present + cyberark_user: + username: Username + new_password: password + disabled: false + state: present + cyberark_session: '{{ cyberark_session }}' + +- name: Logoff from CyberArk Vault + cyberark_authentication: + state: absent + cyberark_session: '{{ cyberark_session }}' +""" + +RETURN = r""" +changed: + description: Whether there was a change done. + type: bool + returned: always +cyberark_user: + description: Dictionary containing result properties. + returned: always + type: complex + contains: + result: + description: user properties when state is present + type: dict + returned: success +status_code: + description: Result HTTP Status code + returned: success + type: int + sample: 200 +""" + +import json + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils._text import to_text +from ansible.module_utils.six.moves import http_client as httplib +from ansible.module_utils.six.moves.urllib.error import HTTPError +from ansible.module_utils.urls import open_url +from ansible.module_utils.six.moves.urllib.parse import quote +import logging + + +def construct_url(api_base_url, end_point): + return "{baseurl}/{endpoint}".format(baseurl=api_base_url.rstrip("/"), endpoint=end_point.lstrip("/")) + + +def user_details(module): + + # Get username from module parameters, and api base url + # along with validate_certs from the cyberark_session established + username = module.params["username"] + cyberark_session = module.params["cyberark_session"] + api_base_url = cyberark_session["api_base_url"] + validate_certs = cyberark_session["validate_certs"] + + # Prepare result, end_point, and headers + result = {} + end_point = "/PasswordVault/WebServices/PIMServices.svc/Users/{pusername}".format(pusername=username) + url = construct_url(api_base_url, end_point) + + headers = { + "Content-Type": "application/json", + "Authorization": cyberark_session["token"], + "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" + } + + try: + + response = open_url( + url, + method="GET", + headers=headers, + validate_certs=validate_certs, + timeout=module.params['timeout'], + ) + result = {"result": json.loads(response.read())} + + return (False, result, response.getcode()) + + except (HTTPError, httplib.HTTPException) as http_exception: + + if http_exception.code == 404: + return (False, None, http_exception.code) + else: + module.fail_json( + msg=( + "Error while performing user_details." + "Please validate parameters provided." + "\n*** end_point=%s\n ==> %s" + % (url, to_text(http_exception)) + ), + headers=headers, + status_code=http_exception.code, + ) + + except Exception as unknown_exception: + + module.fail_json( + msg=( + "Unknown error while performing user_details." + "\n*** end_point=%s\n%s" + % (url, to_text(unknown_exception)) + ), + headers=headers, + status_code=-1, + ) + + +def user_add_or_update(module, HTTPMethod, existing_info): + + # Get username from module parameters, and api base url + # along with validate_certs from the cyberark_session established + username = module.params["username"] + cyberark_session = module.params["cyberark_session"] + api_base_url = cyberark_session["api_base_url"] + validate_certs = cyberark_session["validate_certs"] + + # Prepare result, paylod, and headers + result = {} + payload = {} + headers = { + "Content-Type": "application/json", + "Authorization": cyberark_session["token"], + "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" + } + + # end_point and payload sets different depending on POST/PUT + # for POST -- create -- payload contains username + # for PUT -- update -- username is part of the endpoint + if HTTPMethod == "POST": + end_point = "PasswordVault/api/Users" + payload["UserName"] = username + if ( + "initial_password" in list(module.params.keys()) + and module.params["initial_password"] is not None + ): + payload["InitialPassword"] = module.params["initial_password"] + + elif HTTPMethod == "PUT": + # With the put in this old format, we can not update the vaultAuthorization + end_point = "/PasswordVault/WebServices/PIMServices.svc/Users/{pusername}".format(pusername=username) + + # --- Optionally populate payload based on parameters passed --- + if "new_password" in module.params and module.params["new_password"] is not None: + payload["NewPassword"] = module.params["new_password"] + + if "email" in module.params and module.params["email"] is not None: + payload["Email"] = module.params["email"] + + if "first_name" in module.params and module.params["first_name"] is not None: + payload["FirstName"] = module.params["first_name"] + + if "last_name" in module.params and module.params["last_name"] is not None: + payload["LastName"] = module.params["last_name"] + + if ( + "change_password_on_the_next_logon" in module.params + and module.params["change_password_on_the_next_logon"] is not None + ): + payload["ChangePasswordOnTheNextLogon"] = module.params[ + "change_password_on_the_next_logon" + ] + + if "expiry_date" in module.params and module.params["expiry_date"] is not None: + payload["ExpiryDate"] = module.params["expiry_date"] + + if ( + "user_type_name" in module.params + and module.params["user_type_name"] is not None + ): + payload["UserTypeName"] = module.params["user_type_name"] + # In API V2 the parameter is called userType, V2 ignores the UserTypeName + payload["userType"] = module.params["user_type_name"] + + if "disabled" in module.params and module.params["disabled"] is not None: + payload["Disabled"] = module.params["disabled"] + + if "location" in module.params and module.params["location"] is not None: + payload["Location"] = module.params["location"] + + if module.params.get("authorization", None) is not None: + payload["vaultAuthorization"] = module.params["authorization"] + + # -------------------------------------------------------------- + logging.debug( + "HTTPMethod = " + HTTPMethod + " module.params = " + json.dumps(module.params) + ) + logging.debug("Existing Info: %s", json.dumps(existing_info)) + logging.debug("payload => %s", json.dumps(payload)) + + if HTTPMethod == "PUT" and ( + "new_password" not in module.params or module.params["new_password"] is None + ): + logging.info("Verifying if needs to be updated") + proceed = False + updateable_fields = [ + "Email", + "FirstName", + "LastName", + "ChangePasswordOnTheNextLogon", + "ExpiryDate", + "UserTypeName", + "Disabled", + "Location", + "UserTypeName", + "vaultAuthorization", + ] + for field_name in updateable_fields: + logging.debug("#### field_name : %s", field_name) + if ( + field_name in payload + and field_name in existing_info + and payload[field_name] != existing_info[field_name] + ): + logging.debug("Changing value for %s", field_name) + proceed = True + else: + proceed = True + + if proceed: + logging.info("Proceeding to either update or create") + url = construct_url(api_base_url, end_point) + try: + + # execute REST action + response = open_url( + url, + method=HTTPMethod, + headers=headers, + data=json.dumps(payload), + validate_certs=validate_certs, + timeout=module.params['timeout'], + ) + + result = {"result": json.loads(response.read())} + + return (True, result, response.getcode()) + + except (HTTPError, httplib.HTTPException) as http_exception: + + module.fail_json( + msg=( + "Error while performing user_add_or_update." + "Please validate parameters provided." + "\n*** end_point=%s\n ==> %s" + % (url, to_text(http_exception)) + ), + payload=payload, + headers=headers, + status_code=http_exception.code, + ) + except Exception as unknown_exception: + + module.fail_json( + msg=( + "Unknown error while performing user_add_or_update." + "\n*** end_point=%s\n%s" + % (url, to_text(unknown_exception)) + ), + payload=payload, + headers=headers, + status_code=-1, + ) + else: + return (False, existing_info, 200) + + +def resolve_username_to_id(module): + username = module.params["username"] + cyberark_session = module.params["cyberark_session"] + api_base_url = cyberark_session["api_base_url"] + validate_certs = cyberark_session["validate_certs"] + url = construct_url(api_base_url, "PasswordVault/api/Users?search={pusername}".format(pusername=username)) + headers = { + "Content-Type": "application/json", + "Authorization": cyberark_session["token"], + "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" + } + try: + response = open_url( + url, + method="GET", + headers=headers, + validate_certs=validate_certs, + timeout=module.params['timeout'], + ) + users = json.loads(response.read()) + # Return None if the user does not exist + user_id = None + # Say we have two users: 'someone' and 'someoneelse', a search on someone will return both + # So we will lopp over and see if the username returned matches the username we searched for + # If so, and we somehow found more than one raise an error + for user in users['Users']: + if user['username'] == username: + if user_id is None: + user_id = user['id'] + else: + module.fail_json(msg=("Found more than one user matching %s, this should be impossible" % (username))) + + # If we made it here we had 1 or 0 users, return them + logging.debug("Resolved username {%s} to ID {%s}", username, user_id) + return user_id + + except (HTTPError, httplib.HTTPException) as http_exception: + exception_text = to_text(http_exception) + module.fail_json(msg=( + "Error while performing user_search." + "Please validate parameters provided." + "\n*** end_point=%s\n ==> %s" + % (url, exception_text)), + headers=headers, + status_code=http_exception.code, + ) + except Exception as unknown_exception: + module.fail_json(msg=( + "Unknown error while performing user search." + "\n*** end_point=%s\n%s" + % (url, to_text(unknown_exception))), + headers=headers, + status_code=-1, + ) + + +def user_delete(module): + + # Get username from module parameters, and api base url + # along with validate_certs from the cyberark_session established + cyberark_session = module.params["cyberark_session"] + api_base_url = cyberark_session["api_base_url"] + validate_certs = cyberark_session["validate_certs"] + + # Prepare result, end_point, and headers + result = {} + vault_user_id = resolve_username_to_id(module) + # If the user was not found by username we can return unchanged + if vault_user_id is None: + return (False, result, None) + + end_point = ("PasswordVault/api/Users/{pvaultuserid}").format(pvaultuserid=vault_user_id) + + headers = { + "Content-Type": "application/json", + "Authorization": cyberark_session["token"], + "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" + } + url = construct_url(api_base_url, end_point) + + try: + + # execute REST action + response = open_url( + url, + method="DELETE", + headers=headers, + validate_certs=validate_certs, + timeout=module.params['timeout'], + ) + + result = {"result": {}} + + return (True, result, response.getcode()) + + except (HTTPError, httplib.HTTPException) as http_exception: + + exception_text = to_text(http_exception) + if http_exception.code == 404 and "ITATS003E" in exception_text: + # User does not exist + result = {"result": {}} + return (False, result, http_exception.code) + else: + module.fail_json( + msg=( + "Error while performing user_delete." + "Please validate parameters provided." + "\n*** end_point=%s\n ==> %s" + % (url, exception_text) + ), + headers=headers, + status_code=http_exception.code, + ) + + except Exception as unknown_exception: + + module.fail_json( + msg=( + "Unknown error while performing user_delete." + "\n*** end_point=%s\n%s" + % (url, to_text(unknown_exception)) + ), + headers=headers, + status_code=-1, + ) + + +def resolve_group_name_to_id(module): + group_name = module.params["group_name"] + cyberark_session = module.params["cyberark_session"] + api_base_url = cyberark_session["api_base_url"] + validate_certs = cyberark_session["validate_certs"] + headers = { + "Content-Type": "application/json", + "Authorization": cyberark_session["token"], + "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" + } + url = construct_url(api_base_url, "/PasswordVault/api/UserGroups?search={pgroupname}".format(pgroupname=quote(group_name))) + try: + response = open_url( + url, + method="GET", + headers=headers, + validate_certs=validate_certs, + timeout=module.params['timeout'], + ) + groups = json.loads(response.read()) + # Return None if the user does not exist + group_id = None + # Say we have two groups: 'groupone' and 'grouptwo', a search on group will return both + # So we will lopp over and see if the groupname returned matches the groupsname we searched for + # If so, and we somehow found more than one raise an error + for group in groups['value']: + if group['groupName'] == group_name: + if group_id is None: + group_id = group['id'] + else: + module.fail_json(msg=("Found more than one group matching %s. Use vault_id instead" % (group_name))) + # If we made it here we had 1 or 0 users, return them + logging.debug("Resolved group_name %s to ID %s", group_name, group_id) + return group_id + + except (HTTPError, httplib.HTTPException) as http_exception: + module.fail_json(msg=( + "Error while looking up group %s.\n*** end_point=%s\n ==> %s" + % (group_name, url, to_text(http_exception))), + payload={}, + headers=headers, + status_code=http_exception.code, + ) + except Exception as unknown_exception: + module.fail_json(msg=( + "Unknown error while looking up group %s.\n*** end_point=%s\n%s" + % (group_name, url, to_text(unknown_exception))), + payload={}, + headers=headers, + status_code=-1, + ) + + +def user_add_to_group(module): + + # Get username, and groupname from module parameters, and api base url + # along with validate_certs from the cyberark_session established + + # Not needed for new version + username = module.params["username"] + group_name = module.params["group_name"] + vault_id = module.params["vault_id"] + member_type = ( + "Vault" + if module.params["member_type"] is None + else module.params["member_type"] + ) + domain_name = module.params["domain_name"] if member_type == "domain" else None + + cyberark_session = module.params["cyberark_session"] + api_base_url = cyberark_session["api_base_url"] + validate_certs = cyberark_session["validate_certs"] + + # Prepare result, end_point, headers and payload + result = {} + headers = { + "Content-Type": "application/json", + "Authorization": cyberark_session["token"], + "User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)" + } + + # If we went "old school" and were provided a group_name instead of a vault_id we need to resolve it + if group_name and not vault_id: + # If we were given a group_name we need to lookup the vault_id + vault_id = resolve_group_name_to_id(module) + if vault_id is None: + module.fail_json(msg="Unable to find a user group named {pgroupname}, please create that before adding a user to it".format(pgroupname=group_name)) + + end_point = ("/PasswordVault/api/UserGroups/{pvaultid}/Members").format(pvaultid=vault_id) + + # For some reason the group add uses username instead of id + payload = {"memberId": username, "memberType": member_type} + if domain_name: + payload["domainName"] = domain_name + + url = construct_url(api_base_url, end_point) + try: + + # execute REST action + response = open_url( + url, + method="POST", + headers=headers, + data=json.dumps(payload), + validate_certs=validate_certs, + timeout=module.params['timeout'], + ) + + result = {"result": {}} + + return (True, result, response.getcode()) + + except (HTTPError, httplib.HTTPException) as http_exception: + + exception_text = to_text(http_exception) + exception_body = json.loads(http_exception.read().decode()) + if http_exception.code == 409 and ("ITATS262E" in exception_text or exception_body.get("ErrorCode", "") == "PASWS213E"): + # User is already member of Group + return (False, None, http_exception.code) + else: + module.fail_json( + msg=( + "Error while performing user_add_to_group." + "Please validate parameters provided." + "\n*** end_point=%s\n ==> %s" + % (url, exception_text) + ), + payload=payload, + headers=headers, + status_code=http_exception.code, + response=http_exception.read().decode(), + ) + + except Exception as unknown_exception: + + module.fail_json( + msg=( + "Unknown error while performing user_add_to_group." + "\n*** end_point=%s\n%s" + % (url, to_text(unknown_exception)) + ), + payload=payload, + headers=headers, + status_code=-1, + ) + + +def main(): + + module = AnsibleModule( + argument_spec=dict( + username=dict(type="str", required=True), + state=dict(type="str", default="present", choices=["absent", "present"]), + logging_level=dict( + type="str", default="NOTSET", choices=["NOTSET", "DEBUG", "INFO"] + ), + logging_file=dict(type="str", default="/tmp/ansible_cyberark.log"), + cyberark_session=dict(type="dict", required=True), + initial_password=dict(type="str", no_log=True), + new_password=dict(type="str", no_log=True), + email=dict(type="str"), + first_name=dict(type="str"), + last_name=dict(type="str"), + change_password_on_the_next_logon=dict(type="bool", default=False), + expiry_date=dict(type="str"), + user_type_name=dict(type="str"), + disabled=dict(type="bool", default=False), + location=dict(type="str"), + group_name=dict(type="str"), + vault_id=dict(type="int"), + member_type=dict(type="str"), + domain_name=dict(type="str"), + timeout=dict(type="float", default=10), + authorization=dict(type="list", elements="str", required=False, default=['AddSafes', 'AuditUsers']), + ) + ) + + if module.params["logging_level"] is not None: + logging.basicConfig( + filename=module.params["logging_file"], level=module.params["logging_level"] + ) + + logging.info("Starting Module") + + state = module.params["state"] + group_name = module.params["group_name"] + vault_id = module.params["vault_id"] + + if state == "present": + (changed, result, status_code) = user_details(module) + + if status_code == 200: + # User already exists + + (changed, result, status_code) = user_add_or_update( + module, "PUT", result["result"] + ) + + elif status_code == 404: + # User does not exist, proceed to create it + (changed, result, status_code) = user_add_or_update(module, "POST", None) + + # Add user to group if needed + if group_name is not None or vault_id is not None: + (group_change, no_result, no_status_code) = user_add_to_group(module) + changed = changed or group_change + + elif state == "absent": + (changed, result, status_code) = user_delete(module) + + module.exit_json(changed=changed, cyberark_user=result, status_code=status_code) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/cyberark/pas/roles/aimprovider/README.md b/ansible_collections/cyberark/pas/roles/aimprovider/README.md new file mode 100644 index 000000000..e39863539 --- /dev/null +++ b/ansible_collections/cyberark/pas/roles/aimprovider/README.md @@ -0,0 +1,91 @@ +cyberark.pas.aimprovider +==================== + +Role to install/uninstall CyberArk's AIM Credential Provider. + +Requirements +------------ + +- CyberArk Privileged Account Security Web Services SDK. +- `cyberark.pas` Collection from Ansible Galaxy or Automation Hub + +Role Variables +-------------- +``` +# CyberArk's Privileged Account Security Web Services SDK api base URL (example: https://components.cyberark.local) +rest_api_url: "" + +# Whether to validate certificates for REST api calls. If false, SSL certificates will not be validated. This should only be used on personally controlled sites using self-signed certificates. +validate_certs: true + +# Zip file with distribution of AIM Provider (example: /tmp/binaries/RHELinux x64-Rls-v9.8.zip); this file is located in the Ansible server, and it will be copied to the Ansible nodes. It should point to the current version of AIM distribution to be used when delivering to the nodes in a central folder within the Ansible server. +zip_file_name: "" + +# Folder name within the ZIP file that will be used. By default, it's taken from zip file name, for example: "RHELinux x64" +folder_name: '{{zip_file_name.split("/")[-1].split("-Rls")[0]}}' + +# CyberArk location for App Provider user to be created +app_provider_user_location: "\\Applications" + +# CyberArk Vault Address +vault_address: "" + +# Whether to use shared logon authentication. If true, it will use the "Shared Logon Authentication" as described in the CyberArk's document "Privileged Account Security Web Services SDK Implementation Guide" +use_shared_logon_authentication: false + +# State - can be "present"/"absent" for install/uninstall. +state: "present" +``` + + +Additionally: +- **app_provider_user_group**: The name of the group the Provider user will be added to. + +Dependencies +------------ + +None. + + +Example Playbook +---------------- + +1) Install CyberArk AIM Provider. + +``` +--- +- hosts: all + + roles: + + - role: cyberark.pas.aimprovider + api_base_url: "https://components.cyberark.local" + validate_certs: false + zip_file_name: "/tmp/binaries/RHELinux x64-Rls-v9.8.zip" + vault_address: "10.0.1.10" + use_shared_logon_authentication: true +``` + +2) Uninstall CyberArk AIM Provider. +``` +--- +- hosts: all + + roles: + + - role: cyberark.pas.aimprovider + api_base_url: "https://components.cyberark.local" + use_shared_logon_authentication: true + state: "absent" + validate_certs: false +``` + +License +------- + +MIT + +Author Information +------------------ + +- Edward Nunez (edward.nunez@cyberark.com) diff --git a/ansible_collections/cyberark/pas/roles/aimprovider/defaults/main.yml b/ansible_collections/cyberark/pas/roles/aimprovider/defaults/main.yml new file mode 100644 index 000000000..483c6afb3 --- /dev/null +++ b/ansible_collections/cyberark/pas/roles/aimprovider/defaults/main.yml @@ -0,0 +1,27 @@ +--- + +# Default values for possible input parameters + +# CyberArk's Privileged Account Security Web Services SDK api base URL +rest_api_url: "" + +# Whether to validate certificates for REST api calls +validate_certs: true + +# Zip file with distribution of AIM Provider +zip_file_name: "" + +# Folder name within the ZIP file that will be used by default is taken from zip file name. +folder_name: '{{zip_file_name.split("/")[-1].split("-Rls")[0]}}' + +# CyberArk location for App Provider user to be created +app_provider_user_location: "\\Applications" + +# CyberArk Vault Address +vault_address: "" + +# Whether to use shared logon authentication +use_shared_logon_authentication: false + +# State - the state of the provider: present mean installing the provide and Absent means uninstalling +state: "present" diff --git a/ansible_collections/cyberark/pas/roles/aimprovider/tasks/installAIMProvider.yml b/ansible_collections/cyberark/pas/roles/aimprovider/tasks/installAIMProvider.yml new file mode 100644 index 000000000..ea2217202 --- /dev/null +++ b/ansible_collections/cyberark/pas/roles/aimprovider/tasks/installAIMProvider.yml @@ -0,0 +1,152 @@ +--- +- name: Validating Role Parameters + assert: + that: + - zip_file_name != '' + - vault_address != '' + - api_base_url != '' + - folder_name != '' + - app_provider_user_location != '' + - state == "present" + - (not use_shared_logon_authentication|default(false) and password_object is defined) or + (use_shared_logon_authentication|default(false) and password_object is not defined) + + +- debug: msg="Installation params => zip_file_name = {{zip_file_name}} folder_name={{folder_name}}" + +- block: + + - name: Copy provider zip to target and unzip + unarchive: src="{{zip_file_name}}" dest=/tmp + + - name: Rename aimparms and copy to var/tmp + command: cp "/tmp/{{folder_name}}/aimparms.sample" /var/tmp/aimparms + + - name: Replace parameters" in /var/tmp/aimparms + replace: + dest: /var/tmp/aimparms + regexp: "{{item.regexp}}" + replace: "{{item.replace}}" + with_items: + - {regexp: '^AcceptCyberArkEULA=.*$', replace: 'AcceptCyberArkEULA=Yes'} + - {regexp: '^LicensedProducts=.*$', replace: 'LicensedProducts=AIM'} + - {regexp: '^.?CreateVaultEnvironment=.*$', replace: 'CreateVaultEnvironment=no'} + - {regexp: '^VaultFilePath=.*$', replace: 'VaultFilePath=/tmp/{{folder_name}}/Vault.ini'} + + + - name: Change Vault.ini to the right address + replace: + dest: "/tmp/{{folder_name}}/Vault.ini" + regexp: '^ADDRESS=.*$' + replace: 'ADDRESS={{vault_address}}' + + - name: Change permission on createcredfile + file: + path: "/tmp/{{folder_name}}/CreateCredFile" + mode: 0755 + + - find: + paths: "/tmp/{{folder_name}}" + patterns: "CARKaim-*.rpm" + register: aimrpm + + # debug: msg="{{aimrpm.files[0].path}}" + + - name: Install Provider + package: name='{{aimrpm.files[0].path}}' state=present + + - name: Verify status of service after installing Provider + command: service aimprv status + register: command_result + ignore_errors: true + args: + warn: false + + # debug: msg="status of service RC={{command_result.rc}}" + + - name: Logon to CyberArk Vault using PAS Web Services SDK - use_shared_logon_authentication + cyberark.pas.cyberark_authentication: + api_base_url: "{{ api_base_url }}" + use_shared_logon_authentication: true + validate_certs: "{{ validate_certs }}" + changed_when: false + when: (command_result.rc != 0 and use_shared_logon_authentication|default(false)) + + - name: Logon to CyberArk Vault using PAS Web Services SDK - Not use_shared_logon_authentication + cyberark.pas.cyberark_authentication: + api_base_url: "{{ api_base_url }}" + username: "{{ password_object.password }}" + password: "{{ password_object.passprops.username }}" + validate_certs: "{{ validate_certs }}" + changed_when: false + when: (command_result.rc != 0 and not use_shared_logon_authentication|default(false)) + + # name: Debug message + # debug: + # msg: "{{ cyberark_session.token }}" + # when: (command_result.rc != 0) + + - name: Create provider user + cyberark.pas.cyberark_user: + username: "Prov_{{ ansible_hostname }}" + initial_password: "Cyberark1" + user_type_name: "AppProvider" + location: "{{ app_provider_user_location }}" + group_name: "{{ app_provider_user_group }}" + change_password_on_the_next_logon: false + state: present + cyberark_session: "{{ cyberark_session }}" + register: cyberarkaction + when: (command_result.rc != 0) + + # debug: + # msg: "USERCREATED => {{cyberarkaction}}" + # when: (command_result.rc != 0 and cyberarkaction.status_code == 201) + + - name: Reset provider user credential + cyberark.pas.cyberark_user: + username: "Prov_{{ ansible_hostname }}" + new_password: "Cyberark1" + disabled: false + state: present + cyberark_session: "{{ cyberark_session }}" + register: cyberarkaction + when: (command_result.rc != 0 and cyberarkaction.status_code == 200) + + - name: Logoff from CyberArk Vault + cyberark.pas.cyberark_authentication: + state: absent + cyberark_session: "{{ cyberark_session }}" + changed_when: false + when: (command_result.rc != 0) + + - name: Create Provider Initial Cred File + command: /opt/CARKaim/bin/createcredfile /etc/opt/CARKaim/vault/appprovideruser.cred Password -Username Prov_{{ ansible_hostname }} -Password Cyberark1 + when: (command_result.rc != 0) + + - name: Set vault.ini Into Place + command: cp "/tmp/{{folder_name}}/Vault.ini" /etc/opt/CARKaim/vault/vault.ini + + - name: Start Provider Service + service: name=aimprv state=started + when: (command_result.rc != 0) + + - name: Remove /tmp/{{folder_name}} + file: + path: '/tmp/{{folder_name}}' + state: absent + + - name: Remove /var/tmp/aimparms + file: + path: '/var/tmp/aimparms' + state: absent + + rescue: + + - name: Remove /tmp/{{folder_name}} + file: + path: '/tmp/{{folder_name}}' + state: absent + + - fail: + msg: "AIM Credential Provider Installation failed!" diff --git a/ansible_collections/cyberark/pas/roles/aimprovider/tasks/main.yml b/ansible_collections/cyberark/pas/roles/aimprovider/tasks/main.yml new file mode 100644 index 000000000..9e65e1c61 --- /dev/null +++ b/ansible_collections/cyberark/pas/roles/aimprovider/tasks/main.yml @@ -0,0 +1,24 @@ +--- +- name: Validating Role Parameters + assert: + that: + - api_base_url != '' + - state in ["present", "absent"] + - (not use_shared_logon_authentication|default(false) and password_object is defined) or + (use_shared_logon_authentication|default(false) and password_object is not defined) + +- name: Verify status of aimprv service initially + command: service aimprv status + register: service_already_running + ignore_errors: true + changed_when: false + args: + warn: false + +- debug: msg="status of service RC={{service_already_running.rc}}" + +- import_tasks: installAIMProvider.yml + when: (state == "present" and service_already_running.rc != 0) + +- import_tasks: uninstallAIMProvider.yml + when: (state == "absent" and service_already_running.rc == 0)
\ No newline at end of file diff --git a/ansible_collections/cyberark/pas/roles/aimprovider/tasks/uninstallAIMProvider.yml b/ansible_collections/cyberark/pas/roles/aimprovider/tasks/uninstallAIMProvider.yml new file mode 100644 index 000000000..75c15f7e2 --- /dev/null +++ b/ansible_collections/cyberark/pas/roles/aimprovider/tasks/uninstallAIMProvider.yml @@ -0,0 +1,61 @@ +--- +- block: + + - name: Uninstall Provider + package: name='CARKaim' state=absent + + - name: Logon to CyberArk Vault using PAS Web Services SDK - use_shared_logon_authentication + cyberark.pas.cyberark_authentication: + api_base_url: "{{ api_base_url }}" + use_shared_logon_authentication: true + validate_certs: "{{ validate_certs }}" + when: (use_shared_logon_authentication) + + - name: Logon to CyberArk Vault using PAS Web Services SDK - Not use_shared_logon_authentication + cyberark.pas.cyberark_authentication: + api_base_url: "{{ api_base_url }}" + username: "{{ password_object.password }}" + password: "{{ password_object.passprops.username }}" + validate_certs: "{{ validate_certs }}" + changed_when: false + when: (not use_shared_logon_authentication) + + # name: Debug message + # debug: + # msg: "{{ cyberark_session }}" + # when: (cyberark_session.token is defined) + + - name: Remove Provider User + cyberark.pas.cyberark_user: + username: "Prov_{{ansible_hostname}}" + state: absent + cyberark_session: "{{ cyberark_session }}" + register: cyberarkaction + ignore_errors: true + when: (cyberark_session.token is defined) + + # debug: + # msg: "USERDETAILS => {{cyberarkaction}}" + # when: (cyberarkaction.status_code == 200) + + - name: Logoff from CyberArk Vault + cyberark.pas.cyberark_authentication: + state: absent + cyberark_session: "{{ cyberark_session }}" + changed_when: false + when: (cyberark_session.token is defined) + + - name: Remove /etc/opt/CARKaim + file: + path: '/etc/opt/CARKaim' + state: absent + + - name: Remove /var/opt/CARKaim + file: + path: '/var/opt/CARKaim' + state: absent + + rescue: + + - fail: + msg: "AIM Credential Provider Uninstall failed!"
\ No newline at end of file diff --git a/ansible_collections/cyberark/pas/rulebooks/cyberark_test_rule.yml b/ansible_collections/cyberark/pas/rulebooks/cyberark_test_rule.yml new file mode 100644 index 000000000..021ac4b65 --- /dev/null +++ b/ansible_collections/cyberark/pas/rulebooks/cyberark_test_rule.yml @@ -0,0 +1,25 @@ +- name: Demo rules with CyberArk syslog as source + hosts: localhost + sources: + - cyberark.pas.syslog: + host: 0.0.0.0 + port: 1514 + rules: + - name: Check For User Suspension Event, Then Disable The User and Notify + condition: event.cyberark.syslog.audit_record.Severity == "Error" and event.cyberark.syslog.audit_record.MessageID == "5" + action: + run_playbook: + name: disable_user.yml + extra_vars: + username: "{{ event.cyberark.syslog.audit_record.Issuer }}" + - name: Check For PTA irregular IP OR irregular Hours Access and Notify + condition: event.cyberark.DeviceEventClassID == "25" or event.cyberark.DeviceEventClassID == "23" + action: + run_playbook: + name: pta_disable_notify.yml + extra_vars: + username: "{{ event.cyberark.suser }}" + #username: "{{ event.cyberark.suser | ansible.builtin.regex_search('^[a-zA-Z0-9_]+') }}" + eventname: "{{ event.cyberark.DeviceName }}" + eventurl: "{{ event.cyberark.PTALink }}" + station: "{{ event.cyberark.shost }}" diff --git a/ansible_collections/cyberark/pas/rulebooks/disable_pas_user_kafka.yml b/ansible_collections/cyberark/pas/rulebooks/disable_pas_user_kafka.yml new file mode 100644 index 000000000..b25590d9b --- /dev/null +++ b/ansible_collections/cyberark/pas/rulebooks/disable_pas_user_kafka.yml @@ -0,0 +1,15 @@ +- name: Demo rules with kafka as source + hosts: localhost + sources: + - cyberark.eda.kafka: + topic: ansible + host: localhost + port: 9092 + rules: + - name: Check For User Suspension Event, Then Disable The User and Notify + condition: event.body.syslog.audit_record.Severity == "Error" and event.body.syslog.audit_record.MessageID == "5" + action: + run_playbook: + name: ../../../cyberark/pas/tests/disable_user.yml + extra_vars: + username: "{{ event.body.syslog.audit_record.Issuer }}" diff --git a/ansible_collections/cyberark/pas/rulebooks/disable_pas_user_webhook.yml b/ansible_collections/cyberark/pas/rulebooks/disable_pas_user_webhook.yml new file mode 100644 index 000000000..a39e1bc7c --- /dev/null +++ b/ansible_collections/cyberark/pas/rulebooks/disable_pas_user_webhook.yml @@ -0,0 +1,16 @@ +--- +- name: Demo rules with webhook as source + hosts: all + sources: + - cyberark.eda.webhook: + host: 0.0.0.0 + port: 5000 + + rules: + - name: Listen For User Suspension Event Webhook, Then Disble The User + condition: event.payload.syslog.audit_record.Severity == "Error" and event.payload.syslog.audit_record.MessageID == "5" + action: + run_playbook: + name: ../../../cyberark/pas/tests/disable_user.yml + extra_vars: + username: "{{ event.payload.syslog.audit_record.Issuer }}" diff --git a/ansible_collections/cyberark/pas/rulebooks/disable_user.yml b/ansible_collections/cyberark/pas/rulebooks/disable_user.yml new file mode 100644 index 000000000..9b86ca8bb --- /dev/null +++ b/ansible_collections/cyberark/pas/rulebooks/disable_user.yml @@ -0,0 +1,42 @@ +--- +- hosts: all + connection: local + + collections: + - cyberark.pas + + tasks: + + - name: Logon to CyberArk Vault using PAS Web Services SDK + cyberark_authentication: + api_base_url: "https://BASE_URL" + validate_certs: false + username: "USERNAME" + password: "PASSWORD" + + - name: Disabling a CyberArk User + cyberark_user: + username: "{{ username }}" #this is password from the running yml when condition is met + disabled: true + cyberark_session: "{{ cyberark_session }}" + register: cyberarkaction + + - name: Debug message + debug: + var: cyberarkaction + + - name: Logoff from CyberArk Vault + cyberark_authentication: + state: absent + cyberark_session: "{{ cyberark_session }}" + + - name: Sending an e-mail using Gmail SMTP servers + community.general.mail: + host: SMTPSERVER + port: PORT + username: username@mail.com + password: password + to: First Last <first.last@mail.com> + subject: Ansible-Rulebook Report + body: Ansible Rulebook disabled Cyberark user '{{ username }}' due to too many login attempts. + delegate_to: localhost diff --git a/ansible_collections/cyberark/pas/rulebooks/inventory.yml b/ansible_collections/cyberark/pas/rulebooks/inventory.yml new file mode 100644 index 000000000..bbf327095 --- /dev/null +++ b/ansible_collections/cyberark/pas/rulebooks/inventory.yml @@ -0,0 +1,5 @@ +--- +ungrouped: + hosts: + localhost: + ansible_host: 127.0.0.1 diff --git a/ansible_collections/cyberark/pas/rulebooks/pta_disable_notify.yml b/ansible_collections/cyberark/pas/rulebooks/pta_disable_notify.yml new file mode 100644 index 000000000..5f216a43b --- /dev/null +++ b/ansible_collections/cyberark/pas/rulebooks/pta_disable_notify.yml @@ -0,0 +1,42 @@ +--- +- hosts: all + connection: local + + collections: + - cyberark.pas + + tasks: + + - name: Logon to CyberArk Vault using PAS Web Services SDK + cyberark_authentication: + api_base_url: "https://BASE_URL" + validate_certs: false + username: "USERNAME" + password: "PASSWORD" + + - name: Disabling a CyberArk User + cyberark_user: + username: "{{ username | regex_search('.+?(?=\\()') }}" #more optimal handle user case like suser=testuserFirst.testuserLast@acme.com(Vault user) match up to ( char + disabled: true + cyberark_session: "{{ cyberark_session }}" + register: cyberarkaction + + - name: Debug message + debug: + var: cyberarkaction + + - name: Logoff from CyberArk Vault + cyberark_authentication: + state: absent + cyberark_session: "{{ cyberark_session }}" + + - name: Sending an e-mail using Gmail SMTP servers + community.general.mail: + host: SMTPSERVER + port: PORT + username: username@mail.com + password: password + to: First Last <first.last@mail.com> + subject: Ansible-Rulebook Report + body: Ansible Rulebook notify of PTA Event {{ username }} - {{ eventname }} - from host {{ station }} - For more info please visit - {{ eventurl }} - user disabled! + delegate_to: localhost diff --git a/ansible_collections/cyberark/pas/rulebooks/pta_notify.yml b/ansible_collections/cyberark/pas/rulebooks/pta_notify.yml new file mode 100644 index 000000000..94ed251d2 --- /dev/null +++ b/ansible_collections/cyberark/pas/rulebooks/pta_notify.yml @@ -0,0 +1,19 @@ +--- +- hosts: all + connection: local + + collections: + - cyberark.pas + + tasks: + + - name: Sending an e-mail using Gmail SMTP servers + community.general.mail: + host: SMTPSERVER + port: PORT + username: username@mail.com + password: password + to: First Last <first.last@mail.com> + subject: Ansible-Rulebook Report + body: Ansible Rulebook notify of PTA Event '{{ username | ansible.builtin.regex_search('^[a-zA-Z0-9_]+') }}' '{{ eventname }}' from host '{{ station }}' For more info please visit - '{{ eventurl }}' + delegate_to: localhost diff --git a/ansible_collections/cyberark/pas/tests/change_test.yml b/ansible_collections/cyberark/pas/tests/change_test.yml new file mode 100644 index 000000000..8fc9e4abf --- /dev/null +++ b/ansible_collections/cyberark/pas/tests/change_test.yml @@ -0,0 +1,37 @@ +--- +- hosts: localhost + + collections: + - cyberark.pas + + tasks: + + - name: Logon to CyberArk Vault using PAS Web Services SDK + cyberark_authentication: + api_base_url: "http://components.cyberark.local" + validate_certs: false + username: "bizdev" + password: "Cyberark1" + + - name: Rotate credential via reconcile and providing the password to be changed to + cyberark_account: + identified_by: "address,username" + safe: "Test" + address: "prod.cyberark.local" + username: "admin" + platform_id: WinDomain + platform_account_properties: + ReconcileAccount: "Operating System-WinServerLocal-cyberark.local-administrator-x" + LogonDomain: "PROD" + secret_management: +# new_secret: "Ama123ah12@#!Xaamdjbdkl@#112" +# management_action: "reconcile" + automatic_management_enabled: true + state: present + cyberark_session: "{{ cyberark_session }}" + register: reconcileaccount + + - name: Logoff from CyberArk Vault + cyberark_authentication: + state: absent + cyberark_session: "{{ cyberark_session }}"
\ No newline at end of file diff --git a/ansible_collections/cyberark/pas/tests/changepolicy.yml b/ansible_collections/cyberark/pas/tests/changepolicy.yml new file mode 100644 index 000000000..7ec9045f4 --- /dev/null +++ b/ansible_collections/cyberark/pas/tests/changepolicy.yml @@ -0,0 +1,41 @@ +--- +- hosts: localhost + + collections: + - cyberark.pas + + tasks: + + - name: Logon to CyberArk Vault using PAS Web Services SDK + cyberark_authentication: + api_base_url: "http://components.cyberark.local" + validate_certs: false + username: "bizdev" + password: "Cyberark1" + + + - name: Debug message + debug: + var: cyberark_session + + - name: Account + cyberark_account: + identified_by: "address,username" + safe: "Test" + address: "cyberark.local" + username: "cyberark-administrator" + platform_id: WinDomain-Level2 + cyberark_session: "{{ cyberark_session }}" + register: cyberarkaction + + - name: Debug message + debug: + var: cyberarkaction + + - name: Logoff from CyberArk Vault + cyberark_authentication: + state: absent + cyberark_session: "{{ cyberark_session }}" + + - name: Debug message + debug: var=cyberark_session diff --git a/ansible_collections/cyberark/pas/tests/deprovision_account.yml b/ansible_collections/cyberark/pas/tests/deprovision_account.yml new file mode 100644 index 000000000..6e5bb50d3 --- /dev/null +++ b/ansible_collections/cyberark/pas/tests/deprovision_account.yml @@ -0,0 +1,42 @@ +--- +- hosts: localhost + + collections: + - cyberark.pas + + tasks: + + - name: Logon to CyberArk Vault using PAS Web Services SDK + cyberark_authentication: + api_base_url: "http://components.cyberark.local" + validate_certs: false + username: "bizdev" + password: "Cyberark1" + + + - name: Debug message + debug: + var: cyberark_session + + - name: Account + cyberark_account: + logging_level: DEBUG + identified_by: "address,username" + safe: "Test" + address: "cyberark.local" + username: "cyberark-administrator" + state: absent + cyberark_session: "{{ cyberark_session }}" + register: cyberarkaction + + - name: Debug message + debug: + var: cyberarkaction + + - name: Logoff from CyberArk Vault + cyberark_authentication: + state: absent + cyberark_session: "{{ cyberark_session }}" + + - name: Debug message + debug: var=cyberark_session diff --git a/ansible_collections/cyberark/pas/tests/deprovision_user.yml b/ansible_collections/cyberark/pas/tests/deprovision_user.yml new file mode 100644 index 000000000..76e76cba4 --- /dev/null +++ b/ansible_collections/cyberark/pas/tests/deprovision_user.yml @@ -0,0 +1,39 @@ +--- +- hosts: localhost + + collections: + - cyberark.pas + + tasks: + + - name: Logon to CyberArk Vault using PAS Web Services SDK + cyberark_authentication: + api_base_url: "http://components.cyberark.local" + validate_certs: false + username: "bizdev" + password: "Cyberark1" + + + - name: Debug message + debug: + var: cyberark_session + + - name: Removing a CyberArk User + cyberark_user: + username: "ansibleuser" + state: absent + cyberark_session: "{{ cyberark_session }}" + register: cyberarkaction + + - name: Debug message + debug: + var: cyberarkaction + + + - name: Logoff from CyberArk Vault + cyberark_authentication: + state: absent + cyberark_session: "{{ cyberark_session }}" + + - name: Debug message + debug: var=cyberark_session diff --git a/ansible_collections/cyberark/pas/tests/disable_user.yml b/ansible_collections/cyberark/pas/tests/disable_user.yml new file mode 100644 index 000000000..cad8d3018 --- /dev/null +++ b/ansible_collections/cyberark/pas/tests/disable_user.yml @@ -0,0 +1,31 @@ +--- +- hosts: localhost + + collections: + - cyberark.pas + + tasks: + + - name: Logon to CyberArk Vault using PAS Web Services SDK + cyberark_authentication: + api_base_url: "http://components.cyberark.local" + validate_certs: false + username: "bizdev" + password: "Cyberark1" + + - name: Disabling a CyberArk User + cyberark_user: + username: "ansibleuser" + disabled: true + cyberark_session: "{{ cyberark_session }}" + register: cyberarkaction + + - name: Debug message + debug: + var: cyberarkaction + + + - name: Logoff from CyberArk Vault + cyberark_authentication: + state: absent + cyberark_session: "{{ cyberark_session }}" diff --git a/ansible_collections/cyberark/pas/tests/enable_user.yml b/ansible_collections/cyberark/pas/tests/enable_user.yml new file mode 100644 index 000000000..5b6c5a273 --- /dev/null +++ b/ansible_collections/cyberark/pas/tests/enable_user.yml @@ -0,0 +1,32 @@ +--- +- hosts: localhost + + collections: + - cyberark.pas + + tasks: + + - name: Logon to CyberArk Vault using PAS Web Services SDK + cyberark_authentication: + api_base_url: "http://components.cyberark.local" + validate_certs: false + username: "bizdev" + password: "Cyberark1" + + - name: Enabling a CyberArk User and forcing a password change at next logon + cyberark_user: + username: "ansibleuser" + disabled: false + state: present + change_password_on_the_next_logon: true + cyberark_session: "{{ cyberark_session }}" + register: cyberarkaction + + - name: Debug message + debug: + var: cyberarkaction + + - name: Logoff from CyberArk Vault + cyberark_authentication: + state: absent + cyberark_session: "{{ cyberark_session }}" diff --git a/ansible_collections/cyberark/pas/tests/provision_account.yml b/ansible_collections/cyberark/pas/tests/provision_account.yml new file mode 100644 index 000000000..753ae228b --- /dev/null +++ b/ansible_collections/cyberark/pas/tests/provision_account.yml @@ -0,0 +1,41 @@ +--- +- hosts: localhost + + collections: + - cyberark.pas + + tasks: + + - name: Logon to CyberArk Vault using PAS Web Services SDK + cyberark_authentication: + api_base_url: "http://components.cyberark.local" + validate_certs: false + username: "bizdev" + password: "Cyberark1" + + - name: Account + cyberark_account: + identified_by: "address,username" + safe: "Test" + address: "cyberark.local" + username: "cyberark-administrator" + platform_id: WinDomain-Level2 + secret: "CyberarkFirst" + platform_account_properties: + LogonDomain: "RedHatAnsible" + OwnerName: "James Stutes" + Port: 8080 + secret_management: + automatic_management_enabled: true + state: present + cyberark_session: "{{ cyberark_session }}" + register: cyberarkaction + + - name: Debug message + debug: + var: cyberarkaction + + - name: Logoff from CyberArk Vault + cyberark_authentication: + state: absent + cyberark_session: "{{ cyberark_session }}" diff --git a/ansible_collections/cyberark/pas/tests/provision_user.yml b/ansible_collections/cyberark/pas/tests/provision_user.yml new file mode 100644 index 000000000..e7bbbb790 --- /dev/null +++ b/ansible_collections/cyberark/pas/tests/provision_user.yml @@ -0,0 +1,37 @@ +--- +- hosts: localhost + + collections: + - cyberark.pas + + tasks: + + - name: Logon to CyberArk Vault using PAS Web Services SDK + cyberark_authentication: + api_base_url: "http://components.cyberark.local" + validate_certs: false + username: "bizdev" + password: "Cyberark1" + + - name: Creating a CyberArk User, setting a simple password but forcing a password change at next logon + cyberark_user: + username: "ansibleuser" + first_name: "Ansible" + last_name: "User" + email: "ansibleuser@demo.com" + initial_password: "Cyberark1" + user_type_name: "EPVUser" + group_name: "AnsibleAdmins" + disabled: false + state: present + cyberark_session: "{{ cyberark_session }}" + register: cyberarkaction + + - name: Debug message + debug: + var: cyberarkaction + + - name: Logoff from CyberArk Vault + cyberark_authentication: + state: absent + cyberark_session: "{{ cyberark_session }}" diff --git a/ansible_collections/cyberark/pas/tests/reset_user_password.yml b/ansible_collections/cyberark/pas/tests/reset_user_password.yml new file mode 100644 index 000000000..d25a04cdc --- /dev/null +++ b/ansible_collections/cyberark/pas/tests/reset_user_password.yml @@ -0,0 +1,33 @@ +--- +- hosts: localhost + + collections: + - cyberark.pas + + tasks: + + - name: Logon to CyberArk Vault using PAS Web Services SDK + cyberark_authentication: + api_base_url: "http://components.cyberark.local" + validate_certs: false + username: "bizdev" + password: "Cyberark1" + + - name: Enabling a CyberArk User and forcing a password change at next logon + cyberark_user: + username: "ansibleuser" + disabled: false + new_password: Cyberark1 + state: present + change_password_on_the_next_logon: true + cyberark_session: "{{ cyberark_session }}" + register: cyberarkaction + + - name: Debug message + debug: + var: cyberarkaction + + - name: Logoff from CyberArk Vault + cyberark_authentication: + state: absent + cyberark_session: "{{ cyberark_session }}" diff --git a/ansible_collections/cyberark/pas/tests/test.yml b/ansible_collections/cyberark/pas/tests/test.yml new file mode 100644 index 000000000..7c0834434 --- /dev/null +++ b/ansible_collections/cyberark/pas/tests/test.yml @@ -0,0 +1,65 @@ +--- +- hosts: localhost + + collections: + - cyberark.pas + + tasks: + + - name: Logon to CyberArk Vault using PAS Web Services SDK + cyberark_authentication: + api_base_url: "http://components.cyberark.local" + validate_certs: false + username: "bizdev" + password: "Cyberark1" + + + - name: Debug message + debug: + var: cyberark_session + + - name: User + cyberark_user: + username: "testuser" + initial_password: "Cyberark1" + user_type_name: "EPVUser" + change_password_on_the_next_logon: false + group_name: "Auditors" + disabled: false + state: present + cyberark_session: "{{ cyberark_session }}" + register: cyberarkaction + + - name: Debug message + debug: + var: cyberarkaction + + - name: Account + cyberark_account: +# logging_level: DEBUG + identified_by: "address,username" + name: "EDWARD_ACCOUNT" + safe: "Test" + address: "10.0.1.20" + username: "james_test" + platform_id: WinServerLocal + platform_account_properties: + LogonDomain: "10.0.1.20" + secret_management: + automatic_management_enabled: false + manual_management_reason: "No Reason" + state: present + cyberark_session: "{{ cyberark_session }}" + register: cyberarkaction + + - name: Debug message + debug: + var: cyberarkaction + + - name: Logoff from CyberArk Vault + cyberark_authentication: + state: absent + cyberark_session: "{{ cyberark_session }}" + + - name: Debug message + debug: var=cyberark_session |