Skip to content

Commit

Permalink
Merge pull request rancher#3 from crobby/0802-2migration
Browse files Browse the repository at this point in the history
Adding annotation/labels for migrated objects also blocking login while migration is active
  • Loading branch information
nflynt committed Aug 3, 2023
2 parents 3de5aa3 + b3acab9 commit b6b6085
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 52 deletions.
39 changes: 2 additions & 37 deletions cleanup/ad-guid-unmigration.yaml
Expand Up @@ -61,43 +61,8 @@ metadata:
namespace: default
rules:
- apiGroups:
- ''
resources:
- secrets
verbs:
- get
- apiGroups:
- ''
resources:
- configmaps
verbs:
- get
- create
- update
- apiGroups:
- management.cattle.io
resources:
- authconfigs
- clusterroletemplatebindings
- projectroletemplatebindings
- tokens
- users
verbs:
- '*'
- apiGroups:
- rbac.authorization.k8s.io
resources:
- rolebindings
- clusterrolebindings
verbs:
- list
- get
- delete
- apiGroups:
- batch
resources:
- jobs
- '*'
verbs:
- list
- get
- delete
- '*'
62 changes: 47 additions & 15 deletions pkg/agent/clean/active_directory.go
Expand Up @@ -38,11 +38,18 @@ import (
)

const (
listAdUsersOperation = "list-ad-users"
migrateAdUserOperation = "migrate-ad-user"
activeDirectoryPrefix = "activedirectory_user://"
statusConfigMapName = "ad-guid-migration"
statusConfigMapNamespace = "cattle-system"
listAdUsersOperation = "list-ad-users"
migrateAdUserOperation = "migrate-ad-user"
activeDirectoryPrefix = "activedirectory_user://"
StatusConfigMapName = "ad-guid-migration"
StatusConfigMapNamespace = "cattle-system"
StatusMigrationField = "ad-guid-migration-status"
StatusMigrationFinished = "Finished"
StatusMigrationRunning = "Running"
StatusLoginDisabled = "login is disabled while migration is running"
adGUIDMigrationLabel = "ad-guid-migration"
adGUIDMigrationAnnotation = "ad-guid-migration-data"
migratedLabelValue = "migrated"
)

