Skip to content

Commit

Permalink
add webhook with cert-manager issued certificate
Browse files Browse the repository at this point in the history
  • Loading branch information
hongzhen-ma committed Dec 7, 2021
1 parent 4499505 commit df3d397
Show file tree
Hide file tree
Showing 10 changed files with 211 additions and 479 deletions.
4 changes: 4 additions & 0 deletions cmd/cmdmain.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/kubeovn/kube-ovn/cmd/ovn_monitor"
"github.com/kubeovn/kube-ovn/cmd/pinger"
"github.com/kubeovn/kube-ovn/cmd/speaker"
"github.com/kubeovn/kube-ovn/cmd/webhook"
)

const (
Expand All @@ -19,6 +20,7 @@ const (
CmdMonitor = "kube-ovn-monitor"
CmdPinger = "kube-ovn-pinger"
CmdSpeaker = "kube-ovn-speaker"
CmdWebHook = "kube-ovn-webhook"
)

func main() {
Expand All @@ -35,6 +37,8 @@ func main() {
pinger.CmdMain()
case CmdSpeaker:
speaker.CmdMain()
case CmdWebHook:
webhook.CmdMain()
default:
klog.Fatalf("%s is an unknown command", cmd)
}
Expand Down
53 changes: 6 additions & 47 deletions cmd/webhook/server.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package main
package webhook

import (
"flag"
_ "net/http/pprof" // #nosec
"os"
"time"

appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
Expand All @@ -15,7 +12,6 @@ import (
ctrlwebhook "sigs.k8s.io/controller-runtime/pkg/webhook"

ovnv1 "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1"
"github.com/kubeovn/kube-ovn/pkg/ovs"
ovnwebhook "github.com/kubeovn/kube-ovn/pkg/webhook"
"github.com/kubeovn/kube-ovn/versions"
)
Expand All @@ -40,22 +36,11 @@ func init() {
}
}

func main() {
var (
port int
ovnNbHost string
ovnNbPort int
ovnNbTimeout int
defaultLS string
)
func CmdMain() {
var port int
klog.Infof(versions.String())

flag.IntVar(&port, "port", 8443, "The port webhook listen on.")
flag.IntVar(&ovnNbPort, "ovn-nb-port", 6641, "OVN nb port")
flag.IntVar(&ovnNbTimeout, "ovn-nb-timeout", 30, "OVN nb timeout")
flag.StringVar(&ovnNbHost, "ovn-nb-host", "0.0.0.0", "OVN nb host")
flag.StringVar(&defaultLS, "default-ls", "ovn-default", "The default logical switch name, default: ovn-default")

klog.InitFlags(nil)
flag.Parse()

Expand All @@ -77,47 +62,21 @@ func main() {
panic(err)
}

opt := &ovnwebhook.WebhookOptions{
OvnNbHost: ovnNbHost,
OvnNbPort: ovnNbPort,
OvnNbTimeout: ovnNbTimeout,
DefaultLS: defaultLS,
}
validatingHook, err := ovnwebhook.NewValidatingHook(mgr.GetCache(), opt)
validatingHook, err := ovnwebhook.NewValidatingHook(mgr.GetCache())
if err != nil {
panic(err)
}

klog.Infof("register path /validate-ip")
// Register the webhooks in the server.
hookServer.Register("/validate-ip", &ctrlwebhook.Admission{Handler: validatingHook})

if err := mgr.Add(hookServer); err != nil {
panic(err)
}

go loopOvnNbctlDaemon(ovnNbHost, ovnNbPort)

// Start the server by starting a previously-set-up manager
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
panic(err)
}
}

func loopOvnNbctlDaemon(ovnNbHost string, ovnNbPort int) {
for {
daemonSocket := os.Getenv("OVN_NB_DAEMON")
time.Sleep(5 * time.Second)

if _, err := os.Stat(daemonSocket); os.IsNotExist(err) || daemonSocket == "" {
if err := ovs.StartOvnNbctlDaemon(ovnNbHost); err != nil {
klog.Errorf("failed to start ovn-nbctl daemon, %v", err)
}
}

if err := ovs.CheckAlive(); err != nil {
klog.Warningf("ovn-nbctl daemon doesn't return, start a new daemon")
if err := ovs.StartOvnNbctlDaemon(ovnNbHost); err != nil {
klog.Errorf("failed to start ovn-nbctl daemon, %v", err)
}
}
}
}
3 changes: 2 additions & 1 deletion dist/images/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ RUN ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-controller && \
ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-daemon && \
ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-monitor && \
ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-pinger && \
ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-speaker
ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-speaker && \
ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-webhook
3 changes: 2 additions & 1 deletion dist/images/Dockerfile.rpm
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,5 @@ RUN ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-controller && \
ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-daemon && \
ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-monitor && \
ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-pinger && \
ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-speaker
ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-speaker && \
ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-webhook
77 changes: 77 additions & 0 deletions docs/webhook.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Webhook

From Kube-OVN v1.9.0, webhook is added back. The most important thing for webhook is ip address conflict and subnet cidr validating.

## Pre-request

- Kube-OVN without webhook
- Cert-Manager

## Cert-Manager installation

The webhook needs https, so we use cert-manager here to generate the certificate. Normally cert-manager doesn't use `hostNetwork`, so it needs CNI to allocate IP addresses. As a result, we should install Kube-OVN, cert-manager before webhook.

You can use the command downside to install Cert-Manager

`kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.6.1/cert-manager.yaml`

And the help document refers to [cert-manager](https://cert-manager.io/docs/installation/).

## Webhook installation
The wehook has not been added to the `install.sh` script. So it should be installed manullay with the command `kubectl apply -f yamls/webhook.yaml`.

After installation, you can find a pod in kube-system the same namespace as other pods.

```
apple@bogon kube-ovn % kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-78fcd69978-k576h 1/1 Running 0 2d23h
coredns-78fcd69978-m76xs 1/1 Running 0 2d23h
etcd-kube-ovn-control-plane 1/1 Running 0 2d23h
kube-apiserver-kube-ovn-control-plane 1/1 Running 0 2d23h
kube-controller-manager-kube-ovn-control-plane 1/1 Running 0 2d23h
kube-ovn-cni-kkgz4 1/1 Running 0 2d21h
kube-ovn-cni-q4nf2 1/1 Running 0 2d21h
kube-ovn-controller-7bd57d84d8-z94ck 1/1 Running 1 (2d3h ago) 2d21h
kube-ovn-webhook-5bfccc66d-b8tzh 1/1 Running 0 30m
.
.
.
apple@bogon kube-ovn %
```

## Test
You can create a pod with static ip address `10.16.0.15`.
```
apple@bogon ovn-test % kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
static-7584848b74-fw9dm 1/1 Running 0 2d13h 10.16.0.15 kube-ovn-worker <none> <none>
apple@bogon ovn-test %
```

And use the yaml downside to create another pod with same static ip address.
```
apiVersion: v1
kind: Pod
metadata:
annotations:
ovn.kubernetes.io/ip_address: 10.16.0.15
ovn.kubernetes.io/mac_address: 00:00:00:53:6B:B6
labels:
app: static
managedFields:
name: staticip-pod
namespace: default
spec:
containers:
- image: qaimages:helloworld
imagePullPolicy: IfNotPresent
name: qatest
```

As a result, this operation is denied by the webhook.
```
apple@bogon ovn-test % kubectl apply -f pod-static.yaml
Error from server (annotation ip address 10.16.0.15 is conflict with ip crd static-7584848b74-fw9dm.default 10.16.0.15): error when creating "pod-static.yaml": admission webhook "pod-ip-validaing.kube-ovn.io" denied the request: annotation ip address 10.16.0.15 is conflict with ip crd static-7584848b74-fw9dm.default 10.16.0.15
apple@bogon ovn-test %
```
3 changes: 2 additions & 1 deletion pkg/util/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ const (
VpcExternalNet = "ovn-vpc-external-network"
VpcLbNetworkAttachment = "ovn-vpc-lb"

DefaultVpc = "ovn-cluster"
DefaultVpc = "ovn-cluster"
DefaultSubnet = "ovn-default"

EcmpRouteType = "ecmp"
NormalRouteType = "normal"
Expand Down
22 changes: 21 additions & 1 deletion pkg/util/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func cidrConflict(cidr string) error {
}

func ValidateSubnet(subnet kubeovnv1.Subnet) error {
if !CIDRContainIP(subnet.Spec.CIDRBlock, subnet.Spec.Gateway) {
if subnet.Spec.Gateway != "" && !CIDRContainIP(subnet.Spec.CIDRBlock, subnet.Spec.Gateway) {
return fmt.Errorf(" gateway %s is not in cidr %s", subnet.Spec.Gateway, subnet.Spec.CIDRBlock)
}
if err := cidrConflict(subnet.Spec.CIDRBlock); err != nil {
Expand Down Expand Up @@ -194,3 +194,23 @@ func ValidatePodCidr(cidr, ip string) error {
}
return nil
}

func ValidateCidrConflict(subnet kubeovnv1.Subnet, subnetList []kubeovnv1.Subnet) error {
for _, sub := range subnetList {
if sub.Spec.Vpc != subnet.Spec.Vpc || sub.Spec.Vlan != subnet.Spec.Vlan || sub.Name == subnet.Name {
continue
}

if CIDRConflict(sub.Spec.CIDRBlock, subnet.Spec.CIDRBlock) {
err := fmt.Errorf("subnet %s cidr %s is conflict with subnet %s cidr %s", subnet.Name, subnet.Spec.CIDRBlock, sub.Name, sub.Spec.CIDRBlock)
return err
}

if subnet.Spec.ExternalEgressGateway != "" && sub.Spec.ExternalEgressGateway != "" &&
subnet.Spec.PolicyRoutingTableID == sub.Spec.PolicyRoutingTableID {
err := fmt.Errorf("subnet %s policy routing table ID %d is conflict with subnet %s policy routing table ID %d", subnet.Name, subnet.Spec.PolicyRoutingTableID, sub.Name, sub.Spec.PolicyRoutingTableID)
return err
}
}
return nil
}

0 comments on commit df3d397

Please sign in to comment.