mirror of https://github.com/k3s-io/k3s
Merge pull request #9302 from brendandburns/fix
Add messages to indicate that opening an external firewall may be necessary.pull/6/head
commit
2c54be808e
|
@ -0,0 +1,37 @@
|
|||
# Services and Firewalls
|
||||
|
||||
Many cloud providers (e.g. Google Compute Engine) define firewalls that help keep prevent inadvertent
|
||||
exposure to the internet. When exposing a service to the external world, you may need to open up
|
||||
one or more ports in these firewalls to serve traffic. This document describes this process, as
|
||||
well as any provider specific details that may be necessary.
|
||||
|
||||
|
||||
### Google Compute Engine
|
||||
Google Compute Engine firewalls are documented [elsewhere](https://cloud.google.com/compute/docs/networking#firewalls_1).
|
||||
|
||||
You can add a firewall with the ```gcloud``` command line tool:
|
||||
|
||||
```
|
||||
gcloud compute firewall-rules create my-rule --allow=tcp:<port>
|
||||
```
|
||||
|
||||
**Note**
|
||||
There is one important security note when using firewalls on Google Compute Engine:
|
||||
|
||||
Firewalls are defined per-vm, rather than per-ip address. This means that if you open a firewall for that service's ports,
|
||||
anything that serves on that port on that VM's host IP address may potentially serve traffic.
|
||||
|
||||
Note that this is not a problem for other Kubernetes services, as they listen on IP addresses that are different than the
|
||||
host node's external IP address.
|
||||
|
||||
Consider:
|
||||
* You create a Service with an external load balancer (IP Address 1.2.3.4) and port 80
|
||||
* You open the firewall for port 80 for all nodes in your cluster, so that the external Service actually can deliver packets to your Service
|
||||
* You start an nginx server, running on port 80 on the host virtual machine (IP Address 2.3.4.5). This nginx is **also** exposed to the internet on the VM's external IP address.
|
||||
|
||||
Consequently, please be careful when opening firewalls in Google Compute Engine or Google Container Engine. You may accidentally be exposing other services to the wilds of the internet.
|
||||
|
||||
### Other cloud providers
|
||||
Coming soon.
|
||||
|
||||
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/services-firewalls.md?pixel)]()
|
|
@ -19,12 +19,15 @@ package cmd
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
|
||||
cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
|
@ -102,6 +105,7 @@ func RunCreate(f *cmdutil.Factory, out io.Writer, filenames util.StringList) err
|
|||
}
|
||||
count++
|
||||
info.Refresh(obj, true)
|
||||
printObjectSpecificMessage(info.Object, out)
|
||||
fmt.Fprintf(out, "%s/%s\n", info.Mapping.Resource, info.Name)
|
||||
return nil
|
||||
})
|
||||
|
@ -113,3 +117,36 @@ func RunCreate(f *cmdutil.Factory, out io.Writer, filenames util.StringList) err
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func printObjectSpecificMessage(obj runtime.Object, out io.Writer) {
|
||||
switch obj := obj.(type) {
|
||||
case *api.Service:
|
||||
if obj.Spec.Type == api.ServiceTypeLoadBalancer {
|
||||
msg := fmt.Sprintf(`
|
||||
An external load-balanced service was created. On many platforms (e.g. Google Compute Engine),
|
||||
you will also need to explicitly open a Firewall rule for the service port(s) (%s) to serve traffic.
|
||||
|
||||
See https://github.com/GoogleCloudPlatform/kubernetes/tree/master/docs/services-firewall.md for more details.
|
||||
`, makePortsString(obj.Spec.Ports))
|
||||
out.Write([]byte(msg))
|
||||
}
|
||||
if obj.Spec.Type == api.ServiceTypeNodePort {
|
||||
msg := fmt.Sprintf(`
|
||||
You have exposed your service on an external port on all nodes in your cluster.
|
||||
If you want to expose this service to the external internet, you may need to set up
|
||||
firewall rules for the service port(s) (%s) to serve traffic.
|
||||
|
||||
See https://github.com/GoogleCloudPlatform/kubernetes/tree/master/docs/services-firewall.md for more details.
|
||||
`, makePortsString(obj.Spec.Ports))
|
||||
out.Write([]byte(msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makePortsString(ports []api.ServicePort) string {
|
||||
pieces := make([]string, len(ports))
|
||||
for ix := range ports {
|
||||
pieces[ix] = fmt.Sprintf("%s:%d", strings.ToLower(string(ports[ix].Protocol)), ports[ix].Port)
|
||||
}
|
||||
return strings.Join(pieces, ",")
|
||||
}
|
||||
|
|
|
@ -21,7 +21,9 @@ import (
|
|||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
func TestExtraArgsFail(t *testing.T) {
|
||||
|
@ -129,3 +131,77 @@ func TestCreateDirectory(t *testing.T) {
|
|||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrintObjectSpecificMessage(t *testing.T) {
|
||||
tests := []struct {
|
||||
obj runtime.Object
|
||||
expectOutput bool
|
||||
}{
|
||||
{
|
||||
obj: &api.Service{},
|
||||
expectOutput: false,
|
||||
},
|
||||
{
|
||||
obj: &api.Pod{},
|
||||
expectOutput: false,
|
||||
},
|
||||
{
|
||||
obj: &api.Service{Spec: api.ServiceSpec{Type: api.ServiceTypeLoadBalancer}},
|
||||
expectOutput: true,
|
||||
},
|
||||
{
|
||||
obj: &api.Service{Spec: api.ServiceSpec{Type: api.ServiceTypeNodePort}},
|
||||
expectOutput: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
buff := &bytes.Buffer{}
|
||||
printObjectSpecificMessage(test.obj, buff)
|
||||
if test.expectOutput && buff.Len() == 0 {
|
||||
t.Errorf("Expected output, saw none for %v", test.obj)
|
||||
}
|
||||
if !test.expectOutput && buff.Len() > 0 {
|
||||
t.Errorf("Expected no output, saw %s for %v", buff.String(), test.obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakePortsString(t *testing.T) {
|
||||
tests := []struct {
|
||||
ports []api.ServicePort
|
||||
expectedOutput string
|
||||
}{
|
||||
{ports: nil, expectedOutput: ""},
|
||||
{ports: []api.ServicePort{}, expectedOutput: ""},
|
||||
{ports: []api.ServicePort{
|
||||
{
|
||||
Port: 80,
|
||||
Protocol: "TCP",
|
||||
},
|
||||
},
|
||||
expectedOutput: "tcp:80",
|
||||
},
|
||||
{ports: []api.ServicePort{
|
||||
{
|
||||
Port: 80,
|
||||
Protocol: "TCP",
|
||||
},
|
||||
{
|
||||
Port: 8080,
|
||||
Protocol: "UDP",
|
||||
},
|
||||
{
|
||||
Port: 9000,
|
||||
Protocol: "TCP",
|
||||
},
|
||||
},
|
||||
expectedOutput: "tcp:80,udp:8080,tcp:9000",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
output := makePortsString(test.ports)
|
||||
if output != test.expectedOutput {
|
||||
t.Errorf("expected: %s, saw: %s.", test.expectedOutput, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -201,5 +201,15 @@ func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
|
|||
}
|
||||
}
|
||||
|
||||
if cmdutil.GetFlagBool(cmd, "create-external-load-balancer") {
|
||||
msg := fmt.Sprintf(`
|
||||
An external load-balanced service was created. On many platforms (e.g. Google Compute Engine),
|
||||
you will also need to explicitly open a firewall rule for the service port (%d) to serve traffic.
|
||||
|
||||
See https://github.com/GoogleCloudPlatform/kubernetes/tree/master/docs/services-firewall.md for more details.
|
||||
`, cmdutil.GetFlagInt(cmd, "port"))
|
||||
out.Write([]byte(msg))
|
||||
}
|
||||
|
||||
return f.PrintObject(cmd, object, out)
|
||||
}
|
||||
|
|
|
@ -69,8 +69,7 @@ func TestRunExposeService(t *testing.T) {
|
|||
Selector: map[string]string{"func": "stream"},
|
||||
},
|
||||
},
|
||||
expected: "services/foo",
|
||||
status: 200,
|
||||
status: 200,
|
||||
},
|
||||
{
|
||||
name: "no-name-passed-from-the-cli",
|
||||
|
@ -103,7 +102,40 @@ func TestRunExposeService(t *testing.T) {
|
|||
Selector: map[string]string{"run": "this"},
|
||||
},
|
||||
},
|
||||
expected: "services/mayor",
|
||||
status: 200,
|
||||
},
|
||||
{
|
||||
name: "expose-external-service",
|
||||
args: []string{"service", "baz"},
|
||||
ns: "test",
|
||||
calls: map[string]string{
|
||||
"GET": "/namespaces/test/services/baz",
|
||||
"POST": "/namespaces/test/services",
|
||||
},
|
||||
input: &api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
|
||||
TypeMeta: api.TypeMeta{Kind: "Service", APIVersion: "v1"},
|
||||
Spec: api.ServiceSpec{
|
||||
Selector: map[string]string{"app": "go"},
|
||||
},
|
||||
},
|
||||
flags: map[string]string{"selector": "func=stream", "protocol": "UDP", "port": "14", "name": "foo", "labels": "svc=test", "create-external-load-balancer": "true"},
|
||||
output: &api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "12", Labels: map[string]string{"svc": "test"}},
|
||||
TypeMeta: api.TypeMeta{Kind: "Service", APIVersion: "v1"},
|
||||
Spec: api.ServiceSpec{
|
||||
Ports: []api.ServicePort{
|
||||
{
|
||||
Name: "default",
|
||||
Protocol: api.Protocol("UDP"),
|
||||
Port: 14,
|
||||
},
|
||||
},
|
||||
Selector: map[string]string{"func": "stream"},
|
||||
Type: api.ServiceTypeLoadBalancer,
|
||||
},
|
||||
},
|
||||
expected: "you will also need to explicitly open a firewall",
|
||||
status: 200,
|
||||
},
|
||||
}
|
||||
|
@ -135,9 +167,11 @@ func TestRunExposeService(t *testing.T) {
|
|||
}
|
||||
cmd.Run(cmd, test.args)
|
||||
|
||||
out := buf.String()
|
||||
if strings.Contains(out, test.expected) {
|
||||
t.Errorf("%s: unexpected output: %s", test.name, out)
|
||||
if len(test.expected) > 0 {
|
||||
out := buf.String()
|
||||
if !strings.Contains(out, test.expected) {
|
||||
t.Errorf("%s: unexpected output: %s", test.name, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,6 +115,7 @@ func RunUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
|
|||
return err
|
||||
}
|
||||
info.Refresh(obj, true)
|
||||
printObjectSpecificMessage(obj, out)
|
||||
fmt.Fprintf(out, "%s/%s\n", info.Mapping.Resource, info.Name)
|
||||
return nil
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue