Skip to content
This repository has been archived by the owner on Feb 15, 2024. It is now read-only.

Commit

Permalink
Add support for limiting payloads to specific IPs
Browse files Browse the repository at this point in the history
Check remote IP Address against a list of user-provided or
default IP Addresses. If a match is not found, reject the
connection with forbidden status. If a match is found, accept
the connection.

If the sysadmin does not provide a list of trusted IP Addresses
via config file or CLI flag, accept payloads from any IP
Address. An attempt to balance this choice is provided by
setting the default value in the starter config file to
127.0.0.1, or "localhost".

This behavior is subject to change; the behavior may change
to require an explicit list of IP Addresses to receive
payloads.

Update docs, config template to reflect the changes.

refs GH-18
  • Loading branch information
atc0005 committed Nov 10, 2020
1 parent fcbde52 commit 0cba2e9
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 5 deletions.
44 changes: 44 additions & 0 deletions cmd/brick/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@ import (
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"strings"
"time"

"github.com/apex/log"

"github.com/atc0005/brick/internal/events"
"github.com/atc0005/brick/internal/files"
"github.com/atc0005/brick/internal/textutils"
)

// API endpoint patterns supported by this application
Expand Down Expand Up @@ -87,6 +90,8 @@ func frontPageHandler(w http.ResponseWriter, r *http.Request) {
}

