Browse Source

docs: add serverless release runbook

pull/5591/head
Chase Douglas 2 weeks ago
parent
commit
dbb03a3251
  1. 5
      aws/README.md
  2. 300
      aws/RELEASE_RUNBOOK.md

5
aws/README.md

@ -10,6 +10,11 @@ CloudFront CDN
└─ Web-vault static assets S3 Bucket
```
For upgrades of an existing deployment, follow the
[serverless release runbook](RELEASE_RUNBOOK.md). It records the required
branch order, build and changeset workflow, web-vault compatibility rule,
verification gates, and rollback precautions.
## A Note On AWS Accounts and Security
It is common to have one AWS account host multiple services. But it's easy, and doesn't cost any additional amount, to separate workloads into their own accounts. Doing so makes it easier to control for security concerns and monitor costs. AWS Identity and Access Management (IAM) enforces additional controls for cross-account access than for within-account access, for example, making it harder for security attacks to hop from workload to workload when they are in separate accounts.

300
aws/RELEASE_RUNBOOK.md

@ -0,0 +1,300 @@
# Serverless release runbook
Use this runbook to upgrade this fork to a tagged Vaultwarden release and
deploy it to the existing AWS serverless stack. It is written for an agent or
operator starting from any branch in a possibly dirty worktree.
Loading this runbook does not authorize a deployment, history rewrite, or
other external mutation. Confirm that the user's request includes each action
before doing it.
## Deployment shape
The branch and runtime layers are ordered as follows:
```text
upstream release tag
-> aws-ses (AWS HTTP integration and SES)
-> main (Aurora DSQL and Lambda deployment)
```
Production uses:
- AWS profile `vaultwarden`.
- The stack name and region in `aws/samconfig.toml`.
- An ARM64 Lambda package built by `aws/build-lambda.sh`.
- A static web vault in the `WebVaultAssetsBucket` stack output.
- The web-vault version pinned by `docker/DockerSettings.yaml` at the release
tag. Do not substitute the newest web-vault release.
Never infer account IDs, bucket names, function names, or CloudFront IDs from
old output. Resolve them from CloudFormation on every run.
## 1. Inventory and protect local state
From the repository root, record:
```sh
git status --short --branch
git remote -v
git branch -vv
git log --graph --decorate --oneline --all -30
```
The expected remotes are `upstream` for `dani-garcia/vaultwarden` and `origin`
for this fork. Stop if either URL is unexpected.
Preserve all existing changes before switching branches. Prefer a named stash
that includes untracked files, then record its object ID. Do not discard,
overwrite, or accidentally commit operator files such as
`aws/samconfig.toml` or database dumps.
```sh
git stash push --include-untracked --message "operator state before release"
git stash list --format='%gd %H %s' -1
```
If the tree was already clean, no stash is created. Do not assume an older
`stash@{0}` belongs to this run.
## 2. Authenticate the release
Set the requested release, fetch only from the upstream repository, and read
the immutable GitHub release notes:
```sh
release_version=1.37.2
git fetch upstream "refs/tags/${release_version}:refs/tags/${release_version}" \
refs/heads/main:refs/remotes/upstream/main
gh release view "${release_version}" --repo dani-garcia/vaultwarden
git show --no-patch --format=fuller "${release_version}"
```
Confirm that the tag is annotated, identifies the expected release commit,
and is the immutable non-prerelease requested by the user. Run
`git tag -v "$release_version"` when GPG and the maintainer key are available.
If local signature verification is unavailable, explicitly record that fact;
do not describe the signature as locally verified.
Read the pinned web-vault version before changing branches:
```sh
git show "${release_version}:docker/DockerSettings.yaml"
```
## 3. Rebase the branch stack
Fetch `origin` immediately before rewriting and record its branch tips. These
object IDs become the force-with-lease expectations.
```sh
git fetch origin aws-ses main
old_aws_ses=$(git rev-parse aws-ses)
old_main=$(git rev-parse main)
origin_aws_ses=$(git rev-parse origin/aws-ses)
origin_main=$(git rev-parse origin/main)
```
Create uniquely named local recovery branches. Never overwrite an existing
backup branch.
```sh
git branch "backup-aws-ses-pre-${release_version}" "${old_aws_ses}"
git branch "backup-main-pre-${release_version}" "${old_main}"
```
Rebase the AWS integration layer first:
```sh
git switch aws-ses
git rebase "${release_version}"
```
Resolve conflicts by preserving the upstream behavior and reapplying only the
OpenDAL transport, shared AWS HTTP client integration, and SES transport.
Review each complete resulting commit, not just conflict markers.
Replay only the commits that were above the old `aws-ses` tip onto its new tip:
```sh
git switch main
git rebase --onto aws-ses "${old_aws_ses}"
```
This form avoids replaying the old AWS integration commits as part of `main`.
Restore the operator stash only after both rebases finish and the worktree is
back on `main`. Resolve a stash conflict without losing either version.
Review these invariants before building:
```sh
git merge-base --is-ancestor "${release_version}" aws-ses
git merge-base --is-ancestor aws-ses main
git log --oneline "${release_version}..aws-ses"
git log --oneline "aws-ses..main"
git diff --check "${release_version}..main"
git status --short --branch
```
The release-to-`aws-ses` range should contain the upstreamable OpenDAL HTTP
transport, the separate AWS connector fix, and the SES layer unless the
release already includes any of them. The `aws-ses`-to-`main` range should
contain the DSQL, Lambda deployment, and release documentation commits.
## 4. Validate and publish source
Run formatting and an AWS-feature compile check before the packaging build:
```sh
cargo fmt --all -- --check
cargo check --locked --features aws
```
The S3 feature deliberately injects Vaultwarden's HTTP client into OpenDAL's
reqwest transport. If the rebase changes OpenDAL, confirm that each S3
operator still receives that transport in an `OperationContext`. A compile
check covers the trait API, but an S3 smoke test is still needed to detect
runtime routing failures. Both the OpenDAL and AWS SDK transports should
continue to use `get_reqwest_client_builder(false)` so they inherit the shared
HTTP policy and standard ten-second deadline.
Build from `main`, test the archive, and record its checksum:
```sh
./aws/build-lambda.sh
unzip -t aws/vaultwarden-lambda.zip
shasum -a 256 aws/vaultwarden-lambda.zip
```
The build script and its GitHub workflow must invoke `cargo lambda build` with
the `aws` feature. Stop and fix the build configuration if they do not.
Fetch `origin` again immediately before pushing. Publish rebased history only
with explicit leases recorded above; a lease failure means someone else
updated the branch, so stop and reconcile rather than retrying blindly.
```sh
git fetch origin aws-ses main
git push --force-with-lease="aws-ses:${origin_aws_ses}" \
origin aws-ses:aws-ses
git push --force-with-lease="main:${origin_main}" origin main:main
```
## 5. Create and review the deployment changeset
Confirm AWS identity, stack health, and live versions before mutation:
```sh
aws sts get-caller-identity --profile vaultwarden
aws cloudformation describe-stacks --profile vaultwarden \
--stack-name vaultwarden
curl -fsS https://vault.chasedouglas.net/api/config
curl -fsS https://vault.chasedouglas.net/vw-version.json
```
Resolve the actual stack name and region from `aws/samconfig.toml` if they are
not `vaultwarden` and `us-east-2`. Do not commit that file merely to perform a
release.
Build the SAM staging directory and create, but do not yet execute, a
changeset. Explicitly override the local `disable_rollback` setting:
```sh
cd aws
sam build
sam deploy --profile vaultwarden --no-execute-changeset \
--no-confirm-changeset --no-disable-rollback
```
Capture the exact changeset ARN printed by SAM. Inspect it with
`aws cloudformation describe-change-set` and confirm:
- The stack name, account, and region are correct.
- There are no replacements or deletions.
- The expected Lambda code/configuration is the only material runtime change,
unless the upstream rebase intentionally changed the template.
- Parameter values match the existing stack. No blank local value silently
replaces a production setting.
Stop if the changeset differs from those expectations.
## 6. Execute and verify the backend
Execute the exact reviewed changeset, wait for completion, and inspect stack
events if it fails:
```sh
aws cloudformation execute-change-set --profile vaultwarden \
--change-set-name CHANGESET_ARN
aws cloudformation wait stack-update-complete --profile vaultwarden \
--stack-name vaultwarden
```
Verify that `/api/config` reports the new `main` commit hash and expected
compatibility version. Query recent Lambda logs for startup panics or request
errors. Do not rely on a successful CloudFormation status alone.
## 7. Update the compatible web vault
Extract `vault_version` from `docker/DockerSettings.yaml` on the deployed
`main` branch. Remove its leading `v` only for the JSON version comparison.
Inspect the immutable release and download its tarball, checksum manifest, and
detached signature from `dani-garcia/bw_web_builds`. Verify the entire checksum
manifest before extraction. Reject archives containing absolute paths,
parent-directory traversal, or symlinks.
Resolve current resource IDs:
```sh
aws cloudformation describe-stacks --profile vaultwarden \
--stack-name vaultwarden
aws cloudformation describe-stack-resource --profile vaultwarden \
--stack-name vaultwarden --logical-resource-id CDN
```
If the live `vw-version.json` already matches the pinned version, do not
rewrite the bucket. Otherwise:
1. Download the current bucket into a newly created temporary rollback
directory.
2. Compare the rollback copy with the extracted release by content. Do not
trust `aws s3 sync` as a byte comparison: a changed file with the same size
is skipped when the release archive's timestamp is older than S3. Normalize
timestamps in the temporary extracted copy, or otherwise force every
content difference to appear in the dry run.
3. Run `aws s3 sync ... --delete --dryrun` and inspect uploads and deletions.
4. Sync the extracted `web-vault/` directory with `--delete`.
5. Repeat the dry run with `--size-only`; it must report no changes. Download
the resulting bucket into a separate temporary directory and compare the
complete directory with the extracted release byte for byte.
6. Invalidate `/*` on the resolved CloudFront distribution and wait for it.
7. Verify the public version marker, `index.html`, primary JavaScript bundle,
and WASM module against the release artifact byte for byte.
Keep the rollback directory until all live checks succeed. Never use a bucket
name or distribution ID copied from an earlier release.
## 8. Finish and clean up
Record:
- Release tag and commit.
- New `aws-ses` and `main` tips.
- Lambda package SHA-256.
- CloudFormation changeset ARN and final stack status.
- Backend compatibility version and live `gitHash`.
- Pinned and live web-vault versions.
- CloudFront invalidation ID, if one was needed.
- Any verification that could not be performed.
After successful verification, remove only generated artifacts and temporary
directories created by this run, including `target/`, `aws/.aws-sam/`, the
Lambda ZIP, downloaded web-vault files, and rollback copies. Do not delete
operator files or prune shared Docker caches unless the user separately asks
for that cleanup.
If production verification fails, retain rollback material, stop cleanup, and
report the exact failure. Use the backup branches for source recovery. Restore
the previous Lambda package or web-vault bucket contents only with explicit
authorization for that rollback.
Loading…
Cancel
Save