mirror of
https://codeberg.org/git-pages/git-pages.git
synced 2026-05-14 03:01:48 +00:00
[breaking-change] Accept multiple index repository patterns.
This commit is contained in:
@@ -51,13 +51,13 @@ Authorization
|
||||
DNS is used for authorization of content updates, either via TXT records or by pattern matching. The authorization flow proceeds sequentially in the following order, with the first of multiple applicable rule taking precedence:
|
||||
|
||||
1. **Development Mode:** If the environment variable `INSECURE` is set to the value `very`, the request is authorized to update from any clone URL.
|
||||
2. **DNS Challenge:** If the method is `PUT` or `POST`, and a well-formed `Authorization:` header is provided containing a `<token>`, and a TXT record lookup at `_git-pages-challenge.<hostname>` returns a record whose concatenated value equals `SHA256("<hostname> <token>")`, the request is authorized to update from any clone URL.
|
||||
2. **DNS Challenge:** If the method is `PUT` or `POST`, and a well-formed `Authorization:` header is provided containing a `<token>`, and a TXT record lookup at `_git-pages-challenge.<host>` returns a record whose concatenated value equals `SHA256("<host> <token>")`, the request is authorized to update from any clone URL.
|
||||
- **<code>Pages</code> scheme:** Request includes an `Authorization: Pages <token>` header.
|
||||
- **<code>Basic</code> scheme:** Request includes an `Authorization: Basic <basic>` header, where `<basic>` is equal to `Base64("Pages:<token>")`. (Useful for non-Forgejo forges.)
|
||||
3. **DNS Allowlist:** If the method is `PUT` or `POST`, and a TXT record lookup at `_git-pages-repository.<hostname>` returns a set of well-formed absolute URLs, the request is authorized to update from clone URLs in the set.
|
||||
3. **DNS Allowlist:** If the method is `PUT` or `POST`, and a TXT record lookup at `_git-pages-repository.<host>` returns a set of well-formed absolute URLs, the request is authorized to update from clone URLs in the set.
|
||||
4. **Wildcard Match:** If the method is `POST`, and a `[wildcard]` configuration section is present, and the suffix of a hostname (compared label-wise) is equal to `[wildcard].domain`, the request is authorized to update from a *matching* clone URL.
|
||||
- **Index repository:** If the request URL is `scheme://<hostname>/`, a *matching* clone URL is computed as `sprintf([wildcard].clone-url, <hostname>, [wildcard].index-repo)`.
|
||||
- **Project repository:** If the request URL is `scheme://<hostname>/<project-name>/`, a *matching* clone URL is computed as `sprintf([wildcard].clone-url, <hostname>, <project-name>)`.
|
||||
- **Index repository:** If the request URL is `scheme://<user>.<host>/`, a *matching* clone URL is computed by templating `[wildcard.clone-url]` with `<user>` and `<project>`, where `<project>` is computed by templating each element of `[wildcard].index-repos` with `<user>`.
|
||||
- **Project repository:** If the request URL is `scheme://<user>.<host>/<project>/`, a *matching* clone URL is computed by templating `[wildcard.clone-url]` with `<user>` and `<project>`.
|
||||
5. **Default Deny:** Otherwise, the request is not authorized.
|
||||
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ health = "tcp/:3002"
|
||||
|
||||
# [wildcard]
|
||||
# domain = "codeberg.page"
|
||||
# clone-url = "https://codeberg.org/%s/%s.git"
|
||||
# index-repo = "%s.codeberg.page"
|
||||
# clone-url = "https://codeberg.org/<user>/<project>.git"
|
||||
# index-repos = ["<user>.codeberg.page", "pages"]
|
||||
|
||||
[backend]
|
||||
type = "fs"
|
||||
|
||||
2
go.mod
2
go.mod
@@ -9,6 +9,7 @@ require (
|
||||
github.com/maypok86/otter/v2 v2.2.1
|
||||
github.com/minio/minio-go/v7 v7.0.95
|
||||
github.com/pelletier/go-toml/v2 v2.2.4
|
||||
github.com/valyala/fasttemplate v1.2.2
|
||||
google.golang.org/protobuf v1.36.9
|
||||
)
|
||||
|
||||
@@ -36,6 +37,7 @@ require (
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/sergi/go-diff v1.4.0 // indirect
|
||||
github.com/tinylib/msgp v1.3.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
golang.org/x/crypto v0.41.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b // indirect
|
||||
golang.org/x/net v0.43.0 // indirect
|
||||
|
||||
4
go.sum
4
go.sum
@@ -81,6 +81,10 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww=
|
||||
github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b h1:QoALfVG9rhQ/M7vYDScfPdWjGL9dlsVVM5VGh7aKoAA=
|
||||
|
||||
32
src/auth.go
32
src/auth.go
@@ -147,18 +147,30 @@ func authorizeWildcardMatch(r *http.Request) ([]string, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if slices.Equal(hostParts[1:], strings.Split(config.Wildcard.Domain, ".")) {
|
||||
if slices.Equal(hostParts[1:], wildcardPattern.Domain) {
|
||||
userName := hostParts[0]
|
||||
repoName := projectName
|
||||
if repoName == ".index" {
|
||||
repoName = fmt.Sprintf(config.Wildcard.IndexRepo, userName)
|
||||
var repoURLs []string
|
||||
repoURLTemplate := wildcardPattern.CloneURL
|
||||
if projectName == ".index" {
|
||||
for _, indexRepoTemplate := range wildcardPattern.IndexRepos {
|
||||
indexRepo := indexRepoTemplate.ExecuteString(map[string]any{"user": userName})
|
||||
repoURLs = append(repoURLs, repoURLTemplate.ExecuteString(map[string]interface{}{
|
||||
"user": userName,
|
||||
"project": indexRepo,
|
||||
}))
|
||||
}
|
||||
} else {
|
||||
repoURLs = append(repoURLs, repoURLTemplate.ExecuteString(map[string]interface{}{
|
||||
"user": userName,
|
||||
"project": projectName,
|
||||
}))
|
||||
}
|
||||
return repoURLs, nil
|
||||
} else {
|
||||
return nil, AuthError{
|
||||
http.StatusUnauthorized,
|
||||
fmt.Sprintf("domain %s does not match wildcard *.%s", host, config.Wildcard.Domain),
|
||||
}
|
||||
return []string{fmt.Sprintf(config.Wildcard.CloneURL, userName, repoName)}, nil
|
||||
}
|
||||
|
||||
return nil, AuthError{
|
||||
http.StatusUnauthorized,
|
||||
fmt.Sprintf("domain %s does not match wildcard *.%s", host, config.Wildcard.Domain),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/valyala/fasttemplate"
|
||||
)
|
||||
|
||||
type CacheConfig struct {
|
||||
@@ -19,9 +22,9 @@ type Config struct {
|
||||
Health string `toml:"health"`
|
||||
} `toml:"listen"`
|
||||
Wildcard struct {
|
||||
Domain string `toml:"domain"`
|
||||
CloneURL string `toml:"clone-url"`
|
||||
IndexRepo string `toml:"index-repo"`
|
||||
Domain string `toml:"domain"`
|
||||
CloneURL string `toml:"clone-url"`
|
||||
IndexRepos []string `toml:"index-repos"`
|
||||
} `toml:"wildcard"`
|
||||
Backend struct {
|
||||
Type string `toml:"type"`
|
||||
@@ -41,7 +44,14 @@ type Config struct {
|
||||
} `toml:"backend"`
|
||||
}
|
||||
|
||||
type WildcardPattern struct {
|
||||
Domain []string
|
||||
CloneURL *fasttemplate.Template
|
||||
IndexRepos []*fasttemplate.Template
|
||||
}
|
||||
|
||||
var config Config
|
||||
var wildcardPattern WildcardPattern
|
||||
|
||||
func ReadConfig(path string) error {
|
||||
file, err := os.Open(path)
|
||||
@@ -70,3 +80,25 @@ func UpdateConfigEnv() {
|
||||
updateFromEnv(&config.Backend.S3.Region, "S3_REGION")
|
||||
updateFromEnv(&config.Backend.S3.Bucket, "S3_BUCKET")
|
||||
}
|
||||
|
||||
func CompileWildcardPattern() {
|
||||
wildcardPattern = WildcardPattern{
|
||||
Domain: strings.Split(config.Wildcard.Domain, "."),
|
||||
}
|
||||
|
||||
template, err := fasttemplate.NewTemplate(config.Wildcard.CloneURL, "<", ">")
|
||||
if err != nil {
|
||||
log.Fatalf("wildcard pattern: clone URL: %s", err)
|
||||
} else {
|
||||
wildcardPattern.CloneURL = template
|
||||
}
|
||||
|
||||
for _, indexRepo := range config.Wildcard.IndexRepos {
|
||||
template, err := fasttemplate.NewTemplate(indexRepo, "<", ">")
|
||||
if err != nil {
|
||||
log.Fatalf("wildcard pattern: clone URL: %s", err)
|
||||
} else {
|
||||
wildcardPattern.IndexRepos = append(wildcardPattern.IndexRepos, template)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ func main() {
|
||||
log.Fatalln("config:", err)
|
||||
}
|
||||
UpdateConfigEnv() // environment takes priority
|
||||
CompileWildcardPattern()
|
||||
|
||||
switch config.LogFormat {
|
||||
case "short":
|
||||
|
||||
Reference in New Issue
Block a user