diff --git a/.env.enc b/.env.enc index 21ab2b1..7e90eb9 100644 --- a/.env.enc +++ b/.env.enc @@ -1,32 +1,26 @@ -DB_USER=ENC[AES256_GCM,data:ZuTXZnCIDMObGViREkvE5A==,iv:o/MCq/5KjtaocJMIdf239BqPZpbEiBEW7jKeyljmgwM=,tag:YTWwkCerN9hQoA591v25ww==,type:str] -DB_PASSWORD=ENC[AES256_GCM,data:FQn8WEEAggYtWel0Y4KHDA==,iv:Z8IjzQB/6Jn0P4g1ZU5SSZYvH2vFBU9N26T41AGCndk=,tag:NjZgBOvHRkybASe6PvaGbw==,type:str] -DB_NAME=ENC[AES256_GCM,data:DMDfyT5cXdh6ygzzgoOvFA==,iv:DxiUqQcgUSINhxdXHLKBuHD7k265JLW+X1w7rbPFbiM=,tag:TtFH8EthkWyUJVDcChAoOQ==,type:str] -DB_PORT=ENC[AES256_GCM,data:MxLXFw==,iv:+f7E0r9NEwsA1h9ZUFz9I9LFB8PDSXNZsRyZ0KrGQVk=,tag:FLae3fAWy5/ArnysJ1sgDg==,type:str] -REDIS_PORT=ENC[AES256_GCM,data:rA0BOA==,iv:cqEPtzAN5wcGsGgQp/bD2bPoW0nXhgzbnCk7k4YevHk=,tag:8pgcyAPDkUowrnjh79joiw==,type:str] -FRONTEND_PORT=ENC[AES256_GCM,data:j/4zww==,iv:lvrmXGAX8xrIPDy6Hnx7gLPg0YqGVitoBuGnmsxHqIg=,tag:+64/LuGZw/3PmqpcQP/6Mw==,type:str] -NEXT_PUBLIC_API_URL=ENC[AES256_GCM,data:rNSLpY3/KlPjIr8dJCttIDeNZ8De,iv:bUkNYfawC2vxFkApDHRNvuCFJjh0z5C70RqjCWTfW2U=,tag:OEbhBtz44kUi8Rn0ZsZDIA==,type:str] -NEXT_PUBLIC_WS_URL=ENC[AES256_GCM,data:vYq8w9XKpg14nW0aKrMLav9yxOQ95Q==,iv:JE3eu7hzYi8ic6PcH1zgLuqUXuSmPMHHkn5mBjYrbgs=,tag:G9LiirwL3Sr3eOx9q3Yrng==,type:str] -BACKEND_PORT=ENC[AES256_GCM,data:8ZazJw==,iv:CbHg3xSKdvqXJsWFAVkXIp5yZ2nxzagbIEMIH2KiLQE=,tag:QE6KgJiRy7SRxxdhvjdlJA==,type:str] -SPRING_PROFILE=ENC[AES256_GCM,data:t8kX,iv:PPqdggMykBjRSBsZJ/uTAOHHFdyYR13JZbDBAt2fxjA=,tag:ZriOUlkShkyp05sH9SI+xg==,type:str] -JWT_SECRET=ENC[AES256_GCM,data:yy72mAUDQ3IWrZU+dwoQW+UOM/Y=,iv:olGp+ADjXJwGoayVD3vtnI603ztFvKI+FmRXzAvWklI=,tag:1oNFi5N8eB23CYH8AH0Zpg==,type:str] -SPRING_JPA_HIBERNATE_DDL_AUTO=ENC[AES256_GCM,data:RciRsocc,iv:gb8PjuBC5qkymU4K/fiHQPuqum3RzWdOgBVHH9lH7Ts=,tag:6q7pQlk3jtW/gXvPPtbnPQ==,type:str] -PGADMIN_PORT=ENC[AES256_GCM,data:5bl80A==,iv:eJlyu9vGtXg1iNWS+nDkmvLWEhBOrXw73wYX/TcGdUY=,tag:B1IXGZNiObGoHdvKKCyHRg==,type:str] -PGADMIN_EMAIL=ENC[AES256_GCM,data:BR2bfU5LVuTSppwvfEvm,iv:jNQSh5U4yHQVUcXxWeTz7uEHt/7EYHV4eudW/qLc2mY=,tag:1Q4vKxfYfQ2mTfSzZd247Q==,type:str] -PGADMIN_PASSWORD=ENC[AES256_GCM,data:tVn20EM=,iv:5QY1NjmQekvbxoZ6i4/G2yvzHfq5byWVFnDy/q5B+gQ=,tag:6Wp2/1iR4gnVnd+wlvHiaQ==,type:str] -NEXT_PUBLIC_SUPABASE_URL=ENC[AES256_GCM,data:+PeLo+V+EWwcHVTnFBFT4syMGQtmgZff5UAD31lRBIexLbIlPvvGDA==,iv:/8A/aYoKi0kdrdARUOw2jRCCTDof7y/nll9NhvnjGFw=,tag:AA/snF/wOLvGD1ZCCAq1zQ==,type:str] -NEXT_PUBLIC_SUPABASE_ANON_KEY=ENC[AES256_GCM,data:IygcPTFrpwbvJDrosT1koFRLl1HzTQkqAoJ4WmMGUZW0N+ISdkCyyDArS6xp0+QElN04eigi4GVc55oiU7ojX2bODgwu8QdVUxo9YPWohcsNdmFwfClr7Y2b/+dCW2UFnsdbmr2o77mHPF1ainw/E+uDC1mHQ/Vd5J7jbRriAy9WodhjypptUszGdjR2cY4n3eZ0SWHpbtb1KeXevrPEWUssODvVOA8yxDHsZ3grFnAysK9Y/ZapeApypYh4qgAzCy9pYTqAmidYuYfCxaXb+FHP,iv:29F9/qg9GlJmN3cntiMY1HSszTsBM2gfM5h5O1+Rllk=,tag:Hq17D8k+VIqGZLQCS99ncw==,type:str] -SUPABASE_SERVICE_ROLE_KEY=ENC[AES256_GCM,data:+X3A8DOhf3+kBUe1fUcT5kzIltDGajvQgK0FrMkTHCxDxOYuziyL8Vm4K0yTs8UK4DzPmXiwmPPYwQkK+P1oV/BMOPCpPI/RMMSbV+BrFueNR5wTBpZhwaDGNYchomRfRbsjv1fadr3wNI93uikGipjpXce07EfotMVGIgAFWRjQEhNgfP/L7z+H7Lq7TmPJSZtumM9neEOQnzvaaNxLU3bWplLOrv5TMuRkudj+Ur8RL7Iuq7yqOZOtd5W34aekqV6SzmDfU7jtS9Cktd4F7qXryzx9X+cLvpGFMps=,iv:oiRF14Qmu+Cpd6DjkjYpeKK+oS9Lm3PyuYUx/QXOF9A=,tag:6iNaMcK7ceQc0bNOXhLDQA==,type:str] -SUPABASE_JWT_SECRET=ENC[AES256_GCM,data:GYXfoeot4vk4lpNOZVMjDukD/vpq3zA74qVW+W7Z3AJ7QI43anS8geS48x3cHJSOS81e+vRYyXb2GZH7XhWiM1eMLLLPRJlXaV3XvV+yANP71kAdzzSrbPCU,iv:wF8aIsUTWnM+enWdaJ4VTtZjUxvdnBN19tkdGGUBaYg=,tag:KKJb5HRd4XBPpMaooRDGWg==,type:str] -sops_lastmodified=2025-06-28T05:07:23Z -sops_mac=ENC[AES256_GCM,data:v0iZaxDog6gn9LznoX/dj6Hsa06is7BaI1WA39RuUeuVzlkXacUzSPoM6Mp3dh3jSdkMruNvAm9Or/u035MmQoUu2uQgzy70UyP9k9t35HcrMxlZ2xAvxUMkkEcuzOS/jkT43qikXS+iclu5pP3N7EAgM2qn5cNJ9ZpuTAB2sr0=,iv:RZdfnixevHfVJlPrRDHq7Qzqp/1J3Y2v9SCMqA+rnB4=,tag:FUh4hxTpoz7/E+U/y+55MQ==,type:str] -sops_pgp__list_0__map_created_at=2025-06-28T03:10:57Z -sops_pgp__list_0__map_enc=-----BEGIN PGP MESSAGE-----\n\nhF4DSt16dI0PNiISAQdAJnyGeAm/ZneDUrTRukvLhwHshMxJ0AYwz7oQD7LiunYw\nKbLEg/YRRawB5ARiW9hvQo9d+gBYxaByNvuE0OJ9v/nJLXf5K9NoEU1/tf3f41kc\n1GYBCQIQQQ6qRcTqA5+PtrEd6UOrx8L/JPskywb3HogBsBml9o7FZ9KwgLp2DPLw\n5K3mIauAtqHWn0RDw9kkaIhhahuhxBTox5vNPWMi6lcJN22TmWmT7dRAoE8Lf3Hv\n81YCDVNFwh0=\n=7p6t\n-----END PGP MESSAGE----- -sops_pgp__list_0__map_fp=A42113A260AFDA3A -sops_pgp__list_1__map_created_at=2025-06-28T05:00:17Z -sops_pgp__list_1__map_enc=-----BEGIN PGP MESSAGE-----\n\nhF4DD2ITbbO4/MESAQdA7Cw4ms6XFsk5GF58azUd791DQEQBp8kegb98X+sz+1gw\nqaZiQc6yUqKJ8CLIcNkANrBIRx4bL92giIMDDJCwK1Z3IQxBYQgg97LAitps9+jk\n1GYBCQIQ0fX5o+LZKeahrLy6M0dHmwuPESmL8lMhVSegcZj7NXvU0zKlm2Fu/ZR7\n6lwduoJ+YZHncGd4W5Lu8cLAcNG1PHCBlZ+/GU09EI9m8JpBCu5mEARpVGRbv1c+\n7yqEwVrUNTg=\n=9pza\n-----END PGP MESSAGE----- -sops_pgp__list_1__map_fp=A603931ABDBB8AB72C97AE07ED479F8459ACCFDD -sops_pgp__list_2__map_created_at=2025-06-28T05:07:22Z -sops_pgp__list_2__map_enc=-----BEGIN PGP MESSAGE-----\n\nhF4DugffLwUcW/ESAQdArShOHLcofBL8P+Q7BXc3F72eRcR5GcQngJFdvjFngz8w\nJLPFirGj3qvlgNW95HWHzX/FCJEjsizfLOgmKOf/YCXToTj/+vuMezC1CJA/8Sx3\n1GYBCQIQMA5mKwiYJuaN8vy1U3bb3bM2L9GdlQ5xRN2QYyPlN0TMO8sgNXxp6XKM\nQVvKHTbQGXjtHGA0ISGBEWLZls32qxeJYvh6+lnw4BxKGZk0h04kQRWytFIl/lb3\nWJuyVSUUpgs=\n=PlNl\n-----END PGP MESSAGE----- -sops_pgp__list_2__map_fp=BB58AF94309753F6CDF13B57A9C2279290596F20 -sops_unencrypted_suffix=_unencrypted +DB_USER=ENC[AES256_GCM,data:dNWbJtvYBLGu4cqEdLop2d8=,iv:FcKghlD8V/SAkIPI373mJbyV/uJuneblOwuH4Rr/Bsc=,tag:u5pOdEzzdjvnd+NqqUUMWA==,type:str] +DB_PASSWORD=ENC[AES256_GCM,data:63k982x4Uyed6q0Z9eGjYkM=,iv:MZMnLgxHVVjayzCOyn6iOs5w7d3wHgWGxHz3KLlYjYA=,tag:eCcFgaXCJ8rtuibMCyHNFg==,type:str] +DB_NAME=ENC[AES256_GCM,data:uKxkH3WT6XKFm7qe88erPJ8=,iv:l0V7OfSo7U+cmM41hnFQLVEAmMHMxrCCWcCKAI6PCbw=,tag:TRlibqRtH/YHYPedrQRZzQ==,type:str] +DB_PORT=ENC[AES256_GCM,data:CyeDvso=,iv:l4g1rBSR/GllUMRL9kjurQPgT7vNnGIx4zMAHw+gSuo=,tag:wrl2f14xATY0Nf5vdpXL3w==,type:str] +REDIS_PORT=ENC[AES256_GCM,data:bnFoICA=,iv:6ikTsuTfi17iEkpcrvarMHJ1Mu0QxLmbZXB8FsDMeck=,tag:iN7R6lL8onA3Jpe/YQoNVw==,type:str] +FRONTEND_PORT=ENC[AES256_GCM,data:Z9F9wx0=,iv:KMj373jm9CuOy6YIVqCIMHJBp1Z2iWxfKYLMAp7MB5I=,tag:8CrYpW+NW70Xv/Cyst40Ag==,type:str] +NEXT_PUBLIC_API_URL=ENC[AES256_GCM,data:nDuR51aMZn/1E6ImmlUMT4VUcWXKbQ==,iv:3DIK2RHiLc3dzriYBY11FAk5S6vm8GQPTApC7jNnupo=,tag:Bb9m1xt+5D4g3VBETAo2oA==,type:str] +NEXT_PUBLIC_WS_URL=ENC[AES256_GCM,data:csoyrsCn4TwMWCdSckRzmSEPtLCtHJY=,iv:nosYULKst1lU1vwBcZ/nC4FnlxcKKkBATM22VKz9PgQ=,tag:JRNheQ4cgzc02gJQc8viVQ==,type:str] +BACKEND_PORT=ENC[AES256_GCM,data:wYHQ+Io=,iv:kNK63eN0unAWakgtTOlhFsfDXxxtSxD1ZQR6rMDYHUg=,tag:Pem+s6ZiqSwuAbMzMztmRw==,type:str] +SPRING_PROFILE=ENC[AES256_GCM,data:+Sw2FQ==,iv:bTZIYehAMQlhf4z2K8q+5Fm2tp2kY4QvWsI2vY03KJE=,tag:t4C8aWXI41kp1cZDytTFBQ==,type:str] +JWT_SECRET=ENC[AES256_GCM,data:oH/cdk3ef38Bhkk2N8UnVG0dkcJf,iv:2g4/qU0eGUTqy+6cQdUSxhe1P0DmSSRrltDIO31WRso=,tag:xdC9wpQ6oR7eL8y80CcB8w==,type:str] +SPRING_JPA_HIBERNATE_DDL_AUTO=ENC[AES256_GCM,data:XbTRZMyV/g==,iv:OFlxmt+RP6JjPAX/2o6EtD1u2RxS1T5XhbxE3kU7doA=,tag:KXjo60p75eZuc+c53UDcMQ==,type:str] +PGADMIN_PORT=ENC[AES256_GCM,data:E9dLtTM=,iv:wtSDQogRq9Q/30Q09pLfLw5UJB9WAP23oxEOxEl6KsU=,tag:idC5/4IT27zbzC5dPweESw==,type:str] +PGADMIN_EMAIL=ENC[AES256_GCM,data:WASPLvRzwjuUiBQpcOsHpQ==,iv:j9fm5i0Q1XAqi6ZRfzjMWBgwCHH5GeCamg4TfVUeijA=,tag:+5htvbptVc7zMpS4WYLAPg==,type:str] +PGADMIN_PASSWORD=ENC[AES256_GCM,data:uP/Mi00W,iv:qC2gaOJffcefiU7rMpdxt4okrnbgTJ5BPr0tvtgbhq0=,tag:pkyMuashbvNVofY3XaO4VA==,type:str] +NEXT_PUBLIC_SUPABASE_URL=ENC[AES256_GCM,data:GmRuqMJjk97u1NvmX/UGMNbRDflPcQU0YhYW7qaIAqqNX0iXY4bW5+M=,iv:liM5Dl4hKlSYjCh3ItvJVcGbm2osP/tbHn0A6OCArqs=,tag:G/ryZgCpkJFBGHL0LFs42A==,type:str] +NEXT_PUBLIC_SUPABASE_ANON_KEY=ENC[AES256_GCM,data:fE/XFzNRarL/fDfJhkoXznEWrcekuYwBGPGHwgwn8lq7mnd9wGrMByBYEQ91ElJGNKR5in36LwR/WF/sH5S7LWn/wdWCdvLaEYPvDQiz9ZLV4FpYw/DfZnxHbo8QbNXwrNJOrxE1dITAw580MKfPl3Rg145B2XLvRkpxpOIgYKJllG9AA+VzTm3DS7nGebrK8qWObHCa3vI+7U8qKtGzxoTOOGFTXcVUDZkms2XROMY6Mx6pNUA+INjQzbylYrQdo02Z8iXYuMsVe84xHrR7AR+8iA==,iv:+AF7gmo4Xp5eAhXXI/v+q983cXLd86U/laghpEKKFJw=,tag:/YSEclotJpTRfJsnk/iAQQ==,type:str] +SUPABASE_SERVICE_ROLE_KEY=ENC[AES256_GCM,data:YdRW/ePWPz6/BfRVJoHo8TJ5SlgaTiVbnv99X7QY6i70Xy9oMn6WSyWeVnORTp3i4xD6V8UTSYs3iYMTcXJUzg7rShQj2EakPSb4CP7gDpg5JpUQXKB+BSVsiY4BRx/6Sjm6hQ1VQUNz4AGt0RYTbXxv6yfMs4kJPmxcrOH2vs/upyCp6mdCpZwjKf+icoGG4VHIZysIFLj+etvbRDt3VpDEm6YOb3gVA/8WJVT61srbwJCYaFk4odJNTWp5V6kmvtvbHj61eN9z7rwnOy3rdU8cGma4ZPXSpoIUxNJS,iv:Z8DJFPDsuy+Y2GHOAJ2e67KbviCBKhjpy4bkOsIcdb0=,tag:BDjVTjTET6p43SVwJZRWCA==,type:str] +SUPABASE_JWT_SECRET=ENC[AES256_GCM,data:KdTilsYTjqEW7RDbGOIsoJkmlF6y0aqkLEGtk6CLBWGNlSlVrsqihhEWYHexTv0rZsH3ajSG9gr2pa+yXhFA6vi7CRP7SCd3owqjLVf71F6qqI5i4GW9cZ1puw==,iv:2BxhC3yJ2L4rg2pNYimVODxv+0+yxzPHZgvAke5cb7E=,tag:/8miDM8J1vv41V5CkzuUZA==,type:str] +sops_encrypted_regex=.* +sops_lastmodified=2025-06-28T20:01:43Z +sops_mac=ENC[AES256_GCM,data:O5fnFm1bKAY2lupMF2ysPvNCyDbYJGJGtC12rKmXF+SEF10jS3+17kOH5kSEbQBMh1A5rJ5Y045Ohut8sjSy3falA41ErVFLXWcUv4cyPcGOKiGVOeobam/3wK6BzJyVx4PZe9peRO2l+ki5uCger72LZ0i2mTk9R09ToJ5vyd4=,iv:3zDtkih451rLAjgP+22uxfK1lEMRBjgKPb2EfrsuYkA=,tag:LKOVRbnueRqtzHOArKYpOQ==,type:str] +sops_pgp__list_0__map_created_at=2025-06-28T20:01:43Z +sops_pgp__list_0__map_enc=-----BEGIN PGP MESSAGE-----\n\nhF4DugffLwUcW/ESAQdAiIz81O+kn/NlDCxPZXa3XsPhB9/nsjHFMPnE7oyZlXIw\nTU9wOylLugN3NEwDObxdQLbkcvvn9VGaCjLj119AyKalxT8oImo0bBa2Wt5RKwIN\n1GgBCQIQdqmPVEXjny0OQalPLZntaYweSVy4WsmkwNCNwRm1ZQMeU+Xqwp2tm5z/\nc/jafpS7aZr5Ivv46YK9zApOV2LzjBUvzdH8B6rOU2D7mvTmATCkmnh8NQVdlY1U\nIcjCFe3OeBxEWA==\n=3ema\n-----END PGP MESSAGE----- +sops_pgp__list_0__map_fp=BB58AF94309753F6CDF13B57A9C2279290596F20 sops_version=3.10.2 diff --git a/.github/workflows/sops-onboard.yml b/.github/workflows/sops-onboard.yml index e0a43d4..acd295a 100644 --- a/.github/workflows/sops-onboard.yml +++ b/.github/workflows/sops-onboard.yml @@ -1,4 +1,9 @@ # .github/workflows/sops-onboard.yml + +permissions: + contents: write + pull-requests: write + name: Refresh SOPS config on: @@ -25,29 +30,55 @@ jobs: sudo mv sops /usr/local/bin/ sops --version - # 3) Ensure your update script is executable - - name: Make update script executable - run: chmod +x scripts/update-sops.sh - - # 4) Capture the runner’s TTY for GPG + # 4) Capture the runner's TTY for GPG - id: tty - run: echo "::set-output name=tty::$(tty)" + run: echo "tty=$(tty)" >> $GITHUB_OUTPUT # 5) Import the CI GPG private key (no passphrase needed) - - name: Import CI’s GPG key + - name: Import CI's GPG key env: GPG_TTY: ${{ steps.tty.outputs.tty }} run: | - echo "${{ secrets.CI_GPG_PRIVATE }}" > ci.key.asc - gpg --batch --import ci.key.asc + echo "${{ secrets.CI_GPG_PRIVATE }}" \ + | gpg --batch --import + + - name: Import all teammates' public keys + shell: bash + run: | + shopt -s nullglob + + key_files=(keys/*.asc) + + if [ ${#key_files[@]} -eq 0 ]; then + echo "⚠️ No public keys found in keys/*.asc — skipping import." + exit 0 + fi + + for pub in "${key_files[@]}"; do + echo "🔑 Importing $pub" + gpg --batch --import "$pub" + done # 6) Rebuild .sops.yaml and re-encrypt .env → .env.enc - name: Rebuild SOPS config & re-encrypt - run: bash scripts/update-sops.sh + run: | + bash scripts/update-sops.sh # 7) Commit & push the changes back to main - - name: Commit & push changes - uses: stefanzweifel/git-auto-commit-action@v4 + - name: Create Pull Request with updated env + uses: peter-evans/create-pull-request@v5 with: - commit_message: "chore: onboard new teammate and re-encrypt env" - branch: main + # A descriptive branch name + branch: sops/update-env-${{ github.sha }} + # What base branch to target + base: main + title: "chore: re-encrypt env for new teammate" + body: | + This PR was auto-generated by the SOPS onboarding workflow. + It updates `.sops.yaml` and re-encrypts `.env.enc` to include the new + public key(s) under `keys/*.asc`. + # You can auto-assign reviewers or labels if you like: + reviewers: "lstsk" + labels: "ci,sops" + commit-message: "chore: re-encrypt env for new teammate" + skip-if-no-changes: true diff --git a/.gitignore b/.gitignore index 85ee0e0..ed36d3d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ # Environment files +**/.env .env .env.* # But keep encrypted env files and the example diff --git a/.sops.yaml b/.sops.yaml index 57242d4..a39280d 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -1,4 +1,4 @@ creation_rules: - - path_regex: '(^|/)\.env(\.enc)?$' - encrypted_regex: "^(?!#).*" + - path_regex: '.*\.env(\.enc)?$' + encrypted_regex: ".*" pgp: diff --git a/env.dec b/env.dec new file mode 100644 index 0000000..e69de29 diff --git a/scripts/encrypt.sh b/scripts/encrypt.sh new file mode 100644 index 0000000..c48e630 --- /dev/null +++ b/scripts/encrypt.sh @@ -0,0 +1,14 @@ +# Grab all fingerprints from keys/*.asc +FPS=$(for pub in keys/*.asc; do + gpg --with-colons --import-options show-only --import "$pub" \ + | awk -F: '/^fpr:/ {print $10; exit}' +done) + +# Encrypt each .env → .env.enc +find . -type f -name ".env" | while read -r ENV; do + OUT="${ENV}.enc" + echo "🔒 Encrypting $ENV → $OUT" + sops --encrypt --input-type dotenv --output-type dotenv \ + $(printf -- '--pgp %s ' $FPS) \ + "$ENV" > "$OUT" +done diff --git a/scripts/update-sops.sh b/scripts/update-sops.sh index 2dff5a9..ea895bf 100644 --- a/scripts/update-sops.sh +++ b/scripts/update-sops.sh @@ -1,35 +1,39 @@ #!/usr/bin/env bash set -euo pipefail -# Enable nullglob so missing files result in an empty array\shopt -s nullglob +# 1) Make globs vanish if they don’t match +shopt -s nullglob -ENV_FILE=".env.enc" +# 2) Gather current .asc fingerprints +declare -a current_fps=() +for pub in keys/*.asc; do + fp=$(gpg --with-colons --import-options show-only --import "$pub" \ + | awk -F: '/^fpr:/ {print $10; exit}') + if [[ -n "$fp" ]]; then + current_fps+=("$fp") + else + echo "⚠️ Could not extract fingerprint from $pub; skipping." + fi +done -# Exit early if encrypted file doesn't exist -if [ ! -f "$ENV_FILE" ]; then - echo "⚠️ No $ENV_FILE found; skipping recipient rotation." - exit 0 -fi +# 3) Load existing fingerprints from .sops.yaml +fps_csv=$(IFS=,; echo "${current_fps[*]}") +echo "🔧 Setting .sops.yaml pgp to: $fps_csv" +yq e -i '.creation_rules[0].pgp = "'"$fps_csv"'"' .sops.yaml -# Gather public key files -pub_keys=(keys/*.asc) -if [ ${#pub_keys[@]} -eq 0 ]; then - echo "↩️ No public keys found in keys/*.asc — nothing to do." +# 6) Rotate all .env.enc files (recursively) +mapfile -t env_files < <(find . -type f -name '.env.enc') +if [ ${#env_files[@]} -eq 0 ]; then + echo "⚠️ No .env.enc files found—nothing to re-encrypt." exit 0 fi -# Rotate recipients into the existing encrypted file -for pub in "${pub_keys[@]}"; do - # Extract the full fingerprint - fp=$(gpg --with-colons --import-options show-only --import "$pub" \ - | awk -F: '/^fpr:/ {print $10; exit}') - if [ -z "$fp" ]; then - echo "⚠️ Could not extract fingerprint from $pub; skipping." - continue - fi - echo "🔐 Rotating $ENV_FILE: adding recipient $fp" - # Use explicit dotenv format so SOPS doesn't try JSON - sops --input-type dotenv --output-type dotenv -i --rotate --add-pgp "$fp" "$ENV_FILE" +for file in "${env_files[@]}"; do + echo "🔄 Rotating recipients on $file" + for fp in "${current_fps[@]}"; do + sops --input-type dotenv --output-type dotenv -i \ + --rotate --add-pgp "$fp" "$file" + done done -echo "✅ Rotation complete: $ENV_FILE now accessible by ${#pub_keys[@]} recipient(s)." +echo "✅ Update complete."