From 73b5a6cf8db8f58b00cfa4e41b326d3bb34f8856 Mon Sep 17 00:00:00 2001 From: Patrick Hobusch Date: Tue, 18 Oct 2022 19:29:41 +0800 Subject: [PATCH] Implement initial init container image --- .github/workflows/ci.yml | 62 ++++++++++++++++ .gitignore | 35 +++++++++ Dockerfile | 29 ++++++++ README.md | 7 +- pom.xml | 59 +++++++++++++++ .../aservo/sonar/SonarqubePasswordHash.java | 72 +++++++++++++++++++ 6 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 pom.xml create mode 100644 src/main/java/de/aservo/sonar/SonarqubePasswordHash.java diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..05ba42f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,62 @@ +name: build + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + release: + types: [ published ] + +jobs: + + build: + + name: Build Image + + runs-on: ubuntu-latest + + env: + TAG: ${{ github.event.release.tag_name }} + + steps: + + - name: Checkout + uses: actions/checkout@v2 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Set tag for non-release builds + if: github.event_name != 'release' + run: | + echo "TAG=${{ github.sha }}" >> $GITHUB_ENV + + - name: Login to GitHub Container Registry + # don't even login to GitHub CR if this is not a release + if: github.event_name == 'release' + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build / Release + uses: docker/build-push-action@v2 + with: + # push and load may not be set together at the moment + load: ${{ github.event_name != 'release' }} + push: ${{ github.event_name == 'release' }} + tags: | + ghcr.io/${{ github.repository_owner }}/sonarqube-password-hash:latest + ghcr.io/${{ github.repository_owner }}/sonarqube-password-hash:${{ env.TAG }} + + - name: Inspect + # as push and load may not be set together at the moment + # we can only run this step in non-release builds + if: github.event_name != 'release' + run: | + docker image inspect ghcr.io/${{ github.repository_owner }}/sonarqube-password-hash:latest diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..65f20b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8e77543 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,29 @@ +# Build image + +FROM maven:3.6.0-jdk-11-slim AS build + +COPY src /home/app/src +COPY pom.xml /home/app + +RUN mvn -f /home/app/pom.xml clean package --batch-mode + +# Execution image + +FROM eclipse-temurin:11-jre-jammy + +ENV DEBIAN_FRONTEND=noninteractive + +ARG CLI_JAR=/usr/local/lib/sonarqube-password-hash.jar +ARG CLI_BIN=/usr/local/bin/sonarqube-password-hash + +RUN apt-get update && \ + apt-get --no-install-recommends --yes install \ + postgresql-client \ + && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +COPY --from=build /home/app/target/sonarqube-password-hash-0.0.0-jar-with-dependencies.jar ${CLI_JAR} + +RUN echo "#!/bin/env bash\nset -o pipefail\nset -e\njava -jar ${CLI_JAR} \$@" > ${CLI_BIN} && \ + chmod +x ${CLI_BIN} diff --git a/README.md b/README.md index ef375f1..82b79a5 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,7 @@ -# docker-sonarqube-password-hash +Sonarqube Password Hash +======================= + Docker image with Sonarqube password hash generator intended to be used for Sonarqube configuration + +The code used in this image is based on the Sonarqube local authentication code: +https://github.com/SonarSource/sonarqube/blob/9.6.0.59041/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/CredentialsLocalAuthentication.java diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..ad2585a --- /dev/null +++ b/pom.xml @@ -0,0 +1,59 @@ + + + + 4.0.0 + + de.aservo + sonarqube-password-hash + 0.0.0 + + + 11 + 11 + UTF-8 + + + + + org.mindrot + jbcrypt + 0.4 + + + + commons-lang + commons-lang + 2.6 + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + + + de.aservo.sonar.SonarqubePasswordHash + + + + jar-with-dependencies + + + + + + + + + diff --git a/src/main/java/de/aservo/sonar/SonarqubePasswordHash.java b/src/main/java/de/aservo/sonar/SonarqubePasswordHash.java new file mode 100644 index 0000000..6356835 --- /dev/null +++ b/src/main/java/de/aservo/sonar/SonarqubePasswordHash.java @@ -0,0 +1,72 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package de.aservo.sonar; + +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.util.Base64; + +import static java.lang.String.format; + +public class SonarqubePasswordHash { + + // The following values must match the constants from CredentialsLocalAuthentication.java + private static final int ITERATIONS = 100_000; + private static final char ITERATIONS_HASH_SEPARATOR = '$'; + private static final int KEY_LEN = 512; + private static final String ALGORITHM = String.format("PBKDF2WithHmacSHA%d", KEY_LEN); + + public static void main(final String[] args) { + try { + assert args.length == 2; + + final String salt = args[0]; + final String password = args[1]; + + System.out.println(hashPassword(salt, password)); + System.exit(0); + } catch (Exception e) { + System.out.println("usage: sonarqube-password-hash "); + System.exit(1); + } + } + + private static String hashPassword(final String salt, final String password) { + final byte[] saltBytes = Base64.getDecoder().decode(salt); + return composeEncryptedPassword(hash(saltBytes, password)); + } + + private static String composeEncryptedPassword(final String hash) { + return format("%d%c%s", ITERATIONS, ITERATIONS_HASH_SEPARATOR, hash); + } + + private static String hash(final byte[] salt, final String password) { + try { + final SecretKeyFactory skf = SecretKeyFactory.getInstance(ALGORITHM); + final PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, KEY_LEN); + final byte[] hash = skf.generateSecret(spec).getEncoded(); + return Base64.getEncoder().encodeToString(hash); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + throw new RuntimeException(e); + } + } +}