Skip to content

Commit

Permalink
Merge pull request #133 from mitodl/jkachel/remove-traefik-support
Browse files Browse the repository at this point in the history
Remove Traefik support
  • Loading branch information
jkachel authored Sep 24, 2024
2 parents 0f5b8df + f17079c commit 22cd50f
Show file tree
Hide file tree
Showing 23 changed files with 234 additions and 843 deletions.
30 changes: 7 additions & 23 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ MAILGUN_KEY=
MAILGUN_URL=
MAILGUN_RECIPIENT_OVERRIDE=
MAILGUN_SENDER_DOMAIN=
MAILGUN_FROM_EMAIL=

MITOL_UE_BCC_EMAIL=
MITOL_UE_FROM_EMAIL=donotreply@ue.odl.local

MITOL_UE_BASE_URL=http://ue.odl.local:8073
MITOL_UE_COOKIE_NAME=mitolue
Expand All @@ -24,15 +26,6 @@ MITOL_UE_HOSTNAME=ue.odl.local
MITOL_UE_JWT_SECRET=
MITOL_UE_USE_S3=False

SOCIAL_AUTH_OL_OIDC_OIDC_ENDPOINT=
SOCIAL_AUTH_OL_OIDC_KEY=
SOCIAL_AUTH_OL_OIDC_SECRET=
AUTHORIZATION_URL=
ACCESS_TOKEN_URL=
USERINFO_URL=
KEYCLOAK_BASE_URL=
KEYCLOAK_REALM_NAME=

POSTHOG_API_HOST=https://app.posthog.com
POSTHOG_API_TOKEN=

Expand All @@ -46,16 +39,7 @@ MITOL_PAYMENT_GATEWAY_CYBERSOURCE_MERCHANT_ID=sample-setting
MITOL_PAYMENT_GATEWAY_CYBERSOURCE_MERCHANT_SECRET=sample-setting
MITOL_PAYMENT_GATEWAY_CYBERSOURCE_MERCHANT_SECRET_KEY_ID=sample-setting

KEYCLOAK_REALM=

KEYCLOAK_ADMIN_CLIENT_ID=
KEYCLOAK_ADMIN_CLIENT_SECRET=
KEYCLOAK_ADMIN_URL=
KEYCLOAK_ADMIN_SECURE=False

KEYCLOAK_CLIENT_ID=
KEYCLOAK_CLIENT_SECRET=

TRAEFIK_FORWARD_AUTH_ADMIN_URL=
TRAEFIK_ADMIN_PORT=8081
TRAEFIK_PORT=80
KEYCLOAK_SVC_ADMIN=
KEYCLOAK_SVC_ADMIN_PASSWORD=
KEYCLOAK_SVC_HOSTNAME=
KEYCLOAK_SVC_KEYSTORE_PASSWORD=
12 changes: 8 additions & 4 deletions README-keycloak.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Keycloak Integration

The APISIX Compose file includes a Keycloak instance that you can use for authentication instead of spinning up a separate one or using one of the deployed instances. It's not enabled by default, but you can run it if you prefer not to run your own Keycloak instance.
The Compose file includes a Keycloak instance that you can use for authentication instead of spinning up a separate one or using one of the deployed instances. It's not enabled by default, but you can run it if you prefer not to run your own Keycloak instance.

## Default Settings

Expand All @@ -22,15 +22,19 @@ The client it sets up is called `apisix`. You can change the passwords and get t

## Making it Work

The Keycloak instance is hidden in the `keycloak` profile in the Composer file, so if you want to interact with it, you'll need to run `docker compose --profile keycloak`. (If you start the app without the profile, you can still start Keycloak later by specifying the profile.)

If you want to use the Keycloak instance, follow these steps:

