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
If provided, check remote IP Address against the list
of user-provided 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 11, 2020
1 parent fcbde52 commit 4d28eec
Show file tree
Hide file tree
Showing 10 changed files with 132 additions and 3 deletions.
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@ See also:
- Optional automatic (but not officially documented) termination of user
sessions via official `ezproxy` binary

- Optional filtering of JSON payload sender IP Addresses
- default setting is to accept payloads from any IP Address, relying on
host-level firewall rules to prevent receipt from rouge systems
- if a list of trusted IP Addresses is provided, those IP Addresses will be
the only ones allowed to submit JSON payloads

- `es` CLI application
- small CLI app to list and optionally terminate user sessions for a
specific username
Expand Down Expand Up @@ -154,6 +160,8 @@ See also:

- Logging
- Payload receipt from monitoring system
- logging of rejected payloads
- logging of accepted payloads
- Action taken due to payload
- username ignored
- due to username inclusion in ignore file for usernames
Expand Down Expand Up @@ -188,9 +196,6 @@ Known issues:

- Documentation
- The docs are beginning to take overall shape, but still need a lot of work
- Payloads are accepted from any IP Address
- the expectation is that host-level firewall rules will be used to protect
against this until a feature can be added to filter access (see GH-18)

### Future

Expand Down
51 changes: 51 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,52 @@ 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
switch {
case !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

default:
log.Infof(
"payload accepted; remote IP %q is in the trusted payload senders list: %v",
remoteIPAddr,
trustedPayloadSenders,
)
}
}

if r.Method != http.MethodPost {

log.WithFields(log.Fields{
Expand Down
10 changes: 10 additions & 0 deletions cmd/brick/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,14 @@ func main() {
appConfig.IgnoreLookupErrors(),
)

// log this to help troubleshoot why payloads are (or are not) filtered
switch {
case appConfig.RequireTrustedPayloadSender():
log.Info("OK: Restricting payload sender IP Addresses enabled")
default:
log.Warn("CAUTION: Restricting payload sender IP Addresses disabled")
}

// GET requests
mux.HandleFunc(frontpageEndpointPattern, frontPageHandler)
mux.HandleFunc(apiV1ViewDisabledUsersEndpointPattern, viewDisabledUsersHandler)
Expand All @@ -159,6 +167,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
2 changes: 2 additions & 0 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ FEATURES
• Optional automatic (but not officially documented) termination of user sessions via official EZproxy binary
• Optional filtering of JSON payload sender IP Addresses
USAGE
See the README for examples.
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 | **all** | 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
7 changes: 7 additions & 0 deletions docs/deploy.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,13 @@ 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
- Per the default setting in the config file, only payload submissions from
`127.0.0.1` will be accepted. You need to enter the IP Address of the
Splunk system which is responsible for delivering JSON payloads to this
application.
- **CAUTION**: If you do not define a value for this setting, or comment it
out, payload submissions from all sender IP Addresses will be accepted.
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
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{}
}
}

// 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
18 changes: 18 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,23 @@ func validate(c Config) error {
return fmt.Errorf("local IP Address not provided")
}

// true if sysadmin specified a value via CLI or config file
if c.RequireTrustedPayloadSender() {
switch {
case len(c.TrustedIPAddresses()) < 1:
return fmt.Errorf("empty list of trusted IP Addresses 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 4d28eec

Please sign in to comment.