Skip to content

Commit

Permalink
Merge pull request #331 from AlexVulaj/banned-users-script
Browse files Browse the repository at this point in the history
Add commands to check if cluster owner is a banned user, and to verify that their pull secret matches the cluster's
  • Loading branch information
openshift-merge-robot authored Feb 15, 2023
2 parents 71172d3 + db3444c commit bf7b850
Show file tree
Hide file tree
Showing 4 changed files with 232 additions and 0 deletions.
72 changes: 72 additions & 0 deletions cmd/cluster/checkbanneduser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package cluster

import (
"fmt"
"github.com/openshift/osdctl/pkg/utils"
"github.com/spf13/cobra"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
)

const BanCodeExportControlCompliance = "export_control_compliance"

func newCmdCheckBannedUser() *cobra.Command {
return &cobra.Command{
Use: "check-banned-user [CLUSTER_ID]",
Short: "Checks if the cluster owner is a banned user.",
Args: cobra.ExactArgs(1),
DisableAutoGenTag: true,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(CheckBannedUser(args[0]))
},
}
}

func CheckBannedUser(clusterID string) error {
ocm := utils.CreateConnection()
defer func() {
if ocmCloseErr := ocm.Close(); ocmCloseErr != nil {
fmt.Printf("Cannot close the ocm (possible memory leak): %q", ocmCloseErr)
}
}()

fmt.Print("Finding subscription account: ")
subscription, err := utils.GetSubscription(ocm, clusterID)
if err != nil {
return err
}

if status := subscription.Status(); status != "Active" {
return fmt.Errorf("Expecting status 'Active' found %v\n", status)
}

fmt.Printf("Account %v - %v - %v\n", subscription.SupportLevel(), subscription.Creator().HREF(), subscription.Status())

fmt.Print("Finding account owner: ")
creator, err := utils.GetAccount(ocm, subscription.Creator().ID())
if err != nil {
return err
}

userEmail := creator.Email()
userBanned := creator.Banned()
userBanCode := creator.BanCode()
userBanDescription := creator.BanDescription()
lastUpdate := creator.UpdatedAt()

fmt.Printf("%v\n-------------------\nLast Update : %v\n", userEmail, lastUpdate)

if userBanned {
fmt.Println("User is banned")
fmt.Printf("Ban code = %v\n", userBanCode)
fmt.Printf("Ban description = %v\n", userBanDescription)
if userBanCode == BanCodeExportControlCompliance {
fmt.Println("User banned due to export control compliance.\nPlease follow the steps detailed here: https://github.com/openshift/ops-sop/blob/master/v4/alerts/UpgradeConfigSyncFailureOver4HrSRE.md#user-banneddisabled-due-to-export-control-compliance .")
return nil
}
fmt.Println("Recommend sending service log with:")
fmt.Printf("osdctl servicelog post -t https://raw.githubusercontent.com/openshift/managed-notifications/master/ocm/cluster_owner_disabled.json %v\n", clusterID)
return nil
}
fmt.Println("User allowed")
return nil
}
2 changes: 2 additions & 0 deletions cmd/cluster/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ func NewCmdCluster(streams genericclioptions.IOStreams, flags *genericclioptions
clusterCmd.AddCommand(access.NewCmdAccess(streams, flags))
clusterCmd.AddCommand(newCmdResizeControlPlaneNode(streams, flags, globalOpts))
clusterCmd.AddCommand(newCmdCpd())
clusterCmd.AddCommand(newCmdCheckBannedUser())
clusterCmd.AddCommand(newCmdValidatePullSecret(client, flags))
return clusterCmd
}

Expand Down
106 changes: 106 additions & 0 deletions cmd/cluster/validatepullsecret.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package cluster

import (
"context"
"fmt"
v1 "github.com/openshift-online/ocm-sdk-go/accountsmgmt/v1"
"github.com/openshift/osdctl/pkg/utils"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/cli-runtime/pkg/genericclioptions"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"sigs.k8s.io/controller-runtime/pkg/client"
)

var BackplaneClusterAdmin = "backplane-cluster-admin"

func newCmdValidatePullSecret(kubeCli client.Client, flags *genericclioptions.ConfigFlags) *cobra.Command {
return &cobra.Command{
Use: "validate-pull-secret [CLUSTER_ID]",
Short: "Checks if the pull secret email matches the owner email",
Long: `Checks if the pull secret email matches the owner email.
The owner's email to check will be determined by the cluster identifier passed to the command, while the pull secret checked will be determined by the cluster that the caller is currently logged in to.`,
Args: cobra.ExactArgs(1),
DisableAutoGenTag: true,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(ValidatePullSecret(args[0], kubeCli, flags))
},
}
}

func ValidatePullSecret(clusterID string, kubeCli client.Client, flags *genericclioptions.ConfigFlags) error {
ocm := utils.CreateConnection()
defer func() {
if ocmCloseErr := ocm.Close(); ocmCloseErr != nil {
fmt.Printf("Cannot close the ocm (possible memory leak): %q", ocmCloseErr)
}
}()

fmt.Println("Checking if pull secret email matches user email")

// This is the flagset for the kubeCli object provided from the root command. Set here to retroactively impersonate backplane-cluster-admin
flags.Impersonate = &BackplaneClusterAdmin
secret := &corev1.Secret{}
err := kubeCli.Get(context.TODO(), types.NamespacedName{Namespace: "openshift-config", Name: "pull-secret"}, secret)
if err != nil {
return err
}

clusterPullSecretEmail, err, done := getPullSecretEmail(clusterID, secret)
if done {
return err
}

subscription, err := utils.GetSubscription(ocm, clusterID)
if err != nil {
return err
}

account, err := utils.GetAccount(ocm, subscription.Creator().ID())
if err != nil {
return err
}

if account.Email() != clusterPullSecretEmail {
fmt.Println("Pull secret email doesn't match OCM user email. Recommend sending service log with:")
fmt.Printf("osdctl servicelog post -t https://raw.githubusercontent.com/openshift/managed-notifications/master/osd/pull_secret_user_mismatch.json %v\n", clusterID)
return nil
}

fmt.Println("Email addresses match.")
return nil
}

func getPullSecretEmail(clusterID string, secret *corev1.Secret) (string, error, bool) {
dockerConfigJsonBytes, found := secret.Data[".dockerconfigjson"]
if !found {
// Indicates issue w/ pull-secret, so we can stop evaluating and specify a more direct course of action
fmt.Println("Secret does not contain expected key '.dockerconfigjson'. Recommend sending a service log with the following command:")
fmt.Printf("osdctl servicelog post -t https://raw.githubusercontent.com/openshift/managed-notifications/master/osd/pull_secret_change_breaking_upgradesync.json %v\n", clusterID)
return "", nil, true
}

dockerConfigJson, err := v1.UnmarshalAccessToken(dockerConfigJsonBytes)
if err != nil {
return "", err, true
}

cloudOpenshiftAuth, found := dockerConfigJson.Auths()["cloud.openshift.com"]
if !found {
fmt.Println("Secret does not contain entry for cloud.openshift.com. Recommend sending a service log with the following command:")
fmt.Printf("osdctl servicelog post -t https://raw.githubusercontent.com/openshift/managed-notifications/master/osd/pull_secret_change_breaking_upgradesync.json %v\n", clusterID)
return "", nil, true
}

clusterPullSecretEmail := cloudOpenshiftAuth.Email()
if clusterPullSecretEmail == "" {
fmt.Printf("%v\n%v\n%v\n",
"Couldn't extract email address from pull secret for cloud.openshift.com",
"This can mean the pull secret is misconfigured. Please verify the pull secret manually:",
" oc get secret -n openshift-config pull-secret -o json | jq -r '.data[\".dockerconfigjson\"]' | base64 -d")
return "", nil, true
}
return clusterPullSecretEmail, nil, false
}
52 changes: 52 additions & 0 deletions cmd/cluster/validatepullsecret_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package cluster

import (
corev1 "k8s.io/api/core/v1"
"reflect"
"testing"
)

func Test_getPullSecretEmail(t *testing.T) {
tests := []struct {
name string
secret *corev1.Secret
expectedEmail string
expectedError error
expectedDone bool
}{
{
name: "Missing dockerconfigjson",
secret: &corev1.Secret{Data: map[string][]byte{}},
expectedDone: true,
},
{
name: "Missing cloud.openshift.com auth",
secret: &corev1.Secret{Data: map[string][]byte{".dockerconfigjson": []byte("{\"auths\":{}}")}},
expectedDone: true,
},
{
name: "Missing email",
secret: &corev1.Secret{Data: map[string][]byte{".dockerconfigjson": []byte("{\"auths\":{\"cloud.openshift.com\":{}}}")}},
expectedDone: true,
},
{
name: "Valid pull secret",
secret: &corev1.Secret{Data: map[string][]byte{".dockerconfigjson": []byte("{\"auths\":{\"cloud.openshift.com\":{\"email\":\"[email protected]\"}}}")}},
expectedEmail: "[email protected]",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
email, err, done := getPullSecretEmail("abc123", tt.secret)
if email != tt.expectedEmail {
t.Errorf("getPullSecretEmail() email = %v, expectedEmail %v", email, tt.expectedEmail)
}
if !reflect.DeepEqual(err, tt.expectedError) {
t.Errorf("getPullSecretEmail() err = %v, expectedEmail %v", err, tt.expectedError)
}
if done != tt.expectedDone {
t.Errorf("getPullSecretEmail() done = %v, expectedEmail %v", done, tt.expectedDone)
}
})
}
}

0 comments on commit bf7b850

Please sign in to comment.