8000 Server utility script cleanup by 0xdade · Pull Request #373 · natlas/natlas · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Server utility script cleanup #373

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

Merged
merged 4 commits into from
Aug 2, 2020
Merged
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
32 changes: 16 additions & 16 deletions natlas-server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ Backing services in the Natlas server are defined via environment configs. They

Production-ready Natlas docker containers are [available on dockerhub](https://hub.docker.com/r/natlas/server).

Before you launch your Natlas server, ensure that you've already setup an [Elasticsearch](../#Elasticsearch) cluster that your container will be able to reach. You'll then need to create your environment config file to mount into your container. Finally, choose whether you want to store media on the local filesystem in a mounted directory or if you want to use a docker volume. For simplicity, I'll be using a local filesystem mount.
Before you launch your Natlas server, ensure that you've already setup an [Elasticsearch](/README.md#Elasticsearch) cluster that your container will be able to reach. You'll then need to create your environment config file to mount into your container. Finally, choose whether you want to store media on the local filesystem in a mounted directory or if you want to use a docker volume. For simplicity, I'll be using a local filesystem mount.

### Example natlas_env File
### Example ENV

The following is an example ENV file that assumes your elasticsearch cluster is accessible at `172.17.0.2:9200` and that your mail server is accessible without authentication at `172.17.0.4`. If you do not have a mail server, remove `MAIL_` settings. A complete list of configuration options is [available below](#the-config).
The following is an example ENV file that assumes your elasticsearch cluster is accessible at `172.17.0.2:9200` and that your mail server is accessible with authentication at `172.17.0.4`. If you do not have a mail server, remove `MAIL_` settings. A complete list of configuration options is [available below](#the-config).

```bash
#####
Expand All @@ -43,7 +43,7 @@ ELASTICSEARCH_URL=http://172.17.0.2:9200
#####
MAIL_SERVER=172.17.0.4
MAIL_USERNAME=dade.murphy
MAIL_PASSWORD=examplepassword
MAIL_PASSWORD=this-is-an-invalid-password
MAIL_FROM=noreply@example.com

#####
Expand All @@ -69,7 +69,7 @@ The Natlas server depends on the following:

## Installation (Development)

To setup for development, you'll want to fork this repository and then clone it from your fork. See our [contributing guidelines](https://github.com/natlas/natlas/blob/main/CONTRIBUTING.md) for more information.
To setup for development, you'll want to fork this repository and then clone it from your fork. See our [contributing guidelines](/CONTRIBUTING.md) for more information.

Development makes use of docker through the `docker-compose.yml` file at the root of the repository. You can modify the desired environment variables and run `docker-compose up -d natlas-server`. You can also run the complete stack by running ` docker-compose up -d `. **This method is only suggested for a development environment.**

Expand Down Expand Up @@ -119,14 +119,16 @@ Web configs are loaded from the SQL database and changeable from the web interfa

## Setting the Scope

The scope and blacklist can be set server side without using the admin interface by running the `add-scope.py` script from within the Natlas server container with the `--scope` and `--blacklist` arguments, respectively. These each take a file name to read scope from, which means you need to put them in a volume that is mounted in your container. You may optionally specify `--verbose` to see exactly which scope items succeeded to import, failed to import, or already existed in the scope. A scope is **REQUIRED** for agents to do any work, however a blacklist is optional.
The scope and blacklist can be set server side without using the admin interface by running the `flask scope import` command from within the natlas container with the `--scope` and `--blacklist` arguments, respectively. If neither option is supplied, `--scope` is assumed.

You may optionally specify `--verbose` to see exactly which scope items succeeded to import, failed to import, or already existed in the database. If you're importing a lot of items, it is recommended that you redirect the results to a file.

```bash
$ docker exec -it $(docker ps | grep natlas/server | cut -d' ' -f1) /bin/bash
root@5dd0d2d6ecdf:/opt/natlas/natlas-server# python add-scope.py --scope /data/bootstrap/myscopefile.txt
root@5dd0d2d6ecdf:/opt/natlas/natlas-server# python add-scope.py --blacklist /data/bootstrap/myblacklistfile.txt
docker exec -it $(docker ps | grep natlas/server | cut -d' ' -f1) flask scope import --verbose /data/bootstrap/myscopefile.txt > /data/bootstrap/import_results.json
```

A scope is **REQUIRED** for agents to do any work, however a blacklist is optional.

## Creating Your First User

In order to get started interacting with Natlas, you'll need an administrator account. Admins are allowed to make changes to the following via the web interface:
Expand All @@ -137,27 +139,25 @@ In order to get started interacting with Natlas, you'll need an administrator ac
* scanning exclusions
* services that agents scan for

You can bootstrap your first admin account using the `add-user.py` script. This script supports creating invitations for users with or without an email address. Whether the user will be invited as an admin or not is handled by the `--admin` flag. If a supplied email already exists in the User table, it will be toggled to be an admin. This can be helpful if you accidentally remove yourself as an admin and can't get back into the admin interface.
You can bootstrap your first admin account using the `flask user new` command. This command supports creating invitations for users with or without an email address. Whether the user will be invited as an admin or not is handled by the `--admin` flag.

**NOTE:** This script **requires** the `SERVER_NAME` environment option so that links can be generated correctly. This should be set to whatever you want the link to generate with(e.g. `SERVER_NAME=localhost:5000` will generate `https://localhost:5000/auth/invite?token=blahblahblah`)
**NOTE:** This script **requires** the `SERVER_NAME` environment option so that links can be generated correctly. This should be set to whatever you want the link to generate with(e.g. `SERVER_NAME=localhost:5000` will generate `https://localhost:5000/auth/invite?token=this-is-an-invalid-example-token`)

### With Email

If you have a mail server configured, you can specify the email address and the script will automatically send them an invitation email.

```bash
$ docker exec -it $(docker ps | grep natlas/server | cut -d' ' -f1) /bin/bash
root@5dd0d2d6ecdf:/opt/natlas/natlas-server# SERVER_NAME=localhost:5000 ./add-user.py --email example@example.com --admin
Sent example@example.com an invitation email via localhost
$ docker exec -e SERVER_NAME=example.com -it $(docker ps | grep natlas/server | cut -d' ' -f1) flask user new --email example@example.com --admin
Email sent to example@example.com via localhost
```

### Without Email

Alternatively, you can create a new user invitation link that can be given to anyone.

```bash
$ docker exec -it $(docker ps | grep natlas/server | cut -d' ' -f1) /bin/bash
root@5dd0d2d6ecdf:/opt/natlas/natlas-server# SERVER_NAME=example.com ./add-user.py --admin
$ docker exec -e SERVER_NAME=example.com -it $(docker ps | grep natlas/server | cut -d' ' -f1) flask user new --admin
Accept invitation: http://example.com/auth/invite?token=this-is-an-invalid-example-token
```

Expand Down
62 changes: 0 additions & 62 deletions natlas-server/add-scope.py

This file was deleted.

67 changes: 0 additions & 67 deletions natlas-server/add-user.py

This file was deleted.

9 changes: 9 additions & 0 deletions natlas-server/app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,18 @@ def create_app(config_class=config.Config, load_config=False):
load_natlas_services(app)
load_agent_config(app)
load_agent_scripts(app)

if db.engine.has_table("scope_item"):
ScopeManager.load_all_groups()

from app.cli.user import cli_group as user_cli

app.cli.add_command(user_cli)

from app.cli.scope import cli_group as scope_cli

app.cli.add_command(scope_cli)

from app.errors import bp as errors_bp

app.register_blueprint(errors_bp)
Expand Down
54 changes: 41 additions & 13 deletions natlas-server/app/auth/email.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
from flask import render_template, current_app, flash
from flask import render_template, current_app, flash, url_for
from app.email import send_email
from app.models import User

token_types = {
"reset": {
"subject": "[Natlas] Reset Your Password",
"template": "email/reset_password.txt",
"route": "auth.reset_password",
},
"invite": {
"subject": "[Natlas] You've been invited to Natlas!",
"template": "email/user_invite.txt",
"route": "auth.invite_user",
},
}


def validate_email(addr):
from app.models import User

validemail = User.validate_email(addr)
if not validemail:
flash(
Expand All @@ -14,20 +28,34 @@ def validate_email(addr):
return validemail


def build_email_url(token, token_type):
return url_for(
token_types[token_type]["route"],
token=token,
_external=True,
_scheme=current_app.config["PREFERRED_URL_SCHEME"],
)


def email_configured() -> bool:
return current_app.config["MAIL_FROM"] and current_app.config["MAIL_SERVER"]


def send_auth_email(email, token, token_type):
token_types = {
"reset": {
"subject": "[Natlas] Reset Your Password",
"template": "email/reset_password.txt",
},
"invite": {
"subject": "[Natlas] You've been invited to Natlas!",
"template": "email/user_invite.txt",
},
}
send_email(
token_types[token_type]["subject"],
sender=current_app.config["MAIL_FROM"],
recipients=[email],
text_body=render_template(token_types[token_type]["template"], token=token),
text_body=render_template(
token_types[token_type]["template"], url=build_email_url(token, token_type)
),
)


def deliver_auth_link(email: str, token: str, token_type: str):
if email_configured() and email:
send_auth_email(email, token, token_type)
msg = f"Email sent to {email} via {current_app.config['MAIL_SERVER']}!"
else:
msg = f"Share this link: {build_email_url(token, token_type)}"
return msg
10 changes: 8 additions & 2 deletions natlas-server/app/auth/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
AcceptInviteForm,
)
from app.models import User, UserInvitation
from app.auth.email import send_auth_email, validate_email
from app.auth.email import deliver_auth_link, validate_email, email_configured
from app.auth import bp
from app.auth.wrappers import is_not_authenticated, is_authenticated
from werkzeug.urls import url_parse
Expand Down Expand Up @@ -66,14 +66,20 @@ def register():
@bp.route("/reset_password_request", methods=["GET", "POST"])
@is_not_authenticated
def reset_password_request():
if not email_configured():
flash(
"Sorry, self-service password resets are not currently available. Please contact an administrator for assistance.",
"warning",
)
return redirect(url_for("auth.login"))
form = ResetPasswordRequestForm()
if form.validate_on_submit():
validemail = validate_email(form.email.data)
if not validemail:
return redirect(url_for("auth.reset_password_request"))
user = User.get_reset_token(validemail)
if user:
send_auth_email(user.email, user.password_reset_token, "reset")
deliver_auth_link(user.email, user.password_reset_token, "reset")
db.session.commit()
flash("Check your email for the instructions to reset your password", "info")
return redirect(url_for("auth.login"))
Expand Down
Empty file.
Loading
0