jwks

package module
v0.2.1 Latest Latest
Warning

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

Go to latest
Published: Aug 17, 2023 License: Apache-2.0 Imports: 16 Imported by: 1

README

go-jwks

build Go Reference Go Report Card

go-jwks is a comprehensive library for de/serialising JWK[S] to PEMs, and Go's crypto.[Public,Private]Key types.

pem2jwks binary

The pem2jwks command converts public keys in PEM format (typically used to sign JWTs) to the JWKS format usually required by software that validates them.

Run from container image:

cat key.pem | docker run -i --rm ghcr.io/mt-inside/pem2jwks:v0.2.0

Download single, statically-linked binary

wget -O pem2jwks https://github.com/mt-inside/go-jwks/releases/download/v0.2.0/pem2jwks-$(uname -s)-$(uname -m)
chmod u+x pem2jwks
cat key.pem | ./pem2jwks

Install from source

go install github.com/mt-inside/go-jwks/cmd/pem2jwks@latest
cat key.pem | ${GOPATH}/bin/pem2jwks
Alternatives
  • pem-to-jwk - JavaScript, last commit in 2016, uses string manipulation. Only works on EC keys? Only takes private keys as input? Only emits individual JWKs.
  • pem-jwk - JavaScript, last commit in 2018, uses string manipulation. Only works on RSA keys? Only takes public keys? Only emits individual JWKs.
Istio JWT Auth Example

Generate a keypair, which will be used to sign JWTs and verify them

openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -pubout -out public.pem

Use the private key, in PEM form, to sign the JWT

go install github.com/golang-jwt/jwt/v5/cmd/jwt@latest
echo '{"sub": "one", "iss": "example.local", "iat": 1234567890, "exp": 2345678901}' | jwt -key private.pem -alg RS256 -sign - > one.jwt

Configure Istio to do authN of requests. JWTs will have their signature checked against the public part of the key, which needs to be in JWKS format.

cat public.pem | pem2jwks | jq . > keystore.jwks

kubectl apply -f - << EOF
apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
metadata:
  name: jwt-example
spec:
  selector:
    matchLabels:
      app: http-log
  jwtRules:
    - issuer: "example.local"
      outputPayloadToHeader: "x-end-user"
      forwardOriginalToken: true
      jwks: |
$(cat keystore.jwks | sed 's/^/        /')
EOF

Configure some request authZ rules

  • Only logged-in users can access paths by default (ie anyone with a JWT with valid signature and matching our issuer)
  • Allow anyone to access /public, logged-in or not
  • Allow only the user one to access /admin
kubectl apply -f - << EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: allow-all-public
spec:
  selector:
    matchLabels:
      app: http-log
  action: ALLOW
  rules:
    - to:
        - operation:
            paths: ["/public"]
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: allow-authd-all
spec:
  selector:
    matchLabels:
      app: http-log
  action: ALLOW
  rules:
    - from:
        - source:
            requestPrincipals: ["*"]
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: allow-one-admin
spec:
  selector:
    matchLabels:
      app: http-log
  action: DENY
  rules:
    - from:
        - source:
            notRequestPrincipals: ["example.local/one"]
      to:
        - operation:
            paths: ["/admin"]
EOF

Requests should pass the signed JWT in the :authorization header.

curlie http://$URL/admin
token="$(cat one.jwt | tr -d '\n')"
curlie http://$URL/admin "Authorization: Bearer $token"

Documentation

Overview

On type wrangling in Go:

* Go doesn't allow "extension methods", ie we can't add methods to other package's types, so we can't add MarshalJSON to rsa.PublicKey * Hence, we alias those types and impl the marshal funcs on our aliases * Often you'll hold a variable typed as the crypto.[Public,Private]Key interface, and want to marshal that. * That iface doesn't include MarshalJSON, so again we wanna add it. * However we can't even alias the interface and do it that way, because you can't have iface receivers. * Hence, functions like these at the top that go from the stdlib iface to one of our concrete impls.

TODO

* - X test with istio demo master (move to containerimage) * - X rearrange to lib at top * - X to melange & apko; test * - X test all Just targets * - X tests stable * - X fix up test_keys dir * - X doc.go (explain the ortho of the funcs, eg to/from json vs to/from an [un]marshaler * - README.md * - api comments. Turn linting up to 11 * - X links in the readme like to godoc one * - X check gh action & its outputs * - X tag 0.2 (1.0?)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func JWK2Key

func JWK2Key(j []byte) (any, error)

func JWK2PEM

func JWK2PEM(j []byte) ([]byte, error)

func JWKS2Keys

func JWKS2Keys(j []byte) ([]any, error)

func JWKS2KeysMap

func JWKS2KeysMap(j []byte) (map[string]any, error)

func JWKS2PEM

func JWKS2PEM(j []byte) ([]byte, error)

func Key2JWK

func Key2JWK(k any) (string, error)

func KeyIsPrivate

func KeyIsPrivate(key any) bool

func KeyPublicPart

func KeyPublicPart(key any) crypto.PublicKey

func Keys2JWKS

func Keys2JWKS(ks []any) (string, error)

func Keys2PEM

func Keys2PEM(ks []any) ([]byte, error)

func PEM2JWK

func PEM2JWK(p []byte) (string, error)

func PEM2JWKS

func PEM2JWKS(p []byte) (string, error)

func PEM2Keys

func PEM2Keys(p []byte) ([]any, error)

Types

type JWK

type JWK struct {
	KeyID string
	Key   any
}

func Key2JWKMarshaler

func Key2JWKMarshaler(k any) (*JWK, error)

This does a bit more than the JWKS-version because - needs to check for JWK-unsupported key types. - does the public-part extraction. When we have generics we can do it at render time? No! If we want one type, that won't encode whether we should do it, so we need to do so here at ctor time.

  • TODO factor out to inisial call

TODO: extra key types (wait for go 1.21; this API is being sorted apaz)

func PEM2JWKMarshaler

func PEM2JWKMarshaler(p []byte) (*JWK, error)

func (*JWK) MarshalJSON

func (k *JWK) MarshalJSON() ([]byte, error)

func (*JWK) UnmarshalJSON

func (p *JWK) UnmarshalJSON(data []byte) error

type JWKS

type JWKS struct {
	Keys []*JWK `json:"keys"`
}

func Keys2JWKSMarshaler

func Keys2JWKSMarshaler(ks []any) (*JWKS, error)

func PEM2JWKSMarshaler

func PEM2JWKSMarshaler(p []byte) (*JWKS, error)

Directories

Path Synopsis
cmd
pem2jwks
* pem2jwks * This is the root command, so it's an easy `go install .../mt-inside/pem2jwks`
* pem2jwks * This is the root command, so it's an easy `go install .../mt-inside/pem2jwks`
examples
internal

Jump to

Keyboard shortcuts

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