From 481e1898f356eead0bb54c9dbd581afa8a7fb7a2 Mon Sep 17 00:00:00 2001 From: Nick Moore Date: Tue, 23 Dec 2025 11:01:11 +0000 Subject: [PATCH 1/3] Initial attempt at fetching secrets from Vault KVv2 --- go.mod | 18 ++++++++++++- go.sum | 42 ++++++++++++++++++++++++++--- pkg/env.go | 7 +++++ pkg/main.go | 18 +++++++++++++ pkg/secrets.go | 64 ++++++++++++++++++++++++++++++++++++++++++++- pkg/secrets_test.go | 12 +++++++++ 6 files changed, 155 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 872818e..3e184dc 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,20 @@ require ( github.com/stretchr/testify v1.11.1 ) +require ( + github.com/aws/aws-sdk-go v1.55.7 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/go-jose/go-jose/v4 v4.1.1 // indirect + github.com/hashicorp/go-retryablehttp v0.7.8 // indirect + github.com/hashicorp/go-secure-stdlib/awsutil v0.3.0 // indirect + github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 // indirect + github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect + github.com/hashicorp/go-uuid v1.0.3 // indirect + github.com/hashicorp/hcl v1.0.1-vault-7 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/ryanuber/go-glob v1.0.0 // indirect +) + require ( dario.cat/mergo v1.0.1 // indirect github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect @@ -85,6 +99,8 @@ require ( github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/hashicorp/memberlist v0.5.3 // indirect github.com/hashicorp/serf v0.10.1 // indirect + github.com/hashicorp/vault/api v1.22.0 + github.com/hashicorp/vault/api/auth/aws v0.11.0 github.com/huandu/xstrings v1.5.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -142,7 +158,7 @@ require ( golang.org/x/sync v0.18.0 // indirect golang.org/x/sys v0.38.0 // indirect golang.org/x/text v0.31.0 // indirect - golang.org/x/time v0.11.0 // indirect + golang.org/x/time v0.12.0 // indirect golang.org/x/tools v0.38.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect diff --git a/go.sum b/go.sum index 7e5dbf7..264bf91 100644 --- a/go.sum +++ b/go.sum @@ -53,8 +53,9 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-lambda-go v1.50.0 h1:0GzY18vT4EsCvIyk3kn3ZH5Jg30NRlgYaai1w0aGPMU= github.com/aws/aws-lambda-go v1.50.0/go.mod h1:dpMpZgvWx5vuQJfBt0zqBha60q7Dd7RfgJv23DymV8A= -github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= -github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go v1.34.0/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE= +github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.41.0 h1:tNvqh1s+v0vFYdA1xq0aOJH+Y5cRyZ5upu6roPgPKd4= github.com/aws/aws-sdk-go-v2 v1.41.0/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 h1:489krEF9xIGkOaaX3CE/Be2uWjiXrkCH6gUX+bZA/BU= @@ -110,6 +111,8 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 h1:6lhrsTEnloDPXyeZBvSYvQf8u86jbKehZPVDDlkgDl4= github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -160,6 +163,8 @@ github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/ github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-jose/go-jose/v4 v4.1.1 h1:JYhSgy4mXXzAdF3nUx3ygx347LRXJRrpgyU3adRmkAI= +github.com/go-jose/go-jose/v4 v4.1.1/go.mod h1:BdsZGqgdO3b6tTc6LSE56wcDbMMLuPsw5d4ZD5f94kA= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= @@ -183,7 +188,10 @@ github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-redsync/redsync/v4 v4.13.0 h1:49X6GJfnbLGaIpBBREM/zA4uIMDXKAh1NDkvQ1EkZKA= github.com/go-redsync/redsync/v4 v4.13.0/go.mod h1:HMW4Q224GZQz6x1Xc7040Yfgacukdzu7ifTDAKiyErQ= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= +github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= @@ -268,6 +276,7 @@ github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv github.com/hashicorp/go-cleanhttp v0.5.0/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 v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -285,8 +294,16 @@ github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+ 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.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= +github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= 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/awsutil v0.3.0 h1:I8bynUKMh9I7JdwtW9voJ0xmHvBpxQtLjrMFDYmhOxY= +github.com/hashicorp/go-secure-stdlib/awsutil v0.3.0/go.mod h1:oKHSQs4ivIfZ3fbXGQOop1XuDfdSb8RIsWTGaAanSfg= +github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 h1:U+kC2dOhMFQctRfhK0gRctKAPTloZdMU5ZJxaesJ/VM= +github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0= +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.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw= github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw= @@ -300,6 +317,8 @@ github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.1-vault-7 h1:ag5OxFVy3QYTFTJODRzTKVZ6xvdfLLCA1cy/Y6xGI0I= +github.com/hashicorp/hcl v1.0.1-vault-7/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= @@ -307,10 +326,17 @@ github.com/hashicorp/memberlist v0.5.3 h1:tQ1jOCypD0WvMemw/ZhhtH+PWpzcftQvgCorLu github.com/hashicorp/memberlist v0.5.3/go.mod h1:h60o12SZn/ua/j0B6iKAZezA4eDaGsIuPO70eOaJ6WE= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= +github.com/hashicorp/vault/api v1.22.0 h1:+HYFquE35/B74fHoIeXlZIP2YADVboaPjaSicHEZiH0= +github.com/hashicorp/vault/api v1.22.0/go.mod h1:IUZA2cDvr4Ok3+NtK2Oq/r+lJeXkeCrHRmqdyWfpmGM= +github.com/hashicorp/vault/api/auth/aws v0.11.0 h1:lWdUxrzvPotg6idNr62al4w97BgI9xTDdzMCTViNH2s= +github.com/hashicorp/vault/api/auth/aws v0.11.0/go.mod h1:PWqdH/xqaudapmnnGP9ip2xbxT/kRW2qEgpqiQff6Gc= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -330,6 +356,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -448,9 +475,12 @@ github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRl github.com/redis/rueidis v1.0.19 h1:s65oWtotzlIFN8eMPhyYwxlwLR1lUdhza2KtWprKYSo= github.com/redis/rueidis v1.0.19/go.mod h1:8B+r5wdnjwK3lTFml5VtxjzGOQAC+5UmujoD12pDrEo= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/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/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sercand/kuberesolver/v5 v5.1.1 h1:CYH+d67G0sGBj7q5wLK61yzqJJ8gLLC8aeprPTHb6yY= @@ -478,6 +508,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= @@ -572,6 +603,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= @@ -638,8 +670,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= -golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= -golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -702,10 +734,12 @@ google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9x google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/pkg/env.go b/pkg/env.go index f62463b..377fa13 100644 --- a/pkg/env.go +++ b/pkg/env.go @@ -11,6 +11,9 @@ import ( type secretFetcher interface { FetchFromAWSSecretsManager(ctx context.Context, secretArn string) (string, error) FetchFromAWSSSMParameterStore(ctx context.Context, parameterArn string) (string, error) + FetchFromVault(ctx context.Context, key string) (string, error) + HasVaultConfig() bool + SetVaultConfig(config *VaultKVCredentials) } func loadSensitiveEnv(ctx context.Context, secrets secretFetcher, name string) (string, error) { @@ -19,6 +22,10 @@ func loadSensitiveEnv(ctx context.Context, secrets secretFetcher, name string) ( return "", nil } + if secrets.HasVaultConfig() { + return secrets.FetchFromVault(ctx, envValue) + } + if arn.IsARN(envValue) { parsedArn, err := arn.Parse(envValue) if err != nil { diff --git a/pkg/main.go b/pkg/main.go index a822f55..9de2eea 100644 --- a/pkg/main.go +++ b/pkg/main.go @@ -49,6 +49,24 @@ func setupArguments(ctx context.Context, secretFetcher secretFetcher) { panic(errors.New("required environmental variable WRITE_ADDRESS not present, format: https:///loki/api/v1/push")) } + vaultRole, ok := os.LookupEnv("VAULT_ROLE") + if ok { + fmt.Println("using Vault configuration with role: ", vaultRole) + vaultMount, ok := os.LookupEnv("VAULT_MOUNT") + if !ok { + panic(errors.New("VAULT_ROLE provided, but required environment variable VAULT_MOUNT not provided")) + } + vaultPath, ok := os.LookupEnv("VAULT_PATH") + if !ok { + panic(errors.New("VAULT_ROLE provided, but required environment variable VAULT_PATH not provided")) + } + secretFetcher.SetVaultConfig(&VaultKVCredentials{ + role: vaultRole, + mount: vaultMount, + path: vaultPath, + }) + } + var err error writeAddress, err = url.Parse(addr) if err != nil { diff --git a/pkg/secrets.go b/pkg/secrets.go index 74a96b2..e541aa2 100644 --- a/pkg/secrets.go +++ b/pkg/secrets.go @@ -2,17 +2,29 @@ package main import ( "context" + "errors" "fmt" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/secretsmanager" "github.com/aws/aws-sdk-go-v2/service/ssm" "github.com/aws/smithy-go/ptr" + vault "github.com/hashicorp/vault/api" + auth "github.com/hashicorp/vault/api/auth/aws" ) +type VaultKVCredentials struct { + role string + mount string + path string +} + var _ secretFetcher = &secretClients{} -type secretClients struct{} +type secretClients struct { + vaultConfig *VaultKVCredentials + vaultData map[string]any +} func (c *secretClients) FetchFromAWSSecretsManager(ctx context.Context, secretArn string) (string, error) { cfg, err := config.LoadDefaultConfig(ctx) @@ -48,3 +60,53 @@ func (c *secretClients) FetchFromAWSSSMParameterStore(ctx context.Context, param return *out.Parameter.Value, nil } + +func (c *secretClients) FetchFromVault(ctx context.Context, key string) (string, error) { + if c.vaultData == nil { + if c.vaultConfig == nil { + return "", errors.New("vault configuration required") + } + config := vault.DefaultConfig() + + client, err := vault.NewClient(config) + if err != nil { + return "", fmt.Errorf("unable to initialize Vault client: %w", err) + } + + awsAuth, err := auth.NewAWSAuth( + auth.WithRole(c.vaultConfig.role), // if not provided, Vault will fall back on looking for a role with the IAM role name if you're using the iam auth type, or the EC2 instance's AMI id if using the ec2 auth type + ) + if err != nil { + return "", fmt.Errorf("unable to initialize AWS auth method: %w", err) + } + + authInfo, err := client.Auth().Login(ctx, awsAuth) + if err != nil { + return "", fmt.Errorf("unable to login to AWS auth method: %w", err) + } + if authInfo == nil { + return "", fmt.Errorf("no auth info was returned after login") + } + + data, err := client.KVv2(c.vaultConfig.mount).Get(ctx, c.vaultConfig.path) + if err != nil { + return "", fmt.Errorf("unable to read secret: %w", err) + } + c.vaultData = data.Data + } + + value, ok := c.vaultData[key].(string) + if !ok { + return "", fmt.Errorf("value type assertion failed: %T %#v", c.vaultData[key], c.vaultData[key]) + } + + return value, nil +} + +func (c *secretClients) HasVaultConfig() bool { + return c.vaultConfig != nil +} + +func (c *secretClients) SetVaultConfig(config *VaultKVCredentials) { + c.vaultConfig = config +} diff --git a/pkg/secrets_test.go b/pkg/secrets_test.go index c9c3a7f..f35c300 100644 --- a/pkg/secrets_test.go +++ b/pkg/secrets_test.go @@ -37,3 +37,15 @@ func (c *testSecretsClient) FetchFromAWSSSMParameterStore(_ context.Context, par return c.ReturnValue, nil } + +func (c *testSecretsClient) FetchFromVault(ctx context.Context, key string) (string, error) { + return "", nil +} + +func (c *testSecretsClient) HasVaultConfig() bool { + return false +} + +func (c *testSecretsClient) SetVaultConfig(config *VaultKVCredentials) { + // TODO +} From dae604b0ee93bf04b5f5a2be84ec6ed3c997e6a4 Mon Sep 17 00:00:00 2001 From: Nick Moore Date: Tue, 23 Dec 2025 10:53:07 +0000 Subject: [PATCH 2/3] Implement basic unit tests for env --- pkg/env_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ pkg/secrets_test.go | 27 ++++++++++++++++++++------- 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/pkg/env_test.go b/pkg/env_test.go index 15d1265..8e35c8b 100644 --- a/pkg/env_test.go +++ b/pkg/env_test.go @@ -20,6 +20,7 @@ func Test_loadSensitiveEnv(t *testing.T) { assert.Equal(t, "BAR", value) assert.Equal(t, 0, secretsClient.CallsFetchFromAWSSecretsManager) assert.Equal(t, 0, secretsClient.CallsFetchFromAWSSSMParameterStore) + assert.Equal(t, 0, secretsClient.CallsFetchFromVault) }) t.Run("should not return an error if env is not set", func(t *testing.T) { @@ -31,6 +32,7 @@ func Test_loadSensitiveEnv(t *testing.T) { assert.Empty(t, value) assert.Equal(t, 0, secretsClient.CallsFetchFromAWSSecretsManager) assert.Equal(t, 0, secretsClient.CallsFetchFromAWSSSMParameterStore) + assert.Equal(t, 0, secretsClient.CallsFetchFromVault) }) t.Run("should return an error if the env variable contains an invalid arn", func(t *testing.T) { @@ -43,6 +45,7 @@ func Test_loadSensitiveEnv(t *testing.T) { assert.Empty(t, value) assert.Equal(t, 0, secretsClient.CallsFetchFromAWSSecretsManager) assert.Equal(t, 0, secretsClient.CallsFetchFromAWSSSMParameterStore) + assert.Equal(t, 0, secretsClient.CallsFetchFromVault) }) t.Run("should call FetchFromAWSSecretsManager if the env variable contains a secret ARN", func(t *testing.T) { @@ -57,6 +60,7 @@ func Test_loadSensitiveEnv(t *testing.T) { assert.Equal(t, "bar", value) assert.Equal(t, 1, secretsClient.CallsFetchFromAWSSecretsManager) assert.Equal(t, 0, secretsClient.CallsFetchFromAWSSSMParameterStore) + assert.Equal(t, 0, secretsClient.CallsFetchFromVault) }) t.Run("should call FetchFromAWSSSMParameterStore if the env variable contains a parameter ARN", func(t *testing.T) { @@ -71,5 +75,42 @@ func Test_loadSensitiveEnv(t *testing.T) { assert.Equal(t, "bar", value) assert.Equal(t, 0, secretsClient.CallsFetchFromAWSSecretsManager) assert.Equal(t, 1, secretsClient.CallsFetchFromAWSSSMParameterStore) + assert.Equal(t, 0, secretsClient.CallsFetchFromVault) + }) + + t.Run("should call FetchFromVault if Vault is configured", func(t *testing.T) { + t.Setenv("FOO", "vault_key") + secretsClient := &testSecretsClient{ + VaultConfigured: true, + ExpectedArn: "vault_key", + ReturnValue: "bar", + } + secretsClient.SetVaultConfig(&VaultKVCredentials{ + role: "role_foo", + mount: "mnt_bar", + path: "path_name", + }) + + value, err := loadSensitiveEnv(ctx, secretsClient, "FOO") + assert.NoError(t, err) + assert.Equal(t, "bar", value) + assert.Equal(t, 0, secretsClient.CallsFetchFromAWSSecretsManager) + assert.Equal(t, 0, secretsClient.CallsFetchFromAWSSSMParameterStore) + assert.Equal(t, 1, secretsClient.CallsFetchFromVault) + }) + + t.Run("should return an error if Vault is intended to be used but the Vault config is not set", func(t *testing.T) { + t.Setenv("FOO", "vault_key") + secretsClient := &testSecretsClient{ + VaultConfigured: true, + } + + value, err := loadSensitiveEnv(ctx, secretsClient, "FOO") + + assert.Error(t, err) + assert.Empty(t, value) + assert.Equal(t, 0, secretsClient.CallsFetchFromAWSSecretsManager) + assert.Equal(t, 0, secretsClient.CallsFetchFromAWSSSMParameterStore) + assert.Equal(t, 1, secretsClient.CallsFetchFromVault) }) } diff --git a/pkg/secrets_test.go b/pkg/secrets_test.go index f35c300..e6dfbd2 100644 --- a/pkg/secrets_test.go +++ b/pkg/secrets_test.go @@ -6,16 +6,20 @@ import ( ) var ( - _ secretFetcher = &testSecretsClient{} - errInvalidArn = errors.New("invalid arn") + _ secretFetcher = &testSecretsClient{} + errInvalidArn = errors.New("invalid arn") + errNoVaultConfig = errors.New("no vault config") ) type testSecretsClient struct { CallsFetchFromAWSSecretsManager int CallsFetchFromAWSSSMParameterStore int + CallsFetchFromVault int - ExpectedArn string - ReturnValue string + ExpectedArn string + ReturnValue string + VaultConfigured bool + VaultCredentials *VaultKVCredentials } func (c *testSecretsClient) FetchFromAWSSecretsManager(_ context.Context, secretArn string) (string, error) { @@ -39,13 +43,22 @@ func (c *testSecretsClient) FetchFromAWSSSMParameterStore(_ context.Context, par } func (c *testSecretsClient) FetchFromVault(ctx context.Context, key string) (string, error) { - return "", nil + c.CallsFetchFromVault++ + + if c.VaultCredentials == nil { + return "", errNoVaultConfig + } + if c.ExpectedArn != "" && key != c.ExpectedArn { + return "", errInvalidArn + } + + return c.ReturnValue, nil } func (c *testSecretsClient) HasVaultConfig() bool { - return false + return c.VaultConfigured } func (c *testSecretsClient) SetVaultConfig(config *VaultKVCredentials) { - // TODO + c.VaultCredentials = config } From 46b34fecccc47dfd7d1da9e4a1ae01d1aea2c42f Mon Sep 17 00:00:00 2001 From: Nick Moore Date: Tue, 23 Dec 2025 11:13:59 +0000 Subject: [PATCH 3/3] Mark context as unused in test --- pkg/secrets_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/secrets_test.go b/pkg/secrets_test.go index e6dfbd2..c43dee4 100644 --- a/pkg/secrets_test.go +++ b/pkg/secrets_test.go @@ -42,7 +42,7 @@ func (c *testSecretsClient) FetchFromAWSSSMParameterStore(_ context.Context, par return c.ReturnValue, nil } -func (c *testSecretsClient) FetchFromVault(ctx context.Context, key string) (string, error) { +func (c *testSecretsClient) FetchFromVault(_ context.Context, key string) (string, error) { c.CallsFetchFromVault++ if c.VaultCredentials == nil {