feat: Support backend socket connections (UDS)
This commit is contained in:
parent
cd1790972e
commit
cfa4b8a437
4 changed files with 117 additions and 23 deletions
|
|
@ -11,7 +11,7 @@ Proxy LDAP requests to different LDAP servers based on base DN. Based on code fr
|
||||||
- [ ] Default fallback to `/etc/ldap-rp/config.toml`
|
- [ ] Default fallback to `/etc/ldap-rp/config.toml`
|
||||||
- [x] Unix Domain Socket support (incoming requests)
|
- [x] Unix Domain Socket support (incoming requests)
|
||||||
- [ ] Unix Domain Socket garbage collection (incoming requests)
|
- [ ] Unix Domain Socket garbage collection (incoming requests)
|
||||||
- [ ] Unix Domain Socket support (outgoing requests)
|
- [x] Unix Domain Socket support (outgoing requests)
|
||||||
- [ ] **not planned:** TLS termination (incoming requests)
|
- [ ] **not planned:** TLS termination (incoming requests)
|
||||||
- [ ] **not planned:** TLS backend connections (outgoing requests)
|
- [ ] **not planned:** TLS backend connections (outgoing requests)
|
||||||
- [ ] **not planned:** TLS SNI passthrough
|
- [ ] **not planned:** TLS SNI passthrough
|
||||||
|
|
@ -30,6 +30,10 @@ listen = "[::]:3389"
|
||||||
[[mapping]]
|
[[mapping]]
|
||||||
from = "a.localhost"
|
from = "a.localhost"
|
||||||
to = "example.com"
|
to = "example.com"
|
||||||
|
# The LDAP address of the backend server:
|
||||||
|
# - start with `./` or `/` for a socket URI
|
||||||
|
# - start with anything else for a TCP connection
|
||||||
|
# backend = "/run/lldap/example.com.sock"
|
||||||
backend = "127.0.0.1:4389"
|
backend = "127.0.0.1:4389"
|
||||||
[[mapping]]
|
[[mapping]]
|
||||||
from = "b.localhost"
|
from = "b.localhost"
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@ use futures_util::stream::StreamExt;
|
||||||
use ldap3_proto::LdapCodec;
|
use ldap3_proto::LdapCodec;
|
||||||
use ldap3_proto::control::LdapControl;
|
use ldap3_proto::control::LdapControl;
|
||||||
use ldap3_proto::proto::*;
|
use ldap3_proto::proto::*;
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::{TcpStream, UnixStream};
|
||||||
use tokio::time::timeout;
|
use tokio::time::timeout;
|
||||||
use tokio_util::codec::{FramedRead, FramedWrite};
|
use tokio_util::codec::{FramedRead, FramedWrite};
|
||||||
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::{CR, CW, LdapError};
|
use crate::{AbstractStream, CR, CW, LdapError};
|
||||||
|
|
||||||
pub struct BasicLdapClient {
|
pub struct BasicLdapClient {
|
||||||
r: FramedRead<CR, LdapCodec>,
|
r: FramedRead<CR, LdapCodec>,
|
||||||
|
|
@ -24,24 +24,47 @@ impl BasicLdapClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn build(addr: &str) -> Result<Self, LdapError> {
|
pub async fn build(addr: &str) -> Result<Self, LdapError> {
|
||||||
let tcpstream = match timeout(Duration::from_secs(1), TcpStream::connect(addr)).await {
|
// If addr is a relative or absolute path, consider it's a socket
|
||||||
Ok(Ok(t)) => {
|
let stream: AbstractStream = if addr.starts_with('.') || addr.starts_with('/') {
|
||||||
trace!("connection established to {addr}");
|
let unixstream = match timeout(Duration::from_secs(1), UnixStream::connect(addr)).await
|
||||||
t
|
{
|
||||||
}
|
Ok(Ok(t)) => {
|
||||||
Ok(Err(err)) => {
|
trace!("connection established to {addr}");
|
||||||
// trace!(?addr, ?err, "error");
|
t
|
||||||
error!("error to {addr}: {err}");
|
}
|
||||||
panic!();
|
Ok(Err(err)) => {
|
||||||
}
|
// trace!(?addr, ?err, "error");
|
||||||
Err(_) => {
|
error!("error to {addr}: {err}");
|
||||||
warn!("timeout to {addr}");
|
panic!();
|
||||||
panic!();
|
}
|
||||||
// continue;
|
Err(_) => {
|
||||||
}
|
warn!("timeout to {addr}");
|
||||||
|
panic!();
|
||||||
|
// continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
unixstream.into()
|
||||||
|
} else {
|
||||||
|
let tcpstream = match timeout(Duration::from_secs(1), TcpStream::connect(addr)).await {
|
||||||
|
Ok(Ok(t)) => {
|
||||||
|
trace!("connection established to {addr}");
|
||||||
|
t
|
||||||
|
}
|
||||||
|
Ok(Err(err)) => {
|
||||||
|
// trace!(?addr, ?err, "error");
|
||||||
|
error!("error to {addr}: {err}");
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
warn!("timeout to {addr}");
|
||||||
|
panic!();
|
||||||
|
// continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
tcpstream.into()
|
||||||
};
|
};
|
||||||
|
|
||||||
let (r, w) = tokio::io::split(tcpstream);
|
let (r, w) = tokio::io::split(stream);
|
||||||
|
|
||||||
let w = FramedWrite::new(w, LdapCodec::new(None, None));
|
let w = FramedWrite::new(w, LdapCodec::new(None, None));
|
||||||
let r = FramedRead::new(r, LdapCodec::new(None, None));
|
let r = FramedRead::new(r, LdapCodec::new(None, None));
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ use futures_util::StreamExt;
|
||||||
use ldap3_proto::LdapCodec;
|
use ldap3_proto::LdapCodec;
|
||||||
use ldap3_proto::proto::*;
|
use ldap3_proto::proto::*;
|
||||||
use tokio::io::{AsyncRead, AsyncWrite, ReadHalf, WriteHalf};
|
use tokio::io::{AsyncRead, AsyncWrite, ReadHalf, WriteHalf};
|
||||||
use tokio::net::TcpStream;
|
|
||||||
use tokio::time::timeout;
|
use tokio::time::timeout;
|
||||||
use tokio_util::codec::{FramedRead, FramedWrite};
|
use tokio_util::codec::{FramedRead, FramedWrite};
|
||||||
|
|
||||||
|
|
@ -20,13 +19,15 @@ use crate::client::BasicLdapClient;
|
||||||
mod config;
|
mod config;
|
||||||
use config::Config;
|
use config::Config;
|
||||||
mod dn;
|
mod dn;
|
||||||
mod op;
|
|
||||||
use crate::dn::Dn;
|
use crate::dn::Dn;
|
||||||
|
mod op;
|
||||||
|
mod stream;
|
||||||
|
use stream::AbstractStream;
|
||||||
|
|
||||||
const LDAP_CLIENT_IO_TIMEOUT: Duration = Duration::from_secs(1);
|
const LDAP_CLIENT_IO_TIMEOUT: Duration = Duration::from_secs(1);
|
||||||
|
|
||||||
type CR = ReadHalf<TcpStream>;
|
type CR = ReadHalf<AbstractStream>;
|
||||||
type CW = WriteHalf<TcpStream>;
|
type CW = WriteHalf<AbstractStream>;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum LdapError {
|
pub enum LdapError {
|
||||||
|
|
|
||||||
66
src/stream.rs
Normal file
66
src/stream.rs
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||||
|
use tokio::net::{TcpStream, UnixStream};
|
||||||
|
|
||||||
|
use std::io::Result;
|
||||||
|
use std::marker::Unpin;
|
||||||
|
use std::pin::{Pin, pin};
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
|
pub enum AbstractStream {
|
||||||
|
Tcp(TcpStream),
|
||||||
|
Uds(UnixStream),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TcpStream> for AbstractStream {
|
||||||
|
fn from(stream: TcpStream) -> Self {
|
||||||
|
Self::Tcp(stream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UnixStream> for AbstractStream {
|
||||||
|
fn from(stream: UnixStream) -> Self {
|
||||||
|
Self::Uds(stream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Unpin for AbstractStream {}
|
||||||
|
|
||||||
|
impl AsyncRead for AbstractStream {
|
||||||
|
fn poll_read(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &mut ReadBuf<'_>,
|
||||||
|
) -> Poll<Result<()>> {
|
||||||
|
match &mut *self {
|
||||||
|
Self::Tcp(stream) => pin!(stream).poll_read(cx, buf),
|
||||||
|
Self::Uds(stream) => pin!(stream).poll_read(cx, buf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncWrite for AbstractStream {
|
||||||
|
fn poll_write(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &[u8],
|
||||||
|
) -> Poll<Result<usize>> {
|
||||||
|
match &mut *self {
|
||||||
|
Self::Tcp(stream) => pin!(stream).poll_write(cx, buf),
|
||||||
|
Self::Uds(stream) => pin!(stream).poll_write(cx, buf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
|
||||||
|
match &mut *self {
|
||||||
|
Self::Tcp(stream) => pin!(stream).poll_flush(cx),
|
||||||
|
Self::Uds(stream) => pin!(stream).poll_flush(cx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
|
||||||
|
match &mut *self {
|
||||||
|
Self::Tcp(stream) => pin!(stream).poll_shutdown(cx),
|
||||||
|
Self::Uds(stream) => pin!(stream).poll_shutdown(cx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue