From 1a0c46baf899b1128cb5ed4b80a32727670e2c1f Mon Sep 17 00:00:00 2001 From: James Robinson Date: Mon, 2 Oct 2023 10:36:23 +0100 Subject: [PATCH 1/8] :arrow_up: Add missing oauthlib requirement --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index b8b1a4d..13acf3a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,6 +26,7 @@ classifiers = [ ] dependencies = [ "ldaptor~=21.2.0", + "oauthlib~=3.2.2", "Twisted~=23.8.0", "zope.interface~=6.0", ] From 9125378be242940c8f733c372e1118793ce7bd8e Mon Sep 17 00:00:00 2001 From: James Robinson Date: Mon, 2 Oct 2023 10:37:59 +0100 Subject: [PATCH 2/8] :arrow_up: Add missing requests-oauthlib requirement --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 13acf3a..3cdbf16 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,7 @@ classifiers = [ dependencies = [ "ldaptor~=21.2.0", "oauthlib~=3.2.2", + "requests-oauthlib~=1.3.1", "Twisted~=23.8.0", "zope.interface~=6.0", ] From 6b395fe23263370fe1d99d1bd0290abed0892354 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Mon, 2 Oct 2023 15:13:34 +0100 Subject: [PATCH 3/8] :goal_net: Catch initialisation errors caused by incorrect variable setting --- run.py | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/run.py b/run.py index 1d9e7dd..bd586df 100644 --- a/run.py +++ b/run.py @@ -4,21 +4,28 @@ from apricot.oauth import OAuthBackend if __name__ == "__main__": - parser = argparse.ArgumentParser( - prog="Apricot", - description="Apricot is a proxy for delegating LDAP requests to an OpenID Connect backend.", - ) - # Common options needed for all backends - parser.add_argument("-b", "--backend", type=OAuthBackend, help="Which OAuth backend to use.") - parser.add_argument("-d", "--domain", type=str, help="Which domain users belong to.") - parser.add_argument("-p", "--port", type=int, default=8080, help="Port to run on.") - parser.add_argument("-i", "--client-id", type=str, help="OAuth client ID.") - parser.add_argument("-s", "--client-secret", type=str, help="OAuth client secret.") - # Options for Microsoft Entra backend - parser.add_argument("-t", "--entra-tenant-id", type=str, help="Microsoft Entra tenant ID.", required=False) - # Parse arguments - args = parser.parse_args() + try: + parser = argparse.ArgumentParser( + prog="Apricot", + description="Apricot is a proxy for delegating LDAP requests to an OpenID Connect backend.", + ) + # Common options needed for all backends + parser.add_argument("-b", "--backend", type=OAuthBackend, help="Which OAuth backend to use.") + parser.add_argument("-d", "--domain", type=str, help="Which domain users belong to.") + parser.add_argument("-p", "--port", type=int, default=8080, help="Port to run on.") + parser.add_argument("-i", "--client-id", type=str, help="OAuth client ID.") + parser.add_argument("-s", "--client-secret", type=str, help="OAuth client secret.") + # Options for Microsoft Entra backend + group = parser.add_argument_group("Microsoft Entra") + group.add_argument("-t", "--entra-tenant-id", type=str, help="Microsoft Entra tenant ID.", required=False) + # Parse arguments + args = parser.parse_args() - # Create the Apricot server - reactor = ApricotServer(**vars(args)) + # Create the Apricot server + reactor = ApricotServer(**vars(args)) + except Exception: + msg = "Unable to initialise Apricot server from provided command line arguments." + raise ValueError(msg) + + # Run the Apricot server reactor.run() From 1469cea3ad929106ac405a06eed746b2d461cd54 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Mon, 2 Oct 2023 15:38:32 +0100 Subject: [PATCH 4/8] :recycle: Update default port to 1389 --- README.md | 4 ++-- run.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 46e6c99..5de96a5 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,10 @@ The name is a slightly tortured acronym for: LD**A**P **pr**oxy for Open**I**D * ## Usage -Start the `Apricot` server on port 8080 by running: +Start the `Apricot` server on port 1389 by running: ```bash -python run.py --client-id "" --client-secret "" --backend "" --port 8080 --domain "" +python run.py --client-id "" --client-secret "" --backend "" --port 1389 --domain "" ``` This will create an LDAP tree that looks like this: diff --git a/run.py b/run.py index bd586df..466958a 100644 --- a/run.py +++ b/run.py @@ -12,7 +12,7 @@ # Common options needed for all backends parser.add_argument("-b", "--backend", type=OAuthBackend, help="Which OAuth backend to use.") parser.add_argument("-d", "--domain", type=str, help="Which domain users belong to.") - parser.add_argument("-p", "--port", type=int, default=8080, help="Port to run on.") + parser.add_argument("-p", "--port", type=int, default=1389, help="Port to run on.") parser.add_argument("-i", "--client-id", type=str, help="OAuth client ID.") parser.add_argument("-s", "--client-secret", type=str, help="OAuth client secret.") # Options for Microsoft Entra backend From 6edf8c00fc010c4cb317be2ba503e5797d25e6b5 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Mon, 2 Oct 2023 15:17:23 +0100 Subject: [PATCH 5/8] :wrench: Add basic Dockerfile --- README.md | 10 ++++++++++ docker/Dockerfile | 24 ++++++++++++++++++++++++ docker/docker-compose.yaml | 16 ++++++++++++++++ docker/entrypoint.sh | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+) create mode 100644 docker/Dockerfile create mode 100644 docker/docker-compose.yaml create mode 100644 docker/entrypoint.sh diff --git a/README.md b/README.md index 5de96a5..59638f2 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,16 @@ Start the `Apricot` server on port 1389 by running: python run.py --client-id "" --client-secret "" --backend "" --port 1389 --domain "" ``` +Alternatively, you can run in Docker by editing `docker/docker-compose.yaml` and running: + +```bash +docker-compose up +``` + +from the `docker` directory. + +## Outputs + This will create an LDAP tree that looks like this: ```ldif diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..8dc5108 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,24 @@ +FROM python:3.11-alpine + +WORKDIR /app + +RUN apk add --update --no-cache \ + gcc libc-dev libffi-dev + +# Upload and install Python package and dependencies +COPY ../apricot apricot +COPY ../pyproject.toml . +COPY ../README.md . +RUN pip install --upgrade hatch pip +RUN hatch run true + +# Install executable files and set permissions +COPY ../entrypoint.sh . +COPY ../run.py . +RUN chmod ugo+x ./entrypoint.sh + +# Open appropriate ports +EXPOSE 1389 + +# Run the server +ENTRYPOINT ["./entrypoint.sh"] diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml new file mode 100644 index 0000000..734d82b --- /dev/null +++ b/docker/docker-compose.yaml @@ -0,0 +1,16 @@ +--- +version: "3" +services: + apricot: + container_name: apricot + image: apricot + build: . + environment: + BACKEND: "MicrosoftEntra" + CLIENT_ID: "" + DOMAIN: "" + ENTRA_TENANT_ID: "" + ports: + - "1389:1389" + restart: always \ No newline at end of file diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100644 index 0000000..6655fdd --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,38 @@ +#! /bin/sh +# shellcheck disable=SC2086 +# shellcheck disable=SC2089 + +if [ -z "${BACKEND}" ]; then + echo "BACKEND environment variable is not set" + exit 1 +fi + +if [ -z "${CLIENT_ID}" ]; then + echo "CLIENT_ID environment variable is not set" + exit 1 +fi + +if [ -z "${CLIENT_SECRET}" ]; then + echo "CLIENT_SECRET environment variable is not set" + exit 1 +fi + +if [ -z "${DOMAIN}" ]; then + echo "DOMAIN environment variable is not set" + exit 1 +fi + +# Optional arguments +EXTRA_OPTS="" +if [ -n "${ENTRA_TENANT_ID}" ]; then + EXTRA_OPTS="${EXTRA_OPTS} --entra-tenant-id $ENTRA_TENANT_ID" +fi + +# Run the server +hatch run python run.py \ + --backend "$BACKEND" \ + --client-id "$CLIENT_ID" \ + --client-secret "$CLIENT_SECRET" \ + --domain "$DOMAIN" \ + --port 1389 \ + $EXTRA_OPTS From 207419255bf35ac89a0c3b834f998b0155a1fb43 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Mon, 2 Oct 2023 15:29:08 +0100 Subject: [PATCH 6/8] :wrench: Add an image-building CI job --- .github/workflows/publish_docker.yaml | 42 +++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/publish_docker.yaml diff --git a/.github/workflows/publish_docker.yaml b/.github/workflows/publish_docker.yaml new file mode 100644 index 0000000..b51682d --- /dev/null +++ b/.github/workflows/publish_docker.yaml @@ -0,0 +1,42 @@ +name: Build and publish a Docker image + +on: + push: + branches: ["main"] + tags: ["*"] + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-publish-image: + name: Build Docker image and publish to GitHub container repository + runs-on: ubuntu-latest + permissions: + packages: write + contents: read + steps: + - name: Check out the repo + uses: actions/checkout@v3 + + - name: Log in to the Container registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v4 + with: + images: | + ghcr.io/${{ github.repository }} + + - name: Build and publish Docker images + uses: docker/build-push-action@v4 + with: + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file From c8a7b3443f90749ea8445aa8f84ce0337d08c8e7 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Mon, 2 Oct 2023 15:33:11 +0100 Subject: [PATCH 7/8] :rotating_light: Fix YAML linting errors --- .github/workflows/publish_docker.yaml | 6 ++++-- docker/docker-compose.yaml | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish_docker.yaml b/.github/workflows/publish_docker.yaml index b51682d..7a3a61a 100644 --- a/.github/workflows/publish_docker.yaml +++ b/.github/workflows/publish_docker.yaml @@ -1,6 +1,8 @@ +--- name: Build and publish a Docker image -on: +# Run workflow on pushes to matching branches +on: # yamllint disable-line rule:truthy push: branches: ["main"] tags: ["*"] @@ -39,4 +41,4 @@ jobs: with: push: true tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file + labels: ${{ steps.meta.outputs.labels }} diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 734d82b..2d13d7a 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -13,4 +13,4 @@ services: ENTRA_TENANT_ID: "" ports: - "1389:1389" - restart: always \ No newline at end of file + restart: always From 81354279f7414b969c73f01427988d40448cada4 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Mon, 2 Oct 2023 15:34:01 +0100 Subject: [PATCH 8/8] :bug: Build docker image from top-level directory --- docker/Dockerfile => Dockerfile | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) rename docker/Dockerfile => Dockerfile (71%) diff --git a/docker/Dockerfile b/Dockerfile similarity index 71% rename from docker/Dockerfile rename to Dockerfile index 8dc5108..0033914 100644 --- a/docker/Dockerfile +++ b/Dockerfile @@ -6,15 +6,16 @@ RUN apk add --update --no-cache \ gcc libc-dev libffi-dev # Upload and install Python package and dependencies -COPY ../apricot apricot -COPY ../pyproject.toml . -COPY ../README.md . +COPY ./apricot apricot +COPY ./pyproject.toml . +COPY ./README.md . RUN pip install --upgrade hatch pip +# Initialise environment with hatch RUN hatch run true # Install executable files and set permissions -COPY ../entrypoint.sh . -COPY ../run.py . +COPY ./docker/entrypoint.sh . +COPY ./run.py . RUN chmod ugo+x ./entrypoint.sh # Open appropriate ports