Allow a TCP connection, but not a TLS connection based on an ACL

已查看 105 次
跳至第一个未读帖子

John

未读,
2022年3月28日 18:26:012022/3/28
收件人 golang-nuts
I'm looking to satisfy this:
  • If you are in an ACL, you can make a TLS connection
  • If you are not in an ACL, you can only a TCP connection, but not a TLS connection*
* It would be better if it didn't honor TCP either, unless it is a health probe

Basically I want to move my denials into the listener and not in the http.Server handlers.

I thought I was clever recently, trying to do this with:

func (a *aclListener) Accept() (net.Conn, error) {
conn, err := a.ln.Accept()

if err != nil {

return nil, err

}


host, _, err := net.SplitHostPort(conn.RemoteAddr().String())

if err != nil {

return nil, fmt.Errorf("connection's remote address(%s) could not be split: %s", conn.RemoteAddr().String(), err)

}


// The probe connected, so close the connection and exit.
if a.acls.isProbe(host) {

log.Printf("TCP probe(%s) connection", host)

conn.Close()

return nil, ErrIsProbe

}


  // Block anything that isn't in our ACL.
if err := a.acls.ipAuth(host); err != nil {
return nil, err

}

log.Println("accepting connection from: ", conn.RemoteAddr().String())

return conn, nil

}


aclListener implements a net.Listener and I was going to allow the TCP probe from this
health service, but nothing more (like seeing the TLS header).
However, it turns out erroring on an Accept() will cause the http.Server to stop.

Of course, if this code did work, the difference between the prober and
non-ACL connections is the same, they both can get the TCP socket before being denied.

Does anyone know if I can achieve this in my code without getting super hacky? I can see
some ways to that, but figured someone here might have done this in a simple way.

Cheers and thanks.


Sean Liao

未读,
2022年3月28日 18:36:262022/3/28
收件人 golang-nuts
I would just add a for loop around your code and only return when you have a connection you want to allow, otherwise just log / pass the error elsewhere.


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/4ab235c1-ab52-42de-a22a-a31bde21eb0cn%40googlegroups.com.

robert engels

未读,
2022年3月28日 18:47:132022/3/28
收件人 Sean Liao、golang-nuts
You just need to return a temporary error. It should not be exiting anyway - unless the “done” channel is valid.

ctx := context.WithValue(baseCtx, ServerContextKey, srv)
for {
   rw, err := l.Accept()
   if err != nil {
      select {
      case <-srv.getDoneChan():
         return ErrServerClosed
      default:
      }
      if ne, ok := err.(net.Error); ok && ne.Temporary() {
         if tempDelay == 0 {
            tempDelay = 5 * time.Millisecond
         } else {
            tempDelay *= 2
         }
         if max := 1 * time.Second; tempDelay > max {
            tempDelay = max
         }
         srv.logf("http: Accept error: %v; retrying in %v", err, tempDelay)
         time.Sleep(tempDelay)
         continue
      }
      return err
   }



robert engels

未读,
2022年3月28日 18:49:212022/3/28
收件人 Sean Liao、golang-nuts
Ignore that last part - just use a “temporary” error.

Sean Liao

未读,
2022年3月28日 19:47:512022/3/28
收件人 golang-nuts
abusing temporary delays like that could result in unpredictable performance with up to a second between accepts, not something you want if you are flooded with things you want to deny (which is what an ACL is for).

robert engels

未读,
2022年3月28日 20:18:292022/3/28
收件人 Sean Liao、golang-nuts
I think that is there to avoid DOS attacks - otherwise you will have the same problem in the user space code - that is - since the filtering is no being performed in the kernel - allowing the connection requests to pass up - you need a delay…

In the future, the handler could be changed to support telling the kernel to “block the IP”, etc.

I think you want to pass the error up as it is designed.

John Doak

未读,
2022年3月28日 23:28:312022/3/28
收件人 golang-nuts
Hey Sean and Robert,

Thanks for the suggestions. 

I can see how the temporary error would work, but as Sean is saying, this is going to add delays that are going to go against what I'm wanting to do. 

Sean, I'm not sure I understand the part about looping my code.  Here is a sample on the playground, is it possible you can show me what I'm missing:

Cheers.

You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/tqT_Cv574rU/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAGabyPoLQr5Te5VxXpyvoZn4Cs3Lh64GKPWk%2Bk-LyQNA3KnS1w%40mail.gmail.com.


--

John

未读,
2022年3月30日 15:31:502022/3/30
收件人 golang-nuts
Just as a follow-up, I think the answer to this question is to fork the net/http library to add a line that handles an error type ErrIgnore without the ramifications of a temporary error.  With all the good and bad that it entails (for this use case, it should be fine).

This is just a niche use case, I can't imagine a change to support this getting into the net/http package.  

Diego Joss

未读,
2022年3月31日 03:25:002022/3/31
收件人 golang-nuts
Hi John,

On Mon, Mar 28, 2022 at 11:26 PM John <johns...@gmail.com> wrote:
I'm looking to satisfy this:
  • If you are in an ACL, you can make a TLS connection
  • If you are not in an ACL, you can only a TCP connection, but not a TLS connection*
* It would be better if it didn't honor TCP either, unless it is a health probe

Basically I want to move my denials into the listener and not in the http.Server handlers.

I thought I was clever recently, trying to do this with:

func (a *aclListener) Accept() (net.Conn, error) {
conn, err := a.ln.Accept()
if err != nil {
return nil, err
}

host, _, err := net.SplitHostPort(conn.RemoteAddr().String())
if err != nil {
return nil, fmt.Errorf("connection's remote address(%s) could not be split: %s", conn.RemoteAddr().String(), err)
}

// The probe connected, so close the connection and exit.
if a.acls.isProbe(host) {
log.Printf("TCP probe(%s) connection", host)
conn.Close()
return nil, ErrIsProbe
}

        // Block anything that isn't in our ACL.
if err := a.acls.ipAuth(host); err != nil {
return nil, err
}
log.Println("accepting connection from: ", conn.RemoteAddr().String())
return conn, nil
}

How about (in you (*aclListener).Accept method) looping on net.Listener.Accept until the connection should pass your ACL. Here is some overysimple pseudo-code version:

func (a *aclListener) Accept() (net.Conn, error) {
for {
conn, err := a.ln.Accept()
if isGood(conn) {
return conn, nil
}
}
}

The way I like to see it is that the "Accept" method means: "return the next connection tentative which is good enough to be accepted". Thus the meaning of "Accept" for the net.Listener, and your (*aclListener) are not the same: net.Listener considers as acceptable any connection, (*aclListener) accepts only connections under certain constraints.

Would this work for you?

-- Diego Joss
回复全部
回复作者
转发
0 个新帖子