func disableUserHandler(
requireTrustedPayloadSender bool,
trustedPayloadSenders []string,
reportedUserEventsLog *files.ReportedUserEventsLog,
disabledUsers *files.DisabledUsers,
ignoredSources files.IgnoredSources,
Expand All @@ -103,6 +108,45 @@ func disableUserHandler(
// fmt.Fprintf(mw, "disableUserHandler endpoint hit\n")
log.Debug("disableUserHandler handler hit")

// If a list of trusted IPs is not provided by the sysadmin, the
// default behavior is to accept payloads from all IP Addresses. This
// behavior/logic is balanced by configuring the trusted IP Addresses
// list to include only 127.0.0.1 by default in the starter config
// file.
if requireTrustedPayloadSender {

// get bare sender IP
remoteIPAddr, _, ipHostSplitErr := net.SplitHostPort(events.GetIP(r))
if ipHostSplitErr != nil {
errMsg := fmt.Sprintf(
"remote IP %q is not in IP:port format: %v",
remoteIPAddr,
ipHostSplitErr,
)
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
fmt.Fprint(w, errMsg)
return
}

// confirm that sender IP is in the trusted senders list
if !textutils.InList(remoteIPAddr, trustedPayloadSenders) {
errMsg := fmt.Sprintf(
"rejecting payload; remote IP %q is not in the trusted payload senders list: %v",
remoteIPAddr,
trustedPayloadSenders,
)
log.WithFields(log.Fields{
"url_path": r.URL.Path,
"http_method": r.Method,
"remote_ip_addr": remoteIPAddr,
"trusted_payload_senders": strings.Join(trustedPayloadSenders, ", "),
}).Error(errMsg)
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
fmt.Fprint(w, errMsg)
return
}
}

if r.Method != http.MethodPost {

log.WithFields(log.Fields{
Expand Down
2 changes: 2 additions & 0 deletions cmd/brick/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ func main() {
mux.HandleFunc(
apiV1DisableUserEndpointPattern,
disableUserHandler(
appConfig.RequireTrustedPayloadSender(),
appConfig.TrustedIPAddresses(),
reportedUserEventsLog,
disabledUsers,
ignoredSources,
Expand Down
6 changes: 6 additions & 0 deletions contrib/brick/config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ local_tcp_port = 8000
# Local IP Address that this application should listen on for incoming HTTP requests.
local_ip_address = "localhost"

# One or many single IP Addresses which are trusted for payload submission. If
# this is defined, all other sender IPs are ignored. If this is not defined,
# payloads are accepted from all IP Addresses not otherwise rejected by
# local/remote firewall rules.
trusted_ip_addresses = ["127.0.0.1"]


[logging]

Expand Down
3 changes: 3 additions & 0 deletions docs/configure.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ environment variables) and use the configuration file for the other settings.
| `ignore-lookup-errors` | No | `false` | No | `true`, `false` | Whether application should continue if attempts to lookup existing disabled or ignored status for a username or IP Address fail. This is needed if you do not pre-create files used by this application ahead of time. WARNING: Because this can mask errors, you should probably only use it briefly when this application is first deployed, then later disable the setting once all files are in place. |
| `port` | No | `8000` | No | *valid TCP port number* | TCP port that this application should listen on for incoming HTTP requests. Tip: Use an unreserved port between 1024:49151 (inclusive) for the best results. |
| `ip-address` | No | `localhost` | No | *valid fqdn, local name or IP Address* | Local IP Address that this application should listen on for incoming HTTP requests. |
| `trusted-ip-addresses` | No | `127.0.0.1` | No | *one or many valid fqdn or IP Addresses* | One or many single IP Addresses which are trusted for payload submission. If this is defined, all other sender IPs are ignored. If this is not defined, payloads are accepted from all IP Addresses not otherwise rejected by local/remote firewall rules. |
| `log-level` | No | `info` | No | `fatal`, `error`, `warn`, `info`, `debug` | Log message priority filter. Log messages with a lower level are ignored. |
| `log-output` | No | `stdout` | No | `stdout`, `stderr` | Log messages are written to this output target. |
| `log-format` | No | `text` | No | `cli`, `json`, `logfmt`, `text`, `discard` | Use the specified `apex/log` package "handler" to output log messages in that handler's format. |
Expand Down Expand Up @@ -82,6 +83,7 @@ Arguments](#command-line-arguments) table for more information.
| `ignore-lookup-errors` | `BRICK_IGNORE_LOOKUP_ERRORS` | | `BRICK_IGNORE_LOOKUP_ERRORS="false"` |
| `port` | `BRICK_LOCAL_TCP_PORT` | | `BRICK_LOCAL_TCP_PORT="8000"` |
| `ip-address` | `BRICK_LOCAL_IP_ADDRESS` | | `BRICK_LOCAL_IP_ADDRESS="localhost"` |
| `trusted-ip-addresses` | `BRICK_TRUSTED_IP_ADDRESSES` | | `BRICK_TRUSTED_IP_ADDRESSES="127.0.0.1"` |
| `log-level` | `BRICK_LOG_LEVEL` | | `BRICK_LOG_LEVEL="info"` |
| `log-output` | `BRICK_LOG_OUTPUT` | | `BRICK_LOG_OUTPUT="stdout"` |
| `log-format` | `BRICK_LOG_FORMAT` | | `BRICK_LOG_FORMAT="text"` |
Expand Down Expand Up @@ -124,6 +126,7 @@ settings.
| `ignore-lookup-errors` | `ignore_lookup_errors` | | |
| `port` | `local_tcp_port` | `network` | |
| `ip-address` | `local_ip_address` | `network` | |
| `trusted-ip-addresses` | `trusted_ip_addresses` | `network` | [Multi-line array](https://github.com/toml-lang/toml#user-content-array) |
| `log-level` | `level` | `logging` | |
| `log-format` | `format` | `logging` | |
| `log-output` | `output` | `logging` | |
Expand Down
3 changes: 3 additions & 0 deletions docs/deploy.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@ version.
1. Copy the starter/template configuration file from
`contrib/brick/config.example.toml` and modify accordingly using the
[configuration](configure.md) guide.
1. Configure one or more trusted IP Addresses
- If you skip this step, only payload submissions from `127.0.0.1` will be
accepted (per the default setting in the config file).
1. Decide whether you will enable automatic sessions termination or use
`fail2ban`. See the [fail2ban](fail2ban.md) doc and the
[configuration](configure.md) guide for more information.
Expand Down
11 changes: 6 additions & 5 deletions internal/config/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,12 @@ var emailRegex = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z

// Default (flag, config file, etc) settings if not overridden by user input
const (
defaultLocalTCPPort int = 8000
defaultLocalIP string = "localhost"
defaultLogLevel string = "info"
defaultLogOutput string = "stdout"
defaultLogFormat string = "text"
defaultLocalTCPPort int = 8000
defaultLocalIP string = "localhost"
defaultTrustedIPAddress string = "127.0.0.1"
defaultLogLevel string = "info"
defaultLogOutput string = "stdout"
defaultLogFormat string = "text"

// This application does not assume a specific path for the configuration
// file, so we default to an empty string if the user does not specify a
Expand Down
21 changes: 21 additions & 0 deletions internal/config/getters.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,27 @@ func (c Config) LocalIPAddress() string {
}
}

// RequireTrustedPayloadSender indicates whether the sysadmin specified a list
// of IP Addresses to trust for payload submission.
func (c Config) RequireTrustedPayloadSender() bool {
return c.cliConfig.Network.TrustedIPAddresses != nil ||
c.fileConfig.Network.TrustedIPAddresses != nil
}

// TrustedIPAddresses returns the user-provided list of IP Addresses that
// should be trusted to receive payloads or the the default value if not
// provided. CLI flag values take precedence if provided.
func (c Config) TrustedIPAddresses() []string {
switch {
case c.cliConfig.Network.TrustedIPAddresses != nil:
return c.cliConfig.Network.TrustedIPAddresses
case c.fileConfig.Network.TrustedIPAddresses != nil:
return c.fileConfig.Network.TrustedIPAddresses
default:
return []string{defaultTrustedIPAddress}
}
}

// DisabledUsersFile returns the user-provided path to the EZproxy include
// file where this application should write disabled user accounts or the
// default value if not provided. CLI flag values take precedence if provided.
Expand Down
6 changes: 6 additions & 0 deletions internal/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ type Network struct {
// LocalIPAddress is the IP Address that this application should listen on
// for incoming requests
LocalIPAddress *string `toml:"local_ip_address" arg:"--ip-address,env:BRICK_LOCAL_IP_ADDRESS" help:"Local IP Address that this application should listen on for incoming HTTP requests."`

// TrustedIPAddresses is the collection of single IP Addresses which are
// trusted for payload submission. If this is defined, all other sender
// IPs are ignored. If this is not defined, payloads are accepted from all
// IP Addresses not otherwise rejected by local/remote firewall rules.
TrustedIPAddresses []string `toml:"trusted_ip_addresses" arg:"--trusted-ip-addresses,env:BRICK_TRUSTED_IP_ADDRESSES" help:"One or many single IP Addresses which are trusted for payload submission. If this is defined, all other sender IPs are ignored. If this is not defined, payloads are accepted from all IP Addresses not otherwise rejected by local/remote firewall rules."`
}

// Logging is a collection of logging-related settings provided via CLI and
Expand Down
17 changes: 17 additions & 0 deletions internal/config/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package config

import (
"fmt"
"net"

"github.com/apex/log"

Expand Down Expand Up @@ -142,6 +143,22 @@ func validate(c Config) error {
return fmt.Errorf("local IP Address not provided")
}

// the default value is 127.0.0.1, so if the user specifies a value we
// should have at least one item in the slice
switch {
case len(c.TrustedIPAddresses()) < 1:
return fmt.Errorf("empty list of trusted IP Addesses provided")
default:
for _, ipAddr := range c.TrustedIPAddresses() {
if net.ParseIP(ipAddr) == nil {
return fmt.Errorf(
"invalid IP Address %q provided for trusted IPs list",
ipAddr,
)
}
}
}

switch c.LogLevel() {
case LogLevelFatal:
case LogLevelError:
Expand Down

0 comments on commit 0cba2e9

Please sign in to comment.