As we reevaluate how to best support and maintain Staging Ref in the future, we encourage development teams using this environment to highlight their use cases in the following issue: https://gitlab.com/gitlab-com/gl-infra/software-delivery/framework/software-delivery-framework-issue-tracker/-/issues/36.

Skip to content
Snippets Groups Projects
Commit b786ba0a authored by rrylee's avatar rrylee
Browse files

feat: Call git_autid_event when git operation

parent dea8e58a
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -187,6 +187,9 @@ func successAPI(t *testing.T, handlers ...customHandler) http.Handler {
_, err := fmt.Fprint(w, response)
require.NoError(t, err)
case "/api/v4/internal/shellhorse/git_audit_event":
// response success
return
default:
t.Logf("Unexpected request to successAPI: %s", r.URL.EscapedPath())
t.FailNow()
Loading
Loading
Loading
Loading
@@ -24,6 +24,8 @@ require (
gopkg.in/yaml.v3 v3.0.1
)
require github.com/Microsoft/go-winio v0.6.0 // indirect
require (
cloud.google.com/go v0.110.0 // indirect
cloud.google.com/go/compute v1.19.1 // indirect
Loading
Loading
@@ -33,8 +35,7 @@ require (
cloud.google.com/go/trace v1.9.0 // indirect
contrib.go.opencensus.io/exporter/stackdriver v0.13.14 // indirect
github.com/DataDog/datadog-go v4.4.0+incompatible // indirect
github.com/DataDog/sketches-go v1.0.0 // indirect
github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/DataDog/sketches-go v1.0.0 // indirect; indirect github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/aws/aws-sdk-go v1.44.200 // indirect
github.com/beevik/ntp v1.0.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
Loading
Loading
package gitauditevent
import (
"context"
pb "gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/commandargs"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/shared/accessverifier"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/config"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/gitlabnet/gitauditevent"
"gitlab.com/gitlab-org/labkit/log"
)
// Audit is a command function that call rails to audit `git-receive-pack` and `git-upload-pack` events.
// Calling rails for git audit events is always followed by `git clone`, `git pull`, `git push`,
// So we do not guarantee that two requests will succeed, this is a Distributed transaction.
// Let's just simply ignore errors.
func Audit(ctx context.Context, commandType commandargs.CommandType, c *config.Config, response *accessverifier.Response, packfileStats *pb.PackfileNegotiationStatistics) {
ctxlog := log.WithContextFields(ctx, log.Fields{
"gl_repository": response.Repo,
"command": commandType,
"username": response.Username,
})
ctxlog.Debug("sending git audit event")
gitAuditClient, errOnlyLog := gitauditevent.NewClient(c)
if errOnlyLog != nil {
ctxlog.Errorf("failed to create gitauditevent client: %v", errOnlyLog)
return
}
errOnlyLog = gitAuditClient.Audit(ctx, response.Username, commandType, response.Repo, packfileStats)
if errOnlyLog != nil {
ctxlog.Errorf("failed to audit git event: %v", errOnlyLog)
return
}
}
package gitauditevent
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/gitlab-shell/v14/client/testserver"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/commandargs"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/config"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/gitlabnet/accessverifier"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/gitlabnet/gitauditevent"
)
var (
testUsername = "gitlab-shell"
testRepo = "project-1"
)
func TestGitAudit(t *testing.T) {
called := false
requests := []testserver.TestRequestHandler{
{
Path: "/api/v4/internal/shellhorse/git_audit_event",
Handler: func(w http.ResponseWriter, r *http.Request) {
called = true
body, err := io.ReadAll(r.Body)
require.NoError(t, err)
defer r.Body.Close()
var request *gitauditevent.Request
require.NoError(t, json.Unmarshal(body, &request))
assert.Equal(t, testUsername, request.Username)
assert.Equal(t, testRepo, request.Repo)
w.WriteHeader(http.StatusOK)
},
},
}
defer func() {
assert.True(t, called)
}()
s := &commandargs.Shell{
CommandType: commandargs.UploadArchive,
}
url := testserver.StartSocketHttpServer(t, requests)
Audit(context.Background(), s.CommandType, &config.Config{GitlabUrl: url}, &accessverifier.Response{
Username: testUsername,
Repo: testRepo,
}, nil)
}
Loading
Loading
@@ -4,6 +4,7 @@ import (
"context"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/commandargs"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/gitauditevent"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/githttp"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/readwriter"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/shared/accessverifier"
Loading
Loading
@@ -53,7 +54,13 @@ func (c *Command) Execute(ctx context.Context) error {
return customAction.Execute(ctx, response)
}
return c.performGitalyCall(ctx, response)
err = c.performGitalyCall(ctx, response)
if err != nil {
return err
}
gitauditevent.Audit(ctx, c.Args.CommandType, c.Config, response, nil /* keep nil for `git-receive-pack`*/)
return nil
}
func (c *Command) verifyAccess(ctx context.Context, repo string) (*accessverifier.Response, error) {
Loading
Loading
Loading
Loading
@@ -12,7 +12,7 @@ import (
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/handler"
)
func (c *Command) performGitalyCall(ctx context.Context, response *accessverifier.Response) error {
func (c *Command) performGitalyCall(ctx context.Context, response *accessverifier.Response) (*pb.PackfileNegotiationStatistics, error) {
gc := handler.NewGitalyCommand(c.Config, string(commandargs.UploadPack), response)
request := &pb.SSHUploadPackWithSidechannelRequest{
Loading
Loading
@@ -21,12 +21,24 @@ func (c *Command) performGitalyCall(ctx context.Context, response *accessverifie
GitConfigOptions: response.GitConfigOptions,
}
return gc.RunGitalyCommand(ctx, func(ctx context.Context, conn *grpc.ClientConn) (int32, error) {
var stats *pb.PackfileNegotiationStatistics
err := gc.RunGitalyCommand(ctx, func(ctx context.Context, conn *grpc.ClientConn) (int32, error) {
ctx, cancel := gc.PrepareContext(ctx, request.Repository, c.Args.Env)
defer cancel()
registry := c.Config.GitalyClient.SidechannelRegistry
rw := c.ReadWriter
return client.UploadPackWithSidechannel(ctx, conn, registry, rw.In, rw.Out, rw.ErrOut, request)
var (
result client.UploadPackResult
err error
)
result, err = client.UploadPackWithSidechannelWithResult(ctx, conn, registry, rw.In, rw.Out, rw.ErrOut, request)
if err == nil {
stats = result.PackfileNegotiationStatistics
}
return result.ExitCode, err
})
return stats, err
}
Loading
Loading
@@ -4,6 +4,7 @@ import (
"context"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/commandargs"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/gitauditevent"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/readwriter"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/shared/accessverifier"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/shared/customaction"
Loading
Loading
@@ -38,7 +39,13 @@ func (c *Command) Execute(ctx context.Context) error {
return customAction.Execute(ctx, response)
}
return c.performGitalyCall(ctx, response)
stats, err := c.performGitalyCall(ctx, response)
if err != nil {
return err
}
gitauditevent.Audit(ctx, c.Args.CommandType, c.Config, response, stats)
return nil
}
func (c *Command) verifyAccess(ctx context.Context, repo string) (*accessverifier.Response, error) {
Loading
Loading
Loading
Loading
@@ -9,7 +9,7 @@ import (
"sync"
"time"
yaml "gopkg.in/yaml.v3"
"gopkg.in/yaml.v3"
"gitlab.com/gitlab-org/gitlab-shell/v14/client"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/gitaly"
Loading
Loading
package gitauditevent
import (
"context"
"fmt"
pb "gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb"
"gitlab.com/gitlab-org/gitlab-shell/v14/client"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/commandargs"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/config"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/gitlabnet"
)
const uri = "/api/v4/internal/shellhorse/git_audit_event"
type Client struct {
config *config.Config
client *client.GitlabNetClient
}
func NewClient(config *config.Config) (*Client, error) {
client, err := gitlabnet.GetClient(config)
if err != nil {
return nil, fmt.Errorf("error creating http client: %w", err)
}
return &Client{config: config, client: client}, nil
}
type Request struct {
Action commandargs.CommandType `json:"action"`
Protocol string `json:"protocol"`
Repo string `json:"gl_repository"`
Username string `json:"username"`
PackfileStats *pb.PackfileNegotiationStatistics `json:"packfile_stats,omitempty"`
}
func (c *Client) Audit(ctx context.Context, username string, action commandargs.CommandType, repo string, packfileStats *pb.PackfileNegotiationStatistics) error {
request := &Request{
Action: action,
Repo: repo,
Protocol: "ssh",
Username: username,
PackfileStats: packfileStats,
}
response, err := c.client.Post(ctx, uri, request)
if err != nil {
return err
}
defer response.Body.Close()
return nil
}
package gitauditevent
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
pb "gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb"
"gitlab.com/gitlab-org/gitlab-shell/v14/client/testserver"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/commandargs"
"gitlab.com/gitlab-org/gitlab-shell/v14/internal/config"
)
var (
testUsername = "gitlab-shell"
testAction commandargs.CommandType = "git-upload-pack"
testRepo = "gitlab-org/gitlab-shell"
testPackfileWants int64 = 100
testPackfileHaves int64 = 100
)
func TestAudit(t *testing.T) {
client := setup(t, http.StatusOK)
err := client.Audit(context.Background(), testUsername, testAction, testRepo, &pb.PackfileNegotiationStatistics{
Wants: testPackfileWants,
Haves: testPackfileHaves,
})
require.NoError(t, err)
}
func TestAuditFailed(t *testing.T) {
client := setup(t, http.StatusBadRequest)
err := client.Audit(context.Background(), testUsername, testAction, testRepo, &pb.PackfileNegotiationStatistics{
Wants: testPackfileWants,
Haves: testPackfileHaves,
})
require.Error(t, err)
}
func setup(t *testing.T, responseStatus int) *Client {
requests := []testserver.TestRequestHandler{
{
Path: uri,
Handler: func(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
require.NoError(t, err)
defer r.Body.Close()
var request *Request
require.NoError(t, json.Unmarshal(body, &request))
assert.Equal(t, testUsername, request.Username)
assert.Equal(t, testAction, request.Action)
assert.Equal(t, testRepo, request.Repo)
assert.Equal(t, "ssh", request.Protocol)
assert.Equal(t, testPackfileWants, request.PackfileStats.Wants)
assert.Equal(t, testPackfileHaves, request.PackfileStats.Haves)
w.WriteHeader(responseStatus)
},
},
}
url := testserver.StartSocketHttpServer(t, requests)
client, err := NewClient(&config.Config{GitlabUrl: url})
require.NoError(t, err)
return client
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment