-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add back ldap-authentication.md * add back api-v2-docs.md * update broken readme links --------- Co-authored-by: Paul Osinski <[email protected]>
- Loading branch information
1 parent
4d2bc4f
commit 000728a
Showing
3 changed files
with
319 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
--- | ||
title: "DefectDojo API v2" | ||
description: "DefectDojo's API lets you automate tasks, e.g. uploading scan reports in CI/CD pipelines." | ||
draft: false | ||
weight: 2 | ||
--- | ||
|
||
|
||
|
||
|
||
DefectDojo\'s API is created using [Django Rest | ||
Framework](http://www.django-rest-framework.org/). The documentation of | ||
each endpoint is available within each DefectDojo installation at | ||
[`/api/v2/doc/`](https://demo.defectdojo.org/api/v2/) and can be accessed by choosing the API v2 | ||
Docs link on the user drop down menu in the header. | ||
|
||
![image](../../images/api_v2_1.png) | ||
|
||
The documentation is generated using [drf-spectacular](https://drf-spectacular.readthedocs.io/) at [`/api/v2/oa3/swagger-ui/`](https://demo.defectdojo.org/api/v2/oa3/swagger-ui/), and is | ||
interactive. On the top of API v2 docs is a link that generates an OpenAPI v3 spec. | ||
|
||
To interact with the documentation, a valid Authorization header value | ||
is needed. Visit the `/api/key-v2` view to generate your | ||
API Key (`Token <api_key>`) and copy the header value provided. | ||
|
||
![image](../../images/api_v2_2.png) | ||
|
||
Each section allows you to make calls to the API and view the Request | ||
URL, Response Body, Response Code and Response Headers. | ||
|
||
![image](../../images/api_v2_3.png) | ||
|
||
If you're logged in to the Defect Dojo web UI, you do not need to provide the authorization token. | ||
|
||
## Authentication | ||
|
||
The API uses header authentication with API key. The format of the | ||
header should be: : | ||
|
||
Authorization: Token <api.key> | ||
|
||
For example: : | ||
|
||
Authorization: Token c8572a5adf107a693aa6c72584da31f4d1f1dcff | ||
|
||
### Alternative authentication method | ||
|
||
If you use [an alternative authentication method](../social-authentication/) for users, you may want to disable DefectDojo API tokens because it could bypass your authentication concept. \ | ||
Using of DefectDojo API tokens can be disabled by specifying the environment variable `DD_API_TOKENS_ENABLED` to `False`. | ||
Or only `api/v2/api-token-auth/` endpoint can be disabled by setting `DD_API_TOKEN_AUTH_ENDPOINT_ENABLED` to `False`. | ||
|
||
## Sample Code | ||
|
||
Here are some simple python examples and their results produced against | ||
the `/users` endpoint: : | ||
|
||
{{< highlight python >}} | ||
import requests | ||
|
||
url = 'http://127.0.0.1:8000/api/v2/users' | ||
headers = {'content-type': 'application/json', | ||
'Authorization': 'Token c8572a5adf107a693aa6c72584da31f4d1f1dcff'} | ||
r = requests.get(url, headers=headers, verify=True) # set verify to False if ssl cert is self-signed | ||
|
||
for key, value in r.__dict__.items(): | ||
print(f"'{key}': '{value}'") | ||
print('------------------') | ||
{{< /highlight >}} | ||
|
||
This code will return the list of all the users defined in DefectDojo. | ||
The json object result looks like : : | ||
|
||
{{< highlight json >}} | ||
[ | ||
{ | ||
"first_name": "Tyagi", | ||
"id": 22, | ||
"last_login": "2019-06-18T08:05:51.925743", | ||
"last_name": "Paz", | ||
"username": "dev7958" | ||
}, | ||
{ | ||
"first_name": "saurabh", | ||
"id": 31, | ||
"last_login": "2019-06-06T11:44:32.533035", | ||
"last_name": "", | ||
"username": "saurabh.paz" | ||
} | ||
] | ||
{{< /highlight >}} | ||
|
||
Here is another example against the `/users` endpoint, this | ||
time we will filter the results to include only the users whose user | ||
name includes `jay`: | ||
|
||
{{< highlight python >}} | ||
import requests | ||
|
||
url = 'http://127.0.0.1:8000/api/v2/users/?username__contains=jay' | ||
headers = {'content-type': 'application/json', | ||
'Authorization': 'Token c8572a5adf107a693aa6c72584da31f4d1f1dcff'} | ||
r = requests.get(url, headers=headers, verify=True) # set verify to False if ssl cert is self-signed | ||
|
||
for key, value in r.__dict__.items(): | ||
print(f"'{key}': '{value}'") | ||
print('------------------') | ||
{{< /highlight >}} | ||
|
||
The json object result is: : | ||
|
||
{{< highlight json >}} | ||
[ | ||
{ | ||
"first_name": "Jay", | ||
"id": 22, | ||
"last_login": "2015-10-28T08:05:51.925743", | ||
"last_name": "Paz", | ||
"username": "jay7958" | ||
}, | ||
{ | ||
"first_name": "", | ||
"id": 31, | ||
"last_login": "2015-10-13T11:44:32.533035", | ||
"last_name": "", | ||
"username": "jay.paz" | ||
} | ||
] | ||
{{< /highlight >}} | ||
|
||
See [Django Rest Framework\'s documentation on interacting with an | ||
API](http://www.django-rest-framework.org/topics/api-clients/) for | ||
additional examples and tips. | ||
|
||
## Manually calling the API | ||
|
||
Tools like Postman can be used for testing the API. | ||
|
||
Example for importing a scan result: | ||
|
||
- Verb: POST | ||
- URI: <http://localhost:8080/api/v2/import-scan/> | ||
- Headers tab: | ||
|
||
add the authentication header | ||
: - Key: Authorization | ||
- Value: Token c8572a5adf107a693aa6c72584da31f4d1f1dcff | ||
|
||
- Body tab | ||
|
||
- select \"form-data\", click \"bulk edit\". Example for a ZAP scan: | ||
|
||
<!-- --> | ||
|
||
engagement:3 | ||
verified:true | ||
active:true | ||
lead:1 | ||
tags:test | ||
scan_type:ZAP Scan | ||
minimum_severity:Info | ||
skip_duplicates:true | ||
close_old_findings:false | ||
|
||
- Body tab | ||
|
||
- Click \"Key-value\" edit | ||
- Add a \"file\" parameter of type \"file\". This will trigger | ||
multi-part form data for sending the file content | ||
- Browse for the file to upload | ||
|
||
- Click send | ||
|
||
## Clients / API Wrappers | ||
|
||
| Wrapper | Status | Notes | | ||
| -----------------------------| ------------------------| ------------------------| | ||
| [Specific python wrapper](https://github.com/DefectDojo/defectdojo_api) | working (2021-01-21) | API Wrapper including scripts for continous CI/CD uploading. Is lagging behind a bit on latest API features as we plan to revamp the API wrapper | | ||
| [Openapi python wrapper](https://github.com/alles-klar/defectdojo-api-v2-client) | | proof of concept only where we found out the the OpenAPI spec is not perfect yet | | ||
| [Java library](https://github.com/secureCodeBox/defectdojo-client-java) | working (2021-08-30) | Created by the kind people of [SecureCodeBox](https://github.com/secureCodeBox/secureCodeBox) | | ||
| [Image using the Java library](https://github.com/SDA-SE/defectdojo-client) | working (2021-08-30) | | | ||
| [.Net/C# library](https://www.nuget.org/packages/DefectDojo.Api/) | working (2021-06-08) | | | ||
| [dd-import](https://github.com/MaibornWolff/dd-import) | working (2021-08-24) | dd-import is not directly an API wrapper. It offers some convenience functions to make it easier to import findings and language data from CI/CD pipelines. | | ||
|
||
Some of the api wrappers contain quite a bit of logic to ease scanning and importing in CI/CD environments. We are in the process of simplifying this by making the DefectDojo API smarter (so api wrappers / script can be dumber). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
--- | ||
title: "Authentication via LDAP" | ||
description: "Authenticate users using LDAP" | ||
draft: false | ||
weight: 4 | ||
--- | ||
|
||
## LDAP Authentication | ||
|
||
Out of the box Defect Dojo does not support LDAP authentication. | ||
|
||
*However*, since Defect Dojo is built using Django, it isn't too difficult to add support for LDAP. | ||
So long as you don't mind building your own Docker images... | ||
|
||
We will need to modify a grand total of 4-5 files, depending on how you want to pass Dojo your LDAP secrets. | ||
|
||
- Dockerfile.django-* | ||
- Dockerfile.nginx-* | ||
- requirements.txt | ||
- settings.dist.py | ||
- docker-compose.yml *(Optional)* | ||
|
||
|
||
#### Dockerfile modifications | ||
|
||
In both Dockerfile.django and Dockerfile.nginx, you want to add the following lines to the apt-get install layers: | ||
|
||
```bash | ||
libldap2-dev \ | ||
libsasl2-dev \ | ||
ldap-utils \ | ||
``` | ||
|
||
|
||
#### requirements.txt | ||
|
||
Please check for the latest version of these requirements at the time of implementation on pypi.org and use those if you can. | ||
|
||
- [https://pypi.org/project/python-ldap/](python-ldap) | ||
- [https://pypi.org/project/django-auth-ldap/](django-auth-ldap) | ||
|
||
Otherwise add the following to requirements.txt: | ||
|
||
```python | ||
python-ldap==3.4.2 | ||
django-auth-ldap==4.1.0 | ||
``` | ||
|
||
|
||
#### settings.dist.py | ||
|
||
Find the settings file (hint: `/dojo/settings/settings.dist.py`) and add the following: | ||
|
||
At the top of the file: | ||
```python | ||
import ldap | ||
from django_auth_ldap.config import LDAPSearch, GroupOfNamesType | ||
``` | ||
|
||
Then further down add LDAP settings to the env dict: | ||
```python | ||
# LDAP | ||
DD_LDAP_SERVER_URI=(str, 'ldap://ldap.example.com'), | ||
DD_LDAP_BIND_DN=(str, ''), | ||
DD_LDAP_BIND_PASSWORD=(str, ''), | ||
``` | ||
|
||
Then under the env dict add: | ||
```python | ||
AUTH_LDAP_SERVER_URI = env('DD_LDAP_SERVER_URI') | ||
AUTH_LDAP_BIND_DN = env('DD_LDAP_BIND_DN') | ||
AUTH_LDAP_BIND_PASSWORD = env('DD_LDAP_BIND_PASSWORD') | ||
AUTH_LDAP_USER_SEARCH = LDAPSearch( | ||
"ou=Groups,dc=example,dc=com", ldap.SCOPE_SUBTREE, "(uid=%(user)s)" | ||
) | ||
|
||
AUTH_LDAP_USER_ATTR_MAP = { | ||
"first_name": "givenName", | ||
"last_name": "sn", | ||
"email": "mail", | ||
} | ||
``` | ||
Please make sure to customise all of the LDAP search variables to match your company's configuration. | ||
|
||
|
||
For additional group controls you can add: | ||
```python | ||
# Set up the basic group parameters. | ||
AUTH_LDAP_GROUP_SEARCH = LDAPSearch( | ||
"dc=example,dc=com", | ||
ldap.SCOPE_SUBTREE, | ||
"(objectClass=groupOfNames)", | ||
) | ||
AUTH_LDAP_GROUP_TYPE = GroupOfNamesType(name_attr="cn") | ||
|
||
# Simple group restrictions | ||
AUTH_LDAP_REQUIRE_GROUP = "cn=DD_USER_ACTIVE,ou=Groups,dc=example,dc=com" | ||
|
||
AUTH_LDAP_USER_FLAGS_BY_GROUP = { | ||
"is_active": "cn=DD_USER_ACTIVE,ou=Groups,dc=example,dc=com", | ||
"is_staff": "cn=DD_USER_STAFF,ou=Groups,dc=example,dc=com", | ||
"is_superuser": "cn=DD_USER_ADMIN,ou=Groups,dc=example,dc=com", | ||
} | ||
``` | ||
|
||
Then also add `'django_auth_ldap.backend.LDAPBackend'` to the `AUTHENTICATION_BACKENDS` variable, for example: | ||
```python | ||
AUTHENTICATION_BACKENDS = ( | ||
'django_auth_ldap.backend.LDAPBackend', | ||
'django.contrib.auth.backends.RemoteUserBackend', | ||
'django.contrib.auth.backends.ModelBackend', | ||
) | ||
``` | ||
|
||
Read the docs for Django Authentication with LDAP here: https://django-auth-ldap.readthedocs.io/en/latest/ | ||
|
||
#### docker-compose.yml | ||
|
||
In order to pass the variables to the settings.dist.py file via docker, it's a good idea to add these to the docker compose file. | ||
|
||
You can do this by adding the following variables to the environment section for the uwsgi image: | ||
```yaml | ||
DD_LDAP_SERVER_URI: "${DD_LDAP_SERVER_URI:-ldap://ldap.example.com}" | ||
DD_LDAP_BIND_DN: "${DD_LDAP_BIND_DN:-}" | ||
DD_LDAP_BIND_PASSWORD: "${DD_LDAP_BIND_PASSWORD:-}" | ||
``` | ||
Alternatively you can set these values in a local_settings.py file. |