diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..8afe975 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,39 @@ +name: goreleaser + +on: + push: + branches: + - 'master' + tags: + - 'v*' + pull_request: + +permissions: + contents: write + # packages: write + # issues: write + +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.22.2' + # More assembly might be required: Docker logins, GPG, etc. + # It all depends on your needs. + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v5 + with: + # either 'goreleaser' (default) or 'goreleaser-pro' + distribution: goreleaser + # 'latest', 'nightly', or a semver + version: latest + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e660fd9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +bin/ diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..65a7681 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,21 @@ +before: + hooks: + - go mod tidy + +builds: + - env: + - CGO_ENABLED=0 + goos: + - linux + - darwin + - windows + goarch: + - amd64 + - arm64 + goarm: + - "7" + mod_timestamp: "{{ .CommitTimestamp }}" + flags: + - -trimpath + ldflags: + - -s -w -X main.version={{.Version}} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4e14ffb --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +ARG NODE_VERSION_IMAGE +FROM golang:1.22.2 as build +ARG VERSION_STRING="unknown" + +COPY . /go + +RUN cd /go && set -x; CGO_ENABLED=0 go build -trimpath -ldflags "-s -w -X main.version=$VERSION_STRING" -o run + +FROM ${NODE_VERSION_IMAGE} +RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends openssl ca-certificates curl && apt-get clean && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +RUN chown -R node:node /app + +COPY --from=build /go/run /app/run +RUN chmod +x /app/run + +USER node + +RUN sh -c 'whoami && /app/run true' + +ENTRYPOINT ["/app/run"] diff --git a/Dockerfile.windows b/Dockerfile.windows new file mode 100644 index 0000000..7686cf4 --- /dev/null +++ b/Dockerfile.windows @@ -0,0 +1,15 @@ +ARG NODE_VERSION_IMAGE +FROM golang:1.22.2-windowsservercore-ltsc2022 as build +ARG VERSION_STRING="unknown" + +COPY . /go + +RUN cd /go && set -x; CGO_ENABLED=0 go build -trimpath -ldflags "-s -w -X main.version=$VERSION_STRING" -o entrypoint.exe + +FROM ${DOTNET_VERSION_IMAGE} +ADD https://aka.ms/vs/17/release/vc_redist.x64.exe /vc_redist.x64.exe +RUN c:\vc_redist.x64.exe /install /quiet /norestart + +COPY --from=build /go/entrypoint.exe /entrypoint/entrypoint.exe + +ENTRYPOINT ["/entrypoint/entrypoint.exe"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..91f4123 --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +ifeq ($(strip $(VERSION_STRING)),) +VERSION_STRING := $(shell git rev-parse --short HEAD) +endif + +BINDIR := $(CURDIR)/bin +PLATFORMS := linux/amd64/entrypoint/osusergo*netgo*static_build windows/amd64/entrypoint.exe/osusergo*static_build +BUILDCOMMAND := go build -trimpath -ldflags "-s -w -X main.version=${VERSION_STRING}" +temp = $(subst /, ,$@) +os = $(word 1, $(temp)) +arch = $(word 2, $(temp)) +label = $(word 3, $(temp)) +tags = $(subst *, ,$(word 4, $(temp))) + +UNAME := $(shell uname) +ifeq ($(UNAME), Darwin) +SHACOMMAND := shasum -a 256 +else +SHACOMMAND := sha256sum +endif + +.DEFAULT_GOAL := build + +.PHONY: release +release: $(PLATFORMS) +$(PLATFORMS): + GOOS=$(os) GOARCH=$(arch) CGO_ENABLED=0 $(BUILDCOMMAND) -tags "$(tags)" -o "bin/$(label)" + $(SHACOMMAND) "bin/$(label)" > "bin/$(label).sha256" + +.PHONY: build +build: + $(BUILDCOMMAND) -o bin/entrypoint + +.PHONY: dep +dep: + go mod tidy diff --git a/README.md b/README.md index 74cfb43..917f336 100644 --- a/README.md +++ b/README.md @@ -1 +1,86 @@ -# go-entrypoint \ No newline at end of file +# Entrypoint for docker containers + +Entrypoint for running apps in containers with: +1. Optional generation env variables (only for child process) from Vault secrets. Windows version also set env variables in Registry system-wide +2. SIGTERM and SIGINT propagation to child process +3. Wait for child process for finish and exit with child's exit code + +## Entrypoint binaries delivery + +### With built-in base image + +You could use next Dockerfiles as example to build your base image: +- [Dockerfile for Linux for Node.JS apps](Dockerfile) +- [Dockerfile for Windows for Dot.Net apps](Dockerfile.windows) + +Applications CI will use those base images in `FROM` + +### With S3 storage and Kubernetes host_mount + +1. Create an S3 bucket (like `infra-binaries`) +2. Upload binaries (for linux and windows) to the S3 bucket + 1. New binary should be uploaded to the temp name like `entrypoint.tmp` + 2. Old binary should be renamed to the `entrypoint.old` + 3. New binary should be renamed from temp name `entrypoint.tmp` to `entrypoint` +3. Every k8s node contains a bootstrap code to download relevant entrypoint binary + 1. For linux nodes: + ``` + pre_bootstrap_user_data = <<-EOT + #!/bin/bash + mkdir -p /entrypoint + aws s3 cp s3://infra-binaries/entrypoint/entrypoint /entrypoint/entrypoint || aws s3 cp s3://infra-binaries/entrypoint/entrypoint.old /entrypoint/entrypoint + chmod +x /entrypoint/entrypoint + EOT + ``` + 2. For windows nodes: + ``` + pre_bootstrap_user_data = <<-EOT + Read-S3Object -BucketName "infra-binaries" -Key "entrypoint/entrypoint.exe" -Region "eu-west-2" -File "/entrypoint/entrypoint.exe"; if (-not $?) { Read-S3Object -BucketName "infra-binaries" -Key "entrypoint/entrypoint.exe.old" -Region "eu-west-2" -File "/entrypoint/entrypoint.exe" } + EOT + ``` +4. Configure POD with host volume mount `/entrypoint/` +5. Configure POD's `command` (entrypoint) changed to `/entrypoint/entrypoint` for linix and `/entrypoint/entrypoint.exe` for windows +6. To update `entrypoint` on nodes, could use project [go-entrypoint-updater](https://github.com/alt-dima/go-entrypoint-updater) + +## Entrypoint logic workflow +1. Check if `VAULT_ADDR` env var configured and Vault is reacheble and ready by endpoint `/v1/sys/health` +3. If list with required Vault secrets is not empty: + 1. Read secrets list from `SECRETS_SOURCE_CONFIG` env var, by default: `./secrets_config.json#secrets_list` (`./secrets_config.json` - json file path, `secrets_list` - json path inside file) + 2. Init Vault Client with credentials (env vars `VAULT_APPROLE_RID` and `VAULT_APPROLE_SID`) + 3. Read required secrets from Vault and set env varibales with these values to the child +4. Run child app process with defined arguments +6. Wait until process will be terminated (with signals propagation) or exited by itself + +### Vault secrets: +Regular `/secret/{secret_path}` will be used. + +Required secrets configuration (`secrets_config.json` example): +```json +{ + "secrets_list": [ + "mongodb", + "rabbitmq", + { + "secretname": "mysql#local", + "envvarname": "env1" + }, + ] +} +``` +## Usage: +```bash +export SECRETS_SOURCE_CONFIG=./secrets_config.json#secrets_list +export VAULT_APPROLE_SID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +export VAULT_APPROLE_RID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +export VAULT_ADDR=https://vault-api-address + +entrypoint node app.js appparam1 appparam2 appparam3 +``` +Listed secrets from `secrets_config.json` file will be provided as a child's process env vars (and container-wide for windows) in the following format: +Non `[^a-zA-Z0-9_]` characters in the secret path will be replaced with `_` (like envconsul did) + +```bash +echo $secret_mongodb_url1 +secret_mongodb_url1="xxx" +``` +if one of listed secret's path doesn't exist in Vault - entrypoint will fail. diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..091cc4d --- /dev/null +++ b/go.mod @@ -0,0 +1,35 @@ +module github.com/alt-dima/go-entrypoint + +go 1.22.2 + +require ( + github.com/PaesslerAG/jsonpath v0.1.1 + github.com/hashicorp/vault/api v1.13.0 + github.com/hashicorp/vault/api/auth/approle v0.6.0 + golang.org/x/sys v0.19.0 +) + +require ( + github.com/PaesslerAG/gval v1.2.2 // indirect + github.com/cenkalti/backoff/v3 v3.2.2 // indirect + github.com/go-jose/go-jose/v4 v4.0.1 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-retryablehttp v0.7.5 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 // indirect + github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect + github.com/hashicorp/go-sockaddr v1.0.6 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/ryanuber/go-glob v1.0.0 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + github.com/stretchr/testify v1.9.0 // indirect + golang.org/x/crypto v0.22.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.5.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..f79f750 --- /dev/null +++ b/go.sum @@ -0,0 +1,163 @@ +github.com/PaesslerAG/gval v1.0.0/go.mod h1:y/nm5yEyTeX6av0OfKJNp9rBNj2XrGhAf5+v24IBN1I= +github.com/PaesslerAG/gval v1.2.2 h1:Y7iBzhgE09IGTt5QgGQ2IdaYYYOU134YGHBThD+wm9E= +github.com/PaesslerAG/gval v1.2.2/go.mod h1:XRFLwvmkTEdYziLdaCeCa5ImcGVrfQbeNUbVR+C6xac= +github.com/PaesslerAG/jsonpath v0.1.0/go.mod h1:4BzmtoM/PI8fPO4aQGIusjGxGir2BzcV0grWtFzq1Y8= +github.com/PaesslerAG/jsonpath v0.1.1 h1:c1/AToHQMVsduPAa4Vh6xp2U0evy4t8SWp8imEsylIk= +github.com/PaesslerAG/jsonpath v0.1.1/go.mod h1:lVboNxFGal/VwW6d9JzIy56bUsYAP6tH/x80vjnCseY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= +github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= +github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= +github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= +github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= +github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= +github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs= +github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M= +github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 h1:iBt4Ew4XEGLfh6/bPk4rSYmuZJGizr6/x/AEizP0CQc= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8/go.mod h1:aiJI+PIApBRQG7FZTEBx5GiiX+HbOHilUdNxUZi4eV0= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/go-sockaddr v1.0.6 h1:RSG8rKU28VTUTvEKghe5gIhIQpv8evvNpnDEyqO4u9I= +github.com/hashicorp/go-sockaddr v1.0.6/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/vault/api v1.12.0/go.mod h1:si+lJCYO7oGkIoNPAN8j3azBLTn9SjMGS+jFaHd1Cck= +github.com/hashicorp/vault/api v1.13.0 h1:RTCGpE2Rgkn9jyPcFlc7YmNocomda44k5ck8FKMH41Y= +github.com/hashicorp/vault/api v1.13.0/go.mod h1:0cb/uZUv1w2cVu9DIvuW1SMlXXC6qtATJt+LXJRx+kg= +github.com/hashicorp/vault/api/auth/approle v0.6.0 h1:ELfFFQlTM/e97WJKu1HvNFa7lQ3tlTwwzrR1NJE1V7Y= +github.com/hashicorp/vault/api/auth/approle v0.6.0/go.mod h1:CCoIl1xBC3lAWpd1HV+0ovk76Z8b8Mdepyk21h3pGk0= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= +github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..00c3f04 --- /dev/null +++ b/main.go @@ -0,0 +1,96 @@ +package main + +import ( + "log/slog" + "os" + "os/exec" + "os/signal" + "strconv" + "strings" + "syscall" + "time" + + "github.com/alt-dima/go-entrypoint/utils" +) + +var ( + version string = "unspecified" +) + +func main() { + var logger = utils.Logger + slog.SetDefault(logger) + + if len(os.Args) == 1 { + logger.Error("Entrypoint nothing to be executed") + os.Exit(1) + } else if os.Args[1] == "" { + logger.Error("Entrypoint empty command") + os.Exit(1) + } + cmdToExec := os.Args[1] + + argsToExec := []string{} + for _, value := range os.Args[2:] { + if value != "" { + argsToExec = append(argsToExec, value) + } + } + + sigs := make(chan os.Signal, 2) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, os.Interrupt) + + logger.Debug("Entrypoint version " + version) + + logger.Debug("Entrypoint starting child: " + cmdToExec + " " + strings.Join(argsToExec, " ")) + cmd := exec.Command(cmdToExec, argsToExec...) + cmd.Env = utils.GenerateChildEnvs() + // pipe the commands output to the applications + // standard output + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err := cmd.Start() + if err != nil { + logger.Error("Entrypoint failed to start child: " + err.Error()) + os.Exit(1) + } + + var shutdownStartTime time.Time + + go func() { + sig := <-sigs + logger.Debug("Entrypoint got signal " + sig.String()) + shutdownStartTime = time.Now() + cmd.Process.Signal(sig) + }() + //log.Println("awaiting signal") + + err = cmd.Wait() + exitCodeFinal := 0 + if err != nil && cmd.ProcessState.ExitCode() < 0 { + exitCodeFinal = 1 + logger.Warn("Entrypoint child failed: " + err.Error()) + } else if cmd.ProcessState.ExitCode() == 143 { + exitCodeFinal = 0 + } else { + exitCodeFinal = cmd.ProcessState.ExitCode() + } + + // Could be used to stop sidecars such envoy proxy by sending specific HTTP-request + //utils.StopExtSvcs() + + if !shutdownStartTime.IsZero() { + shutdownElapsedDuration := time.Since(shutdownStartTime) + if shutdownElapsedDuration.Seconds() > 85 { + logger.Warn("Entrypoint slow child termination took " + shutdownElapsedDuration.String()) + } else if shutdownElapsedDuration.Milliseconds() < 50 { + logger.Info("Entrypoint fast child termination took " + shutdownElapsedDuration.String()) + } + } + if exitCodeFinal == 0 { + logger.Debug("Entrypoint exiting with code " + strconv.Itoa(exitCodeFinal)) + } else { + logger.Warn("Entrypoint exiting with code " + strconv.Itoa(exitCodeFinal)) + } + os.Exit(exitCodeFinal) +} diff --git a/utils/extsvcs.go b/utils/extsvcs.go new file mode 100644 index 0000000..7539b93 --- /dev/null +++ b/utils/extsvcs.go @@ -0,0 +1,22 @@ +package utils + +// func sendSimpleHttpReq(method string, url string) { +// newReq, err := http.NewRequest(method, url, nil) +// if err != nil { +// Logger.Debug(err.Error()) +// } +// resp, err := http.DefaultClient.Do(newReq) +// if err == nil { +// io.Copy(io.Discard, resp.Body) +// resp.Body.Close() +// } +// } + +// func StopExtSvcs() { +// //Pause/sleep disable because envoy/istio-proxy does not intercept any traffic from consul-sidecar +// //time.Sleep(5 * time.Second) + +// //Send to envoy +// //logger.Debug("Entrypoint sending quitquitquit signal to envoy-sidecar") +// sendSimpleHttpReq(http.MethodPost, "http://127.0.0.1:15000/quitquitquit") +// } diff --git a/utils/logger.go b/utils/logger.go new file mode 100644 index 0000000..38fca68 --- /dev/null +++ b/utils/logger.go @@ -0,0 +1,19 @@ +package utils + +import ( + "log/slog" + "os" +) + +var Logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ + Level: slog.LevelDebug, + ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { + if a.Key == slog.TimeKey { + return slog.Attr{Key: "timestamp", Value: a.Value} + } + if a.Key == slog.MessageKey { + return slog.Attr{Key: "message", Value: a.Value} + } + return a + }, +})) diff --git a/utils/prestart.go b/utils/prestart.go new file mode 100644 index 0000000..d9cd89e --- /dev/null +++ b/utils/prestart.go @@ -0,0 +1,72 @@ +package utils + +import ( + "fmt" + "io" + "net/http" + "os" + "strings" + "time" +) + +func GenerateChildEnvs() []string { + vaultAddr := os.Getenv("VAULT_ADDR") + removeEnvVars := []string{"VAULT_APPROLE_RID", "VAULT_APPROLE_SID"} + var childEnvs []string + for _, removeEnv := range removeEnvVars { + os.Unsetenv(removeEnv) + } + childEnvs = os.Environ() + + if vaultAddr != "" { + checkCriticalSvcReady(vaultAddr + "/v1/sys/health?standbyok=true") + + var vaultStruct vaultStruct + vaultStruct.initVaultClient(vaultAddr) + + secretSourceConfig := os.Getenv("SECRETS_SOURCE_CONFIG") + + secretsFile := "./secrets_config.json" + secretsJsonPath := "secrets_list" + + if secretSourceConfig != "" { + splitedSourceConfig := strings.SplitN(secretSourceConfig, "#", 2) + secretsFile = splitedSourceConfig[0] + secretsJsonPath = splitedSourceConfig[1] + } + + secretsFromSource := readSecretsfromSource(secretsFile, secretsJsonPath) + secretsEnvList := vaultStruct.getSecretsEnvList(secretsFromSource) + + childEnvs = append(childEnvs, secretsEnvList...) + } + + return childEnvs +} + +func checkCriticalSvcReady(addrToCheck string) { + retryCnt := 5 + waitTime := 4 + for { + resp, err := http.Get(addrToCheck) + if err == nil { + io.Copy(io.Discard, resp.Body) + resp.Body.Close() + } + if err == nil && resp.StatusCode == http.StatusOK { + return + } else if retryCnt < 1 { + //stopExtSvcs() + var reqError string + if err != nil { + reqError = err.Error() + } else { + reqError = fmt.Sprint(resp.StatusCode) + } + Logger.Error("Entrypoint critical svc check not passed: " + reqError) + os.Exit(1) + } + retryCnt-- + time.Sleep(time.Duration(waitTime) * time.Second) + } +} diff --git a/utils/secr_to_reg_unix.go b/utils/secr_to_reg_unix.go new file mode 100644 index 0000000..e64e7d1 --- /dev/null +++ b/utils/secr_to_reg_unix.go @@ -0,0 +1,7 @@ +//go:build !windows +// +build !windows + +package utils + +func addSecretsToReg(secrets map[string]string) { +} diff --git a/utils/secr_to_reg_windows.go b/utils/secr_to_reg_windows.go new file mode 100644 index 0000000..f1eec37 --- /dev/null +++ b/utils/secr_to_reg_windows.go @@ -0,0 +1,25 @@ +package utils + +import ( + "os" + + "golang.org/x/sys/windows/registry" +) + +func addSecretsToReg(secrets map[string]string) { + k, _, err := registry.CreateKey(registry.LOCAL_MACHINE, `System\CurrentControlSet\Control\Session Manager\Environment`, registry.CREATE_SUB_KEY|registry.SET_VALUE) + if err != nil { + Logger.Error("Entrypoint failed to create a key in registry: " + err.Error()) + os.Exit(1) + } + for name, secret := range secrets { + if err := k.SetStringValue(name, secret); err != nil { + Logger.Error("Entrypoint failed to set kv in registry: " + err.Error()) + os.Exit(1) + } + } + if err := k.Close(); err != nil { + Logger.Error("Entrypoint failed to close registry: " + err.Error()) + os.Exit(1) + } +} diff --git a/utils/secrets.go b/utils/secrets.go new file mode 100644 index 0000000..d3e597d --- /dev/null +++ b/utils/secrets.go @@ -0,0 +1,98 @@ +package utils + +import ( + "encoding/json" + "os" + "regexp" + "runtime" + + "github.com/PaesslerAG/jsonpath" +) + +func (vaultStruct *vaultStruct) getSecretsEnvList(secretsFromSource *map[string]string) []string { + + // Convert the secrets to a slice of strings. + secretsEnvList := []string{} + + if len(*secretsFromSource) > 0 { + secretsEnvMap := make(map[string]string) + + for secretPath, secretEnvVarNamePrefix := range *secretsFromSource { + getSecret := vaultStruct.readSecretVault(secretPath) + for secretKey, secretValue := range getSecret { + finalSecretEnvVar := secretEnvVarNamePrefix + "_" + secretKey + Logger.Debug("Entrypoint got secret " + finalSecretEnvVar) + secretsEnvList = append(secretsEnvList, finalSecretEnvVar+"="+secretValue.(string)) + secretsEnvMap[finalSecretEnvVar] = secretValue.(string) + } + } + + if runtime.GOOS == "windows" { + addSecretsToReg(secretsEnvMap) + } + } + + return secretsEnvList +} + +func readSecretsfromSource(secretsFile string, secretsJsonPath string) *map[string]string { + + // invalidRegexp is a regexp for invalid characters in keys + var invalidRegexp = regexp.MustCompile(`[^a-zA-Z0-9_]`) + + // Open menifest json from platform inventory folder + piManifestByte, err := os.ReadFile(secretsFile) + if err != nil { + Logger.Error("entrypoint failed read json with secrets: " + err.Error()) + os.Exit(1) + } + + var secretsStrings = make(map[string]string) + + // Get the path to the secrets field. + path := "$." + secretsJsonPath + + var manifest interface{} + json.Unmarshal(piManifestByte, &manifest) + secrets, err := jsonpath.Get(path, manifest) + if err != nil { + Logger.Warn("Entrypoint failed with specified path in json: " + err.Error()) + } else { + + for _, value := range secrets.([]interface{}) { + switch v := value.(type) { + case string: + fixedSecretPath := invalidRegexp.ReplaceAllString(v, "_") + //fixedSecretPath := strings.ReplaceAll(v, "/", "_") + //fixedSecretPath = strings.ReplaceAll(fixedSecretPath, "-", "_") + finalSecretEnvVarPrefix := "secret_" + fixedSecretPath + secretsStrings[v] = finalSecretEnvVarPrefix + case map[string]interface{}: + if secretPathString, ok := v["secretname"].(string); ok { + var finalSecretEnvVarPrefix string + + if secretnameString, ok := v["envvarname"].(string); ok { + finalSecretEnvVarPrefix = secretnameString + } else { + fixedSecretPath := invalidRegexp.ReplaceAllString(secretPathString, "_") + //fixedSecretPath := strings.ReplaceAll(secretPathString, "/", "_") + //fixedSecretPath = strings.ReplaceAll(fixedSecretPath, "-", "_") + finalSecretEnvVarPrefix = "secret_" + fixedSecretPath + } + + secretsStrings[secretPathString] = finalSecretEnvVarPrefix + + } else { + Logger.Error("Entrypoint wrong secrets list, secretname does not exists or not string") + os.Exit(1) + } + default: + Logger.Error("Entrypoint wrong secrets list") + os.Exit(1) + } + } + + } + + return &secretsStrings +} diff --git a/utils/structures.go b/utils/structures.go new file mode 100644 index 0000000..7d9d658 --- /dev/null +++ b/utils/structures.go @@ -0,0 +1,7 @@ +package utils + +import vault "github.com/hashicorp/vault/api" + +type vaultStruct struct { + vaultClient *vault.Client +} diff --git a/utils/vault-client-go.go.future b/utils/vault-client-go.go.future new file mode 100644 index 0000000..3f22275 --- /dev/null +++ b/utils/vault-client-go.go.future @@ -0,0 +1,55 @@ +package main + +import ( + "context" + "os" + "time" + + "github.com/hashicorp/vault-client-go" + "github.com/hashicorp/vault-client-go/schema" +) + +func initVaultClient() *vault.Client { + // prepare a client with the given base address + client, err := vault.New( + vault.WithAddress(os.Getenv("VAULT_ADDR")), + vault.WithRequestTimeout(10*time.Second), + ) + + if err != nil { + logger.Error(err.Error()) + os.Exit(1) + } + + resp, err := client.Auth.AppRoleLogin( + context.Background(), + schema.AppRoleLoginRequest{ + RoleId: os.Getenv("VAULT_APPROLE_RID"), + SecretId: os.Getenv("VAULT_APPROLE_SID"), + }, + ) + if err != nil { + logger.Error(err.Error()) + os.Exit(1) + } + + if err := client.SetToken(resp.Auth.ClientToken); err != nil { + logger.Error(err.Error()) + os.Exit(1) + } + + return client +} + +func readSecretVault(client *vault.Client, secretName string) map[string]interface{} { + + secretResp, err := client.Read(context.Background(), "/secret/"+secretName) + if err != nil { + logger.Error(err.Error()) + os.Exit(1) + } + + data := secretResp.Data + + return data +} diff --git a/utils/vault.go b/utils/vault.go new file mode 100644 index 0000000..e451629 --- /dev/null +++ b/utils/vault.go @@ -0,0 +1,54 @@ +package utils + +import ( + "context" + "os" + + vault "github.com/hashicorp/vault/api" + auth "github.com/hashicorp/vault/api/auth/approle" +) + +func (VaultStruct *vaultStruct) initVaultClient(vaultAddr string) { + config := vault.DefaultConfig() + config.Address = vaultAddr + + client, err := vault.NewClient(config) + if err != nil { + Logger.Error("Entrypoint failed create Vault client: " + err.Error()) + os.Exit(1) + } + + appRoleAuth, err := auth.NewAppRoleAuth( + os.Getenv("VAULT_APPROLE_RID"), + &auth.SecretID{FromEnv: "VAULT_APPROLE_SID"}, + ) + if err != nil { + Logger.Error("Entrypoint failed to create AppRoleAuth: " + err.Error()) + os.Exit(1) + } + + authInfo, err := client.Auth().Login(context.Background(), appRoleAuth) + if err != nil { + Logger.Error("Entrypoint failed to login to Vault with appRoleAuth: " + err.Error()) + os.Exit(1) + } + if authInfo == nil { + Logger.Error("Entrypoint failed empty authInfo: " + err.Error()) + os.Exit(1) + } + + VaultStruct.vaultClient = client +} + +func (vaultStruct *vaultStruct) readSecretVault(secretPath string) map[string]interface{} { + + secretResp, err := vaultStruct.vaultClient.KVv1("secret").Get(context.Background(), secretPath) //client.Read(context.Background(), "/secret/"+secretName) + if err != nil { + Logger.Error("Entrypoint failed to get secret from Vault: " + err.Error()) + os.Exit(1) + } + + data := secretResp.Data + + return data +}