diff --git a/.github/templates/README.template.md b/.github/templates/README.template.md index 57924fe6..2763833c 100644 --- a/.github/templates/README.template.md +++ b/.github/templates/README.template.md @@ -210,7 +210,7 @@ like Blocked Endpoints and any sort of Auth. > [!NOTE] > Blocked Endpoints can be reactivated by manually configuring them -### Blocked Endpoints +### Endpoints Because Secured Signal API is just a Proxy you can use all of the [Signal REST API](https://github.com/bbernhard/signal-cli-rest-api/blob/master/doc/EXAMPLES.md) endpoints except for... @@ -225,12 +225,27 @@ Because Secured Signal API is just a Proxy you can use all of the [Signal REST A | **/v1/accounts** | | **/v1/contacts** | +> [!NOTE] +> Matching works by checking if the requested Endpoints startswith a Blocked or Allowed Endpoint + These Endpoints are blocked by default due to Security Risks, but can be modified by setting `blockedEndpoints` in your config: ```yaml blockedEndpoints: [/v1/register, /v1/unregister, /v1/qrcodelink, /v1/contacts] ``` +Override Blocked Endpoints by explicitly allowing endpoints in `allowedEndpoints`. + +| Config (Allow) | (Block) | Result | | | | +| :------------------------------- | :---------------------------------- | :--------: | --- | :---------------: | --- | +| `allowedEndpoints: ["/v2/send"]` | `unset` | **all** | 🛑 | **`/v2/send`** | ✅ | +| `unset` | `blockedEndpoints: ["/v1/receive"]` | **all** | ✅ | **`/v1/receive`** | 🛑 | +| `blockedEndpoints: ["/v2"]` | `allowedEndpoints: ["/v2/send"]` | **`/v2*`** | 🛑 | **`/v2/send`** | ✅ | + +```yaml +allowedEndpoints: [/v2/send] +``` + ### Variables Placeholders can be added under `variables` and can then be referenced in the Body, Query or URL. diff --git a/examples/config.yml b/examples/config.yml index f8e24c36..2e5bb2c3 100644 --- a/examples/config.yml +++ b/examples/config.yml @@ -15,3 +15,5 @@ messageAliases: [{ alias: "msg", score: 100 }] blockedEndpoints: - /v1/about +allowedEndpoints: + - /v2/send diff --git a/internals/proxy/middlewares/endpoints.go b/internals/proxy/middlewares/endpoints.go index 173fff21..5de4de0b 100644 --- a/internals/proxy/middlewares/endpoints.go +++ b/internals/proxy/middlewares/endpoints.go @@ -3,6 +3,7 @@ package middlewares import ( "net/http" "slices" + "strings" log "github.com/codeshelldev/secured-signal-api/utils/logger" ) @@ -15,15 +16,18 @@ func (data EndpointsMiddleware) Use() http.Handler { next := data.Next return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - blockedEndpoints := getSettingsByReq(req).BLOCKED_ENDPOINTS + settings := getSettingsByReq(req) - if blockedEndpoints == nil { + blockedEndpoints := settings.BLOCKED_ENDPOINTS + allowedEndpoints := settings.ALLOWED_ENDPOINTS + + if blockedEndpoints == nil && allowedEndpoints == nil { blockedEndpoints = getSettings("*").BLOCKED_ENDPOINTS } reqPath := req.URL.Path - if slices.Contains(blockedEndpoints, reqPath) { + if isBlocked(reqPath, allowedEndpoints, blockedEndpoints) { log.Warn("User tried to access blocked endpoint: ", reqPath) http.Error(w, "Forbidden", http.StatusForbidden) return @@ -32,3 +36,39 @@ func (data EndpointsMiddleware) Use() http.Handler { next.ServeHTTP(w, req) }) } + +func isBlocked(endpoint string, allowed []string, blocked []string) bool { + if blocked == nil { + blocked = []string{} + } + + if allowed == nil { + allowed = []string{} + } + + isExplicitlyBlocked := slices.ContainsFunc(blocked, func(try string) bool { + return strings.HasPrefix(endpoint, try) + }) + + isExplictlyAllowed := slices.ContainsFunc(allowed, func(try string) bool { + return strings.HasPrefix(endpoint, try) + }) + + // Block all except explicitly Allowed + if len(blocked) == 0 && len(allowed) != 0 { + return !isExplictlyAllowed + } + + // Allow all except explicitly Blocked + if len(allowed) == 0 && len(blocked) != 0{ + return isExplicitlyBlocked + } + + // Excplicitly Blocked except excplictly Allowed + if len(blocked) != 0 && len(allowed) != 0 { + return isExplicitlyBlocked && !isExplictlyAllowed + } + + // Block all + return true +} \ No newline at end of file diff --git a/utils/config/loader.go b/utils/config/loader.go index be45893e..598f8060 100644 --- a/utils/config/loader.go +++ b/utils/config/loader.go @@ -8,6 +8,7 @@ import ( "strings" middlewareTypes "github.com/codeshelldev/secured-signal-api/internals/proxy/middlewares/types" + "github.com/codeshelldev/secured-signal-api/utils" log "github.com/codeshelldev/secured-signal-api/utils/logger" "github.com/knadh/koanf/parsers/yaml" ) @@ -26,6 +27,7 @@ type ENV_ struct { type SETTING_ struct { BLOCKED_ENDPOINTS []string `koanf:"blockedendpoints"` + ALLOWED_ENDPOINTS []string `koanf:"allowedendpoints"` VARIABLES map[string]any `koanf:"variables"` MESSAGE_ALIASES []middlewareTypes.MessageAlias `koanf:"messagealiases"` } @@ -38,6 +40,7 @@ var ENV *ENV_ = &ENV_{ SETTINGS: map[string]*SETTING_{ "*": { BLOCKED_ENDPOINTS: []string{}, + ALLOWED_ENDPOINTS: []string{}, MESSAGE_ALIASES: []middlewareTypes.MessageAlias{}, VARIABLES: map[string]any{}, }, @@ -63,6 +66,7 @@ func InitEnv() { config.Unmarshal("variables", &defaultSettings.VARIABLES) defaultSettings.BLOCKED_ENDPOINTS = config.Strings("blockedendpoints") + defaultSettings.ALLOWED_ENDPOINTS = config.Strings("allowedendpoints") } func Load() { @@ -87,6 +91,9 @@ func Load() { InitEnv() log.Info("Finished Loading Configuration") + + log.Dev("Loaded Config:\n" + utils.ToJson(config.All())) + log.Dev("Loaded Token Configs:\n" + utils.ToJson(tokensLayer.All())) } func LoadDefaults() {