...

Source file src/sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane/auth.go

Documentation: sigs.k8s.io/controller-runtime/pkg/internal/testing/controlplane

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package controlplane
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"path/filepath"
    23  
    24  	"k8s.io/client-go/rest"
    25  	"sigs.k8s.io/controller-runtime/pkg/internal/testing/certs"
    26  	"sigs.k8s.io/controller-runtime/pkg/internal/testing/process"
    27  )
    28  
    29  // User represents a Kubernetes user.
    30  type User struct {
    31  	// Name is the user's Name.
    32  	Name string
    33  	// Groups are the groups to which the user belongs.
    34  	Groups []string
    35  }
    36  
    37  // Authn knows how to configure an API server for a particular type of authentication,
    38  // and provision users under that authentication scheme.
    39  //
    40  // The methods must be called in the following order (as presented below in the interface
    41  // for a mnemonic):
    42  //
    43  // 1. Configure
    44  // 2. Start
    45  // 3. AddUsers (0+ calls)
    46  // 4. Stop.
    47  type Authn interface {
    48  	// Configure provides the working directory to this authenticator,
    49  	// and configures the given API server arguments to make use of this authenticator.
    50  	//
    51  	// Should be called first.
    52  	Configure(workDir string, args *process.Arguments) error
    53  	// Start runs this authenticator.  Will be called just before API server start.
    54  	//
    55  	// Must be called after Configure.
    56  	Start() error
    57  	// AddUser provisions a user, returning a copy of the given base rest.Config
    58  	// configured to authenticate as that users.
    59  	//
    60  	// May only be called while the authenticator is "running".
    61  	AddUser(user User, baseCfg *rest.Config) (*rest.Config, error)
    62  	// Stop shuts down this authenticator.
    63  	Stop() error
    64  }
    65  
    66  // CertAuthn is an authenticator (Authn) that makes use of client certificate authn.
    67  type CertAuthn struct {
    68  	// ca is the CA used to sign the client certs
    69  	ca *certs.TinyCA
    70  	// certDir is the directory used to write the CA crt file
    71  	// so that the API server can read it.
    72  	certDir string
    73  }
    74  
    75  // NewCertAuthn creates a new client-cert-based Authn with a new CA.
    76  func NewCertAuthn() (*CertAuthn, error) {
    77  	ca, err := certs.NewTinyCA()
    78  	if err != nil {
    79  		return nil, fmt.Errorf("unable to provision client certificate auth CA: %w", err)
    80  	}
    81  	return &CertAuthn{
    82  		ca: ca,
    83  	}, nil
    84  }
    85  
    86  // AddUser provisions a new user that's authenticated via certificates, with
    87  // the given uesrname and groups embedded in the certificate as expected by the
    88  // API server.
    89  func (c *CertAuthn) AddUser(user User, baseCfg *rest.Config) (*rest.Config, error) {
    90  	certs, err := c.ca.NewClientCert(certs.ClientInfo{
    91  		Name:   user.Name,
    92  		Groups: user.Groups,
    93  	})
    94  	if err != nil {
    95  		return nil, fmt.Errorf("unable to create client certificates for %s: %w", user.Name, err)
    96  	}
    97  
    98  	crt, key, err := certs.AsBytes()
    99  	if err != nil {
   100  		return nil, fmt.Errorf("unable to serialize client certificates for %s: %w", user.Name, err)
   101  	}
   102  
   103  	cfg := rest.CopyConfig(baseCfg)
   104  	cfg.CertData = crt
   105  	cfg.KeyData = key
   106  
   107  	return cfg, nil
   108  }
   109  
   110  // caCrtPath returns the path to the on-disk client-cert CA crt file.
   111  func (c *CertAuthn) caCrtPath() string {
   112  	return filepath.Join(c.certDir, "client-cert-auth-ca.crt")
   113  }
   114  
   115  // Configure provides the working directory to this authenticator,
   116  // and configures the given API server arguments to make use of this authenticator.
   117  func (c *CertAuthn) Configure(workDir string, args *process.Arguments) error {
   118  	c.certDir = workDir
   119  	args.Set("client-ca-file", c.caCrtPath())
   120  	return nil
   121  }
   122  
   123  // Start runs this authenticator.  Will be called just before API server start.
   124  //
   125  // Must be called after Configure.
   126  func (c *CertAuthn) Start() error {
   127  	if len(c.certDir) == 0 {
   128  		return fmt.Errorf("start called before configure")
   129  	}
   130  	caCrt := c.ca.CA.CertBytes()
   131  	if err := os.WriteFile(c.caCrtPath(), caCrt, 0640); err != nil { //nolint:gosec
   132  		return fmt.Errorf("unable to save the client certificate CA to %s: %w", c.caCrtPath(), err)
   133  	}
   134  
   135  	return nil
   136  }
   137  
   138  // Stop shuts down this authenticator.
   139  func (c *CertAuthn) Stop() error {
   140  	// no-op -- our workdir is cleaned up for us automatically
   141  	return nil
   142  }
   143  

View as plain text