Giter Club home page Giter Club logo

crossfeed's Issues

Integrate with login.gov for user accounts and authentication

Right now, authentication is based on AWS Cognito. We will need to manage user accounts and enforce authentication on Crossfeed. For users, we should store the following:

  • User name
  • User email address
  • User role - user or global admin
  • List of organizations with access to if user
  • Other relevant authentication info

We should delegate authentication to an established authentication provider. Either AWS Cognito (currently implemented) or Login.gov are viable options.

login.gov initial setup not working

Running the code from #56:

When setting up code from scratch, I can't do anything because there are no organizations!

image

This is probably because we need to set the first name of the user, so that the user doesn't have to go through this prompt, at this code:

firstName: '',
lastName: '',
userType: process.env.IS_OFFLINE ? 'globalAdmin' : 'standard'

Add pshtt scanner

Add the pshtt scanner (which utilizes sslyze under the hood) in order to scan HTTPS configurations of domains.

As pshtt is a python module, it will be easiest to write this as a python Lambda function, which is then synchronously invoked by a wrapper JS function to manage DB lookups and updates.

At a high level:

  1. Create a wrapper JS function to fetch all live websites (port 80 or 443), see getLiveWebsites in helpers.
  2. Store the list of websites in S3 (we could pass them as input, but that's limited by the 6 MB input limit which we might exceed). For local development use serverless-s3-local.
  3. Synchronously invoke the Python Lambda function, passing the S3 object name as input - docs
  4. Run pshtt against all domains from the Python Lambda. Example of using pshtt programmatically.
  5. Write output to another S3 object, return the object name.
  6. In the wrapper Lambda, store all results in the DB

Build cron lambda service

Build a lambda service that will continuously fetch jobs from the database, see which ones need to be spawned, and spawn the relevant Lambda functions.

Add Censys module

Module to fetch all relevant domains from Censys

API docs

We should start with the Censys Certificates API in order to fetch all known certificates that match a root domain.

For each root domain of each organization, we should:

  • Fetch certificates using the /api/v1/search/certificates endpoint (example web query). Example POST body:

{
"query": "parsed.names: cisa.gov",
"fields":["parsed"]
}

See here for a complete list of data that can be pulled.

Note that Censys has rate limiting of 1 request per second. Each page returns 100 rows, and we should request all pages.

  • For each returned record, insert the domain into the database if it does not exist (see how the bitdiscovery scanner does this)
  • For each piece of useful metadata, store this in the database as a SSLInfo record. We should try to match the fields as closely as possible, though we can create new fields if necessary.

findomain doesn't work with domains with large results

🐛 Bug Report

The findomain lambda function doesn't work with domains that have more than a handful of subdomains.

To Reproduce

Steps to reproduce the behavior:

  1. Add an organization with domain google.com.
  2. Run docker-compose run backend npx serverless invoke local -f findomain.
  3. The function just hangs and never finishes.

Expected behavior

Google has ~36000 subdomains, which takes only 15 seconds if I run findomain -t google.com. These subdomains should show up in the dashboard.

Any helpful log output

My computer just hangs after the last line:

% docker-compose run backend npx serverless invoke local -f findomain  
Starting crossfeed_db_1 ... done
 
 Serverless Warning --------------------------------------
 
  A valid environment variable to satisfy the declaration 'env:CENSYS_API_ID' could not be found.
 
 
 Serverless Warning --------------------------------------
 
  A valid environment variable to satisfy the declaration 'env:CENSYS_API_SECRET' could not be found.
 
Serverless: Compiling with Typescript...
Serverless: Using local tsconfig.json
Serverless: Typescript compiled.
=> DB Connected

Integrate ARIN Whois API

The ARIN Whois API offers the ability to query for IP ranges owned by specific organizations. We can make use of this in order to gather IP ranges that belong to organizations in Crossfeed.

For each organization, we should:

  1. Find associated organizations in ARIN. This is possible by either searching for the organization by name (i.e. http://whois.arin.net/rest/orgs;name=DEPARTMENT%20OF%20HOMELAND%20SECURITY) or tracing known associated IP addresses.
  2. Get ARIN Organization details: https://whois.arin.net/rest/org/DHS-37.html
  3. Collect networks (https://whois.arin.net/rest/org/DHS-37/nets), AS numbers (https://whois.arin.net/rest/org/DHS-37/asns), and contacts (https://whois.arin.net/rest/org/DHS-37/pocs).

This is all possible via the API as well as web interface.

Note that as entries in ARIN may be outdated, we should start scans for these CIDRs in passive mode until confirmed by the owner.

Add loading animations

This will make network requests (especially ones regarding auth / login) feel smoother.

User onboarding flow updates

Pre-pilot

  • - Organization admins should be able to invite users directly to their organization
  • - Implement organization-specific views
  • - Allow global admins to create organization and invite first admin

Future post-pilot tasks:

  • Users should be able to request creating an organization if theirs does not already exist
  • When creating an organization, users should be able to specify root domains and/or IP blocks that they own
  • Implement approval process for new organizations

Add authentication to tests

Backend tests are broken after #56 as authentication is now required. We should add authentication to tests in order to fix.

TypeError: Cannot read property 'filter' of undefined

After login, I get stuck on this page:

image

Logs:

ackend_1 | => DB Connected
backend_1 | TypeError: Cannot read property 'filter' of undefined
backend_1 | at /app/.build/src/api/auth.js:128:26
backend_1 | at step (/app/.build/src/api/auth.js:33:23)
backend_1 | at Object.next (/app/.build/src/api/auth.js:14:53)
backend_1 | at fulfilled (/app/.build/src/api/auth.js:5:58)
backend_1 | at processTicksAndRejections (internal/process/task_queues.js:93:5)
backend_1 | TypeError: Cannot read property 'filter' of undefined
backend_1 | at /app/.build/src/api/auth.js:128:26
backend_1 | at step (/app/.build/src/api/auth.js:33:23)
backend_1 | at Object.next (/app/.build/src/api/auth.js:14:53)
backend_1 | at fulfilled (/app/.build/src/api/auth.js:5:58)
backend_1 | at processTicksAndRejections (internal/process/task_queues.js:93:5)
backend_1 | (node:176) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'filter' of undefined
backend_1 | at /app/.build/src/api/auth.js:128:26
backend_1 | at step (/app/.build/src/api/auth.js:33:23)
backend_1 | at Object.next (/app/.build/src/api/auth.js:14:53)
backend_1 | at fulfilled (/app/.build/src/api/auth.js:5:58)
backend_1 | at processTicksAndRejections (internal/process/task_queues.js:93:5)
backend_1 | (Use node --trace-warnings ... to show where the warning was created)
backend_1 | (node:176) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
backend_1 | (node:176) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
backend_1 | => DB Connected
backend_1 | (node:187) UnhandledPromiseRejectionWarning: TypeError [ERR_MISSING_ARGS]: The "message" argument must be specified
backend_1 | at process.target._send (internal/child_process.js:721:13)
backend_1 | at process.target.send (internal/child_process.js:706:19)
backend_1 | at process. (/app/node_modules/serverless-offline/dist/lambda/handler-runner/child-process-runner/childProcessHelper.js:47:11)
backend_1 | at processTicksAndRejections (internal/process/task_queues.js:93:5)
backend_1 | (Use node --trace-warnings ... to show where the warning was created)
backend_1 | (node:187) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
backend_1 | (node:187) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Add web crawler

One option: https://github.com/hakluke/hakrawler

We should extract and make accessible JavaScript files, both for finding internal endpoints and external dependencies.

Steps:

  • Use hakrawler to get a list of all pages
  • Create a Webpage model
  • Display this data in a table, visualize it like a directory listing
  • Tags (login page, admin page, directory listings, signup pages, form pages, php code)

Data to store in a Webpage:

  • status code
  • response length
  • response html (maybe look into elasticsearch)
  • GET requests
  • screenshot (S3 url)

Later steps:

  • Visualize hyperlinks / connections using a map

Frontend frequently runs out of memory

The frontend container frequently runs out of memory when running locally.

frontend_1  | The build failed because the process exited too early. This probably means the system ran out of memory or someone called `kill -9` on the process.
frontend_1  | npm ERR! code ELIFECYCLE
frontend_1  | npm ERR! errno 1
frontend_1  | npm ERR! [email protected] start: `react-scripts start`
frontend_1  | npm ERR! Exit status 1
frontend_1  | npm ERR! 
frontend_1  | npm ERR! Failed at the [email protected] start script.
frontend_1  | npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
frontend_1  | 
frontend_1  | npm ERR! A complete log of this run can be found in:
frontend_1  | npm ERR!     /root/.npm/_logs/2020-07-11T21_44_44_085Z-debug.log

Periodic backend errors when running locally

When running locally, I get periodic backend errors with the following message:

backend_1   | (node:493) UnhandledPromiseRejectionWarning: TypeError [ERR_MISSING_ARGS]: The "message" argument must be specified
backend_1   |     at process.target._send (internal/child_process.js:721:13)
backend_1   |     at process.target.send (internal/child_process.js:706:19)
backend_1   |     at process.<anonymous> (/app/node_modules/serverless-offline/dist/lambda/handler-runner/child-process-runner/childProcessHelper.js:47:11)
backend_1   |     at processTicksAndRejections (internal/process/task_queues.js:93:5)
backend_1   | (Use `node --trace-warnings ...` to show where the warning was created)
backend_1   | (node:493) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
backend_1   | (node:493) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

JWT improvements

Add ability to automatically refresh JWT tokens after a set period of time (e.g. 15 mins)

Examine Fargate quotas / request an increase

From this page, here are some limits on Fargate tasks:

Service quota Description Default quota value
Clusters per account The maximum number of clusters per Region, per account. 10,000
Container instances per cluster The maximum number of container instances per cluster. 2,000
Services per cluster The maximum number of services per cluster. 2,000
Tasks using the EC2 launch type per service (the desired count) The maximum number of tasks using the EC2 launch type per service (the desired count). This limit applies to both standalone tasks and tasks launched as part of a service. 2,000
Tasks using the Fargate launch type or the FARGATE capacity provider, per Region, per account The maximum number of tasks using the Fargate launch type or the FARGATE capacity provider, per Region. This limit applies to both standalone tasks and tasks launched as part of a service. 100
Fargate Spot tasks, per Region, per account The maximum number of tasks using the FARGATE_SPOT capacity provider, per Region. 250
Public IP addresses for tasks using the Fargate launch type The maximum number of public IP addresses used by tasks using the Fargate launch type, per Region. 100

These quotas are adjustable, though. When we deploy this to production, we should estimate the quota of Fargate tasks that we need, then request an increase from AWS support.

"ERR_MISSING_ARGS" error produced by scheduler

🐛 Bug Report

This can happen when I try to paginate to go to the next page.

It makes a request to /domain/search, but then the request just hangs and never completes.

In the Docker logs for the backend, I get:

(node:326) UnhandledPromiseRejectionWarning: TypeError [ERR_MISSING_ARGS]: The "message" argument must be specified
    at process.target._send (internal/child_process.js:721:13)
    at process.target.send (internal/child_process.js:706:19)
    at process.<anonymous> (/app/node_modules/serverless-offline/dist/lambda/handler-runner/child-process-runner/childProcessHelper.js:47:11)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
(Use `node --trace-warnings ...` to show where the warning was created)

Parse Censys IP snapshot raw data - https://censys.io/data/ipv4

This will allow us to extract full port and banner information. See https://censys.io/data/ipv4.

We will want to implement an ETL pipeline that can ingest this data using Lambda functions, where each function processes one chunk of data. Censys divides each daily scan into ~2500 separate ~100 MB chunks.

We should use an SQS queue to trigger these Lambda functions. Reference: https://www.serverless.com/blog/aws-lambda-sqs-serverless-integration/

Use serverless-webpack for lambda deployment

This will allow us to have smaller lambda deployment packages (as we won't have to include node_modules in the deployment packages), thus speeding up both packaging time and cold start times.

Add amass module

Utilize amass in order to passively collect subdomains given an organization's root domain.

  • Run amass on all root domains
  • Store new domains/IP address combinations in database
  • (optional) add task scheduling logic to schedule passive port scans directly after amass runs

This should be hosted initially as a Lambda function, though we may have to switch to ECS Fargate if execution time gets to be too long.

Refactor scanning backend

Switch from using lambda -> fargate for tasks.

DB

Keep the same schemas as the existing Scan table:

id name arguments frequency lastRun
id1 findomain scan cisa.gov 1 hour null

Deployment

  • We should push a single Docker image with all our tools (amass, findomain, etc.) to ECR, then create a task definition with this image -- this definition will be used for all Fargate tasks.
  • I think it's easier to manage a single docker image with all our tools on it, rather than having to create docker images for each tool.

Fargate

  • When launching the Fargate task, we can use overrides to specify a different start command (which tool to use) and parameters (domain name).
  • Fargate tasks have a minimum of 20 GB ephemeral storage -- I think that is enough for us, so I don't think we need EFS for storage right now. We could add it later if needed, though. thoughts @cablej ?
  • The Fargate task will be in our private subnet so that it can access the database. Potential problem: I'm not sure how networking with Fargate works. If each Fargate instance requires a separate ENI (and thus a separate private IP), we'll be limited by our subnet size -- we would need to make sure the subnet size is big enough. AWS lambda behaved the same way and had the same issue with private subnets -- until a recent update in which lambda functions were able to share ENIs and thus only require a handful of IP addresses available in a subnet. We should ask AWS Support about this.

Lambda

  • Run a "scheduler" lambda function that reads from each and every entry in the scan table and calls RunTask, using the task definition that we created earlier and passing in the right arguments to it.
  • Limitations: we can only launch at most 10 Fargate tasks every 10 seconds, so we might have to implement some kind of backoff strategy. Hope this doesn't run into the lambda time limit if we have too many lambda functions -- we might want to test this out by running like 100 Fargate tasks in a single lambda function to see if we have any issues. -- thoughts @cablej ?
    • Two possible ways around this issue:
      1. Have lambda send tasks to SQS -- each item in SQS will then call a Fargate task.
      2. Use Fargate only for long-running tasks, and use lambda for the other tasks.

Also, an alternative to lambda is EventsBridge, which appears to be AWS's recommended way of scheduling ECS tasks:

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.