[mirror] Scalable static site server for Git forges (like GitHub Pages)

Implement `-audit-log` option.

Also, record the principal of `git-pages -{freeze,unfreeze}-domain`
and `git-pages -update-site` as the CLI administrator.

+130 -47
+1 -1
flake.nix
··· 43 "-s -w" 44 ]; 45 46 - vendorHash = "sha256-LkHC/gFiSfYz9Z4bYMq1QNdapPYp8h1DSMRfFU9f7mw="; 47 }; 48 in 49 {
··· 43 "-s -w" 44 ]; 45 46 + vendorHash = "sha256-40LyEXdJDpWPe9UvqM2siqXdpbae1ba7kN7FtySPpBc="; 47 }; 48 in 49 {
+3
go.mod
··· 8 github.com/KimMachineGun/automemlimit v0.7.5 9 github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 10 github.com/creasty/defaults v1.8.0 11 github.com/getsentry/sentry-go v0.40.0 12 github.com/getsentry/sentry-go/slog v0.40.0 13 github.com/go-git/go-billy/v6 v6.0.0-20251126203821-7f9c95185ee0 ··· 43 github.com/klauspost/cpuid/v2 v2.3.0 // indirect 44 github.com/klauspost/crc32 v1.3.0 // indirect 45 github.com/leodido/go-syslog/v4 v4.3.0 // indirect 46 github.com/minio/crc64nvme v1.1.0 // indirect 47 github.com/minio/md5-simd v1.1.2 // indirect 48 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
··· 8 github.com/KimMachineGun/automemlimit v0.7.5 9 github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 10 github.com/creasty/defaults v1.8.0 11 + github.com/fatih/color v1.18.0 12 github.com/getsentry/sentry-go v0.40.0 13 github.com/getsentry/sentry-go/slog v0.40.0 14 github.com/go-git/go-billy/v6 v6.0.0-20251126203821-7f9c95185ee0 ··· 44 github.com/klauspost/cpuid/v2 v2.3.0 // indirect 45 github.com/klauspost/crc32 v1.3.0 // indirect 46 github.com/leodido/go-syslog/v4 v4.3.0 // indirect 47 + github.com/mattn/go-colorable v0.1.13 // indirect 48 + github.com/mattn/go-isatty v0.0.20 // indirect 49 github.com/minio/crc64nvme v1.1.0 // indirect 50 github.com/minio/md5-simd v1.1.2 // indirect 51 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+9
go.sum
··· 33 github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= 34 github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= 35 github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= 36 github.com/getsentry/sentry-go v0.40.0 h1:VTJMN9zbTvqDqPwheRVLcp0qcUcM+8eFivvGocAaSbo= 37 github.com/getsentry/sentry-go v0.40.0/go.mod h1:eRXCoh3uvmjQLY6qu63BjUZnaBu5L5WhMV1RwYO8W5s= 38 github.com/getsentry/sentry-go/slog v0.40.0 h1:uR2EPL9w6uHw3XB983IAqzqM9mP+fjJpNY9kfob3/Z8= ··· 81 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 82 github.com/leodido/go-syslog/v4 v4.3.0 h1:bbSpI/41bYK9iSdlYzcwvlxuLOE8yi4VTFmedtnghdA= 83 github.com/leodido/go-syslog/v4 v4.3.0/go.mod h1:eJ8rUfDN5OS6dOkCOBYlg2a+hbAg6pJa99QXXgMrd98= 84 github.com/maypok86/otter/v2 v2.2.1 h1:hnGssisMFkdisYcvQ8L019zpYQcdtPse+g0ps2i7cfI= 85 github.com/maypok86/otter/v2 v2.2.1/go.mod h1:1NKY9bY+kB5jwCXBJfE59u+zAwOt6C7ni1FTlFFMqVs= 86 github.com/minio/crc64nvme v1.1.0 h1:e/tAguZ+4cw32D+IO/8GSf5UVr9y+3eJcxZI2WOO/7Q= ··· 150 golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= 151 golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= 152 golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= 153 golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= 154 golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 155 golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
··· 33 github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= 34 github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= 35 github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= 36 + github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= 37 + github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= 38 github.com/getsentry/sentry-go v0.40.0 h1:VTJMN9zbTvqDqPwheRVLcp0qcUcM+8eFivvGocAaSbo= 39 github.com/getsentry/sentry-go v0.40.0/go.mod h1:eRXCoh3uvmjQLY6qu63BjUZnaBu5L5WhMV1RwYO8W5s= 40 github.com/getsentry/sentry-go/slog v0.40.0 h1:uR2EPL9w6uHw3XB983IAqzqM9mP+fjJpNY9kfob3/Z8= ··· 83 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 84 github.com/leodido/go-syslog/v4 v4.3.0 h1:bbSpI/41bYK9iSdlYzcwvlxuLOE8yi4VTFmedtnghdA= 85 github.com/leodido/go-syslog/v4 v4.3.0/go.mod h1:eJ8rUfDN5OS6dOkCOBYlg2a+hbAg6pJa99QXXgMrd98= 86 + github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 87 + github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 88 + github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 89 + github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 90 + github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 91 github.com/maypok86/otter/v2 v2.2.1 h1:hnGssisMFkdisYcvQ8L019zpYQcdtPse+g0ps2i7cfI= 92 github.com/maypok86/otter/v2 v2.2.1/go.mod h1:1NKY9bY+kB5jwCXBJfE59u+zAwOt6C7ni1FTlFFMqVs= 93 github.com/minio/crc64nvme v1.1.0 h1:e/tAguZ+4cw32D+IO/8GSf5UVr9y+3eJcxZI2WOO/7Q= ··· 157 golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= 158 golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= 159 golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= 160 + golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 161 + golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 162 golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= 163 golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 164 golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
+31
src/audit.go
··· 87 return 88 } 89 90 type AuditRecordScope int 91 92 const (
··· 87 return 88 } 89 90 + func (record *AuditRecord) GetAuditID() AuditID { 91 + return AuditID(record.GetId()) 92 + } 93 + 94 + func (record *AuditRecord) DescribePrincipal() string { 95 + var items []string 96 + if record.Principal != nil { 97 + if record.Principal.GetIpAddress() != "" { 98 + items = append(items, record.Principal.GetIpAddress()) 99 + } 100 + if record.Principal.GetCliAdmin() { 101 + items = append(items, "<cli-admin>") 102 + } 103 + } 104 + if len(items) > 0 { 105 + return strings.Join(items, ";") 106 + } else { 107 + return "<unknown>" 108 + } 109 + } 110 + 111 + func (record *AuditRecord) DescribeResource() string { 112 + desc := "<unknown>" 113 + if record.Domain != nil && record.Project != nil { 114 + desc = fmt.Sprintf("%s/%s", *record.Domain, *record.Project) 115 + } else if record.Domain != nil { 116 + desc = *record.Domain 117 + } 118 + return desc 119 + } 120 + 121 type AuditRecordScope int 122 123 const (
+6 -6
src/backend.go
··· 52 IfMatch string 53 } 54 55 - type QueryAuditLogOptions struct { 56 // Inclusive lower bound on returned audit records, per their Snowflake ID (which may differ 57 // slightly from the embedded timestamp). If zero, audit records are returned since beginning 58 // of time. ··· 63 Until time.Time 64 } 65 66 - type QueryAuditLogResult struct { 67 ID AuditID 68 Err error 69 } ··· 130 QueryAuditLog(ctx context.Context, id AuditID) (record *AuditRecord, err error) 131 132 // Retrieve records from the audit log by time range. 133 - SearchAuditLog(ctx context.Context, opts QueryAuditLogOptions) iter.Seq[QueryAuditLogResult] 134 } 135 136 - func CreateBackend(config *StorageConfig) (backend Backend, err error) { 137 switch config.Type { 138 case "fs": 139 - if backend, err = NewFSBackend(context.Background(), &config.FS); err != nil { 140 err = fmt.Errorf("fs backend: %w", err) 141 } 142 case "s3": 143 - if backend, err = NewS3Backend(context.Background(), &config.S3); err != nil { 144 err = fmt.Errorf("s3 backend: %w", err) 145 } 146 default:
··· 52 IfMatch string 53 } 54 55 + type SearchAuditLogOptions struct { 56 // Inclusive lower bound on returned audit records, per their Snowflake ID (which may differ 57 // slightly from the embedded timestamp). If zero, audit records are returned since beginning 58 // of time. ··· 63 Until time.Time 64 } 65 66 + type SearchAuditLogResult struct { 67 ID AuditID 68 Err error 69 } ··· 130 QueryAuditLog(ctx context.Context, id AuditID) (record *AuditRecord, err error) 131 132 // Retrieve records from the audit log by time range. 133 + SearchAuditLog(ctx context.Context, opts SearchAuditLogOptions) iter.Seq2[AuditID, error] 134 } 135 136 + func CreateBackend(ctx context.Context, config *StorageConfig) (backend Backend, err error) { 137 switch config.Type { 138 case "fs": 139 + if backend, err = NewFSBackend(ctx, &config.FS); err != nil { 140 err = fmt.Errorf("fs backend: %w", err) 141 } 142 case "s3": 143 + if backend, err = NewS3Backend(ctx, &config.S3); err != nil { 144 err = fmt.Errorf("s3 backend: %w", err) 145 } 146 default:
+13 -15
src/backend_fs.go
··· 434 } 435 436 func (fs *FSBackend) SearchAuditLog( 437 - ctx context.Context, opts QueryAuditLogOptions, 438 - ) iter.Seq[QueryAuditLogResult] { 439 - return func(yield func(QueryAuditLogResult) bool) { 440 iofs.WalkDir(fs.auditRoot.FS(), ".", 441 func(path string, entry iofs.DirEntry, err error) error { 442 if path == "." { 443 - return nil 444 } 445 - var result QueryAuditLogResult 446 if err != nil { 447 - result.Err = err 448 - } else if id, err := ParseAuditID(path); err != nil { 449 - result.Err = err 450 } else if !opts.Since.IsZero() && id.CompareTime(opts.Since) < 0 { 451 - return nil 452 } else if !opts.Until.IsZero() && id.CompareTime(opts.Until) > 0 { 453 - return nil 454 - } else { 455 - result.ID = id 456 } 457 - if !yield(result) { 458 - return iofs.SkipAll 459 } else { 460 - return nil 461 } 462 }) 463 }
··· 434 } 435 436 func (fs *FSBackend) SearchAuditLog( 437 + ctx context.Context, opts SearchAuditLogOptions, 438 + ) iter.Seq2[AuditID, error] { 439 + return func(yield func(AuditID, error) bool) { 440 iofs.WalkDir(fs.auditRoot.FS(), ".", 441 func(path string, entry iofs.DirEntry, err error) error { 442 if path == "." { 443 + return nil // skip 444 } 445 + var id AuditID 446 if err != nil { 447 + // report error 448 + } else if id, err = ParseAuditID(path); err != nil { 449 + // report error 450 } else if !opts.Since.IsZero() && id.CompareTime(opts.Since) < 0 { 451 + return nil // skip 452 } else if !opts.Until.IsZero() && id.CompareTime(opts.Until) > 0 { 453 + return nil // skip 454 } 455 + if !yield(id, err) { 456 + return iofs.SkipAll // break 457 } else { 458 + return nil // continue 459 } 460 }) 461 }
+8 -9
src/backend_s3.go
··· 734 } 735 736 func (s3 *S3Backend) SearchAuditLog( 737 - ctx context.Context, opts QueryAuditLogOptions, 738 - ) iter.Seq[QueryAuditLogResult] { 739 - return func(yield func(QueryAuditLogResult) bool) { 740 logc.Printf(ctx, "s3: query audit\n") 741 742 ctx, cancel := context.WithCancel(ctx) ··· 746 for object := range s3.client.ListObjectsIter(ctx, s3.bucket, minio.ListObjectsOptions{ 747 Prefix: prefix, 748 }) { 749 - var result QueryAuditLogResult 750 if object.Err != nil { 751 - result.Err = object.Err 752 - } else if id, err := ParseAuditID(strings.TrimPrefix(object.Key, prefix)); err != nil { 753 - result.Err = err 754 } else { 755 - result.ID = id 756 } 757 - if !yield(result) { 758 break 759 } 760 }
··· 734 } 735 736 func (s3 *S3Backend) SearchAuditLog( 737 + ctx context.Context, opts SearchAuditLogOptions, 738 + ) iter.Seq2[AuditID, error] { 739 + return func(yield func(AuditID, error) bool) { 740 logc.Printf(ctx, "s3: query audit\n") 741 742 ctx, cancel := context.WithCancel(ctx) ··· 746 for object := range s3.client.ListObjectsIter(ctx, s3.bucket, minio.ListObjectsOptions{ 747 Prefix: prefix, 748 }) { 749 + var id AuditID 750 + var err error 751 if object.Err != nil { 752 + err = object.Err 753 } else { 754 + id, err = ParseAuditID(strings.TrimPrefix(object.Key, prefix)) 755 } 756 + if !yield(id, err) { 757 break 758 } 759 }
+42 -9
src/main.go
··· 20 21 automemlimit "github.com/KimMachineGun/automemlimit/memlimit" 22 "github.com/c2h5oh/datasize" 23 "github.com/kankanreno/go-snowflake" 24 "github.com/prometheus/client_golang/prometheus/promhttp" 25 ) 26 27 var config *Config ··· 175 fmt.Fprintf(os.Stderr, "(admin) "+ 176 "git-pages {-run-migration <name>|-freeze-domain <domain>|-unfreeze-domain <domain>}\n") 177 fmt.Fprintf(os.Stderr, "(audit) "+ 178 - "git-pages {-audit-read <id>}\n") 179 fmt.Fprintf(os.Stderr, "(info) "+ 180 "git-pages {-print-config-env-vars|-print-config}\n") 181 fmt.Fprintf(os.Stderr, "(cli) "+ ··· 209 "prevent any site uploads to a given `domain`") 210 unfreezeDomain := flag.String("unfreeze-domain", "", 211 "allow site uploads to a `domain` again after it has been frozen") 212 auditRead := flag.String("audit-read", "", 213 "extract contents of audit record `id` to files '<id>-*'") 214 flag.Parse() ··· 222 *updateSite != "", 223 *freezeDomain != "", 224 *unfreezeDomain != "", 225 *auditRead != "", 226 } { 227 if selected { ··· 270 271 switch { 272 case *runMigration != "": 273 - if backend, err = CreateBackend(&config.Storage); err != nil { 274 logc.Fatalln(ctx, err) 275 } 276 ··· 279 } 280 281 case *getBlob != "": 282 - if backend, err = CreateBackend(&config.Storage); err != nil { 283 logc.Fatalln(ctx, err) 284 } 285 ··· 290 io.Copy(fileOutputArg(), reader) 291 292 case *getManifest != "": 293 - if backend, err = CreateBackend(&config.Storage); err != nil { 294 logc.Fatalln(ctx, err) 295 } 296 ··· 302 fmt.Fprintln(fileOutputArg(), string(ManifestJSON(manifest))) 303 304 case *getArchive != "": 305 - if backend, err = CreateBackend(&config.Storage); err != nil { 306 logc.Fatalln(ctx, err) 307 } 308 ··· 317 } 318 319 case *updateSite != "": 320 - if backend, err = CreateBackend(&config.Storage); err != nil { 321 logc.Fatalln(ctx, err) 322 } 323 ··· 382 } 383 384 case *freezeDomain != "" || *unfreezeDomain != "": 385 var domain string 386 var freeze bool 387 if *freezeDomain != "" { ··· 392 freeze = false 393 } 394 395 - if backend, err = CreateBackend(&config.Storage); err != nil { 396 logc.Fatalln(ctx, err) 397 } 398 ··· 405 logc.Println(ctx, "thawed") 406 } 407 408 case *auditRead != "": 409 - if backend, err = CreateBackend(&config.Storage); err != nil { 410 logc.Fatalln(ctx, err) 411 } 412 ··· 475 caddyListener := listen(ctx, "caddy", config.Server.Caddy) 476 metricsListener := listen(ctx, "metrics", config.Server.Metrics) 477 478 - if backend, err = CreateBackend(&config.Storage); err != nil { 479 logc.Fatalln(ctx, err) 480 } 481 backend = NewObservedBackend(backend)
··· 20 21 automemlimit "github.com/KimMachineGun/automemlimit/memlimit" 22 "github.com/c2h5oh/datasize" 23 + "github.com/fatih/color" 24 "github.com/kankanreno/go-snowflake" 25 "github.com/prometheus/client_golang/prometheus/promhttp" 26 + "google.golang.org/protobuf/proto" 27 ) 28 29 var config *Config ··· 177 fmt.Fprintf(os.Stderr, "(admin) "+ 178 "git-pages {-run-migration <name>|-freeze-domain <domain>|-unfreeze-domain <domain>}\n") 179 fmt.Fprintf(os.Stderr, "(audit) "+ 180 + "git-pages {-audit-log|-audit-read <id>}\n") 181 fmt.Fprintf(os.Stderr, "(info) "+ 182 "git-pages {-print-config-env-vars|-print-config}\n") 183 fmt.Fprintf(os.Stderr, "(cli) "+ ··· 211 "prevent any site uploads to a given `domain`") 212 unfreezeDomain := flag.String("unfreeze-domain", "", 213 "allow site uploads to a `domain` again after it has been frozen") 214 + auditLog := flag.Bool("audit-log", false, 215 + "display audit log") 216 auditRead := flag.String("audit-read", "", 217 "extract contents of audit record `id` to files '<id>-*'") 218 flag.Parse() ··· 226 *updateSite != "", 227 *freezeDomain != "", 228 *unfreezeDomain != "", 229 + *auditLog, 230 *auditRead != "", 231 } { 232 if selected { ··· 275 276 switch { 277 case *runMigration != "": 278 + if backend, err = CreateBackend(ctx, &config.Storage); err != nil { 279 logc.Fatalln(ctx, err) 280 } 281 ··· 284 } 285 286 case *getBlob != "": 287 + if backend, err = CreateBackend(ctx, &config.Storage); err != nil { 288 logc.Fatalln(ctx, err) 289 } 290 ··· 295 io.Copy(fileOutputArg(), reader) 296 297 case *getManifest != "": 298 + if backend, err = CreateBackend(ctx, &config.Storage); err != nil { 299 logc.Fatalln(ctx, err) 300 } 301 ··· 307 fmt.Fprintln(fileOutputArg(), string(ManifestJSON(manifest))) 308 309 case *getArchive != "": 310 + if backend, err = CreateBackend(ctx, &config.Storage); err != nil { 311 logc.Fatalln(ctx, err) 312 } 313 ··· 322 } 323 324 case *updateSite != "": 325 + ctx = WithPrincipal(ctx) 326 + GetPrincipal(ctx).CliAdmin = proto.Bool(true) 327 + 328 + if backend, err = CreateBackend(ctx, &config.Storage); err != nil { 329 logc.Fatalln(ctx, err) 330 } 331 ··· 390 } 391 392 case *freezeDomain != "" || *unfreezeDomain != "": 393 + ctx = WithPrincipal(ctx) 394 + GetPrincipal(ctx).CliAdmin = proto.Bool(true) 395 + 396 var domain string 397 var freeze bool 398 if *freezeDomain != "" { ··· 403 freeze = false 404 } 405 406 + if backend, err = CreateBackend(ctx, &config.Storage); err != nil { 407 logc.Fatalln(ctx, err) 408 } 409 ··· 416 logc.Println(ctx, "thawed") 417 } 418 419 + case *auditLog: 420 + if backend, err = CreateBackend(ctx, &config.Storage); err != nil { 421 + logc.Fatalln(ctx, err) 422 + } 423 + 424 + for id, err := range backend.SearchAuditLog(ctx, SearchAuditLogOptions{}) { 425 + if err != nil { 426 + logc.Fatalln(ctx, err) 427 + } 428 + record, err := backend.QueryAuditLog(ctx, id) 429 + if err != nil { 430 + logc.Fatalln(ctx, err) 431 + } 432 + fmt.Fprintf(color.Output, "%s %s %s %s %s\n", 433 + record.GetAuditID().String(), 434 + color.HiWhiteString(record.GetTimestamp().AsTime().UTC().Format(time.RFC3339)), 435 + color.HiMagentaString(record.DescribePrincipal()), 436 + color.HiGreenString(record.DescribeResource()), 437 + record.GetEvent(), 438 + ) 439 + } 440 + 441 case *auditRead != "": 442 + if backend, err = CreateBackend(ctx, &config.Storage); err != nil { 443 logc.Fatalln(ctx, err) 444 } 445 ··· 508 caddyListener := listen(ctx, "caddy", config.Server.Caddy) 509 metricsListener := listen(ctx, "metrics", config.Server.Metrics) 510 511 + if backend, err = CreateBackend(ctx, &config.Storage); err != nil { 512 logc.Fatalln(ctx, err) 513 } 514 backend = NewObservedBackend(backend)
+5 -5
src/observe.go
··· 457 } 458 459 func (backend *observedBackend) SearchAuditLog( 460 - ctx context.Context, opts QueryAuditLogOptions, 461 - ) iter.Seq[QueryAuditLogResult] { 462 - return func(yield func(QueryAuditLogResult) bool) { 463 span, ctx := ObserveFunction(ctx, "SearchAuditLog", 464 "audit.search.since", opts.Since, 465 "audit.search.until", opts.Until, 466 ) 467 - for result := range backend.inner.SearchAuditLog(ctx, opts) { 468 - if !yield(result) { 469 break 470 } 471 }
··· 457 } 458 459 func (backend *observedBackend) SearchAuditLog( 460 + ctx context.Context, opts SearchAuditLogOptions, 461 + ) iter.Seq2[AuditID, error] { 462 + return func(yield func(AuditID, error) bool) { 463 span, ctx := ObserveFunction(ctx, "SearchAuditLog", 464 "audit.search.since", opts.Since, 465 "audit.search.until", opts.Until, 466 ) 467 + for id, err := range backend.inner.SearchAuditLog(ctx, opts) { 468 + if !yield(id, err) { 469 break 470 } 471 }
+11 -2
src/schema.pb.go
··· 749 type Principal struct { 750 state protoimpl.MessageState `protogen:"open.v1"` 751 IpAddress *string `protobuf:"bytes,1,opt,name=ip_address,json=ipAddress" json:"ip_address,omitempty"` 752 unknownFields protoimpl.UnknownFields 753 sizeCache protoimpl.SizeCache 754 } ··· 788 return *x.IpAddress 789 } 790 return "" 791 } 792 793 var File_schema_proto protoreflect.FileDescriptor ··· 845 "\x06domain\x18\n" + 846 " \x01(\tR\x06domain\x12\x18\n" + 847 "\aproject\x18\v \x01(\tR\aproject\x12%\n" + 848 - "\bmanifest\x18\f \x01(\v2\t.ManifestR\bmanifest\"*\n" + 849 "\tPrincipal\x12\x1d\n" + 850 "\n" + 851 - "ip_address\x18\x01 \x01(\tR\tipAddress*V\n" + 852 "\x04Type\x12\x10\n" + 853 "\fInvalidEntry\x10\x00\x12\r\n" + 854 "\tDirectory\x10\x01\x12\x0e\n" +
··· 749 type Principal struct { 750 state protoimpl.MessageState `protogen:"open.v1"` 751 IpAddress *string `protobuf:"bytes,1,opt,name=ip_address,json=ipAddress" json:"ip_address,omitempty"` 752 + CliAdmin *bool `protobuf:"varint,2,opt,name=cli_admin,json=cliAdmin" json:"cli_admin,omitempty"` 753 unknownFields protoimpl.UnknownFields 754 sizeCache protoimpl.SizeCache 755 } ··· 789 return *x.IpAddress 790 } 791 return "" 792 + } 793 + 794 + func (x *Principal) GetCliAdmin() bool { 795 + if x != nil && x.CliAdmin != nil { 796 + return *x.CliAdmin 797 + } 798 + return false 799 } 800 801 var File_schema_proto protoreflect.FileDescriptor ··· 853 "\x06domain\x18\n" + 854 " \x01(\tR\x06domain\x12\x18\n" + 855 "\aproject\x18\v \x01(\tR\aproject\x12%\n" + 856 + "\bmanifest\x18\f \x01(\v2\t.ManifestR\bmanifest\"G\n" + 857 "\tPrincipal\x12\x1d\n" + 858 "\n" + 859 + "ip_address\x18\x01 \x01(\tR\tipAddress\x12\x1b\n" + 860 + "\tcli_admin\x18\x02 \x01(\bR\bcliAdmin*V\n" + 861 "\x04Type\x12\x10\n" + 862 "\fInvalidEntry\x10\x00\x12\r\n" + 863 "\tDirectory\x10\x01\x12\x0e\n" +
+1
src/schema.proto
··· 131 132 message Principal { 133 string ip_address = 1; 134 }
··· 131 132 message Principal { 133 string ip_address = 1; 134 + bool cli_admin = 2; 135 }