Init: mediaserver

This commit is contained in:
2023-02-08 12:13:28 +01:00
parent 848bc9739c
commit f7c23d4ba9
31914 changed files with 6175775 additions and 0 deletions

View File

@@ -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

View File

@@ -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') }}"
```

File diff suppressed because it is too large Load Diff

View File

@@ -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)
}
}
}

View File

@@ -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.

View File

@@ -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
}

View File

@@ -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).

View File

@@ -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.

View File

@@ -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

View File

@@ -0,0 +1,6 @@
#!/bin/bash -ex
docker run \
--rm \
--volume "${PWD}/CHANGELOG.md":/CHANGELOG.md \
cyberark/parse-a-changelog

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 ]

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,4 @@
FROM centos:7
# Install Python so Ansible can run against node
RUN yum update -y && yum install -y python3

View File

@@ -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

View File

@@ -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"

View File

@@ -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) }}"

View File

@@ -0,0 +1,2 @@
---
requires_ansible: '>=2.9'

View File

@@ -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

View File

@@ -0,0 +1 @@
ansible>=2.9

View File

@@ -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).

View File

@@ -0,0 +1,6 @@
summon:
version: 0.8.2
os: linux-amd64
summon_conjur:
version: 0.5.3
os: linux-amd64

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,6 @@
---
- import_tasks: install.yml
when: state|default('present') == "present"
- import_tasks: uninstall.yml
when: state|default('present') == "absent"

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,5 @@
account: {{conjur_account}}
appliance_url: {{conjur_appliance_url}}
cert_file: {{conjur_ssl_certificate_path}}
netrc_path: /etc/conjur.identity
plugins: []

View File

@@ -0,0 +1,3 @@
machine {{conjur_appliance_url}}/authn
login host/{{conjur_host_name}}
password {{host_factory_response.json.api_key}}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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 ]

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,4 @@
FROM centos:7
# Install Python so Ansible can run against node
RUN yum update -y && yum install -y python3

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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"

View File

@@ -0,0 +1,2 @@
---
GALAXY_API_KEY: !var ecosystems/ansible/galaxy/api-key