diff --git a/deployments/openbao/README.md b/deployments/openbao/README.md index 0e1527e..3a1d05e 100644 --- a/deployments/openbao/README.md +++ b/deployments/openbao/README.md @@ -120,7 +120,7 @@ Docker. Copy `tls.crt` to that host first and pass it as `BAO_CACERT`: ```bash BAO_ADDR=https://$OPENBAO_ADDR:8200 BAO_CACERT=/path/to/openbao-ca.crt \ - BAO_TOKEN= bash bootstrap.sh + bash bootstrap.sh # export BAO_TOKEN beforehand rather than passing it inline ``` It enables the `kanrisha-tape` KV-v2 mount (with **effectively-unlimited** @@ -178,7 +178,7 @@ deploy step. ```bash cd /srv/openbao bash update.sh check # declared / running / latest + seal state -BAO_TOKEN= bash update.sh update # snapshot, then upgrade +bash update.sh update # snapshot, then upgrade (prompts) ``` Unlike the copyparty and ergo updaters in this repo, this one **will not update @@ -191,8 +191,11 @@ configured — only auto-unseal makes the vault come back on its own. Before it changes anything, `update` requires that the container is running, the vault is **unsealed** (a sealed vault cannot produce a snapshot, so there would -be no rollback plan), and a `BAO_TOKEN` with `sys/storage/raft/snapshot` — the -root token works. It writes the snapshot to `/var/backups/openbao` at `0600`, +be no rollback plan), and a token with `sys/storage/raft/snapshot` — the root +token works. It **prompts** for that token with echo off; never pass it as +`BAO_TOKEN=... bash update.sh`, which puts it in your shell history and in +`/proc//cmdline`, where every user on the box can read it. For unattended +use set `BAO_TOKEN_FILE` to a `0600` file. It writes the snapshot to `/var/backups/openbao` at `0600`, verifies it is a valid non-empty gzip archive, and refuses to continue if it is not. **Copy it off the host**: it is the rollback plan, and OpenBao's upgrade guide is explicit that reverting the image alone does not roll back the data @@ -211,19 +214,26 @@ the external `plugin "kms" "pkcs11"` first. The vault is the sole recovery path for encrypted tapes — back it up: Snapshot save/restore are **token-gated** (`sys/storage/raft/snapshot` is -sudo-capable) — pass a token that has that path (the root token works, or mint a -dedicated snapshot-policy token). The container has no ambient token, so supply -it via `-e BAO_TOKEN`. +sudo-capable) — they need a token that has that path (the root token works, or +mint a dedicated snapshot-policy token). The container has no ambient token. + +**Do not put the token on a command line.** `docker ... -e BAO_TOKEN=` +places it in the docker process's argv, and `/proc//cmdline` is readable by +every user on the host — so it leaks further than your shell history does. Use +`update.sh snapshot`, which prompts with echo off and passes the token to the +container over stdin: ```bash -# Consistent raft snapshot (safe while running); write it to a scratch path, NOT -# into the live raft dir: -docker compose exec -T -e BAO_TOKEN= openbao \ - bao operator raft snapshot save -address=https://127.0.0.1:8200 -tls-skip-verify /tmp/openbao.snap -# then stream the RAW file off-box, age-encrypted. Use `exec -T ... cat`, not -# `compose cp openbao:… -` (which emits a TAR wrapper that won't restore): -docker compose exec -T openbao cat /tmp/openbao.snap | \ - age -r "$(cat /path/to/globals/age-pubkey.txt)" > "openbao-$(date +%F).snap.age" +cd /srv/openbao +bash update.sh snapshot # prompts; writes to /var/backups/openbao, 0600 +``` + +Then age-encrypt it and move it off-box: + +```bash +age -r "$(cat /path/to/globals/age-pubkey.txt)" \ + < /var/backups/openbao/openbao-pre-manual-*.snap \ + > "openbao-$(date +%F).snap.age" ``` Restore (into a fresh, unsealed vault): decrypt, copy the raw `.snap` in, and @@ -232,8 +242,12 @@ apply it (also token-gated): ```bash age -d -i ~/.age/key.txt openbao-DATE.snap.age > openbao.snap docker compose cp openbao.snap openbao:/tmp/openbao.snap -docker compose exec -T -e BAO_TOKEN= openbao \ - bao operator raft snapshot restore -address=https://127.0.0.1:8200 -tls-skip-verify /tmp/openbao.snap +# Same argv problem as above, so hand the token over stdin rather than -e: +read -rs -p "token: " T; echo +printf '%s\n' "$T" | docker compose exec -T openbao sh -c \ + 'read -r t; BAO_TOKEN="$t" exec bao operator raft snapshot restore \ + -address=https://127.0.0.1:8200 -tls-skip-verify /tmp/openbao.snap' +unset T ``` Snapshots do **not** contain the unseal keys or root token — you still need diff --git a/deployments/openbao/deploy.sh b/deployments/openbao/deploy.sh index 1d793f5..7e91262 100644 --- a/deployments/openbao/deploy.sh +++ b/deployments/openbao/deploy.sh @@ -496,23 +496,23 @@ TLS: ${STACK_DIR}/tls/tls.crt (give this to the Kanrisha daemon as this vault host only ships Docker), pointed at this vault. Copy tls.crt there first as the BAO_CACERT: BAO_ADDR=https://${OPENBAO_ADDR}:8200 BAO_CACERT=/path/to/openbao-ca.crt \\ - BAO_TOKEN= bash bootstrap.sh + bash bootstrap.sh # export BAO_TOKEN first; do not pass it inline Then point the Kanrisha daemon at: [encryption.openbao] address = "https://${OPENBAO_ADDR}:8200" ca_cert = "/etc/kanrisha/openbao-ca.crt" # = tls.crt above -3. Back it up (this vault is the sole recovery path for encrypted tapes). Snapshot - save is token-gated -- pass a token with sys/storage/raft/snapshot (the root - token works): - docker compose exec -T -e BAO_TOKEN= openbao \\ - bao operator raft snapshot save -address=https://127.0.0.1:8200 -tls-skip-verify /tmp/openbao.snap - docker compose exec -T openbao cat /tmp/openbao.snap > ${STACK_DIR}/openbao.snap +3. Back it up (this vault is the sole recovery path for its secrets). Snapshot + save is token-gated; update.sh prompts for the token with echo off and hands + it to the container over stdin, so it never reaches a command line: + cd ${STACK_DIR} && bash update.sh snapshot ...then age-encrypt + copy it off-box. See the README for the full DR flow. + Do NOT use `-e BAO_TOKEN=`: that puts the token in the docker + process's argv, and /proc//cmdline is world-readable. Manage (run these from ${STACK_DIR}): bash update.sh check # declared/running/latest + seal state - BAO_TOKEN= bash update.sh update # snapshot, then upgrade (comes back SEALED) + bash update.sh update # snapshot, then upgrade (comes back SEALED) docker compose logs -f docker compose pull && docker compose up -d # update docker compose down # stop, keep the vault data @@ -530,183 +530,198 @@ fi # __ARCHIVE_BELOW__ is the embedded tar.gz payload (base64), added by build.sh. exit 0 __ARCHIVE_BELOW__ -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 -AA== +H4sIAAAAAAAAA+xb63LbRpb2bz5Fh3JiMRFAUhc7oSNXaIm2WZIplSgllVK8FAg0SYxwGzQgieNo +ax5innCeZL9zunEhJSeT2kxmaytMyhKB7tOnz/073fJi91qmlhuHSaykvQyDJ7/7p4PP893dJ53n +ne6LvS7/7HS6/Byfve29nSdd/Lu7hzcvtp90ujt4/ER0fn9WHn5ylTmpEE/2Xkx+iIMb+Ues+X/o +syFOEhm9dmIBObjX4p9//4dYOKknI+mJzEmkJSM3XSaZH0fiWi4xLE6lmMWpOHKi1FcLx25sNDbE +eZzEQTxfik3lhNI67o+2xOhEpPJGpkqKJI3vlq0eBopyItMXi1hlwrIWWZYoy3olYvAzdeLe19ud +DgZvRk7m30hxfjwWYCFbSHHj5EEm/EzJYNbixS+iwL+W/DLGP6nejBI+TfAVuEhiegvO8S2KxYHj +eUvRFscye6bEQG+xJxzQUtJNZabMRjOZhj5YkIo50IsKJ/IEk3XcBeQUY4+8OO86ijP6AlJJPg18 +F1yASiQzW5NQwhFExVL+nKTsyjQTm55Mgnhpq4WYQ/Ypr+hnLQFBE1cH/WJ44vipWMa58NI4oR3a +7SxQYlPac1vc+A69S8U4dIJAZTLBTM1f/+D9oGU3GhHU0yuk3GjcxEEeStVriOKZ5TmZ0xP82RCp +M8t4B3PiyWOxOHPJhlIqAzJMoGYfC0YZCT+TjYaS6Y3vrpCmX4XwQxAoWWgX755+PDkdjF73Tybn +/bc9a9t+bm/f8wQ3jjLHh1Qmq8zrd2EIdfQELYdtIpRFM3++X5Bt6+/mh71wA56WSvJ7qDyPAqmU +hW0lifQaetPnCwmbzW5j4cAGhqPB2bldOgrJHUNFGCB2CpUnSQwFYuN+KoPlllAxmcDCj+aQmyFI +kip3IWiiEqEM43TJtnTl+cqZBnKiae6LmRMoeUXarfhmyzH0yEUF7yBPhEzTOLWJ6SXzey2TDL4S +LMVUuk6u2DH0K8wLab+0qiGVgo0bYhaDQnEb54GHZ/ABKFGQ9y6Ftk0y1XILFADgR3N8sw2hI6wR +YlLqO8yrZmM2E9jctZguxeFw3H99PBy9FeMf+qdb4nbhuwtRGb4XS6WJuU4ygYdqcxHCEsPTg8nx +ycERP8gDP4QjFm8hR5JaT1hdfkLqKF9uiBHHD4tcr386tMUpOSVWM8EELgvqWlybhQG+Ho4OW6RI +P9MBIyvJyTtKlB5NzyMonJ2CPXzmwNhtBL3zASKJONSZNdHLkeOSmYDa4ah//swrCZL+p8vEUQoe +TxxxQByOTi/OxQwmdQtHZqNC1IBnu5JtN/VdDsk6qJHkqg2fnEMvUB6YJy+NHNjHrXBclxR/62cL +Ud+n+ErHTQ7x0FwISZW0NhFaJqn8aw5GJuB0AnvwZ8uJG/gw+AmFrhbZYfQ3Z9VYt3SMjPwM1uAr +WVKsBfAwlJ4PRknyDomzGu6ZMS6eTyXTeUZiAtVlHEmYTlxSpDEciSle2nV7kjMiola3m8Xl9/7h +4RlIyUjruaYSmIRdGl+zikxEoWd1bP7vnnMU/9PkwbVYqmfatbDT+3RE6qVxbQpEXo2lL7W3KyG6 +HETfeIiMbvw0jkLopnKA06OD8Ua3K5w8i608UhJKPh2O2DbY7CE8T4uBlMPvm8m1q7rdZqnbSjgy +okjlrarbFu+KyLKZRwg5Xktn4lsos5AkCfDd+P0Ei/fqQi2eWffNcpV3fWTnENrzrSj2JDMKr/Jg +f5nPEY390pPPTBomptiZETfgIMouSbGmT4esbazLhUav3a7Wpxf3NTUWkw6OL8bng7N/YWK3aWJg +B7FbixgS2lzgZ7ZYtl6KbTw3T1Hi5JF5o92aNxhJOAOiMDsCDTUUsc0bX/nYnS0sWIOlrv3E0m5Y +j+8i4OyL0Bw6S0EPH1YZWiZ6bRQuiJlmvyg2kAovmwfvD5tbogmboh+Ux3NFv1lGqPuFDLrbL9gH +ulpsNGSNt+YHQ5tj440T9MROp7CizA9lnGPJveIJSq7Uh+uIHfOAk9sEVYUfI7l3MfU/XSr/v/xU +LvzvW+NX8N/zne3dCv/tYVyXAOCf+O+P+FT4r478UG8YEPebMODoAVQrg9ImisUVLEj5Y4ZclbW2 +6vCC4AaBMIMyNt1AIiKqyEnUIs6sqUO1weFZi8pdCo26ktCF0/jkeNCggtaNuWxNHJQ6xKVh3yBa +xVsNYmWK3qqwRxoVfSoR8NAE4Y2qpMUMzNUV8SpFW7wm7IwiAtX4JiOmgmPV4kLIgElGhSYFQ5Cq +ncYEFuNr5F7EQ1TLYkrDN5XUQf1s0D98P0CJhkIlzaNpHF8ThivE06S1muIjYiZvFp990VypCygz +UYKZ+B69KwoIZKz7BsH247FVYFySR6EwWyCe226aUQViXxv4UE8o02Wt1IK6UasqUh3Y3GLU+q9i +Vl+pXNN7gFxBrsSurLVVubAVyKqf4DnAMjC8FE6tdIkAQAwil5UB20YCH2zXmeikWBppM3MTLU2T +73QqIrmZkq+sEqguptmTmY+6oy50vGkb2RUDIT497tGBeEsDyRNrJThv11Te0L9J+I7QlbduGsB/ +QhZH2UjpMSG96i+X7WAFcpK18cVLx/C69llnXQ+3XEfvlK3pSMqErOi6DgVLCAh1eFS8P4CC9e4H +QRlWXWurobEMhjPAqxA4Q9Z1BN6j5oyIb4uyFEaHYJMCHqRUWRjnQ3mu2HMKWgtHlYi+wPIUM8yS +p4g/SpoVTUiLiNBPzRXM/lOzQO1aK2zKhhHyaDNYVNFIqFuHfAG253h2EyTHCEExbxnmB1WvNgVo +XQoQ0ucOFyqqXArL+lTvoLGhO02PdQoKeC0uC1z9gZk0wLpA1NS+2ABL6x1iDgXUBchEIGcZeSjK +bdoAdB9IXbBTZKoKdjKKsmYkxma0m6J8dRBb8ZXnEdPBrbNUoNc/OB9+P2DWIoq7UIMHi3bBorY+ +hBBubUDQER5jn6njzxcUUktwb1wZYXNDvIH4K2SRBHpNhXQnsxWcAI9bRwBEk1GmRh2caB7BHbrn +SN0elhdBslLJDSfxSfBp4VCfKKcbboAIJlM99lPjumQzFP+muR9kFta9lVNxMbTFOJ+qzM/ybD1M +s2kW6IVGDjPiW1GnNpK3DdPeyFPZM60P7qp5Op3TV5QFZY7QGB+IXDe7KnFgNG+kocG5ExDCXJJx +c/bUw7NFGufzBQuLGzPneRpx04yaV7T27cIhC8uoW0WZIKG2KDRYtQnaJpcSH0E8NzVFCIOapvGt +IhZVXEvnnHTJfKnEWFCsMNoywwtNkZ85RUdRLWQQAGYqN42DYIpMbzdyfyUyfncx/I4c4J//+Dv+ +F/0a0t4sGistjuiHgzf9i+NzMRyL9/3RRf9YXIzGA/zQU//3/4OLw8Hp2eCgfz447PHmXl8Mj88t +IH4N6jXCn8ogvtVNbB1MIYkiJt5s27AzVm9Dd2wA0SkcAaNNcy4DKZb7ivqBfmSyN8ZF9qPdhmIY +OQ11qImivKOmOF4evR+LJMjnWH/zyvzSvA5Vs2pCfLy/ahUtMD6lyGAZoEWYmbrkcGeFfyhCT+WM +Ky3nmkyJ9nFHjv8Ddb1QgjI7pqdRO/hIYWOkhcGhCTZE9wqZrjI6vZUrsXm30wJBRDeYhi4GTS8Z +FhyvbNuh7igYc0qhQIxtrva2QI8615TKtZSRcwPtYz7ehnEe8SlCOTWMvTygSsCT1FWnmjlebStr +d0QkA7W11gqXXIhEPS7TxVqP5yM/E/DraS3X5ypt40lbxbNsoUL63fy6bau4aeaoAAVsWSQVT1EE +VJRo3Xa7xk0xisqiwIEh0qhrU8QZ4RVjQomYEvkqZPp33c7XL5r1qmRDHBy9n/QH48nbg/c85/7P +RsFv+MxlRJ0bJId/3xoE8l/s7X0K/+90Xujz387znZ0XO4z/6Uj4T/z/B3w2PmNHn/pRmyoV4OsF +x4jKLjhtFWeSa01FwikMJr7ikp/q5jqgLgsFKkZngmo8fZpSlgQJYidioC1GXN9RKXmb+nT2Ke8w +mWI4gRGl83is+5orx6H69PTXgGTtCFT8hZIFA1ED08RXlOUMzC3jKsxijqoQBWdZFVB85RCNwjUG +kuDerRUnuvtxofhYk2VYl98lpDQ5HJ59KGLWpjmY0CiYsskAIZLelAegmDHuj8bCnG8KfGFpbgne +afNwNO5x78MOnGhreNrraoDa7fCXokwsoug67cP+j0QbGML3/Ix7MR5K7oqzr7f3yhPe0Lkr6iPF +p0hJ1qLT3UxYMo8R6xNM8oNGA3vcbz792O1ZvLH7ZoP2sF/r9Bf76lm0AWAMJyDUusIzphF769Po +Wc8CX3jfCK9JLVYimk+xKB7Aui4vhTXTD0r8Lb74YvUhYW3x4cNLLlaAfZFdYtG8NNr6wAZdAtc1 +IyUpESHdPpLOjSlWHQIvNkF42GwmOo2Z32iYY2lh3fCZjVKBeNVG6m5HOfL89qsvuuLnn8XHBwwU +g8lJZigCvKZ49cX2S026+1IwzB4cHI774tTa3nv+sMfPkIDshYsiPwKW8IqD3AKjrPQMQHCTWD04 +HrbYwbLYnKeVZ3Xc0ME3CJ0kxN0E33W4EiTdA/4VjKfyr8K62+t8IywgCvIo6UJT+AWFMH6fAJY7 +IfY7cXOU170k9UOJjdx0BQNHJX6CJC0an2frmrNWn7GKLbZcPISBNPVklU//ggrmYLRvGhYYha2j +5BRNegeo2A+yEbDM/tOPZI9kU+4CJZZ43umsLVq82N1dW7nRWFfeWxMlvRWtFNGQI5WxoeaDuYJ0 +pnrCMFTzTnpGm7v3Hs56S/3WOlcF8l3viTnqsSV/qS+GOKUPwOvttCKit2Dv/+nU9efnd/jkiQeD +/beWf79S/+11drvF/b9uZ7fznOq/7t7zP+u/P+LzqfqvtAtqMeovK6VdiTptcRCHCSAatYjiqte0 +xVUTKjBJ6JZy5Fz5nibCIN5cG3w3HIuL08P++eCMmiKHg+Ph68EZvh7/KC5Gx8OjAcYMxAn+ORsL +QFmecTY4PbHBRLJEOsmWBkPLdE6chVJQl8b0JELE4huq1RixO+XdJru2F6rleIpuAvT4loxpfRQl +0XjhhMh/BE63mAyDftMryBapXG0yZctEN1Xo0opY5CH3sIr2RHkJhntMqmqicc/AFn2h6GZhThcG +jPQdlBY7PaQnvqKlmUvLbkedZjybcbM4jzI/QOEcSqq8szyNFB1Q8Q0gGk18ai2MddKo1uTTpNHg +eyiF8PfcXE1K84hbdNTxJYVzc5HFw61EumaZZkTTqHRyenI8PPhxn7oidMNINzeo2EA9wp03vnqD +beR0tqbv4q30UHzqGek+ep7S7Qmr7Brz3RUmYc4Miv1XFkCNKn0ooPdJ7c6AkAQERtX7ylFdqZw4 +d7UujFKKm2qRab3HM3OYoO+aav2gNjamQF380riekcjnqQPLn+e+7m/LuyTwXcqs1ODkU1FCOsbe ++HakLir1xuhAgDqPektsknRQyUeKpr/p1HbhF0cdRbdSJIQRNOwiiAZYhKpC14krs6gxZc25fiEV +uU4UsVRYXGS9prdqLrKYynAKKZBItIx0l9hNfRR7fMWETY4PeiFtlhfYZWKoG0nKCyeY3TrLoh9s +DDKfmgpa6TvDTGsNP7VgfG7gkFncqMo2UaXQ7dlsq+z2ZVIb3e1CEgc1UKQ/fOfGyVDZeJW/6ZuQ +2L1yZiiU4WsIdcYXzP1OWzfTChnSORp54rpdcXcaeIFv5CEijEf90/G7k3MChUC+5q6oDOimFBE0 +DPDH4xIsim97BEPg13S6Yb2qiON3jxrZ+EldN/pBCINuGSVMDXIpN8od9bqng50bqF29FPGUotaK +2/J0E8V1s6+YpY3Qh/9dgfyV5jqqhtaOy4o5rNbTGIa/FJsry5hb4ZApnXDW1asPfXQAeiz+vDQR +h0MBE+FAIyoZ8q02h842+PRIH+Zxa0FxKAGcOBkh0wA/Pog6tU8VgGxuJRMM8WFgvPsi7OlzvnjF +PDS2B76iqMP37NiH2jJzi8NUy6RaWuNl/QYfAnWkjHDG5/2DI7KX/bZKb4qpmrnVUKvFyJMYOp8c +DUb74sHHHA5zt+RKc3DVviqs6soWp7A2yIyCTxabrvIvf0zzmlMPZEtH9c1xniTBsrhowRGmqY8e +7FUWJ2+Gx4NVPllc5bQi+PDZ9CZKyE6r6MtoCdWcar9946CmQezLE1UIi0ed98/eDs4nyGvj4cma +XMh9ENwS6RK4LXNc7UhIxxUmdHj24+TsYrTfeUQQgLSkPfI1HUQWTpJQz99YsjEPzfbR8HRS8L5G +7bA/ejs4O7kY98occmvOMVZiOxN6+25CRVGBeKtdczR9oAVtVleViXxLV1Fe6eZVVfwZy8A+0mu1 +xf5yeMJGwYGpZ7JukEumF1DAphDHDTg+OBOwCmSq5dphobkf5KTzG33YhpQLSSVpzLeUjQOSCfCJ +IqxUdzByOqUzk6fxHR8ntWlW+9vE91613dAjAzTXg0wi4jjCBJM8UzWbImAcLYu/IRCF6SLiLvRO +Sjxd3XyPC1oq8yjgWvqmM7krdgtWwkTfIOBHUBfDbroEgSDDd2D47wP4sgECKPWOmNwbPmSqspBC +0kpisqRVJ6FC0BHkAOwL9d4j01nTYD1xrn02yJK0IKh0kd5j8+vZ7cF8vVtVNn6Z2GNU6intN1BZ +Cw98XaptU9Dki1OPLvKYn/Pfknxi+GOdzPHg+M1+8+mm6wn86/kp/d0JXVl+3R+/m4xPLs4OBped +D/fNFrcYk1uv1X66SZfTPj2w0Vi59Gyi9sHJ6E1v/1P54L5ZdDZTzH1k6kozk2Q5WMsf1d8mEb2e +Lpppxxye1rrxeK6vCKOWg9MN34z3dRTG8hN2jp9/Zm4icMMP9PpecUFd0Nvy1f5/i/+6vOypxHFl +78OHLzdouvU3kgKPaLdrb9v3RIukWZwWlzQn1/vFlM8/3//yvvlSTG7KRxtf7vMTPeh6nWidM833 +NS8EVh4sVAz5+Nnk+qu7+2Icx/MZtXL17Gefq2f0640mzneavv2EfrgZzIovU3hvJYeDQ369ksV7 +Jo0XL0s/6O0/eMSuUT2v58Heo4mwGLnqIxUFk9p6+52SZj1J1Z6bnNNbTzrUTh2MvtdO23xa7rxN +Z9DNBglm+Hby7uB45WV1FRnTg3i+2RLiYyH8Zz91dnYuuy93tsPLrz7wl04oPlc/RaSKL5vUGb91 +0giTHs7ZCS8/e2SO7qzfNzxfPr5WN7y8++S8lY68viRVXvpRbkzo3+QOfWcBUYTRWv1vQrbMRUjK +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 +bgCgAAA= diff --git a/deployments/openbao/update.sh b/deployments/openbao/update.sh index 6d663ff..b5a5de4 100644 --- a/deployments/openbao/update.sh +++ b/deployments/openbao/update.sh @@ -23,6 +23,7 @@ # Subcommands: # check (default) declared vs running vs latest, seal state, and whether # an unattended update would be safe here. Changes nothing. +# snapshot take a raft snapshot and leave it in SNAPSHOT_DIR. Nothing else. # update do it now: preflight -> snapshot -> down -> pin -> pull -> up # run what the schedule invokes; obeys UPDATE_POLICY # install schedule the daily `run` @@ -35,17 +36,29 @@ # # Env (also read from /etc/openbao-update.conf; environment wins): # STACK_DIR=/srv/openbao UPDATE_POLICY=notify -# BAO_TOKEN= required by `update`: the snapshot is token-gated +# BAO_TOKEN= required by `update`/`snapshot`. Prefer NOT to set +# this inline -- see "Supplying the token" below. +# BAO_TOKEN_FILE= read the token from a file (0600) instead # SNAPSHOT_DIR=/var/backups/openbao # TARGET_VERSION= pin a specific version instead of latest # DRY_RUN=0 print what would happen, change nothing # SKIP_SNAPSHOT=0 DANGEROUS: upgrade with no rollback plan # GH_REPO=openbao/openbao # +# Supplying the token: +# `BAO_TOKEN= bash update.sh update` works, but DO NOT do it: the value +# lands in your shell history, and anything on the argv of a child process is +# readable by every user on the box via /proc//cmdline. This script never +# puts the token on any command line -- it hands it to the container over +# stdin -- and it will prompt for it with echo off if you do not supply one. +# For unattended use, point BAO_TOKEN_FILE at a 0600 file. +# # Usage: -# bash update.sh check -# BAO_TOKEN= bash update.sh update -# BAO_TOKEN= TARGET_VERSION=2.6.2 bash update.sh update +# bash update.sh check # no token needed +# bash update.sh snapshot # prompts for the token +# bash update.sh update # prompts for the token +# BAO_TOKEN_FILE=/root/.bao-root bash update.sh update +# TARGET_VERSION=2.6.2 bash update.sh update set -euo pipefail @@ -65,6 +78,7 @@ fi : "${STACK_DIR:=/srv/openbao}" : "${UPDATE_POLICY:=notify}" : "${BAO_TOKEN:=}" +: "${BAO_TOKEN_FILE:=}" : "${SNAPSHOT_DIR:=/var/backups/openbao}" : "${TARGET_VERSION:=}" : "${DRY_RUN:=0}" @@ -105,6 +119,44 @@ env_get() { # -> value from .env ('' if absent) sed -n "s/^$1=//p" "$ENV_FILE" | tail -n1 } +# --------------------------------------------------------------------------- +# Token handling +# +# The token never appears on a command line. `docker exec -e BAO_TOKEN=` +# would put it in the docker process's argv, and /proc//cmdline is +# world-readable -- so it would leak to every user on the host, not just to the +# shell history. Instead the value is written to the container's stdin and the +# container's own shell reads it into the environment it then execs `bao` with. +# --------------------------------------------------------------------------- + +acquire_token() { + [[ -n "$BAO_TOKEN" ]] && return 0 + if [[ -n "$BAO_TOKEN_FILE" ]]; then + [[ -r "$BAO_TOKEN_FILE" ]] || die "BAO_TOKEN_FILE=${BAO_TOKEN_FILE} is not readable." + BAO_TOKEN="$(head -n1 "$BAO_TOKEN_FILE")" + [[ -n "$BAO_TOKEN" ]] || die "BAO_TOKEN_FILE=${BAO_TOKEN_FILE} is empty." + return 0 + fi + if [[ -t 0 ]]; then + read -rs -p "OpenBao token (input hidden): " BAO_TOKEN + printf '\n' >&2 + [[ -n "$BAO_TOKEN" ]] || die "No token entered." + return 0 + fi + die "No token available and stdin is not a terminal. Set BAO_TOKEN_FILE=/path/to/a/0600/file, or run interactively to be prompted. Avoid BAO_TOKEN=... on the command line: it goes into your shell history and into /proc//cmdline." +} + +# Run `bao ` inside the container with BAO_TOKEN set, passing the token +# over stdin so it never reaches an argv anywhere on the host. +bao_with_token() { + printf '%s\n' "$BAO_TOKEN" | dc exec -T openbao sh -c ' + read -r _t + BAO_TOKEN="$_t" + export BAO_TOKEN + exec "$@" + ' sh bao "$@" +} + # --------------------------------------------------------------------------- # State # --------------------------------------------------------------------------- @@ -178,7 +230,7 @@ take_snapshot() { # -> echoes the snapshot path return 0 fi - [[ -n "$BAO_TOKEN" ]] || die "BAO_TOKEN is required: the raft snapshot is token-gated (sys/storage/raft/snapshot). The root token works." + acquire_token if [[ "$DRY_RUN" == "1" ]]; then echo "DRY: snapshot -> ${dest}" >&2 @@ -190,8 +242,8 @@ take_snapshot() { # -> echoes the snapshot path log "Taking a raft snapshot to ${dest}..." >&2 # Write inside the container, then stream the RAW file out. `compose cp` is # NOT usable here: it emits a TAR wrapper that will not restore. - dc exec -T -e BAO_TOKEN="$BAO_TOKEN" openbao \ - bao operator raft snapshot save -address=https://127.0.0.1:8200 -tls-skip-verify /tmp/pre-upgrade.snap \ + bao_with_token operator raft snapshot save \ + -address=https://127.0.0.1:8200 -tls-skip-verify /tmp/pre-upgrade.snap \ >/dev/null || die "Snapshot failed -- not upgrading. Check the token has sys/storage/raft/snapshot." dc exec -T openbao cat /tmp/pre-upgrade.snap > "$dest" || die "Could not stream the snapshot out -- not upgrading." dc exec -T openbao rm -f /tmp/pre-upgrade.snap >/dev/null 2>&1 || true @@ -250,7 +302,7 @@ do_check() { log "Update available: ${declared:-?} -> ${latest}" if [[ "$auto" == no ]]; then warn "A restart will leave the vault SEALED until someone enters three unseal keys." - warn "Run it when you can do that: BAO_TOKEN= bash ${SELF} update" + warn "Run it when you can do that: bash ${SELF} update (it will prompt for a token)" fi } @@ -332,6 +384,16 @@ data store. Restore the pre-upgrade snapshot -- see the README's DR runbook. EOF } +do_snapshot() { + [[ -d "$STACK_DIR" ]] || die "No stack at $STACK_DIR (set STACK_DIR)." + container_running || die "The openbao container is not running." + local state; state="$(seal_state)" + [[ "$state" == 0 ]] || die "The vault is SEALED (or unreadable): a snapshot can only be taken from an unsealed vault." + local dest; dest="$(take_snapshot manual)" + [[ -n "$dest" ]] || die "No snapshot was taken." + log "Snapshot written to ${dest} -- copy it OFF this host." +} + do_run() { # What the schedule invokes. The whole point of this branch is that it is # conservative: an unattended update of a manually-unsealed vault takes it @@ -371,9 +433,11 @@ STACK_DIR=${STACK_DIR} UPDATE_POLICY=${UPDATE_POLICY} SNAPSHOT_DIR=${SNAPSHOT_DIR} GH_REPO=${GH_REPO} -# BAO_TOKEN is intentionally absent. \`update\` needs one for the snapshot; pass -# it in the environment for that run. UPDATE_POLICY=auto therefore only works -# with auto-unseal AND a token supplied some other way. +# BAO_TOKEN is intentionally absent: a long-lived root token in a file next to +# the vault it opens defeats the vault. \`update\`/\`snapshot\` prompt for one. +# For unattended use set BAO_TOKEN_FILE below to a 0600 file holding a token +# with sys/storage/raft/snapshot -- not the root token. +#BAO_TOKEN_FILE=/root/.bao-snapshot-token EOF chmod 0600 "$OPENBAO_UPDATE_CONF" } @@ -439,9 +503,10 @@ do_uninstall() { case "${1:-check}" in check) do_check ;; + snapshot) do_snapshot ;; update) do_update ;; run) do_run ;; install) do_install ;; uninstall) do_uninstall ;; - *) die "Usage: $(basename "$0") {check|update|run|install|uninstall}" ;; + *) die "Usage: $(basename "$0") {check|snapshot|update|run|install|uninstall}" ;; esac