Skip to content

Commit 336113a

Browse files
Added cert-controller for easy webhook cert generation
Signed-off-by: Patryk Strusiewicz-Surmacki <patryk-pawel.strusiewicz-surmacki@external.telekom.de> Co-authored-by: Tomoki Sugiura <tomoki.sugiura@mail.shanpu.info>
1 parent 639292f commit 336113a

File tree

23 files changed

+558
-67
lines changed

23 files changed

+558
-67
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-generation
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/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 generation](#enable-automatic-certs-generation)
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 an admission webhook server, and it 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 generation
55+
56+
Run `make enable-certs-generation` under `v2/` directory to enable automatic certificate generation in `coil`.
57+
58+
```console
59+
$ make enable-certs-generation
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: 51 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,21 @@ 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+
@sed -i -E "{s/(^ +files.*)/# \1/g}" ${$@_FILE}
300+
@sed -i -E "{s/(^ +.*\.pem.*)/# \1/g}" ${$@_FILE}
301+
@sed -i -E "{s/(^ +.*\/tls.*)/# \1/g}" ${$@_FILE}
302+
endef
303+
304+
.PHONY: enable-certs-generation
305+
enable-certs-generation:
306+
@sed -i "23,35 {s/^# //}" kustomization.yaml
307+
@sed -i -E 's/^(- coil-.*controller_role\.yaml)/# \1/g' config/rbac/kustomization.yaml
308+
@sed -i -E 's/^# (- coil-.*controller-certs_role\.yaml)/\1/g' config/rbac/kustomization.yaml
309+
@$(call comment_certs,"config/default/kustomization.yaml")
310+
@$(call comment_certs,"config/default/egress/v4/kustomization.yaml")
311+
@$(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)

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

Lines changed: 39 additions & 7 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
"strconv"
@@ -9,6 +10,7 @@ import (
910
v2 "github.com/cybozu-go/coil/v2"
1011
coilv2 "github.com/cybozu-go/coil/v2/api/v2"
1112
"github.com/cybozu-go/coil/v2/controllers"
13+
"github.com/cybozu-go/coil/v2/pkg/cert"
1214
"github.com/cybozu-go/coil/v2/pkg/indexing"
1315
"github.com/cybozu-go/coil/v2/pkg/ipam"
1416
"github.com/cybozu-go/coil/v2/runners"
@@ -78,6 +80,40 @@ func subMain() error {
7880
return err
7981
}
8082

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

83119
pm := ipam.NewPoolManager(mgr.GetClient(), mgr.GetAPIReader(), ctrl.Log.WithName("pool-manager"), scheme)
@@ -90,7 +126,6 @@ func subMain() error {
90126
return err
91127
}
92128

93-
ctx := ctrl.SetupSignalHandler()
94129
if err := indexing.SetupIndexForAddressBlock(ctx, mgr); err != nil {
95130
return err
96131
}
@@ -104,6 +139,9 @@ func subMain() error {
104139
return err
105140
}
106141

142+
// wait for certificates to be configured
143+
<-certCompleted
144+
107145
// register webhooks
108146

109147
if err := (&coilv2.AddressPool{}).SetupWebhookWithManager(mgr); err != nil {
@@ -117,11 +155,5 @@ func subMain() error {
117155
return err
118156
}
119157

120-
setupLog.Info(fmt.Sprintf("starting manager (version: %s)", v2.Version()))
121-
if err := mgr.Start(ctx); err != nil {
122-
setupLog.Error(err, "problem running manager")
123-
return err
124-
}
125-
126158
return nil
127159
}

0 commit comments

Comments
 (0)