Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/add cyberark provider #127

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ This app supports the following popular secrets backends:
| [AWS Systems Manager Parameter Store](https://aws.amazon.com/secrets-manager/) | [Other: Key/value pairs](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html) | [AWS credentials](https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html) (see Usage section below) |
| [HashiCorp Vault](https://www.vaultproject.io) | [K/V Version 2](https://www.vaultproject.io/docs/secrets/kv/kv-v2)<br/>[K/V Version 1](https://developer.hashicorp.com/vault/docs/secrets/kv/kv-v1) | [Token](https://www.vaultproject.io/docs/auth/token)<br/>[AppRole](https://www.vaultproject.io/docs/auth/approle)<br/>[AWS](https://www.vaultproject.io/docs/auth/aws)<br/>[Kubernetes](https://www.vaultproject.io/docs/auth/kubernetes) |
| [Delinea/Thycotic Secret Server](https://delinea.com/products/secret-server) | [Secret Server Cloud](https://github.com/DelineaXPM/python-tss-sdk#secret-server-cloud)<br/>[Secret Server (on-prem)](https://github.com/DelineaXPM/python-tss-sdk#initializing-secretserver)| [Access Token Authorization](https://github.com/DelineaXPM/python-tss-sdk#access-token-authorization)<br/>[Domain Authorization](https://github.com/DelineaXPM/python-tss-sdk#domain-authorization)<br/>[Password Authorization](https://github.com/DelineaXPM/python-tss-sdk#password-authorization)<br/> |
| [CyberARK AIM](https://www.cyberark.com/) | [Password](https://www.cyberark.com/what-is/secrets-management/) | Token<br/>Username/Password |

## Screenshots

Expand Down Expand Up @@ -85,6 +86,12 @@ The Delinea/Thycotic Secret Server provider requires the `python-tss-sdk` librar
pip install nautobot-secrets-providers[thycotic]
```

### CyberARK AIM

```no-highlight
pip install nautobot-secrets-providers[cyberark]
```

### Enabling Secrets Providers

To ensure Nautobot Secrets Providers is automatically re-installed during future upgrades, create a file named `local_requirements.txt` (if not already existing) in the Nautobot root directory (alongside `requirements.txt`) and list the `secrets` package:
Expand Down Expand Up @@ -228,6 +235,34 @@ PLUGINS_CONFIG = {
- `token` - (optional) Required for 'Access Token Authorization'.
- `username` - (optional) Required for 'Secret Server Cloud', 'Password Authorization', 'Domain Authorization'.

### CyberARK AIM

The CyberARK AIM Secret Provider includes only one provider:

- **`CyberARK AIM`**

This provider allows to get a password from CyberARK Password Vault using API.
The user configured to access to CyberARK needs to be able to see the Vault where the account is stored to see it, if not it will return an error.

#### Configuration

```python
PLUGINS_CONFIG = {
"nautobot_secrets_providers": {
"cyberark": {
"url": os.getenv("NAUTOBOT_CYBERARK_URL", None),
"token": os.getenv("NAUTOBOT_CYBERARK_TOKEN", None),
"username": os.getenv("NAUTOBOT_CYBERARK_USERNAME", None),
"password": os.getenv("NAUTOBOT_CYBERARK_PASSWORD", None),
}
}
}
```
- `url` - (required) The URL to access to the CyberARK instance. _e.g. 'https://cyberark.example.local' (Without the slash '/' char at the end)
- `token` - (optional) Either token or username/password need to be set to access the CyberARK API.
- `username` - (optional) Either token or username/password need to be set to access the CyberARK API.
- `password` - (optional) Either token or username/password need to be set to access the CyberARK API.

## Contributing

Pull requests are welcomed and automatically built and tested against multiple version of Python and multiple version of Nautobot through GitHub Actions.
Expand Down
2 changes: 2 additions & 0 deletions nautobot_secrets_providers/providers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
from .aws import AWSSecretsManagerSecretsProvider, AWSSystemsManagerParameterStore
from .hashicorp import HashiCorpVaultSecretsProvider
from .delinea import ThycoticSecretServerSecretsProviderId, ThycoticSecretServerSecretsProviderPath
from .cyberark import CyberARKSecretsProvider

__all__ = ( # type: ignore
AWSSecretsManagerSecretsProvider, # pylint: disable=invalid-all-object
AWSSystemsManagerParameterStore, # pylint: disable=invalid-all-object
CyberARKSecretsProvider, # pylint: disable=invalid-all-object
HashiCorpVaultSecretsProvider, # pylint: disable=invalid-all-object
ThycoticSecretServerSecretsProviderId, # pylint: disable=invalid-all-object
ThycoticSecretServerSecretsProviderPath, # pylint: disable=invalid-all-object
Expand Down
104 changes: 104 additions & 0 deletions nautobot_secrets_providers/providers/cyberark.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import socket
from email.policy import default

from django import forms
from django.conf import settings

try:
import requests
except ImportError:
requests = None

from nautobot.extras.secrets import SecretsProvider, exceptions
from nautobot.core.forms import BootstrapMixin

__all__ = ("CyberARKSecretsProvider")

class CyberARKSecretsProvider(SecretsProvider):
"""
A secrets provider for CyberARK AIM.
"""

slug = "cyberark"
name = "CyberARK"
is_available = requests is None

class ParametersForm(BootstrapMixin, forms.Form):
"""
Required parameters for CyberARK.
"""

object = forms.CharField(
required = False,
help_text = "Object to requestin CyberARK (not required if username is given)"
)
username = forms.CharField(
required = True if object.empty_value else False,
help_text = "Username to request in CyberARK (not required if objects is given)"
)
object.required = True if username.empty_value else False

@classmethod
def get_value_for_secret(cls, secret, obj=None, **kwargs):
"""
Return the value stored under the

Args:
secret (_type_): _description_
obj (_type_, optional): _description_. Defaults to None.
"""

plugin_settings = settings.PLUGINS_CONFIG['nautobot_secret_providers']
if 'cyberark' not in plugin_settings:
raise exceptions.SecretProviderError(secret, cls, "CyberARK is not configured")

cyberark_settings = plugin_settings["cyberark"]

if "url" not in cyberark_settings:
raise exceptions.SecretProviderError(secret, cls, "CyberARK url is not set in PLUGINS_CONFIG['nautobot_secret_providers']['cyberark']")

parameters = secret.rendered_parameters(obj=obj)
try:
secret_username = parameters["username"]
secret_object = parameters["object"]
except KeyError as err:
msg = f"The secret parameter could not be retrieved for field {err}"
raise exceptions.SecretParametersError(secret, cls, msg) from err

try:
if "token" in cyberark_settings:
token = cyberark_settings["token"]
else:
cyberark_settings["username"]
cyberark_settings["password"]
cyberark_settings["auth_method"]
except KeyError as err:
raise exceptions.SecretProviderError(secret, cls, f"Can't retrieve field {err} from PLUGINS_CONFIG['nautobot_secret_providers']['cyberark']")

if "token" not in cyberark_settings:
response = requests.post(
f"{cyberark_settings['url']}/PasswordVault/API/auth/{cyberark_settings['auth_method']}/Logon",
verify = cyberark_settings['verifySsl'], # If needs to disable cert check only for CyberARK
data = {
"username": cyberark_settings["username"],
"password": cyberark_settings["password"]
}
)
if response.status_code != 200:
raise exceptions.SecretProviderError(secret, cls, f"Can't get token: {response.status_code} {response.text}")

token = response.text.replace('"', '')

response = requests.get(
f"{cyberark_settings['url']}/PasswordVault/api/Accounts/?search={secret_username}", # This call can be very long to answer depending of the CyberARK instance setup
verify = cyberark_settings['verifySsl'],
headers = {"Authorization": token},
)

if response.status_code != 200:
raise exceptions.SecretProviderError(secret, cls, f"Can't get value: {response.status_code} {response.text}")
return requests.post(
f"{cyberark_settings['url']}/PasswordVault/api/Accounts/{response.json()['value'][0]['id']}/Password/Retrieve",
verify = cyberark_settings["verifySsl"],
headers = { 'Authorization': token },
).text.replace('"', '')
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ <h1>{% block title %}Secrets Providers Home{% endblock %}</h1>
<a href="https://github.com/DelineaXPM/python-tss-sdk#domain-authorization" rel="nofollow">Domain Authorization</a><br/>
<a href="https://github.com/DelineaXPM/python-tss-sdk#access-token-authorization" rel="nofollow">Access Token Authorization</a></td>
</tr>
<tr>
<td><a href="https://www.cyberark.com/" rel="nofollow">CyberARK</a></td>
<td><a href="https://www.cyberark.com/what-is/secrets-management/" rel="nofollow">Password</a></td>
<td>Username/Password</td>
</tr>
</tbody>
</table>
</div>
Expand Down
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "nautobot-secrets-providers"
version = "2.0.1"
version = "2.0.2"
description = "Nautobot Secrets Providers App"
authors = ["Network to Code, LLC <[email protected]>"]
license = "Apache-2.0"
Expand Down Expand Up @@ -60,11 +60,13 @@ all = [
"boto3",
"hvac",
"python-tss-sdk",
"requests"
]
aws = ["boto3"]
hashicorp = ["hvac"]
nautobot = ["nautobot"]
thycotic = ["python-tss-sdk"]
cyberark = ["requests"]

[tool.black]
line-length = 120
Expand Down