-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Samples on how to try Guard directly on any Kubernetes system (#81)
* kube * kube * kube
- Loading branch information
1 parent
c88fed5
commit 4459d49
Showing
13 changed files
with
575 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# Guard on Vanilla Kubernetes | ||
|
||
Please refer to [An Opinionated Kubernetes](https://davidhadas.wordpress.com/2022/08/29/knative-an-opinionated-kubernetes/) to learn why Knative should be your preferred path for deploying web services over Kubernetes rather than deploying directly on Kubernetes. | ||
|
||
For direct deployment on Kubernetes, Guard needs to be deployed in the service pods as a reverse proxy sidecar and the pod needs to be configured to pass ingress requests via the guard sidecar. | ||
|
||
See [documentation and a sidecar image](./cmd/guard-rproxy/README.md). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# Guard Reverse Proxy Sidecar | ||
|
||
guard-rproxy is a reverse proxy embedded with a guard-gate and packed as a container image. The container image can than be used: | ||
|
||
1. As a sidecar while deploying a Kubernetes microservice. This is the recommended mode of operation and offers both client request monitoring and control and microservice pod monitoring and control. | ||
|
||
1. As a standalone exposed Pod in from of a protected unexposed microservice. This mode is simple to try out. It offers client request monitoring and control but does not offer microservice pod monitoring and control. | ||
|
||
## Installing Security-Guard | ||
|
||
Security-Guard includes automation for auto-learning a per service Guardian. | ||
Auto-learning requires you to deploy a `guard-service` on your kubernetes cluster. | ||
`guard-service` should be installed in any namespace where you deploy knative services that require Security-Guard protection. | ||
|
||
### Install from source | ||
|
||
1. Clone the Security-Guard repository using `git clone git@github.com:knative-sandbox/security-guard.git` | ||
1. Do `cd security-guard` | ||
1. Run `ko apply -Rf ./config/resources/` | ||
1. Run `ko apply -Rf ./config/deploy/guard-service.yaml` | ||
|
||
### Install from released images and yamls | ||
|
||
Use released images to update your system to enable Security-Guard: | ||
|
||
1. Add the necessary Security-Guard resources to your cluster using: | ||
|
||
kubectl apply -f https://raw.githubusercontent.com/knative-sandbox/security-guard/release-0.1/config/resources/gateAccount.yaml | ||
kubectl apply -f https://raw.githubusercontent.com/knative-sandbox/security-guard/release-0.1/config/resources/serviceAccount.yaml | ||
kubectl apply -f https://raw.githubusercontent.com/knative-sandbox/security-guard/release-0.1/config/resources/guardiansCrd.yaml | ||
|
||
1. Deploy `guard-service` on your system to enable automated learning of micro-rules. In the current version, it is recommended to deploy `guard-service` in any namespace where knative services are deployed. | ||
|
||
An easy way to do that is using: | ||
|
||
kubectl apply -f https://github.com/knative-sandbox/security-guard/releases/download/v0.1.2/guard-service.yaml | ||
|
||
## Deploying a pod with a Security-Guard sidecar | ||
|
||
Use the following example yaml to deploy an example helloworld container with a guard sidecar: | ||
|
||
kubectl apply -f https://github.com/knative-sandbox/security-guard/releases/download/v0.1.2/secured-helloworld.yaml | ||
|
||
Security alerts can be seen in the `guard-rproxy` container of the `secured-helloworld` pod using: | ||
|
||
kubectl logs deployment/secured-helloworld guard-rproxy -f | ||
|
||
## Deploying a separate Security-Guard Pod | ||
|
||
Use the following example yaml to deploy one example `myapp` pod that include a container running helloworld, (this pod is not exposed outside of the cluster) and one `myapp-guard` pod that include a guard container to expose the myapp service outside the cluster while performing security-behavior monitoring and control on client requests: | ||
|
||
kubectl apply -f https://github.com/knative-sandbox/security-guard/releases/download/v0.1.2/secured-layered-myapp.yaml | ||
|
||
Security alerts can be seen in the `myapp-guard` pod using: | ||
|
||
kubectl logs deployment/myapp-guard -f |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/* | ||
Copyright 2022 The Knative Authors | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package main | ||
|
||
// Uncomment when running in a development environment out side of the cluster | ||
// import _ "k8s.io/client-go/plugin/pkg/client/auth" | ||
// import _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
/* | ||
Copyright 2022 The Knative Authors | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package main | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"net/http" | ||
"net/http/httputil" | ||
"net/url" | ||
"os" | ||
"runtime/debug" | ||
|
||
"github.com/kelseyhightower/envconfig" | ||
"go.uber.org/zap" | ||
|
||
"knative.dev/pkg/signals" | ||
_ "knative.dev/security-guard/pkg/guard-gate" | ||
utils "knative.dev/security-guard/pkg/guard-utils" | ||
pi "knative.dev/security-guard/pkg/pluginterfaces" | ||
) | ||
|
||
type config struct { | ||
ServiceName string `split_words:"true" required:"true"` | ||
Namespace string `split_words:"true" required:"true"` | ||
ServiceUrl string `split_words:"true" required:"true"` | ||
UseCrd bool `split_words:"true" required:"false"` | ||
MonitorPod bool `split_words:"true" required:"false"` | ||
GuardUrl string `split_words:"true" required:"false"` | ||
LogLevel string `split_words:"true" required:"false"` | ||
GuardProxyPort string `split_words:"true" required:"false"` | ||
PodMonitorInterval string `split_words:"true" required:"false"` | ||
ReportPileInterval string `split_words:"true" required:"false"` | ||
GuardianLoadInterval string `split_words:"true" required:"false"` | ||
} | ||
|
||
type GuardGate struct { | ||
nextRoundTripper http.RoundTripper // the next roundtripper | ||
securityPlug pi.RoundTripPlug | ||
} | ||
|
||
func (p *GuardGate) RoundTrip(req *http.Request) (resp *http.Response, err error) { | ||
defer func() { | ||
if recovered := recover(); recovered != nil { | ||
pi.Log.Warnf("Recovered from panic during RoundTrip! Recover: %v\n", recovered) | ||
pi.Log.Debugf("Stacktrace from panic: \n %s\n" + string(debug.Stack())) | ||
err = errors.New("paniced during RoundTrip") | ||
resp = nil | ||
} | ||
}() | ||
req.Host = "" // req.URL.Host | ||
|
||
if req, err = p.securityPlug.ApproveRequest(req); err == nil { | ||
if resp, err = p.nextRoundTripper.RoundTrip(req); err == nil { | ||
resp, err = p.securityPlug.ApproveResponse(req, resp) | ||
} | ||
} | ||
if err != nil { | ||
pi.Log.Debugf("%s: returning error %v", p.securityPlug.PlugName(), err) | ||
resp = nil | ||
} | ||
return | ||
} | ||
|
||
func (p *GuardGate) Transport(t http.RoundTripper) http.RoundTripper { | ||
if t == nil { | ||
t = http.DefaultTransport | ||
} | ||
p.nextRoundTripper = t | ||
return p | ||
} | ||
|
||
func preMain() (guardGate *GuardGate, mux *http.ServeMux, target string, plugConfig map[string]string, sid string, ns string, log *zap.SugaredLogger) { | ||
var env config | ||
if err := envconfig.Process("", &env); err != nil { | ||
fmt.Fprintf(os.Stderr, "Failed to process environment: %s\n", err.Error()) | ||
return | ||
} | ||
|
||
plugConfig = make(map[string]string) | ||
guardGate = new(GuardGate) | ||
|
||
log = utils.CreateLogger(env.LogLevel) | ||
defer log.Sync() | ||
pi.Log = log | ||
|
||
if env.GuardUrl == "" { | ||
// use default | ||
env.GuardUrl = "http://guard-service.default" | ||
} else { | ||
plugConfig["guard-url"] = env.GuardUrl | ||
} | ||
|
||
// When using a Reverse Proxy, it has a default to not use pod monitoring | ||
plugConfig["monitor-pod"] = "false" // default when used as a standalone | ||
if env.MonitorPod { | ||
plugConfig["monitor-pod"] = "true" | ||
} | ||
|
||
// When using a Reverse Proxy, it has a default to work with CM | ||
plugConfig["use-cm"] = "true" | ||
if env.UseCrd { | ||
plugConfig["use-cm"] = "false" | ||
} | ||
|
||
plugConfig["guardian-load-interval"] = env.GuardianLoadInterval | ||
plugConfig["report-pile-interval"] = env.ReportPileInterval | ||
plugConfig["pod-monitor-interval"] = env.PodMonitorInterval | ||
|
||
sid = env.ServiceName | ||
ns = env.Namespace | ||
|
||
log.Infof("guard-proxy serving serviceName: %s, namespace: %s, serviceUrl: %s", sid, ns, env.ServiceUrl) | ||
parsedUrl, err := url.Parse(env.ServiceUrl) | ||
if err != nil { | ||
log.Errorf("Failed to parse serviceUrl: %s", err.Error()) | ||
return | ||
} | ||
log.Infof("guard-proxy parsedUrl: %v", parsedUrl) | ||
|
||
proxy := httputil.NewSingleHostReverseProxy(parsedUrl) | ||
|
||
// Hook using RoundTripper | ||
|
||
securityPlug := pi.GetPlug() | ||
|
||
guardGate.securityPlug = securityPlug | ||
proxy.Transport = guardGate.Transport(proxy.Transport) | ||
|
||
target = ":22000" | ||
if env.GuardProxyPort != "" { | ||
target = fmt.Sprintf(":%s", env.GuardProxyPort) | ||
} | ||
|
||
mux = http.NewServeMux() | ||
mux.Handle("/", proxy) | ||
log.Infof("Starting Reverse Proxy on port %s", target) | ||
return | ||
} | ||
|
||
func main() { | ||
guardGate, mux, target, plugConfig, sid, ns, log := preMain() | ||
if mux == nil { | ||
os.Exit(1) | ||
} | ||
|
||
guardGate.securityPlug.Init(signals.NewContext(), plugConfig, sid, ns, log) | ||
defer guardGate.securityPlug.Shutdown() | ||
|
||
err := http.ListenAndServe(target, mux) | ||
log.Fatalf("Failed to open http local service: %s", err.Error()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/* | ||
Copyright 2022 The Knative Authors | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package main | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
|
||
_ "knative.dev/security-guard/pkg/guard-gate" | ||
) | ||
|
||
func Test_preMain(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
target string | ||
mux bool | ||
env map[string]string | ||
}{ | ||
{ | ||
name: "missing env", | ||
}, | ||
{ | ||
name: "missing service_url", | ||
env: map[string]string{"SERVICE_NAME": "sid", "NAMESPACE": "ns"}, | ||
}, | ||
{ | ||
name: "missing service_sid", | ||
env: map[string]string{"SERVICE_URL": "http://127.0.0.1:80", "NAMESPACE": "ns"}, | ||
}, | ||
{ | ||
name: "missing service_ns", | ||
env: map[string]string{"SERVICE_NAME": "sid", "SERVICE_URL": "http://127.0.0.1:80"}, | ||
}, | ||
{ | ||
name: "envok", | ||
env: map[string]string{"SERVICE_NAME": "sid", "NAMESPACE": "ns", "SERVICE_URL": "http://127.0.0.1:80"}, | ||
mux: true, | ||
target: ":22000", | ||
}, | ||
{ | ||
name: "fullenv", | ||
env: map[string]string{ | ||
"SERVICE_NAME": "sid", | ||
"NAMESPACE": "ns", | ||
"SERVICE_URL": "http://127.0.0.1:81", | ||
"GUARD_URL": "http://127.0.0.1:82", | ||
"MONITOR_POD": "true", | ||
"USE_CRD": "true", | ||
"GUARD_PROXY_PORT": "8888", | ||
}, | ||
mux: true, | ||
target: ":8888", | ||
}, | ||
{ | ||
name: "wrongenv", | ||
env: map[string]string{ | ||
"SERVICE_NAME": "sid", | ||
"NAMESPACE": "ns", | ||
"SERVICE_URL": "http://user:abc{DEf1=ghi@example.com:5432", | ||
"GUARD_URL": "http://user:abc{DEf1=ghi@example.com:5432", | ||
"MONITOR_POD": "true", | ||
"USE_CRD": "true", | ||
"GUARD_PROXY_PORT": "88881", | ||
}, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
for k, v := range tt.env { | ||
os.Setenv(k, v) | ||
} | ||
//guardGate, mux, target, plugConfig, sid, ns, log := preMain() | ||
_, mux, target, _, _, _, _ := preMain() | ||
if (mux != nil) != tt.mux { | ||
t.Errorf("preMain() mux expected %t, received %t", tt.mux, mux != nil) | ||
} | ||
if target != tt.target { | ||
t.Errorf("preMain() target expected %v, received %v", tt.target, target) | ||
} | ||
for k := range tt.env { | ||
os.Unsetenv(k) | ||
} | ||
}) | ||
} | ||
} |
Oops, something went wrong.