diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 0000000..4148d77 --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,77 @@ +# Build and Push Docker Image and Helm Chart Github Action + +This Github Action builds a Docker image from a Dockerfile and pushes it to Docker Hub, GitHub Container Registry, and Jfrog Container Registry, and pushes a deployable Helm Chart to Jfrog Artifactory. + +## Usage + +1. Create a `.github/workflows/docker.yml` file in your Github repository. +1. Copy the contents of this action into the `docker.yml` file. +1. For Jfrog Artifactory, login to your instance and + 1. Select `+ Add Repositories` from the `Repositories > Repositories` view. + 1. Create a `Local Repository` that will serve as our Artifactory. + 1. Select `Helm` as the package type. + 1. Set a value for `Repository Key` that will serve as the Artifactory's name. + 1. Select your repository from the `Artifactory > Artifacts` view. + 1. Your Jfrog fully qualified domain name (e.g; `artifactory.yourcompany.io`) will be your URL (`JFROG_URL` as we'll see in the steps below). + 1. Click `+ Generate Token` from the `User Management > Access Tokens` view. + 1. Scope permissions for the access token if desired. + 1. Click `Generate` and save the generated token. This will be your pipeline's access token (`JFROG_ACCESS_TOKEN` as we'll see in the steps below) +1. For Jfrog Container Registry, login to your instance and + 1. Select `+ Add Repositories` from the `Repositories > Repositories` view. + 1. Create a `Local Repository` that will serve as our Container Registry. + 1. Select `Docker` as the package type. + 1. Set a value for `Repository Key`. This will be your repository name (`JFROG_REPOSITORY` as we'll see in the steps below). + 1. Select your repository from the `Artifactory > Artifacts` view. + 1. Click `Set Me Up` and `Generate token and create instructions`. + 1. Save the generated token, this will be your registry password (`JFROG_TOKEN` as we'll see in the steps below). + 1. Your Jfrog email will be your username (`JFROG_USERNAME` as we'll see in the steps below). + 1. Your Jfrog fully qualified domain name (e.g; `artifactory.yourcompany.io`) will be your registry name (`JFROG_REGISTRY` as we'll see in the steps below). +1. For GitHub Container Registry, login to your GitHub account and + 1. Generate a new token from the `Settings > Developer settings > Personal access tokens` view with the following permissions: `read:packages`, `write:packages`, `delete:packages` as well as access to your repo if it is private. + 1. Save the generated token, this will be your registry password (`GH_TOKEN` as we'll see in the steps below). +1. For Docker Container Registry, login to your DockerHub account and + 1. Select `New Access Token` from the `Settings > Security` view with `read`, `write`, `delete` permissions. + 1. Save the generated token, this will be your registry password (`DOCKERHUB_TOKEN` as we'll see in the steps below). + 1. Your DockerHub username will be your username (`DOCKERHUB_USERNAME` as we'll see in the steps below). +1. Create secrets for your Docker Hub username and token, your GitHub token, and your Jfrog username, token, registry, and repository. Add these secrets to your repository's secrets. + +```yaml +secrets: + DOCKERHUB_USERNAME: + DOCKERHUB_TOKEN: + + GH_TOKEN: + + JFROG_REGISTRY: + JFROG_USERNAME: + JFROG_TOKEN: + JFROG_REPOSITORY: + + JFROG_ACCESS_TOKEN: + JFROG_URL: +``` + +4. Modify the tags field in the Build and push step to reflect your desired Docker image name and version. +```yaml +- name: Build and push + uses: docker/build-push-action@v4 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: | + ${{ secrets.DOCKERHUB_USERNAME }}/sammwise:latest + ghcr.io/${{ github.repository_owner }}/sammwise:latest + ${{ secrets.JFROG_REGISTRY }}/${{ secrets.JFROG_REPOSITORY }}/sammwise:latest +``` +## Workflow +1. The workflow is triggered when code is pushed to the main branch, or when it is manually triggered from the Actions tab. +1. The build job is started on an ubuntu-latest runner. +1. The Docker image is built from the Dockerfile located in the ./docker directory. +1. The image is tagged with the specified tags. +1. The Docker image is pushed to Docker Hub and GitHub Container Registry. + +## Acknowledgements +This Github Action was adapted from the [Build and Push Docker Image action by Docker](https://github.com/marketplace/actions/build-and-push-docker-images). + + diff --git a/.github/workflows/action.yml b/.github/workflows/action.yml new file mode 100644 index 0000000..b285430 --- /dev/null +++ b/.github/workflows/action.yml @@ -0,0 +1,88 @@ +# This is a basic workflow to help you get started with Actions + +name: Build and push docker image + +# Controls when the workflow will run +on: + # commented to not be triggered on each commit + # # Triggers the workflow on push or pull request events but only for the "main" branch + push: + branches: ["main"] + # pull_request: + # branches: [ "main" ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "build" + build: + runs-on: ubuntu-latest + # env: + # JF_URL: ${{ secrets.JFROG_URL }} + # JF_ACCESS_TOKEN: ${{ secrets.JFROG_ACCESS_TOKEN }} + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + + # Runs a single command using the runners shell + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + # - name: Login to GitHub Container Registry + # uses: docker/login-action@v2 + # with: + # registry: ghcr.io + # username: ${{ github.repository_owner }} + # password: ${{ secrets.GH_TOKEN }} + # - name: Login to Jfrog Container Registry + # uses: docker/login-action@v2 + # with: + # registry: sju.jfrog.io + # username: ${{ secrets.JFROG_USERNAME }} + # password: ${{ secrets.JFROG_TOKEN }} + + - name: Build and push (pewpew) + uses: docker/build-push-action@v4 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: | + sjultra/pewpew:latest + + - name: Build and push (adapter) + uses: docker/build-push-action@v4 + with: + context: ./docker/adapter + file: ./docker/adapter/Dockerfile + push: true + tags: | + sjultra/pewpew-adapter:latest + + # ghcr.io/${{ github.repository_owner }}/pewpew:latest + # install and authenticate jfrog/artifactory CLI + # - uses: jfrog/setup-jfrog-cli@v3.0.0 + # - name: Verify artifactory server connection + # run: jf rt ping + + # - name: Cleanup artifactory + # run: | + # jf rt del "pewpew-helm/*" + + # - name: Distribute source to artifactory + # run: | + # cd ./helm + # tar -cvzf pewpew.tgz ./service/ + # jf rt upload pewpew.tgz pewpew-helm/ + # cd - diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..40b878d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..9ff85e9 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,22 @@ +--- +version: "1.0" +services: + pewpew: + build: + context: . + dockerfile: ./docker/Dockerfile + volumes: + - ./docker/adapter/data:/opt/pewpew/data + ports: + - 8080:8080 + restart: unless-stopped + # depends_on: + # - "pewpew_data" + + # pewpew_data: + # build: + # context: ./docker/datasource + # dockerfile: ./docker/datasource/Dockerfile + # volumes: + # - ./docker/datasource/data:/datasource/data + # restart: unless-stopped diff --git a/Dockerfile b/docker/Dockerfile similarity index 58% rename from Dockerfile rename to docker/Dockerfile index 25286ad..e560012 100644 --- a/Dockerfile +++ b/docker/Dockerfile @@ -1,8 +1,18 @@ FROM node:latest + EXPOSE 8080 + WORKDIR /opt/pewpew COPY . /opt/pewpew RUN npm install http-server -g RUN mkdir -p /opt/pewpew + +RUN mkdir ./data +VOLUME ./data + +# init external data file +RUN touch ./data/data.json +RUN echo "{\"data\": []}" > ./data/data.json + CMD ["http-server","-a","0.0.0.0","-p","8080","/opt/pewpew"] diff --git a/docker/adapter/Dockerfile b/docker/adapter/Dockerfile new file mode 100644 index 0000000..d52bb20 --- /dev/null +++ b/docker/adapter/Dockerfile @@ -0,0 +1,13 @@ +FROM node:latest + +EXPOSE 8090 +WORKDIR /adapter +COPY . . +RUN npm install --include-dev + +VOLUME ./data +ENV TENANT_ID +ENV CLIENT_ID +ENV CLIENT_SECRET + +CMD ["npm","run","trace"] diff --git a/docker/adapter/data/data.json b/docker/adapter/data/data.json new file mode 100644 index 0000000..70f1e82 --- /dev/null +++ b/docker/adapter/data/data.json @@ -0,0 +1 @@ +{"data":[{"hit":{"origin":{"latitude":37.3083,"longitude":-121.9643},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":37.7506,"longitude":-122.4121},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":37.7506,"longitude":-122.4121},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":37.3083,"longitude":-121.9643},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":37.3083,"longitude":-121.9643},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":37.3083,"longitude":-121.9643},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":37.3083,"longitude":-121.9643},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":37.3083,"longitude":-121.9643},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":30.5875,"longitude":-97.8535},"destination":{"latitude":37.3512,"longitude":-121.8846}}},{"hit":{"origin":{"latitude":37.3083,"longitude":-121.9643},"destination":{"latitude":37.3512,"longitude":-121.8846}}}]} \ No newline at end of file diff --git a/docker/adapter/package-lock.json b/docker/adapter/package-lock.json new file mode 100644 index 0000000..20ef1e6 --- /dev/null +++ b/docker/adapter/package-lock.json @@ -0,0 +1,193 @@ +{ + "name": "pewpew-datasource", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pewpew-datasource", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@types/node": "^20.8.7", + "ts-node": "^10.9.1", + "typescript": "^5.2.2" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==" + }, + "node_modules/@types/node": { + "version": "20.8.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.7.tgz", + "integrity": "sha512-21TKHHh3eUHIi2MloeptJWALuCu5H7HQTdTrWIFReA8ad+aggoX+lRes3ex7/FtpC+sVUpFMQ+QTfYr74mruiQ==", + "dependencies": { + "undici-types": "~5.25.1" + } + }, + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.25.3", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz", + "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "engines": { + "node": ">=6" + } + } + } +} diff --git a/docker/adapter/package.json b/docker/adapter/package.json new file mode 100644 index 0000000..3533ba8 --- /dev/null +++ b/docker/adapter/package.json @@ -0,0 +1,16 @@ +{ + "name": "pewpew-adapter", + "version": "1.0.0", + "description": "An adapter for pewpew to load an external datasource", + "main": "./src/index.ts", + "scripts": { + "trace": "ts-node ./src/index.ts" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@types/node": "^20.8.7", + "ts-node": "^10.9.1", + "typescript": "^5.2.2" + } +} diff --git a/docker/adapter/src/index.ts b/docker/adapter/src/index.ts new file mode 100644 index 0000000..1611a8b --- /dev/null +++ b/docker/adapter/src/index.ts @@ -0,0 +1,151 @@ +import fs from 'fs' + +const tenant_id = process.env.TENANT_ID +const client_id = process.env.CLIENT_ID +const client_secret = process.env.CLIENT_SECRET +const AUTHORITY = `https://login.microsoftonline.com/${tenant_id}` +const GRAPH = `https://graph.microsoft.com` +const TOKEN_URL = `${AUTHORITY}/oauth2/v2.0/token` +const SIGN_INS_URL = `${GRAPH}/v1.0/auditLogs/signIns` +const LOOKUP_IP_URL = 'http://ip-api.com/json' +const LOOKUP_IP_LIMIT = 35 + +const PAYLOAD: Record = { + grant_type: "client_credentials", + client_id: client_id, + client_secret: client_secret, + scope: "https://graph.microsoft.com/.default", +} + +try { + main() +} catch (error: any) { + console.error(error); + +} + +async function main() { + const accessToken = await getAccessToken() + const signIns = await getSignIns(accessToken) + + + const datapoints = await traceSignIns(signIns, false) + + console.log('[WRITE] writing datapoints to disk'); + await writeToDisk({data: datapoints}) + console.log('[WRITE] done.'); + +} + +async function getAccessToken(): Promise { + console.log('Fetching ACCESS_TOKEN...'); + + const response = await fetch(TOKEN_URL, { + method: "POST", + body: Object.keys(PAYLOAD).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(PAYLOAD[key])).join('&'), + headers: { + 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' + }, + }) + + const body: any = await response.json() + console.log('Fetched ACCESS_TOKEN'); + return body["access_token"] + +} + +interface SignIn { + ip: string, + occurredAt: Date +} +async function getSignIns(accessToken: string): Promise { + console.log('Fetching SIGN_INS...'); + const response = await fetch(`${SIGN_INS_URL}?${encodeURIComponent("$filter")}=${encodeURIComponent("createdDateTime ge 2023-10-17T21:00:00Z")}`, { + headers: { + 'Authorization': `Bearer ${accessToken}` + }, + }) + const body: any = await response.json() + console.log('Fetched SIGN_INS'); + return body.value.map(({ ipAddress, createdDateTime }: { [key: string]: any }) => ({ ip: ipAddress, occurredAt: createdDateTime })) +} + +// hit: { + // origin: Coordinates, + // destination: Coordinates + // } + // strokeColor: string + // reason: string +interface Coordinates { + latitude: number + longitude: number +} + +async function lookupIP(ip: string): Promise { + const response = await fetch(`${LOOKUP_IP_URL}/${encodeURIComponent(ip)}`) + const body: any = await response.json() + return { + latitude: body.lat, + longitude: body.lon, + } +} + +async function sleep(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)) +} + +interface Hit { + origin: Coordinates + destination: Coordinates +} + +interface DataPoint { + hit: Hit + strokeColor?: string + reason?: string +} +async function traceSignIns(signIns: SignIn[], smoke: boolean = true): Promise { + console.log('Fetching destination IP info...'); + const destination = await lookupIP("67.180.150.47") + console.log('Fetched destination IP info'); + + let counter = 0 + let iteration = 1 + const origins: DataPoint[] = [] + for (const {ip, occurredAt} of signIns) { + if (counter >= LOOKUP_IP_LIMIT) { + if (smoke) { + console.log("[SMOKE] smoke run completed."); + break + } + console.log("[SLEEP] 62500ms/62.5s"); + await sleep(62500) + counter = 0 + iteration += 1 + } + + if (counter === 0) { + console.log(`Fetching IP info for ${LOOKUP_IP_LIMIT} IP addresses (batch ${iteration}/${Math.ceil(signIns.length/LOOKUP_IP_LIMIT)})`); + + } + + console.log(`Processing ${counter+1}/${LOOKUP_IP_LIMIT}`); + + const origin = await lookupIP(ip) + const datapoint = { + hit: { + origin, + destination + } + } + origins.push(datapoint) + counter += 1 + } + return origins +} + +async function writeToDisk(data: any) { + return new Promise((resolve) => + fs.writeFile('data/data.json', JSON.stringify(data), 'utf8', resolve) + ) +} \ No newline at end of file diff --git a/helm/README.md b/helm/README.md new file mode 100644 index 0000000..d7457cd --- /dev/null +++ b/helm/README.md @@ -0,0 +1,6 @@ +# Helm chart to deploy a pewpew instance + + +``` +helm install pewpew ./service +``` diff --git a/helm/service/Chart.yaml b/helm/service/Chart.yaml new file mode 100644 index 0000000..ce0a9db --- /dev/null +++ b/helm/service/Chart.yaml @@ -0,0 +1,9 @@ +apiVersion: v2 +name: pewpew-chart +description: Pewpew Helm Chart +type: application +version: 0.1.0 +appVersion: "1.0.0" +maintainers: + - email: contact@sjultra.com + name: devopssju diff --git a/helm/service/README.md b/helm/service/README.md new file mode 100644 index 0000000..f86e541 --- /dev/null +++ b/helm/service/README.md @@ -0,0 +1 @@ +# This is a Helm Chart to expose a K8s service for pewpew \ No newline at end of file diff --git a/helm/service/templates/adapter.yaml b/helm/service/templates/adapter.yaml new file mode 100644 index 0000000..b22e99a --- /dev/null +++ b/helm/service/templates/adapter.yaml @@ -0,0 +1,36 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: adapter +spec: + schedule: {{ .Values.adapter.schedule }} + successfulJobsHistoryLimit: 0 + jobTemplate: + spec: + template: + metadata: + labels: + app: adapter + spec: + containers: + - name: adapter + image: "{{ .Values.adapter.image }}:{{ .Values.adapter.tag }}" + + volumeMounts: + - name: {{ .Values.pvc.mount }} + mountPath: /adapter/data + + env: + - name: TENANT_ID + value: {{ .Values.adapter.tenantId }} + - name: CLIENT_ID + value: {{ .Values.adapter.clientId }} + - name: CLIENT_SECRET + value: {{ .Values.adapter.clientSecret }} + + volumes: + - name: {{ .Values.pvc.mount }} + persistentVolumeClaim: + claimName: {{ .Values.pvc.claim }} + + restartPolicy: Never \ No newline at end of file diff --git a/helm/service/templates/pewpew.yaml b/helm/service/templates/pewpew.yaml new file mode 100644 index 0000000..76d0463 --- /dev/null +++ b/helm/service/templates/pewpew.yaml @@ -0,0 +1,39 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: pewpew +spec: + replicas: 1 + selector: + matchLabels: + app: pewpew + template: + metadata: + labels: + app: pewpew + spec: + containers: + - name: pewpew + image: "{{ .Values.pewpew.image }}:{{ .Values.pewpew.tag }}" + + volumeMounts: + - name: {{ .Values.pvc.mount }} + mountPath: /opt/pewpew/data + + volumes: + - name: {{ .Values.pvc.mount }} + persistentVolumeClaim: + claimName: {{ .Values.pvc.claim }} +--- +apiVersion: v1 +kind: Service +metadata: + name: pewpew +spec: + selector: + app: pewpew + ports: + - protocol: TCP + port: {{ .Values.pewpew.httpPort }} + targetPort: {{ .Values.pewpew.targetPort }} + diff --git a/helm/service/templates/pvc.yaml b/helm/service/templates/pvc.yaml new file mode 100644 index 0000000..b4ff4ee --- /dev/null +++ b/helm/service/templates/pvc.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ .Values.pvc.claim }} +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi \ No newline at end of file diff --git a/helm/service/values.yaml b/helm/service/values.yaml new file mode 100644 index 0000000..1730276 --- /dev/null +++ b/helm/service/values.yaml @@ -0,0 +1,22 @@ +# Default values for Pewpew. +# Update these values according to your requirements. +ingressHost: https://kubernetes.default.svc + +pewpew: + image: sjultra/pewpew + tag: latest + httpPort: 80 + targetPort: 8080 + +adapter: + image: sjultra/pewpew-adapter + tag: latest + schedule: '"0 * * * *"' + +pvc: + mount: pewpew-adapter-volume + claim: pewpew-adapter-pvc + name: pewpew-adapter-pv + tenantId: "YOUR-TENANT-ID" + clientId: "YOUR-CLIENT-ID" + clientSecret: "YOUR-CLIENT-SECRET" diff --git a/index.html b/index.html index 4c2ee41..b44b242 100644 --- a/index.html +++ b/index.html @@ -1,114 +1,156 @@ + - - - - - - - - - - - - - + + + + + + + + + + + + + @@ -124,13 +166,16 @@ -
+
+
+
IPew Attack Map
- Creative Commons License + Creative Commons License
@@ -145,40 +190,38 @@

About IPew

- × + × + - + + \ No newline at end of file