Skip to content

Commit

Permalink
Merge pull request #992 from ripienaar/jwt_anon_auth
Browse files Browse the repository at this point in the history
(#987) anon tls requires JWTs
  • Loading branch information
ripienaar authored Sep 12, 2020
2 parents ba746f3 + f7932e2 commit 4988d80
Show file tree
Hide file tree
Showing 14 changed files with 600 additions and 65 deletions.
53 changes: 30 additions & 23 deletions CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,29 +59,30 @@ A few special types are defined, the rest map to standard Go types
|[plugin.choria.require_client_filter](#pluginchoriarequire_client_filter)|[plugin.choria.security.certname_whitelist](#pluginchoriasecuritycertname_whitelist)|
|[plugin.choria.security.privileged_users](#pluginchoriasecurityprivileged_users)|[plugin.choria.security.request_signer.token_environment](#pluginchoriasecurityrequest_signertoken_environment)|
|[plugin.choria.security.request_signer.token_file](#pluginchoriasecurityrequest_signertoken_file)|[plugin.choria.security.request_signer.url](#pluginchoriasecurityrequest_signerurl)|
|[plugin.choria.security.serializer](#pluginchoriasecurityserializer)|[plugin.choria.server.provision](#pluginchoriaserverprovision)|
|[plugin.choria.srv_domain](#pluginchoriasrv_domain)|[plugin.choria.ssldir](#pluginchoriassldir)|
|[plugin.choria.stats_address](#pluginchoriastats_address)|[plugin.choria.stats_port](#pluginchoriastats_port)|
|[plugin.choria.status_file_path](#pluginchoriastatus_file_path)|[plugin.choria.status_update_interval](#pluginchoriastatus_update_interval)|
|[plugin.choria.use_srv](#pluginchoriause_srv)|[plugin.nats.credentials](#pluginnatscredentials)|
|[plugin.nats.ngs](#pluginnatsngs)|[plugin.nats.pass](#pluginnatspass)|
|[plugin.nats.user](#pluginnatsuser)|[plugin.scout.overrides](#pluginscoutoverrides)|
|[plugin.scout.tags](#pluginscouttags)|[plugin.security.always_overwrite_cache](#pluginsecurityalways_overwrite_cache)|
|[plugin.security.certmanager.alt_names](#pluginsecuritycertmanageralt_names)|[plugin.security.certmanager.issuer](#pluginsecuritycertmanagerissuer)|
|[plugin.security.certmanager.namespace](#pluginsecuritycertmanagernamespace)|[plugin.security.certmanager.replace](#pluginsecuritycertmanagerreplace)|
|[plugin.security.cipher_suites](#pluginsecuritycipher_suites)|[plugin.security.client_anon_tls](#pluginsecurityclient_anon_tls)|
|[plugin.security.ecc_curves](#pluginsecurityecc_curves)|[plugin.security.file.ca](#pluginsecurityfileca)|
|[plugin.security.file.cache](#pluginsecurityfilecache)|[plugin.security.file.certificate](#pluginsecurityfilecertificate)|
|[plugin.security.file.key](#pluginsecurityfilekey)|[plugin.security.pkcs11.driver_file](#pluginsecuritypkcs11driver_file)|
|[plugin.security.pkcs11.slot](#pluginsecuritypkcs11slot)|[plugin.security.provider](#pluginsecurityprovider)|
|[plugin.yaml](#pluginyaml)|[publish_timeout](#publish_timeout)|
|[registerinterval](#registerinterval)|[registration](#registration)|
|[registration_collective](#registration_collective)|[registration_splay](#registration_splay)|
|[rpcaudit](#rpcaudit)|[rpcauditprovider](#rpcauditprovider)|
|[rpcauthorization](#rpcauthorization)|[rpcauthprovider](#rpcauthprovider)|
|[rpclimitmethod](#rpclimitmethod)|[securityprovider](#securityprovider)|
|[soft_shutdown](#soft_shutdown)|[soft_shutdown_timeout](#soft_shutdown_timeout)|
|[threaded](#threaded)|[ttl](#ttl)|
|[plugin.choria.security.request_signing_certificate](#pluginchoriasecurityrequest_signing_certificate)|[plugin.choria.security.serializer](#pluginchoriasecurityserializer)|
|[plugin.choria.server.provision](#pluginchoriaserverprovision)|[plugin.choria.srv_domain](#pluginchoriasrv_domain)|
|[plugin.choria.ssldir](#pluginchoriassldir)|[plugin.choria.stats_address](#pluginchoriastats_address)|
|[plugin.choria.stats_port](#pluginchoriastats_port)|[plugin.choria.status_file_path](#pluginchoriastatus_file_path)|
|[plugin.choria.status_update_interval](#pluginchoriastatus_update_interval)|[plugin.choria.use_srv](#pluginchoriause_srv)|
|[plugin.nats.credentials](#pluginnatscredentials)|[plugin.nats.ngs](#pluginnatsngs)|
|[plugin.nats.pass](#pluginnatspass)|[plugin.nats.user](#pluginnatsuser)|
|[plugin.scout.overrides](#pluginscoutoverrides)|[plugin.scout.tags](#pluginscouttags)|
|[plugin.security.always_overwrite_cache](#pluginsecurityalways_overwrite_cache)|[plugin.security.certmanager.alt_names](#pluginsecuritycertmanageralt_names)|
|[plugin.security.certmanager.issuer](#pluginsecuritycertmanagerissuer)|[plugin.security.certmanager.namespace](#pluginsecuritycertmanagernamespace)|
|[plugin.security.certmanager.replace](#pluginsecuritycertmanagerreplace)|[plugin.security.cipher_suites](#pluginsecuritycipher_suites)|
|[plugin.security.client_anon_tls](#pluginsecurityclient_anon_tls)|[plugin.security.ecc_curves](#pluginsecurityecc_curves)|
|[plugin.security.file.ca](#pluginsecurityfileca)|[plugin.security.file.cache](#pluginsecurityfilecache)|
|[plugin.security.file.certificate](#pluginsecurityfilecertificate)|[plugin.security.file.key](#pluginsecurityfilekey)|
|[plugin.security.pkcs11.driver_file](#pluginsecuritypkcs11driver_file)|[plugin.security.pkcs11.slot](#pluginsecuritypkcs11slot)|
|[plugin.security.provider](#pluginsecurityprovider)|[plugin.yaml](#pluginyaml)|
|[publish_timeout](#publish_timeout)|[registerinterval](#registerinterval)|
|[registration](#registration)|[registration_collective](#registration_collective)|
|[registration_splay](#registration_splay)|[rpcaudit](#rpcaudit)|
|[rpcauditprovider](#rpcauditprovider)|[rpcauthorization](#rpcauthorization)|
|[rpcauthprovider](#rpcauthprovider)|[rpclimitmethod](#rpclimitmethod)|
|[securityprovider](#securityprovider)|[soft_shutdown](#soft_shutdown)|
|[soft_shutdown_timeout](#soft_shutdown_timeout)|[threaded](#threaded)|
|[ttl](#ttl)|[](#)|


## activate_agents
Expand Down Expand Up @@ -631,6 +632,12 @@ Path to the token used to access a Central Authenticator

URL to the Signing Service

## plugin.choria.security.request_signing_certificate

* **Type:** string

The public certificate of the key used to sign the JWTs in the Signing Service

## plugin.choria.security.serializer

* **Type:** string
Expand Down
135 changes: 126 additions & 9 deletions broker/network/ipauth.go
Original file line number Diff line number Diff line change
@@ -1,46 +1,165 @@
package network

import (
"crypto/md5"
"crypto/rsa"
"fmt"
"io/ioutil"
"net"
"strings"

"github.com/dgrijalva/jwt-go"
"github.com/nats-io/nats-server/v2/server"
"github.com/sirupsen/logrus"
)

// IPAuth implements gnatsd server.Authentication interface and
// IPAuth implements Nats Server server.Authentication interface and
// allows IP limits to be configured, connections that do not match
// the configured IP or CIDRs are not allowed to publish to the
// network targets used by clients to request actions on nodes
// network targets used by clients to request actions on nodes.
//
// Additionally when the server is running in a mode where anonymous
// TLS connections is accepted then servers are entirely denied and
// clients are allowed but restricted based on the JWT issued by the
// AAA Service.
type IPAuth struct {
allowList []string
anonTLS bool
denyServers bool
jwtSigner string
log *logrus.Entry
}

// Check checks and registers the incoming connection
func (a *IPAuth) Check(c server.ClientAuthentication) (verified bool) {
user := a.createUser(c)

remote := c.RemoteAddress()
if remote != nil && !a.remoteInClientAllowList(c.RemoteAddress()) {
jwts := c.GetOpts().Token
caller := ""

var err error

if a.anonTLS {
if remote == nil {
a.log.Warn("Denying unknown remote client while in AnonTLS mode")
return false
}

caller, err = a.parseAnonTLSJWTUser(jwts)
if err != nil {
a.log.Warnf("Could not parse JWT from %s, denying client: %s", remote.String(), err)
return false
}
}

// only if allow lists are set else its a noop and all traffic is passed
switch {
case a.remoteInClientAllowList(remote):
a.setClientPermissions(user, caller)

case len(a.allowList) > 0:
a.setServerPermissions(user)

}

c.RegisterUser(user)

return true
}

func (a *IPAuth) parseAnonTLSJWTUser(jwts string) (string, error) {
if a.jwtSigner == "" {
return "", fmt.Errorf("anonymous TLS JWT Signer not set in plugin.choria.security.request_signing_certificate, denying all clients")
}

if jwts == "" {
return "", fmt.Errorf("no JWT received")
}

signKey, err := a.jwtSignerKey()
if err != nil {
return "", fmt.Errorf("signing key error: %s", err)
}

token, err := jwt.Parse(jwts, func(token *jwt.Token) (interface{}, error) {
return signKey, nil
})
if err != nil {
return "", fmt.Errorf("invalid JWT: %s", err)
}

claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
return "", fmt.Errorf("invalid claims")
}

err = claims.Valid()
if err != nil {
return "", fmt.Errorf("invalid claims")
}

caller, ok := claims["callerid"].(string)
if !ok {
return "", fmt.Errorf("no callerid in claims")
}

if caller == "" {
return "", fmt.Errorf("empty callerid in claims")
}

return caller, nil
}

func (a *IPAuth) jwtSignerKey() (*rsa.PublicKey, error) {
certBytes, err := ioutil.ReadFile(a.jwtSigner)
if err != nil {
return nil, err
}

signKey, err := jwt.ParseRSAPublicKeyFromPEM(certBytes)
if err != nil {
return nil, err
}

return signKey, nil
}

func (a *IPAuth) setClientPermissions(user *server.User, caller string) {
if !a.anonTLS {
return
}

replys := "*.reply.>"
if caller != "" {
replys = fmt.Sprintf("*.reply.%x.>", md5.Sum([]byte(caller)))
}

user.Permissions.Subscribe = &server.SubjectPermission{
Allow: []string{
replys,
},
}

user.Permissions.Publish = &server.SubjectPermission{
Allow: []string{
"*.broadcast.agent.>",
"*.node.>",
"choria.federation.*.federation",
},
}
}

func (a *IPAuth) setServerPermissions(user *server.User) {
matchAll := []string{">"}

switch {
case a.denyServers:
user.Permissions.Subscribe = &server.SubjectPermission{
Deny: []string{">"},
Deny: matchAll,
}

user.Permissions.Publish = &server.SubjectPermission{
Deny: []string{">"},
Deny: matchAll,
}

default:
Expand All @@ -53,9 +172,7 @@ func (a *IPAuth) setServerPermissions(user *server.User) {
}

user.Permissions.Publish = &server.SubjectPermission{
Allow: []string{
">",
},
Allow: matchAll,

Deny: []string{
"*.broadcast.agent.>",
Expand Down
Loading

0 comments on commit 4988d80

Please sign in to comment.