diff --git a/Makefile b/Makefile index 796b59f..abc9cd8 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,3 @@ build: @mkdir -p bin - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ./bin/ingress-controller ./cmd/caddy - -publish-docker: - @echo 'TODO :- not implemented' \ No newline at end of file + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ./bin/ingress-controller ./cmd/caddy \ No newline at end of file diff --git a/README.md b/README.md index 4a9f9bb..377af54 100644 --- a/README.md +++ b/README.md @@ -47,3 +47,54 @@ View the pod logs: ```sh kubectl logs -n caddy-system ``` + +## Automatic HTTPS + +By default, any hosts defined in an ingress resource will configure caddy to automatically get certificates from let's encrypt and +will serve your side over HTTPS. + +To disable automattic https you can set the argument `tls` on the caddy ingress controller to `false`. + +Example: + +Add args `tls=false` to the deployment. + +``` + args: + - -tls=false +``` + +## Bringing Your Own Certificates + +If you would like to disable automatic HTTPS for a specific host and use your own certificates you can create a new TLS secret in Kubernetes and define +what certificates to use when serving your application on the ingress resource. + +Example: + +Create TLS secret `mycerts`, where `./tls.key` and `./tls.crt` are valid certificates for `test.com`. + +``` +kubectl create secret tls mycerts --key ./tls.key --cert ./tls.crt +``` + +``` +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: example + annotations: + kubernetes.io/ingress.class: caddy +spec: + rules: + - host: test.com + http: + paths: + - path: / + backend: + serviceName: test + servicePort: 8080 + tls: + - hosts: + - test.com + secretName: mycerts # use mycerts for host test.com +``` \ No newline at end of file diff --git a/cmd/caddy/flag.go b/cmd/caddy/flag.go index 1dcd56e..1f7c752 100644 --- a/cmd/caddy/flag.go +++ b/cmd/caddy/flag.go @@ -12,7 +12,7 @@ func parseFlags() caddy.ControllerConfig { flag.StringVar(&email, "email", "", "the email address to use for requesting tls certificates if automatic https is enabled.") var namespace string - flag.StringVar(&namespace, "observe-namespace", "", "the namespace that you would like to observe kubernetes ingress resources in.") + flag.StringVar(&namespace, "namespace", "", "the namespace that you would like to observe kubernetes ingress resources in.") var enableAutomaticTLS bool flag.BoolVar(&enableAutomaticTLS, "tls", false, "defines if automatic tls should be enabled for hostnames defined in ingress resources.") diff --git a/cmd/caddy/main.go b/cmd/caddy/main.go index ada2b94..5863bf9 100644 --- a/cmd/caddy/main.go +++ b/cmd/caddy/main.go @@ -37,22 +37,14 @@ func main() { // get client to access the kubernetes service api kubeClient, err := createApiserverClient() if err != nil { - msg := ` - Error while initiating a connection to the Kubernetes API server. - This could mean the cluster is misconfigured (e.g. it has invalid - API server certificates or Service Accounts configuration) - ` - + msg := "Could not establish a connection to the Kubernetes API Server." logrus.Fatalf(msg, err) } restClient := kubeClient.ExtensionsV1beta1().RESTClient() - - // start ingress controller c := controller.NewCaddyController(kubeClient, restClient, cfg) reg := prometheus.NewRegistry() - reg.MustRegister(prometheus.NewGoCollector()) reg.MustRegister(prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{ PidFn: func() (int, error) { return os.Getpid(), nil }, @@ -69,6 +61,7 @@ func main() { logrus.Info("Starting the caddy ingress controller") go c.Run(stopCh) + // TODO :- listen to sigterm select {} } @@ -103,12 +96,11 @@ func createApiserverClient() (*kubernetes.Clientset, error) { return nil, err } + logrus.Infof("Creating API client for %s", cfg.Host) + cfg.QPS = defaultQPS cfg.Burst = defaultBurst cfg.ContentType = "application/vnd.kubernetes.protobuf" - - logrus.Infof("Creating API client for %s", cfg.Host) - client, err := kubernetes.NewForConfig(cfg) if err != nil { return nil, err @@ -143,13 +135,9 @@ func createApiserverClient() (*kubernetes.Clientset, error) { return nil, lastErr } - // this should not happen, warn the user if retries > 0 { logrus.Warningf("Initial connection to the Kubernetes API server was retried %d times.", retries) } - msg := "Running in Kubernetes cluster version v%v.%v (%v) - git (%v) commit %v - platform %v" - logrus.Infof(msg, v.Major, v.Minor, v.GitVersion, v.GitTreeState, v.GitCommit, v.Platform) - return client, nil } diff --git a/examples/concat.yaml b/examples/concat.yaml deleted file mode 100644 index a3df3e9..0000000 --- a/examples/concat.yaml +++ /dev/null @@ -1,94 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: example - labels: - app: example -spec: - replicas: 1 - selector: - matchLabels: - app: example - template: - metadata: - labels: - app: example - spec: - containers: - - name: httpecho - image: hashicorp/http-echo - args: - - "-listen=:8080" - - "-text=hello world" - ports: - - containerPort: 8080 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: example2 - labels: - app: example2 -spec: - replicas: 1 - selector: - matchLabels: - app: example2 - template: - metadata: - labels: - app: example2 - spec: - containers: - - name: httpecho - image: hashicorp/http-echo - args: - - "-listen=:8080" - - "-text=hello world 2" - ports: - - containerPort: 8080 ---- -kind: Service -apiVersion: v1 -metadata: - name: example -spec: - type: ClusterIP - selector: - app: example - ports: - - protocol: TCP - port: 80 - targetPort: 8080 ---- -kind: Service -apiVersion: v1 -metadata: - name: example2 -spec: - type: ClusterIP - selector: - app: example2 - ports: - - protocol: TCP - port: 80 - targetPort: 8080 ---- -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - name: example - annotations: - kubernetes.io/ingress.class: caddy -spec: - rules: - - host: caddy2.kubed.co - http: - paths: - - path: /hello2 - backend: - serviceName: example2 - servicePort: 8080 - - backend: - serviceName: example - servicePort: 8080 \ No newline at end of file diff --git a/internal/caddy/convert.go b/internal/caddy/convert.go index 4a715ad..2f19dce 100644 --- a/internal/caddy/convert.go +++ b/internal/caddy/convert.go @@ -12,12 +12,10 @@ import ( // This is not used when this ingress controller is configured with a config map, so that we don't // override user defined routes. func ConvertToCaddyConfig(ings []*v1beta1.Ingress) (caddyhttp.RouteList, error) { - // ~~~~ // TODO :- // when setting the upstream url we should should bypass kube-dns and get the ip address of // the pod for the deployment we are proxying to so that we can proxy to that ip address port. // this is good for session affinity and increases performance. - // ~~~~ // create a server route for each ingress route var routes caddyhttp.RouteList @@ -46,6 +44,7 @@ func ConvertToCaddyConfig(ings []*v1beta1.Ingress) (caddyhttp.RouteList, error) return routes, nil } +// TODO :- configure log middleware for all routes func baseRoute(upstream string) caddyhttp.ServerRoute { return caddyhttp.ServerRoute{ // Apply: []json.RawMessage{ diff --git a/internal/controller/action.go b/internal/controller/action.go index 3be929a..e3e78e9 100644 --- a/internal/controller/action.go +++ b/internal/controller/action.go @@ -148,12 +148,11 @@ func (r ResourceDeletedAction) handle(c *CaddyController) error { return nil } -// TODO :- Cleanup This Fn -// updateConfig updates internal caddy config with new ingress info +// updateConfig updates internal caddy config with new ingress info. func updateConfig(c *CaddyController) error { - // if certs are defined on an ingress resource start a shared informer factory - // to listen to any changes for these certs. If the certs are updated, reload - // them into the caddy instance. + apps := c.resourceStore.CaddyConfig.Apps + + // if certs are defined on an ingress resource we need to handle them. tlsCfg, err := c.HandleOwnCertManagement(c.resourceStore.Ingresses) if err != nil { return errors.Wrap(err, "caddy config reload") @@ -161,38 +160,20 @@ func updateConfig(c *CaddyController) error { // after TLS secrets are synched we should load them in the cert pool. if tlsCfg != nil { - if c, exists := c.resourceStore.CaddyConfig.Apps["tls"]; exists { - if cfg, ok := c.(caddytls.TLS); ok { - cfg.Certificates["load_folders"] = tlsCfg["load_folders"].(json.RawMessage) - } - } + apps["tls"].(caddytls.TLS).Certificates["load_folders"] = tlsCfg["load_folders"].(json.RawMessage) } else { // reset cert loading - if c, exists := c.resourceStore.CaddyConfig.Apps["tls"]; exists { - if cfg, ok := c.(caddytls.TLS); ok { - cfg.Certificates["load_folders"] = json.RawMessage(`[]`) - } - } + apps["tls"].(caddytls.TLS).Certificates["load_folders"] = json.RawMessage(`[]`) } // skip auto https for hosts with certs provided if tlsCfg != nil { - if skipHosts, exists := tlsCfg["hosts"]; exists { - if hosts, ok := skipHosts.([]string); ok { - if httpCfg, exists := c.resourceStore.CaddyConfig.Apps["http"]; exists { - if cfg, ok := httpCfg.(caddyhttp.App); ok { - cfg.Servers["ingress_server"].AutoHTTPS.Skip = hosts - } - } - } + if hosts, ok := tlsCfg["hosts"].([]string); ok { + apps["http"].(caddyhttp.App).Servers["ingress_server"].AutoHTTPS.Skip = hosts } } else { // reset any skipped hosts set - if httpCfg, exists := c.resourceStore.CaddyConfig.Apps["http"]; exists { - if cfg, ok := httpCfg.(caddyhttp.App); ok { - cfg.Servers["ingress_server"].AutoHTTPS.Skip = make([]string, 0) - } - } + apps["http"].(caddyhttp.App).Servers["ingress_server"].AutoHTTPS.Skip = make([]string, 0) } if !c.usingConfigMap { @@ -202,11 +183,7 @@ func updateConfig(c *CaddyController) error { } // set the http server routes - if httpCfg, exists := c.resourceStore.CaddyConfig.Apps["http"]; exists { - if cfg, ok := httpCfg.(caddyhttp.App); ok { - cfg.Servers["ingress_server"].Routes = serverRoutes - } - } + apps["http"].(caddyhttp.App).Servers["ingress_server"].Routes = serverRoutes } // reload caddy with new config diff --git a/internal/controller/status.go b/internal/controller/status.go index 119ccde..043e551 100644 --- a/internal/controller/status.go +++ b/internal/controller/status.go @@ -31,10 +31,7 @@ func (c *CaddyController) syncStatus(ings []*v1beta1.Ingress) error { return err } - // this happens about every 30 seconds and can pollute the logs, so we - // only want to log on higher verbosity levels. logrus.Info("Synching Ingress resource source addresses") - c.updateIngStatuses(sliceToLoadBalancerIngress(addrs), ings) return nil diff --git a/internal/controller/tls.go b/internal/controller/tls.go index 92aee86..7a712a9 100644 --- a/internal/controller/tls.go +++ b/internal/controller/tls.go @@ -2,7 +2,6 @@ package controller import ( "encoding/json" - "fmt" "io/ioutil" "os" "path/filepath" @@ -73,8 +72,6 @@ func (c *CaddyController) HandleOwnCertManagement(ings []*v1beta1.Ingress) (map[ go informer.Run(c.stopChan) } - fmt.Printf("\nCERTS: %+v - %+v\n", len(certs), certs) - if len(certs) > 0 { return getTLSConfig(hosts), nil } diff --git a/internal/store/store.go b/internal/store/store.go index caf317c..cf02ed0 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -28,24 +28,26 @@ func NewStore(kubeClient *kubernetes.Clientset, namespace string, cfg c.Controll Ingresses: []*v1beta1.Ingress{}, } - if cfgMapConfig == nil { - s.CaddyConfig = c.NewConfig(namespace, cfg) - } else { - // set cert-magic storage provider - cfgMapConfig.Storage = c.Storage{ - System: "secret_store", - StorageValues: c.StorageValues{ - Namespace: namespace, - }, - } - - s.CaddyConfig = cfgMapConfig - } - for _, i := range ingresses.Items { s.Ingresses = append(s.Ingresses, &i) } + // not using cfg map to configure the ingress controller + if cfgMapConfig == nil { + s.CaddyConfig = c.NewConfig(namespace, cfg) + return s + } + + // set cert-magic storage provider + cfgMapConfig.Storage = c.Storage{ + System: "secret_store", + StorageValues: c.StorageValues{ + Namespace: namespace, + }, + } + + s.CaddyConfig = cfgMapConfig + return s }