Skip to content

Commit

Permalink
Merge pull request #223 from NTIA/cert_auth
Browse files Browse the repository at this point in the history
Cert auth
  • Loading branch information
dboulware authored Jan 18, 2024
2 parents ef32047 + dfa62b0 commit 9c7a857
Show file tree
Hide file tree
Showing 36 changed files with 903 additions and 1,250 deletions.
218 changes: 113 additions & 105 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion configs/certs/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Certs

Add SSL certs and JWT public key here.
Add SSL certs here.
7 changes: 3 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@ services:
- DEBUG
- DOCKER_GIT_CREDENTIALS
environment:
- ADMIN_NAME
- ADMIN_EMAIL
- ADMIN_PASSWORD
- ADDITIONAL_USER_NAMES
- ADDITIONAL_USER_PASSWORD
- AUTHENTICATION
- CALLBACK_AUTHENTICATION
- CALLBACK_SSL_VERIFICATION
- CALLBACK_TIMEOUT
- CLIENT_ID
- CLIENT_SECRET
- DEBUG
- DOCKER_TAG
- DOMAINS
Expand All @@ -53,9 +54,7 @@ services:
- MAX_DISK_USAGE
- MOCK_SIGAN
- MOCK_SIGAN_RANDOM
- OAUTH_TOKEN_URL
- PATH_TO_CLIENT_CERT
- PATH_TO_JWT_PUBLIC_KEY
- PATH_TO_VERIFY_CERT
- POSTGRES_PASSWORD
- SCOS_SENSOR_GIT_TAG
Expand Down
53 changes: 30 additions & 23 deletions docs/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
},
"host": "testserver",
"schemes": [
"http"
"https"
],
"basePath": "/api",
"consumes": [
Expand All @@ -24,14 +24,21 @@
"application/json"
],
"securityDefinitions": {
"cert": {
"type": "cert",
"description": "Certificate based mutual TLS authentication. AUTHENTICATION must be set to 'CERT'. This is done by the client verifying the server certificate and the server verifying the client certificate. The client certificate Common Name (CN) should contain the username of a user that exists in the database. Client certificate verification is handled by NGINX. For more information, see https://www.rfc-editor.org/rfc/rfc5246."
},
"token": {
"type": "apiKey",
"description": "Tokens are automatically generated for all users. You can view yours by going to your User Details view in the browsable API at `/api/v1/users/me` and looking for the `auth_token` key. New user accounts do not initially have a password and so can not log in to the browsable API. To set a password for a user (for testing purposes), an admin can do that in the Sensor Configuration Portal, but only the account's token should be stored and used for general purpose API access. Example cURL call: `curl -kLsS -H \"Authorization: Token 529c30e6e04b3b546f2e073e879b75fdfa147c15\" https://localhost/api/v1`",
"description": "Tokens are automatically generated for all users. You can view yours by going to your User Details view in the browsable API at `/api/v1/users/me` and looking for the `auth_token` key. New user accounts do not initially have a password and so can not log in to the browsable API. To set a password for a user (for testing purposes), an admin can do that in the Sensor Configuration Portal, but only the account's token should be stored and used for general purpose API access. Example cURL call: `curl -kLsS -H \"Authorization: Token 529c30e6e04b3b546f2e073e879b75fdfa147c15\" https://localhost/api/v1`. AUTHENTICATION should be set to 'TOKEN'",
"name": "Token",
"in": "header"
}
},
"security": [
{
"cert": []
},
{
"token": []
}
Expand Down Expand Up @@ -642,13 +649,13 @@
},
"parameters": [
{
"name": "format",
"name": "schedule_entry_name",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "schedule_entry_name",
"name": "format",
"in": "path",
"required": true,
"type": "string"
Expand Down Expand Up @@ -752,12 +759,6 @@
]
},
"parameters": [
{
"name": "format",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "schedule_entry_name",
"in": "path",
Expand All @@ -770,6 +771,12 @@
"description": "The id of the task relative to the result",
"required": true,
"type": "integer"
},
{
"name": "format",
"in": "path",
"required": true,
"type": "string"
}
]
},
Expand Down Expand Up @@ -804,12 +811,6 @@
]
},
"parameters": [
{
"name": "format",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "schedule_entry_name",
"in": "path",
Expand All @@ -822,6 +823,12 @@
"description": "The id of the task relative to the result",
"required": true,
"type": "integer"
},
{
"name": "format",
"in": "path",
"required": true,
"type": "string"
}
]
},
Expand Down Expand Up @@ -911,13 +918,13 @@
},
"parameters": [
{
"name": "format",
"name": "schedule_entry_name",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "schedule_entry_name",
"name": "format",
"in": "path",
"required": true,
"type": "string"
Expand Down Expand Up @@ -1570,17 +1577,17 @@
},
"parameters": [
{
"name": "format",
"name": "id",
"in": "path",
"description": "A unique integer value identifying this user.",
"required": true,
"type": "string"
"type": "integer"
},
{
"name": "id",
"name": "format",
"in": "path",
"description": "A unique integer value identifying this user.",
"required": true,
"type": "integer"
"type": "string"
}
]
},
Expand Down
21 changes: 9 additions & 12 deletions env.template
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ GIT_BRANCH="git:$(git rev-parse --abbrev-ref HEAD)@$(git rev-parse --short HEAD)
# If admin user email and password set, admin user will be generated.
ADMIN_EMAIL="[email protected]"
ADMIN_PASSWORD=password
ADMIN_NAME=Admin
ADDITIONAL_USER_NAMES="" # comma separated
ADDITIONAL_USER_PASSWORD=""

