Skip to content

Commit

Permalink
Add DNS resolver and server
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinpollet committed Jan 11, 2021
1 parent ee9d994 commit 309becb
Show file tree
Hide file tree
Showing 49 changed files with 1,082 additions and 927 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
*.exe
cover.out
traefik-mesh
!traefik-mesh/
/dist/
.vscode
vendor
Expand Down
2 changes: 1 addition & 1 deletion cmd/cleanup/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type Configuration struct {
func NewConfiguration() *Configuration {
return &Configuration{
KubeConfig: os.Getenv("KUBECONFIG"),
Namespace: "maesh",
Namespace: "default",
LogLevel: "error",
LogFormat: "common",
}
Expand Down
28 changes: 28 additions & 0 deletions cmd/dns/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package dns

import "os"

// Configuration holds the configuration for the dns command.
type Configuration struct {
KubeConfig string `description:"Path to a kubeconfig. Only required if out-of-cluster." export:"true"`
MasterURL string `description:"The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster." export:"true"`
LogLevel string `description:"The log level." export:"true"`
LogFormat string `description:"The log format." export:"true"`
Port int32 `description:"The DNS server port." export:"true"`
Namespace string `description:"The namespace that Traefik Mesh is installed in." export:"true"`
ServiceName string `description:"The DNS service name." export:"true"`
ServicePort int32 `description:"The DNS service port." export:"true"`
}

// NewConfiguration creates the dns command configuration with default values.
func NewConfiguration() *Configuration {
return &Configuration{
KubeConfig: os.Getenv("KUBECONFIG"),
LogLevel: "error",
LogFormat: "common",
Port: 9053,
Namespace: "default",
ServiceName: "traefik-mesh-dns",
ServicePort: 53,
}
}
126 changes: 126 additions & 0 deletions cmd/dns/dns.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package dns

import (
"context"
"fmt"
"time"

"github.com/sirupsen/logrus"
"github.com/traefik/mesh/v2/cmd"
"github.com/traefik/mesh/v2/pkg/dns"
"github.com/traefik/mesh/v2/pkg/k8s"
"github.com/traefik/paerser/cli"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
listers "k8s.io/client-go/listers/core/v1"
)

// NewCmd builds a new dns command.
func NewCmd(config *Configuration, loaders []cli.ResourceLoader) *cli.Command {
return &cli.Command{
Name: "dns",
Description: `DNS command.`,
Configuration: config,
Run: func(_ []string) error {
return dnsCommand(config)
},
Resources: loaders,
}
}

func dnsCommand(config *Configuration) error {
ctx := cmd.ContextWithSignal(context.Background())

logger, err := cmd.NewLogger(config.LogFormat, config.LogLevel)
if err != nil {
return fmt.Errorf("could not create logger: %w", err)
}

logger.Debug("Starting DNS server...")
logger.Debugf("Using masterURL: %q", config.MasterURL)
logger.Debugf("Using kubeconfig: %q", config.KubeConfig)

clients, err := k8s.NewClient(logger, config.MasterURL, config.KubeConfig)
if err != nil {
return fmt.Errorf("error building clients: %w", err)
}

// Configure DNS.
if err = configureDNS(ctx, clients.KubernetesClient(), logger, config); err != nil {
return err
}

// Start DNS server.
serviceLister, err := newServiceLister(ctx, clients.KubernetesClient(), config)
if err != nil {
return err
}

resolver := dns.NewShadowServiceResolver("traefik.mesh", config.Namespace, serviceLister)
server := dns.NewServer(config.Port, resolver, logger)

errCh := make(chan error)

go func() {
if err := server.ListenAndServe(); err != nil {
errCh <- fmt.Errorf("DNS server has stopped unexpectedly: %w", err)
}
}()

select {
case err := <-errCh:
return err

case <-ctx.Done():
if stopErr := stopDNSServer(server); stopErr != nil {
return fmt.Errorf("unable to stop DNS server: %w", stopErr)
}
}

return nil
}

func configureDNS(ctx context.Context, kubeClient kubernetes.Interface, logger logrus.FieldLogger, config *Configuration) error {
dnsClient := dns.NewClient(logger, kubeClient)

dnsProvider, err := dnsClient.CheckDNSProvider(ctx)
if err != nil {
return fmt.Errorf("unable to find suitable DNS provider: %w", err)
}

switch dnsProvider {
case dns.CoreDNS:
if err := dnsClient.ConfigureCoreDNS(ctx, config.Namespace, config.ServiceName, config.ServicePort); err != nil {
return fmt.Errorf("unable to configure CoreDNS: %w", err)
}

case dns.KubeDNS:
if err := dnsClient.ConfigureKubeDNS(ctx, config.Namespace, config.ServiceName, config.ServicePort); err != nil {
return fmt.Errorf("unable to configure KubeDNS: %w", err)
}
}

return nil
}

func newServiceLister(ctx context.Context, kubeClient kubernetes.Interface, config *Configuration) (listers.ServiceLister, error) {
kubernetesFactory := informers.NewSharedInformerFactoryWithOptions(kubeClient, k8s.ResyncPeriod, informers.WithNamespace(config.Namespace))
serviceLister := kubernetesFactory.Core().V1().Services().Lister()

kubernetesFactory.Start(ctx.Done())

for t, ok := range kubernetesFactory.WaitForCacheSync(ctx.Done()) {
if !ok {
return nil, fmt.Errorf("timed out waiting for informer caches to sync: %s", t)
}
}

return serviceLister, nil
}

func stopDNSServer(dnsServer *dns.Server) error {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()

return dnsServer.ShutdownContext(ctx)
}
2 changes: 1 addition & 1 deletion cmd/mesh/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func NewConfiguration() *Configuration {
LogFormat: "common",
ACL: false,
DefaultMode: "http",
Namespace: "maesh",
Namespace: "default",
APIPort: 9000,
APIHost: "",
LimitHTTPPort: 10,
Expand Down
13 changes: 9 additions & 4 deletions cmd/mesh/mesh.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

"github.com/traefik/mesh/v2/cmd"
"github.com/traefik/mesh/v2/cmd/cleanup"
"github.com/traefik/mesh/v2/cmd/prepare"
"github.com/traefik/mesh/v2/cmd/dns"
"github.com/traefik/mesh/v2/cmd/version"
"github.com/traefik/mesh/v2/pkg/api"
"github.com/traefik/mesh/v2/pkg/controller"
Expand Down Expand Up @@ -40,8 +40,8 @@ func main() {
},
}

prepareConfig := prepare.NewConfiguration()
if err := traefikMeshCmd.AddCommand(prepare.NewCmd(prepareConfig, loaders)); err != nil {
dnsConfig := dns.NewConfiguration()
if err := traefikMeshCmd.AddCommand(dns.NewCmd(dnsConfig, loaders)); err != nil {
stdlog.Println(err)
os.Exit(1)
}
Expand Down Expand Up @@ -76,14 +76,19 @@ func traefikMeshCommand(config *Configuration) error {
logger.Debug("Starting controller...")
logger.Debugf("Using masterURL: %q", config.MasterURL)
logger.Debugf("Using kubeconfig: %q", config.KubeConfig)
logger.Debugf("ACL mode enabled: %t", config.ACL)

clients, err := k8s.NewClient(logger, config.MasterURL, config.KubeConfig)
if err != nil {
return fmt.Errorf("error building clients: %w", err)
}

logger.Debugf("ACL mode enabled: %t", config.ACL)
// Check SMI versions.
if err = k8s.CheckSMIVersion(clients.KubernetesClient(), config.ACL); err != nil {
return fmt.Errorf("unsupported SMI version: %w", err)
}

// Start controller and API server.
apiServer := api.NewAPI(logger, config.APIPort, config.APIHost, config.Namespace)

ctr := controller.NewMeshController(clients, controller.Config{
Expand Down
25 changes: 0 additions & 25 deletions cmd/prepare/config.go

This file was deleted.

71 changes: 0 additions & 71 deletions cmd/prepare/prepare.go

This file was deleted.

18 changes: 0 additions & 18 deletions docs/content/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,6 @@ To deploy the Helm Chart, run:
helm install traefik-mesh traefik-mesh/traefik-mesh --set controller.image.pullPolicy=IfNotPresent --set controller.image.tag=latest
```

## KubeDNS support

Traefik Mesh supports KubeDNS:

```bash
helm install traefik-mesh traefik-mesh/traefik-mesh --set kubedns=true
```

With the `kubedns` parameter Traefik Mesh will install CoreDNS and patch KubeDNS to use it as a [stubDomain](https://v1-17.docs.kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/#example-stub-domain).

## Custom cluster domain

If you use a cluster domain other than `cluster.local` set it by using the `clusterDomain` parameter:

```bash
helm install traefik-mesh traefik-mesh/traefik-mesh --set clusterDomain=my.custom.domain.com
```

## Access Control List

By default, Traefik Mesh does not restrict traffic between pods and services. However, some scenarios require more control over the rules for internal communication.
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/google/uuid v1.1.1
github.com/gorilla/mux v1.7.3
github.com/hashicorp/go-version v1.2.1
github.com/miekg/dns v1.1.34
github.com/servicemeshinterface/smi-sdk-go v0.4.1
github.com/sirupsen/logrus v1.6.0
github.com/stretchr/testify v1.6.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0j
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.34 h1:SgTzfkN+oLoIHF1bgUP+C71mzuDl3AhLApHzCCIAMWM=
github.com/miekg/dns v1.1.34/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
Expand Down
2 changes: 2 additions & 0 deletions integration/acl_disabled_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,12 @@ func (s *ACLDisabledSuite) SetUpSuite(c *check.C) {
c.Assert(s.cluster.Apply(s.logger, "testdata/tool/tool.yaml"), checker.IsNil)
c.Assert(s.cluster.Apply(s.logger, "testdata/traefik-mesh/controller-acl-disabled.yaml"), checker.IsNil)
c.Assert(s.cluster.Apply(s.logger, "testdata/traefik-mesh/proxy.yaml"), checker.IsNil)
c.Assert(s.cluster.Apply(s.logger, "testdata/traefik-mesh/dns.yaml"), checker.IsNil)

c.Assert(s.cluster.WaitReadyPod("tool", testNamespace, 60*time.Second), checker.IsNil)
c.Assert(s.cluster.WaitReadyDeployment("traefik-mesh-controller", traefikMeshNamespace, 60*time.Second), checker.IsNil)
c.Assert(s.cluster.WaitReadyDaemonSet("traefik-mesh-proxy", traefikMeshNamespace, 60*time.Second), checker.IsNil)
c.Assert(s.cluster.WaitReadyDeployment("traefik-mesh-dns", traefikMeshNamespace, 60*time.Second), checker.IsNil)

s.tool = tool.New(s.logger, "tool", testNamespace)
}
Expand Down
2 changes: 2 additions & 0 deletions integration/acl_enabled_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,13 @@ func (s *ACLEnabledSuite) SetUpSuite(c *check.C) {
c.Assert(s.cluster.Apply(s.logger, "testdata/tool/tool-forbidden.yaml"), checker.IsNil)
c.Assert(s.cluster.Apply(s.logger, "testdata/traefik-mesh/controller-acl-enabled.yaml"), checker.IsNil)
c.Assert(s.cluster.Apply(s.logger, "testdata/traefik-mesh/proxy.yaml"), checker.IsNil)
c.Assert(s.cluster.Apply(s.logger, "testdata/traefik-mesh/dns.yaml"), checker.IsNil)

c.Assert(s.cluster.WaitReadyPod("tool-authorized", testNamespace, 60*time.Second), checker.IsNil)
c.Assert(s.cluster.WaitReadyPod("tool-forbidden", testNamespace, 60*time.Second), checker.IsNil)
c.Assert(s.cluster.WaitReadyDeployment("traefik-mesh-controller", traefikMeshNamespace, 60*time.Second), checker.IsNil)
c.Assert(s.cluster.WaitReadyDaemonSet("traefik-mesh-proxy", traefikMeshNamespace, 60*time.Second), checker.IsNil)
c.Assert(s.cluster.WaitReadyDeployment("traefik-mesh-dns", traefikMeshNamespace, 60*time.Second), checker.IsNil)

s.toolAuthorized = tool.New(s.logger, "tool-authorized", testNamespace)
s.toolForbidden = tool.New(s.logger, "tool-forbidden", testNamespace)
Expand Down

0 comments on commit 309becb

Please sign in to comment.