Skip to content

Commit 11e3afb

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 52b38c6 commit 11e3afb

File tree

21 files changed

+386
-34
lines changed

21 files changed

+386
-34
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 those manually or `coil` can generate those for you upon start.
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: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ check-generate:
7575
$(MAKE) generate
7676
$(MAKE) manifests
7777
go mod tidy
78-
git diff --exit-code
78+
git diff --exit-code -- ':!config/rbac/coil-egress-controller_role.yaml' ':!config/rbac/coil-ipam-controller_role.yaml'
7979

8080
# Generate manifests e.g. CRD, RBAC etc.
8181
.PHONY: manifests
@@ -110,7 +110,8 @@ $(YQ):
110110
COIL_IPAM_CONTROLLER_ROLE_DEPENDS = controllers/addresspool_controller.go \
111111
controllers/blockrequest_controller.go \
112112
pkg/ipam/pool.go \
113-
runners/garbage_collector.go
113+
runners/garbage_collector.go \
114+
pkg/cert/cert.go
114115

115116
config/rbac/coil-ipam-controller_role.yaml: $(COIL_IPAM_CONTROLLER_ROLE_DEPENDS)
116117
-rm -rf work
@@ -119,19 +120,22 @@ config/rbac/coil-ipam-controller_role.yaml: $(COIL_IPAM_CONTROLLER_ROLE_DEPENDS)
119120
sed '0,/^package/s/.*/package work/' controllers/blockrequest_controller.go > work/blockrequest_controller.go
120121
sed '0,/^package/s/.*/package work/' pkg/ipam/pool.go > work/pool.go
121122
sed '0,/^package/s/.*/package work/' runners/garbage_collector.go > work/garbage_collector.go
123+
sed '0,/^package/s/.*/package work/' pkg/cert/cert.go > work/cert.go
122124
$(CONTROLLER_GEN) rbac:roleName=coil-ipam-controller paths=./work output:stdout > $@
123125
rm -rf work
124126

125127
COIL_EGRESS_CONTROLLER_ROLE_DEPENDS = controllers/egress_controller.go \
126-
controllers/clusterrolebinding_controller.go
128+
controllers/clusterrolebinding_controller.go \
129+
pkg/cert/cert.go
127130

128131
config/rbac/coil-egress-controller_role.yaml: $(COIL_EGRESS_CONTROLLER_ROLE_DEPENDS)
129132
-rm -rf work
130133
mkdir work
131134
sed '0,/^package/s/.*/package work/' controllers/egress_controller.go > work/egress_controller.go
132135
sed '0,/^package/s/.*/package work/' controllers/clusterrolebinding_controller.go > work/clusterrolebinding_controller.go
136+
sed '0,/^package/s/.*/package work/' pkg/cert/cert.go > work/cert.go
133137
$(CONTROLLER_GEN) rbac:roleName=coil-egress-controller paths=./work output:stdout > $@
134-
# rm -rf work
138+
rm -rf work
135139

136140
COILD_DEPENDS = controllers/blockrequest_watcher.go \
137141
pkg/ipam/node.go \
@@ -259,3 +263,19 @@ staticcheck:
259263
if ! which staticcheck >/dev/null; then \
260264
env GOFLAGS= go install honnef.co/go/tools/cmd/staticcheck@latest; \
261265
fi
266+
267+
define comment_certs
268+
$(eval $@_FILE = $(1))
269+
sed -i -E "{s/(^patchesStrategicMerge.*)/# \1/}" ${$@_FILE}
270+
sed -i -E "{s/(.*webhook_manifests_patch.*)/# \1/}" ${$@_FILE}
271+
sed -i -E "{s/(.*files.*)/# \1/g}" ${$@_FILE}
272+
sed -i -E "{s/(.*\.pem.*)/# \1/g}" ${$@_FILE}
273+
sed -i -E "{s/(.*\/tls.*)/# \1/g}" ${$@_FILE}
274+
endef
275+
276+
.PHONY: enable-certs-generation
277+
enable-certs-generation:
278+
sed -i "22,47 {s/^# //}" kustomization.yaml
279+
@$(call comment_certs,"config/default/kustomization.yaml")
280+
@$(call comment_certs,"config/default/egress/v4/kustomization.yaml")
281+
@$(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: 39 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,41 @@ func subMain() error {
7880
return err
7981
}
8082

81-
// register controllers
83+
setupFinished := make(chan struct{})
84+
85+
if config.enableCertRotation {
86+
setupFinished, err = cert.SetupRotator(mgr, "egress", config.enableRestartOnCertRefresh, setupFinished)
87+
if err != nil {
88+
return fmt.Errorf("failed to setup Rotator: %w", err)
89+
}
90+
} else {
91+
close(setupFinished)
92+
}
93+
94+
setupErr := make(chan error)
95+
96+
go func() {
97+
setupErr <- setupManager(mgr, setupFinished)
98+
close(setupErr)
99+
}()
100+
101+
mgrCtx, cancel := context.WithCancel(ctrl.SetupSignalHandler())
102+
defer cancel()
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+
}
82115

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

85119
podNS := os.Getenv(constants.EnvPodNamespace)
86120
podName := os.Getenv(constants.EnvPodName)
@@ -102,19 +136,13 @@ func subMain() error {
102136
return err
103137
}
104138

105-
// register webhooks
139+
// wait for certificates to be configured
140+
<-certCompleted
106141

142+
// register webhooks
107143
if err := (&coilv2.Egress{}).SetupWebhookWithManager(mgr); err != nil {
108144
return err
109145
}
110146

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-
119147
return nil
120148
}

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: 40 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,41 @@ func subMain() error {
7880
return err
7981
}
8082

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

83120
pm := ipam.NewPoolManager(mgr.GetClient(), mgr.GetAPIReader(), ctrl.Log.WithName("pool-manager"), scheme)
@@ -90,7 +127,6 @@ func subMain() error {
90127
return err
91128
}
92129

93-
ctx := ctrl.SetupSignalHandler()
94130
if err := indexing.SetupIndexForAddressBlock(ctx, mgr); err != nil {
95131
return err
96132
}
@@ -104,6 +140,9 @@ func subMain() error {
104140
return err
105141
}
106142

143+
// wait for certificates to be configured
144+
<-certCompleted
145+
107146
// register webhooks
108147

109148
if err := (&coilv2.AddressPool{}).SetupWebhookWithManager(mgr); err != nil {
@@ -117,11 +156,5 @@ func subMain() error {
117156
return err
118157
}
119158

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-
126159
return nil
127160
}

v2/config/default/egress/v4/kustomization.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ resources:
44
- ../../../pod/egress/v4
55
- ../../../webhook/egress
66

7+
# [CERTS] Following lines should be commented if automatic cert generation is used.
78
patchesStrategicMerge:
89
- ../webhook_manifests_patch.yaml
910

@@ -13,8 +14,9 @@ generatorOptions:
1314
secretGenerator:
1415
# [EGRESS] Following lines be uncommented to enable Egress NAT features.
1516
- name: coilv2-egress-webhook-server-cert
17+
# [CERTS] Following lines should be commented if automatic cert generation is used.
1618
files:
1719
- ca.crt=../../cert.pem
1820
- tls.crt=../../egress-cert.pem
1921
- tls.key=../../egress-key.pem
20-
type: "kubernetes.io/tls"
22+
type: "kubernetes.io/tls"

0 commit comments

Comments
 (0)