diff --git a/README.md b/README.md
new file mode 100644
index 0000000..903e395
--- /dev/null
+++ b/README.md
@@ -0,0 +1,29 @@
+# Potato Farmer
+
+Manages `potato_field` and `potato_plants`. Main way to administrate containers.
+
+## Setup
+
+### Dependencies
+
+There are several dependencies required for the development and deployment of this project.
+The first is Docker Engine and Docker Compose. The second is Python3.
+The following list links to the installation instructions for each of these:
+
+* [Docker Engine installation instructions.](https://docs.docker.com/engine/install/)
+* [Docker Compose installation instructions.](https://docs.docker.com/compose/install/)
+* [Python3 installation instructions.](https://www.python.org/downloads/)
+
+### Sub-repository management
+
+The tool `setup.py` can be used to clone (or symlink) project repositories for deployment.
+
+For instance, the following command will clone the `potato_plant_dashboard` and `potato_plant_missions`
+repositories (hosted in the BourbonWarfare organization) into the project root:
+```bash
+python3 setup.py add plant_dashboard plant_missions
+```
+The resulting repositories would then be cloned into `potato_field/` and are symlinked into the
+`potato/` directory.
+
+
diff --git a/docs/backend/diagrams/backend.drawio b/docs/backend/diagrams/backend.drawio
new file mode 100644
index 0000000..edd4003
--- /dev/null
+++ b/docs/backend/diagrams/backend.drawio
@@ -0,0 +1 @@
+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
\ No newline at end of file
diff --git a/docs/backend/diagrams/backend.html b/docs/backend/diagrams/backend.html
new file mode 100644
index 0000000..1bd5ed7
--- /dev/null
+++ b/docs/backend/diagrams/backend.html
@@ -0,0 +1,11 @@
+
+
+
+
+backend-architecture-diagram
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/backend/diagrams/backend.png b/docs/backend/diagrams/backend.png
new file mode 100644
index 0000000..6b3687a
Binary files /dev/null and b/docs/backend/diagrams/backend.png differ
diff --git a/docs/threat-model.md b/docs/threat-model.md
new file mode 100644
index 0000000..8794002
--- /dev/null
+++ b/docs/threat-model.md
@@ -0,0 +1,154 @@
+# `potato_farmer` Threat Model
+
+## Project Description
+
+This project manages `potato_field` and `potato_plants`. It's the main way to administrate
+containers; services that run in the backend to support the main application.
+
+### User System
+
+There will be a user system with a hierarchy of users, ranging from administrators to normal users
+that have restricted access to applications. A user should be uniquely identified and should not
+be able to pose as another user.
+
+### Backend Services
+
+There will be a number of backend services that expose an API for other backend applications to
+interact with. These backend services should **not** be exposed to the outside world; rather,
+this should be managed by the API Gateway.
+
+### API Gateway
+
+The API Gateway takes requests from the outside world -- that is, from users and outside automated
+services -- and collates data and operations from one or more backend services to satisfy the
+request. The API Gateway should generally be the **only** way for users to access applications.
+
+## Threats
+
+### (1) User Impersonation
+
+As users should be uniquely identified, it would violate the assumptions of our application if a
+user was able to successfully pose as another user. This would possibly permit a user to access
+another user's information, ruin their reputation in community spaces/applications, or utilize
+privileges that they should not have access to, as described in (2).
+
+### (2) Privilege Escalation
+
+As there will be a hierarchy of users with varying capabilities, it should not be possible for a
+user to gain additional privileges without them being granted by an administrator. Violation of
+this assumption would allow ordinary users to take advantage of privileges they should not have
+access to, potentially wreaking havoc regardless of malicious intent.
+
+### (3) Direct Access to Backend Services
+
+Backend services should not be directly accessible. Violation of this principle could permit actors
+to access APIs they should not have access to, potentially wreaking havoc on internal state or
+exposing data that should remain private to them.
+
+### (4) API Gateway Overloading
+
+As the API Gateway is the main way for users to interact with applications, it serves as a single
+point of failure for the entire application. By overloading the API Gateway, malicious actors (or
+just a sudden spike of traffic) can disrupt the availability of the application for all users.
+
+### (5) Network Eavesdropping
+
+There may be actors that are listening in on communications between our users or between services
+in our application. Through eavesdropping on network communications, malicious actors could learn
+private user/system/operational data that they should not have access to. Further, they may be able
+to construct spoofed requests for abuse in conjunction with Threat (3).
+
+## Defense Mechanisms
+
+### (1, 2) User Authentication
+
+To protect against user impersonation, the application will utilize mechanisms of user authentication
+to validate the user's identity prior to allowing access to any privileged operations or data (including
+operations or data tied to user identity).
+
+#### Password Authentication
+
+Upon user creation, users will select a password to use for authentication. If possible, we should
+include a password strength meter to help users choose a strong password. We will impose several
+requirements to ensure the password is not trivially bruteforceable.
+
+1. Minimum of 12 characters; maximum of 60 characters
+2. Includes at least one number
+3. Includes at least one uppercase and lowercase alphabetic character
+4. Includes at least one non-alphabetic character (symbol)
+
+These passwords will be stored in a User Authentication service. The modern algorithm Argon2id is
+considering the most secure and correct to use in modern applications, but is resource-heavy. All
+passwords should be salted, but Argon2id should handle this itself. Password hashes should be
+compared using only safe functions that have been vetted by security experts.
+
+#### Two Factor Authentication (2FA)
+
+As user passwords may be shared between multiple services, may be bruteforceable, may be leaked
+through a variety of means, etc., our application will utilize 2FA to further validate user
+identities. This 2FA mechanism will be opt-in and will utilize email to send a user a six-digit
+one-time code to be validated after username/password validation succeeds. Bypassing this mechanism
+requires malicious actors to also have access to the user's email.
+
+### (1, 2) User Sessions
+
+It is not practical to require users to authenticate for every request. This presents a poor UX
+and is expensive for our backend services. To alleviate this pressure, we will implement user
+sessions to permit users to authenticate once and continue submitting requests for a time without
+requiring authentication again.
+
+User sessions will be implemented by a `SessionID` that is a 128-bit string, the length chosen
+to harden `SessionID`s against bruteforce attacks. These will be randomly generated with
+cryptographically strong randomness. `SessionID`s will be stored in a Redis memory store alongside
+a `UserID` and a session timeout. Sessions will have an idle timeout of 1 hour and an absolute
+timeout of 1 day after authentication. We could consider introducing a renewal timeout as well,
+but this would add additional complexity.
+
+Users will receive their 128-bit `SessionID` after successful user authentication. This will have
+to be sent with every privileged request in a manner specified by the application API. The API
+Gateway would check the Redis memory store to validate the `SessionID` before serving the request.
+Failure to validate the `SessionID` would result in a failure which would be communicated to the user.
+
+Note that as `SessionID`s will be sent as a HTTP Cookie, it is important to properly protect against
+CSRF attacks with the proper use of CSRF tokens.
+
+### (3) Internal Docker Network
+
+Backend services will operate within an internal Docker network that would not have exposed ports
+to the outside world. Only the API Gateway will be publicly accessible. This should prevent
+outside access of restricted backend services and force users to use the API Gateway to interact
+with applications.
+
+To clarify, the API Gateway will also be containerized and will access backend service APIs through
+the internal Docker network. Docker containers within the internal network will be assumed to be
+trustworthy and can talk at will.
+
+### (4) Rate Limiting
+
+As the application continues to grow in scale, users will be rate limited and the API Gateway will
+not serve requests beyond the rate limit. Rate limiting should be stricter for non-authenticated
+endpoints. Users who repeatedly attempt to violate rate limiting rules should be restricted from
+accessing authenticated endpoints. Anonymous actors who repeatedly attempt to violate rate limiting
+rules may have their source IP banned (note that this may be problematic if many machines share a
+NAT).
+
+We may consider implementing our own rate limiting mechanisms, or using off-the-shelf solutions.
+This mechanism will also likely depend on the implementation of our API Gateway.
+
+### (5) TLS/SSL/DTLS
+
+All user-application communications and backend-service communications will utilize TLS/SSL for TCP
+connections to ensure that private user/system/operational data is not leaked to external eavesdroppers.
+Thanks to Mechanism (3), it should not be possible for eavesdroppers to have access to private
+backend service communications, but these should still be encrypted as much as possible regardless
+to ensure a layered defense.
+
+For UDP communications, we can consider a variety of integrity mechanisms. DTLS has been advertised
+as an integrity mechanism analogous to TLS for TCP, but the practical usage of this protocol may
+need more investigation.
+
+## Appendix
+
+Consult the [Web Security Confluence Page](https://bourbonwarfare.atlassian.net/wiki/spaces/~62fd97fc88b05653fa7d6975/pages/557057/Web+Security)
+for a list of some of the sources consulted in the making of this document, as well as a list of
+resources on how to implement some of these security mechanisms correctly.
diff --git a/setup.py b/setup.py
index 44d964d..8b03035 100644
--- a/setup.py
+++ b/setup.py
@@ -15,31 +15,44 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
"""
+
+
import argparse
-import shutil
import os
+import shutil
import subprocess
+import sys
+
BW_GITHUB_PATH = 'git@github.com:BourbonWarfare/potato_{}.git'
THIS_PATH = os.path.dirname(os.path.realpath(__file__))
REPO_SYMLINK_PATH_UNFORMATTED = 'potato/{}'
-def has_requirements():
- if shutil.which('git') is None:
- return False
- return True
+def missing_requirements():
+ '''Returns a list of missing requirements; the empty list is returned
+ if all requirements are satisfied.'''
+
+ reqs = ['git']
+
+ res = []
+ for req in reqs:
+ if shutil.which(req) is None:
+ res.append(req)
+ return res
+
def is_path(potential_path):
if os.path.isdir(potential_path):
return potential_path
- else:
- raise NotADirectoryError(potential_path)
+
+ raise NotADirectoryError(potential_path)
+
def link_directories(repo_path):
try:
is_path(repo_path)
- except:
+ except NotADirectoryError:
print('"{}" is not a valid path to symlink!'.format(repo_path))
return
@@ -51,46 +64,81 @@ def link_directories(repo_path):
print('Linking "{}" to "{}"'.format(repo_path, symlink_path))
os.symlink(repo_path, symlink_path)
+
def clone_repo(clone_path, repository):
repo_name = "potato_" + repository
repo_path = os.path.join(clone_path, repo_name)
- subprocess.run(['git', 'clone', BW_GITHUB_PATH.format(repository), repo_path])
- link_directories(repo_path)
+ try:
+ subprocess.run(['git', 'clone', BW_GITHUB_PATH.format(repository), repo_path],
+ check=False)
+ link_directories(repo_path)
+ except Exception as e:
+ print("Error cloning {} to {}: {}!".format(
+ BW_GITHUB_PATH.format(repository), repo_path, e))
+
def create_repo(clone_path, repository):
print("Not implemented")
+
def link(args):
link_directories(args.repositories[0])
-
+
+
def add(args):
args.path = os.path.join(args.path, 'potato_field')
for repo in args.repositories:
- clone_repo(args.path, repo)
-
-if not has_requirements():
- exit()
-
-parser = argparse.ArgumentParser(
- prog = 'setup.py',
- description = 'Manages the setup of any repository that falls under potato_field',
- epilog = """
- potato_field_manager Copyright (C) 2022 Bailey Danyluk
- This program comes with ABSOLUTELY NO WARRANTY.
- This is free software, and you are welcome to redistribute it
- under certain conditions."""
-)
-
-parser.add_argument('mode', choices=['link', 'add'], default='add', help='Whether to setup a new repository or link an existing one')
-parser.add_argument('repositories', nargs='+', help='Select which repositories to clone and setup')
-parser.add_argument('--path', '-p', required=False, type=is_path, default=os.path.dirname(os.path.realpath(__file__)), help='The directory where the repository will be cloned to. If not specified, repository will be cloned within the directory this tool is run')
-args = parser.parse_args()
-
-if args.mode == 'link':
- link(args)
-elif args.mode == 'add':
- add(args)
-
-
+ if args.new:
+ create_repo(args.path, repo)
+ else:
+ clone_repo(args.path, repo)
+
+
+def main():
+ missing = missing_requirements()
+ if len(missing) > 0:
+ print("The following requirements are missing:", missing)
+ print("Exiting...")
+ sys.exit(1)
+
+ parser = argparse.ArgumentParser(
+ prog='setup.py',
+ description='Manages the setup of any repository that falls under potato_field',
+ epilog="""
+ potato_field_manager Copyright (C) 2022 Bailey Danyluk
+ This program comes with ABSOLUTELY NO WARRANTY.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions."""
+ )
+
+ parser.add_argument(
+ 'mode',
+ choices=['link', 'add'],
+ default='add',
+ help='Whether to setup a new repository or link an existing one')
+ parser.add_argument('repositories',
+ nargs='+',
+ help='Select which repositories to clone and setup')
+ parser.add_argument('--path', '-p',
+ nargs=1,
+ required=False,
+ type=is_path,
+ default=os.path.dirname(os.path.realpath(__file__)),
+ help='The directory where the repository will be cloned to. If not \
+ specified, repository will be cloned within the directory this tool is run')
+ parser.add_argument('--new',
+ required=False,
+ default=False,
+ help='Create the repository instead of cloning an existing repository')
+ args = parser.parse_args()
+
+ if args.mode == 'link':
+ link(args)
+ elif args.mode == 'add':
+ add(args)
+
+
+if __name__ == '__main__':
+ main()