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

Create default passwords when dev mode is set. #441 #442

Merged
merged 43 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
3a974cb
WIP. Create default passwords when dev mode is set. #441
cmoulliard Nov 8, 2024
a9c683f
Set the devMode to BuildCustomization
cmoulliard Nov 8, 2024
6d85851
Pass the proper object containing devMode to the function populating …
cmoulliard Nov 8, 2024
8dedc1e
Adding the code to patch the argocd-secret to use the hashed password…
cmoulliard Nov 8, 2024
704bc78
Use cost 0 as argocd code #441
cmoulliard Nov 8, 2024
ee9dc06
Re-creating too the inital admin secret for argocd. #441
cmoulliard Nov 8, 2024
d392287
Move the current time function. Generate the missing field. #441
cmoulliard Nov 8, 2024
0c62564
Include needed argocd k8s resources and change the code to set the de…
cmoulliard Nov 12, 2024
1e1b980
Fix wrong key as account should be accounts
cmoulliard Nov 12, 2024
3d884da
Change the gitea user from giteaAdmin to developer for a user's devel…
cmoulliard Nov 12, 2024
f31a08e
Reverting to giteaAdmin till we know why a different user - developer…
cmoulliard Nov 12, 2024
c355ba1
Change the number from 58 to 59 as we install a new ConfigMap - RBAC …
cmoulliard Nov 12, 2024
8158551
Remove from the util go file the setupLog and replace it with fmt.Err…
cmoulliard Nov 21, 2024
78e1e40
Removed non used argocd constants
cmoulliard Nov 21, 2024
7d81b27
Use r.client instead of k8s.GetKubeClient()
cmoulliard Nov 21, 2024
a8c3016
Rename the flag from dev to dev-mode
cmoulliard Nov 21, 2024
80a785b
Use an unstructured object to avoid managing fields we do not care ab…
cmoulliard Nov 21, 2024
3c3511d
Include the developer username and password to the command get secret
cmoulliard Nov 22, 2024
bf8e2cc
Refactor the code to update the argocd password post reconciliation. …
cmoulliard Dec 9, 2024
f76db50
Refactor the code to update the gitea password post reconciliation. #441
cmoulliard Dec 10, 2024
86b26a2
Change the logging level from info to debug
cmoulliard Dec 11, 2024
35fc902
Rename devMode, dev-mode to staticPasswords and static-passwords. Fix…
cmoulliard Dec 11, 2024
e4c78de
Remove else block not needed when we verify if the password changed
cmoulliard Dec 11, 2024
700a841
Improve the code and remove the non needed return statement
cmoulliard Dec 11, 2024
5830b22
Improve code and variables
cmoulliard Dec 11, 2024
d1ed81a
Remove non used functions
cmoulliard Dec 11, 2024
f0754f7
Created a new util package for gitea and moved there: const and funct…
cmoulliard Dec 11, 2024
3ab4fd3
Move GiteaBaseUrl to the util package of gitea
cmoulliard Dec 11, 2024
c8ac946
Fix error r.r.GiteaBaseUrl => util.r.GiteaBaseUrl
cmoulliard Dec 11, 2024
e02a5d2
Refactor the code to also patch the secret's password of gitea and ar…
cmoulliard Dec 11, 2024
f67a91c
Regenerate the token for gitea
cmoulliard Dec 11, 2024
dbf56c2
Fix wrong call to the function: GetGiteaToken
cmoulliard Dec 11, 2024
d029f62
Reverting the change to use as user arg: dev-mode instead of staticPa…
cmoulliard Dec 12, 2024
570ca30
Define an util const: StaticPassword
cmoulliard Dec 13, 2024
2fe31a3
Move the function ArgocdInitialAdminSecretObject() to the argocd.go f…
cmoulliard Dec 13, 2024
e4831cd
Rename functions. Remove the var about status and change the signatur…
cmoulliard Dec 13, 2024
1cd1773
Remove devMode as not used
cmoulliard Dec 13, 2024
70e3d37
Rename dev-mode to dev-password
cmoulliard Dec 13, 2024
45c7a90
Merge branch 'main' into dev-mode
cmoulliard Jan 2, 2025
6c183d1
Import missing package needed: util
cmoulliard Jan 2, 2025
06fd823
Use singular instead of plural
cmoulliard Jan 2, 2025
5a17562
Merge branch 'main' into dev-mode
cmoulliard Jan 6, 2025
bc2aa2e
Fix wrong import indentation
cmoulliard Jan 6, 2025
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
1 change: 1 addition & 0 deletions api/v1alpha1/localbuild_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type BuildCustomizationSpec struct {
Port string `json:"port,omitempty"`
UsePathRouting bool `json:"usePathRouting,omitempty"`
SelfSignedCert string `json:"selfSignedCert,omitempty"`
StaticPassword bool `json:"staticPassword,omitempty"`
}

