Skip to content

fix: add requester IP to workspace build audit logs #10242

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions cli/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/trace"
"golang.org/x/mod/semver"
"golang.org/x/oauth2"
Expand Down Expand Up @@ -2020,6 +2022,13 @@ func ConfigureTraceProvider(
sqlDriver = "postgres"
)

otel.SetTextMapPropagator(
propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{},
propagation.Baggage{},
),
)

if cfg.Trace.Enable.Value() || cfg.Trace.DataDog.Value() || cfg.Trace.HoneycombAPIKey != "" {
sdkTracerProvider, _closeTracing, err := tracing.TracerProvider(ctx, "coderd", tracing.TracerOpts{
Default: cfg.Trace.Enable.Value(),
Expand Down
2 changes: 1 addition & 1 deletion cli/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ package cli
import (
"time"

"github.com/coder/pretty"
"github.com/google/uuid"
"golang.org/x/xerrors"

"github.com/coder/coder/v2/cli/clibase"
"github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/pretty"
)

func (r *RootCmd) templates() *clibase.Cmd {
Expand Down
2 changes: 1 addition & 1 deletion cli/templateversionarchive.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import (
"strings"
"time"

"github.com/coder/pretty"
"golang.org/x/xerrors"

"github.com/coder/coder/v2/cli/clibase"
"github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/pretty"
)

func (r *RootCmd) unarchiveTemplateVersion() *clibase.Cmd {
Expand Down
2 changes: 1 addition & 1 deletion cli/templateversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import (
"strings"
"time"

"github.com/coder/pretty"
"github.com/google/uuid"
"golang.org/x/xerrors"

"github.com/coder/coder/v2/cli/clibase"
"github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/pretty"
)

func (r *RootCmd) templateVersions() *clibase.Cmd {
Expand Down
1 change: 0 additions & 1 deletion cli/user_delete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ func TestUserDelete(t *testing.T) {
// pw, err := cryptorand.String(16)
// require.NoError(t, err)

// fmt.Println(aUser.OrganizationID)
// toDelete, err := client.CreateUser(ctx, codersdk.CreateUserRequest{
// Email: "colin5@coder.com",
// Username: "coolin",
Expand Down
67 changes: 61 additions & 6 deletions coderd/audit/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (

"github.com/google/uuid"
"github.com/sqlc-dev/pqtype"
"go.opentelemetry.io/otel/baggage"
"golang.org/x/xerrors"

"cdr.dev/slog"
"github.com/coder/coder/v2/coderd/database"
Expand Down Expand Up @@ -54,6 +56,7 @@ type BuildAuditParams[T Auditable] struct {
Status int
Action database.AuditAction
OrganizationID uuid.UUID
IP string
AdditionalFields json.RawMessage

New T
Expand Down Expand Up @@ -248,9 +251,7 @@ func InitRequest[T Auditable](w http.ResponseWriter, p *RequestParams) (*Request
// WorkspaceBuildAudit creates an audit log for a workspace build.
// The audit log is committed upon invocation.
func WorkspaceBuildAudit[T Auditable](ctx context.Context, p *BuildAuditParams[T]) {
// As the audit request has not been initiated directly by a user, we omit
// certain user details.
ip := parseIP("")
ip := parseIP(p.IP)

diff := Diff(p.Audit, p.Old, p.New)
var err error
Expand Down Expand Up @@ -280,16 +281,70 @@ func WorkspaceBuildAudit[T Auditable](ctx context.Context, p *BuildAuditParams[T
RequestID: p.JobID,
AdditionalFields: p.AdditionalFields,
}
exportErr := p.Audit.Export(ctx, auditLog)
if exportErr != nil {
err = p.Audit.Export(ctx, auditLog)
if err != nil {
p.Log.Error(ctx, "export audit log",
slog.F("audit_log", auditLog),
slog.Error(err),
)
return
}
}

type WorkspaceBuildBaggage struct {
IP string
}

func (b WorkspaceBuildBaggage) Props() ([]baggage.Property, error) {
ipProp, err := baggage.NewKeyValueProperty("ip", b.IP)
if err != nil {
return nil, xerrors.Errorf("create ip kv property: %w", err)
}

return []baggage.Property{ipProp}, nil
}

func WorkspaceBuildBaggageFromRequest(r *http.Request) WorkspaceBuildBaggage {
return WorkspaceBuildBaggage{IP: r.RemoteAddr}
}

type Baggage interface {
Props() ([]baggage.Property, error)
}

func BaggageToContext(ctx context.Context, d Baggage) (context.Context, error) {
props, err := d.Props()
if err != nil {
return ctx, xerrors.Errorf("create baggage properties: %w", err)
}

m, err := baggage.NewMember("audit", "baggage", props...)
if err != nil {
return ctx, xerrors.Errorf("create new baggage member: %w", err)
}

b, err := baggage.New(m)
if err != nil {
return ctx, xerrors.Errorf("create new baggage carrier: %w", err)
}

return baggage.ContextWithBaggage(ctx, b), nil
}

func BaggageFromContext(ctx context.Context) WorkspaceBuildBaggage {
d := WorkspaceBuildBaggage{}
b := baggage.FromContext(ctx)
props := b.Member("audit").Properties()
for _, prop := range props {
switch prop.Key() {
case "ip":
d.IP, _ = prop.Value()
default:
}
}

return d
}

func either[T Auditable, R any](old, new T, fn func(T) R, auditAction database.AuditAction) R {
if ResourceID(new) != uuid.Nil {
return fn(new)
Expand Down
33 changes: 33 additions & 0 deletions coderd/audit/request_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package audit_test

import (
"context"
"testing"

"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/propagation"

"github.com/coder/coder/v2/coderd/audit"
)

func TestBaggage(t *testing.T) {
t.Parallel()
prop := propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{},
propagation.Baggage{},
)

expected := audit.WorkspaceBuildBaggage{
IP: "127.0.0.1",
}

ctx, err := audit.BaggageToContext(context.Background(), expected)
require.NoError(t, err)

carrier := propagation.MapCarrier{}
prop.Inject(ctx, carrier)
bCtx := prop.Extract(ctx, carrier)
got := audit.BaggageFromContext(bCtx)

require.Equal(t, expected, got)
}
3 changes: 2 additions & 1 deletion coderd/autobuild/lifecycle_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,8 @@ func (e *Executor) runOnce(t time.Time) Stats {
builder = builder.ActiveVersion()
}

build, job, err = builder.Build(e.ctx, tx, nil)
build, job, err = builder.Build(e.ctx, tx, nil, audit.WorkspaceBuildBaggage{IP: "127.0.0.1"})

if err != nil {
log.Error(e.ctx, "unable to transition workspace",
slog.F("transition", nextTransition),
Expand Down
1 change: 1 addition & 0 deletions coderd/database/dbfake/dbfake.go
Original file line number Diff line number Diff line change
Expand Up @@ -4564,6 +4564,7 @@ func (q *FakeQuerier) InsertProvisionerJob(_ context.Context, arg database.Inser
Type: arg.Type,
Input: arg.Input,
Tags: arg.Tags,
TraceMetadata: arg.TraceMetadata,
}
job.JobStatus = provisonerJobStatus(job)
q.provisionerJobs = append(q.provisionerJobs, job)
Expand Down
3 changes: 1 addition & 2 deletions coderd/externalauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ import (
"fmt"
"net/http"

"golang.org/x/sync/errgroup"

"github.com/sqlc-dev/pqtype"
"golang.org/x/sync/errgroup"

"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbtime"
Expand Down
6 changes: 6 additions & 0 deletions coderd/provisionerdserver/provisionerdserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -903,12 +903,15 @@ func (s *server) FailJob(ctx context.Context, failJob *proto.FailedJob) (*proto.
s.Logger.Error(ctx, "marshal workspace resource info for failed job", slog.Error(err))
}

bag := audit.BaggageFromContext(ctx)

audit.WorkspaceBuildAudit(ctx, &audit.BuildAuditParams[database.WorkspaceBuild]{
Audit: *auditor,
Log: s.Logger,
UserID: job.InitiatorID,
OrganizationID: workspace.OrganizationID,
JobID: job.ID,
IP: bag.IP,
Action: auditAction,
Old: previousBuild,
New: build,
Expand Down Expand Up @@ -1245,12 +1248,15 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
s.Logger.Error(ctx, "marshal resource info for successful job", slog.Error(err))
}

bag := audit.BaggageFromContext(ctx)

audit.WorkspaceBuildAudit(ctx, &audit.BuildAuditParams[database.WorkspaceBuild]{
Audit: *auditor,
Log: s.Logger,
UserID: job.InitiatorID,
OrganizationID: workspace.OrganizationID,
JobID: job.ID,
IP: bag.IP,
Action: auditAction,
Old: previousBuild,
New: workspaceBuild,
Expand Down
2 changes: 2 additions & 0 deletions coderd/workspacebuilds.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (

"cdr.dev/slog"

"github.com/coder/coder/v2/coderd/audit"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/db2sdk"
"github.com/coder/coder/v2/coderd/database/dbauthz"
Expand Down Expand Up @@ -372,6 +373,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
func(action rbac.Action, object rbac.Objecter) bool {
return api.Authorize(r, action, object)
},
audit.WorkspaceBuildBaggageFromRequest(r),
)
var buildErr wsbuilder.BuildError
if xerrors.As(err, &buildErr) {
Expand Down
Loading