1. Start the stack normally. The `db` container needs to be up and running, at least.
1. In `config/keycloak/tls`, copy `tls.crt.default` and `tls.key.default` to `tls.crt` and `tls.key`. (Or, you can regenerate them - see the README in that folder.)
2. Create a database called `keycloak`. For example: `docker compose -f docker-compose-apisix.yml run --rm -ti db psql -h db -U postgres -c 'create database keycloak;'` (then enter the default password of `postgres` when it asks)
3. Optionally add `KEYCLOAK_SVC_HOSTNAME`, `KEYCLOAK_SVC_ADMIN`, and `KEYCLOAK_SVC_ADMIN_PASSWORD` to your `.env` file.
2. Create a database called `keycloak`. For example: `docker compose --profile keycloak run --rm -ti db psql -h db -U postgres -c 'create database keycloak;'` (then enter the default password of `postgres` when it asks)
3. Add a keystore password to your `.env` file. This should be set in `KEYCLOAK_SVC_KEYSTORE_PASSWORD`. This is required, but the password need not be anything special.
4. Optionally add `KEYCLOAK_SVC_HOSTNAME`, `KEYCLOAK_SVC_ADMIN`, and `KEYCLOAK_SVC_ADMIN_PASSWORD` to your `.env` file.
1. `KEYCLOAK_SVC_HOSTNAME` is the hostname you want to use for the instance - the default is `kc.odl.local`.
2. `KEYCLOAK_SVC_ADMIN` is the admin username. The default is `admin`.
3. `KEYCLOAK_SVC_ADMIN_PASSWORD` is the admin password. The default is `admin`.
4. Start the Keycloak service: `docker compose -f docker-compose-apisix.yml --profile keycloak up -d keycloak`
5. Start the Keycloak service: `docker compose --profile keycloak up -d keycloak`

The Keycloak container should start and stay running. Once it does, you should be able to log in at `https://kc.odl.local:7443/` with username and password `admin` (or the values you supplied).

