3.8 KiB
Contributing
Thank you for looking at the bastion. It is one Go package that ends up on machines people own, so the bar is: nothing lands that widens what a host can do on a machine without the person's grant and, where the design calls for it, their approval on that machine.
What you can do today
Issues and pull requests are welcome on GitHub, at github.com/Privatae/bastion. Development happens in a private repository, so an accepted change is carried into the development repository and arrives here with the next release, as its own commit. Sign-in on the git.privatae.ai forge is not reachable from the internet, so issues and pull requests cannot be opened there. You can also read, clone, build and verify a release, and fork under the GPL. Report a security problem by email, not in a public issue (see SECURITY.md).
Checks a change has to pass
cd client-go
gofmt -l . # must print nothing
go vet ./...
CGO_ENABLED=0 go build .
go test ./...
cd .. && deploy/staticcheck.sh # must find nothing, on linux, darwin and windows
Some claim tests need a real machine and root: the op sandboxes, the
namespace tests and the root command watch skip in a container and as a
user. GitHub's CI runs every test docs/claims.md cites as root on its own
virtual machine and fails if one skips; to run them yourself, use a machine
you can throw away.
The backup tests also check this code against the age command itself when
one is on PATH or named by AGE_BIN; CI installs a pinned age release and
sets PRIVATAE_REQUIRE_AGE, so there they fail rather than skip.
The test suite covers all three platforms' parsing and judgement logic from
one go test on any OS. Platform-specific decisions live in files with no
build tag; only the enumerators are tagged. Keep it that way: a new judgement
goes in an untagged file with a table test. Check it with
GOOS=windows go vet ./... and GOOS=darwin go vet ./... as well: go build
does not compile test files, so only vet sees a test that uses a Linux-only
name.
A change to a wall a host's input goes through (a path check, a grant or family check, a signed record's parser) also runs the fuzz targets, and adds one if the wall has none:
deploy/fuzz.sh # every client-go/fuzz_*_test.go target, 20 seconds each
An input the fuzzer finds is written under client-go/testdata/fuzz/; commit
it with the fix, and every later go test runs it.
What a change has to respect
- The bastion dials out. No new listener on a non-loopback interface. A new listener inside the tunnel is a capability family with a grant check.
- Fixed argv. A management op runs a fixed command line. A caller value reaches a program only through a strict charset check and never as part of a shell string.
- No shell in file handlers. Ops that touch the person's files are Go handlers. The one that runs a program takes a tool name from a fixed table.
- Approval on the machine. Anything that writes to disk, downloads onto it, or opens a root shell is gated by the enrolled second factor. The host never handles the code.
- Withhold, don't fake. A platform with no implementation for a capability must not advertise it. The host has to be able to tell quiet from blind.
- Archive, never delete. There is no delete op on shared stores.
Style
- Comments explain why, in plain words. State the invariant and the failure it prevents. No ticket numbers, no dates, no names.
- One package, files named by concern. A new concern gets its own file and
its own
_test.go. - Error text that a person will read is a full sentence saying what was refused and what they can do about it.
License
By contributing you agree that your contribution is licensed under the GNU General Public License, version 3 or later, like the rest of the repository.