Skip to content

Commit

Permalink
Add Hub target to secure calls
Browse files Browse the repository at this point in the history
  • Loading branch information
jderusse committed Oct 14, 2019
1 parent 57b4fee commit 52b77b3
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 43 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.12
go-version: 1.13
- name: Check out code
uses: actions/checkout@v1
- name: Install dependency
Expand Down
17 changes: 4 additions & 13 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,12 @@ jobs:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.12
- name: Check out code
uses: actions/checkout@v1
- name: Install dependency
run: |
go mod download
- name: Lint Go Code
run: |
export PATH=$PATH:$(go env GOPATH)/bin # temporary fix. See https://github.com/actions/setup-go/issues/14
go get -u github.com/golangci/golangci-lint/cmd/golangci-lint
golangci-lint run ./...
uses: docker://golangci/golangci-lint:latest
with:
args: golangci-lint run ./...

test:
name: Test
Expand All @@ -28,7 +19,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.12
go-version: 1.13
- name: Check out code
uses: actions/checkout@v1
- name: Install dependency
Expand Down
56 changes: 30 additions & 26 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,37 @@
The HTTP Broadcaster follows [the twelve-factor app methodology](https://12factor.net/)
and is configurable using [environment variables](https://en.wikipedia.org/wiki/Environment_variable):

### Configuration
## Configuration

| Variable | Required/Default | Description |
|-------------------------------|------------------|-------------|
| `AGENT_ENDPOINT` | _undefined_ | the address to broadcast requests to (example: `127.0.0.1:6800`). When not defined, the broadcaster will only listen on requests. `SERVER_ADDR` or `AGENT_ENDPOINT` is required. |
| `AGENT_RETRY_DELAY` | `60s` | maximum duration for retrying the replay of the request. |
| `DEBUG` | `0` | set to `1` to enable the debug mode (prints recovery stack traces). |
| `HUB_ENDPOINT` | **required** | the address of the the mercure hub to push and fetch messages (example: `https://example.com/hub`). |
| `HUB_GUARD_TOKEN` | =`HUB_TOPIC` | the token used to prevent infinite loop (in case an agent broadcast request to iteself). |
| `HUB_PUBLISH_TOKEN` | =`HUB_TOKEN` | valid JWT token to allow publishing. |
| `HUB_SUBSCRIBE_TOKEN` | =`HUB_TOKEN` | valid JWT token to allow subscribing. |
| `HUB_TIMEOUT` | `5s` | maximum duration for pushing the message into the HUB, set to `0s` to disable. |
| `HUB_TOKEN` | **required** | valid JWT token to allow both publishing and subscribing. On could use [this example](https://jwt.io/#debugger-io?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXJjdXJlIjp7InN1YnNjcmliZSI6WyJodHRwLWJyb2FkY2FzdCJdLCJwdWJsaXNoIjpbImh0dHAtYnJvYWRjYXN0Il19fQ._CEt9vXo2zGHSRTmd6LkoOYEtT014m75AVBn9KfA2Go) to generate a new one|
| `HUB_TOPIC` | `http-broadcast` | name of the Mercure's topic to exchange messages. This parameter can also be defined with by the queryString of `HUB_ENDPOINT`. example `HUB_ENDPOINT=https://example.com/hub?topic=my_topic`. |
| `LOG_FORMAT` | `text` | the log format, can be `json`, `fluentd` or `text`. |
| `LOG_LEVEL` | `info` | the log verbosity, can be `trace`, `debug`, `info`, `warn`, `error`, `fatal`. |
| `SERVER_ADDR` | _undefined_ | the address to listen on (example: `0.0.0.0:6081`). When not defined, the broadcaster will only pusblish requests. `SERVER_ADDR` or `AGENT_ENDPOINT` is required. |
| `SERVER_CORS_ALLOWED_ORIGINS` | _undefined_ | a comma separated list of allowed CORS origins, can be `*` for all. |
| `SERVER_INSECURE` | =`DEBUG` | trust everyone in [ProxyProtocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt). |
| `SERVER_READ_TIMEOUT` | `0s` | maximum duration before timing out writes of the response, set to `0s` to disable, example: `2m`. |
| `SERVER_TLS_ACME_ADDR` | `:http` | the address use by the acme server to listen on (example: `0.0.0.0:8080`). |
| `SERVER_TLS_ACME_CERT_DIR` | _undefined_ | the directory where to store Let's Encrypt certificates. |
| `SERVER_TLS_ACME_HOSTS` | _undefined_ | a comma separated list of hosts for which Let's Encrypt certificates must be issued. |
| `SERVER_TLS_CERT_FILE` | _undefined_ | a cert file (to use a custom certificate). |
| `SERVER_TLS_KEY_FILE` | _undefined_ | a key file (to use a custom certificate). |
| `SERVER_TRUSTED_IPS` | _undefined_ | list of trusted ips which lead to remote client address replacement in [ProxyProtocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt). |
| `SERVER_WRITE_TIMEOUT` | `0s` | maximum duration for reading the entire request, including the body, set to `0s` to disable, example: `2m`. |
| Variable | Required/Default | Description |
|-------------------------------|------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `AGENT_ENDPOINT` | _undefined_ | the address to broadcast requests to (example: `127.0.0.1:6800`). When not defined, the broadcaster will only listen on requests. `SERVER_ADDR` or `AGENT_ENDPOINT` is required. |
| `AGENT_RETRY_DELAY` | `60s` | maximum duration for retrying the replay of the request. |
| `DEBUG` | `0` | set to `1` to enable the debug mode (prints recovery stack traces). |
| `HUB_ENDPOINT` | **required** | the address of the the mercure hub to push and fetch messages (example: `https://example.com/hub`). |
| `HUB_GUARD_TOKEN` | =`HUB_TOPIC` | the token used to prevent infinite loop (in case an agent broadcast request to iteself). |
| `HUB_PUBLISH_TOKEN` | =`HUB_TOKEN` | valid JWT token to allow publishing. |
| `HUB_SUBSCRIBE_TOKEN` | =`HUB_TOKEN` | valid JWT token to allow subscribing. |
| `HUB_TARGET` | _undefined_ | name of the mercure's target. Used to secure the communication between the hub and the agent (example `http-broadcast`). This parameter can also be defined with by the queryString of `HUB_ENDPOINT`. example `HUB_ENDPOINT=https://example.com/hub?target=my_target`. |
| `HUB_TIMEOUT` | `5s` | maximum duration for pushing the message into the HUB, set to `0s` to disable. |
| `HUB_TOKEN` | **required** | valid JWT token to allow both publishing and subscribing. On could use this [JWT example] to generate a new one. |
| `HUB_TOPIC` | `http-broadcast` | name of the Mercure's topic to exchange messages. This parameter can also be defined with by the queryString of `HUB_ENDPOINT`. example `HUB_ENDPOINT=https://example.com/hub?topic=my_topic`. |
| `LOG_FORMAT` | `text` | the log format, can be `json`, `fluentd` or `text`. |
| `LOG_LEVEL` | `info` | the log verbosity, can be `trace`, `debug`, `info`, `warn`, `error`, `fatal`. |
| `SERVER_ADDR` | _undefined_ | the address to listen on (example: `0.0.0.0:6081`). When not defined, the broadcaster will only pusblish requests. `SERVER_ADDR` or `AGENT_ENDPOINT` is required. |
| `SERVER_CORS_ALLOWED_ORIGINS` | _undefined_ | a comma separated list of allowed CORS origins, can be `*` for all. |
| `SERVER_INSECURE` | =`DEBUG` | trust everyone in [ProxyProtocol]. |
| `SERVER_READ_TIMEOUT` | `0s` | maximum duration before timing out writes of the response, set to `0s` to disable, example: `2m`. |
| `SERVER_TLS_ACME_ADDR` | `:http` | the address use by the acme server to listen on (example: `0.0.0.0:8080`). |
| `SERVER_TLS_ACME_CERT_DIR` | _undefined_ | the directory where to store Let's Encrypt certificates. |
| `SERVER_TLS_ACME_HOSTS` | _undefined_ | a comma separated list of hosts for which Let's Encrypt certificates must be issued. |
| `SERVER_TLS_CERT_FILE` | _undefined_ | a cert file (to use a custom certificate). |
| `SERVER_TLS_KEY_FILE` | _undefined_ | a key file (to use a custom certificate). |
| `SERVER_TRUSTED_IPS` | _undefined_ | list of trusted ips which lead to remote client address replacement in [ProxyProtocol]. |
| `SERVER_WRITE_TIMEOUT` | `0s` | maximum duration for reading the entire request, including the body, set to `0s` to disable, example: `2m`.

[JWT example]: https://jwt.io/#debugger-io?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXJjdXJlIjp7InN1YnNjcmliZSI6WyJteV90YXJnZXQiXSwicHVibGlzaCI6WyJteV90YXJnZXQiXX19.qiIYEsa9ikJAr5U4kGe97sqQsXjxD7_FmWbXaIJKXvk "JWT example"
[ProxyProtocol]: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt "ProxyProtocol"

## Next step

Expand Down
14 changes: 14 additions & 0 deletions pkg/config/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,12 @@ type HubOptions struct {
SubscribeToken string
Timeout time.Duration
Topic string
Target string
}

// NewOptionsFromEnv creates a new option instance from environment
// It returns an error if mandatory env env vars are missing
//nolint:gocognit
func NewOptionsFromEnv() (*Options, error) {
agentRetryDelay, err := time.ParseDuration(getEnv("AGENT_RETRY_DELAY", "60s"))
if err != nil {
Expand Down Expand Up @@ -102,6 +104,17 @@ func NewOptionsFromEnv() (*Options, error) {
hubTopic = "http-broadcast"
}

hubTarget := os.Getenv("HUB_TARGET")
if hubEndpoint != nil {
if hubTarget == "" {
hubTarget = hubEndpoint.Query().Get("target")
}

q := hubEndpoint.Query()
q.Del("target")
hubEndpoint.RawQuery = q.Encode()
}

options := &Options{
Debug: getEnv("DEBUG", "0") == "1",
Agent: AgentOptions{
Expand All @@ -115,6 +128,7 @@ func NewOptionsFromEnv() (*Options, error) {
SubscribeToken: getEnv("HUB_SUBSCRIBE_TOKEN", os.Getenv("HUB_TOKEN")),
Timeout: hubTimeout,
Topic: hubTopic,
Target: hubTarget,
},
Server: ServerOptions{
Addr: os.Getenv("SERVER_ADDR"),
Expand Down
6 changes: 4 additions & 2 deletions pkg/config/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ func TestNewOptionsFormNew(t *testing.T) {
"HUB_SUBSCRIBE_TOKEN": "sub_token",
"HUB_TIMEOUT": "1m",
"HUB_TOKEN": "token",
"HUB_TOPIC": "HUB_TOPIC",
"HUB_TOPIC": "my_topic",
"HUB_TARGET": "my_target",
"LOG_FORMAT": "json",
"LOG_LEVEL": "warn",
"SERVER_ADDR": "0.0.0.0:81",
Expand Down Expand Up @@ -54,7 +55,8 @@ func TestNewOptionsFormNew(t *testing.T) {
PublishToken: "pub_token",
SubscribeToken: "sub_token",
Timeout: 1 * time.Minute,
Topic: "HUB_TOPIC",
Topic: "my_topic",
Target: "my_target",
},
Server: ServerOptions{
Addr: "0.0.0.0:81",
Expand Down
3 changes: 2 additions & 1 deletion pkg/server/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ func (s *Server) handle(w http.ResponseWriter, r *http.Request) {
}

// building Hub Request
log.WithFields(log.Fields{"data": string(data), "topic": s.options.Hub.Topic, "hub": s.options.Hub.Endpoint}).Debug("Server: Pushing message to HUB")
log.WithFields(log.Fields{"data": string(data), "topic": s.options.Hub.Topic, "target": s.options.Hub.Target, "hub": s.options.Hub.Endpoint}).Debug("Server: Pushing message to HUB")

form := url.Values{}
form.Set("topic", s.options.Hub.Topic)
form.Set("target", s.options.Hub.Target)
form.Set("data", string(data))
formData := form.Encode()

Expand Down
2 changes: 2 additions & 0 deletions pkg/server/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func TestHandle(t *testing.T) {
Hub: config.HubOptions{
Endpoint: parseSafeURL(httpServer.URL),
Topic: "my-topic",
Target: "my-target",
GuardToken: "-",
},
})
Expand All @@ -56,6 +57,7 @@ func TestHandle(t *testing.T) {
require.NoError(t, err)

assert.Equal(t, "my-topic", form.Get("topic"))
assert.Equal(t, "my-target", form.Get("target"))
assert.Equal(t, `{"Method":"POST","Host":"127.0.0.1:8002","Path":"/","Header":{"Accept-Encoding":["gzip"],"Content-Length":["5"],"Content-Type":["text/plain"],"User-Agent":["Go-http-client/1.1"],"X-Forwarded-Host":["127.0.0.1:8002"],"X-Forwarded-Port":["8002"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["`+hostname+`"],"X-Httpbroadcast-Guard":["-"],"X-Real-Ip":["127.0.0.1"]},"Body":"SGVsbG8="}`, form.Get("data"))
}

Expand Down

0 comments on commit 52b77b3

Please sign in to comment.