Replies: 8 comments 6 replies
-
|
Some compelling use cases here. |
Beta Was this translation helpful? Give feedback.
-
|
For the side-project driving my interest in these features, I could just create a couple of Nix-Package overlays (I'm using nixos in my homelab), patching NATS. If that gets to messy I could fork nats-server (UDC+Header rewriting) and the nats-cli (UDC). Some client APIs might already support UDC via URLs (if they can unix:///...). My problem is that I probably will not have the capacity to maintain it for upstream changes, at least not reliably. That's bad for a security sensitive application. I believe there is a lot of value in these propositions:
I can see no downside (other than the implementation cost, which I believe is limited). |
Beta Was this translation helpful? Give feedback.
-
|
Here a proposal for how to implement the UNIX Domain Socket part of my idea properly (@derekcollison feeback on this would be welcome. I'm going to implement that for my project, but it would be great if this would eventually end up upstream, would like to do it in a way that's acceptable for you guys). UNIX Domain SocketsImplementation Changes
Identity and UsernameIdentity (canonical, for audit/logging):
Username (NATS
Username derivation:
Peercred MatchingSyntax:
Extension point: Permission Accumulation
ConfigurationWhen user contains a uid-query and RationaleThis would allow clients connecting via UDS to act similar to clients using user/password or TLS credentials. Clients could still use JWT/NKEY authentication overwriting these local configs (if I understand the auth procedure correctly). As a result, the UNIX permission model could be fully integrated in a system local scope. If the local nats instance has an account, it can act as signatory for local processes in connected NATS meshes (would need additional config). The query syntax and extension point would make it easy to integrate things like kerberos, pam, etc. Nats running as unprivileged user can delegate verifications requiring caps to external processes (that could integrate as NATS services), example: The syntax is analog to TLS subject names. Multiple rule matches and negatives make it possible to easily add deny rules (e.g. "uid!=0,gid:name!=wheel" -> deny privileged stuff). Rule matching is efficient (if c.isUds && rule ~ '[ugp]id[:=]') is no overhead, so there should be no noticable penalty for settings not using this. |
Beta Was this translation helpful? Give feedback.
-
|
Here a proposal regarding identity headers. Again, I would appreciate feedback a lot. I'm a bit uncertain where and how to implement that (if this is interesting for you @derekcollison). I changed my mind about general header rewriting. This is actually the only application for touching headers on the server-side I could think of: Identity Headers for Incoming MessagesAll incoming messages will (see Implementation/Configuration) receive a server generated message header PurposeNATS permissions are have two properties that make them difficult to use in certain situations:
The proposed solution is for a NATS server to prepend an identity header to each incoming message, adding In the absense of other NATS server side mechanisms prepending message headers, this is semantically like If that degree of transparency is not generally desired, a server can strip or filter this chain or replace Servers can prevent untrusted (in regards to ID chain) clients from forging identities by stripping Header Name and FormatPreliminary header name: Server AccountThis identifies a server by a NATS account or a public key, that uniquely identifies the NATS instance and TypeThis specifies the authentication type of the identity. This determines the format and semantics of Client Identity
Optional SignatureIt might be desirable to let a server generating identity headers sign them with their account key (if Reasons not to sign:
ImplementationInjection Point Candidates
Caching of Identities
Configuration
|
Beta Was this translation helpful? Give feedback.
-
|
TCP/UDS "bench" (16 cores, i7):
Another run with in-proc (the results are very consistent for TCP/UDS): Total ops/s:
Per client ops/s:
Using this: func pingPong(b *testing.B, c net.Conn, n int) time.Duration {
buf := make([]byte, 4096)
ping := []byte("PING\r\n")
start := time.Now()
for i := 0; i < n; i++ {
c.Write(ping)
c.SetReadDeadline(time.Now())
_, err := c.Read(buf)
if err != nil {
continue
}
for {
c.SetReadDeadline(time.Now())
_, err = c.Read(buf)
if err != nil {
break
}
}
}
elapsed := time.Since(start)
c.SetReadDeadline(time.Time{})
c.Read(buf)
return elapsed
}
...
func Benchmark_Ping_10(b *testing.B) {
b.Run("TCP", func(b *testing.B) {
s := runServerTCP(8423)
defer s.Shutdown()
benchPing(b, dialTCP(8423), 10)
})
b.Run("UDS", func(b *testing.B) {
s := runServerUDS("/tmp/nats-bench.sock")
defer s.Shutdown()
benchPing(b, dialUDS("/tmp/nats-bench.sock"), 10)
})
}I was surprized, that UDS is faster than inproc for one client. I thought that uses pipes (should be same as UDS), but learned it doesn't. My interpretation:
|
Beta Was this translation helpful? Give feedback.
-
|
Here is a first implementation (domain sockets + auth): https://github.com/mutech/nats-server |
Beta Was this translation helpful? Give feedback.
-
|
My concern here is not necessarily with the idea of supporting UNIX domain sockets as they are relatively easy to support, but rather how quickly this grows arms and legs in order to support additional auth modes, header rewriting etc. This seems like a significant set of changes to the security model that have an ongoing maintenance cost for us. |
Beta Was this translation helpful? Give feedback.
-
|
I understand that my approach is not attractive for you guys, and i actually agree, considering that my use case is too far away from the main stream. The main reasons why I cannot implement my features using an embedded approach are:
Currently, NATS has a monolithic start/shutdown and config-reload procedure. This makes it impossible to have a staged startup. One of the things I need for my use case is to start NATS during early boot, before the network is up, and serve clients with reduced capabilities. Once the network is up, I need to start networked modules and transition into full featured mode. I thought about replicating the start/shutdown/reload functionality in a separate module in a fork, using a dependency-aware service manager. This would not add any features, it would only modularize the life cycle management and transform dependencies implied by the code into explicit ones. Given such an architecture, the UDS and permission model extensions I wrote here could be easy to implement using an embedded server, simply by adding a new service (UDS client listener). I could also use a staggered start up procedure for my use case. This could be an experimental API, not affecting the rest of NATS, that could be tested for equivalence with the current architecture. If it turns out to be robust and not having significant downsides, NATS can eventually migrate to that architecture. I believe it would have major benefits, one of which would be that it's much easier to understand explicit dependencies than implicit assumptions in code. It would make it much easier to add integrations for other messaging protocols (like mqtt). And of course, it would make it much easier for people like me to create custom solutions that require a deeper integration than what embedding NATS can offer today. I'm closing this, seems to be the wrong approach. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
(see https://github.com/mutech/nats-server for a preliminary implementation)
NATS seems to be a revolutionary technology that - if my understanding is correct - should be THE hype. I'm surprised it is not, though it gets quite a lot of attention.
I have two use cases in mind for which NATS would be a complete game changer for my project (-ideas):
1. Linux "system-nats"
This would start early in the boot process providing a UNIX domain socket to which clients can connect. The nats would serve various components to do IPC, communicate state, etc.
The reason why this should be or even has to use a UNIX domain socket, is not so much the possible performance characteristics, but that the connection provides the UID and PID of the client. The UID allows the system-nats to authenticate the client reliably. The PID allows nats to do further inspections of the clients credibility (has it been started as a systemd unit via PPID chain, is it a setuid process, etc.).
Security
This is huge, because the system-nats could act as identity broker, provisioning credentials to local processes. It could implement things like:
This would allow the use of arbitrary system-local mechanisms to ensure identity without requiring each client to manage their secrets. This client side secret management is mostly useless, since the protection of secrets is almost exclusively based on file system permissions and user identities. This mechanism would eliminate all attack vectors based on proper FS setup, it would remove the need for complex secret deployment for apps using this mechanism and centralize secret deployment for apps relying on traditional mechanisms.
Bridging authentication to the world
Once a local client can seamlessly connect to a local nats server, the local identity can be translated to externals, either via connectivity of system-nats to other servers or system-nats could even pass authenticated connections to clients (the server could open a connection to a service, authenticate (obtain vault tokens, JWT, certs, etc.) and pass the connection to the client through the unix socket.
The system-nats would become the ultimate authority for establishing system verified identity to the outer world. This is consistent with the fact that UID + system meta data is all the security a non-interactive process on a Linux process can have. This is better than just file system access, because it adds all the additional checks available.
The downside is the complexity added to the verification process and the privileges required to perform the checks. This is not trivial, but so is local secret provisioning.
Observability
Once any process on a local system can interact with system-nats without complex authentication procedures, nats becomes very accessible. Any service, process, script can log typed events and update state. Logs can be fed to nats (for RT alerting, workflows, forwarding to central logging, etc.).
Instead of logging to prometheus or a log stash, define alert queries and doing all that dance, you just observe events and can act on them anywhere, on the local system or in the network. You don't need to setup a specific log/metrics collector, you just subscribe to system logs and feed them into whatever system without having to install software on each system (using nats transport).
All the NATS advantages
I have another 20 uses for this, that all rely on the frictionless authentication provided by unix sockets. I leave that part out because it's not relevant. But the relevant effect is that this approach would bridge all the wonderful potential of a distributed communication platform to the local system and integrating that system into the mesh.
This low friction approach is important, because if you need to authenticate each service manually, the mechanism just isn't attractive.
2. User-nats
Users are not local, they live outside of computers, smart phones and web browser sessions, but they have representations in login sessions connected to applications. NATS could be a wonderful bridge connecting these sessions.
It does not take much imagination to come up with use cases, if my smart watch and phone, my browser session, my login session could communicate with each other.
I am using Linux workstations most of the time. I find it incredible annoying when I have to tap my yubikey ten times when running an ansible-playbook. It's idiotic when I'm logged into a remote system via ssh where a stale desktop session and a confirmation dialog is opening on the GUI that I don't see and my shell hangs.
For me, the UNIX domain socket is important here too, because I don't want to have to type a password or deploy a secret on every host. If I can connect to a local (user-session-) nats, I get reliable system-level authentication (that can determine if my process is running in a remote ssh-session) and for critical operations, this can query my yubikey, request confirmation on my watch or phone.
This is not only convenient, but it adds security.
UNIX domain sockets
I believe this is a major game changer. Can we please have them supported?
Beta Was this translation helpful? Give feedback.
All reactions