# Session password for Postgres. Username is "postgres".
# SECURITY WARNING: generate unique key with something like
# `openssl rand -base64 12`
POSTGRES_PASSWORD="$(python3 -c 'import secrets; import base64; print(base64.b64encode(secrets.token_bytes(32)).decode("utf-8"))')"

# Set to enable monitoring sensors with your sentry.io account
SENTRY_DSN=

if $DEBUG; then
GUNICORN_LOG_LEVEL=debug
RAY_record_ref_creation_sites=1
Expand All @@ -64,18 +64,15 @@ MANAGER_IP="$(hostname -I | cut -d' ' -f1)"

BASE_IMAGE=ghcr.io/ntia/scos-tekrsa/tekrsa_usb:0.2.3
# Default callback api/results
# Set to OAUTH if using OAuth Password Flow Authentication, callback url needs to be api/v2/results
# Set to CERT for certificate authentication
CALLBACK_AUTHENTICATION=TOKEN
CALLBACK_TIMEOUT=2

CLIENT_ID=sensor01.sms.internal
CLIENT_SECRET=sensor-secret

# Sensor certificate with private key used as client cert
# Sensor certificate with private key used as client cert for callback URL
# Paths relative to configs/certs
PATH_TO_CLIENT_CERT=sensor01.pem
# Trusted Certificate Authority certificate to verify authserver and callback URL server certificate
# Trusted Certificate Authority certificate to verify callback URL server certificate
PATH_TO_VERIFY_CERT=scos_test_ca.crt
# Path relative to configs/certs
PATH_TO_JWT_PUBLIC_KEY=jwt_pubkey.pem
# set to JWT to enable JWT authentication

# set to CERT to enable scos-sensor certificate authentication
AUTHENTICATION=TOKEN
7 changes: 5 additions & 2 deletions nginx/conf.template
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ server {
ssl_session_tickets off;
ssl_certificate /etc/ssl/certs/ssl-cert.pem;
ssl_certificate_key /etc/ssl/private/ssl-cert.key;
ssl_protocols TLSv1.2;
ssl_client_certificate /etc/ssl/certs/ca.crt;
ssl_protocols TLSv1.2 TLSv1.3;
# ssl_client_certificate /etc/ssl/certs/ca.crt;
# ssl_verify_client on;
# ssl_ocsp on; # Enable OCSP validation
ssl_verify_depth 4;
Expand All @@ -49,11 +49,14 @@ server {

# Pass off requests to Gunicorn
location @proxy_to_wsgi_server {

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Host $http_host;
proxy_set_header X-SSL-CLIENT-DN $ssl_client_s_dn;
proxy_redirect off;
proxy_pass http://wsgi-server;

}

}
55 changes: 46 additions & 9 deletions scripts/create_superuser.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,61 @@
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sensor.settings")
django.setup()

UserModel = get_user_model()


def add_user(username, password, email=None):
try:
admin_user = UserModel._default_manager.get(username=username)
if email:
admin_user.email = email
admin_user.set_password(password)
admin_user.save()
print("Reset admin account password and email from environment")
except UserModel.DoesNotExist:
UserModel._default_manager.create_superuser(username, email, password)
print("Created admin account with password and email from environment")


try:
password = os.environ["ADMIN_PASSWORD"]
print("Retreived admin password from environment variable ADMIN_PASSWORD")
email = os.environ["ADMIN_EMAIL"]
print("Retreived admin email from environment variable ADMIN_EMAIL")
username = os.environ["ADMIN_NAME"]
print("Retreived admin name from environment variable ADMIN_NAME")
add_user(username.strip(), password.strip(), email.strip())
except KeyError:
print("Not on a managed sensor, so not auto-generating admin account.")
print("You can add an admin later with `./manage.py createsuperuser`")
sys.exit(0)

UserModel = get_user_model()

additional_user_names = ""
additional_user_password = ""
try:
admin_user = UserModel._default_manager.get(username="admin")
admin_user.email = email
admin_user.set_password(password)
print("Reset admin account password and email from environment")
except UserModel.DoesNotExist:
UserModel._default_manager.create_superuser("admin", email, password)
print("Created admin account with password and email from environment")
additional_user_names = os.environ["ADDITIONAL_USER_NAMES"]
print(
"Retreived additional user names from environment variable ADDITIONAL_USER_NAMES"
)
if (
"ADDITIONAL_USER_PASSWORD" in os.environ
and os.environ["ADDITIONAL_USER_PASSWORD"]
):
additional_user_password = os.environ["ADDITIONAL_USER_PASSWORD"].strip()
else:
# user will have unusable password
# https://docs.djangoproject.com/en/3.2/ref/contrib/auth/#django.contrib.auth.models.UserManager.create_user
additional_user_password = None
print(
"Retreived additional user password from environment variable ADDITIONAL_USER_PASSWORD"
)
except KeyError:
print("Not creating any additonal users.")


if additional_user_names != "" and additional_user_password != "":
if "," in additional_user_names:
for additional_user_name in additional_user_names.split(","):
add_user(additional_user_name.strip(), additional_user_password)
else:
add_user(additional_user_names.strip(), additional_user_password)
66 changes: 0 additions & 66 deletions scripts/restore_fixture.sh

This file was deleted.

2 changes: 1 addition & 1 deletion src/.isort.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ include_trailing_comma=True
force_grid_wrap=0
use_parentheses=True
line_length=88
known_third_party=cryptography,django,drf_yasg,environs,its_preselector,jsonfield,jwt,numpy,oauthlib,pytest,requests,requests_mock,requests_oauthlib,rest_framework,scos_actions,sigmf
known_third_party=cryptography,django,drf_yasg,environs,its_preselector,jsonfield,numpy,pytest,requests,requests_mock,rest_framework,scos_actions,sigmf
Loading

0 comments on commit 9c7a857

Please sign in to comment.