Skip to content

Commit

Permalink
Add OIDC to bootstrap CLI (#3399)
Browse files Browse the repository at this point in the history
* init gitops bootstrap command for wge

* add checks for entitlement and flux

* Add wge version choise list

* add create admin username and password secret

* add install wge

* fix adding admin password

* fix linting

* run go mod tidy

* enable ingress

* add domain type selector

* add option to bootstrap flux

* adjust bootstraping flux and domain

* add install extra controllers

* fix lint

* rename package checks to commands

* refactor packages

* early exit

* refactor errors and add utils for git repos

* refactor wge installation to use files

* refactor extra controllers

* remove extra unneeded values

* Add OIDC

* update portforward

* add admin password revert

* refactor error handeling

* fix unhandeled errors

* refactor creating helmrepos and helmreleases

* refactor styling

* go mod tidy

* split utils

* Add OIDC

* refactor creating extra controllers

* move install extra controller to commands

* add oidc

* Add OIDC

* install capi controller

* add terraform controller

* refactor OIDC cli

* fix linting

* fix lintting

* fix lint

* fix lint

* update OIDC CLI

* handle error msgs

* move check mark to method

* reformat error

* cleanup

* update CLI OIDC

* fix confirm input

* type

* Add localhost portforward info

* add gitopssets controller

* enable pipelines controller and cluster-controller and gitopssets by default

* remove gitops sets

* handle existing secrets

* Update onboarding CLI messages

* cleanup utils

* cleanup utils

* cleanup domain

* cleanup utils

* cleanup input

* cleanup input

* edit messages and variableNames

* update cli messages

* cleanup variables

* cleanup constants

* cleanup constants

* add unit test for k8s utils and refactor accordingly

* add unit tests for flux

* add unit test for admin password

* Add unit -test for adding capi & policy-agent

* move controllers under gitops add

* add unit test for wge version

* test oidc get issuer

* Move controllers under gitops add

* Move controllers under gitops add

* pause add controllers tests

* restore install controller function after setup

* fix controllers

* pass opts to controllers

* refactor git utilits

* refactor git utilities

* prepare bootstrap bommand for release1

* address messages and languague

* clean extra variables

* remove oidc flow to another branch

* Update cmd/gitops/app/bootstrap/cmd.go

Co-authored-by: Eneko Fernández <[email protected]>

* Update cmd/gitops/app/bootstrap/cmd.go

Co-authored-by: Eneko Fernández <[email protected]>

* refactor git utility & add unit-test

* remove out of scope componenets

* apply code review comments to refactor file names and error messages

* move bootstrap package under pkg

* refactor using the k8s client and add silent mode

refactor git utils

* fix admin password

* verify username and password

* remove unused methods

* Add OIDC configs

* fix spelling

* Add OIDC

* Add OIDC

* merge 3165-onboarding-cli into oidc-flow-update

* remove aws related stuff

* use git library

add check for previous installation

* fix lint

* imporve error messages to stage failures

* update OIDC command

* add tests for getting OIDC configs

* update OIDC prompt

* adjust gitopssets values and installation checks

* fix getting wge version

* clean silent mode

* add checks for kubeconfig

* adjust admin password

* refactor commands to use config interface struct

* improve error messages

* improve OIDC flow

* remove unused variable

* wip adding cli design doc

* wip adding cli design doc

* add error guidance

* rename config to bootstrapper to be more clear

* refactor commands to use steps pattern

* continue on refactor commands to use steps pattern

* update oidc commands

* refactor oidc to use steps pattern

* add OIDC discoveruUrl check attemps

* Add bootstrap oidc external command

* pause oidc_test

* remove admin user revert

* fix ssh authenticate by explictly asking for private key file

in case the key is not loaded in ssh agent. this usually happens on macos

* added integration test, refactored configuration and design (#3458)

* Changes after the review to enhance the following aspects

Testing:

- Added integration test so we could test the functionality e2e: it uses some local configuration that we need to test but already provides the acceptance layer that we were missing:

Design:

- Bootstrap workflow moved to the domain layer within `pkg` so it could be presented in different forms.
- Integrated configuration chain of responsibility into a single a builder pattern, so we have configurability in this layer. As a result:
  - we dont need to pass the flags to the steps
  - we config the stepsbefore the workflow is executed which seems the right moment.

Other refactors:
- Moved steps to package `steps` from `command`  for consistency

* integrated ssh key management

* Add OIDC test

* add events and error messages and fix domain bug

* add current context

* fix lint

* add entitlement expiration message

* fix entitlement expiration

* fix entitlement expiration

* seperate entitlement secret validations

* add messages for flux

* update OIDC with new structure

* Add OIDC steps

* Add oidc flags

* add oidc flags to integration-test

* remove unused messages

* freze oidc_unit test & use integration test

* remove oidc external command

* edit messages to small letter and update success messages

* Add oidc unit-test

* add validation for password and wge version

* add test cases for admin password create creds

* add test cases for domain type

* update entitlement test

* add validation on password input

* remove aws related message

* panic in case of casting error to give more context about the error

* handle portforward and error messages

* fix external dns spacing

* add validation on domain type

* add error check on unit-test

* Add bootstrap Auth

* Add external OIDC command

* Add auth check

* fix merge conflicts

* remove empty struct in create/delete secret

* merge prompt in oidc config

* move bootstrap auth under pkg

* add examples for cli auth

* make use of global flags

* remove unnecessary flags from oidc

* move oidc to pkg layer

* rename step

* remove install extra and merge functionality to utils

* refactor to oidc file

* fix tests

* add unit tests and address review comments

* remove leftover variable

* fix oidc prompt

* add example to start with oidc

* added oidc scenario (#3543)

* added oidc scenario

* can run acceptance on oidc

* fix oidc prompt for existing creds

* fix oidc domaintype

* modify log message

* modify log message

---------

Co-authored-by: Waleed Hammam <[email protected]>
Co-authored-by: Waleed Hammam <[email protected]>
Co-authored-by: Eneko Fernández <[email protected]>
Co-authored-by: Eneko Fernandez <[email protected]>
  • Loading branch information
5 people authored Oct 26, 2023
1 parent 8ef84a4 commit 6912a53
Show file tree
Hide file tree
Showing 12 changed files with 1,056 additions and 37 deletions.
40 changes: 28 additions & 12 deletions cmd/gitops/app/bootstrap/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,28 @@ gitops bootstrap --kubeconfig <your-kubeconfig-location>
# Start WGE installation with given 'username' and 'password'
gitops bootstrap --username wego-admin --password=hell0!
# Start WGE installation using OIDC
gitops bootstrap --client-id <client-id> --client-secret <client-secret> --discovery-url <discovery-url>
`
)

type bootstrapFlags struct {
username string
password string
version string
domainType string
domain string
// wge version flags
version string

// domain flags
domainType string
domain string

// private key flags
privateKeyPath string
privateKeyPassword string

// oidc flags
discoveryURL string
clientID string
clientSecret string
}

var flags bootstrapFlags
Expand All @@ -53,13 +64,17 @@ func Command(opts *config.Options) *cobra.Command {
RunE: getBootstrapCmdRun(opts),
}

cmd.Flags().StringVarP(&flags.username, "username", "u", "", "dashboard admin username")
cmd.Flags().StringVarP(&flags.password, "password", "p", "", "dashboard admin password")
cmd.Flags().StringVarP(&flags.version, "version", "v", "", "version of Weave GitOps Enterprise (should be from the latest 3 versions)")
cmd.Flags().StringVarP(&flags.domainType, "domain-type", "t", "", "dashboard domain type: could be 'localhost' or 'externaldns'")
cmd.Flags().StringVarP(&flags.domain, "domain", "d", "", "indicate the domain to use in case of using `externaldns`")
cmd.Flags().StringVarP(&flags.privateKeyPath, "private-key", "k", "", "private key path. This key will be used to push the Weave GitOps Enterprise's resources to the default cluster repository")
cmd.Flags().StringVarP(&flags.privateKeyPassword, "private-key-password", "c", "", "private key password. If the private key is encrypted using password")
cmd.Flags().StringVarP(&flags.version, "version", "v", "", "version of Weave GitOps Enterprise (should be from the latest 3 versions)")
cmd.PersistentFlags().StringVarP(&flags.privateKeyPath, "private-key", "k", "", "private key path. This key will be used to push the Weave GitOps Enterprise's resources to the default cluster repository")
cmd.PersistentFlags().StringVarP(&flags.privateKeyPassword, "private-key-password", "c", "", "private key password. If the private key is encrypted using password")
cmd.PersistentFlags().StringVarP(&flags.discoveryURL, "discovery-url", "", "", "OIDC discovery URL")
cmd.PersistentFlags().StringVarP(&flags.clientID, "client-id", "i", "", "OIDC client ID")
cmd.PersistentFlags().StringVarP(&flags.clientSecret, "client-secret", "s", "", "OIDC client secret")

cmd.AddCommand(AuthCommand(opts))

return cmd
}

Expand All @@ -72,12 +87,13 @@ func getBootstrapCmdRun(opts *config.Options) func(*cobra.Command, []string) err
c, err := steps.NewConfigBuilder().
WithLogWriter(cliLogger).
WithKubeconfig(opts.Kubeconfig).
WithUsername(flags.username).
WithPassword(flags.password).
WithUsername(opts.Username).
WithPassword(opts.Password).
WithVersion(flags.version).
WithDomainType(flags.domainType).
WithDomain(flags.domain).
WithPrivateKey(flags.privateKeyPath, flags.privateKeyPassword).
WithOIDCConfig(flags.discoveryURL, flags.clientID, flags.clientSecret, true).
Build()

if err != nil {
Expand Down
10 changes: 9 additions & 1 deletion cmd/gitops/app/bootstrap/cmd_acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ func TestBootstrapCmd(t *testing.T) {
privateKeyFlag := fmt.Sprintf("--private-key=%s", privateKeyFile)
kubeconfigFlag := fmt.Sprintf("--kubeconfig=%s", kubeconfigPath)

oidcClientSecret := os.Getenv("OIDC_CLIENT_SECRET")
g.Expect(oidcClientSecret).NotTo(BeEmpty())
oidcClientSecretFlag := fmt.Sprintf("--client-secret=%s", oidcClientSecret)

_ = k8sClient.Create(context.Background(), &fluxSystemNamespace)

tests := []struct {
Expand All @@ -87,13 +91,16 @@ func TestBootstrapCmd(t *testing.T) {
reset func(t *testing.T)
}{
{
name: "should install with ssh repo",
name: "should bootstrap non-interactive with valid arguments",
flags: []string{kubeconfigFlag,
"--version=0.33.0",
privateKeyFlag, "--private-key-password=\"\"",
"--username=admin",
"--password=admin123",
"--domain-type=localhost",
"--discovery-url=https://dex-01.wge.dev.weave.works/.well-known/openid-configuration",
"--client-id=weave-gitops-enterprise",
oidcClientSecretFlag,
},
setup: func(t *testing.T) {
bootstrapFluxSsh(g, kubeconfigFlag)
Expand Down Expand Up @@ -122,6 +129,7 @@ func TestBootstrapCmd(t *testing.T) {
bootstrapCmdArgs := []string{"bootstrap"}
bootstrapCmdArgs = append(bootstrapCmdArgs, tt.flags...)
cmd.SetArgs(bootstrapCmdArgs)
fmt.Println("bootstrap args: ", bootstrapCmdArgs)

err := cmd.Execute()
if tt.expectedErrorStr != "" {
Expand Down
78 changes: 78 additions & 0 deletions cmd/gitops/app/bootstrap/cmd_auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package bootstrap

import (
"fmt"
"os"

"github.com/spf13/cobra"
. "github.com/weaveworks/weave-gitops-enterprise/pkg/bootstrap"
"github.com/weaveworks/weave-gitops-enterprise/pkg/bootstrap/steps"
"github.com/weaveworks/weave-gitops/cmd/gitops/config"
"github.com/weaveworks/weave-gitops/pkg/logger"
)

const (
autCmdName = "auth"
autCmdShortDescription = "Generate authentication configuration for Weave GitOps. You can specify the type of authentication using the '--type' flag. Currently, only OIDC is supported."
authCmdExamples = `
# Add OIDC configuration to your cluster.
gitops bootstrap auth --type=oidc
# Add OIDC configuration from a specific kubeconfig
gitops bootstrap auth --type=oidc --kubeconfig <your-kubeconfig-location>
# Add OIDC configuration with given oidc configurations 'discoveryURL' 'client-id' 'client-secret'
gitops bootstrap auth --type=oidc --client-id <client-id> --client-secret <client-secret> --discovery-url <discovery-url>
`
)

type authConfigFlags struct {
authType string
}

var authFlags authConfigFlags

func AuthCommand(opts *config.Options) *cobra.Command {
cmd := &cobra.Command{
Use: autCmdName,
Short: autCmdShortDescription,
Example: authCmdExamples,
Run: func(cmd *cobra.Command, args []string) {
err := getAuthCmdRun(opts)(cmd, args)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
},
}

cmd.Flags().StringVarP(&authFlags.authType, "type", "t", "", "type of authentication to be configured")

return cmd
}

func getAuthCmdRun(opts *config.Options) func(*cobra.Command, []string) error {
return func(cmd *cobra.Command, args []string) error {
cliLogger := logger.NewCLILogger(os.Stdout)

c, err := steps.NewConfigBuilder().
WithLogWriter(cliLogger).
WithKubeconfig(opts.Kubeconfig).
WithPrivateKey(flags.privateKeyPath, flags.privateKeyPassword).
WithOIDCConfig(flags.discoveryURL, flags.clientID, flags.clientSecret, false).
Build()

if err != nil {
return fmt.Errorf("cannot config bootstrap auth: %v", err)

}

err = BootstrapAuth(c)
if err != nil {
return fmt.Errorf("cannot bootstrap auth: %v", err)
}

return nil

}
}
2 changes: 2 additions & 0 deletions pkg/bootstrap/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ func Bootstrap(config steps.Config) error {
steps.NewAskAdminCredsSecretStep(config),
steps.NewSelectDomainType(config),
steps.NewInstallWGEStep(config),
steps.NewInstallOIDCStep(config),
steps.NewOIDCConfigStep(config),
steps.CheckUIDomainStep,
}

Expand Down
42 changes: 42 additions & 0 deletions pkg/bootstrap/bootstrap_auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package bootstrap

import (
"fmt"

"github.com/weaveworks/weave-gitops-enterprise/pkg/bootstrap/steps"
)

// BootstrapAuth initiated by the command runs the WGE bootstrap auth steps
func BootstrapAuth(config steps.Config) error {
// use bootstrapAuth function to bootstrap the authentication
switch config.AuthType {
case steps.AuthOIDC:
err := bootstrapOIDC(config)
if err != nil {
return fmt.Errorf("cannot bootstrap auth: %v", err)
}
default:
return fmt.Errorf("authentication type %s is not supported", config.AuthType)

}
return nil
}

func bootstrapOIDC(config steps.Config) error {
var steps = []steps.BootstrapStep{
steps.VerifyFluxInstallation,
steps.CheckEntitlementSecret,
steps.NewAskPrivateKeyStep(config),
steps.NewInstallOIDCStep(config),
steps.NewOIDCConfigStep(config),
}

for _, step := range steps {
config.Logger.Waitingf(step.Name)
err := step.Execute(&config)
if err != nil {
return err
}
}
return nil
}
82 changes: 63 additions & 19 deletions pkg/bootstrap/steps/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import (
k8s_client "sigs.k8s.io/controller-runtime/pkg/client"
)

// auth types
const (
AuthOIDC = "oidc"
)

const (
defaultAdminUsername = "wego-admin"
defaultAdminPassword = "password"
Expand All @@ -25,6 +30,11 @@ const (
PrivateKeyPassword = "privateKeyPassword"
existingCreds = "existingCreds"
domainType = "domainType"
DiscoveryURL = "discoveryURL"
ClientID = "clientID"
ClientSecret = "clientSecret"
oidcInstalled = "oidcInstalled"
existingOIDC = "existingOIDC"
)

// input/output types
Expand All @@ -39,15 +49,21 @@ const (

// ConfigBuilder contains all the different configuration options that a user can introduce
type ConfigBuilder struct {
logger logger.Logger
kubeconfig string
username string
password string
wGEVersion string
domainType string
domain string
privateKeyPath string
privateKeyPassword string
logger logger.Logger
kubeconfig string
username string
password string
wgeVersion string
domainType string
domain string
privateKeyPath string
privateKeyPassword string
authType string
installOIDC string
discoveryURL string
clientID string
clientSecret string
PromptedForDiscoveryURL bool
}

func NewConfigBuilder() *ConfigBuilder {
Expand Down Expand Up @@ -75,7 +91,7 @@ func (c *ConfigBuilder) WithKubeconfig(kubeconfig string) *ConfigBuilder {
}

func (c *ConfigBuilder) WithVersion(version string) *ConfigBuilder {
c.wGEVersion = version
c.wgeVersion = version
return c
}

Expand All @@ -97,6 +113,19 @@ func (c *ConfigBuilder) WithPrivateKey(privateKeyPath string, privateKeyPassword
return c
}

func (c *ConfigBuilder) WithOIDCConfig(discoveryURL string, clientID string, clientSecret string, prompted bool) *ConfigBuilder {
c.authType = AuthOIDC
c.discoveryURL = discoveryURL
c.clientID = clientID
c.clientSecret = clientSecret
if discoveryURL != "" && clientID != "" && clientSecret != "" {
prompted = false
}
c.PromptedForDiscoveryURL = prompted
c.installOIDC = "y" // todo: change to parameter
return c
}

// Config is the configuration struct to user for WGE installation. It includes
// configuration values as well as other required structs like clients
type Config struct {
Expand All @@ -113,6 +142,15 @@ type Config struct {

PrivateKeyPath string
PrivateKeyPassword string

AuthType string
InstallOIDC string
DiscoveryURL string
IssuerURL string
ClientID string
ClientSecret string
RedirectURL string
PromptedForDiscoveryURL bool
}

// Builds creates a valid config so boostrap could be executed. It uses values introduced
Expand Down Expand Up @@ -140,15 +178,21 @@ func (cb *ConfigBuilder) Build() (Config, error) {

//TODO we should do validations in case invalid values and throw an error early
return Config{
KubernetesClient: kubeHttp.Client,
WGEVersion: cb.wGEVersion,
Username: cb.username,
Password: cb.password,
Logger: cb.logger,
DomainType: cb.domainType,
UserDomain: cb.domain,
PrivateKeyPath: cb.privateKeyPath,
PrivateKeyPassword: cb.privateKeyPassword,
KubernetesClient: kubeHttp.Client,
WGEVersion: cb.wgeVersion,
Username: cb.username,
Password: cb.password,
Logger: cb.logger,
DomainType: cb.domainType,
UserDomain: cb.domain,
PrivateKeyPath: cb.privateKeyPath,
PrivateKeyPassword: cb.privateKeyPassword,
AuthType: cb.authType,
InstallOIDC: cb.installOIDC,
DiscoveryURL: cb.discoveryURL,
ClientID: cb.clientID,
ClientSecret: cb.clientSecret,
PromptedForDiscoveryURL: cb.PromptedForDiscoveryURL,
}, nil

}
Expand Down
Loading

0 comments on commit 6912a53

Please sign in to comment.