type LocalbuildSpec struct {
Expand Down
1 change: 1 addition & 0 deletions hack/argo-cd/argocd-cm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ metadata:
name: argocd-cm
data:
application.resourceTrackingMethod: annotation
accounts.developer: apiKey, login
timeout.reconciliation: 60s
resource.exclusions: |
- kinds:
Expand Down
12 changes: 12 additions & 0 deletions hack/argo-cd/argocd-rbac-dev.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: v1
kind: ConfigMap
metadata:
labels:
app.kubernetes.io/name: argocd-rbac-cm
app.kubernetes.io/part-of: argocd
name: argocd-rbac-cm
namespace: argocd
data:
policy.csv: |
p, role:developer, applications, *, *, allow
Copy link
Collaborator

Choose a reason for hiding this comment

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

What's the purpose of having a separate account that has a very similar permissions as admin?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is an account for the developers and it only allows to handle applications

Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you tell me if this is inline with what you are thinking?

  1. Use a random password for the developer and admin account if the dev password flag is unset.
  2. Use a static, known password for the developer and admin account if the dev password flag is set.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Option 2 => Use a known password for the developer and admin account if the dev password flag is set

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ok that sounds good to me. Looks like Gitea static password isn't working for some reason?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok that sounds good to me. Looks like Gitea static password isn't working for some reason?

For gitea when using dev-mode, we should still use as user: giteaAdmin and password = developer

Copy link
Collaborator

Choose a reason for hiding this comment

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

Since you are adding a user called developer to argocd, we may as well add the developer user in Gitea.

g, developer, role:developer
1 change: 1 addition & 0 deletions hack/argo-cd/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://raw.githubusercontent.com/argoproj/argo-cd/v2.10.7/manifests/install.yaml
- argocd-rbac-dev.yaml

patches:
- path: dex-server.yaml
Expand Down
3 changes: 2 additions & 1 deletion pkg/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,5 +284,6 @@ func isBuildCustomizationSpecEqual(s1, s2 v1alpha1.BuildCustomizationSpec) bool
s1.IngressHost == s2.IngressHost &&
s1.Port == s2.Port &&
s1.UsePathRouting == s2.UsePathRouting &&
s1.SelfSignedCert == s2.SelfSignedCert
s1.SelfSignedCert == s2.SelfSignedCert &&
s1.StaticPassword == s2.StaticPassword
}
4 changes: 4 additions & 0 deletions pkg/cmd/create/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
const (
recreateClusterUsage = "Delete cluster first if it already exists."
buildNameUsage = "Name for build (Prefix for kind cluster name, pod names, etc)."
devPasswordUsage = "Set the password \"developer\" for the admin user of the applications: argocd & gitea."
kubeVersionUsage = "Version of the kind kubernetes cluster to create."
extraPortsMappingUsage = "List of extra ports to expose on the docker container and kubernetes cluster as nodePort " +
"(e.g. \"22:32222,9090:39090,etc\")."
Expand All @@ -40,6 +41,7 @@ var (
// Flags
recreateCluster bool
buildName string
devPassword bool
kubeVersion string
extraPortsMapping string
kindConfigPath string
Expand Down Expand Up @@ -68,6 +70,7 @@ func init() {
CreateCmd.PersistentFlags().StringVar(&buildName, "build-name", "localdev", buildNameUsage)
CreateCmd.PersistentFlags().MarkDeprecated("build-name", "use --name instead.")
CreateCmd.PersistentFlags().StringVar(&buildName, "name", "localdev", buildNameUsage)
CreateCmd.PersistentFlags().BoolVar(&devPassword, "dev-password", false, devPasswordUsage)
CreateCmd.PersistentFlags().StringVar(&kubeVersion, "kube-version", "v1.30.3", kubeVersionUsage)
CreateCmd.PersistentFlags().StringVar(&extraPortsMapping, "extra-ports", "", extraPortsMappingUsage)
CreateCmd.PersistentFlags().StringVar(&kindConfigPath, "kind-config", "", kindConfigPathUsage)
Expand Down Expand Up @@ -145,6 +148,7 @@ func create(cmd *cobra.Command, args []string) error {
IngressHost: ingressHost,
Port: port,
UsePathRouting: pathRouting,
StaticPassword: devPassword,
},

CustomPackageDirs: absDirPaths,
Expand Down
8 changes: 2 additions & 6 deletions pkg/cmd/get/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/cnoe-io/idpbuilder/pkg/entity"
"github.com/cnoe-io/idpbuilder/pkg/printer"
"github.com/cnoe-io/idpbuilder/pkg/util"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/cnoe-io/idpbuilder/api/v1alpha1"
Expand Down Expand Up @@ -238,13 +239,8 @@ func getSecretsByCNOELabel(ctx context.Context, kubeClient client.Client, l labe
return secrets, kubeClient.List(ctx, &secrets, &opts)
}

func getSecretByName(ctx context.Context, kubeClient client.Client, ns, name string) (v1.Secret, error) {
s := v1.Secret{}
return s, kubeClient.Get(ctx, client.ObjectKey{Name: name, Namespace: ns}, &s)
}

func getCorePackageSecret(ctx context.Context, kubeClient client.Client, ns, name string) (v1.Secret, error) {
s, err := getSecretByName(ctx, kubeClient, ns, name)
s, err := util.GetSecretByName(ctx, kubeClient, ns, name)
if err != nil {
return v1.Secret{}, err
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/controllers/gitrepository/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ func (r *RepositoryReconciler) reconcileGitRepo(ctx context.Context, repo *v1alp
return ctrl.Result{}, fmt.Errorf("getting git provider credentials: %w", err)
}

if r.Config.StaticPassword {
creds.password = util.StaticPassword
}

err = provider.setProviderCredentials(ctx, repo, creds)
if err != nil {
return ctrl.Result{}, fmt.Errorf("setting git provider credentials: %w", err)
Expand Down
1 change: 1 addition & 0 deletions pkg/controllers/gitrepository/gitea.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func (g *giteaProvider) getProviderCredentials(ctx context.Context, repo *v1alph
if !ok {
return gitProviderCredentials{}, fmt.Errorf("%s key not found in secret %s in %s ns", giteaAdminPasswordKey, repo.Spec.SecretRef.Name, repo.Spec.SecretRef.Namespace)
}

return gitProviderCredentials{
username: string(username),
password: string(password),
Expand Down
1 change: 0 additions & 1 deletion pkg/controllers/localbuild/argo.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package localbuild
import (
"context"
"embed"

"github.com/cnoe-io/idpbuilder/api/v1alpha1"
"github.com/cnoe-io/idpbuilder/globals"
"github.com/cnoe-io/idpbuilder/pkg/k8s"
Expand Down
4 changes: 2 additions & 2 deletions pkg/controllers/localbuild/argo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ func TestGetK8sInstallResources(t *testing.T) {
t.Fatalf("GetK8sInstallResources() error: %v", err)
}

if len(objs) != 58 {
t.Fatalf("Expected 58 Argo Install Resources, got: %d", len(objs))
if len(objs) != 59 {
t.Fatalf("Expected 59 Argo Install Resources, got: %d", len(objs))
}
}

Expand Down
212 changes: 212 additions & 0 deletions pkg/controllers/localbuild/controller.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package localbuild

import (
"bytes"
"code.gitea.io/sdk/gitea"
"context"
"encoding/json"
"fmt"
"io"
"k8s.io/apimachinery/pkg/types"
"net/http"
"os"
"path/filepath"
"strings"
Expand Down Expand Up @@ -41,6 +47,10 @@ const (
argoCDApplicationSetAnnotationKeyRefreshTrue = "true"
)

type ArgocdSession struct {
Token string `json:"token"`
}

type LocalbuildReconciler struct {
client.Client
Scheme *runtime.Scheme
Expand Down Expand Up @@ -91,6 +101,48 @@ func (r *LocalbuildReconciler) Reconcile(ctx context.Context, req ctrl.Request)
}
}

if r.Config.StaticPassword {
logger.V(1).Info("static password is enabled")

// Check if the Argocd Initial admin secret exists
argocdInitialAdminPassword, err := r.extractArgocdInitialAdminSecret(ctx)
if err != nil {
// Argocd initial admin secret is not yet available ...
return ctrl.Result{RequeueAfter: defaultRequeueTime}, nil
}

logger.V(1).Info("Initial argocd admin secret found ...")

// Secret containing the initial argocd password exists
// Lets try to update the password
if argocdInitialAdminPassword != "" {
err = r.updateArgocdPassword(ctx, argocdInitialAdminPassword)
if err != nil {
return ctrl.Result{}, err
} else {
logger.V(1).Info(fmt.Sprintf("Argocd admin password change succeeded !"))
}
}

// Check if the Gitea credentials secret exists
giteaAdminPassword, err := r.extractGiteaAdminSecret(ctx)
if err != nil {
// Gitea admin secret is not yet available ...
return ctrl.Result{RequeueAfter: defaultRequeueTime}, nil
}
logger.V(1).Info("Gitea admin secret found ...")
// Secret containing the gitea password exists
// Lets try to update the password
if giteaAdminPassword != "" {
err = r.updateGiteaPassword(ctx, giteaAdminPassword)
if err != nil {
return ctrl.Result{}, err
} else {
logger.V(1).Info(fmt.Sprintf("Gitea admin password change succeeded !"))
}
}
}

logger.V(1).Info("done installing core packages. passing control to argocd")
_, err = r.ReconcileArgoAppsWithGitea(ctx, req, &localBuild)
if err != nil {
Expand Down Expand Up @@ -610,6 +662,166 @@ func (r *LocalbuildReconciler) requestArgoCDAppSetRefresh(ctx context.Context) e
return nil
}

func (r *LocalbuildReconciler) extractArgocdInitialAdminSecret(ctx context.Context) (string, error) {
sec := util.ArgocdInitialAdminSecretObject()
err := r.Client.Get(ctx, types.NamespacedName{
Namespace: sec.GetNamespace(),
Name: sec.GetName(),
}, &sec)

if err != nil {
if k8serrors.IsNotFound(err) {
return "", fmt.Errorf("initial admin secret not found")
}
}
return string(sec.Data["password"]), nil
}

func (r *LocalbuildReconciler) extractGiteaAdminSecret(ctx context.Context) (string, error) {
sec := util.GiteaAdminSecretObject()
err := r.Client.Get(ctx, types.NamespacedName{
Namespace: sec.GetNamespace(),
Name: sec.GetName(),
}, &sec)

if err != nil {
if k8serrors.IsNotFound(err) {
return "", fmt.Errorf("gitea admin secret not found")
}
}
return string(sec.Data["password"]), nil
}

func (r *LocalbuildReconciler) updateGiteaPassword(ctx context.Context, adminPassword string) error {
client, err := gitea.NewClient(util.GiteaBaseUrl(r.Config), gitea.SetHTTPClient(util.GetHttpClient()),
gitea.SetBasicAuth("giteaAdmin", adminPassword), gitea.SetContext(ctx),
)
if err != nil {
return fmt.Errorf("cannot create gitea client: %w", err)
}

opts := gitea.EditUserOption{
LoginName: "giteaAdmin",
Password: util.StaticPassword,
}

resp, err := client.AdminEditUser("giteaAdmin", opts)
if err != nil {
return fmt.Errorf("cannot update gitea admin user. status: %d error : %w", resp.StatusCode, err)
}

err = util.PatchPasswordSecret(ctx, r.Client, r.Config, util.GiteaNamespace, util.GiteaAdminSecret, util.GiteaAdminName, util.StaticPassword)
if err != nil {
return fmt.Errorf("patching the gitea credentials failed : %w", err)
}
return nil
}

func (r *LocalbuildReconciler) updateArgocdPassword(ctx context.Context, adminPassword string) error {
argocdEndpoint := util.ArgocdBaseUrl(r.Config) + "/api/v1"

payload := map[string]string{
"username": "admin",
"password": adminPassword,
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
return fmt.Errorf("Error creating JSON payload: %v\n", err)
}

// Create an HTTP POST request to get the Session token
req, err := http.NewRequest("POST", argocdEndpoint+"/session", bytes.NewBuffer(payloadBytes))
if err != nil {
return fmt.Errorf("Error creating HTTP request: %v\n", err)
}
req.Header.Set("Content-Type", "application/json")

// Create an HTTP c and disable TLS verification
c := util.GetHttpClient()

// Send the request
resp, err := c.Do(req)
if err != nil {
return fmt.Errorf("Error sending request: %v\n", err)
}
defer resp.Body.Close()

// Read the response body
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("Error reading response body: %v\n", err)
}

// We got a session Token, so we can update the Argocd admin password
if resp.StatusCode == 200 {
var argocdSession ArgocdSession

err := json.Unmarshal([]byte(body), &argocdSession)
if err != nil {
return fmt.Errorf("Error unmarshalling JSON: %v", err)
}

payload := map[string]string{
"name": "admin",
"currentPassword": adminPassword,
"newPassword": util.StaticPassword,
}

payloadBytes, err := json.Marshal(payload)
if err != nil {
return fmt.Errorf("Error creating JSON payload: %v\n", err)
}

req, err := http.NewRequest("PUT", argocdEndpoint+"/account/password", bytes.NewBuffer(payloadBytes))
if req != nil {
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", argocdSession.Token))
req.Header.Set("Content-Type", "application/json")
}

resp, err := c.Do(req)
if err != nil {
return fmt.Errorf("Error sending request: %v\n", err)
}
defer resp.Body.Close()

// Lets checking the new admin password
payload = map[string]string{
"username": "admin",
"password": util.StaticPassword,
}
payloadBytes, err = json.Marshal(payload)
if err != nil {
return fmt.Errorf("Error creating JSON payload: %v\n", err)
}

// Define the request able to verify if the username and password changed works
req, err = http.NewRequest("POST", argocdEndpoint+"/session", bytes.NewBuffer(payloadBytes))
if err != nil {
return fmt.Errorf("Error creating HTTP request: %v\n", err)
}
req.Header.Set("Content-Type", "application/json")

// Send the request
resp, err = c.Do(req)
if err != nil {
return fmt.Errorf("Error sending request: %v\n", err)
}
defer resp.Body.Close()

// Password verification succeeded !
if resp.StatusCode == 200 {
// Let's patch the existing secret now
err = util.PatchPasswordSecret(ctx, r.Client, r.Config, util.ArgocdNamespace, util.ArgocdInitialAdminSecretName, util.ArgocdAdminName, util.StaticPassword)
if err != nil {
return fmt.Errorf("patching the argocd initial secret failed : %w", err)
}
return nil
}
}
// No session token has been received and by consequence the admin password has not been changed
return nil
}

func (r *LocalbuildReconciler) applyArgoCDAnnotation(ctx context.Context, obj client.Object, argoCDType, annotationKey, annotationValue string) error {
annotations := obj.GetAnnotations()
if annotations != nil {
Expand Down
Loading
Loading