From da959b15e2a3576057af1f415bf13cd795118c2e Mon Sep 17 00:00:00 2001 From: Diego <38155922+Diegomcha@users.noreply.github.com> Date: Sun, 2 Mar 2025 14:52:04 +0100 Subject: [PATCH] =?UTF-8?q?fix(llm):=20=F0=9F=94=90=20fixed=20llm=20key=20?= =?UTF-8?q?leak?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a979665d0b764e65f31902acaf0d91a57a75b935 Merge: e60b874 85f9a59 Author: Pablo González Date: Mon Feb 24 12:55:06 2025 +0100 Merge pull request #45 from Arquisoft/fix_llmkey_exposure_v2 Fix llmkey exposure v2 commit 85f9a59b3eee9fa90babfae13c13fe3d1dbd50c9 Author: pglez82 Date: Mon Feb 24 12:28:16 2025 +0100 updating docker compose commit 70ae7edb9a21beff108e3fef143008e2f1a08136 Author: pglez82 Date: Mon Feb 24 11:09:36 2025 +0100 modifying readme commit e5dd60489357cf55cef445c6cc810c745ac206fb Author: pglez82 Date: Mon Feb 24 11:01:33 2025 +0100 removing api key from docker image commit e60b8741ad49e3d3d2e291a42a9e9c35d791fa9f Author: pglez82 Date: Thu Feb 20 11:05:29 2025 +0100 checking change commit 294aaa36799fc78ee3dbb16b80e7a530b633aabc Merge: 25a54d1 b9e7e1b Author: Pablo González Date: Thu Feb 20 11:04:55 2025 +0100 Merge pull request #30 from Arquisoft/fix_llmkey_exposure Fix llmkey exposure commit b9e7e1b4890d7e5441a87fa7b79bf711bb51fb32 Author: pglez82 Date: Mon Feb 17 23:37:11 2025 +0100 modify readme commit d5278339ad2296d5bd937d613d378bfbad6f097a Author: pglez82 Date: Mon Feb 17 22:59:20 2025 +0100 fixing tests commit 7f08852a283895ed0612a3c43c4371cde21de33a Author: pglez82 Date: Mon Feb 17 22:43:47 2025 +0100 fixing api key exposure commit 25a54d1ba55b6b7ad2737d65478781d58a1f7f74 Author: Pablo González Date: Mon Feb 17 19:46:15 2025 +0100 Update Dockerfile commit 16d79276cd03f636ad7d6012a9d24268fe1facf8 Author: Pablo González Date: Mon Feb 17 19:12:41 2025 +0100 Update Dockerfile commit 03f29baf2f04647eb0a3631c8ab3f3c76ac13da5 Author: Pablo González Date: Mon Feb 17 19:09:04 2025 +0100 Update Dockerfile commit df799eca459dcff937591821b89c3f0f9a3eda84 Author: pglez82 Date: Fri Feb 7 12:44:19 2025 +0100 add code to deploy commit 54babf4825acd57ece3ca8120bca0277e9a55dfc Author: pglez82 Date: Fri Feb 7 12:31:03 2025 +0100 updating api endpoint Signed-off-by: Diego <38155922+Diegomcha@users.noreply.github.com> --- .github/workflows/release.yml | 178 +++++++++++++++++---------------- .vscode/settings.json | 3 + README.md | 7 +- docker-compose.yml | 10 +- llmservice/.dockerignore | 3 +- llmservice/llm-service.js | 13 ++- llmservice/llm-service.test.js | 5 +- llmservice/package-lock.json | 13 +++ llmservice/package.json | 17 ++-- webapp/Dockerfile | 4 +- webapp/src/App.js | 2 +- webapp/src/components/Login.js | 12 +-- 12 files changed, 146 insertions(+), 121 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2a7733979..bcbdb57af 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,39 +8,39 @@ jobs: unit-tests: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: 22 - - run: npm --prefix users/authservice ci - - run: npm --prefix users/userservice ci - - run: npm --prefix llmservice ci - - run: npm --prefix gatewayservice ci - - run: npm --prefix webapp ci - - run: npm --prefix users/authservice test -- --coverage - - run: npm --prefix users/userservice test -- --coverage - - run: npm --prefix llmservice test -- --coverage - - run: npm --prefix gatewayservice test -- --coverage - - run: npm --prefix webapp test -- --coverage - - name: Analyze with SonarCloud - uses: SonarSource/sonarqube-scan-action@master - env: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + - run: npm --prefix users/authservice ci + - run: npm --prefix users/userservice ci + - run: npm --prefix llmservice ci + - run: npm --prefix gatewayservice ci + - run: npm --prefix webapp ci + - run: npm --prefix users/authservice test -- --coverage + - run: npm --prefix users/userservice test -- --coverage + - run: npm --prefix llmservice test -- --coverage + - run: npm --prefix gatewayservice test -- --coverage + - run: npm --prefix webapp test -- --coverage + - name: Analyze with SonarCloud + uses: SonarSource/sonarqube-scan-action@master + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} e2e-tests: needs: [unit-tests] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: 22 - - run: npm --prefix users/authservice install - - run: npm --prefix users/userservice install - - run: npm --prefix llmservice install - - run: npm --prefix gatewayservice install - - run: npm --prefix webapp install - - run: npm --prefix webapp run build - - run: npm --prefix webapp run test:e2e + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + - run: npm --prefix users/authservice install + - run: npm --prefix users/userservice install + - run: npm --prefix llmservice install + - run: npm --prefix gatewayservice install + - run: npm --prefix webapp install + - run: npm --prefix webapp run build + - run: npm --prefix webapp run test:e2e docker-push-webapp: name: Push webapp Docker Image to GitHub Packages runs-on: ubuntu-latest @@ -49,19 +49,18 @@ jobs: packages: write needs: [e2e-tests] steps: - - uses: actions/checkout@v4 - - name: Publish to Registry - uses: elgohr/Publish-Docker-Github-Action@v5 - env: - API_URI: http://${{ secrets.DEPLOY_HOST }}:8000 - LLM_API_KEY: ${{ secrets.LLM_API_KEY }} - with: + - uses: actions/checkout@v4 + - name: Publish to Registry + uses: elgohr/Publish-Docker-Github-Action@v5 + env: + API_URI: http://${{ secrets.DEPLOY_HOST }}:8000 + with: name: arquisoft/wichat_en1c/webapp username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} registry: ghcr.io workdir: webapp - buildargs: API_URI,LLM_API_KEY + buildargs: API_URI docker-push-authservice: name: Push auth service Docker Image to GitHub Packages runs-on: ubuntu-latest @@ -70,19 +69,19 @@ jobs: packages: write needs: [e2e-tests] steps: - - uses: actions/checkout@v4 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: Publish to Registry - uses: elgohr/Publish-Docker-Github-Action@v5 - with: + - uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Publish to Registry + uses: elgohr/Publish-Docker-Github-Action@v5 + with: name: arquisoft/wichat_en1c/authservice username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} registry: ghcr.io workdir: users/authservice platforms: linux/amd64,linux/arm64 - + docker-push-userservice: name: Push user service Docker Image to GitHub Packages runs-on: ubuntu-latest @@ -91,15 +90,15 @@ jobs: packages: write needs: [e2e-tests] steps: - - uses: actions/checkout@v4 - - name: Publish to Registry - uses: elgohr/Publish-Docker-Github-Action@v5 - with: - name: arquisoft/wichat_en1c/userservice - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - registry: ghcr.io - workdir: users/userservice + - uses: actions/checkout@v4 + - name: Publish to Registry + uses: elgohr/Publish-Docker-Github-Action@v5 + with: + name: arquisoft/wichat_en1c/userservice + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + registry: ghcr.io + workdir: users/userservice docker-push-llmservice: name: Push llm service Docker Image to GitHub Packages @@ -109,15 +108,15 @@ jobs: packages: write needs: [e2e-tests] steps: - - uses: actions/checkout@v4 - - name: Publish to Registry - uses: elgohr/Publish-Docker-Github-Action@v5 - with: - name: arquisoft/wichat_en1c/llmservice - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - registry: ghcr.io - workdir: llmservice + - uses: actions/checkout@v4 + - name: Publish to Registry + uses: elgohr/Publish-Docker-Github-Action@v5 + with: + name: arquisoft/wichat_en1c/llmservice + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + registry: ghcr.io + workdir: llmservice docker-push-gatewayservice: name: Push gateway service Docker Image to GitHub Packages @@ -127,31 +126,38 @@ jobs: packages: write needs: [e2e-tests] steps: - - uses: actions/checkout@v4 - - name: Update OpenAPI configuration - run: | - DEPLOY_HOST=${{ secrets.DEPLOY_HOST }} - sed -i "s/SOMEIP/${DEPLOY_HOST}/g" gatewayservice/openapi.yaml - - name: Publish to Registry - uses: elgohr/Publish-Docker-Github-Action@v5 - with: - name: arquisoft/wichat_en1c/gatewayservice - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - registry: ghcr.io - workdir: gatewayservice + - uses: actions/checkout@v4 + - name: Update OpenAPI configuration + run: | + DEPLOY_HOST=${{ secrets.DEPLOY_HOST }} + sed -i "s/SOMEIP/${DEPLOY_HOST}/g" gatewayservice/openapi.yaml + - name: Publish to Registry + uses: elgohr/Publish-Docker-Github-Action@v5 + with: + name: arquisoft/wichat_en1c/gatewayservice + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + registry: ghcr.io + workdir: gatewayservice deploy: name: Deploy over SSH runs-on: ubuntu-latest - needs: [docker-push-userservice,docker-push-authservice,docker-push-llmservice,docker-push-gatewayservice,docker-push-webapp] + needs: + [ + docker-push-userservice, + docker-push-authservice, + docker-push-llmservice, + docker-push-gatewayservice, + docker-push-webapp, + ] steps: - - name: Deploy over SSH - uses: fifsky/ssh-action@master - with: - host: ${{ secrets.DEPLOY_HOST }} - user: ${{ secrets.DEPLOY_USER }} - key: ${{ secrets.DEPLOY_KEY }} - command: | - wget https://raw.githubusercontent.com/arquisoft/wichat_en1c/master/docker-compose.yml -O docker-compose.yml - docker compose --profile prod down - docker compose --profile prod up -d --pull always + - name: Deploy over SSH + uses: fifsky/ssh-action@master + with: + host: ${{ secrets.DEPLOY_HOST }} + user: ${{ secrets.DEPLOY_USER }} + key: ${{ secrets.DEPLOY_KEY }} + command: | + wget https://raw.githubusercontent.com/arquisoft/wichat_en1c/master/docker-compose.yml -O docker-compose.yml + docker compose --profile prod down + docker compose --profile prod up -d --pull always diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..6b0e5abfc --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "postman.settings.dotenv-detection-notification-visibility": false +} \ No newline at end of file diff --git a/README.md b/README.md index 575b722d1..e3a7c221e 100644 --- a/README.md +++ b/README.md @@ -37,9 +37,9 @@ First, clone the project: In order to communicate with the LLM integrated in this project, we need to setup an API key. Two integrations are available in this propotipe: gemini and empaphy. The API key provided must match the LLM provider used. We need to create two .env files. -- The first one in the webapp directory (for executing the webapp using ```npm start```). The content of this .env file should be as follows: +- The first one in the llmservice directory (for executing the llmservice using ```npm start```). The content of this .env file should be as follows: ``` -REACT_APP_LLM_API_KEY="YOUR-API-KEY" +LLM_API_KEY="YOUR-API-KEY" ``` - The second one located in the root of the project (along the docker-compose.yml). This .env file is used for the docker-compose when launching the app with docker. The content of this .env file should be as follows: ``` @@ -48,8 +48,7 @@ LLM_API_KEY="YOUR-API-KEY" Note that these files must NOT be uploaded to the github repository (they are excluded in the .gitignore). -An extra configuration for the LLM to work in the deployed version of the app is to include it as a repository secret (LLM_API_KEY). This secret will be used by GitHub Action when building and deploying the application. - +An extra configuration for the LLM to work in the deployed version of the app is to create the same .env file (with the LLM_API_KEY variable) in the virtual machine (in the home of the azureuser directory). ### Launching Using docker For launching the propotipe using docker compose, just type: diff --git a/docker-compose.yml b/docker-compose.yml index 88ebb7756..90af88a5a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -42,7 +42,10 @@ services: container_name: llmservice-wichat_en1c image: ghcr.io/arquisoft/wichat_en1c/llmservice:latest profiles: ["dev", "prod"] - build: ./llmservice + env_file: + - .env + build: + context: ./llmservice ports: - "8003:8003" networks: @@ -71,10 +74,7 @@ services: container_name: webapp-wichat_en1c image: ghcr.io/arquisoft/wichat_en1c/webapp:latest profiles: ["dev", "prod"] - build: - context: ./webapp - args: - LLM_API_KEY: ${LLM_API_KEY} + build: ./webapp depends_on: - gatewayservice ports: diff --git a/llmservice/.dockerignore b/llmservice/.dockerignore index 3091757a3..c92776b4b 100644 --- a/llmservice/.dockerignore +++ b/llmservice/.dockerignore @@ -1,2 +1,3 @@ node_modules -coverage \ No newline at end of file +coverage +.env \ No newline at end of file diff --git a/llmservice/llm-service.js b/llmservice/llm-service.js index 63fd34ef4..e4bdb3d38 100644 --- a/llmservice/llm-service.js +++ b/llmservice/llm-service.js @@ -6,6 +6,8 @@ const port = 8003; // Middleware to parse JSON in request body app.use(express.json()); +// Load enviornment variables +require('dotenv').config(); // Define configurations for different LLM APIs const llmConfigs = { @@ -19,7 +21,7 @@ const llmConfigs = { empathy: { url: () => 'https://empathyai.prod.empathy.co/v1/chat/completions', transformRequest: (question) => ({ - model: "qwen/Qwen2.5-Coder-7B-Instruct", + model: "mistralai/Mistral-7B-Instruct-v0.3", messages: [ { role: "system", content: "You are a helpful assistant." }, { role: "user", content: question } @@ -71,9 +73,14 @@ async function sendQuestionToLLM(question, apiKey, model = 'gemini') { app.post('/ask', async (req, res) => { try { // Check if required fields are present in the request body - validateRequiredFields(req, ['question', 'model', 'apiKey']); + validateRequiredFields(req, ['question', 'model']); - const { question, model, apiKey } = req.body; + const { question, model } = req.body; + //load the api key from an environment variable + const apiKey = process.env.LLM_API_KEY; + if (!apiKey) { + return res.status(400).json({ error: 'API key is missing.' }); + } const answer = await sendQuestionToLLM(question, apiKey, model); res.json({ answer }); diff --git a/llmservice/llm-service.test.js b/llmservice/llm-service.test.js index e8b0b7cf4..b64a81de7 100644 --- a/llmservice/llm-service.test.js +++ b/llmservice/llm-service.test.js @@ -1,3 +1,6 @@ +//set a fake api key +process.env.LLM_API_KEY = 'test-api-key'; + const request = require('supertest'); const axios = require('axios'); const app = require('./llm-service'); @@ -22,7 +25,7 @@ describe('LLM Service', () => { it('the llm should reply', async () => { const response = await request(app) .post('/ask') - .send({ question: 'a question', apiKey: 'apiKey', model: 'gemini' }); + .send({ question: 'a question', model: 'gemini' }); expect(response.statusCode).toBe(200); expect(response.body.answer).toBe('llmanswer'); diff --git a/llmservice/package-lock.json b/llmservice/package-lock.json index c801c4a74..f88e73de5 100644 --- a/llmservice/package-lock.json +++ b/llmservice/package-lock.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "axios": "^1.7.9", + "dotenv": "^16.4.7", "express": "^4.21.2" }, "devDependencies": { @@ -1803,6 +1804,18 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", diff --git a/llmservice/package.json b/llmservice/package.json index c85df80af..f0eb3d72b 100644 --- a/llmservice/package.json +++ b/llmservice/package.json @@ -9,12 +9,13 @@ "license": "ISC", "description": "", "homepage": "https://github.com/arquisoft/wichat_en1c#readme", - "dependencies": { - "axios": "^1.7.9", - "express": "^4.21.2" - }, - "devDependencies": { - "jest": "^29.7.0", - "supertest": "^7.0.0" - } + "dependencies": { + "axios": "^1.7.9", + "dotenv": "^16.4.7", + "express": "^4.21.2" + }, + "devDependencies": { + "jest": "^29.7.0", + "supertest": "^7.0.0" + } } diff --git a/webapp/Dockerfile b/webapp/Dockerfile index 53160db5e..20c397350 100644 --- a/webapp/Dockerfile +++ b/webapp/Dockerfile @@ -7,9 +7,7 @@ WORKDIR /app RUN npm install --omit=dev ARG API_URI="http://localhost:8000" -ARG LLM_API_KEY ENV REACT_APP_API_ENDPOINT=$API_URI -ENV REACT_APP_LLM_API_KEY=$LLM_API_KEY #Create an optimized version of the webapp RUN npm run build @@ -17,4 +15,4 @@ RUN npm install -g serve --production #Execute npm run prod to run the server CMD [ "npm", "run", "prod" ] -#CMD ["npm", "start"] \ No newline at end of file +#CMD ["npm", "start"] diff --git a/webapp/src/App.js b/webapp/src/App.js index 1f6f1b29d..f1e3fc993 100644 --- a/webapp/src/App.js +++ b/webapp/src/App.js @@ -17,7 +17,7 @@ function App() { - Welcome to the 2025 edition of the Software Architecture course + Welcome to the 2025 edition of the Software Architecture course! {showLogin ? : } diff --git a/webapp/src/components/Login.js b/webapp/src/components/Login.js index c9b4097be..afbe048e0 100644 --- a/webapp/src/components/Login.js +++ b/webapp/src/components/Login.js @@ -14,7 +14,7 @@ const Login = () => { const [openSnackbar, setOpenSnackbar] = useState(false); const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000'; - const apiKey = process.env.REACT_APP_LLM_API_KEY || 'None'; + const loginUser = async () => { try { @@ -22,14 +22,8 @@ const Login = () => { const question = "Please, generate a greeting message for a student called " + username + " that is a student of the Software Architecture course in the University of Oviedo. Be nice and polite. Two to three sentences max."; const model = "empathy" - - if (apiKey==='None'){ - setMessage("LLM API key is not set. Cannot contact the LLM."); - } - else{ - const message = await axios.post(`${apiEndpoint}/askllm`, { question, model, apiKey }) - setMessage(message.data.answer); - } + const message = await axios.post(`${apiEndpoint}/askllm`, { question, model }) + setMessage(message.data.answer); // Extract data from the response const { createdAt: userCreatedAt } = response.data;