Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[liqoctl] generate peering-user #2911

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/liqoctl/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
"github.com/liqotech/liqo/pkg/liqoctl/rest/identity"
"github.com/liqotech/liqo/pkg/liqoctl/rest/kubeconfig"
"github.com/liqotech/liqo/pkg/liqoctl/rest/nonce"
peeringuser "github.com/liqotech/liqo/pkg/liqoctl/rest/peering-user"
"github.com/liqotech/liqo/pkg/liqoctl/rest/publickey"
"github.com/liqotech/liqo/pkg/liqoctl/rest/resourceslice"
"github.com/liqotech/liqo/pkg/liqoctl/rest/tenant"
Expand All @@ -56,6 +57,7 @@ var liqoResources = []rest.APIProvider{
publickey.PublicKey,
tenant.Tenant,
nonce.Nonce,
peeringuser.PeeringUser,
identity.Identity,
resourceslice.ResourceSlice,
kubeconfig.Kubeconfig,
Expand Down
4 changes: 2 additions & 2 deletions cmd/liqoctl/cmd/unpeer.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ offloaded workloads to be rescheduled. The Identity and Tenant are respectively
removed from the consumer and provider clusters, and the networking between the
two clusters is destroyed.

The reverse peering, if any, is preserved, and the remote cluster can continue
The reverse peering, if any, is preserved, and the remote cluster can continue
offloading workloads to its virtual node representing the local cluster.

Examples:
Expand Down Expand Up @@ -66,7 +66,7 @@ func newUnpeerCommand(ctx context.Context, f *factory.Factory) *cobra.Command {

cmd.PersistentFlags().DurationVar(&options.Timeout, "timeout", 120*time.Second, "Timeout for unpeering completion")
cmd.PersistentFlags().BoolVar(&options.Wait, "wait", true, "Wait for resource to be deleted before returning")
cmd.PersistentFlags().BoolVar(&options.KeepNamespaces, "keep-namespaces", false, "Keep tenant namespaces after unpeering")
cmd.PersistentFlags().BoolVar(&options.DeleteNamespace, "delete-namespaces", false, "Delete the tenant namespace after unpeering")

options.LocalFactory.AddFlags(cmd.PersistentFlags(), cmd.RegisterFlagCompletionFunc)
options.RemoteFactory.AddFlags(cmd.PersistentFlags(), cmd.RegisterFlagCompletionFunc)
Expand Down
65 changes: 65 additions & 0 deletions deployments/liqo/files/liqo-peering-user-ClusterRole.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
rules:
- apiGroups:
- "networking.liqo.io"
resources:
- "configurations"
- "gatewayclients"
- "gatewayservers"
- "publickeies"
verbs:
- "create"
- "update"
- "get"
- "list"
- "delete"
- apiGroups:
- "networking.liqo.io"
resources:
- "connections"
verbs:
- "get"
- "list"
- apiGroups:
- "networking.liqo.io"
resources:
- "gatewayclients/status"
- "gatewayservers/status"
verbs:
- "get"
- apiGroups:
- ""
resources:
- "configmaps"
- "secrets"
verbs:
- "create"
- "get"
- "list"
- "delete"
- apiGroups:
- ""
resources:
- "services"
verbs:
- "get"
- apiGroups:
- "apps"
resources:
- "deployments"
verbs:
- "get"
- apiGroups:
- "ipam.liqo.io"
resources:
- "ips"
verbs:
- "create"
- "update"
- "get"
- "delete"
- apiGroups:
- "authentication.liqo.io"
resources:
- "tenants/status"
verbs:
- "get"
17 changes: 17 additions & 0 deletions deployments/liqo/files/liqo-peering-user-Role.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
rules:
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
- list
- watch
- apiGroups:
- "networking.liqo.io"
resources:
- "wggatewayservertemplates"
- "wggatewayclienttemplates"
verbs:
- "get"
- "list"
17 changes: 17 additions & 0 deletions deployments/liqo/templates/liqo-peer-rbac.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{{- $peeringroles := (merge (dict "name" "peering-user" "module" "peering-user") .) -}}

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ include "liqo.prefixedName" $peeringroles}}
labels:
{{- include "liqo.labels" $peeringroles| nindent 4 }}
{{ .Files.Get (include "liqo.cluster-role-filename" (dict "prefix" ( include "liqo.prefixedName" $peeringroles))) }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ include "liqo.prefixedName" $peeringroles}}
labels:
{{- include "liqo.labels" $peeringroles| nindent 4 }}
{{ .Files.Get (include "liqo.role-filename" (dict "prefix" ( include "liqo.prefixedName" $peeringroles))) }}
42 changes: 39 additions & 3 deletions docs/usage/peer.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,48 @@ You can configure and fine-tune each module separately using the individual comm
For the majority and the cases the `liqoctl peer` is enough.
However, **to know the best strategy for each case and the requirements of each approach, check the [peering strategies guide](/advanced/peering-strategies.md)**.

