saml

package
v1.2.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 10, 2021 License: Apache-2.0 Imports: 21 Imported by: 1

README

SAML Security

This package contains functions and types for enabling SAML as a security mechanism in a microservice.

Setting up a SAML security

There are a couple of things you need to do to enable the SAML security middleware. For details on SAML you can find many resources on the official site: http://saml.xml.org/.

Setting up the secret keys

Service providers should use the same saml keys as SAML IdP.

In order to create them create a directory in which you'll keep your key-pair:

mkdir saml-keys
cd saml-keys

Each service provider must have an self-signed X.509 key pair established. You can generate your own with something like this:

openssl req -x509 -newkey rsa:2048 -keyout service.key -out service.cert -days 365 -nodes -subj "/CN=myservice.example.com"

NOTE: Make sure you have service.key and service.cert files in the saml-keys directory

Set up SAML in Goa

Create a security file app/security.go with the following content:

package app

import (
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"net/url"
	"os"

	"github.com/crewjam/saml/samlsp"
)

// NewSAMLSecurity creates a SAML security definition.
func NewSAMLSecurityMiddleware(cert, key string) *samlsp.Middleware {
	keyPair, err := tls.LoadX509KeyPair(cert, key)
	if err != nil {
		panic(err)
	}

	keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0])
	if err != nil {
		panic(err)
	}

	gatewayURL := os.Getenv("API_GATEWAY_URL")
	if gatewayURL == "" {
		gatewayURL = "https://localhost:8082"
	}

	rootURL, err := url.Parse(fmt.Sprintf("%s/users", gatewayURL))
	if err != nil {
		panic(err)
	}

	idpMetadataURL, err := url.Parse(fmt.Sprintf("%s/saml/idp/metadata", gatewayURL))
	if err != nil {
		panic(err)
	}

	samlSP, err := samlsp.New(samlsp.Options{
		IDPMetadataURL: idpMetadataURL,
		URL:            *rootURL,
		Key:            keyPair.PrivateKey.(*rsa.PrivateKey),
		Certificate:    keyPair.Leaf,
	})

	return samlSP
}

In the config.json file map of URLs for the internal services:

{
	other config info here,
 	"services": {
		"microservice-registration": "https://kong:8000/registration",
		"microservice-user": "http://kong:8000/users"
	}
}

In order to use Google as IdP you must use https over http. More details:

More details on how to configure the SAML security are available on the following site:

Setting up a SecurityChain

You need to set up a security chain for the microservice.

In the main.go file of your microservice, set up the SAML Security Chain middleware and add it to the security chain.


import (
	"github.com/Microkubes/microservice-security/saml"
	"github.com/Microkubes/microservice-security/chain"
)

func main() {
	// Create new SAML security chain
  	// "saml-keys" is the directory containing the keys
  	spMiddleware := app.NewSAMLSecurityMiddleware("saml-keys/service.cert", "saml-keys/service.key")
	SAMLMiddleware := saml.NewSAMLSecurity(spMiddleware)
	sc := chain.NewSecurityChain().AddMiddleware(SAMLMiddleware)

    // other initializations here...

    service.Use(chain.AsGoaMiddleware(sc)) // attach the security chain as Goa middleware

	// Send SP Metadata to the SAML IdP
	unregisterSP, err := saml.RegisterSP(spMiddleware)
	if err != nil {
		service.LogError("setupSAML", "err", err)
		return
	}
	defer unregisterSP()
}

Testing the setup

To test the setup, you'll need to generate and sign a JWT token.

Example of the JWT token:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjgwODIvc2FtbC9tZXRhZGF0YSIsImF0dHIiO
nsib3JnYW5pemF0aW9ucyI6Ik96cmcxLCBPcmcyIiwicm9sZXMiOiJ1c2VyLCBhZG1pbiIsInVzZXJJZCI6IjU5YTAwNmFlMDAwMDAwMDA
wMDAwMDAwMCIsInVzZXJuYW1lIjoidGVzdC11c2VyIn19.vLl5hWsbYDSybhokeA4sFKJnZznesiUje5tzsCYZzl4

(Note that the JWT is actually one line. For readability purposes it is displayed here in multiple lines.)

Then you will need to set up the cookie named "saml_token":

 curl -v -b "saml_token=eyJhbGciO...<full token here>...zsCYZzl4" http://localhost:8082/profiles/me

Documentation

Index

Constants

View Source
const (
	// SAMLSecurityType is the name of the security type (JWT, OAUTH2, SAML...)
	SAMLSecurityType = "SAML"
	// CookieName name for saml token
	CookieName = "token"
)

Variables

This section is empty.

Functions

func NewSAMLSecurity

func NewSAMLSecurity(spMiddleware *samlsp.Middleware, samlConf *config.SAMLConfig) chain.SecurityChainMiddleware

NewSAMLSecurity creates a SAML SecurityChainMiddleware using RSA private key.

func NewSAMLSecurityMiddleware

func NewSAMLSecurityMiddleware(spMiddleware *samlsp.Middleware, samlConfig *config.SAMLConfig) goa.Middleware

NewSAMLSecurityMiddleware creates a middleware that checks for the presence of a cookie and validates its content. It also serve SP metadata on /saml/metadata route and SAML Assertion Consumer Service on /saml/acs route.

func RedirectUser

func RedirectUser(spMiddleware *samlsp.Middleware, rw http.ResponseWriter, req *http.Request)

RedirectUser redirects user to the IdP that is set in the metadata

func RegisterSP

func RegisterSP(spMiddleware *samlsp.Middleware, conf *config.SAMLConfig) (func(), error)

RegisterSP sends SP metadata to the SAML IdP

func UnregisterSP

func UnregisterSP(spMiddleware *samlsp.Middleware, conf *config.SAMLConfig)

UnregisterSP deletes SP from SAML IdP

Types

type EmailPayload

type EmailPayload struct {
	// Email of the user
	Email string
}

EmailPayload holds the email payload

type TokenClaims

type TokenClaims struct {
	jwt.StandardClaims
	Attributes map[string][]string `json:"attr"`
}

TokenClaims SAML claims

type UserPayload

type UserPayload struct {
	// Status of user account
	Active bool `form:"active" json:"active" xml:"active"`
	// Email of user
	Email string `form:"email" json:"email" xml:"email"`
	// External id of user
	ExternalID string `form:"externalId,omitempty" json:"externalId,omitempty" xml:"externalId,omitempty"`
	// Full name of user
	Fullname string `form:"fullname" json:"fullname" xml:"fullname"`
	// Roles of user
	Roles []string `form:"roles" json:"roles" xml:"roles"`
}

UserPayload is the user payload

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL