konnect

package module
v0.23.6 Latest Latest
Warning

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

Go to latest
Published: Jul 9, 2019 License: Apache-2.0 Imports: 5 Imported by: 5

README

Konnect

Kopano Konnect implements an OpenID provider (OP) with integrated web login and consent forms.

Go Report Card

Quickstart

Either download a Konnect binary release from https://download.kopano.io/community/konnect:/ or use the Docker image from https://hub.docker.com/r/kopano/konnectd/ to run Konnect. For details how to run Konnect see below.

Standards supported by Konnect

Konnect provides services based on open standards. To get you an idea what Konnect can do and how you could use it, this section lists the OpenID Connect standards which are implemented.

Furthermore the following extensions/base specifications extend, define and combine the implementation details.

Build dependencies

Make sure you have Go 1.10 or later installed. This assumes your GOPATH is ~/go and you have ~/go/bin in your $PATH and you have Dep installed as well.

Konnect also includes a modern web app which requires a couple of additional build dependencies which are furthermore also assumed to be in your $PATH.

To build Konnect, a Makefile is provided, which requires make.

When building, third party dependencies will tried to be fetched from the Internet if not there already.

Building from source

mkdir -p ~/go/src/stash.kopano.io/kc
cd ~/go/src/stash.kopano.io/kc
git clone <THIS-PROJECT> konnect
cd konnect
make
Optional build dependencies

Some optional build dependencies are required for linting and continous integration. Those tools are mostly used by make to perform various tasks and are expected to be found in your $PATH.

Running Konnect

Konnect can provide user login with Kopano Groupware Storage server as backend, use a cookie aware web login area which supports the ?continue parameter, or also can directly connect to a LDAP server.

All backends require certain general parameters to be present. Create a RSA key-pair file with openssl genpkey -algorithm RSA -out private-key.pem -pkeyopt rsa_keygen_bits:4096 and provide the key file with the --signing-private-key parameter. Konnect can load PEM encoded PKCS#1 and PKCS#8 key files and JSON Web Keys from .json files If you skip this, Konnect will create a random non-persistent RSA key on startup.

To encrypt certain values, Konnect needs a secure encryption key. Create a suitable key of 32 bytes with openssl rand -out encryption.key 32 and provide the full path to that file via the --encryption-secret parameter. If you skip this, Konnect will generate a random key on startup.

To run a functional OpenID Connect provider, an issuer identifier is required. The iss is a full qualified https:// URI pointing to the web server which serves the requests to Konnect (example: https://example.com). Provide the Issuer Identifier with the --iss parametter when starting Konnect.

Furthermore to allow clients to utilize the Konnect services, clients need to be known/registered. For now Konnect uses a static configuration file which allows clients and their allowed urls to be registered. See the the example at identifier-registration.yaml.in. Copy and modify that file to include all the clients which should be able to use OpenID Connect and/or OAuth2 and start Konnect with the --identifier-registration-conf parameter pointing to that file. Without any explicitly registered clients, Konnect will only accept clients which redirect to an URI which starts with the value provided with the --iss parameter.

Konnect cryptography and validation

A tool can be used to create keys for Konnect and also to validate tokens to ensure correct operation is Step CLI. This helps since OpenSSL is not able to create or validate all of the different key formats, ciphers and curves which are supported by Konnect.

Here are some examples relevant for Konnect.

step crypto keypair 1-rsa.pub 1-rsa.pem \
  --kty RSA --size 4096 --no-password --insecure
step crypto keypair 1-ecdsa-p-256.pub 1-ecdsa-p-256.pem \
  --kty EC --curve P-256 --no-password --insecure
step crypto jwk create 1-eddsa-ed25519.pub.json 1-eddsa-ed25519.key.json \
  -kty OKP --crv Ed25519 --no-password --insecure
echo $TOKEN_VALUE | step crypto jwt verify --iss $ISS \
  --aud playground-trusted.js --jwks $ISS/konnect/v1/jwks.json

URL endpoints

Take a look at Caddyfile.example on the URL endpoints provided by Konnect and how to expose them through a TLS proxy.

