diff --git a/go.mod b/go.mod index 8e20251..32a8df7 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.21 require ( github.com/databus23/goslo.policy v0.0.0-20210929125152-81bf2876dbdb + github.com/databus23/keystone v0.0.0-20180111110916-350fd0e663cd github.com/gin-gonic/gin v1.9.1 github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.18.2 @@ -13,7 +14,6 @@ require ( k8s.io/apiextensions-apiserver v0.29.0 k8s.io/apimachinery v0.29.0 k8s.io/client-go v0.29.0 - k8s.io/klog/v2 v2.110.1 opendev.org/airship/armada-operator v0.0.0-20240307081059-16b2953b2143 sigs.k8s.io/controller-runtime v0.17.2 sigs.k8s.io/yaml v1.4.0 @@ -82,6 +82,7 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + k8s.io/klog/v2 v2.110.1 // indirect k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect diff --git a/go.sum b/go.sum index e416dcb..aff97f4 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/databus23/goslo.policy v0.0.0-20210929125152-81bf2876dbdb h1:8JB2G8t3o1iCL8vCzssUj2Nn2qjqSab2/G3xXhvkpPQ= github.com/databus23/goslo.policy v0.0.0-20210929125152-81bf2876dbdb/go.mod h1:tRj172JgwQmUmEqZZJBWzYWFStitMFTtb95NtUnmpkw= +github.com/databus23/keystone v0.0.0-20180111110916-350fd0e663cd h1:OptdAs3t90tBs6w+lAJVVhBQj3/gqHh1tAQQBL5r08M= +github.com/databus23/keystone v0.0.0-20180111110916-350fd0e663cd/go.mod h1:TtJx0X0i4vIrVWmEEDScoV1pI2IRk0xnLSOdkBOSNgQ= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= diff --git a/pkg/apply/apply.go b/pkg/apply/apply.go index 506f37e..044d4c8 100644 --- a/pkg/apply/apply.go +++ b/pkg/apply/apply.go @@ -18,14 +18,15 @@ import ( "bufio" "context" "errors" - "flag" "fmt" "io" "net/http" "net/url" "opendev.org/airship/armada-go/pkg/auth" + "opendev.org/airship/armada-go/pkg/log" "os" "regexp" + "sigs.k8s.io/controller-runtime/pkg/log/zap" "strings" "time" @@ -45,7 +46,6 @@ import ( "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" - "k8s.io/klog/v2" "sigs.k8s.io/yaml" "opendev.org/airship/armada-go/pkg/config" @@ -102,12 +102,7 @@ type AirshipChart struct { // RunE runs the phase func (c *RunCommand) RunE() error { - klog.InitFlags(nil) - klog.SetOutput(c.Out) - if err := flag.Set("v", "5"); err != nil { - return err - } - klog.V(2).Infof("armada-go apply, manifests path %s", c.Manifests) + log.Printf("armada-go apply, manifests path %s", c.Manifests) if err := c.ParseManifests(); err != nil { return err @@ -115,7 +110,7 @@ func (c *RunCommand) RunE() error { k8sConfig, err := rest.InClusterConfig() if err != nil { - klog.V(2).Infoln("Unable to load in-cluster kubeconfig, reason: ", err) + log.Printf("Unable to load in-cluster kubeconfig, reason: ", err) k8sConfig, err = clientcmd.NewNonInteractiveDeferredLoadingClientConfig( clientcmd.NewDefaultClientConfigLoadingRules(), &clientcmd.ConfigOverrides{}).ClientConfig() if err != nil { @@ -140,11 +135,11 @@ func (c *RunCommand) RunE() error { for _, cgName := range c.airManifest.ChartGroups { cg := c.airGroups[cgName] - klog.V(5).Infof("processing chart group %s, sequenced %s", cgName, cg.Sequenced) + log.Printf("processing chart group %s, sequenced %s", cgName, cg.Sequenced) if !cg.Sequenced { eg := errgroup.Group{} for _, cName := range cg.ChartGroup { - klog.V(5).Infof("adding 1 chart to wg %s", cName) + log.Printf("adding 1 chart to wg %s", cName) chp := c.airCharts[cName] chpc := c.ConvertChart(chp) eg.Go(func() error { @@ -156,7 +151,7 @@ func (c *RunCommand) RunE() error { } } else { for _, cName := range cg.ChartGroup { - klog.V(5).Infof("sequential chart install %s", cName) + log.Printf("sequential chart install %s", cName) if err = c.InstallChart(c.ConvertChart(c.airCharts[cName]), resClient, k8sConfig); err != nil { return err } @@ -171,7 +166,7 @@ func (c *RunCommand) InstallChart( resClient dynamic.NamespaceableResourceInterface, restConfig *rest.Config) error { - klog.V(5).Infof("installing chart %s %s %s", chart.GetName(), chart.Name, chart.Namespace) + log.Printf("installing chart %s %s %s", chart.GetName(), chart.Name, chart.Namespace) obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(chart) if err != nil { return err @@ -179,26 +174,26 @@ func (c *RunCommand) InstallChart( if oldObj, err := resClient.Namespace(chart.Namespace).Get( context.Background(), chart.GetName(), metav1.GetOptions{}); err != nil { - klog.V(5).Infof("unable to get chart %s: %s, creating", chart.Name, err.Error()) + log.Printf("unable to get chart %s: %s, creating", chart.Name, err.Error()) if _, err = resClient.Namespace(chart.Namespace).Create( context.Background(), &unstructured.Unstructured{Object: obj}, metav1.CreateOptions{}); err != nil { return err } - klog.V(5).Infof("chart has been successfully created %s", chart.Name) + log.Printf("chart has been successfully created %s", chart.Name) } else { uObj := &unstructured.Unstructured{Object: obj} uObj.SetResourceVersion(oldObj.GetResourceVersion()) - klog.V(5).Infof("chart %s was found, updating", chart.Name) + log.Printf("chart %s was found, updating", chart.Name) if _, err = resClient.Namespace(chart.Namespace).Update( context.Background(), uObj, metav1.UpdateOptions{}); err != nil { - klog.V(5).Infof("resource update error: %s", err.Error()) + log.Printf("resource update error: %s", err.Error()) if strings.Contains(err.Error(), "the object has been modified") { - klog.V(5).Infof("resource expired, retrying %s", err.Error()) + log.Printf("resource expired, retrying %s", err.Error()) return c.InstallChart(chart, resClient, restConfig) } return err } - klog.V(5).Infof("chart has been successfully updated %s", chart.Name) + log.Printf("chart has been successfully updated %s", chart.Name) } wOpts := armadawait.WaitOptions{ @@ -208,11 +203,11 @@ func (c *RunCommand) InstallChart( fmt.Sprintf("%s-%s", c.airManifest.ReleasePrefix, chart.Spec.Release)), ResourceType: "armadacharts", Timeout: time.Second * time.Duration(chart.Spec.Wait.Timeout), - Logger: klog.FromContext(context.Background()), + Logger: zap.New(zap.WriteTo(c.Out), zap.ConsoleEncoder()), } err = wOpts.Wait(context.Background()) - klog.V(5).Infof("finished with chart %s", chart.GetName()) + log.Printf("finished with chart %s", chart.GetName()) return err } @@ -237,14 +232,14 @@ func (c *RunCommand) CheckCRD(restConfig *rest.Config) error { crdClient := apiextension.NewForConfigOrDie(restConfig) if _, err := crdClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.Background(), "armadacharts.armada.airshipit.org", metav1.GetOptions{}); err != nil { if apierrors.IsNotFound(err) { - klog.V(5).Infof("armadacharts CRD not found, creating: %s", err.Error()) + log.Printf("armadacharts CRD not found, creating: %s", err.Error()) objToapp, err := c.ReadCRD() if err != nil { return err } _, err = crdClient.ApiextensionsV1().CustomResourceDefinitions().Create(context.Background(), objToapp, metav1.CreateOptions{}) if err != nil { - klog.V(5).Infof("error while creating crd %t", err) + log.Printf("error while creating crd %t", err) return err } } else { @@ -287,10 +282,10 @@ func (c *RunCommand) VerifyNamespaces(rsc *rest.Config) error { } } for k, _ := range namespaces { - klog.V(5).Infof("processing namespace %s", k) + log.Printf("processing namespace %s", k) if _, err := cs.CoreV1().Namespaces().Get(context.Background(), k, metav1.GetOptions{}); err != nil { if apierrors.IsNotFound(err) { - klog.V(5).Infof("namespace %s not found, creating", k) + log.Printf("namespace %s not found, creating", k) if _, err = cs.CoreV1().Namespaces().Create(context.Background(), &v1.Namespace{ ObjectMeta: metav1.ObjectMeta{Name: k}}, metav1.CreateOptions{}); err != nil { return err @@ -300,7 +295,7 @@ func (c *RunCommand) VerifyNamespaces(rsc *rest.Config) error { } } } - klog.V(5).Infof("all namespaces validated successfully") + log.Printf("all namespaces validated successfully") return nil } @@ -324,12 +319,12 @@ func (c *RunCommand) ValidateManifests() error { return errors.New(fmt.Sprintf("no group document with name %s found", cgname)) } } - klog.V(5).Infof("all airship manifests validated successfully") + log.Printf("all airship manifests validated successfully") return nil } func (c *RunCommand) ParseManifests() error { - klog.V(5).Infof("parsing manifests started, path: %s", c.Manifests) + log.Printf("parsing manifests started, path: %s", c.Manifests) var f io.ReadCloser u, err := url.Parse(c.Manifests) @@ -378,7 +373,7 @@ func (c *RunCommand) ParseManifests() error { } var typeMeta AirshipDocument if err := yaml.Unmarshal(buf, &typeMeta); err != nil { - klog.V(2).Infof("unmarshalling error %s, continuing...", err.Error()) + log.Printf("unmarshalling error %s, continuing...", err.Error()) continue } @@ -389,7 +384,7 @@ func (c *RunCommand) ParseManifests() error { if err := yaml.Unmarshal(buf, &airManifest); err != nil { return err } - klog.V(2).Infof("found airship manifest %s", airManifest.Metadata.Name) + log.Printf("found airship manifest %s", airManifest.Metadata.Name) c.airManifest = &airManifest } } diff --git a/pkg/server/server.go b/pkg/server/server.go index 63446a8..25845e4 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -17,12 +17,12 @@ package server import ( "fmt" policy "github.com/databus23/goslo.policy" + "github.com/databus23/keystone" "github.com/gin-gonic/gin" "github.com/spf13/viper" "gopkg.in/yaml.v3" "net/http" "opendev.org/airship/armada-go/pkg/apply" - auth2 "opendev.org/airship/armada-go/pkg/auth" "opendev.org/airship/armada-go/pkg/config" "opendev.org/airship/armada-go/pkg/log" "os" @@ -39,16 +39,30 @@ type JsonDataRequest struct { Overrides []any `json:"overrides"` } -func PolicyEnforcer(enforcer *policy.Enforcer, rule string) gin.HandlerFunc { - return func(context *gin.Context) { - ctx := policy.Context{ - Roles: strings.Split(context.GetHeader("X-Roles"), ","), - Logger: log.Printf, - } - if enforcer.Enforce(rule, ctx) { - context.Next() +func Enforcer(enforcer *policy.Enforcer, rule string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("X-Identity-Status") != "Confirmed" { + w.WriteHeader(401) + _, _ = fmt.Fprint(w, "Invalid or no token provided") } else { - context.String(401, "oslo policy error") + ctx := policy.Context{ + Roles: strings.Split(r.Header.Get("X-Roles"), ","), + Logger: log.Printf, + } + if !enforcer.Enforce(rule, ctx) { + w.WriteHeader(401) + _, _ = fmt.Fprint(w, "Oslo policy error") + } + log.Printf("Request from authenticated user %s with roles %s", r.Header.Get("X-User-Name"), r.Header.Get("X-Roles")) + } + } +} + +func Authenticator(h http.Handler) gin.HandlerFunc { + return func(c *gin.Context) { + h.ServeHTTP(c.Writer, c.Request) + if c.Writer.Status() == 401 { + c.Abort() } } } @@ -127,8 +141,10 @@ func (c *RunCommand) RunE() error { } log.Printf("armada-go server has been started") - r := gin.Default() - auth := auth2.New(viper.Sub("keystone_authtoken").GetString("auth_url")) + r := gin.New() + r.Use(gin.Recovery()) + + auth := keystone.New(viper.Sub("keystone_authtoken").GetString("auth_url")) buf, err := os.ReadFile("/etc/armada/policy.yaml") if err != nil { @@ -146,9 +162,9 @@ func (c *RunCommand) RunE() error { return err } - r.POST("/api/v1.0/apply", gin.Logger(), auth.Handler(r.Handler()), PolicyEnforcer(enf, "armada:create_endpoints"), Apply) - r.POST("/api/v1.0/validatedesign", gin.Logger(), auth.Handler(r.Handler()), PolicyEnforcer(enf, "armada:validate_manifest"), Validate) - r.GET("/api/v1.0/releases", gin.Logger(), auth.Handler(r.Handler()), PolicyEnforcer(enf, "armada:get_release"), Releases) + r.POST("/api/v1.0/apply", gin.Logger(), Authenticator(auth.Handler(Enforcer(enf, "armada:create_endpoints"))), Apply) + r.POST("/api/v1.0/validatedesign", gin.Logger(), Authenticator(auth.Handler(Enforcer(enf, "armada:validate_manifest"))), Validate) + r.GET("/api/v1.0/releases", gin.Logger(), Authenticator(auth.Handler(Enforcer(enf, "armada:get_release"))), Releases) r.GET("/api/v1.0/health", Health) return r.Run(":8000") }