Every transport authorizes through one interface. A real authentication and authorization layer can therefore drop in later, with no change to transport code.
internal/auth is transport-neutral on purpose. It imports context and
golang.org/x/crypto/ssh, which supplies the public-key wire type. It does
not import gliderlabs/ssh or go-git.
The whole seam is one method:
Authorizer.Authorize(ctx, auth.Request) auth.Decisionauth.Request carries four fields:
| Field | Meaning |
|---|---|
Repo |
The repository path. |
Operation |
Read, Write, or Admin. |
Credential |
What the client presented. |
Transport |
Which transport asked. |
Credential is a sum type. An unexported method seals it, so no other package
can add a case.
| Case | Source |
|---|---|
Anonymous{} |
git://, or HTTP and SSH with nothing presented. |
PublicKey{Key} |
SSH. |
BasicAuth{Username, Password} |
HTTP. |
CAUTION: BasicAuth is unvalidated. The Authorizer owns the user store, so
the Authorizer must check the password itself.
Decision is Allow, Deny, or Unauthenticated.
Unauthenticated is the seam that lets HTTP send a 401 WWW-Authenticate
challenge. SSH and git:// treat every value other than Allow as a denial.
Each transport has three jobs and no more:
- It collects the credential.
- It maps the git service to an
Operation, through the sharedoperationForhelper. - It renders the
Decisionin its own dialect.
The dialects differ: a pktline error for git://, a 401 or a 403 for HTTP,
and stderr plus a non-zero exit status for SSH.
auth.AllowAnonymous{AllowWrite} allows read for everyone. It allows write
only when AllowWrite is set. For now, it also allows Admin for everyone.
The webhook-settings commands request Admin: the queries over HTTP and SSH,
and objgit-webhook-set over SSH. Thus, any client can read the raw signing
secret of every repository, and change its settings. This is a
temporary measure until an ACL-backed authorizer replaces it.
main.go wires it as AllowAnonymous{AllowWrite: *allowPush}. The
-allow-push flag is therefore configuration for this default, and not a
field on daemon.