### Peering establishment
### Getting the required permissions to establish a peering

To proceed, ensure that you are operating in the *consumer* cluster, and then issue the *liqoctl peer* command:
To create a peering with a *provider* cluster, you will require a kubeconfig with a set of permissions to establish a connection with it.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
To create a peering with a *provider* cluster, you will require a kubeconfig with a set of permissions to establish a connection with it.
The setup of a peering with a *provider* cluster requires the creation of a `kubeconfig` tailored with the proper set of permissions to minimize security risks, namely, to allow only to the peering on the remote cluster while preventing any other actions on the other resources of the cluster.


The [liqoctl](../installation/liqoctl.md) CLI tool provides utility functions to manage the permissions of users able to create a peering connection with the current cluster.

**From the *provider* cluster**, you can run the following command to generate a *kubeconfig*:

```bash
liqoctl generate peering-user \
--kubeconfig $PROVIDER_KUBECONFIG_PATH \
--consumer-cluster-id $CONSUMER_CLUSTER_ID > $CONSUMER_KUBECONFIG_PATH
```

```{warning}
Once you generate the *kubeconfig*, take note of it as it will not be stored by Liqo.
If you lose it, you will need to delete and recreate it.
```

This command will create a *kubeconfig* with **the minimum permissions to create and destroy a peering with the current cluster** from a cluster with ID `$CONSUMER_CLUSTER_ID`.

You are allowed to have a single peering user for each consumer cluster, so you will not be able to create a new kubeconfig for the same consumer cluster unless you delete the previous one.

````{admonition} Note
To delete a peering user for the consumer cluster with ID `$CONSUMER_CLUSTER_ID`, run:

```bash
liqoctl delete peering-user \
--consumer-cluster-id $CONSUMER_CLUSTER_ID
```

**Once you delete a peering user, its kubeconfig will not be valid anymore, even though a new peering user for the same cluster is created.**
````

### Establish a peering connection

To establish a peering connection between two clusters, ensure that you are operating in the *consumer* cluster, then issue the *liqoctl peer* command:

```bash
liqoctl --kubeconfig=$CONSUMER_KUBECONFIG_PATH peer --remote-kubeconfig $PROVIDER_KUBECONFIG_PATH
liqoctl peer \
--kubeconfig=$CONSUMER_KUBECONFIG_PATH \
--remote-kubeconfig $PROVIDER_KUBECONFIG_PATH
```

