commit a769467771489c5767137eaf721720161cfae414 Author: xZero707 Date: Thu May 29 17:34:58 2025 +0200 Initial commit diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..d186494 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +.idea/ +data/ +secrets/ +compose.override.yaml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d186494 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea/ +data/ +secrets/ +compose.override.yaml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..70088ed --- /dev/null +++ b/Dockerfile @@ -0,0 +1,28 @@ +ARG INFISICAL_CLI_VERSION=0.41.2 +ARG ALPINE_VERSION=3.21.3 + +FROM scratch AS rootfs + +COPY ["./src/entrypoint.sh", "/entrypoint.sh"] +COPY ["./src/adapt-user.sh", "/usr/local/bin/adapt-user"] + + + +ARG ALPINE_VERSION +FROM alpine:${ALPINE_VERSION} + +# Define build-time argument for Infisical version +ARG INFISICAL_CLI_VERSION + +RUN set -eux \ + && apk --no-cache add bash curl gnupg runuser shadow \ + && groupmod -g 1000 users \ + && useradd -u 911 -U -d /data -s /bin/false abc \ + && usermod -G users abc \ + && curl -1sLf 'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.alpine.sh' | bash \ + && apk --no-cache add "infisical=${INFISICAL_CLI_VERSION}" \ + && rm -rf /var/cache/apk/* /tmp/* + +COPY --from=rootfs ["/", "/"] + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/bin/build b/bin/build new file mode 100755 index 0000000..76cf97d --- /dev/null +++ b/bin/build @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -e + +main() { + local image="${XCR_REGISTRY:?}/router0/infisicial-cli" + local git_hash + git_hash="$(git rev-parse --short HEAD)" + + docker build -t "${image}:latest" . --provenance=false + docker tag "${image}:latest" "${image}:${git_hash}" + docker push "${image}:latest" + docker push "${image}:${git_hash}" +} + +main diff --git a/bin/infisical b/bin/infisical new file mode 100755 index 0000000..06b9f6c --- /dev/null +++ b/bin/infisical @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -e + +main() { + docker compose run --rm -t cli infisical "${@}" +} + +main "${@}" diff --git a/build/docker-bake.hcl b/build/docker-bake.hcl new file mode 100644 index 0000000..5f9bde9 --- /dev/null +++ b/build/docker-bake.hcl @@ -0,0 +1,87 @@ +group "default" { + targets = [ + "0_41_2", + ] +} + +target "build-dockerfile" { + dockerfile = "Dockerfile" +} + +target "build-platforms" { + platforms = ["linux/amd64", "linux/aarch64"] +} + +target "build-platforms-cudnn" { + platforms = ["linux/amd64"] +} + +target "build-common" { + pull = true +} + +###################### +# Define the variables +###################### + +variable "REGISTRY_CACHE" { + default = "dcrl.xrf.nu/router0/infisical-cli-cache" +} + +###################### +# Define the functions +###################### + +# Get the arguments for the build +function "get-args" { + params = [infisical_cli_version, alpine_version] + result = { + INFISICAL_CLI_VERSION = infisical_cli_version + ALPINE_VERSION = alpine_version + } +} + +# Get the cache-from configuration +function "get-cache-from" { + params = [version] + result = [ + "type=registry,ref=${REGISTRY_CACHE}:${sha1("${version}-${BAKE_LOCAL_PLATFORM}")}" + ] +} + +# Get the cache-to configuration +function "get-cache-to" { + params = [version] + result = [ + "type=registry,mode=max,ref=${REGISTRY_CACHE}:${sha1("${version}-${BAKE_LOCAL_PLATFORM}")}" + ] +} + +# Get list of image tags and registries +# Takes a version and a list of extra versions to tag +# eg. get-tags("0.29.0", ["0.29", "latest"]) +function "get-tags" { + params = [version, extra_versions] + result = concat( + [ + "xcr.se/router0/infisical-cli:${version}" + ], + flatten([ + for extra_version in extra_versions : [ + "xcr.se/router0/infisical-cli:${extra_version}" + ] + ]) + ) +} + +########################## +# Define the build targets +########################## + +target "0_41_2" { + inherits = ["build-dockerfile", "build-platforms", "build-common"] + cache-from = get-cache-from("0.41.2") + cache-to = get-cache-to("0.41.2") + tags = get-tags("0.41.2", ["0.41", "latest"]) + args = get-args("0.41.2", "3.21") +} diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..cc25869 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,17 @@ +services: + cli: + image: xcr.se/router0/infisical-cli:latest + init: true + command: ["sleep", "infinity"] + deploy: + replicas: 1 + restart_policy: + condition: none + build: + context: . + environment: + INFISICAL_API_URL: "https://infisical-instance.example.com/api" + RUNTIME_USER_ID: 1000 + RUNTIME_GROUP_ID: 1000 + volumes: + - ./data/cli/home:/data diff --git a/dist/scripts/src/fetch-secrets.sh b/dist/scripts/src/fetch-secrets.sh new file mode 100755 index 0000000..e413a08 --- /dev/null +++ b/dist/scripts/src/fetch-secrets.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +fetch_secret() { + local target_secret="${1:?Target secret local_secret is required}" + local env="${2:?Environment is required}" + local output_file="${3:?}" + + script -q /dev/null \ + -c "docker compose run --rm -t cli infisical secrets --plain get ""${target_secret}"" --env ""${env}""" \ + >"${output_file}" + + # Check if file is empty + if [[ ! -s ${output_file} ]]; then + return 1 + fi +} + +main() { + local config_file="${1:-./secrets.json}" + local secrets_dir="${2:-./secrets}" + + if ! command -v jq &> /dev/null; then + printf "Error: jq is required but not installed\n" >&2 + return 1 + fi + + if [[ ! -f "${config_file}" ]]; then + printf "Error: Config file %s not found\n" "${config_file}" >&2 + return 1 + fi + + mkdir -p "${secrets_dir}" + + mapfile -t entries < <(jq -c '.[]' "${config_file}") + + local local_secret target_secret env obj + + for obj in "${entries[@]}"; do + local_secret="$(jq -r .secret_name <<<"${obj}")" + target_secret="$(jq -r .target_secret <<<"${obj}")" + env="$(jq -r .env <<<"${obj}")" + output_file="${secrets_dir}/${local_secret}" + + if [[ -z "${env}" ]]; then + printf "Warning: Environment not specified for secret %s, assuming 'prod'\n" "${local_secret}" >&2 + fi + + printf "Processing %s -> %s (%s)\n" "${local_secret}" "${target_secret}" "${env}" + if fetch_secret "${target_secret}" "${env}" "${output_file}"; then + printf "✔ saved to %s\n" "${output_file}" + continue + fi + + rm -f "${output_file}" # Clean up if fetch failed + printf "✘ failed to fetch %s\n" "${target_secret}" >&2 + done +} + +main "$@" diff --git a/dist/scripts/src/secrets.json b/dist/scripts/src/secrets.json new file mode 100644 index 0000000..5907ece --- /dev/null +++ b/dist/scripts/src/secrets.json @@ -0,0 +1,12 @@ +[ + { + "secret_name": "gpg_key", + "target_secret": "PERSONAL_GPG_KEY_123", + "env": "prod" + }, + { + "secret_name": "ssh_key", + "target_secret": "PERSONAL_GITHUB_SSH_KEY_123", + "env": "prod" + } +] diff --git a/src/adapt-user.sh b/src/adapt-user.sh new file mode 100755 index 0000000..932c213 --- /dev/null +++ b/src/adapt-user.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +main() { + local username="${1:-abc}" + local uid="${2:-1000}" + local gid="${3:-1000}" + local home="${4:-/home/abc}" + + usermod -d "/root" "${username}" + groupmod -o -g "${gid}" "${username}" + usermod -o -u "${uid}" "${username}" + usermod -d "${home}" "${username}" + + # Ensure tty group exists with GID 4 (Alpine standard) + if ! getent group tty >/dev/null; then + addgroup -g 4 tty + fi + + # Add user to tty group for /dev/pts/* access + addgroup "${username}" tty 2>/dev/null + + if tty -s; then + local tty_device + tty_device="$(tty)" + + if [ -f "${tty_device}" ]; then + chmod g+rw "${tty_device}" 2>/dev/null + fi + fi +} +main "${@}" diff --git a/src/entrypoint.sh b/src/entrypoint.sh new file mode 100755 index 0000000..1a97d9b --- /dev/null +++ b/src/entrypoint.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +set -euo pipefail + +main() { + local user_id group_id + user_id="${RUNTIME_USER_ID:?}" + group_id="${RUNTIME_GROUP_ID:?}" + + /usr/local/bin/adapt-user "abc" "${user_id}" "${group_id}" "/data/" + + runuser -u abc -- mkdir -p "/data/project" + cd /data/project + + #stty sane + exec runuser -u abc -- "$@" +} + +main "${@}"