Skip to content

Commit 42c130d

Browse files
committed
Fixes #283
1 parent 92c850a commit 42c130d

File tree

8 files changed

+210
-3
lines changed

8 files changed

+210
-3
lines changed

Dockerfile.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ RUN apt-get update && \
1212
COPY . /src
1313
WORKDIR /src
1414
RUN chmod +x /src/run-tests.sh
15+
RUN go get -d -v -t
1516

1617
CMD ["sh", "-c", "/src/run-tests.sh"]

actions/reconfigure_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,25 @@ backend https-myService-be1234
264264
s.Equal(expected, actual)
265265
}
266266

267+
func (s ReconfigureTestSuite) Test_GetTemplates_AddsHttpDeny() {
268+
s.reconfigure.Mode = "service"
269+
s.reconfigure.Service.HttpsPort = 4321
270+
s.reconfigure.Service.ServiceDest[0].Port = "1234"
271+
s.reconfigure.Service.ServiceDest[0].DenyHttp = true
272+
expected := `
273+
backend myService-be1234
274+
mode http
275+
http-request deny if !{ ssl_fc }
276+
server myService myService:1234
277+
backend https-myService-be1234
278+
mode http
279+
server myService myService:4321`
280+
281+
_, actual, _ := s.reconfigure.GetTemplates()
282+
283+
s.Equal(expected, actual)
284+
}
285+
267286
func (s ReconfigureTestSuite) Test_GetTemplates_AddsLoggin_WhenDebug() {
268287
debugOrig := os.Getenv("DEBUG")
269288
defer func() { os.Setenv("DEBUG", debugOrig) }()

docs/blocking.md

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
# Blocking Requests
2+
3+
This article provides examples that can be used as a starting point when configuring the proxy to block requests based on their method type or protocol.
4+
5+
## Requirements
6+
7+
The examples that follow assume that you are using Docker v1.13+, Docker Compose v1.10+, and Docker Machine v0.9+.
8+
9+
!!! info
10+
If you are a Windows user, please run all the examples from *Git Bash* (installed through *Docker Toolbox*). Also, make sure that your Git client is configured to check out the code *AS-IS*. Otherwise, Windows might change carriage returns to the Windows format.
11+
12+
Please note that *Docker Flow Proxy* is not limited to *Docker Machine*. We're using it as an easy way to create a cluster.
13+
14+
## Swarm Cluster Setup
15+
16+
To setup an example Swarm cluster using Docker Machine, please run the commands that follow.
17+
18+
!!! tip
19+
Feel free to skip this section if you already have a working Swarm cluster.
20+
21+
```bash
22+
curl -o swarm-cluster.sh \
23+
https://raw.githubusercontent.com/\
24+
vfarcic/docker-flow-proxy/master/scripts/swarm-cluster.sh
25+
26+
chmod +x swarm-cluster.sh
27+
28+
./swarm-cluster.sh
29+
30+
eval $(docker-machine env node-1)
31+
```
32+
33+
Now we're ready to deploy the services that form the proxy stack and the demo services.
34+
35+
```bash
36+
docker network create --driver overlay proxy
37+
38+
curl -o docker-compose-stack.yml \
39+
https://raw.githubusercontent.com/\
40+
vfarcic/docker-flow-proxy/master/docker-compose-stack.yml
41+
42+
docker stack deploy -c docker-compose-stack.yml proxy
43+
44+
curl -o docker-compose-go-demo.yml \
45+
https://raw.githubusercontent.com/\
46+
vfarcic/go-demo/master/docker-compose-stack.yml
47+
48+
docker stack deploy -c docker-compose-go-demo.yml go-demo
49+
```
50+
51+
Please consult [Using Docker Stack To Run Docker Flow Proxy In Swarm Mode](/swarm-mode-stack/) for a more detailed set of examples of deployment with Docker stack.
52+
53+
We should wait until all the services are running before proceeding towards the examples that will block requests.
54+
55+
```bash
56+
docker service ls
57+
```
58+
59+
Now we are ready to explore way to block access requests.
60+
61+
## Blocking Requests Based on Request Type
62+
63+
In some cases, we want to deny certain types of methods to requests sent through the proxy. A common use case would be a service that can accept `DELETE` request which should be performed only by other services connected to it through internal networking.
64+
65+
We can block requests by specifying which types are allowed.
66+
67+
Please execute the command that follows.
68+
69+
```
70+
docker service update \
71+
--label-add "com.df.allowedMethods=GET,DELETE" \
72+
go-demo_main
73+
```
74+
75+
We specified the `com.df.allowedMethods` label that tells the proxy that only `GET` and `DELETE` methods are allowed. A request with any other method will be denied.
76+
77+
Let's confirm that the feature indeed works as expected.
78+
79+
```bash
80+
curl -i "http://$(docker-machine ip node-1)/demo/hello"
81+
```
82+
83+
We sent an `GET` request (default type) and the output is as follows.
84+
85+
```
86+
TODO
87+
```
88+
89+
Since get is on the list of allowed request methods, we got OK (status code `200`) indicating that the proxy allowed it to pass to the destination service.
90+
91+
Let's confirm that the behavior is the same with a `DELETE` request.
92+
93+
```bash
94+
curl -i -XDELETE \
95+
"http://$(docker-machine ip node-1)/demo/hello"
96+
```
97+
98+
Just as with the `GET` request, the response is `200`. The proxy allowed it as well.
99+
100+
According to the current configuration, any other request method should be denied. Let's test it with, for example, a `PUT` request.
101+
102+
```bash
103+
curl -i -XPUT \
104+
"http://$(docker-machine ip node-1)/demo/hello"
105+
```
106+
107+
```
108+
TODO
109+
```
110+
111+
This time, the proxy responded with TODO (status code TODO). The request method is not on the list of those that are allowed and proxy choose not to forward it to the destination service. Instead, it returned with TODO.
112+
113+
Similarly, we can choose which methods to deny.
114+
115+
```bash
116+
docker service update \
117+
--label-rm "com.df.allowedMethods" \
118+
--label-add "com.df.deniedMethods=DELETE" \
119+
go-demo_main
120+
```
121+
122+
We removed the `com.df.allowedMethods` label and created `com.df.deniedMethods` with the value `DELETE`.
123+
124+
If we send an `GET` request, the response should be `200` since it is not on the list of those that are denied.
125+
126+
```bash
127+
curl -i \
128+
"http://$(docker-machine ip node-1)/demo/hello"
129+
```
130+
131+
On the other hand, if we choose to send an `DELETE` request, the response should be denied.
132+
133+
```bash
134+
curl -i -XDELETE \
135+
"http://$(docker-machine ip node-1)/demo/hello"
136+
```
137+
138+
We got the response TODO proving that no one can send a `DELETE` request to our service.
139+
140+
Let's remove the `deniedMethods` label and explore how we can block HTTP request.
141+
142+
```bash
143+
docker service update \
144+
--label-rm "com.df.deniedMethods" \
145+
go-demo_main
146+
```
147+
148+
## Blocking HTTP Requests
149+
150+
TODO: Continue writing
151+
152+
```bash
153+
docker service update \
154+
--label-add "com.df.denyHttp=true" \
155+
go-demo_main
156+
157+
curl -i \
158+
"http://$(docker-machine ip node-1)/demo/hello"
159+
160+
# NOTE: No certs, so not HTTPS
161+
```
162+
163+
## Summary
164+
165+
TODO: Write
166+
167+
```bash
168+
docker-machine rm node-1 node-2 node-3
169+
```

docs/usage.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ The following query parameters can be used to send a *reconfigure* request to *D
3737
|timeoutTunnel |The tunnel timeout in seconds.<br>**Example:** `3600` |3600 |
3838
|xForwardedProto|Whether to add "X-Forwarded-Proto https" header.<br>**Example:** `true` |false |
3939

40-
Multiple destinations for a single service can be specified by adding index as a suffix to `servicePath`, `srcPort`, `port`, `userAgent`, `ignoreAuthorization`, `serviceDomain``allowedMethods`, `deniedMethods`, or `ReqMode` parameters. In that case, `srcPort` is required.
40+
Multiple destinations for a single service can be specified by adding index as a suffix to `servicePath`, `srcPort`, `port`, `userAgent`, `ignoreAuthorization`, `serviceDomain``allowedMethods`, `deniedMethods`, `denyHttp` or `ReqMode` parameters. In that case, `srcPort` is required.
4141

4242
### HTTP Mode Query Parameters
4343

@@ -47,6 +47,7 @@ The following query parameters can be used only when `reqMode` is set to `http`
4747
|-------------|--------------------------------------------------------------------------------|
4848
|allowedMethods|The list of allowed methods. If specified, a request with a method that is not on the list will be denied. Multiple methods can be separated with comma (`,`). The parameter can be prefixed with an index thus allowing definition of multiple destinations for a single service (e.g. `allowedMethods.1`, `allowedMethods.2`, and so on).<br>**Example:** `GET,DELETE`|
4949
|deniedMethods|The list of denied methods. If specified, a request with a method that is on the list will be denied. Multiple methods can be separated with comma (`,`). The parameter can be prefixed with an index thus allowing definition of multiple destinations for a single service (e.g. `deniedMethods.1`, `deniedMethods.2`, and so on).<br>**Example:** `PUT,POST`|
50+
|denyHttp |Whether to deny HTTP requests thus allowing only HTTPS. The parameter can be prefixed with an index thus allowing definition of multiple destinations for a single service (e.g. `denyHttp.1`, `denyHttp.2`, and so on).<br>**Example:** `true`<br>**Default Value:** `false`|
5051
|httpsOnly |If set to true, HTTP requests to the service will be redirected to HTTPS.<br>**Example:** `true`<br>**Default Value:** `false`|
5152
|outboundHostname|The hostname where the service is running, for instance on a separate swarm. If specified, the proxy will dispatch requests to that domain.<br>**Example:** `ecme.com`|
5253
|pathType |The ACL derivative. Defaults to *path_beg*. See [HAProxy path](https://cbonte.github.io/haproxy-dconv/configuration-1.5.html#7.3.6-path) for more info.<br>**Example:** `path_beg`|
@@ -65,7 +66,7 @@ The following query parameters can be used only when `reqMode` is set to `http`
6566
|usersPassEncrypted|Indicates whether passwords provided by `users` or `usersSecret` contain encrypted data. Passwords can be encrypted with the command `mkpasswd -m sha-512 password1`.<br>**Example:** `true`<br>**Default Value:** `false`|
6667
|verifyClientSsl|Whether to verify client SSL and, if it is not valid, deny request and return 403 Forbidden status code. SSL is validated against the `ca-file` specified through the environment variable `CA_FILE`.<br>**Example:** true<br>**Default Value:** `false`|
6768

68-
Multiple destinations for a single service can be specified by adding index as a suffix to `servicePath`, `srcPort`, `port`, `userAgent`, `ignoreAuthorization`, `serviceDomain`, `allowedMethods`, `deniedMethods`, or `ReqMode` parameters. In that case, `srcPort` is required.
69+
Multiple destinations for a single service can be specified by adding index as a suffix to `servicePath`, `srcPort`, `port`, `userAgent`, `ignoreAuthorization`, `serviceDomain`, `allowedMethods`, `deniedMethods`, `denyHttp` or `ReqMode` parameters. In that case, `srcPort` is required.
6970

7071
### TCP Mode HTTP Query Parameters
7172

integration_tests/integration_swarm_test.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,15 @@ func (s IntegrationSwarmTestSuite) Test_Methods() {
610610
s.Equal(403, resp.StatusCode, s.getProxyConf())
611611
}
612612

613+
func (s IntegrationSwarmTestSuite) Test_DenyHttp() {
614+
s.reconfigureGoDemo("&denyHttp=true")
615+
616+
resp, err := s.sendHelloRequest()
617+
618+
s.NoError(err)
619+
s.Equal(403, resp.StatusCode, s.getProxyConf())
620+
}
621+
613622
// Util
614623

615624
func (s *IntegrationSwarmTestSuite) areContainersRunning(expected int, name string) bool {
@@ -651,7 +660,7 @@ func (s *IntegrationSwarmTestSuite) waitForContainers(expected int, name string)
651660
i = i + 1
652661
time.Sleep(1 * time.Second)
653662
}
654-
time.Sleep(5 * time.Second)
663+
time.Sleep(2 * time.Second)
655664
}
656665

657666
func (s *IntegrationSwarmTestSuite) createGoDemoService() {

proxy/template.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ backend {{$.ServiceName}}-be{{.Port}}
144144
{{- if .DeniedMethods}}
145145
acl valid_denied_method method{{range .DeniedMethods}} {{.}}{{end}}
146146
http-request deny if valid_denied_method
147+
{{- end}}
148+
{{- if .DenyHttp}}
149+
http-request deny if !{ ssl_fc }
147150
{{- end}}
148151
{{- if $.HttpsOnly}}
149152
redirect scheme https if !{ ssl_fc }

proxy/types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ type ServiceDest struct {
1414
AllowedMethods []string
1515
// The list of denied methods. If specified, a request with a method that is on the list will be denied.
1616
DeniedMethods []string
17+
// Whether to deny HTTP requests thus allowing only HTTPS.
18+
DenyHttp bool
1719
// Whether to ignore authorization for this service destination.
1820
IgnoreAuthorization bool
1921
// The internal port of a service that should be reconfigured.
@@ -369,6 +371,7 @@ func getServiceDest(sr *Service, provider ServiceParameterProvider, index int) S
369371
return ServiceDest{
370372
AllowedMethods: getSliceFromString(provider, fmt.Sprintf("allowedMethods%s", suffix)),
371373
DeniedMethods: getSliceFromString(provider, fmt.Sprintf("deniedMethods%s", suffix)),
374+
DenyHttp: getBoolParam(provider, fmt.Sprintf("denyHttp%s", suffix)),
372375
IgnoreAuthorization: getBoolParam(provider, fmt.Sprintf("ignoreAuthorization%s", suffix)),
373376
Port: provider.GetString(fmt.Sprintf("port%s", suffix)),
374377
ReqMode: reqMode,

proxy/types_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ func (s *TypesTestSuite) getServiceMap(expected Service, indexSuffix string) map
229229
// ServiceDest
230230
"allowedMethods" + indexSuffix: strings.Join(expected.ServiceDest[0].AllowedMethods, ","),
231231
"deniedMethods" + indexSuffix: strings.Join(expected.ServiceDest[0].DeniedMethods, ","),
232+
"denyHttp" + indexSuffix: strconv.FormatBool(expected.ServiceDest[0].DenyHttp),
232233
"ignoreAuthorization" + indexSuffix: strconv.FormatBool(expected.ServiceDest[0].IgnoreAuthorization),
233234
"port" + indexSuffix: expected.ServiceDest[0].Port,
234235
"reqMode" + indexSuffix: expected.ServiceDest[0].ReqMode,
@@ -262,6 +263,7 @@ func (s *TypesTestSuite) getExpectedService() Service {
262263
ServiceDest: []ServiceDest{{
263264
AllowedMethods: []string{"GET", "DELETE"},
264265
DeniedMethods: []string{"PUT", "POST"},
266+
DenyHttp: true,
265267
IgnoreAuthorization: true,
266268
ServiceDomain: []string{"domain1", "domain2"},
267269
ServiceHeader: map[string]string{"X-Version": "3", "name": "Viktor"},

0 commit comments

Comments
 (0)