```{warning}
Expand Down
3 changes: 3 additions & 0 deletions pkg/consts/authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,7 @@ const (

// RenewAnnotation is the value of the annotation that enables the renewal of a resource.
RenewAnnotation = "liqo.io/renew"

// PeeringUserNameLabelKey labels all the resources created to grant peering permissions to the user doing a pering toward this cluster.
PeeringUserNameLabelKey = "liqo.io/peering-user-name"
)
20 changes: 20 additions & 0 deletions pkg/liqo-controller-manager/authentication/csr.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,26 @@ func GenerateCSRForControlPlane(key ed25519.PrivateKey, clusterID liqov1beta1.Cl
return generateCSR(key, CommonNameControlPlaneCSR(clusterID), OrganizationControlPlaneCSR())
}

// GenerateCSRForPeerUser generates a new CSR given a private key and the clusterID from which the peering will start.
func GenerateCSRForPeerUser(key ed25519.PrivateKey, clusterID liqov1beta1.ClusterID) (csrBytes []byte, userCN string, err error) {
userCN, err = commonNamePeerUser(clusterID)
if err != nil {
return nil, "", fmt.Errorf("unable to generate user CN: %w", err)
}

csrBytes, err = generateCSR(key, userCN, OrganizationControlPlaneCSR())
return
}

// commonNamePeerUser returns the common name for the user creating the peering. To avoid reuses of the same name, a suffix is added.
func commonNamePeerUser(clusterID liqov1beta1.ClusterID) (string, error) {
randSuffix := make([]byte, 16)
if _, err := rand.Read(randSuffix); err != nil {
return "", err
}
return fmt.Sprintf("liqo-peer-user-%s-%x", clusterID, randSuffix), nil
}

// CommonNameControlPlaneCSR returns the common name for a control plane CSR.
func CommonNameControlPlaneCSR(clusterID liqov1beta1.ClusterID) string {
return string(clusterID)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func (r *TenantReconciler) Reconcile(ctx context.Context, req ctrl.Request) (res
if authv1beta1.GetAuthzPolicyValue(tenant.Spec.AuthzPolicy) != authv1beta1.TolerateNoHandshake {
// get the nonce for the tenant

nonceSecret, err := getters.GetNonceSecretByClusterID(ctx, r.Client, clusterID)
nonceSecret, err := getters.GetNonceSecretByClusterID(ctx, r.Client, clusterID, corev1.NamespaceAll)
if err != nil {
klog.Errorf("Unable to get the nonce for the Tenant %q: %s", req.Name, err)
r.EventRecorder.Event(tenant, corev1.EventTypeWarning, "NonceNotFound", err.Error())
Expand Down
10 changes: 5 additions & 5 deletions pkg/liqo-controller-manager/authentication/utils/nonce.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func EnsureNonceSecret(ctx context.Context, cl client.Client,
// already a nonce secret in the tenant namespace.
func EnsureSignedNonceSecret(ctx context.Context, cl client.Client,
remoteClusterID liqov1beta1.ClusterID, tenantNamespace string, nonce *string) error {
nonceSecret, err := getters.GetSignedNonceSecretByClusterID(ctx, cl, remoteClusterID)
nonceSecret, err := getters.GetSignedNonceSecretByClusterID(ctx, cl, remoteClusterID, tenantNamespace)
switch {
case errors.IsNotFound(err):
// Secret not found. Create it given the provided nonce.
Expand Down Expand Up @@ -80,8 +80,8 @@ func EnsureSignedNonceSecret(ctx context.Context, cl client.Client,
}

// RetrieveNonce retrieves the nonce from the secret in the tenant namespace.
func RetrieveNonce(ctx context.Context, cl client.Client, remoteClusterID liqov1beta1.ClusterID) ([]byte, error) {
nonce, err := getters.GetNonceSecretByClusterID(ctx, cl, remoteClusterID)
func RetrieveNonce(ctx context.Context, cl client.Client, remoteClusterID liqov1beta1.ClusterID, tenantNs string) ([]byte, error) {
nonce, err := getters.GetNonceSecretByClusterID(ctx, cl, remoteClusterID, tenantNs)
if err != nil {
return nil, fmt.Errorf("unable to get nonce secret: %w", err)
}
Expand All @@ -90,8 +90,8 @@ func RetrieveNonce(ctx context.Context, cl client.Client, remoteClusterID liqov1
}

// RetrieveSignedNonce retrieves the signed nonce from the secret in the tenant namespace.
func RetrieveSignedNonce(ctx context.Context, cl client.Client, remoteClusterID liqov1beta1.ClusterID) ([]byte, error) {
secret, err := getters.GetSignedNonceSecretByClusterID(ctx, cl, remoteClusterID)
func RetrieveSignedNonce(ctx context.Context, cl client.Client, remoteClusterID liqov1beta1.ClusterID, tenantNs string) ([]byte, error) {
secret, err := getters.GetSignedNonceSecretByClusterID(ctx, cl, remoteClusterID, tenantNs)
if err != nil {
return nil, fmt.Errorf("unable to get signed nonce secret: %w", err)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,12 +171,14 @@ func (r *VirtualNodeReconciler) ensureVirtualKubeletDeploymentAbsence(
return err
}

crbName := k8strings.ShortenString(fmt.Sprintf("%s%s", vkMachinery.CRBPrefix, virtualNode.Name), 253)
err = r.Client.Delete(ctx, &rbacv1.ClusterRoleBinding{ObjectMeta: metav1.ObjectMeta{
Name: k8strings.ShortenString(fmt.Sprintf("%s%s", vkMachinery.CRBPrefix, virtualNode.Name), 253),
Name: crbName,
}})
if client.IgnoreNotFound(err) != nil {
return err
}
klog.Info(fmt.Sprintf("[%v] Deleted virtual-kubelet CRB %s", virtualNode.Spec.ClusterID, crbName))

err = r.Client.Delete(ctx, &corev1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{
Name: virtualNode.Name, Namespace: virtualNode.Namespace,
Expand Down
8 changes: 4 additions & 4 deletions pkg/liqoctl/authenticate/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,13 @@ func (c *Cluster) EnsureNonce(ctx context.Context) ([]byte, error) {
s.Success("Nonce secret ensured")

// Wait for secret to be filled with the nonce.
if err := c.waiter.ForNonce(ctx, c.RemoteClusterID, false); err != nil {
if err := c.waiter.ForNonce(ctx, c.RemoteClusterID, c.TenantNamespace, false); err != nil {
return nil, err
}

// Retrieve nonce from secret.
s = c.local.Printer.StartSpinner("Retrieving nonce")
nonceValue, err := authutils.RetrieveNonce(ctx, c.local.CRClient, c.RemoteClusterID)
nonceValue, err := authutils.RetrieveNonce(ctx, c.local.CRClient, c.RemoteClusterID, c.TenantNamespace)
if err != nil {
s.Fail(fmt.Sprintf("Unable to retrieve nonce: %v", output.PrettyErr(err)))
return nil, err
Expand All @@ -135,13 +135,13 @@ func (c *Cluster) EnsureSignedNonce(ctx context.Context, nonce []byte) ([]byte,
s.Success("Signed nonce secret ensured")

// Wait for secret to be filled with the signed nonce.
if err := c.waiter.ForSignedNonce(ctx, c.RemoteClusterID, false); err != nil {
if err := c.waiter.ForSignedNonce(ctx, c.RemoteClusterID, false, c.TenantNamespace); err != nil {
return nil, err
}

// Retrieve signed nonce from secret.
s = c.local.Printer.StartSpinner("Retrieving signed nonce")
signedNonceValue, err := authutils.RetrieveSignedNonce(ctx, c.local.CRClient, c.RemoteClusterID)
signedNonceValue, err := authutils.RetrieveSignedNonce(ctx, c.local.CRClient, c.RemoteClusterID, c.TenantNamespace)
if err != nil {
s.Fail(fmt.Sprintf("Unable to retrieve signed nonce: %v", output.PrettyErr(err)))
return nil, err
Expand Down
21 changes: 3 additions & 18 deletions pkg/liqoctl/info/localstatus/local_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"

liqov1beta1 "github.com/liqotech/liqo/apis/core/v1beta1"
"github.com/liqotech/liqo/pkg/consts"
"github.com/liqotech/liqo/pkg/liqoctl/info"
"github.com/liqotech/liqo/pkg/liqoctl/output"
liqoctlutils "github.com/liqotech/liqo/pkg/liqoctl/utils"
Expand All @@ -46,10 +47,6 @@ type InstallationChecker struct {
data Installation
}

const (
ctrlManagerContainerName = "controller-manager"
)

// Collect data about the local installation of Liqo.
func (l *InstallationChecker) Collect(ctx context.Context, options info.Options) {
// Get the cluster ID of the local cluster
Expand All @@ -64,7 +61,7 @@ func (l *InstallationChecker) Collect(ctx context.Context, options info.Options)
if err != nil {
l.AddCollectionError(fmt.Errorf("unable to get Liqo version and cluster labels: %w", err))
} else {
ctrlContainer, err := l.getCtrlManagerContainer(ctrlDeployment)
ctrlContainer, err := liqoctlutils.GetCtrlManagerContainer(ctrlDeployment)
if err != nil {
l.AddCollectionError(fmt.Errorf("unable to get Liqo instance info: %w", err))
} else {
Expand Down Expand Up @@ -143,22 +140,10 @@ func (l *InstallationChecker) collectClusterLabels(ctrlContainer *corev1.Contain
}

func (l *InstallationChecker) collectLiqoVersion(ctrlDeployment *appsv1.Deployment) error {
version, err := getters.GetContainerImageVersion(ctrlDeployment.Spec.Template.Spec.Containers, ctrlManagerContainerName)
version, err := getters.GetContainerImageVersion(ctrlDeployment.Spec.Template.Spec.Containers, consts.ControllerManagerAppName)
if err != nil {
return err
}
l.data.Version = version
return nil
}

func (l *InstallationChecker) getCtrlManagerContainer(ctrlDeployment *appsv1.Deployment) (*corev1.Container, error) {
// Get the container of the controller manager
containers := ctrlDeployment.Spec.Template.Spec.Containers
for i := range containers {
if containers[i].Name == ctrlManagerContainerName {
return &containers[i], nil
}
}

return nil, fmt.Errorf("invalid controller manager deployment: no container with name %q found", ctrlManagerContainerName)
}
6 changes: 5 additions & 1 deletion pkg/liqoctl/output/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,10 @@ func NewGlobalPrinter(scoped, verbose bool) *Printer {
}

func newPrinter(scope string, color pterm.Color, scoped, verbose bool) *Printer {
generic := &pterm.PrefixPrinter{MessageStyle: pterm.NewStyle(pterm.FgDefault)}
generic := &pterm.PrefixPrinter{
MessageStyle: pterm.NewStyle(pterm.FgDefault),
Writer: os.Stderr,
}

if scoped {
generic = generic.WithScope(pterm.Scope{Text: scope, Style: pterm.NewStyle(pterm.FgGray)})
Expand Down Expand Up @@ -311,6 +314,7 @@ func newPrinter(scope string, color pterm.Color, scoped, verbose bool) *Printer
ShowTimer: true,
TimerRoundingFactor: time.Second,
TimerStyle: &pterm.ThemeDefault.TimerStyle,
Writer: os.Stderr,
}

printer.BulletList = &pterm.BulletListPrinter{}
Expand Down
2 changes: 1 addition & 1 deletion pkg/liqoctl/peer/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func (o *Options) RunPeer(ctx context.Context) error {
// Ensure networking
if !o.NetworkingDisabled {
if err := ensureNetworking(ctx, o); err != nil {
o.LocalFactory.PrinterGlobal.Error.Println("unable to ensure networking")
o.LocalFactory.PrinterGlobal.Error.Printfln("Unable to ensure networking: %v", err)
return err
}
}
Expand Down
Loading
Loading