Skip to content

Commit a15ecf6

Browse files
authored
Merge pull request #319 from p-strusiewiczsurmacki-mobica/add-cert-controller
Added cert-controller for easy webhook cert generation
2 parents 8960d90 + 06113fb commit a15ecf6

25 files changed

+582
-81
lines changed

.github/workflows/ci.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,32 @@ jobs:
8989
with:
9090
name: logs-ipv6-${{ matrix.ipv6 }}-with-ipam-${{ matrix.with-ipam }}-${{ matrix.kindest-node }}.tar.gz
9191
path: v2/e2e/logs.tar.gz
92+
certs-generation:
93+
name: Cert generation test
94+
strategy:
95+
matrix:
96+
kindest-node: ["1.29.12", "1.30.8", "1.31.4"]
97+
runs-on: ubuntu-24.04
98+
steps:
99+
- uses: actions/checkout@v4
100+
- uses: actions/setup-go@v5
101+
with:
102+
go-version: ${{ env.go-version }}
103+
cache-dependency-path: "**/go.sum"
104+
- run: make image
105+
- run: make enable-certs-rotation
106+
working-directory: v2/e2e
107+
- run: make start KUBERNETES_VERSION=${{ matrix.kindest-node }}
108+
working-directory: v2/e2e
109+
- run: make install-coil
110+
working-directory: v2/e2e
111+
- run: make test
112+
working-directory: v2/e2e
113+
- run: make logs
114+
working-directory: v2/e2e
115+
if: always()
116+
- uses: actions/upload-artifact@v4
117+
if: always()
118+
with:
119+
name: logs-cert-generation-${{ matrix.kindest-node }}.tar.gz
120+
path: v2/e2e/logs.tar.gz

docs/cmd-coil-egress-controller.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@ and binds it to the **ClusterRoles** for `coil-egress`.
1616

1717
```
1818
Flags:
19-
--cert-dir string directory to locate TLS certs for webhook (default "/certs")
20-
--egress-port int32 UDP port number used by coil-egress (default 5555)
21-
--health-addr string bind address of health/readiness probes (default ":9387")
22-
-h, --help help for coil-egress-controller
23-
--metrics-addr string bind address of metrics endpoint (default ":9386")
24-
-v, --version version for coil-egress-controller
25-
--webhook-addr string bind address of admission webhook (default ":9443")
19+
--cert-dir string directory to locate TLS certs for webhook (default "/certs")
20+
--egress-port int32 UDP port number used by coil-egress (default 5555)
21+
--health-addr string bind address of health/readiness probes (default ":9387")
22+
-h, --help help for coil-egress-controller
23+
--metrics-addr string bind address of metrics endpoint (default ":9386")
24+
-v, --version version for coil-egress-controller
25+
--webhook-addr string bind address of admission webhook (default ":9443")
26+
--enable-cert-rotation enables webhook's certificate generation
27+
--enable-restart-on-cert-refresh enables pod's restart on webhook certificate refresh
2628
```

docs/cmd-coil-ipam-controller.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@ address blocks from the requested pool.
2222

2323
```
2424
Flags:
25-
--cert-dir string directory to locate TLS certs for webhook (default "/certs")
26-
--gc-interval duration garbage collection interval (default 1h0m0s)
27-
--health-addr string bind address of health/readiness probes (default ":9387")
28-
-h, --help help for coil-ipam-controller
29-
--metrics-addr string bind address of metrics endpoint (default ":9386")
30-
-v, --version version for coil-ipam-controller
31-
--webhook-addr string bind address of admission webhook (default ":9443")
25+
--cert-dir string directory to locate TLS certs for webhook (default "/certs")
26+
--gc-interval duration garbage collection interval (default 1h0m0s)
27+
--health-addr string bind address of health/readiness probes (default ":9387")
28+
-h, --help help for coil-ipam-controller
29+
--metrics-addr string bind address of metrics endpoint (default ":9386")
30+
-v, --version version for coil-ipam-controller
31+
--webhook-addr string bind address of admission webhook (default ":9443")
32+
--enable-cert-rotation enables webhook's certificate generation
33+
--enable-restart-on-cert-refresh enables pod's restart on webhook certificate refresh
3234
```
3335

3436
## Prometheus metrics