The base URL of the frontend proxy is what will become the value of the --iss parameter when starting up Konnect. OIDC requires the Issuer Identifier to be secure (https:// required).

Kopano Groupware Storage server backend

This assumes that Konnect can connect directly to a Kopano server via SOAP either using a unix socket or a TCP connection.

Kopano Groupware Storage server backend connections can either use a dedicated service connection which might require a TLS certificate or a certain unix user to access the unix socket (not recommended) or bind the Konnect session and tokens to the underlaying Groupware Storage server's session (default).

export KOPANO_SERVER_DEFAULT_URI=http://mykopano.local:236

bin/konnectd serve --listen=127.0.0.1:8777 \
  --iss=https://mykonnect.local \
  kc

Give dedicated user session credentials via environment variables as shown in the example below.

export KOPANO_SERVER_DEFAULT_URI=http://mykopano.local:236
export KOPANO_SERVER_USERNAME=my-kopano-user
export KOPANO_SERVER_PASSWORD=my-kopano-password

bin/konnectd serve --listen=127.0.0.1:8777 \
  --iss=https://mykonnect.local \
  kc

Or run Konnect as local_admin_unix user. Only run konnectd like this when you actually use that authentication scheme. Otherwise ensure that Konnect is not running as local admin user for best security.

su - kopano
export KOPANO_SERVER_DEFAULT_URI=file:///run/kopano/server.sock
export KOPANO_SERVER_USERNAME=SYSTEM

bin/konnectd serve --listen=127.0.0.1:8777 \
  --iss=https://mykonnect.local \
  kc

In some situations the only option is to authenticate to the backend by providing TLS client certificate and private key via environment variables.

export KOPANO_SERVER_DEFAULT_URI=https://mykopano.local:237
export KOPANO_SERVER_USERNAME=SYSTEM
export KOPANO_CLIENT_CERTIFICATE=/path/to/client-tls/client-0.pem
export KOPANO_CLIENT_PRIVATE_KEY=/path/to/client-tls/client-0.key

bin/konnectd serve --listen=127.0.0.1:8777 \
  --iss=https://mykonnect.local \
  kc
LDAP backend

This assumes that Konnect can directly connect to an LDAP server via TCP.

export LDAP_URI=ldap://myldap.local:389
export LDAP_BINDDN="cn=admin,dc=example,dc=local"
export LDAP_BINDPW="its-a-secret"
export LDAP_BASEDN="dc=example,dc=local"
export LDAP_SCOPE=sub
export LDAP_LOGIN_ATTRIBUTE=uid
export LDAP_EMAIL_ATTRIBUTE=mail
export LDAP_NAME_ATTRIBUTE=cn
export LDAP_UUID_ATTRIBUTE=uidNumber
export LDAP_UUID_ATTRIBUTE_TYPE=text
export LDAP_FILTER="(objectClass=organizationalPerson)"

bin/konnectd serve --listen=127.0.0.1:8777 \
  --iss=https://mykonnect.local \
  ldap

A cookie backend is also there for testing. It has limited amount of features and should not be used in production. Essentially this backend assumes a login area uses a HTTP cookie for authentication and Konnect is runnig in the same scope as this cookie so the Konnect request can read and validate the cookie using an internal proxy request.

This assumes that you have a set-up Kopano with a reverse proxy on https://mykopano.local together with the proper proxy configuration to pass through all requests to the /konnect/v1/ prefix to 127.0.0.1:8777. Kopano Webapp supports the ?continue= request parameter and the domains of possible OIDC clients need to be added into webapp/config.php with the REDIRECT_ALLOWED_DOMAINS setting.

bin/konnectd serve --listen=127.0.0.1:8777 \
  --iss=https://mykopano.local \
  --sign-in-uri=https://mykopano.local/webapp/ \
  cookie https://mykopano.local/webapp/?load=custom&name=oidcuser "KOPANO_WEBAPP encryption-store-key"
Run with Docker

Kopano Konnect supports Docker to easily be run inside a container. Running with Docker supports all features of Kopano Konnect and can make use of Docker Secrets to manage sensitive data like keys.

Kopano provides official Docker images for Konnect.

docker pull kopano/konnectd
Run Konnect with Docker Swarm

Setup the Docker container in swarm mode like this:

cat /etc/kopano/konnectd-tokens-signing-key.pem | docker secret create konnectd_signing_private_key -
openssl rand 32 | docker secret create konnectd_encryption_secret -
docker service create \
	--read-only \
	--user=$(id -u kopano) \
	--group=$(id -g kopano) \
	--mount type=bind,source=/etc/ssl/certs,target=/etc/ssl/certs,readonly \
	--secret konnectd_signing_private_key \
	--secret konnectd_encryption_secret \
	--env KOPANO_SERVER_DEFAULT_URI=file:///run/kopano/server.sock \
	--mount type=bind,source=/run/kopano,target=/run/kopano \
	--publish published=8777,target=8777,mode=host \
	--name=konnectd \
	kopano/konnectd \
	serve \
	--iss=https://mykonnect.local \
	kc

This example assumes the local system has a user kopano which can access the Kopano Groupware Core unix socket as admin user SYSTEM.

Run Konnect from Docker image
openssl rand 32 -out /etc/kopano/konnectd-encryption-secret.key
docker run --rm=true --name=konnectd \
	--read-only \
	--user=$(id -u kopano):$(id -g kopano) \
	--volume /etc/ssl/certs:/etc/ssl/certs:ro \
	--volume /etc/kopano/konnectd-tokens-signing-key.pem:/run/secrets/konnectd_signing_private_key:ro \
	--volume /etc/kopano/konnectd-encryption.key:/run/secrets/konnectd_encryption_secret:ro \
	--env KOPANO_SERVER_DEFAULT_URI=file:///run/kopano/server.sock \
	--volume /run/kopano:/run/kopano:rw \
	--publish 127.0.0.1:8777:8777 \
	kopano/konnectd \
	serve \
	--iss=https://mykonnect.local \
	kc

Of course modify the paths and ports according to your requirements. The Docker examples are for the kc identity manager, but work for the others as well if you adapt the parameters and environment variables. The above example assumes the local system has a user kopano which can access the Kopano Groupware Core unix socket as admin user SYSTEM.

Build Konnect Docker image

This project includes a Dockerfile which can be used to build a Docker container from the locally build version. Similarly the Dockerfile.release builds the Docker image locally from the latest release download.

docker build -t kopano/konnectd .
docker build -f Dockerfile.release -t kopano/konnectd .

Run unit tests

cd ~/go/src/stash.kopano.io/kc/konnect
make test

Development

As Konnect includes a web application (identifier), a Caddyfile.dev file is provided which exposes the identifier's web application directly via a webpack dev server.

Debugging

Konnect is built stripped and without debug symbols by default. To build for debugging, compile with additional environment variables which override/reset build optimization like this

LDFLAGS="" GCFLAGS="all=-N -l" ASMFLAGS="" make cmd/konnectd

The resulting binary is not stripped and sutiable to be debugged with Delve.

To connect Delve to a running Konnect binary you can use the make dlv command. Control its behavior via DLV_* environment variables. See the Makefile source for details.

DLV_ARGS= make dlv
Remote debugging

To use remote debugging, pass additional args like this.

DLV_ARGS=--listen=:2345 make dlv
IDE integration for development and debugging

Some editors like VSCode are offering integrated debugger support for Go based software via extensions. Konnect can be started directly from such an IDE by pointing the IDE to the corresponding cmd which should be debugged. This is most likely cmd/konnect.

Example .vscode/launch.json for building and running konnectd directly from VSCode:

{
	"version": "0.2.0",
	"configurations": [
		{
			"name": "Run Konnect kc",
			"type": "go",
			"request": "launch",
			"mode": "debug",
			"program": "${workspaceFolder}/cmd/konnectd",
			"args": [
				"serve",
				"--listen=0.0.0.0:8777",
				"--iss=https://your-issuer:8443",
				"--log-level=debug",
				"--identifier-client-path=${workspaceFolder}/identifier/build",
				"--identifier-registration-conf=${workspaceFolder}/identifier-registration.yaml",
				"--identifier-scopes-conf=${workspaceFolder}/scopes.yaml",
				"--signing-method=ES256",
				"--signing-private-key=${workspaceFolder}/examples/keys/example-1-ecdsa-p-256.pem",
				"--encryption-secret=${workspaceFolder}/examples/encryption.key",
				"--validation-keys-path=${workspaceFolder}/examples/keys",
				"--allow-dynamic-client-registration",
				"--allow-client-guests",
				"kc",
			],
			"env": {
				"KOPANO_SERVER_DEFAULT_URI": "http://127.0.0.1:236",
			},
		},
		{
			"name": "Attach",
			"type": "go",
			"request": "launch",
			"remotePath": "${workspaceFolder}/.gopath/src/stash.kopano.io/kc/konnect",
			"mode": "remote",
			"port": 2345,
			"host": "127.0.0.1",
			"program": "${workspaceFolder}",
		},
	]
}

The Run konnect kc launcher builds and runs konnectd with the debugger attached to it directly, while the Attach launcher connects to a remote debugger.

See https://github.com/go-delve/delve/blob/master/Documentation/EditorIntegration.md for further examples and details.

Documentation

Overview

Package konnect is a Go implementation of an OpenID Connect server with flexibale authorization and authentication backends and consent screen.

See README.md for more info.

Index

Constants

View Source
const (
	IsAccessTokenClaim    = "kc.isAccessToken"
	AuthorizedScopesClaim = "kc.authorizedScopes"
	IsRefreshTokenClaim   = "kc.isRefreshToken"
	RefClaim              = "kc.ref"
	IdentityClaim         = "kc.identity"
	IdentityProvider      = "kc.provider"
)

Access token claims used by Konnect.

View Source
const (
	IdentifiedUserClaim        = "kc.i.us"
	IdentifiedUserIDClaim      = "kc.i.id"
	IdentifiedUsernameClaim    = "kc.i.un"
	IdentifiedDisplayNameClaim = "kc.i.dn"
	IdentifiedData             = "kc.i.da"
	IdentifiedUserIsGuest      = "kc.i.guest"
)

Identifier identity sub claims used by Konnect.

View Source
const (
	// ScopeID is the string value for the Konnect ID scope.
	ScopeID = "konnect/id"
	// ScopeUniqueUserID is the string value for the Konnect Unique User ID scope.
	ScopeUniqueUserID = "konnect/uuid"

	// ScopeRawSubject is the string value for the Konnect Raw Subject scope.
	ScopeRawSubject = "konnect/raw_sub"

	// ScopeGuestOK is the string value for the Konnect Guest OK scope.
	ScopeGuestOK = "konnect/guestok"
)

Variables

This section is empty.

Functions

func FromClaimsContext

func FromClaimsContext(ctx context.Context) (jwt.Claims, bool)

FromClaimsContext returns the AuthRecord value stored in ctx, if any.

func NewClaimsContext

func NewClaimsContext(ctx context.Context, claims jwt.Claims) context.Context

NewClaimsContext returns a new Context that carries value auth.

Types

type AccessTokenClaims

type AccessTokenClaims struct {
	jwt.StandardClaims

	IsAccessToken           bool                   `json:"kc.isAccessToken"`
	AuthorizedScopesList    []string               `json:"kc.authorizedScopes"`
	AuthorizedClaimsRequest *payload.ClaimsRequest `json:"kc.authorizedClaims,omitempty"`

	IdentityClaims   jwt.MapClaims `json:"kc.identity"`
	IdentityProvider string        `json:"kc.provider,omitempty"`
}

AccessTokenClaims define the claims found in access tokens issued by Konnect.

func (AccessTokenClaims) AuthorizedScopes

func (c AccessTokenClaims) AuthorizedScopes() map[string]bool

AuthorizedScopes returns a map with scope keys and true value of all scopes set in the accociated access token.

func (AccessTokenClaims) Valid

func (c AccessTokenClaims) Valid() error

Valid implements the jwt.Claims interface.

type AccessTokenProvider

type AccessTokenProvider interface {
	MakeAccessToken(ctx context.Context, audience string, auth identity.AuthRecord) (string, error)
}

AccessTokenProvider is an interface for something which can create access tokens.

type IDClaims

type IDClaims struct {
	// NOTE(longsleep): Always keep these claims compatible with the GitLab API
	// https://docs.gitlab.com/ce/api/users.html#for-user.
	KCID         int64  `json:"id,omitempty"`
	KCIDUsername string `json:"username,omitempty"`
}

IDClaims define the claims used with the konnect/id scope.

func (IDClaims) Valid

func (c IDClaims) Valid() error

Valid implements the jwt.Claims interface.

type RefreshTokenClaims

type RefreshTokenClaims struct {
	jwt.StandardClaims

	IsRefreshToken        bool                   `json:"kc.isRefreshToken"`
	ApprovedScopesList    []string               `json:"kc.approvedScopes"`
	ApprovedClaimsRequest *payload.ClaimsRequest `json:"kc.approvedClaims,omitempty"`
	Ref                   string                 `json:"kc.ref"`

	IdentityClaims   jwt.MapClaims `json:"kc.identity"`
	IdentityProvider string        `json:"kc.provider,omitempty"`
}

RefreshTokenClaims define the claims used by refresh tokens.

func (RefreshTokenClaims) Valid

func (c RefreshTokenClaims) Valid() error

Valid implements the jwt.Claims interface.

type UniqueUserIDClaims

type UniqueUserIDClaims struct {
	KCUniqueUserID string `json:"kc.uuid,omitempty"`
}

UniqueUserIDClaims define the claims used with the konnect/uuid scope.

func (UniqueUserIDClaims) Valid

func (c UniqueUserIDClaims) Valid() error

Valid implements the jwt.Claims interface.

type UserInfoResponse

type UserInfoResponse struct {
	*payload.UserInfoResponse

	*IDClaims
	*UniqueUserIDClaims
}

UserInfoResponse defines the data returned from the Konnect UserInfo endpoint. It is the standard ODIC response, extended with additional claims.

Jump to

Keyboard shortcuts

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