mirror of
https://github.com/pomerium/pomerium.git
synced 2025-07-25 20:49:30 +02:00
authenticate: support kubernetes (#62)
- cmd/pomerium: made ping a toplevel route for all services. - docs: updated kubernetes script and corresponding configuration yaml's
This commit is contained in:
parent
c13459bb88
commit
0a501b61ed
11 changed files with 115 additions and 202 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,6 +2,7 @@
|
||||||
pem
|
pem
|
||||||
env
|
env
|
||||||
coverage.txt
|
coverage.txt
|
||||||
|
gsuite.service.account.json
|
||||||
*.pem
|
*.pem
|
||||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
*.o
|
*.o
|
||||||
|
|
|
@ -89,12 +89,11 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
topMux := http.NewServeMux()
|
topMux := http.NewServeMux()
|
||||||
|
topMux.HandleFunc("/ping", func(rw http.ResponseWriter, _ *http.Request) {
|
||||||
|
rw.WriteHeader(http.StatusOK)
|
||||||
|
fmt.Fprintf(rw, "OK")
|
||||||
|
})
|
||||||
if authenticateService != nil {
|
if authenticateService != nil {
|
||||||
// Need to handle ping without host lookup for LB
|
|
||||||
topMux.HandleFunc("/ping", func(rw http.ResponseWriter, _ *http.Request) {
|
|
||||||
rw.WriteHeader(http.StatusOK)
|
|
||||||
fmt.Fprintf(rw, "OK")
|
|
||||||
})
|
|
||||||
topMux.Handle(authHost+"/", authenticateService.Handler())
|
topMux.Handle(authHost+"/", authenticateService.Handler())
|
||||||
}
|
}
|
||||||
if proxyService != nil {
|
if proxyService != nil {
|
||||||
|
|
|
@ -35,8 +35,6 @@ spec:
|
||||||
value: 851877082059-bfgkpj09noog7as3gpc3t7r6n9sjbgs6.apps.googleusercontent.com
|
value: 851877082059-bfgkpj09noog7as3gpc3t7r6n9sjbgs6.apps.googleusercontent.com
|
||||||
- name: PROXY_ROOT_DOMAIN
|
- name: PROXY_ROOT_DOMAIN
|
||||||
value: beyondperimeter.com
|
value: beyondperimeter.com
|
||||||
- name: ALLOWED_DOMAINS
|
|
||||||
value: "*"
|
|
||||||
- name: SHARED_SECRET
|
- name: SHARED_SECRET
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
|
@ -52,6 +50,11 @@ spec:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: idp-client-secret
|
name: idp-client-secret
|
||||||
key: idp-client-secret
|
key: idp-client-secret
|
||||||
|
- name: IDP_SERVICE_ACCOUNT
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: idp-service-account
|
||||||
|
key: idp-service-account
|
||||||
- name: CERTIFICATE
|
- name: CERTIFICATE
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
|
|
59
docs/docs/examples/kubernetes/authorize.deploy.yml
Normal file
59
docs/docs/examples/kubernetes/authorize.deploy.yml
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: pomerium-authorize
|
||||||
|
labels:
|
||||||
|
app: pomerium-authorize
|
||||||
|
namespace: pomerium
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: pomerium-authorize
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: pomerium-authorize
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: pomerium/pomerium:latest
|
||||||
|
name: pomerium-authorize
|
||||||
|
ports:
|
||||||
|
- containerPort: 443
|
||||||
|
name: https
|
||||||
|
protocol: TCP
|
||||||
|
env:
|
||||||
|
- name: POLICY
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: policy
|
||||||
|
key: policy
|
||||||
|
- name: SERVICES
|
||||||
|
value: authorize
|
||||||
|
- name: SHARED_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: shared-secret
|
||||||
|
key: shared-secret
|
||||||
|
- name: CERTIFICATE
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: certificate
|
||||||
|
key: certificate
|
||||||
|
- name: CERTIFICATE_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: certificate-key
|
||||||
|
key: certificate-key
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /ping
|
||||||
|
port: 443
|
||||||
|
scheme: HTTPS
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /ping
|
||||||
|
port: 443
|
||||||
|
scheme: HTTPS
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
timeoutSeconds: 1
|
14
docs/docs/examples/kubernetes/authorize.service.yml
Normal file
14
docs/docs/examples/kubernetes/authorize.service.yml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: pomerium-authorize-service
|
||||||
|
namespace: pomerium
|
||||||
|
annotations:
|
||||||
|
cloud.google.com/app-protocols: '{"https":"HTTPS"}'
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 443
|
||||||
|
name: https
|
||||||
|
selector:
|
||||||
|
app: pomerium-authorize
|
||||||
|
type: NodePort
|
|
@ -23,7 +23,6 @@ spec:
|
||||||
backend:
|
backend:
|
||||||
serviceName: pomerium-proxy-service
|
serviceName: pomerium-proxy-service
|
||||||
servicePort: https
|
servicePort: https
|
||||||
|
|
||||||
- host: "auth.corp.beyondperimeter.com"
|
- host: "auth.corp.beyondperimeter.com"
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
|
|
|
@ -50,6 +50,12 @@ spec:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: idp-client-secret
|
name: idp-client-secret
|
||||||
key: idp-client-secret
|
key: idp-client-secret
|
||||||
|
# e.g. service account for group retrieval, e.g. gsuite
|
||||||
|
- name: IDP_SERVICE_ACCOUNT
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: idp-service-account
|
||||||
|
key: idp-service-account
|
||||||
- name: CERTIFICATE
|
- name: CERTIFICATE
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
|
@ -60,6 +66,11 @@ spec:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: certificate-key
|
name: certificate-key
|
||||||
key: certificate-key
|
key: certificate-key
|
||||||
|
- name: POLICY
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: policy
|
||||||
|
key: policy
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /ping
|
path: /ping
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
package options // import "github.com/pomerium/pomerium/internal/options"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewEmailValidator returns a function that checks whether a given email is valid based on a list
|
|
||||||
// of domains. The domain "*" is a wild card that matches any non-empty email.
|
|
||||||
func NewEmailValidator(domains []string) func(string) bool {
|
|
||||||
allowAll := false
|
|
||||||
for i, domain := range domains {
|
|
||||||
if domain == "*" {
|
|
||||||
allowAll = true
|
|
||||||
}
|
|
||||||
domains[i] = fmt.Sprintf("@%s", strings.ToLower(domain))
|
|
||||||
}
|
|
||||||
|
|
||||||
if allowAll {
|
|
||||||
return func(email string) bool { return email != "" }
|
|
||||||
}
|
|
||||||
|
|
||||||
return func(email string) bool {
|
|
||||||
if email == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
email = strings.ToLower(email)
|
|
||||||
for _, domain := range domains {
|
|
||||||
if strings.HasSuffix(email, domain) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,121 +0,0 @@
|
||||||
package options // import "github.com/pomerium/pomerium/internal/options"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEmailValidatorValidator(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
domains []string
|
|
||||||
email string
|
|
||||||
expectValid bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "nothing should validate when domain list is empty",
|
|
||||||
domains: []string(nil),
|
|
||||||
email: "foo@example.com",
|
|
||||||
expectValid: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "single domain validation",
|
|
||||||
domains: []string{"example.com"},
|
|
||||||
email: "foo@example.com",
|
|
||||||
expectValid: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "substring matches are rejected",
|
|
||||||
domains: []string{"example.com"},
|
|
||||||
email: "foo@hackerexample.com",
|
|
||||||
expectValid: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "no subdomain rollup happens",
|
|
||||||
domains: []string{"example.com"},
|
|
||||||
email: "foo@bar.example.com",
|
|
||||||
expectValid: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "multiple domain validation still rejects other domains",
|
|
||||||
domains: []string{"abc.com", "xyz.com"},
|
|
||||||
email: "foo@example.com",
|
|
||||||
expectValid: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "multiple domain validation still accepts emails from either domain",
|
|
||||||
domains: []string{"abc.com", "xyz.com"},
|
|
||||||
email: "foo@abc.com",
|
|
||||||
expectValid: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "multiple domain validation still rejects other domains",
|
|
||||||
domains: []string{"abc.com", "xyz.com"},
|
|
||||||
email: "bar@xyz.com",
|
|
||||||
expectValid: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "comparisons are case insensitive",
|
|
||||||
domains: []string{"Example.Com"},
|
|
||||||
email: "foo@example.com",
|
|
||||||
expectValid: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "comparisons are case insensitive",
|
|
||||||
domains: []string{"Example.Com"},
|
|
||||||
email: "foo@EXAMPLE.COM",
|
|
||||||
expectValid: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "comparisons are case insensitive",
|
|
||||||
domains: []string{"example.com"},
|
|
||||||
email: "foo@ExAmPlE.CoM",
|
|
||||||
expectValid: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "single wildcard allows all",
|
|
||||||
domains: []string{"*"},
|
|
||||||
email: "foo@example.com",
|
|
||||||
expectValid: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "single wildcard allows all",
|
|
||||||
domains: []string{"*"},
|
|
||||||
email: "bar@gmail.com",
|
|
||||||
expectValid: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "wildcard in list allows all",
|
|
||||||
domains: []string{"example.com", "*"},
|
|
||||||
email: "foo@example.com",
|
|
||||||
expectValid: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "wildcard in list allows all",
|
|
||||||
domains: []string{"example.com", "*"},
|
|
||||||
email: "foo@gmail.com",
|
|
||||||
expectValid: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "empty email rejected",
|
|
||||||
domains: []string{"example.com"},
|
|
||||||
email: "",
|
|
||||||
expectValid: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "wildcard still rejects empty emails",
|
|
||||||
domains: []string{"*"},
|
|
||||||
email: "",
|
|
||||||
expectValid: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range testCases {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
emailValidator := NewEmailValidator(tc.domains)
|
|
||||||
valid := emailValidator(tc.email)
|
|
||||||
if valid != tc.expectValid {
|
|
||||||
t.Fatalf("expected %v, got %v", tc.expectValid, valid)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,10 +5,10 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
pb "github.com/pomerium/pomerium/proto/authorize"
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
"github.com/pomerium/pomerium/internal/sessions"
|
"github.com/pomerium/pomerium/internal/sessions"
|
||||||
|
pb "github.com/pomerium/pomerium/proto/authorize"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Authorizer provides the authorize service interface
|
// Authorizer provides the authorize service interface
|
||||||
|
|
|
@ -1,64 +1,47 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# NOTE! This will create real resources on Google's cloud. Make sure you clean up any unused
|
# NOTE! This will create real resources on Google's cloud. Make sure you clean up any unused
|
||||||
# resources to avoid being billed. For reference, this tutorial cost me <10 cents for a couple of hours.
|
# resources to avoid being billed. For reference, this tutorial cost me <10 cents for a couple of hours.
|
||||||
|
# NOTE! You must change the identity provider client secret setting, and service account setting!
|
||||||
|
|
||||||
# create a cluster
|
echo "=> creating cluster"
|
||||||
gcloud container clusters create pomerium --num-nodes 1
|
gcloud container clusters create pomerium --num-nodes 1
|
||||||
# get cluster credentials os we can use kubctl locally
|
|
||||||
|
echo "=> get cluster credentials os we can use kubctl locally"
|
||||||
gcloud container clusters get-credentials pomerium
|
gcloud container clusters get-credentials pomerium
|
||||||
# create `pomerium` namespace
|
|
||||||
|
echo "=> create pomerium namespace"
|
||||||
kubectl create ns pomerium
|
kubectl create ns pomerium
|
||||||
|
|
||||||
######################################################################
|
echo "=> create our cryptographically random keys for $(shared-secret) and $(cookie-secret) from urandom"
|
||||||
#### UNCOMMENT to use helm to install cert-manager & nginx-ingress####
|
|
||||||
######################################################################
|
|
||||||
# setup service account for tiller used by helm
|
|
||||||
# kubectl create serviceaccount --namespace kube-system tiller
|
|
||||||
# kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller
|
|
||||||
# helm init --service-account tiller
|
|
||||||
# # update helm
|
|
||||||
# helm repo update
|
|
||||||
# kubectl get deployments -n kube-system
|
|
||||||
# # create nginx-ingress
|
|
||||||
# helm install --name nginx-ingress stable/nginx-ingress --set rbac.create=true
|
|
||||||
# # install cert-manager to auto grab lets encrypt certificates
|
|
||||||
# kubectl apply -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.6/deploy/manifests/00-crds.yaml
|
|
||||||
# helm repo update
|
|
||||||
# helm install --name cert-manager --namespace cert-manager stable/cert-manager
|
|
||||||
# configure Let’s Encrypt Issuer
|
|
||||||
# kubectl apply -f docs/docs/examples/kubernetes/issuer.le.prod.yml
|
|
||||||
# kubectl apply -f docs/docs/examples/kubernetes/issuer.le.stage.yml
|
|
||||||
# kubectl get certificate
|
|
||||||
|
|
||||||
# add our policy
|
|
||||||
kubectl create configmap -n pomerium policy --from-literal=POLICY=$(cat policy.example.yaml | base64)
|
|
||||||
|
|
||||||
# create our cryptographically random keys
|
|
||||||
kubectl create secret generic -n pomerium shared-secret --from-literal=shared-secret=$(head -c32 /dev/urandom | base64)
|
kubectl create secret generic -n pomerium shared-secret --from-literal=shared-secret=$(head -c32 /dev/urandom | base64)
|
||||||
kubectl create secret generic -n pomerium cookie-secret --from-literal=cookie-secret=$(head -c32 /dev/urandom | base64)
|
kubectl create secret generic -n pomerium cookie-secret --from-literal=cookie-secret=$(head -c32 /dev/urandom | base64)
|
||||||
|
|
||||||
# load TLS for pomerium services
|
echo "=> initiliaze secrets for TLS wild card certificates $(certificate) and $(certificate-key)"
|
||||||
kubectl create secret generic -n pomerium certificate --from-literal=certificate=$(base64 -i cert.pem)
|
kubectl create secret generic -n pomerium certificate --from-literal=certificate=$(base64 -i cert.pem)
|
||||||
kubectl create secret generic -n pomerium certificate-key --from-literal=certificate-key=$(base64 -i privkey.pem)
|
kubectl create secret generic -n pomerium certificate-key --from-literal=certificate-key=$(base64 -i privkey.pem)
|
||||||
|
|
||||||
# load TLS to ingress
|
echo "=> load TLS to ingress"
|
||||||
kubectl create secret tls -n pomerium pomerium-tls --key privkey.pem --cert cert.pem
|
kubectl create secret tls -n pomerium pomerium-tls --key privkey.pem --cert cert.pem
|
||||||
|
|
||||||
# !!! IMPORTANT !!!
|
echo "=> initiliaze a configmap setting for POLICY from $(policy.example.yaml)"
|
||||||
# YOU MUST CHANGE THE Identity Provider Client Secret
|
kubectl create configmap -n pomerium policy --from-literal=policy=$(cat policy.example.yaml | base64)
|
||||||
# !!! IMPORTANT !!!
|
|
||||||
# kubectl create secret generic -n pomerium idp-client-secret --from-literal=REPLACE_ME
|
|
||||||
|
|
||||||
# Create the proxy & authenticate deployment
|
echo "=> setting $(idp-client-secret), you changed this right? :)"
|
||||||
|
exit 1 # comment out or delete this line once you change the following two settings
|
||||||
|
# kubectl create secret generic -n pomerium idp-client-secret --from-literal=idp-client-secret=REPLACEME
|
||||||
|
# kubectl create secret generic -n pomerium idp-service-account --from-literal=idp-service-account=$(base64 -i gsuite.service.account.json)
|
||||||
|
|
||||||
|
echo "=> apply the proxy, authorize, and authenticate deployment configs"
|
||||||
kubectl apply -f docs/docs/examples/kubernetes/authorize.deploy.yml
|
kubectl apply -f docs/docs/examples/kubernetes/authorize.deploy.yml
|
||||||
kubectl apply -f docs/docs/examples/kubernetes/authenticate.deploy.yml
|
kubectl apply -f docs/docs/examples/kubernetes/authenticate.deploy.yml
|
||||||
kubectl apply -f docs/docs/examples/kubernetes/proxy.deploy.yml
|
kubectl apply -f docs/docs/examples/kubernetes/proxy.deploy.yml
|
||||||
# Create the proxy & authenticate services
|
|
||||||
|
echo "=> apply the proxy, authorize, and authenticate service configs"
|
||||||
kubectl apply -f docs/docs/examples/kubernetes/proxy.service.yml
|
kubectl apply -f docs/docs/examples/kubernetes/proxy.service.yml
|
||||||
kubectl apply -f docs/docs/examples/kubernetes/authenticate.service.yml
|
kubectl apply -f docs/docs/examples/kubernetes/authenticate.service.yml
|
||||||
kubectl apply -f docs/docs/examples/kubernetes/authorize.service.yml
|
kubectl apply -f docs/docs/examples/kubernetes/authorize.service.yml
|
||||||
|
|
||||||
# Create and apply the Ingress; this is GKE specific
|
echo "=> create and apply the Ingress; this is GKE specific"
|
||||||
kubectl apply -f docs/docs/examples/kubernetes/ingress.yml
|
kubectl apply -f docs/docs/examples/kubernetes/ingress.yml
|
||||||
|
|
||||||
# Alternatively, nginx-ingress can be used
|
# Alternatively, nginx-ingress can be used
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue