Skip to content

Commit

Permalink
Add spec.api.bindAddress configuration
Browse files Browse the repository at this point in the history
Signed-off-by: Alex Hutchins <gakio12@gmail.com>
Signed-off-by: gakio12 <gakio12@gmail.com>
Signed-off-by: Phillip Schichtel <phillip@schich.tel>
  • Loading branch information
gakio12 authored and pschichtel committed Dec 18, 2023
1 parent 13fc3eb commit 5d4f908
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 12 deletions.
2 changes: 1 addition & 1 deletion cmd/controller/certificates.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func (c *Certificates) Init(ctx context.Context) error {
return fmt.Errorf("failed to read ca cert: %w", err)
}
c.CACert = string(cert)
kubeConfigAPIUrl := fmt.Sprintf("https://localhost:%d", c.ClusterSpec.API.Port)
kubeConfigAPIUrl := fmt.Sprintf("https://%s:%d", c.ClusterSpec.API.APIServerAddress(), c.ClusterSpec.API.Port)
eg.Go(func() error {
// Front proxy CA
if err := c.CertManager.EnsureCA("front-proxy-ca", "kubernetes-front-proxy-ca"); err != nil {
Expand Down
1 change: 1 addition & 0 deletions cmd/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ func (c *command) start(ctx context.Context) error {
}

logrus.Infof("using api address: %s", nodeConfig.Spec.API.Address)
logrus.Infof("using api bind-address: %s", nodeConfig.Spec.API.BindAddress)
logrus.Infof("using listen port: %d", nodeConfig.Spec.API.Port)
logrus.Infof("using sans: %s", nodeConfig.Spec.API.SANs)

Expand Down
6 changes: 4 additions & 2 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ spec:
externalAddress: my-lb-address.example.com
k0sApiPort: 9443
port: 6443
bindAddress: 192.0.2.1
sans:
- 192.168.68.104
controllerManager: {}
Expand Down Expand Up @@ -124,9 +125,10 @@ spec:
### `spec.api`
| Element | Description |
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|--------------------------| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `externalAddress` | The loadbalancer address (for k0s controllers running behind a loadbalancer). Configures all cluster components to connect to this address and also configures this address for use when joining new nodes to the cluster. |
| `address` | Local address on which to bind an API. Also serves as one of the addresses pushed on the k0s create service certificate on the API. Defaults to first non-local address found on the node. |
| `address` | Local address on which to bind an API. Also serves as one of the addresses pushed on the k0s create service certificate on the API. Defaults to first non-local address found on the node. |
| `bindAddress` | The IP address for the Kubernetes API server to to listen on. The associated interface(s) must be reachable by the rest of the cluster. Will be added as an additional subject alternative name to the API server's TLS certificate. If blank or an unspecified address (`0.0.0.0` or `::`), all interfaces and IP address families will be used. This is effectively the value for the API server's `--bind-address` CLI flag. |
| `sans` | List of additional addresses to push to API servers serving the certificate. |
| `extraArgs` | Map of key-values (strings) for any extra arguments to pass down to Kubernetes api-server process. |
| `port`¹ | Custom port for kube-api server to listen on (default: 6443) |
Expand Down
31 changes: 26 additions & 5 deletions pkg/apis/k0s/v1beta1/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ type APISpec struct {
// Local address on which to bind an API
Address string `json:"address"`

// The IP address for the Kubernetes API server to to listen on.
// +kubebuilder:validation:Pattern=`^([0-9.]+|[0-9a-fA-F:%]+)$`
BindAddress string `json:"bindAddress,omitempty"`

// The loadbalancer address (for k0s controllers running behind a loadbalancer)
ExternalAddress string `json:"externalAddress,omitempty"`
// Map of key-values (strings) for any extra arguments to pass down to Kubernetes api-server process
Expand All @@ -57,11 +61,12 @@ func DefaultAPISpec() *APISpec {
addresses, _ := iface.AllAddresses()
publicAddress, _ := iface.FirstPublicAddress()
return &APISpec{
Port: defaultKasPort,
K0sAPIPort: 9443,
SANs: addresses,
Address: publicAddress,
ExtraArgs: make(map[string]string),
Port: defaultKasPort,
K0sAPIPort: 9443,
BindAddress: "0.0.0.0",
SANs: addresses,
Address: publicAddress,
ExtraArgs: make(map[string]string),
}
}

Expand Down Expand Up @@ -100,10 +105,21 @@ func (a *APISpec) getExternalURIForPort(port int) string {
return fmt.Sprintf("https://%s:%d", addr, port)
}

// APIServerAddress returns the address the API is listening on
func (a *APISpec) APIServerAddress() string {
if a.BindAddress == "" {
return "localhost"
}
return a.BindAddress
}

// Sans return the given SANS plus all local adresses and externalAddress if given
func (a *APISpec) Sans() []string {
sans, _ := iface.AllAddresses()
sans = append(sans, a.Address)
if a.BindAddress != "" {
sans = append(sans, a.BindAddress)
}
sans = append(sans, a.SANs...)
if a.ExternalAddress != "" {
sans = append(sans, a.ExternalAddress)
Expand Down Expand Up @@ -139,5 +155,10 @@ func (a *APISpec) Validate() []error {
if a.ExternalAddress != "" {
validateIPAddressOrDNSName(field.NewPath("externalAddress"), a.ExternalAddress)
}

if a.BindAddress != "" && !govalidator.IsIP(a.BindAddress) {
errors = append(errors, field.Invalid(field.NewPath("bindAddress"), a.BindAddress, "invalid IP address"))
}

return errors
}
21 changes: 18 additions & 3 deletions pkg/apis/k0s/v1beta1/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ func (s *APISuite) TestValidation() {

s.T().Run("accepts_ipv6_as_address", func(t *testing.T) {
a := APISpec{
Address: "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
Address: "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
BindAddress: "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
}

s.Nil(a.Validate())
Expand All @@ -44,7 +45,8 @@ func (s *APISuite) TestValidation() {

s.T().Run("invalid_api_address", func(t *testing.T) {
a := APISpec{
Address: "something.that.is.not.valid//(())",
Address: "something.that.is.not.valid//(())",
BindAddress: "0.0.0.0",
}

errors := a.Validate()
Expand All @@ -56,7 +58,8 @@ func (s *APISuite) TestValidation() {

s.T().Run("invalid_sans_address", func(t *testing.T) {
a := APISpec{
Address: "1.2.3.4",
Address: "1.2.3.4",
BindAddress: "0.0.0.0",
SANs: []string{
"something.that.is.not.valid//(())",
},
Expand All @@ -68,6 +71,18 @@ func (s *APISuite) TestValidation() {
s.ErrorContains(errors[0], `sans[0]: Invalid value: "something.that.is.not.valid//(())": invalid IP address / DNS name`)
}
})

s.T().Run("invalid_api_bind_address", func(t *testing.T) {
a := APISpec{
Address: "1.2.3.4",
BindAddress: "somehting.that.is.not.valid//(())",
}

errors := a.Validate()
s.NotNil(errors)
s.Len(errors, 1)
s.Contains(errors[0].Error(), "is not IP address")
})
}

func TestApiSuite(t *testing.T) {
Expand Down
6 changes: 5 additions & 1 deletion pkg/component/controller/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ func (a *APIServer) Start(_ context.Context) error {
"enable-admission-plugins": "NodeRestriction",
}

if a.ClusterConfig.Spec.API.BindAddress != "" {
args["bind-address"] = a.ClusterConfig.Spec.API.BindAddress
}

apiAudiences := []string{"https://kubernetes.default.svc"}

if a.EnableKonnectivity {
Expand Down Expand Up @@ -229,7 +233,7 @@ func (a *APIServer) Ready() error {
TLSClientConfig: tlsConfig,
}
client := &http.Client{Transport: tr}
resp, err := client.Get(fmt.Sprintf("https://localhost:%d/readyz?verbose", a.ClusterConfig.Spec.API.Port))
resp, err := client.Get(fmt.Sprintf("https://%s:%d/readyz?verbose", a.ClusterConfig.Spec.API.APIServerAddress(), a.ClusterConfig.Spec.API.Port))
if err != nil {
return err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ spec:
address:
description: Local address on which to bind an API
type: string
bindAddress:
description: The IP address for the Kubernetes API server to to
listen on.
pattern: ^([0-9.]+|[0-9a-fA-F:%]+)$
type: string
externalAddress:
description: The loadbalancer address (for k0s controllers running
behind a loadbalancer)
Expand Down

0 comments on commit 5d4f908

Please sign in to comment.