A suite for managing risks in multiple projects.
Development
Pre requirements
This Suite uses django, and go-task for basic fast setup of django.
-
poetry:
pacman -Sy poetry
Startup of the suite
-
clone this repository
-
run
go-task first-init-
it will ask for a password, this password is for your superuser with the username admin
-
This task migrates alle migrations, and adds all initial fixtures to the databases
-
-
to start the development server from now on run
go-task run-devel
Adding initial test data
To create a simple testbase run go-task add-test-fixture, this also creates a user called testuser with the password: LetMeIn-01
Testing with local ldap
Setup and test a local ldap
-
clone this repo
-
get docker up and running
-
run
go-task spawn-glauth, this will start a glauth docker, and create self signed certs for ldaps-
reach ldap at 0.0.0.0:3389
-
reach ldaps at 0.0.0.0:3636
-
-
to test if a simple search ldapsearch works, run:
$ ldapsearch -LLL -H \ ldap://localhost:3389 \ -D cn=serviceuser,ou=domain-admins,dc=glauth,dc=com \ -w domain-nimda \ -x -bdc=glauth,dc=com \ cn=viewer -
or with ldaps
$ LDAPTLS_REQCERT=allow ldapsearch -v -LLL -H \ ldaps://localhost:3636 \ -D cn=serviceuser,ou=domain-admins,dc=glauth,dc=com \ -w domain-nimda \ -x -bdc=glauth,dc=com \ cn=viewer -
all necessary user and group info can be found inside the
_supplementary/glauth/glauth-ldap-config.cfg
Setup Product Risk Suite to use LDAP(S)
-
create a custom settings file with content similar to:
product_risk_suite.ldap_settings.pyimport ldap from django_auth_ldap.config import LDAPSearch from product_risk_suite.settings import * AUTHENTICATION_BACKENDS = ( 'django_auth_ldap.backend.LDAPBackend', ) + AUTHENTICATION_BACKENDS AUTH_LDAP_SERVER_URI = "ldap://localhost:3389" AUTH_LDAP_BIND_DN = 'cn=serviceuser,ou=domain-admins,dc=glauth,dc=com' AUTH_LDAP_BIND_PASSWORD = 'domain-nimda' AUTH_LDAP_USER_SEARCH = LDAPSearch( 'dc=glauth,dc=com', ldap.SCOPE_SUBTREE, '(uid=%(user)s)', ) # Or: # AUTH_LDAP_USER_DN_TEMPLATE = 'uid=%(user)s,ou=users,dc=example,dc=com' # Set up the basic group parameters. # AUTH_LDAP_GROUP_SEARCH = LDAPSearch( # 'ou=django,ou=groups,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=enabled,ou=django,ou=groups,dc=example,dc=com' # AUTH_LDAP_DENY_GROUP = 'cn=disabled,ou=django,ou=groups,dc=example,dc=com' # Populate the Django user from the LDAP directory. # AUTH_LDAP_USER_ATTR_MAP = { # 'first_name': 'givenName', # 'last_name': 'sn', # 'email': 'mail', # } # AUTH_LDAP_USER_FLAGS_BY_GROUP = { # 'is_active': 'cn=active,ou=django,ou=groups,dc=example,dc=com', # 'is_staff': 'cn=staff,ou=django,ou=groups,dc=example,dc=com', # 'is_superuser': 'cn=superuser,ou=django,ou=groups,dc=example,dc=com', # } # This is the default, but I like to be explicit. AUTH_LDAP_ALWAYS_UPDATE_USER = True # Use LDAP group membership to calculate group permissions. # make local project permissions AUTH_LDAP_FIND_GROUP_PERMS = False # Cache distinguished names and group memberships for an hour to minimize # LDAP traffic. AUTH_LDAP_CACHE_TIMEOUT = 3600 -
adjust the values so it matches your setup. If you need more configuration values, head over to django-auth-ldap and checkout what is possible.
-
Start the product risk suite with your custom configuration, like:
poetry run product_risk_suite/manage.py runserver --settings=product_risk_suite.ldap_settings
Using the ldap auth
First you should login as the administrator you setup locally during initial setup, then checkout the Users Table. None of the ldap users is listed. Now whenever a LDAP user logs into the product risk suite, the user will added to the Users group, the administrator is then able to assign permissions. While the user is unassigned it will not be able to see anything.
The administrator then should go to the products administration page and for each product the user should have access to add an object permission. As soon as the permission was assigned a simple reload of the products page will show the newly added capabilities for that user (no re-login required).
If a user already exists with that username, then you could disable the password login for that user and as soon as the user tries to login the credentials from the LDAP will be used. This way if you know the system of your usernames within the LDAP you could prepare accounts and assign object permissions before the users will login the first time
Usage
Adding risks
In general a Risk can be defined without a specific product/project in mind. That way multiple product/project teams can share their knowledge about possible risks and only one source will be used for those, if one changes/enhances the description all others will have that knew information available. Each risk will then be evaluated on a per project basis. Depending on the project a risk might be nearly non existent for one but would have a heavy impact on the other project. So the impact will be project dependant. Mitigations will then be shared through projects, again other projects can reuse the thoughts of others on how to mitigate/fix risks.
To add or change risks you have to enter the admin interface of the system (http://127.0.0.1:8000/admin) login as an admin and start adding risks and mitigations
Adding projects
-
First you need to create a new project
-
Then you create a new Risk analysis and give it a "nice name"
-
then you add risks to that analysis and add initial risk ratings
-
later on you can add possible mitigations, evidences and after mitigation risk analysis’s
The views
-
Are accessible via http://127.0.0.1:8000 will just show the risk analysis’s nice colorized and not so spread through the tables.
-
They also display charts of the current project
The api
Browse to /graphql to see an interactive GraphQl Ui to build up your graphql query.
-
example single api call with login:
#!/bin/bash BASE_URL=https://localhost:8443/ LOGIN_URL=${BASE_URL}login YOUR_USER='admin' YOUR_PASS='nimda' COOKIES=/tmp/cookies.txt CURL_CALL="curl -k -s -c ${COOKIES} -b ${COOKIES} -e ${LOGIN_URL}" echo "Django Auth: get csrftoken ..." ${CURL_CALL} ${LOGIN_URL} > /dev/null DJANGO_TOKEN="csrfmiddlewaretoken=$(grep csrftoken ${COOKIES} | sed 's/^.*csrftoken\s*//')" echo " perform login ..." ${CURL_CALL} \ -d "${DJANGO_TOKEN}&username=${YOUR_USER}&password=${YOUR_PASS}" \ -X POST \ ${LOGIN_URL} echo " do api call ..." ${CURL_CALL} \ -H "Content-Type: application/json" \ -d '{"query": "query { productByTitle(title:\"IconTrust HMI\") { analyzes { riskEntries { risk { asset { name }, origin { name }, stride { name }, title, description } } } } }"}' \ -X GET \ ${BASE_URL}graphql echo echo " logout" rm ${COOKIES} -
example that creates an adoc table from the result:
#!/bin/bash BASE_URL=https://localhost:8443/ LOGIN_URL=${BASE_URL}login YOUR_USER='admin' YOUR_PASS='nimda' COOKIES=/tmp/cookies.txt CURL_CALL="curl -k -s -c ${COOKIES} -b ${COOKIES} -e ${LOGIN_URL}" OUTPUT_ADOC_TABLE=/tmp/output-api-table.adoc trap 'rm -f ${COOKIES}' EXIT # get csrf token ${CURL_CALL} ${LOGIN_URL} > /dev/null DJANGO_TOKEN="csrfmiddlewaretoken=$(grep csrftoken ${COOKIES} | sed 's/^.*csrftoken\s*//')" # login ${CURL_CALL} \ -d "${DJANGO_TOKEN}&username=${YOUR_USER}&password=${YOUR_PASS}" \ -X POST \ ${LOGIN_URL} ${CURL_CALL} \ -H "Content-Type: application/json" \ -d '{"query": "query { productByTitle(title:\"IconTrust HMI\") { analyzes { riskEntries { risk { asset { name }, origin { name }, stride { name, full }, title, description } } } } }"}' \ -X GET \ ${BASE_URL}graphql > /tmp/result.json # trap 'rm -f /tmp/result.json' EXIT echo "{counter2:index:0}" > ${OUTPUT_ADOC_TABLE} echo "|===" >> ${OUTPUT_ADOC_TABLE} echo "| Nr | Asset | Origin | Stride | Title | Description" >> ${OUTPUT_ADOC_TABLE} echo "" >> ${OUTPUT_ADOC_TABLE} jq -r '.data.productByTitle.analyzes[].riskEntries[] | " | {counter:index} | \(.risk.asset.name) | \(.risk.origin.name) | \(.risk.stride | map(.full) | join(", ")) | \(.risk.title) | \(.risk.description | gsub("</li><li>"; " +\n") | gsub("</div>"; " +\n") | gsub("<[^>]*>"; "") | gsub("\r\n"; " +\n")) "' \ /tmp/result.json \ >> ${OUTPUT_ADOC_TABLE} echo "|===" >> ${OUTPUT_ADOC_TABLE} echo "created ADOC table at: »${OUTPUT_ADOC_TABLE}«"
Production docker
docker run --rm -it \
--name product-risk-suite-production \
--volume ./docker/data:/data \
--volume ./docker/config:/config \
-e DJANGO_SUPERUSER_USERNAME=admin \
-e DJANGO_SUPERUSER_PASSWORD=nimda \
-e DATABASE_ENGINE=sqlite3 \
-e DATABASE_NAME=/data/product-risk-db.sqlite3 \
-e DJANGO_DEBUG=${DJANGO_DEBUG} \
-e IN_DOCKER=true \
-e SET_FIXTURES=true \
-e DJANGO_ALLOWED_HOSTS=${DJANGO_ALLOWED_HOSTS} \
-e DJANGO_CSRF_TRUSTED_ORIGINS=${DJANGO_CSRF_TRUSTED_ORIGINS} \
-p 8443:443 \
product-risk-suite:latest
Or run the the task: go-task docker-build-setup-run, which will also build the container.
docker run --rm -it \
--name product-risk-suite-production \
--volume ./docker/data:/data \
--volume ./docker/config:/config \
-e DATABASE_ENGINE=sqlite3 \
-e DATABASE_NAME=/data/product-risk-db.sqlite3 \
-e DJANGO_DEBUG=${DJANGO_DEBUG} \
-e IN_DOCKER=true \
-e SET_FIXTURES= \
-e DJANGO_ALLOWED_HOSTS=${DJANGO_ALLOWED_HOSTS} \
-e DJANGO_CSRF_TRUSTED_ORIGINS=${DJANGO_CSRF_TRUSTED_ORIGINS} \
-p 8443:443 \
product-risk-suite:latest
Or run the the task: go-task docker-build-run, which will also build the container.
ENV DJANGO_SECRET_KEY="django-insecure-scwz(sj+*te-dbfuc)vnkju5y1=p1en^d#8mz5*keg2(fj^mn-dudi-bla%" ENV DJANGO_DEBUG= ENV DJANGO_SUPERUSER_USERNAME=admin ENV DJANGO_SUPERUSER_PASSWORD=adminpassword ENV DJANGO_SUPERUSER_EMAIL=admin@example.com ENV DATABASE_ENGINE=sqlite3 ENV DATABASE_NAME= ENV DATABASE_USERNAME= ENV DATABASE_PASSWORD= ENV DATABASE_HOST= ENV DATABASE_PORT= ENV SET_FIXTURES=true ENV DJANGO_LOG_LEVEL=INFO
you should know which to overwrite with values you suite the most
VOLUME /data VOLUME /config
To customize your run you can use a local .env file, for this copy the file dot-env.example to .env (next to the topmost Taskfile.yml).
Adjust the parameters inside the .env, and then simply run one of the above taskfile jobs
Threat model support
Creating a suitable threat model
Currently only threat models created with drawio are supported.
Use the "Threat Modeling" items or any other you might like.
The important part is that each communication way you want to add risks to need a recognizable id set.
To set such an id you need to click the item inside drawio, select the Style Tab, then click on the Edit-Button and select Edit Data from the drop down.
Inside the Dialog do a double click on the text behind the ID and change it to something useful.
Basically each editor is capable of creating a working threat model as long as the svg items that should be selectable contain an attribute called data-cell-id with a name you recognize.