Skip to content

Commit

Permalink
Initial implementation of Kubernetes support
Browse files Browse the repository at this point in the history
This issue updates #1986.

This is intial, experimental implementation that will
be updated with tests and edge cases prior to production 2.7.0 release.

Teleport proxy adds support for Kubernetes API protocol.
Auth server uses Kubernetes API to receive certificates
issued by Kubernetes CA.

Proxy intercepts and forwards API requests to the Kubernetes
API server and captures live session traffic, making
recordings available in the audit log.

Tsh login now updates kubeconfig configuration to use
Teleport as a proxy server.
  • Loading branch information
klizhentas committed Jun 3, 2018
1 parent f340e7d commit cece4be
Show file tree
Hide file tree
Showing 1,409 changed files with 376,285 additions and 1,272 deletions.
415 changes: 198 additions & 217 deletions Gopkg.lock

Large diffs are not rendered by default.

20 changes: 15 additions & 5 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ ignored = ["github.com/Sirupsen/logrus"]
name = "github.com/alecthomas/units"
branch = "master"

[[constraint]]
name = "k8s.io/apimachinery"
revision = "1fd2e63a9a370677308a42f24fd40c86438afddf"

[[constraint]]
name = "github.com/vulcand/predicate"
Expand Down Expand Up @@ -107,8 +104,8 @@ ignored = ["github.com/Sirupsen/logrus"]
version = ">=0.0.2, <=1.0.0-gc98f59f"

[[constraint]]
branch = "alexander/copy"
name = "github.com/mailgun/oxy"
version = "1.0.1"
name = "github.com/gravitational/oxy"

[[constraint]]
name = "github.com/pborman/uuid"
Expand Down Expand Up @@ -151,3 +148,16 @@ ignored = ["github.com/Sirupsen/logrus"]
[[constraint]]
name = "github.com/gravitational/license"
version = "0.0.4"

[[override]]
name = "k8s.io/api"
branch = "release-1.9"

[[override]]
name = "k8s.io/apimachinery"
branch = "release-1.9"

[[constraint]]
version = "6.0.0"
name = "k8s.io/client-go"

37 changes: 28 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# Naming convention:
# for stable releases we use "1.0.0" format
# for pre-releases, we use "1.0.0-beta.2" format
VERSION=2.6.0
VERSION=2.7.0-alpha.1

# These are standard autotools variables, don't change them please
BUILDDIR ?= build
Expand All @@ -28,11 +28,6 @@ RELEASE=teleport-$(GITTAG)-$(ARCH)-bin
BINARIES=$(BUILDDIR)/teleport $(BUILDDIR)/tctl $(BUILDDIR)/tsh

VERSRC = version.go gitref.go
LIBS = $(shell find lib -type f -name '*.go') *.go
TCTLSRC = $(shell find tool/tctl -type f -name '*.go')
TELEPORTSRC = $(shell find tool/teleport -type f -name '*.go')
TSHSRC = $(shell find tool/tsh -type f -name '*.go')
TELEPORTVENDOR = $(shell find vendor -type f -name '*.go')

# PAM support will only be built into Teleport if headers exist at build time.
PAM_MESSAGE = "Building Teleport without PAM support."
Expand All @@ -53,13 +48,21 @@ all: $(VERSRC)
@echo $(PAM_MESSAGE)
$(MAKE) $(BINARIES)

$(BUILDDIR)/tctl: $(LIBS) $(TCTLSRC) $(TELEPORTVENDOR)
# By making these 3 targets below (tsh, tctl and teleport) PHONY we are solving
# several problems:
# * Build will rely on go build internal caching https://golang.org/doc/go1.10 at all times
# * Manual change detection was broken on a large dependency tree
# If you are considering changing this behavior, please consult with dev team first
.PHONY: $(BUILDDIR)/tctl
$(BUILDDIR)/tctl:
go build $(PAMFLAGS) -o $(BUILDDIR)/tctl -i $(BUILDFLAGS) ./tool/tctl

$(BUILDDIR)/teleport: $(LIBS) $(TELEPORTSRC) $(TELEPORTVENDOR)
.PHONY: $(BUILDDIR)/teleport
$(BUILDDIR)/teleport:
go build $(PAMFLAGS) -o $(BUILDDIR)/teleport -i $(BUILDFLAGS) ./tool/teleport

$(BUILDDIR)/tsh: $(LIBS) $(TSHSRC) $(TELEPORTVENDOR)
.PHONY: $(BUILDDIR)/tsh
$(BUILDDIR)/tsh:
go build $(PAMFLAGS) -o $(BUILDDIR)/tsh -i $(BUILDFLAGS) ./tool/tsh

#
Expand Down Expand Up @@ -238,3 +241,19 @@ install: build
cp -f $(BUILDDIR)/teleport $(BINDIR)/
mkdir -p $(DATADIR)


.PHONY: image
image:
if [ -f e/Makefile ]; then $(MAKE) -C e image; fi

.PHONY: publish
publish:
if [ -f e/Makefile ]; then $(MAKE) -C e publish; fi

.PHONY: print-version
print-version:
@echo $(VERSION)

.PHONY: chart-ent
chart-ent:
$(MAKE) -C e chart
4 changes: 2 additions & 2 deletions build.assets/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
teleport
tctl
./teleport
./tctl
8 changes: 8 additions & 0 deletions build.assets/charts/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
FROM quay.io/gravitational/debian-grande:0.0.1

# Bundle teleport and control binary
ADD teleport /usr/local/bin/teleport
ADD tctl /usr/local/bin/tctl

# By setting this entry point, we expose make target as command
ENTRYPOINT ["/usr/bin/dumb-init", "teleport", "start", "-c", "/etc/teleport/teleport.yaml"]
32 changes: 32 additions & 0 deletions constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ const (
// to bypass firewall restrictions
ComponentReverseTunnelAgent = "proxy:agent"

// ComponentKube is a kubernetes proxy
ComponentKube = "proxy:kube"

// ComponentAuth is the cluster CA node (auth server API)
ComponentAuth = "auth"

Expand Down Expand Up @@ -344,10 +347,39 @@ const (
const (
// SharedDirMode is a mode for a directory shared with group
SharedDirMode = 0750

// PrivateDirMode is a mode for private directories
PrivateDirMode = 0700
)

const (
// SessionEvent is sent by servers to clients when an audit event occurs on
// the session.
SessionEvent = "x-teleport-event"
)

const (
// EnvKubeConfig is environment variable for kubeconfig
EnvKubeConfig = "KUBECONFIG"

// KubeConfigDir is a default directory where k8s stores its user local config
KubeConfigDir = ".kube"

// KubeConfigFile is a default filename where k8s stores its user local config
KubeConfigFile = "config"

// EnvHome is home environment variable
EnvHome = "HOME"

// KubeServiceAddr is an address for kubernetes endpoint service
KubeServiceAddr = "kubernetes.default.svc.cluster.local:443"

// KubeCAPath is a hardcode of mounted CA inside every pod of K8s
KubeCAPath = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"

// KubeKindCSR is a certificate signing requests
KubeKindCSR = "CertificateSigningRequest"

// KubeMetadataNameSelector is a selector for name metadata in API requests
KubeMetadataNameSelector = "metadata.name"
)
2 changes: 1 addition & 1 deletion e
Submodule e updated from 5e5c7f to 4a413d
6 changes: 6 additions & 0 deletions examples/chart/teleport/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name: teleport
version: 0.0.1
description: Teleport Enterprise
keywords:
- Teleport Enterprise
tillerVersion: ">=2.8.0"
31 changes: 31 additions & 0 deletions examples/chart/teleport/config/teleport.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
teleport:
log:
output: stderr
severity: DEBUG

data_dir: /var/lib/teleport
storage:
type: dir

auth_service:
enabled: yes
license_file: /var/lib/license/license-enterprise.pem

authentication:
type: oidc

public_addr: lens.gravitational.co:3025
cluster_name: lens.gravitational.co

ssh_service:
enabled: yes
public_addr: lens.gravitational.co:3022

proxy_service:
enabled: yes
public_addr: lens.gravitational.co:443
web_listen_addr: 0.0.0.0:8080
listen_addr: 0.0.0.0:3023
kube_listen_addr: 0.0.0.0:3026
https_key_file: /var/lib/certs/key
https_cert_file: /var/lib/certs/crt
8 changes: 8 additions & 0 deletions examples/chart/teleport/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
12 changes: 12 additions & 0 deletions examples/chart/teleport/templates/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "fullname" . }}
labels:
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ .Chart.Name }}
data:
teleport.yaml: |
{{ .Files.Get "config/teleport.yaml" | indent 4 }}
52 changes: 52 additions & 0 deletions examples/chart/teleport/templates/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: {{ .Chart.Name }}
labels:
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: 1
template:
metadata:
labels:
app: {{ .Chart.Name }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
serviceAccount: {{ .Chart.Name }}
containers:
- name: teleport
image: quay.io/gravitational/teleport-ent:{{ .Chart.Version }}
imagePullPolicy: Always
resources:
requests:
cpu: 1m
ports:
- containerPort: 8080
name: keygen
volumeMounts:
- mountPath: /var/lib/certs
name: tls-web
readOnly: true
- mountPath: /etc/teleport
name: config
readOnly: true
- mountPath: /var/lib/license
name: license
readOnly: true
- mountPath: /var/lib/teleport
name: storage
volumes:
- name: tls-web
secret:
secretName: tls-web
- name: license
secret:
secretName: license
- name: config
configMap:
name: {{ template "fullname" . }}
- name: storage
emptyDir: {}

20 changes: 20 additions & 0 deletions examples/chart/teleport/templates/rbac.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ .Chart.Name }}
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: {{ .Chart.Name }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: {{ .Chart.Name }}
namespace: default


25 changes: 25 additions & 0 deletions examples/chart/teleport/templates/service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}
spec:
type: LoadBalancer
ports:
- name: web
port: 443
targetPort: 8080
protocol: TCP
- name: api
port: 3025
targetPort: 3025
protocol: TCP
- name: proxy-ssh
port: 3023
targetPort: 3023
protocol: TCP
- name: proxy-kube
port: 3026
targetPort: 3026
protocol: TCP
selector:
app: {{ .Chart.Name }}
21 changes: 19 additions & 2 deletions lib/auth/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ func NewAPIServer(config *APIConfig) http.Handler {
}
srv.Router = *httprouter.New()

// Kubernetes extensions
srv.POST("/:version/kube/csr", srv.withAuth(srv.processKubeCSR))

// Operations on certificate authorities
srv.GET("/:version/domain", srv.withAuth(srv.getDomainName))

Expand Down Expand Up @@ -229,8 +232,7 @@ type HandlerWithAuthFunc func(auth ClientI, w http.ResponseWriter, r *http.Reque
func (s *APIServer) withAuth(handler HandlerWithAuthFunc) httprouter.Handle {
const accessDeniedMsg = "auth API: access denied "
return httplib.MakeHandler(func(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
// SSH-to-HTTP gateway (tun server) expects the auth
// context to be set by SSH server
// HTTPS server expects auth context to be set by the auth middleware
authContext, err := s.Authorizer.Authorize(r.Context())
if err != nil {
// propagate connection problem error so we can differentiate
Expand Down Expand Up @@ -2243,6 +2245,21 @@ func (s *APIServer) deleteAllRemoteClusters(auth ClientI, w http.ResponseWriter,
return message("ok"), nil
}

func (s *APIServer) processKubeCSR(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
var req KubeCSR

if err := httplib.ReadJSON(r, &req); err != nil {
return nil, trace.Wrap(err)
}

re, err := auth.ProcessKubeCSR(req)
if err != nil {
return nil, trace.Wrap(err)
}

return re, nil
}

func message(msg string) map[string]interface{} {
return map[string]interface{}{"message": msg}
}
Loading

0 comments on commit cece4be

Please sign in to comment.