mirror of
https://github.com/rancher/os.git
synced 2025-09-07 01:31:06 +00:00
185 lines
5.9 KiB
Go
185 lines
5.9 KiB
Go
package server
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
v1 "github.com/rancher/os2/pkg/apis/rancheros.cattle.io/v1"
|
|
"github.com/rancher/os2/pkg/clients"
|
|
ranchercontrollers "github.com/rancher/os2/pkg/generated/controllers/management.cattle.io/v3"
|
|
roscontrollers "github.com/rancher/os2/pkg/generated/controllers/rancheros.cattle.io/v1"
|
|
"github.com/rancher/os2/pkg/tpm"
|
|
v3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
|
|
corecontrollers "github.com/rancher/wrangler/pkg/generated/controllers/core/v1"
|
|
corev1 "k8s.io/api/core/v1"
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
)
|
|
|
|
var (
|
|
tokenType = "rancheros.cattle.io/token"
|
|
tokenKey = "token"
|
|
tokenIndex = "tokenIndex"
|
|
machineBySecretNameIndex = "machineBySecretNameIndex"
|
|
registrationTokenIndex = "registrationTokenIndex"
|
|
tpmHashIndex = "tpmHashIndex"
|
|
)
|
|
|
|
type authenticator interface {
|
|
Authenticate(resp http.ResponseWriter, req *http.Request, registerNamespace string) (*v1.MachineInventory, bool, io.WriteCloser, error)
|
|
}
|
|
|
|
type InventoryServer struct {
|
|
settingCache ranchercontrollers.SettingCache
|
|
secretCache corecontrollers.SecretCache
|
|
machineCache roscontrollers.MachineInventoryCache
|
|
machineClient roscontrollers.MachineInventoryClient
|
|
machineRegistrationCache roscontrollers.MachineRegistrationCache
|
|
clusterRegistrationToken ranchercontrollers.ClusterRegistrationTokenCache
|
|
authenticators []authenticator
|
|
}
|
|
|
|
func New(clients *clients.Clients) *InventoryServer {
|
|
server := &InventoryServer{
|
|
authenticators: []authenticator{
|
|
tpm.New(clients),
|
|
newSharedSecretAuth(clients),
|
|
},
|
|
secretCache: clients.Core.Secret().Cache(),
|
|
machineCache: clients.OS.MachineInventory().Cache(),
|
|
machineClient: clients.OS.MachineInventory(),
|
|
machineRegistrationCache: clients.OS.MachineRegistration().Cache(),
|
|
settingCache: clients.Rancher.Setting().Cache(),
|
|
clusterRegistrationToken: clients.Rancher.ClusterRegistrationToken().Cache(),
|
|
}
|
|
|
|
server.secretCache.AddIndexer(tokenHash, func(obj *corev1.Secret) ([]string, error) {
|
|
if string(obj.Type) != tokenType {
|
|
return nil, nil
|
|
}
|
|
if token := obj.Data[tokenKey]; len(token) > 0 {
|
|
hash := sha256.Sum256(token)
|
|
return []string{base64.StdEncoding.EncodeToString(hash[:])}, nil
|
|
}
|
|
return nil, nil
|
|
})
|
|
|
|
server.machineCache.AddIndexer(tokenHash, func(obj *v1.MachineInventory) ([]string, error) {
|
|
if obj.Spec.TPMHash == "" {
|
|
return nil, nil
|
|
}
|
|
hash := sha256.Sum256([]byte(obj.Spec.TPMHash))
|
|
return []string{base64.StdEncoding.EncodeToString(hash[:])}, nil
|
|
})
|
|
|
|
server.machineRegistrationCache.AddIndexer(registrationTokenIndex, func(obj *v1.MachineRegistration) ([]string, error) {
|
|
if obj.Status.RegistrationToken == "" {
|
|
return nil, nil
|
|
}
|
|
return []string{
|
|
obj.Status.RegistrationToken,
|
|
}, nil
|
|
})
|
|
|
|
server.machineCache.AddIndexer(tpmHashIndex, func(obj *v1.MachineInventory) ([]string, error) {
|
|
if obj.Spec.TPMHash == "" {
|
|
return nil, nil
|
|
}
|
|
return []string{obj.Spec.TPMHash}, nil
|
|
})
|
|
|
|
return server
|
|
}
|
|
|
|
func (i *InventoryServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
|
|
if strings.Contains(req.URL.Path, "/registration/") {
|
|
i.register(resp, req)
|
|
} else if strings.HasSuffix(req.URL.Path, "/cacerts") {
|
|
i.cacerts(resp, req)
|
|
} else {
|
|
i.handle(resp, req)
|
|
}
|
|
}
|
|
|
|
func (i *InventoryServer) authMachine(resp http.ResponseWriter, req *http.Request, registerNamespace string) (*v1.MachineInventory, io.WriteCloser, error) {
|
|
for _, auth := range i.authenticators {
|
|
machine, cont, writer, err := auth.Authenticate(resp, req, registerNamespace)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
if machine != nil || !cont {
|
|
return machine, writer, nil
|
|
}
|
|
}
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func writeErr(writer io.Writer, resp http.ResponseWriter, err error) {
|
|
message := "Unauthorized"
|
|
if err != nil {
|
|
message = err.Error()
|
|
}
|
|
if writer == nil {
|
|
http.Error(resp, message, http.StatusUnauthorized)
|
|
} else {
|
|
writer.Write([]byte(message))
|
|
}
|
|
}
|
|
|
|
func (i *InventoryServer) handle(resp http.ResponseWriter, req *http.Request) {
|
|
machine, writer, err := i.authMachine(resp, req, "")
|
|
if machine == nil || err != nil {
|
|
writeErr(writer, resp, err)
|
|
return
|
|
}
|
|
defer writer.Close()
|
|
|
|
if machine.Spec.ClusterName == "" {
|
|
writeErr(writer, resp, errors.New("cluster not assigned"))
|
|
return
|
|
}
|
|
crt, err := i.clusterRegistrationToken.Get(machine.Status.ClusterRegistrationTokenNamespace,
|
|
machine.Status.ClusterRegistrationTokenName)
|
|
if apierrors.IsNotFound(err) || crt.Status.Token == "" {
|
|
writeErr(writer, resp, errors.New("cluster token not assigned"))
|
|
}
|
|
|
|
if err := writeResponse(writer, machine, crt); err != nil {
|
|
writeErr(writer, resp, err)
|
|
}
|
|
}
|
|
|
|
type config struct {
|
|
Role string `json:"role,omitempty"`
|
|
NodeName string `json:"nodeName,omitempty"`
|
|
Address string `json:"address,omitempty"`
|
|
InternalAddress string `json:"internalAddress,omitempty"`
|
|
Taints []string `json:"taints,omitempty"`
|
|
Labels []string `json:"labels,omitempty"`
|
|
Token string `json:"token,omitempty"`
|
|
}
|
|
|
|
func writeResponse(writer io.Writer, inventory *v1.MachineInventory, crt *v3.ClusterRegistrationToken) error {
|
|
config := config{
|
|
Role: inventory.Spec.Config.Role,
|
|
NodeName: inventory.Spec.Config.NodeName,
|
|
Address: inventory.Spec.Config.Address,
|
|
InternalAddress: inventory.Spec.Config.InternalAddress,
|
|
Taints: nil,
|
|
Labels: nil,
|
|
Token: crt.Status.Token,
|
|
}
|
|
for k, v := range inventory.Spec.Config.Labels {
|
|
config.Labels = append(config.Labels, fmt.Sprintf("%s=%s", k, v))
|
|
}
|
|
for _, taint := range inventory.Spec.Config.Taints {
|
|
config.Labels = append(config.Labels, taint.ToString())
|
|
}
|
|
return json.NewEncoder(writer).Encode(config)
|
|
}
|