Open petri opened 7 years ago
Yes, please post the config.
Ultimate having docs and (ideally) an example for this would be great!
Here's the crossbar config used, with the domain & some other stuff obfuscated.
version: 2
controller: {}
workers:
- type: router
realms:
- name: 'realm1'
roles:
- name: abc
permissions:
- uri: ''
match: prefix
allow:
call: true
register: true
publish: true
subscribe: true
disclose:
caller: false
publisher: false
cache: true
transports:
- type: websocket
auth:
tls:
type: static
principals:
client0:
certificate-sha1: "5C:CE:DE:00:51:0F:5D:9F:93:45:3E:92:CE:11:FD:17:13:AA:DB:13"
role: abc
endpoint:
type: tcp
port: 443
tls:
key: /etc/letsencrypt/live/xxx.com/privkey.pem
certificate: /etc/letsencrypt/live/xxx.com/cert.pem
chain_certificates: ["/etc/letsencrypt/live/xxx.com/chain.pem"]
ca_certificates: ["clientca.cert.pem"]
url: wss://xxx.com/ws
- type: web
endpoint:
type: tcp
port: 8080
tls:
key: /etc/letsencrypt/live/xxx.com/privkey.pem
certificate: /etc/letsencrypt/live/xxx.com/cert.pem
chain_certificates: ["/etc/letsencrypt/live/xxx.com/chain.pem"]
paths:
/:
type: static
directory: ../www
I tried connecting using curl, and got no error up to the point of websocket upgrade (whereby failure is expected):
curl --cacert ./clientca.cert.pem --cert ./client.crt --key ./client.key https://xxx.com:443/ws
Likewise, dropping the cert and key from curl command results in SSL error in crossbar logs. So it looks like the server might be configured properly...
Taking a look at the crossbar logs, it seems to me the abovementioned "cannot authenticate" error happens after the TLS authentication and connection already succeeded... how weird is that? It's as if the WAMP level does not understand the authentication already took place?
Bummer. TLS client certificate auth does work. It's joining the realm that fails with the abovementioned error. Should've guessed, given the error is in WAMP code :(
Solved. Apparently ApplicationSession.join() requires an extra param in this case:
def join(self, realm, authmethods=None, authid=None, authrole=None, authextra=None):
So, all that's required for the TLS client cert auth to work with the above crossbar configuration is passing authmethods=['tls']
.
This requirement should be explicitly mentioned in the docs and examples. Maybe it's already there in that one example I did not thoroughly peruse through :(
Even better - it would alleviate the complexity of configuring all this if the ApplicationSession base class join
default implementation could check the SSLContext for the presence of TLS client certificate and inject the tls
authmethod upon join
, if it's not explicitly given by the user. How's that as an idea?
@petri so you got everything working as you expect?
Sorry for costing you time figuring this out .. we definitely should document that and probably also should add more asyncio examples.
Even better - it would alleviate the complexity of configuring all this if the ApplicationSession ... SNIP
I'm not sure tbh. It adds magic which might come unexpected. And if we had documented it / had an example, I'd say it is trivial to use (well, at least trivial to activate): authmethods=['tls']
. This also serves as documentation: user wants to use TLS client auth. Otherwise, it is implicit.
I understand. But it's already explicit, is it not, given that the user has configured the SSL context for the client certificate auth?
But perhaps this is really a question of whether or not the join
authmethods
should only refer to WAMP-level realm authentication methods, overall?
Yeah, it's kind of a fine line/point. Personally, I think "being explicit" is nice -- especially if we fix the docs so it doesn't take digging to figure that out. BUT on the other hand if you already leaped through enough fire-y hoops to make a client-side certificate work, ...
That said, I'm trying to imagine a case where you'd want to do TLS client-auth on the transport, but do some other thing to authenticate the realm at the WAMP level. If there's never such a case, then maybe "non-explict, slightly magic" is fine...
I do have some PoC stuff in the works to make authentication way better overall -- so you don't have to muck around with join()
or overriding onChallenge
at all (except for "odd" cases).
I guess all WAMP clients of a customer could share a client certificate, and then authenticate using something else, would that be a possibility? I think it might.
Somehow, configuring lower-level auth at WAMP level just feels it goes completely against the whole idea of separation of networking layers.
Another thought: what if we configure TLS client certificates for a "plain" websocket connection? Then the SSL context is all that it takes?
Authenticating to WAMP realms via TLS is definitely kind of the "odd one out" as far as crossing the boundaries between transport and protocol -- it's kind of the "higher" protocol layer delegating auth to the "lower" transport layer. (Or, sort of saying "if-and-only-if the transport is client-authenticated, you get a free pass to the realm X").
So, that's kind of a point in favour of "explicit" -- you're saying, "I trust the TLS auth enough that I don't care about any other authentication for this realm". Whereas, I can certainly see that some people might regard TLS as "bare minimum" (even with client-side certs) to get in the door -- and still want higher-level auth.
(BTW, the "auth stuff" I mention two comments up is more-general than TLS etc...sorry for distracting this ticket :/ )
@petri Separating layers is certainly not out of fashion;) Separation of concerns and decoupling are top design goals of WAMP. Eg witness WAMPs ability to run over different transports (WebSocket|RawSocket|Longpoll X TCP|TLS|UDS X JSON|MsgPack|UBJSON|CBOR = dozens of transports) transparently (that is, with no app code change at all).
I can see why TLS client auth could be seen as blurring the line. In a way it does. On the other hand, WAMP always authenticates at the WAMP level (during the WAMP HELLO
, CHALLENGE
, AUTHENTICATE
, WELCOME
message exchange). But WAMP allows to derive credentials from the underlying transport. Eg with WebSocket, which starts as HTTP, you can use HTTP cookies. You can't use cookies with RawSocket. Similar with TLS. But it is always the WAMP layer that does final auth decision (eg Dynamic-TLS .. the dynamic authenticator gets the cert info, and will decide. It is not the TLS layer that decides, for example, here).
Then, using authmethods=["tls", "cookie", "wampcra"]
will behave differently than authmethods=["cookie", "tls", "wampcra"]
etc (a router will chose the first authmethod it is willing to perform for the given client - router decides, client announces preference).
Then, we want to add multi-factor auth to WAMP. Eg this could look like authmethods=[["tls", "wampcra"], ["tls", "cookie"]]
- either require TLS client cert and WAMP-CRA or TLS client cert and Cookie.
You cannot currently do that - and this is a real gap. We definitely want multi-factor auth ..
I'd also like to point out WAMP-cryptosign - for our own use in WAMP/Crossbar.io based apps, we'll be moving to WAMP-cryptosign (as soon as I find time to add it to AutobahnJS;).
Finally, I agree with @meejah : a) explicit is better than implicit and b) the real issue rgd AutobahnPython API is in ApplicationRunner
. It is too rigid, not flexible etc .. it works "ok", but I am not happy with it. meejah has been working on different flavors of "new API", and I hope we get this nailed and shipped in a not too distant future.
Yes. And I agree that explicit is better - I just feel it already is. But like @meejah said, it's a fine line. Thanks for laying out the big picture.
Autobahn Python can use TLS client certificates to authenticate for example with crossbar, when using Twisted. However, there is no documentation anywhere on what's the status on asyncio. The official word appears to be "it might work". Hence this issue.
FWIW, I tried the following client code:
The resulting client error:
Crossbar log error:
As far as I can tell, crossbar config for server & client TLS use is ok per all the docs, when these errors occur. I can post the config here; I posted it to the crossbar google group as well, already.