diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..37df756 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +.git +.gitlab-ci.yml +Jenkinsfile.deprecated +startgcpdevapi.sh +.gitea +node_modules +scan +debian +tools +*.tgz diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..ba4710e --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,61 @@ +name: Build + push image +on: + push: + branches: [master] + workflow_dispatch: + +env: + REGISTRY: gitea.saas.vnc.biz + IMAGE: gitea.saas.vnc.biz/vnciac/vnc-hybridauth-api + +jobs: + build-and-push: + runs-on: ubuntu-latest + env: + REGISTRY_USER: ${{ secrets.REGISTRY_USER }} + REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} + steps: + - uses: actions/checkout@v4 + + - name: Compute image tag + run: | + SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-8) + echo "SHORT_SHA=${SHORT_SHA}" >> $GITHUB_ENV + + # kaniko builds straight from a Dockerfile and pushes to the registry, running + # as a static binary inside the job container — the runner has no Docker daemon. + - name: Fetch kaniko (daemon-less image builder) + run: | + set -euo pipefail + curl -fsSL -o /tmp/crane.tgz \ + https://github.com/google/go-containerregistry/releases/download/v0.21.9/go-containerregistry_Linux_x86_64.tar.gz + tar -xzf /tmp/crane.tgz -C /usr/local/bin crane + mkdir -p /tmp/kaniko-root + crane export gcr.io/kaniko-project/executor:debug /tmp/kaniko-fs.tar + tar -xf /tmp/kaniko-fs.tar -C /tmp/kaniko-root + cp /tmp/kaniko-root/kaniko/executor /usr/local/bin/kaniko-executor + chmod +x /usr/local/bin/kaniko-executor + /usr/local/bin/kaniko-executor version + + # kaniko's push auth reads $DOCKER_CONFIG/config.json (go-containerregistry + # authn.DefaultKeychain). Write to both locations and export DOCKER_CONFIG so + # auth is picked up regardless of how $HOME resolves. + - name: Configure registry auth for push + run: | + AUTH=$(printf '%s:%s' "${REGISTRY_USER}" "${REGISTRY_TOKEN}" | base64 -w0) + CFG=$(printf '{"auths":{"%s":{"auth":"%s"}}}' "${REGISTRY}" "${AUTH}") + mkdir -p /kaniko/.docker "$HOME/.docker" + printf '%s' "$CFG" > /kaniko/.docker/config.json + printf '%s' "$CFG" > "$HOME/.docker/config.json" + echo "DOCKER_CONFIG=/kaniko/.docker" >> "$GITHUB_ENV" + + # The Dockerfile lives at the repo root; its ADD/COPY paths (app/, package.json, + # yarn.lock, config.js) are relative to the root. + - name: Build + push image + run: | + /usr/local/bin/kaniko-executor \ + --context "dir://${GITHUB_WORKSPACE}" \ + --dockerfile Dockerfile \ + --skip-tls-verify-registry="${REGISTRY}" \ + --destination "${IMAGE}:sha-${SHORT_SHA}" \ + --destination "${IMAGE}:latest" diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 2e1edd1..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,103 +0,0 @@ -image: docker:20.10.17 - -variables: - DOCKER_HOST: tcp://localhost:2375 - DOCKER_TLS_CERTDIR: '' - DEV_CONTAINER_REGISTRY: eu.gcr.io/vnc-development/vnc-hybridauth-api:development - IMAGE_TAG: eu.gcr.io/vnc-development/vnc-hybridauth-api:development-$CI_COMMIT_SHORT_SHA - IMAGE_TAG_PROD: eu.gcr.io/vnc-dev-275609/vnc-hybridauth-api:production - IMAGE_TAG_STABLE: eu.gcr.io/vnc-stable/vnc-hybridauth-api:stable - -services: - - name: docker:20.10.17-dind - alias: docker - -stages: - - docker - - scan - - notification - -dev_deploy: - before_script: - - until docker info; do sleep 1; done - - base64 -d $SA_Development | docker login -u _json_key --password-stdin https://eu.gcr.io - stage: docker - artifacts: - public: false - expire_in: 12h - paths: - - scan - script: - - docker build -t $IMAGE_TAG . - - docker push $IMAGE_TAG - - docker image save $IMAGE_TAG -o scan/hybridauth.tgz - only: - - master - -dev_trivy: - image: aquasec/trivy:latest - stage: scan - script: - - trivy image --input scan/hybridauth.tgz - only: - - master - - - -prod_deploy: - before_script: - - until docker info; do sleep 1; done - - base64 -d $SA_Production | docker login -u _json_key --password-stdin https://eu.gcr.io - stage: docker - script: - - echo $CI_COMMIT_TAG - - COMMIT_VERSION=$(echo $CI_COMMIT_TAG | awk -F'prod-' '{print $2}') - - echo $COMMIT_VERSION - - - DEV_TAG=$DEV_CONTAINER_REGISTRY-$COMMIT_VERSION - - echo $DEV_TAG - - docker pull $DEV_TAG - - PROD_IMAGE=$IMAGE_TAG_PROD-$COMMIT_VERSION - - docker tag $DEV_TAG $PROD_IMAGE - - docker push $PROD_IMAGE - rules: - - if: $CI_COMMIT_TAG =~ /^prod-.*/ - -stable_deploy: - before_script: - - until docker info; do sleep 1; done - - base64 -d $SA_Production | docker login -u _json_key --password-stdin https://eu.gcr.io - stage: docker - script: - - echo $CI_COMMIT_TAG - - COMMIT_VERSION=$(echo $CI_COMMIT_TAG | awk -F'stable-' '{print $2}') - - echo $COMMIT_VERSION - - - DEV_TAG=$DEV_CONTAINER_REGISTRY-$COMMIT_VERSION - - echo $DEV_TAG - - docker pull $DEV_TAG - - STABLE_IMAGE=$IMAGE_TAG_STABLE-$COMMIT_VERSION - - docker tag $DEV_TAG $STABLE_IMAGE - - docker push $STABLE_IMAGE - rules: - - if: $CI_COMMIT_TAG =~ /^stable-.*/ - -successful-results: - image: eu.gcr.io/vnc-development/docker-vnc-deb-builder:latest - stage: notification - script: - - echo "SUCCESS" > result.txt - - ./tools/nagnotify.sh || echo "done" - rules: - - if: '$CI_COMMIT_BRANCH == "master"' - when: on_success - -failed-results: - image: eu.gcr.io/vnc-development/docker-vnc-deb-builder:latest - stage: notification - script: - - echo "FAILURE" > result.txt - - ./tools/nagnotify.sh || echo "done" - rules: - - if: '$CI_COMMIT_BRANCH == "master"' - when: on_failure diff --git a/Jenkinsfile.deprecated b/Jenkinsfile.deprecated deleted file mode 100644 index 9acb82c..0000000 --- a/Jenkinsfile.deprecated +++ /dev/null @@ -1,90 +0,0 @@ -#!groovy - -pipeline { - agent none - - environment { - git_commit_message = '' - git_commit_diff = '' - git_commit_author = '' - git_commit_author_name = '' - git_commit_author_email = '' - ANDROID_HOME = '/opt/android-sdk/' - } - - stages { - - // --- BUILD --- - stage('Build') { - agent { - label 'vncportal' - } - steps { - parallel( - 'frontend': { - node('vncportal') { - deleteDir() - checkout scm - sh 'yarn cache clean' - sh 'yarn install' - sh "tar cfz frontend.tgz node_modules" - stash includes: 'frontend.tgz', name: 'frontend' - deleteDir() - } - }, - ) - } - } - - - stage('Packaging') { - agent { - label 'master' - } - steps { - sh "echo 'Packaging'" - deleteDir() - checkout scm - sh "git fetch --tags" - sh "makechangelog.uxf > debian/changelog" - sh "cat debian/changelog" - sh "mkdir src" - sh "sed -n 1p debian/changelog | grep -oP '\\((.*?)\\)' > src/version.txt" - sh "echo '####################################################'" - sh "cat src/version.txt" - sh "echo '####################################################'" - - sh 'rm config/vnc-hybrid-authenticator.js' - sh 'cp -P conf.template/vnc-hybridauth2.js config/vnc-hybrid-authenticator.js' - // yarn fails on node4 with kurento stuff - unstash 'frontend' - sh 'tar xfz frontend.tgz' - lock('debianbuild') { - sh "cd debian; debuild --check-dirname-level 0 --no-tgz-check --no-lintian -kjenkins@vnc.biz -p'gpg --no-tty --passphrase q3tx65wurstbrot'; cd .." - } - sh "mkdir -p ../pkgarchive/" - - // Import to GCP deb repository - sh "scp ../*.deb repo@34.89.202.54:/srv/repo/apt/incoming/" - sh "ssh repo@34.89.202.54 /srv/repo/bin/import-new-packages.sh" - - sh "mv ../*.deb ../pkgarchive/" - sh "rm ../*.dsc" - sh "rm ../*amd64.build" - sh "rm ../*amd64.changes" - } - } - } - - post { - always { - emailext ( - to: 'stefan.saenger@vnc.biz', - subject: "${env.JOB_NAME} #${env.BUILD_NUMBER}", - body: "Build URL: ${env.BUILD_URL}.", - attachLog: false, - ) - } - } - -} diff --git a/README.md b/README.md new file mode 100644 index 0000000..11d200c --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# vnc-hybridauth2 (vnctalk-hybrid-authenticator2) + +Hybrid authenticator for VNCtalk / VNCmail. An Express service that sits between the +auth-proxy's `hybridAuthURL` and the directory backends: it authenticates users against +**LDAP** and the **VNCdirectory** PostgreSQL DB, and issues/verifies the XMPP JWT +(`xmppToken`) that Prosody and the mail/talk APIs share for SSO. + +Prosody's `mod_auth_http_async` points `hybridaAuthUrl` at this service's HTTP-basic +endpoint (default port `9544`). + +## Build & image + +Built by Gitea Actions (`.gitea/workflows/deploy.yml`) on every push to `master`, using +kaniko (no Docker daemon on the runner). The image is pushed to the Gitea registry: + +- `gitea.saas.vnc.biz/vnciac/vnc-hybridauth-api:latest` +- `gitea.saas.vnc.biz/vnciac/vnc-hybridauth-api:sha-` + +Registry auth uses the `REGISTRY_USER` / `REGISTRY_TOKEN` Actions secrets. The image runs +as the non-root `vncuser` (uid 1001) and starts `node /usr/share/vnctalk-hybrid-authenticator2/app/app.js`. + +## Configuration + +`config.js` (copied into the image as `config/vnc-hybrid-authenticator.js`) is env-driven; +the `development` profile reads `NODE_ENV`-scoped values from `process.env` (LDAP URI/bind, +Postgres host/port/name/user/pass, `xmppToken`, `domain`). Provide them via the deployment +(ArgoCD in the `vnc-iac-env` GitOps repo), sourced from Infisical — do **not** commit +secrets to this repo. + +## Deploy + +Deployed by the ArgoCD application in `vnc-iac-env` (the hybrid-auth service that Prosody +and the mail/talk APIs call). This repo has no live deploy manifest; the image tag is pinned +in that GitOps repo. + +## Legacy removed + +The GitLab CI (`.gitlab-ci.yml`), `Jenkinsfile.deprecated`, and the GCP dev bootstrap +(`startgcpdevapi.sh`) were removed during the GitLab → Gitea migration. The old +`eu.gcr.io/vnc-development/vnc-hybridauth-api` promotion flow (`prod-*` / `stable-*` tags) +is not carried over; the current build pushes a single `master` → `sha-*` + `latest`. diff --git a/package.json b/package.json index 398efae..196aa98 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "app.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "start": "sh startgcpdevapi.sh", + "start": "node app/app.js", "start:gr13": "NODE_ENV=gr13 node app/app.js" }, "author": "VNC Software AG", diff --git a/startgcpdevapi.sh b/startgcpdevapi.sh deleted file mode 100755 index 5518fd1..0000000 --- a/startgcpdevapi.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash - -USERNAME=$USER; -RSA_FILE=~/.ssh/id_rsa - -if [ -n "$ID_RSA" ]; then - RSA_FILE=$ID_RSA -fi - -echo "RSA_FILE = $RSA_FILE" - -RSA_FILE=~/.ssh/id_rsa - -if [ -n "$ID_RSA" ]; then - RSA_FILE=$ID_RSA -fi - -echo "RSA_FILE = $RSA_FILE" - - -if [ -f ~/.vnc-gcp-user ]; then - echo "found username config..." - USERNAME=$(cat ~/.vnc-gcp-user | grep "^USERNAME" | awk -F "=" '{print $2}'); - echo "using username $USERNAME for gcp ssh tunnels" -fi - -if [ -S "/tmp/vncddb.sock" ]; then - echo "Socket for vncdirectory database already active - skipping" -else - echo "starting tunnel for vncdirectory db" - ssh -i $RSA_FILE "$USERNAME@10.0.201.44" -o "proxycommand ssh -W %h:%p $USERNAME@34.107.1.238" -f -N -M -S /tmp/vncddb.sock -L 54322:127.0.0.1:5432 - sleep 3 -fi - - -echo "starting..." -export NODE_ENV=development -export PORT=8000 -node app/app.js 2>&1 | tee -a /tmp/vncha-api.log & - -echo "waiting for api service to start..." -sleep 10 - -osslug=$(uname); -serverup="1"; - -while [ "$serverup" = 1 ]; do - serverup="0"; - if [ "$osslug" = "Linux" ]; then - serverup=$(netstat -nlp 2>/dev/null | grep -c "9544"); - fi - if [ "$osslug" = "Darwin" ]; then - serverup=$(netstat -an -ptcp 2>/dev/null | grep LISTEN | grep -c "9544"); - fi - - sleep 10 -done - -echo "api service no longer running - closing tunnels" - -ssh -S /tmp/vncddb.sock -O exit "$USER@10.0.201.44"