docs/setup.md

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ The YAML manifests of Coil can be generated using [kustomize](https://kubernetes
88
You can tweak optional parameters by editing [`kustomization.yaml`](../v2/kustomization.yaml) file.
99

1010
- [Install `kustomize`](#install-kustomize)
11-
- [Generate TLS certificate](#generate-tls-certificate)
11+
- [TLS certificates](#tls-certificates)
12+
- [Generate certificates manually](#generate-certificates-manually)
13+
- [Enable automatic certs rotation](#enable-automatic-certs-rotation)
1214
- [Edit `kustomization.yaml`](#edit-kustomizationyaml)
1315
- [Edit `netconf.json`](#edit-netconfjson)
1416
- [Compile and apply the manifest](#compile-and-apply-the-manifest)
@@ -19,17 +21,23 @@ You can tweak optional parameters by editing [`kustomization.yaml`](../v2/kustom
1921
- [Note on CRI runtime compatibility](#note-on-cri-runtime-compatibility)
2022
- [Standalone egress](#standalone-egress)
2123
- [Configuration](#configuration)
24+
- [Testing standalone egress](#testing-standalone-egress)
25+
- [Testing with Kindnet using IPv4](#testing-with-kindnet-using-ipv4)
26+
- [Testing with Kindnet using IPv6](#testing-with-kindnet-using-ipv6)
2227

2328
## Install `kustomize`
2429

2530
Follow the instructions: https://kubectl.docs.kubernetes.io/installation/kustomize/
2631

2732
`kustomize` 4.1.3 is verified to work for Coil.
2833

29-
## Generate TLS certificate
34+
## TLS certificates
3035

31-
Coil runs an admission webhook server, and it needs a self-signed certificate.
32-
Run `make certs` under `v2/` directory to generate the certificate.
36+
Coil runs admission webhook servers, and each one needs a self-signed certificate. You can either generate certificates manually or have Coil create them when it starts up.
37+
38+
### Generate certificates manually
39+
40+
Run `make certs` under `v2/` directory to generate the certificates.
3341

3442
```console
3543
$ make certs
@@ -43,6 +51,16 @@ config/default/cert.pem config/default/egress-key.pem config/default/ip
4351
config/default/egress-cert.pem config/default/ipam-cert.pem config/default/key.pem
4452
```
4553

54+
### Enable automatic certs rotation
55+
56+
Run `make enable-certs-rotation` under `v2/` directory to enable automatic certificate generation in `coil`.
57+
58+
```console
59+
$ make enable-certs-rotation
60+
```
61+
62+
This will configure the kustomization files that will can be later used by `make install-coil` target.
63+
4664
## Edit `kustomization.yaml`
4765

4866
`kustomization.yaml` under `v2/` directory contains some commented option settings.

v2/Makefile

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ SETUP_ENVTEST := $(shell pwd)/bin/setup-envtest
1616
YQ := $(shell pwd)/bin/yq
1717
CRD_OPTIONS = "crd:crdVersions=v1"
1818
ROLES = config/rbac/coil-ipam-controller_role.yaml \
19+
config/rbac/coil-ipam-controller-certs_role.yaml \
1920
config/rbac/coil-egress-controller_role.yaml \
21+
config/rbac/coil-egress-controller-certs_role.yaml \
2022
config/rbac/coild_role.yaml \
2123
config/rbac/coil-router_role.yaml \
2224
config/rbac/coil-egress_role.yaml
@@ -122,6 +124,23 @@ config/rbac/coil-ipam-controller_role.yaml: $(COIL_IPAM_CONTROLLER_ROLE_DEPENDS)
122124
$(CONTROLLER_GEN) rbac:roleName=coil-ipam-controller paths=./work output:stdout > $@
123125
rm -rf work
124126

127+
COIL_IPAM_CONTROLLER_CERTS_ROLE_DEPENDS = controllers/addresspool_controller.go \
128+
controllers/blockrequest_controller.go \
129+
pkg/ipam/pool.go \
130+
runners/garbage_collector.go \
131+
pkg/cert/cert.go
132+
133+
config/rbac/coil-ipam-controller-certs_role.yaml: $(COIL_IPAM_CONTROLLER_CERTS_ROLE_DEPENDS)
134+
-rm -rf work
135+
mkdir work
136+
sed '0,/^package/s/.*/package work/' controllers/addresspool_controller.go > work/addresspool_controller.go
137+
sed '0,/^package/s/.*/package work/' controllers/blockrequest_controller.go > work/blockrequest_controller.go
138+
sed '0,/^package/s/.*/package work/' pkg/ipam/pool.go > work/pool.go
139+
sed '0,/^package/s/.*/package work/' runners/garbage_collector.go > work/garbage_collector.go
140+
sed '0,/^package/s/.*/package work/' pkg/cert/cert.go > work/cert.go
141+
$(CONTROLLER_GEN) rbac:roleName=coil-ipam-controller paths=./work output:stdout > $@
142+
rm -rf work
143+
125144
COIL_EGRESS_CONTROLLER_ROLE_DEPENDS = controllers/egress_controller.go \
126145
controllers/clusterrolebinding_controller.go
127146

@@ -131,7 +150,20 @@ config/rbac/coil-egress-controller_role.yaml: $(COIL_EGRESS_CONTROLLER_ROLE_DEPE
131150
sed '0,/^package/s/.*/package work/' controllers/egress_controller.go > work/egress_controller.go
132151
sed '0,/^package/s/.*/package work/' controllers/clusterrolebinding_controller.go > work/clusterrolebinding_controller.go
133152
$(CONTROLLER_GEN) rbac:roleName=coil-egress-controller paths=./work output:stdout > $@
134-
# rm -rf work
153+
rm -rf work
154+
155+
COIL_EGRESS_CONTROLLER_CERTS_ROLE_DEPENDS = controllers/egress_controller.go \
156+
controllers/clusterrolebinding_controller.go \
157+
pkg/cert/cert.go
158+
159+
config/rbac/coil-egress-controller-certs_role.yaml: $(COIL_EGRESS_CONTROLLER_CERTS_ROLE_DEPENDS)
160+
-rm -rf work
161+
mkdir work
162+
sed '0,/^package/s/.*/package work/' controllers/egress_controller.go > work/egress_controller.go
163+
sed '0,/^package/s/.*/package work/' controllers/clusterrolebinding_controller.go > work/clusterrolebinding_controller.go
164+
sed '0,/^package/s/.*/package work/' pkg/cert/cert.go > work/cert.go
165+
$(CONTROLLER_GEN) rbac:roleName=coil-egress-controller paths=./work output:stdout > $@
166+
rm -rf work
135167

136168
COILD_DEPENDS = controllers/blockrequest_watcher.go \
137169
pkg/ipam/node.go \
@@ -259,3 +291,23 @@ staticcheck:
259291
if ! which staticcheck >/dev/null; then \
260292
env GOFLAGS= go install honnef.co/go/tools/cmd/staticcheck@latest; \
261293
fi
294+
295+
define comment_certs
296+
$(eval $@_FILE = $(1))
297+
@sed -i -E "{s/(^patchesStrategicMerge.*)/# \1/}" ${$@_FILE}
298+
@sed -i -E "{s/(^-.*webhook_manifests_patch.*)/# \1/}" ${$@_FILE}
299+
endef
300+
301+
.PHONY: enable-certs-rotation
302+
enable-certs-rotation:
303+
@sed -i "9,21 {s/^# //}" kustomization.yaml
304+
@sed -i -E 's/^(- coil-.*controller_role\.yaml)/# \1/g' ../config/rbac/kustomization.yaml
305+
@sed -i -E 's/^# (- coil-.*controller-certs_role\.yaml)/\1/g' ../config/rbac/kustomization.yaml
306+
@touch config/default/cert.pem
307+
@touch config/default/ipam-cert.pem
308+
@touch config/default/ipam-key.pem
309+
@touch config/default/egress-cert.pem
310+
@touch config/default/egress-key.pem
311+
@$(call comment_certs,"config/default/kustomization.yaml")
312+
@$(call comment_certs,"config/default/egress/v4/kustomization.yaml")
313+
@$(call comment_certs,"config/default/egress/v6/kustomization.yaml")

v2/cmd/coil-egress-controller/sub/root.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"os"
77

88
v2 "github.com/cybozu-go/coil/v2"
9+
"github.com/cybozu-go/coil/v2/pkg/constants"
910
"github.com/spf13/cobra"
1011
"k8s.io/klog/v2"
1112
"sigs.k8s.io/controller-runtime/pkg/log/zap"
@@ -18,6 +19,9 @@ var config struct {
1819
certDir string
1920
egressPort int32
2021
zapOpts zap.Options
22+
23+
enableCertRotation bool
24+
enableRestartOnCertRefresh bool
2125
}
2226

2327
var rootCmd = &cobra.Command{
@@ -47,6 +51,8 @@ func init() {
4751
pf.StringVar(&config.webhookAddr, "webhook-addr", ":9444", "bind address of admission webhook")
4852
pf.StringVar(&config.certDir, "cert-dir", "/certs", "directory to locate TLS certs for webhook")
4953
pf.Int32Var(&config.egressPort, "egress-port", 5555, "UDP port number used by coil-egress")
54+
pf.BoolVar(&config.enableCertRotation, "enable-cert-rotation", constants.DefaultEnableCertRotation, "enables webhook's certificate generation")
55+
pf.BoolVar(&config.enableRestartOnCertRefresh, "enable-restart-on-cert-refresh", constants.DefaultEnableRestartOnCertRefresh, "enables pod's restart on webhook certificate refresh")
5056

5157
goflags := flag.NewFlagSet("klog", flag.ExitOnError)
5258
klog.InitFlags(goflags)

v2/cmd/coil-egress-controller/sub/run.go

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package sub
22

33
import (
4+
"context"
45
"fmt"
56
"net"
67
"os"
@@ -10,6 +11,7 @@ import (
1011
v2 "github.com/cybozu-go/coil/v2"
1112
coilv2 "github.com/cybozu-go/coil/v2/api/v2"
1213
"github.com/cybozu-go/coil/v2/controllers"
14+
"github.com/cybozu-go/coil/v2/pkg/cert"
1315
"github.com/cybozu-go/coil/v2/pkg/constants"
1416
"k8s.io/apimachinery/pkg/runtime"
1517
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
@@ -78,9 +80,40 @@ func subMain() error {
7880
return err
7981
}
8082

81-
// register controllers
83+
certCompleted := make(chan struct{})
84+
85+
if config.enableCertRotation {
86+
if certCompleted, err = cert.SetupRotator(mgr, "egress", config.enableRestartOnCertRefresh, certCompleted); err != nil {
87+
return fmt.Errorf("failed to setup Rotator: %w", err)
88+
}
89+
} else {
90+
close(certCompleted)
91+
}
92+
93+
setupErr := make(chan error)
94+
95+
go func() {
96+
setupErr <- setupManager(mgr, certCompleted)
97+
close(setupErr)
98+
}()
99+
100+
mgrCtx, cancel := context.WithCancel(ctrl.SetupSignalHandler())
101+
defer cancel()
102+
103+
mgrErr := make(chan error)
104+
go func() {
105+
setupLog.Info(fmt.Sprintf("starting manager (version: %s)", v2.Version()))
106+
if err := mgr.Start(mgrCtx); err != nil {
107+
mgrErr <- err
108+
}
109+
close(mgrErr)
110+
}()
111+
112+
return cert.WaitForExit(setupErr, mgrErr, cancel)
113+
}
82114

83-
ctx := ctrl.SetupSignalHandler()
115+
func setupManager(mgr ctrl.Manager, certCompleted chan struct{}) error {
116+
// register controllers
84117

85118
podNS := os.Getenv(constants.EnvPodNamespace)
86119
podName := os.Getenv(constants.EnvPodName)
@@ -102,19 +135,13 @@ func subMain() error {
102135
return err
103136
}
104137

105-
// register webhooks
138+
// wait for certificates to be configured
139+
<-certCompleted
106140

141+
// register webhooks
107142
if err := (&coilv2.Egress{}).SetupWebhookWithManager(mgr); err != nil {
108143
return err
109144
}
110145

111-
// start manager
112-
113-
setupLog.Info(fmt.Sprintf("starting manager (version: %s)", v2.Version()))
114-
if err := mgr.Start(ctx); err != nil {
115-
setupLog.Error(err, "problem running manager")
116-
return err
117-
}
118-
119146
return nil
120147
}

v2/cmd/coil-ipam-controller/sub/root.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"time"
88

99
v2 "github.com/cybozu-go/coil/v2"
10+
"github.com/cybozu-go/coil/v2/pkg/constants"
1011
"github.com/spf13/cobra"
1112
"k8s.io/klog/v2"
1213
"sigs.k8s.io/controller-runtime/pkg/log/zap"
@@ -19,6 +20,9 @@ var config struct {
1920
certDir string
2021
gcInterval time.Duration
2122
zapOpts zap.Options
23+
24+
enableCertRotation bool
25+
enableRestartOnCertRefresh bool
2226
}
2327

2428
var rootCmd = &cobra.Command{
@@ -48,6 +52,8 @@ func init() {
4852
pf.StringVar(&config.webhookAddr, "webhook-addr", ":9443", "bind address of admission webhook")
4953
pf.StringVar(&config.certDir, "cert-dir", "/certs", "directory to locate TLS certs for webhook")
5054
pf.DurationVar(&config.gcInterval, "gc-interval", 1*time.Hour, "garbage collection interval")
55+
pf.BoolVar(&config.enableCertRotation, "enable-cert-rotation", constants.DefaultEnableCertRotation, "enables webhook's certificate generation")
56+
pf.BoolVar(&config.enableRestartOnCertRefresh, "enable-restart-on-cert-refresh", constants.DefaultEnableRestartOnCertRefresh, "enables pod's restart on webhook certificate refresh")
5157

5258
goflags := flag.NewFlagSet("klog", flag.ExitOnError)
5359
klog.InitFlags(goflags)

0 commit comments

Comments
 (0)