diff --git a/.changeset/spotty-ducks-learn.md b/.changeset/spotty-ducks-learn.md new file mode 100644 index 00000000..4b8e41c2 --- /dev/null +++ b/.changeset/spotty-ducks-learn.md @@ -0,0 +1,6 @@ +--- +"github.com/livekit/protocol": minor +"@livekit/protocol": minor +--- + +Move egress request redacting routines to protocol diff --git a/egress/redact.go b/egress/redact.go new file mode 100644 index 00000000..905467a8 --- /dev/null +++ b/egress/redact.go @@ -0,0 +1,66 @@ +package egress + +import ( + "github.com/livekit/protocol/livekit" + "github.com/livekit/protocol/utils" +) + +func RedactUpload(req UploadRequest) { + if s3 := req.GetS3(); s3 != nil { + s3.AccessKey = utils.Redact(s3.AccessKey, "{access_key}") + s3.Secret = utils.Redact(s3.Secret, "{secret}") + return + } + + if gcp := req.GetGcp(); gcp != nil { + gcp.Credentials = utils.Redact(gcp.Credentials, "{credentials}") + return + } + + if azure := req.GetAzure(); azure != nil { + azure.AccountName = utils.Redact(azure.AccountName, "{account_name}") + azure.AccountKey = utils.Redact(azure.AccountKey, "{account_key}") + return + } + + if aliOSS := req.GetAliOSS(); aliOSS != nil { + aliOSS.AccessKey = utils.Redact(aliOSS.AccessKey, "{access_key}") + aliOSS.Secret = utils.Redact(aliOSS.Secret, "{secret}") + return + } +} + +func RedactEncodedOutputs(out EncodedOutput) { + if files := out.GetFileOutputs(); len(files) == 1 { + RedactUpload(files[0]) + } + if streams := out.GetStreamOutputs(); len(streams) == 1 { + RedactStreamKeys(streams[0]) + } + if segments := out.GetSegmentOutputs(); len(segments) == 1 { + RedactUpload(segments[0]) + } + if o, ok := out.(EncodedOutputDeprecated); ok { + if file := o.GetFile(); file != nil { + RedactUpload(file) + } else if stream := o.GetStream(); stream != nil { + RedactStreamKeys(stream) + } else if segment := o.GetSegments(); segment != nil { + RedactUpload(segment) + } + } +} + +func RedactDirectOutputs(out DirectOutput) { + if f := out.GetFile(); f != nil { + RedactUpload(f) + } +} + +func RedactStreamKeys(stream *livekit.StreamOutput) { + for i, url := range stream.Urls { + if redacted, ok := utils.RedactStreamKey(url); ok { + stream.Urls[i] = redacted + } + } +} diff --git a/egress/redact_test.go b/egress/redact_test.go new file mode 100644 index 00000000..6d6edc1a --- /dev/null +++ b/egress/redact_test.go @@ -0,0 +1,141 @@ +package egress + +import ( + "testing" + + "google.golang.org/protobuf/proto" + + "github.com/livekit/protocol/livekit" + "github.com/stretchr/testify/require" +) + +var ( + file1 = &livekit.EncodedFileOutput{ + Output: &livekit.EncodedFileOutput_S3{ + S3: &livekit.S3Upload{ + AccessKey: "ACCESS_KEY", + Secret: "LONG_SECRET_STRING", + }, + }, + } + + file2 = &livekit.EncodedFileOutput{ + Output: &livekit.EncodedFileOutput_AliOSS{ + AliOSS: &livekit.AliOSSUpload{ + AccessKey: "ACCESS_KEY", + Secret: "LONG_SECRET_STRING", + }, + }, + } + + segments = &livekit.SegmentedFileOutput{ + Output: &livekit.SegmentedFileOutput_Gcp{ + Gcp: &livekit.GCPUpload{ + Credentials: "CREDENTIALS", + }, + }, + } + + directFile = &livekit.DirectFileOutput{ + Output: &livekit.DirectFileOutput_Azure{ + Azure: &livekit.AzureBlobUpload{ + AccountName: "ACCOUNT_NAME", + AccountKey: "ACCOUNT_KEY", + }, + }, + } +) + +func TestRedactUpload(t *testing.T) { + + cl := proto.Clone(file1) + RedactUpload(cl.(UploadRequest)) + + require.Equal(t, "{access_key}", cl.(*livekit.EncodedFileOutput).Output.(*livekit.EncodedFileOutput_S3).S3.AccessKey) + require.Equal(t, "{secret}", cl.(*livekit.EncodedFileOutput).Output.(*livekit.EncodedFileOutput_S3).S3.Secret) + + cl = proto.Clone(file2) + RedactUpload(cl.(UploadRequest)) + + require.Equal(t, "{access_key}", cl.(*livekit.EncodedFileOutput).Output.(*livekit.EncodedFileOutput_AliOSS).AliOSS.AccessKey) + require.Equal(t, "{secret}", cl.(*livekit.EncodedFileOutput).Output.(*livekit.EncodedFileOutput_AliOSS).AliOSS.Secret) + + cl = proto.Clone(segments) + RedactUpload(cl.(UploadRequest)) + + require.Equal(t, "{credentials}", cl.(*livekit.SegmentedFileOutput).Output.(*livekit.SegmentedFileOutput_Gcp).Gcp.Credentials) + + cl = proto.Clone(directFile) + RedactUpload(cl.(UploadRequest)) + + require.Equal(t, "{account_name}", cl.(*livekit.DirectFileOutput).Output.(*livekit.DirectFileOutput_Azure).Azure.AccountName) + require.Equal(t, "{account_key}", cl.(*livekit.DirectFileOutput).Output.(*livekit.DirectFileOutput_Azure).Azure.AccountKey) + +} + +func TestRedactStreamOutput(t *testing.T) { + so := &livekit.StreamOutput{ + Urls: []string{ + "rtmps://foo.bar.com/app/secret_stream_key", + }, + } + + RedactStreamKeys(so) + require.Equal(t, "rtmps://foo.bar.com/app/{sec...key}", so.Urls[0]) +} + +func TestRedactEncodedOutputs(t *testing.T) { + trackComposite := &livekit.TrackCompositeEgressRequest{ + FileOutputs: []*livekit.EncodedFileOutput{ + file1, + }, + } + + cl := proto.Clone(trackComposite) + RedactEncodedOutputs(cl.(EncodedOutput)) + + require.Equal(t, "{access_key}", cl.(*livekit.TrackCompositeEgressRequest).FileOutputs[0].Output.(*livekit.EncodedFileOutput_S3).S3.AccessKey) + require.Equal(t, "{secret}", cl.(*livekit.TrackCompositeEgressRequest).FileOutputs[0].Output.(*livekit.EncodedFileOutput_S3).S3.Secret) + + roomComposite := &livekit.RoomCompositeEgressRequest{ + FileOutputs: []*livekit.EncodedFileOutput{ + file2, + }, + } + + cl = proto.Clone(roomComposite) + RedactEncodedOutputs(cl.(EncodedOutput)) + + require.Equal(t, "{access_key}", cl.(*livekit.RoomCompositeEgressRequest).FileOutputs[0].Output.(*livekit.EncodedFileOutput_AliOSS).AliOSS.AccessKey) + require.Equal(t, "{secret}", cl.(*livekit.RoomCompositeEgressRequest).FileOutputs[0].Output.(*livekit.EncodedFileOutput_AliOSS).AliOSS.Secret) + + participant := &livekit.ParticipantEgressRequest{ + SegmentOutputs: []*livekit.SegmentedFileOutput{ + segments, + }, + } + + cl = proto.Clone(participant) + RedactEncodedOutputs(cl.(EncodedOutput)) + + require.Equal(t, "{credentials}", cl.(*livekit.ParticipantEgressRequest).SegmentOutputs[0].Output.(*livekit.SegmentedFileOutput_Gcp).Gcp.Credentials) +} + +func TestRedactDirectOutput(t *testing.T) { + track := &livekit.TrackEgressRequest{ + Output: &livekit.TrackEgressRequest_File{ + File: &livekit.DirectFileOutput{ + Output: &livekit.DirectFileOutput_S3{ + S3: &livekit.S3Upload{ + AccessKey: "ACCESS_KEY", + Secret: "SECRET", + }, + }, + }, + }, + } + + RedactDirectOutputs(track) + require.Equal(t, "{access_key}", track.Output.(*livekit.TrackEgressRequest_File).File.Output.(*livekit.DirectFileOutput_S3).S3.AccessKey) + require.Equal(t, "{secret}", track.Output.(*livekit.TrackEgressRequest_File).File.Output.(*livekit.DirectFileOutput_S3).S3.Secret) +} diff --git a/egress/types.go b/egress/types.go index 31ca9977..dc002e26 100644 --- a/egress/types.go +++ b/egress/types.go @@ -50,6 +50,13 @@ type DirectOutput interface { GetWebsocketUrl() string } +type UploadRequest interface { + GetS3() *livekit.S3Upload + GetGcp() *livekit.GCPUpload + GetAzure() *livekit.AzureBlobUpload + GetAliOSS() *livekit.AliOSSUpload +} + func GetTypes(request interface{}) (string, string) { switch req := request.(type) { case *livekit.EgressInfo_RoomComposite: