You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@dubbo.apache.org by "mfordjody (via GitHub)" <gi...@apache.org> on 2025/07/30 07:10:01 UTC
[PR] [ingress] Customize the default ingress controller and httputils reve… [dubbo-go-pixiu]
mfordjody opened a new pull request, #711:
URL: https://github.com/apache/dubbo-go-pixiu/pull/711
<!-- Thanks for sending a pull request!
-->
**What this PR does**:
Customized default kubernetes ingress controller and httputils reverse proxy.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org
Re: [PR] [ingress] Customize the default ingress controller and httputils reve… [dubbo-go-pixiu]
Posted by "Copilot (via GitHub)" <gi...@apache.org>.
Copilot commented on code in PR #711:
URL: https://github.com/apache/dubbo-go-pixiu/pull/711#discussion_r2242394265
##########
controller/pkg/proxy/proxy.go:
##########
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package proxy
+
+import (
+ "context"
+ "crypto/tls"
+ "fmt"
+ "github.com/apache/dubbo-go-pixiu/controller/pkg/server"
+ "github.com/demdxx/gocast"
+ "github.com/rs/zerolog/log"
+ "io"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/client-go/kubernetes"
+ llog "log"
+ "net/http"
+ "strings"
+)
+
+func Render(client *kubernetes.Clientset) {
+ // Retrieve all Ingress resources across all namespaces
+ ingressList, err := client.NetworkingV1().Ingresses("").List(context.TODO(), metav1.ListOptions{})
+ if err != nil {
+ log.Error().Err(err).Msg("Failed to list ingresses")
+ return
+ }
+
+ // Iterate through each Ingress resource
+ serverMap := make(map[string]*server.Server)
+ for _, ing := range ingressList.Items {
+ for _, rule := range ing.Spec.Rules {
+ for _, path := range rule.HTTP.Paths {
+ host := rule.Host + path.Path
+ if _, ok := serverMap[path.Path]; !ok {
+ var srv *server.Server
+ srv = &server.Server{
+ Name: path.Backend.Service.Name,
+ Port: gocast.ToString(path.Backend.Service.Port.Number),
+ Host: strings.ReplaceAll(host, "*", ".*"),
+ Client: client,
+ }
+ http.Handle(path.Path, srv)
+ serverMap[path.Path] = srv
+ } else {
+ srv := serverMap[path.Path]
+ srv.Name = path.Backend.Service.Name
+ srv.Port = gocast.ToString(path.Backend.Service.Port.Number)
+ srv.Host = strings.ReplaceAll(host, "*", ".*")
+ }
+ }
+ }
+
+ var tlsCertMap = make(map[string]tls.Certificate)
+ for _, sr := range ing.Spec.TLS {
+ if sr.SecretName != "" {
+ secrets, err := client.CoreV1().Secrets(ing.Namespace).Get(context.TODO(), sr.SecretName, metav1.GetOptions{})
+ if err != nil {
+ log.Error().Err(err).Str("namespace", ing.Namespace).Str("name", sr.SecretName).Msg("failed to get TLS secret")
+ continue
+ }
+ cert, err := tls.X509KeyPair(secrets.Data["tls.crt"], secrets.Data["tls.key"])
+ if err != nil {
+ log.Error().Err(err).Str("namespace", ing.Namespace).Str("name", sr.SecretName).Msg("invalid tls certificate")
+ continue
+ }
+ tlsCertMap[sr.SecretName] = cert
+ }
+ }
+ }
+}
+
+func Run(client *kubernetes.Clientset, ingressClass string) error {
+ // Save the mapping of hostname -> TLS cert (for SNI)
+ tlsCertMap := map[string]tls.Certificate{}
+
+ // Retrieve all Ingress resources across all namespaces
+ ingresses, err := client.NetworkingV1().Ingresses("").List(context.Background(), metav1.ListOptions{})
+ if err != nil {
+ return fmt.Errorf("failed to list ingresses: %w", err)
+ }
+
+ // Iterate through each Ingress resource
+ for _, ingress := range ingresses.Items {
+ for _, secrets := range ingress.Spec.TLS {
+ secretName := secrets.SecretName
+ if secretName == "" {
+ continue
+ }
+ secret, err := client.CoreV1().Secrets(ingress.Namespace).Get(context.TODO(), secretName, metav1.GetOptions{})
+ if err != nil {
+ log.Error().Err(err).Str("namespace", ingress.Namespace).Str("secret", secretName).Msg("Failed to load TLS secret")
+ continue
+ }
+ cert, err := tls.X509KeyPair(secret.Data["tls.crt"], secret.Data["tls.key"])
+ if err != nil {
+ log.Error().Err(err).Str("namespace", ingress.Namespace).Str("secret", secretName).Msg("Invalid TLS cert")
+ continue
+ }
+ for _, host := range secrets.Hosts {
+ tlsCertMap[host] = cert
+ log.Info().Str("host", host).Msg("Loaded certificate for host")
+ }
+ }
+
+ // Check IngressClass
+ if ingress.Spec.IngressClassName != nil && *ingress.Spec.IngressClassName == ingressClass {
+ log.Info().Str("ingress", ingress.Name).Msg("Found matching ingress class, starting proxy")
+ } else if ingress.Spec.IngressClassName != nil && *ingress.Spec.IngressClassName != ingressClass {
+ continue
+ }
+ }
+
+ // Determine whether there is a certificate
+ hasTLS := len(tlsCertMap) > 0
+
+ // HTTP Server
+ httpServer := &http.Server{
+ Addr: ":80",
+ }
+
+ // HTTP automatically redirects to HTTPS if TLS is available
+ if hasTLS {
+ httpServer.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ target := "https://" + r.Host + r.URL.RequestURI()
+ http.Redirect(w, r, target, http.StatusMovedPermanently)
+ })
+ } else {
+ httpServer.Handler = nil // 使用默认 handler
Review Comment:
The comment contains Chinese characters. Comments should be in English for consistency with the rest of the codebase.
```suggestion
httpServer.Handler = nil // Use the default handler
```
##########
controller/pkg/proxy/proxy.go:
##########
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package proxy
+
+import (
+ "context"
+ "crypto/tls"
+ "fmt"
+ "github.com/apache/dubbo-go-pixiu/controller/pkg/server"
+ "github.com/demdxx/gocast"
+ "github.com/rs/zerolog/log"
+ "io"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/client-go/kubernetes"
+ llog "log"
+ "net/http"
+ "strings"
+)
+
+func Render(client *kubernetes.Clientset) {
+ // Retrieve all Ingress resources across all namespaces
+ ingressList, err := client.NetworkingV1().Ingresses("").List(context.TODO(), metav1.ListOptions{})
+ if err != nil {
+ log.Error().Err(err).Msg("Failed to list ingresses")
+ return
+ }
+
+ // Iterate through each Ingress resource
+ serverMap := make(map[string]*server.Server)
+ for _, ing := range ingressList.Items {
+ for _, rule := range ing.Spec.Rules {
+ for _, path := range rule.HTTP.Paths {
+ host := rule.Host + path.Path
+ if _, ok := serverMap[path.Path]; !ok {
+ var srv *server.Server
+ srv = &server.Server{
+ Name: path.Backend.Service.Name,
+ Port: gocast.ToString(path.Backend.Service.Port.Number),
+ Host: strings.ReplaceAll(host, "*", ".*"),
+ Client: client,
+ }
+ http.Handle(path.Path, srv)
+ serverMap[path.Path] = srv
+ } else {
+ srv := serverMap[path.Path]
+ srv.Name = path.Backend.Service.Name
+ srv.Port = gocast.ToString(path.Backend.Service.Port.Number)
+ srv.Host = strings.ReplaceAll(host, "*", ".*")
+ }
+ }
+ }
+
+ var tlsCertMap = make(map[string]tls.Certificate)
+ for _, sr := range ing.Spec.TLS {
+ if sr.SecretName != "" {
+ secrets, err := client.CoreV1().Secrets(ing.Namespace).Get(context.TODO(), sr.SecretName, metav1.GetOptions{})
+ if err != nil {
+ log.Error().Err(err).Str("namespace", ing.Namespace).Str("name", sr.SecretName).Msg("failed to get TLS secret")
+ continue
+ }
+ cert, err := tls.X509KeyPair(secrets.Data["tls.crt"], secrets.Data["tls.key"])
+ if err != nil {
+ log.Error().Err(err).Str("namespace", ing.Namespace).Str("name", sr.SecretName).Msg("invalid tls certificate")
+ continue
+ }
+ tlsCertMap[sr.SecretName] = cert
+ }
+ }
+ }
+}
+
+func Run(client *kubernetes.Clientset, ingressClass string) error {
+ // Save the mapping of hostname -> TLS cert (for SNI)
+ tlsCertMap := map[string]tls.Certificate{}
+
+ // Retrieve all Ingress resources across all namespaces
+ ingresses, err := client.NetworkingV1().Ingresses("").List(context.Background(), metav1.ListOptions{})
+ if err != nil {
+ return fmt.Errorf("failed to list ingresses: %w", err)
+ }
+
+ // Iterate through each Ingress resource
+ for _, ingress := range ingresses.Items {
+ for _, secrets := range ingress.Spec.TLS {
+ secretName := secrets.SecretName
+ if secretName == "" {
+ continue
+ }
+ secret, err := client.CoreV1().Secrets(ingress.Namespace).Get(context.TODO(), secretName, metav1.GetOptions{})
+ if err != nil {
+ log.Error().Err(err).Str("namespace", ingress.Namespace).Str("secret", secretName).Msg("Failed to load TLS secret")
+ continue
+ }
+ cert, err := tls.X509KeyPair(secret.Data["tls.crt"], secret.Data["tls.key"])
+ if err != nil {
+ log.Error().Err(err).Str("namespace", ingress.Namespace).Str("secret", secretName).Msg("Invalid TLS cert")
+ continue
+ }
+ for _, host := range secrets.Hosts {
+ tlsCertMap[host] = cert
+ log.Info().Str("host", host).Msg("Loaded certificate for host")
+ }
+ }
+
+ // Check IngressClass
+ if ingress.Spec.IngressClassName != nil && *ingress.Spec.IngressClassName == ingressClass {
+ log.Info().Str("ingress", ingress.Name).Msg("Found matching ingress class, starting proxy")
+ } else if ingress.Spec.IngressClassName != nil && *ingress.Spec.IngressClassName != ingressClass {
+ continue
+ }
+ }
+
+ // Determine whether there is a certificate
+ hasTLS := len(tlsCertMap) > 0
+
+ // HTTP Server
+ httpServer := &http.Server{
+ Addr: ":80",
+ }
+
+ // HTTP automatically redirects to HTTPS if TLS is available
+ if hasTLS {
+ httpServer.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ target := "https://" + r.Host + r.URL.RequestURI()
+ http.Redirect(w, r, target, http.StatusMovedPermanently)
+ })
+ } else {
+ httpServer.Handler = nil // 使用默认 handler
+ }
+
+ // HTTPS Server with SNI
+ httpsServer := &http.Server{
+ Addr: ":443",
+ Handler: nil,
+ ErrorLog: llog.New(io.Discard, "", 0), // Shield error log
Review Comment:
[nitpick] The comment 'Shield error log' should be 'Suppress error log' or 'Disable error log' for better clarity.
```suggestion
ErrorLog: llog.New(io.Discard, "", 0), // Suppress error log
```
##########
controller/pkg/controller/controller.go:
##########
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package controller
+
+import (
+ "context"
+ "fmt"
+ "github.com/apache/dubbo-go-pixiu/controller/pkg/proxy"
+ "github.com/apache/dubbo-go-pixiu/controller/pkg/utils"
+ "github.com/rs/zerolog/log"
+ networkingV1 "k8s.io/api/networking/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/fields"
+ "k8s.io/apimachinery/pkg/util/runtime"
+ "k8s.io/apimachinery/pkg/util/wait"
+ "k8s.io/client-go/kubernetes"
+ "k8s.io/client-go/tools/cache"
+ "k8s.io/client-go/util/workqueue"
+ "k8s.io/klog/v2"
+ "time"
+)
+
+type IngressController struct {
+ clientSet *kubernetes.Clientset
+ queue workqueue.RateLimitingInterface
+ indexer cache.Indexer
+ informer cache.Controller
+ ingressClass string
+}
+
+func NewIngressController(client *kubernetes.Clientset, ingressClass string) *IngressController {
+ ListWatcher := cache.NewListWatchFromClient(client.NetworkingV1().RESTClient(), "ingresses", "", fields.Everything())
+ queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
+ indexer, informer := cache.NewIndexerInformer(ListWatcher, &networkingV1.Ingress{}, 0, cache.ResourceEventHandlerFuncs{
+ AddFunc: func(obj interface{}) {
+ key, err := cache.MetaNamespaceKeyFunc(obj)
+ if err == nil {
+ log.Info().Str("object", key).Msg("watch.add.Ingress")
+ queue.Add(&Event{Type: Add, Obj: key})
+ }
+ },
+ UpdateFunc: func(oldObj, newObj interface{}) {
+ key, err := cache.MetaNamespaceKeyFunc(newObj)
+ if err == nil {
+ log.Info().Str("object", key).Msg("watch.update.Ingress")
+ queue.Add(&Event{Type: Update, Obj: key})
+ }
+ },
+ DeleteFunc: func(obj interface{}) {
+ key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
+ if err == nil {
+ log.Info().Str("object", key).Msg("watch.delete.Ingress")
+ queue.Add(&Event{Type: Delete, Obj: key, Tombstone: obj})
+ }
+ },
+ }, cache.Indexers{})
+
+ return &IngressController{
+ clientSet: client,
+ queue: queue,
+ indexer: indexer,
+ informer: informer,
+ ingressClass: ingressClass,
Review Comment:
The controller fetches all ingresses across all namespaces on every event. Consider adding namespace filtering or field selectors to reduce API calls and improve performance.
##########
controller/pkg/server/server.go:
##########
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package server
+
+import (
+ "context"
+ "crypto/tls"
+ "fmt"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/client-go/kubernetes"
+ "log"
+ "net/http"
+ "net/http/httputil"
+ "net/url"
+ "strings"
+)
+
+type Server struct {
+ Client *kubernetes.Clientset
+ Host string
+ Name string
+ Port string
+}
+
+func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ host := r.Host
+
+ // Get all Ingress
+ ingressList, err := s.Client.NetworkingV1().Ingresses("").List(context.TODO(), metav1.ListOptions{})
+ if err != nil {
+ http.Error(w, "Failed to list Ingresses", http.StatusInternalServerError)
+ return
+ }
+
+ for _, ingress := range ingressList.Items {
+ for _, rule := range ingress.Spec.Rules {
+ if rule.Host != host {
+ continue
+ }
+
+ for _, path := range rule.HTTP.Paths {
+ if !strings.HasPrefix(r.URL.Path, path.Path) {
+ continue
+ }
+
+ // Get Service
+ svc, err := s.Client.CoreV1().Services(ingress.Namespace).Get(context.TODO(), path.Backend.Service.Name, metav1.GetOptions{})
+ if err != nil {
+ http.Error(w, "Service not found", http.StatusBadGateway)
+ return
+ }
+
+ // Get Endpoints
+ endpoints, err := s.Client.CoreV1().Endpoints(ingress.Namespace).Get(context.TODO(), svc.Name, metav1.GetOptions{})
+ if err != nil || len(endpoints.Subsets) == 0 {
+ http.Error(w, "No backend Pod available", http.StatusBadGateway)
+ return
+ }
+
+ // Find the first available Pod IP + port
+ var targetIP string
+ var targetPort int32
+ var scheme string = "http" // default http
Review Comment:
The variable declaration can be simplified to 'scheme := "http"' following Go conventions.
```suggestion
scheme := "http" // default http
```
##########
controller/pkg/proxy/proxy.go:
##########
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package proxy
+
+import (
+ "context"
+ "crypto/tls"
+ "fmt"
+ "github.com/apache/dubbo-go-pixiu/controller/pkg/server"
+ "github.com/demdxx/gocast"
+ "github.com/rs/zerolog/log"
+ "io"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/client-go/kubernetes"
+ llog "log"
+ "net/http"
+ "strings"
+)
+
+func Render(client *kubernetes.Clientset) {
+ // Retrieve all Ingress resources across all namespaces
+ ingressList, err := client.NetworkingV1().Ingresses("").List(context.TODO(), metav1.ListOptions{})
+ if err != nil {
+ log.Error().Err(err).Msg("Failed to list ingresses")
+ return
+ }
+
+ // Iterate through each Ingress resource
+ serverMap := make(map[string]*server.Server)
+ for _, ing := range ingressList.Items {
+ for _, rule := range ing.Spec.Rules {
+ for _, path := range rule.HTTP.Paths {
+ host := rule.Host + path.Path
+ if _, ok := serverMap[path.Path]; !ok {
+ var srv *server.Server
+ srv = &server.Server{
+ Name: path.Backend.Service.Name,
+ Port: gocast.ToString(path.Backend.Service.Port.Number),
+ Host: strings.ReplaceAll(host, "*", ".*"),
+ Client: client,
+ }
+ http.Handle(path.Path, srv)
+ serverMap[path.Path] = srv
+ } else {
+ srv := serverMap[path.Path]
+ srv.Name = path.Backend.Service.Name
+ srv.Port = gocast.ToString(path.Backend.Service.Port.Number)
+ srv.Host = strings.ReplaceAll(host, "*", ".*")
+ }
+ }
+ }
+
+ var tlsCertMap = make(map[string]tls.Certificate)
+ for _, sr := range ing.Spec.TLS {
+ if sr.SecretName != "" {
+ secrets, err := client.CoreV1().Secrets(ing.Namespace).Get(context.TODO(), sr.SecretName, metav1.GetOptions{})
+ if err != nil {
+ log.Error().Err(err).Str("namespace", ing.Namespace).Str("name", sr.SecretName).Msg("failed to get TLS secret")
+ continue
+ }
+ cert, err := tls.X509KeyPair(secrets.Data["tls.crt"], secrets.Data["tls.key"])
+ if err != nil {
+ log.Error().Err(err).Str("namespace", ing.Namespace).Str("name", sr.SecretName).Msg("invalid tls certificate")
+ continue
+ }
+ tlsCertMap[sr.SecretName] = cert
+ }
+ }
+ }
+}
+
+func Run(client *kubernetes.Clientset, ingressClass string) error {
+ // Save the mapping of hostname -> TLS cert (for SNI)
+ tlsCertMap := map[string]tls.Certificate{}
+
+ // Retrieve all Ingress resources across all namespaces
+ ingresses, err := client.NetworkingV1().Ingresses("").List(context.Background(), metav1.ListOptions{})
+ if err != nil {
+ return fmt.Errorf("failed to list ingresses: %w", err)
+ }
+
+ // Iterate through each Ingress resource
+ for _, ingress := range ingresses.Items {
+ for _, secrets := range ingress.Spec.TLS {
+ secretName := secrets.SecretName
+ if secretName == "" {
+ continue
+ }
+ secret, err := client.CoreV1().Secrets(ingress.Namespace).Get(context.TODO(), secretName, metav1.GetOptions{})
+ if err != nil {
+ log.Error().Err(err).Str("namespace", ingress.Namespace).Str("secret", secretName).Msg("Failed to load TLS secret")
+ continue
+ }
+ cert, err := tls.X509KeyPair(secret.Data["tls.crt"], secret.Data["tls.key"])
+ if err != nil {
+ log.Error().Err(err).Str("namespace", ingress.Namespace).Str("secret", secretName).Msg("Invalid TLS cert")
+ continue
+ }
+ for _, host := range secrets.Hosts {
+ tlsCertMap[host] = cert
+ log.Info().Str("host", host).Msg("Loaded certificate for host")
+ }
+ }
+
+ // Check IngressClass
+ if ingress.Spec.IngressClassName != nil && *ingress.Spec.IngressClassName == ingressClass {
+ log.Info().Str("ingress", ingress.Name).Msg("Found matching ingress class, starting proxy")
+ } else if ingress.Spec.IngressClassName != nil && *ingress.Spec.IngressClassName != ingressClass {
+ continue
+ }
+ }
+
+ // Determine whether there is a certificate
+ hasTLS := len(tlsCertMap) > 0
+
+ // HTTP Server
+ httpServer := &http.Server{
+ Addr: ":80",
+ }
+
+ // HTTP automatically redirects to HTTPS if TLS is available
+ if hasTLS {
+ httpServer.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ target := "https://" + r.Host + r.URL.RequestURI()
+ http.Redirect(w, r, target, http.StatusMovedPermanently)
+ })
+ } else {
+ httpServer.Handler = nil // 使用默认 handler
+ }
+
+ // HTTPS Server with SNI
+ httpsServer := &http.Server{
+ Addr: ":443",
+ Handler: nil,
+ ErrorLog: llog.New(io.Discard, "", 0), // Shield error log
+ TLSConfig: &tls.Config{
+ GetCertificate: func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
+ if cert, ok := tlsCertMap[clientHello.ServerName]; ok {
+ return &cert, nil
+ }
+ log.Warn().Str("host", clientHello.ServerName).Msg("No matching certificate found")
+ return nil, fmt.Errorf("no certificate for host %s", clientHello.ServerName)
+ },
+ },
+ }
+
+ // Start HTTP
+ go func() {
+ log.Info().Str("addr", "80").Msg("starting HTTP server")
+ if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
+ log.Fatal().Err(err).Msg("HTTP server failed")
+ }
+ }()
+
+ // Start HTTPS
+ if hasTLS {
+ go func() {
+ log.Info().Str("addr", "443").Msg("starting HTTPS server")
+ if err := httpsServer.ListenAndServeTLS("", ""); err != nil && err != http.ErrServerClosed {
+ log.Fatal().Err(err).Msg("HTTPS server failed")
+ }
+ }()
+ } else {
+ log.Warn().Msg("No TLS certs loaded, HTTPS server not started")
+ }
+
+ select {}
Review Comment:
[nitpick] Using an empty select statement to block forever is unclear. Consider using a more explicit approach like 'select {}' with a comment explaining the purpose, or use a context-based cancellation mechanism.
```suggestion
// Create a context that listens for termination signals
ctx, stop := context.WithCancel(context.Background())
defer stop()
// Listen for OS signals to gracefully shut down
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM)
go func() {
<-signalChan
log.Info().Msg("Received termination signal, shutting down...")
stop()
}()
// Block until the context is canceled
<-ctx.Done()
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org
Re: [PR] [ingress] Customize the default ingress controller and httputils reve… [dubbo-go-pixiu]
Posted by "mfordjody (via GitHub)" <gi...@apache.org>.
mfordjody closed pull request #711: [ingress] Customize the default ingress controller and httputils reve…
URL: https://github.com/apache/dubbo-go-pixiu/pull/711
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org
Re: [PR] [ingress] Customize the default ingress controller and httputils reve… [dubbo-go-pixiu]
Posted by "sonarqubecloud[bot] (via GitHub)" <gi...@apache.org>.
sonarqubecloud[bot] commented on PR #711:
URL: https://github.com/apache/dubbo-go-pixiu/pull/711#issuecomment-3135116981
## [](https://sonarcloud.io/dashboard?id=apache_dubbo-go-pixiu&pullRequest=711) **Quality Gate failed**
Failed conditions
 [D Security Rating on New Code](https://sonarcloud.io/dashboard?id=apache_dubbo-go-pixiu&pullRequest=711) (required ≥ A)
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=apache_dubbo-go-pixiu&pullRequest=711)
##
 Catch issues before they fail your Quality Gate with our IDE extension  [SonarQube for IDE](https://www.sonarsource.com/products/sonarlint/features/connected-mode/?referrer=pull-request)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org
Re: [PR] [ingress] Customize the default ingress controller and httputils reve… [dubbo-go-pixiu]
Posted by "solisamicus (via GitHub)" <gi...@apache.org>.
solisamicus commented on PR #711:
URL: https://github.com/apache/dubbo-go-pixiu/pull/711#issuecomment-3146460217
https://github.com/apache/dubbo-go-pixiu/issues/553
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org
Re: [PR] [ingress] Customize the default ingress controller and httputils reve… [dubbo-go-pixiu]
Posted by "github-advanced-security[bot] (via GitHub)" <gi...@apache.org>.
github-advanced-security[bot] commented on code in PR #711:
URL: https://github.com/apache/dubbo-go-pixiu/pull/711#discussion_r2241736360
##########
controller/examples/deployment.yaml:
##########
@@ -0,0 +1,70 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: httpbin-deployment
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: httpbin-deployment
+ template:
+ metadata:
+ labels:
+ app: httpbin-deployment
+ spec:
+ containers:
Review Comment:
## Service account permissions should be restricted
<!--SONAR_ISSUE_KEY:AZhaKwXaCyW2e-qWbWRY-->Bind this resource's automounted service account to RBAC or disable automounting. <p>See more on <a href="https://sonarcloud.io/project/issues?id=apache_dubbo-go-pixiu&issues=AZhaKwXaCyW2e-qWbWRY&open=AZhaKwXaCyW2e-qWbWRY&pullRequest=711">SonarQube Cloud</a></p>
[Show more details](https://github.com/apache/dubbo-go-pixiu/security/code-scanning/257)
##########
controller/pkg/server/server.go:
##########
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package server
+
+import (
+ "context"
+ "crypto/tls"
+ "fmt"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/client-go/kubernetes"
+ "log"
+ "net/http"
+ "net/http/httputil"
+ "net/url"
+ "strings"
+)
+
+type Server struct {
+ Client *kubernetes.Clientset
+ Host string
+ Name string
+ Port string
+}
+
+func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ host := r.Host
+
+ // Get all Ingress
+ ingressList, err := s.Client.NetworkingV1().Ingresses("").List(context.TODO(), metav1.ListOptions{})
+ if err != nil {
+ http.Error(w, "Failed to list Ingresses", http.StatusInternalServerError)
+ return
+ }
+
+ for _, ingress := range ingressList.Items {
+ for _, rule := range ingress.Spec.Rules {
+ if rule.Host != host {
+ continue
+ }
+
+ for _, path := range rule.HTTP.Paths {
+ if !strings.HasPrefix(r.URL.Path, path.Path) {
+ continue
+ }
+
+ // Get Service
+ svc, err := s.Client.CoreV1().Services(ingress.Namespace).Get(context.TODO(), path.Backend.Service.Name, metav1.GetOptions{})
+ if err != nil {
+ http.Error(w, "Service not found", http.StatusBadGateway)
+ return
+ }
+
+ // Get Endpoints
+ endpoints, err := s.Client.CoreV1().Endpoints(ingress.Namespace).Get(context.TODO(), svc.Name, metav1.GetOptions{})
+ if err != nil || len(endpoints.Subsets) == 0 {
+ http.Error(w, "No backend Pod available", http.StatusBadGateway)
+ return
+ }
+
+ // Find the first available Pod IP + port
+ var targetIP string
+ var targetPort int32
+ var scheme string = "http" // default http
+
+ found := false
+ for _, subset := range endpoints.Subsets {
+ for _, addr := range subset.Addresses {
+ for _, port := range subset.Ports {
+ targetIP = addr.IP
+ targetPort = port.Port
+ portName := strings.ToLower(port.Name)
+ if port.Port == 443 || strings.Contains(portName, "https") {
+ scheme = "https"
+ }
+ found = true
+ break
+ }
+ if found {
+ break
+ }
+ }
+ if found {
+ break
+ }
+ }
+
+ if !found {
+ http.Error(w, "No backend Pod available", http.StatusBadGateway)
+ return
+ }
+
+ // Constructing the backend URL
+ backendURL, _ := url.Parse(fmt.Sprintf("%s://%s:%d", scheme, targetIP, targetPort))
+ log.Printf("[proxy] %s%s -> %s\n", r.Host, r.URL.Path, backendURL.String())
+
+ // Building a reverse proxy
+ proxy := httputil.NewSingleHostReverseProxy(backendURL)
+ proxy.Director = func(req *http.Request) {
+ req.URL.Scheme = backendURL.Scheme
+ req.URL.Host = backendURL.Host
+ req.Host = r.Host
+ }
+ proxy.Transport = &http.Transport{
+ TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
Review Comment:
## Server certificates should be verified during SSL/TLS connections
<!--SONAR_ISSUE_KEY:AZhaKwUCCyW2e-qWbWRS-->Enable server certificate validation on this SSL/TLS connection. <p>See more on <a href="https://sonarcloud.io/project/issues?id=apache_dubbo-go-pixiu&issues=AZhaKwUCCyW2e-qWbWRS&open=AZhaKwUCCyW2e-qWbWRS&pullRequest=711">SonarQube Cloud</a></p>
[Show more details](https://github.com/apache/dubbo-go-pixiu/security/code-scanning/254)
##########
controller/examples/deployment.yaml:
##########
@@ -0,0 +1,70 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: httpbin-deployment
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: httpbin-deployment
+ template:
+ metadata:
+ labels:
+ app: httpbin-deployment
+ spec:
+ containers:
+ - name: httpbin-deployment
Review Comment:
## Memory limits should be enforced
<!--SONAR_ISSUE_KEY:AZhaKwXaCyW2e-qWbWRW-->Specify a memory limit for this container. <p>See more on <a href="https://sonarcloud.io/project/issues?id=apache_dubbo-go-pixiu&issues=AZhaKwXaCyW2e-qWbWRW&open=AZhaKwXaCyW2e-qWbWRW&pullRequest=711">SonarQube Cloud</a></p>
[Show more details](https://github.com/apache/dubbo-go-pixiu/security/code-scanning/256)
##########
controller/pkg/server/server.go:
##########
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package server
+
+import (
+ "context"
+ "crypto/tls"
+ "fmt"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/client-go/kubernetes"
+ "log"
+ "net/http"
+ "net/http/httputil"
+ "net/url"
+ "strings"
+)
+
+type Server struct {
+ Client *kubernetes.Clientset
+ Host string
+ Name string
+ Port string
+}
+
+func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ host := r.Host
+
+ // Get all Ingress
+ ingressList, err := s.Client.NetworkingV1().Ingresses("").List(context.TODO(), metav1.ListOptions{})
+ if err != nil {
+ http.Error(w, "Failed to list Ingresses", http.StatusInternalServerError)
+ return
+ }
+
+ for _, ingress := range ingressList.Items {
+ for _, rule := range ingress.Spec.Rules {
+ if rule.Host != host {
+ continue
+ }
+
+ for _, path := range rule.HTTP.Paths {
+ if !strings.HasPrefix(r.URL.Path, path.Path) {
+ continue
+ }
+
+ // Get Service
+ svc, err := s.Client.CoreV1().Services(ingress.Namespace).Get(context.TODO(), path.Backend.Service.Name, metav1.GetOptions{})
+ if err != nil {
+ http.Error(w, "Service not found", http.StatusBadGateway)
+ return
+ }
+
+ // Get Endpoints
+ endpoints, err := s.Client.CoreV1().Endpoints(ingress.Namespace).Get(context.TODO(), svc.Name, metav1.GetOptions{})
+ if err != nil || len(endpoints.Subsets) == 0 {
+ http.Error(w, "No backend Pod available", http.StatusBadGateway)
+ return
+ }
+
+ // Find the first available Pod IP + port
+ var targetIP string
+ var targetPort int32
+ var scheme string = "http" // default http
+
+ found := false
+ for _, subset := range endpoints.Subsets {
+ for _, addr := range subset.Addresses {
+ for _, port := range subset.Ports {
+ targetIP = addr.IP
+ targetPort = port.Port
+ portName := strings.ToLower(port.Name)
+ if port.Port == 443 || strings.Contains(portName, "https") {
+ scheme = "https"
+ }
+ found = true
+ break
+ }
+ if found {
+ break
+ }
+ }
+ if found {
+ break
+ }
+ }
+
+ if !found {
+ http.Error(w, "No backend Pod available", http.StatusBadGateway)
+ return
+ }
+
+ // Constructing the backend URL
+ backendURL, _ := url.Parse(fmt.Sprintf("%s://%s:%d", scheme, targetIP, targetPort))
+ log.Printf("[proxy] %s%s -> %s\n", r.Host, r.URL.Path, backendURL.String())
+
+ // Building a reverse proxy
+ proxy := httputil.NewSingleHostReverseProxy(backendURL)
+ proxy.Director = func(req *http.Request) {
+ req.URL.Scheme = backendURL.Scheme
+ req.URL.Host = backendURL.Host
+ req.Host = r.Host
+ }
+ proxy.Transport = &http.Transport{
+ TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
Review Comment:
## Server hostnames should be verified during SSL/TLS connections
<!--SONAR_ISSUE_KEY:AZhaKwUCCyW2e-qWbWRT-->Enable server hostname verification on this SSL/TLS connection. <p>See more on <a href="https://sonarcloud.io/project/issues?id=apache_dubbo-go-pixiu&issues=AZhaKwUCCyW2e-qWbWRT&open=AZhaKwUCCyW2e-qWbWRT&pullRequest=711">SonarQube Cloud</a></p>
[Show more details](https://github.com/apache/dubbo-go-pixiu/security/code-scanning/255)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org