Skip to content
This repository was archived by the owner on Nov 17, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ node_modules
# Go workspaces
go.work
go.work.sum
.idea
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ The Sliding Sync proxy requires some environment variables set to function. They

Here is a short description of each, as of writing:
```
SYNCV3_SERVER Required. The destination homeserver to talk to (CS API HTTPS URL) e.g 'https://matrix-client.matrix.org'
SYNCV3_SERVER Required. The destination homeserver to talk to (CS API HTTPS URL) e.g 'https://matrix-client.matrix.org' (Supports unix socket: /path/to/socket)
SYNCV3_DB Required. The postgres connection string: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
SYNCV3_SECRET Required. A secret to use to encrypt access tokens. Must remain the same for the lifetime of the database.
SYNCV3_BINDADDR Default: 0.0.0.0:8008. The interface and port to listen on.
SYNCV3_BINDADDR Default: 0.0.0.0:8008. The interface and port to listen on. (Supports unix socket: /path/to/socket)
SYNCV3_TLS_CERT Default: unset. Path to a certificate file to serve to HTTPS clients. Specifying this enables TLS on the bound address.
SYNCV3_TLS_KEY Default: unset. Path to a key file for the certificate. Must be provided along with the certificate file.
SYNCV3_PPROF Default: unset. The bind addr for pprof debugging e.g ':6060'. If not set, does not listen.
Expand Down
4 changes: 2 additions & 2 deletions cmd/syncv3/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ const (

var helpMsg = fmt.Sprintf(`
Environment var
%s Required. The destination homeserver to talk to (CS API HTTPS URL) e.g 'https://matrix-client.matrix.org'
%s Required. The destination homeserver to talk to (CS API HTTPS URL) e.g 'https://matrix-client.matrix.org' (Supports unix socket: /path/to/socket)
%s Required. The postgres connection string: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
%s Required. A secret to use to encrypt access tokens. Must remain the same for the lifetime of the database.
%s Default: 0.0.0.0:8008. The interface and port to listen on.
%s Default: 0.0.0.0:8008. The interface and port to listen on. (Supports unix socket: /path/to/socket)
%s Default: unset. Path to a certificate file to serve to HTTPS clients. Specifying this enables TLS on the bound address.
%s Default: unset. Path to a key file for the certificate. Must be provided along with the certificate file.
%s Default: unset. The bind addr for pprof debugging e.g ':6060'. If not set, does not listen.
Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (
github.com/pressly/goose/v3 v3.14.0
github.com/prometheus/client_golang v1.13.0
github.com/rs/zerolog v1.29.0
github.com/stretchr/testify v1.8.4
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't add an entire test package just for basic equality checks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

go mod tidy please.

github.com/tidwall/gjson v1.16.0
github.com/tidwall/sjson v1.2.5
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0
Expand All @@ -31,6 +32,7 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
Expand All @@ -40,6 +42,7 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.11.0 // indirect
Expand All @@ -59,5 +62,6 @@ require (
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/grpc v1.58.3 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
maunium.net/go/mautrix v0.11.0 // indirect
)
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,10 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
Expand Down Expand Up @@ -242,6 +244,7 @@ github.com/prometheus/procfs v0.11.0 h1:5EAgkfkMl659uZPbe9AS2N68a7Cc1TJbPEuGzFuR
github.com/prometheus/procfs v0.11.0/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY=
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w=
Expand All @@ -258,6 +261,7 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg=
github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
Expand Down Expand Up @@ -591,6 +595,7 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
Expand All @@ -602,6 +607,7 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
Expand Down
6 changes: 3 additions & 3 deletions internal/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func ExpiredSessionError() *HandlerError {
// An optional debugContext map can be provided. If it is present and sentry is configured,
// it is added as context to the sentry events generated for failed assertions.
func Assert(msg string, expr bool, debugContext ...map[string]interface{}) {
assert(msg, expr)
assertCustom(msg, expr)
if !expr {
sentry.WithScope(func(scope *sentry.Scope) {
if len(debugContext) > 0 {
Expand All @@ -90,13 +90,13 @@ func Assert(msg string, expr bool, debugContext ...map[string]interface{}) {
// AssertWithContext is a version of Assert that associates any sentry events with a
// request context.
func AssertWithContext(ctx context.Context, msg string, expr bool) {
assert(msg, expr)
assertCustom(msg, expr)
if !expr {
GetSentryHubFromContextOrDefault(ctx).CaptureException(fmt.Errorf("assertion failed: %s", msg))
}
}

func assert(msg string, expr bool) {
func assertCustom(msg string, expr bool) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the rename?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert.

if expr {
return
}
Expand Down
25 changes: 25 additions & 0 deletions internal/home_server_url.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package internal

import "strings"

type HomeServerUrl struct {
HttpOrUnixStr string
}

func (u HomeServerUrl) IsUnixSocket() bool {
return strings.HasPrefix(u.HttpOrUnixStr, "/")
}

func (u HomeServerUrl) GetUnixSocket() string {
if u.IsUnixSocket() {
return u.HttpOrUnixStr
}
return ""
}

func (u HomeServerUrl) GetBaseUrl() string {
if u.IsUnixSocket() {
return "http://unix"
}
return u.HttpOrUnixStr
}
30 changes: 30 additions & 0 deletions internal/home_server_url_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package internal

import (
"github.com/stretchr/testify/assert"
"testing"
)

func TestHomeServerUrl_IsUnixSocket_True(t *testing.T) {
assert.True(t, HomeServerUrl{"/path/to/socket"}.IsUnixSocket())
}

func TestHomeServerUrl_IsUnixSocket_False(t *testing.T) {
assert.False(t, HomeServerUrl{"localhost:8080"}.IsUnixSocket())
}

func TestHomeServerUrl_GetUnixSocket(t *testing.T) {
assert.Equal(t, "/path/to/socket", HomeServerUrl{"/path/to/socket"}.GetUnixSocket())
}

func TestHomeServerUrl_GetUnixSocket_Http(t *testing.T) {
assert.Equal(t, "", HomeServerUrl{"localhost:8080"}.GetUnixSocket())
}

func TestHomeServerUrl_GetBaseUrl_UnixSocket(t *testing.T) {
assert.Equal(t, "http://unix", HomeServerUrl{"/path/to/socket"}.GetBaseUrl())
}

func TestHomeServerUrl_GetBaseUrl_Http(t *testing.T) {
assert.Equal(t, "localhost:8080", HomeServerUrl{"localhost:8080"}.GetBaseUrl())
}
36 changes: 24 additions & 12 deletions sync2/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"github.com/matrix-org/sliding-sync/internal"
"io"
"net"
"net/http"
"net/url"
"time"
Expand Down Expand Up @@ -39,16 +41,26 @@ type HTTPClient struct {
}

func NewHTTPClient(shortTimeout, longTimeout time.Duration, destHomeServer string) *HTTPClient {
hsUrl := internal.HomeServerUrl{HttpOrUnixStr: destHomeServer}
return &HTTPClient{
LongTimeoutClient: &http.Client{
Timeout: longTimeout,
Transport: otelhttp.NewTransport(http.DefaultTransport),
},
Client: &http.Client{
Timeout: shortTimeout,
Transport: otelhttp.NewTransport(http.DefaultTransport),
},
DestinationServer: destHomeServer,
LongTimeoutClient: newClient(longTimeout, hsUrl),
Client: newClient(shortTimeout, hsUrl),
DestinationServer: hsUrl.GetBaseUrl(),
}
}

func newClient(timeout time.Duration, url internal.HomeServerUrl) *http.Client {
transport := http.DefaultTransport
if url.IsUnixSocket() {
transport = &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return net.Dial("unix", url.GetUnixSocket())
},
}
}
return &http.Client{
Timeout: timeout,
Transport: otelhttp.NewTransport(transport),
}
}

Expand All @@ -66,7 +78,7 @@ func (v *HTTPClient) Versions(ctx context.Context) ([]string, error) {
return nil, fmt.Errorf("/versions returned HTTP %d", res.StatusCode)
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -99,7 +111,7 @@ func (v *HTTPClient) WhoAmI(ctx context.Context, accessToken string) (string, st
return "", "", fmt.Errorf("/whoami returned HTTP %d", res.StatusCode)
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
if err != nil {
return "", "", err
}
Expand Down
35 changes: 30 additions & 5 deletions v3.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import (
"context"
"embed"
"encoding/json"
"errors"
"fmt"
"io/fs"
"net"
"net/http"
"os"
"strings"
Expand Down Expand Up @@ -216,12 +219,18 @@ func RunSyncV3Server(h http.Handler, bindAddr, destV2Server, tlsCert, tlsKey str

// Block forever
var err error
if tlsCert != "" && tlsKey != "" {
logger.Info().Msgf("listening TLS on %s", bindAddr)
err = http.ListenAndServeTLS(bindAddr, tlsCert, tlsKey, srv)
if strings.HasPrefix(bindAddr, "/") {
logger.Info().Msgf("listening on unix socket %s", bindAddr)
listener := unixSocketListener(bindAddr)
err = http.Serve(listener, srv)
} else {
logger.Info().Msgf("listening on %s", bindAddr)
err = http.ListenAndServe(bindAddr, srv)
if tlsCert != "" && tlsKey != "" {
logger.Info().Msgf("listening TLS on %s", bindAddr)
err = http.ListenAndServeTLS(bindAddr, tlsCert, tlsKey, srv)
} else {
logger.Info().Msgf("listening on %s", bindAddr)
err = http.ListenAndServe(bindAddr, srv)
}
}
if err != nil {
sentry.CaptureException(err)
Expand All @@ -230,6 +239,22 @@ func RunSyncV3Server(h http.Handler, bindAddr, destV2Server, tlsCert, tlsKey str
}
}

func unixSocketListener(bindAddr string) net.Listener {
err := os.Remove(bindAddr)
if err != nil && !errors.Is(err, fs.ErrNotExist) {
logger.Fatal().Err(err).Msg("failed to remove existing unix socket")
}
listener, err := net.Listen("unix", bindAddr)
if err != nil {
logger.Fatal().Err(err).Msg("failed to serve unix socket")
}
err = os.Chmod(bindAddr, 0755)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document the respective r/w/x permissions you wish to give to this socket as a comment.

Comment on lines +251 to +252

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what is meant by "safe default" here.
Unix sockets don't need the write permissions.

To connect to and use a unix socket as a client, all you need is the read permissions bit.

0755, which translates to u=rwx,go=rw, renders this unix socket world readable, thus making it world-connectable.

Copy link
Contributor Author

@cyberb cyberb Nov 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was under impression that 755 will only allow the owner to use it.
After googling looks like eXecute is not need but read and write are needed to use it.
Ideally we pass it as a parameter, if not then probably 600 is what I wanted.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to unix(7), write permission is required to connect to a unix domain socket.

On Linux, connecting to a stream socket object requires write permission on that socket; sending a datagram to a datagram socket likewise requires write permission on that socket.

I'd suggest to make it at least user and group connectable, that is 0660 (execution bit has no effect).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First and foremost: Sorry for my original comment.
I was in a bit of a rush when I wrote it, and should just have waited a bit longer before commenting.

Multiple /bit flips/ happened in my original comment, which I want to point out:

  1. write perms are needed to connect and talk over a unix socket, not read, as @n3vu0r correctly pointed out.
  2. 0755 does not translate to u=rwx,go=rw but rather u=rwx,go=rx.
  3. Which in turn makes, as intended, the socket not world-connectable but rather only by the owner itself.

I just was very happy to see unix socket support implemented so soon, but then saw some unusual permission bits.

Given I worked on unix socket permission bits as part of caddyserver/caddy#4741, I figured I should comment. Which then lead to that hastily, and more importantly, incorrect comment. Sorry for that.

According to the current unix(7), only write perms are needed.
There are, however, still a lot of old unix(7) versions hosted on the web, which still mention read and write being needed.

For more details on this, see caddyserver/caddy#4741 (comment).


I'd suggest to make it at least user and group connectable, that is 0660 (execution bit has no effect).

I would suggest the same, but without read, so 0220.

if err != nil {
logger.Fatal().Err(err).Msg("failed to set unix socket permissions")
}
return listener
}

type HandlerError struct {
StatusCode int
Err error
Expand Down