Expand Down
117 changes: 26 additions & 91 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ This application provides a central system to handle ecommerce activities across
- [Configure required `.env` settings](#configure-required-env-settings)
- [Loading and Accessing Data](#loading-and-accessing-data)
- [Run with API Gateway](#run-with-api-gateway)
- [Traefik](#traefik)
- [Others (APISIX)](#others-apisix)
- [API Access](#api-access)
- [Code Generation](#code-generation)
- [Committing \& Formatting](#committing--formatting)
- [Optional Setup](#optional-setup)
Expand Down Expand Up @@ -40,51 +39,11 @@ The following settings must be configured before running the app:

Sets the Django secret for the application. This just needs to be a random string.

If you're going to use the included Traefik Composer environment, also set these:

- `KEYCLOAK_ADMIN_URL`

Sets the base URL for the Keycloak instance. Do not append a trailing slash.

- `KEYCLOAK_ADMIN_CLIENT_ID`

Sets the client ID for the service account. (This should be in the `master` realm.)

- `KEYCLOAK_ADMIN_CLIENT_SECRET`

Sets the client secret for the service account in the `master` realm.

- `KEYCLOAK_ADMIN_SECURE`

Check the Keycloak instance's certificates for validity. Defaults to True - set to False when running locally.

- `KEYCLOAK_REALM`

Sets the realm that the forward auth should use for authentication.

- `KEYCLOAK_CLIENT_ID`

Sets the client ID for authentication within the realm by the forward auth.

- `KEYCLOAK_CLIENT_SECRET`

Sets the client secret for authentication within the realm by the forward auth.

- `TRAEFIK_FORWARD_AUTH_ADMIN_URL`

Sets base URI for the provider - this should be the _plain HTTP_ version of `KEYCLOAK_ADMIN_URL`.

- `TRAEFIK_PORT`

Sets the port that Traefik uses for incoming requests. (Usually, this is port 80.)

- `TRAEFIK_ADMIN_PORT`

Sets the port that Traefik's admin dashboard lives on. (8081 is a good choice.)

### Loading and Accessing Data

You'll need an integrated system and product for that system to be able to do much of anything. Once you've done initial setup, run these commands:
You'll need an integrated system and product for that system to be able to do much of anything. A management command exists to create the test data: `create_test_data`. This will create a system and add some products with random (but usable) prices in it.

Alternatively, you can create them manually:

* Create an integrated system: `./manage.py add_system <name> -d <description> -s <slug`
* Create a product: `./manage.py manage_product add -s <system slug> --sku <an SKU> --name <name> --description <description> --price <price>`
Expand All @@ -93,65 +52,31 @@ The `add_system` command will generate an API key for the system's use. You can

> Alternatively, you can create these records through the Django Admin, but be advised that it won't create the API key for you. The management command uses a UUID for the key but any value will do, as long as it's unique.
You can interact with the API directly through the Swagger interface: `<root url>/api/schema/swagger-ui/`

The system also exposes a Redoc version of the API at `<root url>/api/schema/redoc/`

Navigating to an API endpoint in the browser should also get you the normal DRF interface as well.

> Most API endpoints will require a session of some kind. See Run with API Gateway below for more info. But, the integrated systems and products APIs are anoymous read-only, so you should be able to get something out of them without having the API gateway set up.
### Run with API Gateway

The app depends on an outside system to provide the authentication layer for itself. This outside system is an API gateway. We've specifically tested two: Traefik Proxy and APISIX.

#### Traefik

If you want to run the system behind a Traefik install, there is a separate Compose file that will start the app with Traefik for you. Use the `docker-compose-traefik.yml` file for this purpose. This will expose the service on port 80, and you should be able to get to the Traefik dashboard at port 8080. The Compose file is set up with the forward auth support necessary to talk to Keycloak. Make sure you've included the extra `.env` settings mentioned above.

The Traefik forward auth will need a client to authenticate users with. Create this client in the realm you want to use for user authentication. The steps to do this are:

1. Log into Keycloak Admin and navigate to the realm you want to use for authentication.
2. Go to Clients, then click Create client.
3. Fill out the form.
1. The `Client ID` can be any valid string - a good choice is `traefik-client`. Save this in your `.env` as `KEYCLOAK_CLIENT_ID`.
2. For local testing, it's OK to use `*` for both `Valid redirect URIs` and `Web origins`. This is not OK for anything attached to the Internet.
3. Make sure `Client authentication` is on, and `Standard flow` and `Implicit flow` are checked.
4. After you've saved the client, go to Credentials and copy out the `Client secret`. (You may need to manually cut and paste; the copy to clipboard button has never worked for me.) Set this in your `.env` as `KEYCLOAK_CLIENT_SECRET`.

Additionally, you'll need to create a service account for the application in Keycloak, as the app will need to call Keycloak directly to get user information. To do this:

1. Create a Client in the master realm that has "Client authentication" turned on, which should also allow you to turn on "Service account roles". (You can follow the same steps above for this; just make sure "Service account roles" is turned on.)
2. After saving, go to the Advanced tab and turn on "Use refresh tokens" and "Use refresh tokens for client credentials grant" under Open ID Connect Compatibility Modes. Save there (it has its own button).
3. Under Service Accounts Roles, click Assign Role and assign everything.

Then, specify the _client ID and secret_ as the `KEYCLOAK_ADMIN_CLIENT_ID` and `KEYCLOAK_ADMIN_CLIENT_SECRET` in the `.env` file.

Set `KEYCLOAK_ADMIN_URL` to the base HTTPS URL for the Keycloak instance. This does need to be the HTTPS one. For local development, you'll also want to set `KEYCLOAK_ADMIN_SECURE` to `False` so that calls to Keycloak are done without checking the SSL certificates. (This defaults to True.)
As noted, you'll need to set up APISIX as the API gateway for this. The app comes with one and you'll need to set this up before you can access the app.

Finally, you will need a `TRAEFIK_FORWARD_AUTH_ADMIN_URL` set up in your `.env` file - this should be the same as `KEYCLOAK_ADMIN_URL` but is separated out so you can use plain HTTP for this. The forward auth handler for Traefik can't be set to ignore invalid (self-signed) SSL certificates, so it will fail to start if you're running Keycloak without a real certificate. (The Django OAuth2 code _can_ be set to be OK with invalid certs, hence the split.)
> [!WARNING]
> The APISIX configuration is not acceptable for production use.
If you don't want to use the pack-in Traefik instance, you can attach the necessary tags to the `nginx` container by running the regular `docker-compose.yml` _with_ the `docker-compose-traefik-override.yml` file. You'll also likely want to create a `docker-compose-override.yml` to put the `nginx` container in the same network as your existing Traefik instance. You'll need to set up the forward auth as appropriate for your instance.
You'll need to define routes for APISIX before it will handle traffic for the appplication. These are defined using the API as some of the settings are instance-specific. Here are the steps to accomplish that:

#### Others (APISIX)

The system is also set up to run using APISIX as the gateway (to an extent). Use the `docker-compose-apisix.yml` file to spin up the application with APISIX and etcd. The APISIX configuration is not ready for production use - it's only here for assist in local testing and development.

You'll need to define routes for APISIX before it will handle traffic for the appplication. Here are the steps to accomplish that:
> The shell script below is also at `scripts/bootstrap_apisix.sh`. Set the variables listed below and run it to set up your routes.
1. In your Keycloak instance, create a new Client in the realm you are going to use for UE.
1. The `Client ID` can be any valid string - a good choice is `apisix-client`. Set this in your shell as `CLIENT_ID`.
1. For local testing, it's OK to use `*` for both `Valid redirect URIs` and `Web origins`. This is not OK for anything attached to the Internet.
2. Make sure `Client authentication` is on, and `Standard flow` and `Implicit flow` are checked.
3. After you've saved the client, go to Credentials and copy out the `Client secret`. (You may need to manually cut and paste; the copy to clipboard button has never worked for me.) Set this in your shell as `CLIENT_SECRET`.
2. For local testing, it's OK to use `*` for both `Valid redirect URIs` and `Web origins`. This is not OK for anything attached to the Internet.
3. Make sure `Client authentication` is on, and `Standard flow` and `Implicit flow` are checked.
4. After you've saved the client, go to Credentials and copy out the `Client secret`. (You may need to manually cut and paste; the copy to clipboard button has never worked for me.) Set this in your shell as `CLIENT_SECRET`.
2. Set the realm you're using in your shell as `OIDC_REALM`.
3. In your Keycloak Realm Settings, you should be able to find the OpenID Endpoint Configuration link. Copy/paste this somewhere - you'll need it later. Set this in your shell as `DISCOVERY_URL`.
4. From the `config/apisix/apisix.yml` file, get the `key` out. This should be on line 11. You can also reset it here if you wish. Set this as `API_KEY`.
5. Start the entire thing: `docker compose -f docker-compose-apisix.yml up`. This will bring up Universal Ecommerce and the APISIX instance.
5. Start the entire thing: `docker compose up`. This will bring up Universal Ecommerce and the APISIX instance.
6. Create an all-encompassing route for UE in APISIX. This uses the APISIX API - be sure to read this through before running it and fill out placeholders.
```bash
# Set variables - skip if you were doing this in each step above

APISIX_ROOT=<root location for APISIX - no trailing slash>
API_KEY=<api key>
OIDC_REALM=<your Keycloak realm>
CLIENT_ID=<your client ID>
Expand All @@ -175,7 +100,7 @@ curl "http://127.0.0.1:9180/apisix/admin/upstreams/2" \

postbody=$(cat << ROUTE_END
{
"uris": [ "/checkout/result/", "/static" ],
"uris": [ "/checkout/result/", "/static/*", "/api/schema/*" ],
"plugins": {},
"upstream_id": 2,
"priority": 0,
Expand Down Expand Up @@ -211,11 +136,21 @@ postbody=$(cat << ROUTE_END
ROUTE_END
)

curl http://127.0.0.1:9180/apisix/admin/routes/ue-default -H "X-API-KEY: $API_KEY" -X PUT -d "$postbody"
curl http://127.0.0.1:9180/apisix/admin/routes/ue -H "X-API-KEY: $API_KEY" -X PUT -d ${postbody}
```

You should now be able to get to the app via APISIX. There is an internal API at `http://ue.odl.local:9080/_/v0/meta/apisix_test_request/` that you can hit to see if it worked. The wildcard route above will route all UE traffic (or, more correctly, all traffic going into APISIX) through Keycloak and then into UE, so you should also be able to access the Django Admin through it if you've set your Keycloak user to be an admin.

### API Access

You can interact with the API directly through the Swagger interface: `<root url>/api/schema/swagger-ui/`

The system also exposes a Redoc version of the API at `<root url>/api/schema/redoc/`

Navigating to an API endpoint in the browser should also get you the normal DRF interface as well.

> Most API endpoints require authentication, so you won't be able to get a lot of these to work without the API Gateway in place.
## Code Generation

Unified Ecommerce uses [drf-spectacular](https://drf-spectacular.readthedocs.io/en/latest/) to generate an OpenAPI spec from Django views. Additionally, we use [OpenAPITools/openapi-generator](https://github.com/OpenAPITools/openapi-generator) to generate Typescript declarations and an API Client. These generated files are checked into source control; CI checks that they are up-to-date. To regenerate these files, run
Expand Down
Empty file removed authentication/__init__.py
Empty file.
8 changes: 0 additions & 8 deletions authentication/admin.py

This file was deleted.

Loading

0 comments on commit 22cd50f

Please sign in to comment.