type migrateUserWorkUnit struct {
Expand Down Expand Up @@ -77,6 +84,13 @@ func (e LdapConnectionPermanentlyFailed) Error() string {
return "ldap search failed to connect after exhausting maximum retry attempts"
}

type LoginDisabledError struct{}

// Error provides a string representation of an LdapErrorNotFound
func (e LoginDisabledError) Error() string {
return StatusLoginDisabled
}

func scaledContext(restConfig *restclient.Config) (*config.ScaledContext, error) {
sc, err := config.NewScaledContext(*restConfig, nil)
if err != nil {
Expand Down Expand Up @@ -291,7 +305,7 @@ func ListAdUsers(clientConfig *restclient.Config) error {
}
defer lConn.Close()

err = updateMigrationStatus(sc, "ad-guid-migration-status", "Running")
err = updateMigrationStatus(sc, StatusMigrationField, StatusMigrationRunning)
if err != nil {
return fmt.Errorf("unable to update migration status configmap: %v", err)
}
Expand All @@ -315,7 +329,7 @@ func ListAdUsers(clientConfig *restclient.Config) error {
// If any of the binding replacements fail, then the resulting rancher state for this user is inconsistent
// and we should NOT attempt to modify the user or delete any of its duplicates. This situation is unusual
// and must be investigated by the local admin.
err := migrateTokens(userToMigrate.originalUser.Name, dnPrincipal, sc, dryRun)
err := migrateTokens(userToMigrate, dnPrincipal, sc, dryRun)
if err != nil {
logrus.Errorf("[%v] unable to migrate tokens for user %v: %v", listAdUsersOperation, userToMigrate.originalUser.Name, err)
continue
Expand Down Expand Up @@ -372,6 +386,9 @@ func ListAdUsers(clientConfig *restclient.Config) error {
}
// Having updated all permissions bindings and resolved all potential principal ID conflicts, it is
// finally safe to save the modified original user

userToMigrate.originalUser.Annotations[adGUIDMigrationAnnotation] = userToMigrate.guid
userToMigrate.originalUser.Labels[adGUIDMigrationLabel] = migratedLabelValue
_, err = sc.Management.Users("").Update(userToMigrate.originalUser)
if err != nil {
logrus.Errorf("[%v] failed to save modified user '%v' with: %v", listAdUsersOperation, userToMigrate.originalUser.Name, err)
Expand All @@ -380,7 +397,7 @@ func ListAdUsers(clientConfig *restclient.Config) error {
}
}

err = updateMigrationStatus(sc, "ad-guid-migration-status", "Finished")
err = updateMigrationStatus(sc, StatusMigrationField, StatusMigrationFinished)
if err != nil {
return fmt.Errorf("unable to update migration status configmap: %v", err)
}
Expand Down Expand Up @@ -568,9 +585,9 @@ func getExternalIdAndScope(principalID string) (string, string, error) {
return externalID, scope, nil
}

func migrateTokens(userName string, newPrincipalID string, sc *config.ScaledContext, dryRun bool) error {
func migrateTokens(user migrateUserWorkUnit, newPrincipalID string, sc *config.ScaledContext, dryRun bool) error {
tokenLabelSelector := labels.SelectorFromSet(labels.Set{
"authn.management.cattle.io/token-userId": userName,
"authn.management.cattle.io/token-userId": user.originalUser.Username,
})
tokenListOptions := metav1.ListOptions{
LabelSelector: tokenLabelSelector.String(),
Expand All @@ -584,6 +601,9 @@ func migrateTokens(userName string, newPrincipalID string, sc *config.ScaledCont

for _, userToken := range tokens.Items {
userToken.UserPrincipal.Name = newPrincipalID
userToken.Annotations[adGUIDMigrationAnnotation] = user.guid
userToken.Labels[adGUIDMigrationLabel] = migratedLabelValue

if dryRun {
logrus.Infof("Dry Run: Skipping update of %s", userToken.Name)
} else {
Expand All @@ -608,11 +628,17 @@ func migrateCRTB(guid string, newPrincipalID string, sc *config.ScaledContext, d
if dryRun {
logrus.Infof("Dry Run: Skipping update of %s", oldCrtb.Name)
} else {
newAnnotations := oldCrtb.Annotations
newAnnotations[adGUIDMigrationAnnotation] = guid
newLabels := oldCrtb.Labels
newLabels[adGUIDMigrationLabel] = migratedLabelValue
newCrtb := &v3.ClusterRoleTemplateBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "",
Namespace: oldCrtb.ObjectMeta.Namespace,
GenerateName: "crtb-",
Annotations: newAnnotations,
Labels: newLabels,
},
ClusterName: oldCrtb.ClusterName,
UserName: oldCrtb.UserName,
Expand Down Expand Up @@ -645,11 +671,17 @@ func migratePRTB(guid string, newPrincipalID string, sc *config.ScaledContext, d
if dryRun {
logrus.Infof("Dry Run: Skipping update of %s", oldPrtb.Name)
} else {
newAnnotations := oldPrtb.Annotations
newAnnotations[adGUIDMigrationAnnotation] = guid
newLabels := oldPrtb.Labels
newLabels[adGUIDMigrationLabel] = migratedLabelValue
newPrtb := &v3.ProjectRoleTemplateBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "",
Namespace: oldPrtb.ObjectMeta.Namespace,
GenerateName: "prtb-",
Annotations: newAnnotations,
Labels: newLabels,
},
ProjectName: oldPrtb.ProjectName,
UserName: oldPrtb.UserName,
Expand All @@ -671,26 +703,26 @@ func migratePRTB(guid string, newPrincipalID string, sc *config.ScaledContext, d
}

func updateMigrationStatus(sc *config.ScaledContext, status string, value string) error {
cm, err := sc.Core.ConfigMaps(statusConfigMapNamespace).Get(statusConfigMapName, metav1.GetOptions{})
cm, err := sc.Core.ConfigMaps(StatusConfigMapNamespace).Get(StatusConfigMapName, metav1.GetOptions{})
if err != nil {
// Create a new ConfigMap if it doesn't exist
if !apierrors.IsNotFound(err) {
return err
}
cm = &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: statusConfigMapName,
Namespace: statusConfigMapNamespace,
Name: StatusConfigMapName,
Namespace: StatusConfigMapNamespace,
},
}
}

cm.Data = map[string]string{status: value}

if _, err := sc.Core.ConfigMaps(statusConfigMapNamespace).Update(cm); err != nil {
if _, err := sc.Core.ConfigMaps(StatusConfigMapNamespace).Update(cm); err != nil {
// If the ConfigMap does not exist, create it
if apierrors.IsNotFound(err) {
_, err = sc.Core.ConfigMaps(statusConfigMapNamespace).Create(cm)
_, err = sc.Core.ConfigMaps(StatusConfigMapNamespace).Create(cm)
if err != nil {
return fmt.Errorf("unable to create migration status configmap: %v", err)
}
Expand Down
Expand Up @@ -36,6 +36,7 @@ var scopes = []string{UserScope, GroupScope}
type adProvider struct {
ctx context.Context
authConfigs v3.AuthConfigInterface
configMaps corev1.ConfigMapInterface
secrets corev1.SecretInterface
userMGR user.Manager
certs string
Expand All @@ -47,6 +48,7 @@ func Configure(ctx context.Context, mgmtCtx *config.ScaledContext, userMGR user.
return &adProvider{
ctx: ctx,
authConfigs: mgmtCtx.Management.AuthConfigs(""),
configMaps: mgmtCtx.Core.ConfigMaps(""),
secrets: mgmtCtx.Core.Secrets(""),
userMGR: userMGR,
tokenMGR: tokenMGR,
Expand Down Expand Up @@ -78,6 +80,12 @@ func (p *adProvider) AuthenticateUser(ctx context.Context, input interface{}) (v
return v3.Principal{}, nil, "", errors.New("unexpected input type")
}

migrationConfigMap, _ := p.configMaps.GetNamespaced("cattle-system", "ad-guid-migration", metav1.GetOptions{})
migrationStatus := migrationConfigMap.Data["ad-guid-migration-status"]
if migrationStatus == "Running" {
return v3.Principal{}, nil, "Unable to perform login while migration is running", fmt.Errorf("login is disabled while migration is running")
}

config, caPool, err := p.getActiveDirectoryConfig()
if err != nil {
return v3.Principal{}, nil, "", errors.New("can't find authprovider")
Expand Down
4 changes: 4 additions & 0 deletions pkg/auth/providers/publicapi/login.go
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/rancher/rancher/pkg/agent/clean"
"io/ioutil"
"net/http"
"strconv"
Expand Down Expand Up @@ -76,6 +77,9 @@ func (h *loginHandler) login(actionName string, action *types.Action, request *t
if httperror.IsAPIError(err) {
return err
}
if err.Error() == clean.StatusLoginDisabled {
return httperror.WrapAPIError(err, httperror.ClusterUnavailable, clean.StatusLoginDisabled)
}
return httperror.WrapAPIError(err, httperror.ServerError, "Server error while authenticating")
}

Expand Down

0 comments on commit b6b6085

Please sign in to comment.