Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
9b4f7a3
AWS synchronisation button (#8)
henkberendsen Feb 24, 2023
2d4b50f
Add boto3 and moto dependencies (#11)
1058274 Feb 24, 2023
a237906
Add logger and replace prints with logs
1058274 Feb 26, 2023
02c2243
Add function to create AWS organization
1058274 Feb 26, 2023
6b55b19
Add unit tests for creating AWS organization
1058274 Feb 26, 2023
c01057a
Merge pull request #14 from GiPHouse/create-aws-org
BrentHag Feb 26, 2023
686e9f1
Merge branch 'GiPHouse:main' into development
1058274 Mar 1, 2023
2bb9d9f
Deliverable sprint 1 (#19)
henkberendsen Mar 3, 2023
784c16c
Added logger setlevel (#20)
Jer111 Mar 3, 2023
b28ef9b
Merge branch 'main' into development
henkberendsen Mar 3, 2023
8c83d5d
Updated deliverable sprint 1 (#22)
henkberendsen Mar 3, 2023
baf6f28
Db sync (#16)
Jer111 Mar 14, 2023
65d1048
Db sync (#25)
mitchellboes Mar 14, 2023
c562c69
Added function to generate which users have to be invited after the s…
mitchellboes Mar 14, 2023
2bf3048
Create and attach SCP policies (#29)
1058274 Apr 4, 2023
9661415
12 moto helper (#36)
Jer111 Apr 11, 2023
45e70df
Add checks for edge cases between AWS and Giphouse databases (#37)
mitchellboes Apr 13, 2023
e8086b1
Extraction of AWS data
flam123 Apr 14, 2023
d99c9c2
AWS synchronization pipeline (and integration bug fixes) (#42)
1058274 Apr 22, 2023
5ae9396
Sprint 2 deliverable (#43)
Fiflakos Apr 22, 2023
9f6738f
Dedicated module for AWS helper data structures (#47)
1058274 Apr 25, 2023
0acac89
44 class for handling all aws api calls (#50)
henkberendsen May 9, 2023
d87c288
Merge #41 (check double iteration names, members in correct iteration…
mitchellboes May 9, 2023
a4dfc69
Refactor pipeline preconditions (#54)
1058274 May 10, 2023
f9187c7
Refactor creating course OU and attaching policy (#57)
1058274 May 11, 2023
32d8230
Remove unnecessary moto_client variables in test_awsapitalker.py (#56)
henkberendsen May 12, 2023
54e11da
Refactor generate synchronization list and extract AWS tree
Jer111 May 16, 2023
497abc9
Refactor create and move accounts function
flam123 May 23, 2023
301e711
62 policy id and tag fields on frontend panel (#65)
Jer111 May 30, 2023
4ddf785
Refactor AWSSync pipeline (#67)
Jer111 Jun 2, 2023
df330ad
changes for resolving security (#68)
Fiflakos Jun 6, 2023
f5974ce
Documentation AWS integration feature (#70)
1058274 Jun 6, 2023
5a56803
Finishing touches (#71)
henkberendsen Jun 6, 2023
aaa7c8b
Documentation AWS configuration (#72)
mitchellboes Jun 9, 2023
0c3c746
Merge branch 'main' into development
1058274 Jun 9, 2023
858bc3e
Merge upstream main
1058274 Jun 9, 2023
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
108 changes: 104 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ This is the code for the website of [GiPHouse](http://giphouse.nl/) powered by [

## Table of Contents
- [GiPHouse website](#giphouse-website)
- [Table of Contents](#table-of-contents)
- [Features](#features)
- [Authentication and Users](#authentication-and-users)
- [GitHub OAuth](#github-oauth)
Expand All @@ -16,15 +17,18 @@ This is the code for the website of [GiPHouse](http://giphouse.nl/) powered by [
- [Questionnaires](#questionnaires)
- [Room Reservations](#room-reservations)
- [Course, Project and Static Information](#course-project-and-static-information)
- [Projects and Repositories](#projects-and-repositories)
- [Projects, Repositories and AWS](#projects-repositories-and-aws)
- [GitHub Synchronization](#github-synchronization)
- [AWS Synchronization](#aws-synchronization)
- [Mailing Lists](#mailing-lists)
- [Tasks](#tasks)
- [Styling](#styling)
- [Development and Contributing](#development-and-contributing)
- [Getting Started](#getting-started)
- [Logging into the Backend](#logging-into-the-backend)
- [Registering a GitHub App for repository synchronisation](#registering-a-github-app-for-repository-synchronisation)
- [Registering a G Suite service account for mailing list synchronisation](#registering-a-g-suite-service-account-for-mailing-list-synchronisation)
- [Registering an AWS environment for synchronisation](#registering-an-aws-environment-for-synchronisation)
- [Dependency Management](#dependency-management)
- [Fixtures](#fixtures)
- [Tests](#tests)
Expand All @@ -44,7 +48,7 @@ This is the code for the website of [GiPHouse](http://giphouse.nl/) powered by [
- [`build-docker` job](#build-docker-job)
- [`deploy` job](#deploy-job)
- [Secrets](#secrets)
- [Server](#server)
- [Server Configuration](#server-configuration)
- [Keeping Everything Up to Date](#keeping-everything-up-to-date)

## Features
Expand Down Expand Up @@ -122,10 +126,10 @@ The room reservation is built using [FullCalendar](https://fullcalendar.io/), a
### Course, Project and Static Information
Admin users can add information about the course lectures and the projects in the backend. There are also a small amount of static HTML webpages with information about GiPHouse.

### Projects and Repositories
### Projects, Repositories and AWS
#### GitHub Synchronization
The projects module provides synchronisation functionality with a GitHub organization using the [GitHub API v3](https://developer.github.com/v3/). For this, a repository model is included in Django. Project(team)s can have one or multiple repositories, which are then synchronised with GitHub. For this functionality, a [GitHub App](https://developer.github.com/v3/apps/) must be registered and installed in the organization. Details on this are explained later.

#### GitHub Synchronization
Projects and repositories contain a field `github_team_id` and `github_repo_id` that corresponds to the respective `id` of the object on GitHub. These fields are automatically set and should not be touched under normal circumstances. Teams and repositories on GitHub that do not match one of these id's will not be touched by the GitHub synchronization.
If the `github_team_id` or `github_repo_id` are `None`, it is assumed the objects do not exist and new objects will be created on synchronization (except for archived projects and teams).

Expand All @@ -149,6 +153,72 @@ Synchronization can only be initialized via actions on specific sets of objects

Synchronization currently does not regard the role of directors of GipHouse. This needs to be configured manually. Note that it is however not possible to add directors manually to a team on GitHub, since they will be removed after each sync.

#### AWS Synchronization
The projects module provides synchronisation functionality with [AWS Organizations](https://aws.amazon.com/organizations/) using the official [boto3 Python AWS SDK](https://boto3.amazonaws.com/v1/documentation/api/latest/index.html).
The AWS synchronisation process only applies to the current semester and is one-directional (from GiPHouse to AWS, but not vice versa).

Each project in the current semester with a team mailing list gets its own AWS member account that is part of GiPHouse's AWS organization.
Since all AWS member accounts have isolated environments, each team is able to configure their own AWS environment as desired.
The AWS member accounts are restricted in their abilities using a pre-configured [SCP policy](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps.html) that is applied to the course semester Organizational Unit (OU) where all team member accounts reside.
For example, the SCP policy can be set such that only (certain types of) [EC2](https://aws.amazon.com/ec2/) instances may be launched.
Such specific configuration details can be found under the [Getting Started](#registering-an-aws-environment-for-synchronisation) section.

The entire AWS synchronization process, also referred to as the pipeline, can be initiated in the Django admin interface under Projects by pressing the large `SYNCHRONIZE PROJECTS OF THE CURRENT SEMESTER TO AWS` at the top-right and roughly goes through the following stages:

1. Preliminary checks
- Pipeline preconditions
1. Locatable boto3 credentials and successful AWS API connection
2. Check allowed AWS API actions based on IAM policy of caller
3. Existing organization for AWS API caller
4. AWS API caller acts under same account ID as organization's management account ID
5. SCP policy type feature enabled for organization
- Edge case checks
1. No duplicate course semester OU names
2. Create current course semester OU (if non-existent)
3. Attach SCP policy to current course semester OU (if non-existent)
4. Synchronization
- Determine new accounts to be invited based on AWS and GiPHouse data.
5. Create new AWS member accounts in AWS organization
6. Move new AWS member accounts to course semester OU

![pipeline-flowchart](resources/pipeline-flowchart.drawio.png)

After the synchronization process has finished, a response box is returned indicating success (green), soft-fail (orange) or hard-fail (red).
Verbose details for each synchronization run is logged using the `logging` module and can be accessed in the backend. for example to inspect causes of failed runs.

An example of a possible AWS Organizations environment in the form a tree is the following:
```
base (root/OU)
├── Fall 2022 (OU)
│ ├── team-alice@giphouse.nl (member account)
│ └── team-bob@giphouse.nl (member account)
├── Spring 2023 (OU)
│ ├── team-charlie@giphouse.nl (member account)
│ └── team-david@giphouse.nl (member account)
└── admin@giphouse.nl (management account)
```

The "base" (either root or OU), under which all relevant resources are created and operated on as part of the synchronization process, offers flexibility by being configurable in the Django admin panel.

When an AWS member account has been created for a team mailing list as part of an AWS Organization, an e-mail is sent by AWS.
This process might take some time and is under AWS' control.
It is important to be aware that gaining initial access to the member account is only possible by formally resetting the password; there is no other way.
Also note well that each project team member will receive such mails because the team mailing list works as a one-to-many mail forwarder.

By default, all newly created member accounts under an AWS organization are placed under root.
Once the member accounts have been created under root, they are automatically moved to the current course semester OU.
Note that: (1) it is not possible to create a new member account that gets placed in a specific OU and (2) new requested member accounts can not be moved unless the account creation has been finalized to `SUCCESS` and AWS does not specify an upper bound for the time it takes for a new member account creation to finalize.

Due to point (2), the code contains the variables `ACCOUNT_REQUEST_MAX_ATTEMPTS` for the number of times to check the status of a new member account request, and `ACCOUNT_REQUEST_INTERVAL_SECONDS` for the time to wait in between attempts.
These values are currently hard-coded and can be tweaked, should they cause problems with the synchronization process.

Points (1) and (2) pose the possibility of there being a time period between having a newly created member account under root and moving it to its corresponding OU that is restricted with an attached SCP policy, possibly giving the member account excessive permissions.
To mitigate this risk, every newly created account comes with a pre-defined [tag](https://docs.aws.amazon.com/tag-editor/latest/userguide/tagging.html) and the SCP policy attached to root should deny all permissions for accounts under root with the specific tag (see [Getting Started](#registering-an-aws-environment-for-synchronisation) section for more details on SCP policy and tag configuration).
The tag then automatically gets removed after the account has been moved to its destination course semester OU.

### Mailing Lists
Admin users can create mailing lists using the Django admin interface. A mailing list can be connected to projects, users and 'extra' email addresses that are not tied to a user. Relating a mailing list to a project implicitly makes the members of that project a member of the mailing list. Removing a mailing list in the Django admin will result in the corresponding mailing list to be archived or deleted in G suite during the next synchronization, respecting the 'archive instead of delete' property of the deleted mailing list. To sync a mailing list with G Suite, one can run the management command: `./manage.py sync_mailing_list` or use the button in the model admin. This will sync all mailing lists and the automatic lists into G Suite at the specified domain.

Expand Down Expand Up @@ -214,6 +284,35 @@ To enable the synchronisation feature of mailing lists to G Suite, a project and

The credentials and admin user can then be setup in Github secrets. The email of the G Suite user used to manage to the G Suite domain has to be stored in the Github secret `DJANGO_GSUITE_ADMIN_USER`. The credentials json file has to be `base64` encoded and stored in the Github secret `DJANGO_GSUITE_ADMIN_CREDENTIALS_BASE64` (you can use the linux command `base64` for encoding the json file).

#### Registering an AWS environment for synchronisation
To enable the AWS synchronisation feature, the following points need to be configured only once in advance:

- Create AWS Organizations with all features enabled.
- Ensure Service Control Policies (SCPs) feature is enabled.
- Enable AWS CloudTrail for logging account activity (optional, recommended).
- Increase AWS Organizations quota for maximum number of member accounts to expected amount.
- Default quota is set to 10.
- Expected amount should be at least the number of unique projects in the current semester.
- Set AWS API credentials for `boto3` as environment variables.
- `AWS_ACCESS_KEY_ID`: access key for AWS account.
- `AWS_SECRET_ACCESS_KEY`: secret key for AWS account.
- **(!)** Currently not automated using GitHub secrets.
- AWS API caller has sufficient permissions for all synchronization actions.
- AWS API caller is IAM user acting on behalf of the management account of the AWS Organizations.
- Ensure you are logged in as the management account when creating the IAM user for API access.
- Pre-defined IAM policies `AWSOrganizationsFullAccess` and `IAMFullAccess` are more than sufficient.
- **(!)** Restrictive custom IAM policy adhering to the principle of least privilege is recommended.
- Create SCP policies under AWS Organizations.
- SCP policy restricting member accounts under root with a custom key-value tag.
- Manually attach policy to root.
- SCP policy for course semester OUs (e.g. to only allow EC2 resources of a specific type).
- Automatically attached to course semester OUs.
- Configure a current AWS Policy under `Projects/AWS Policies` in the Django admin panel.
- Set the base ID (root or OU) value.
- Set the SCP policy ID value for course semester OUs.
- Set the restricting custom key-value tag specified in the root SCP policy.
- Mark the `Is current policy` checkbox to make the configuration active.

### Dependency Management
The Python dependencies are managed using a tool called [Poetry](https://python-poetry.org/), which automatically creates virtual environments that ease development and makes it easy to manage the dependencies. See the [Poetry documentation](https://python-poetry.org/docs/) for more information.

Expand Down Expand Up @@ -312,6 +411,7 @@ This repository is public and the GitHub Actions CI runner logs are also public,
The current server is an Amazon Web Services Elastic Cloud Computing (AWS EC2) instance that runs Ubuntu 18.04. EC2 instances have a default `ubuntu` user, that is allowed to execute `sudo` without password. The `docker-compose.yaml` file includes all services that are necessary to run the website in a production environment. That is why Docker is the only dependency on the host.

These steps are the necessary setup for a production server.

1. Add the SSH public keys of engineers to the `authorized_keys` of the `ubuntu` user.
2. Disable SSH password login.
3. Install `docker` and `docker-compose`.
Expand Down
Loading