Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TLS based etcd can't get certs info from request. #4135

Closed
ringtail opened this issue Jan 5, 2016 · 18 comments
Closed

TLS based etcd can't get certs info from request. #4135

ringtail opened this issue Jan 5, 2016 · 18 comments

Comments

@ringtail
Copy link
Contributor

ringtail commented Jan 5, 2016

Hi,I found a problem in release-2.2,I need to read cert info from request.here is the request handler.


func (h *keysHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) 

cert info will be parsed from r


r.TLS.PeerCertificates

but It doesn't work in release-2.2. so I found the facts.


        if fdLimit, err := runtimeutil.FDLimit(); err == nil {
                    if fdLimit <= reservedInternalFDNum {
                        plog.Fatalf("file descriptor limit[%d] of etcd process is too low, and should be set higher than %d to ensure internal usage", fdLimit, reservedInternalFDNum)
                    }
                    l = netutil.LimitListener(l, int(fdLimit-reservedInternalFDNum))
                }


//code in github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/netutil
func (l *limitListener) Accept() (net.Conn, error) {
    l.acquire()
    c, err := l.Listener.Accept()
    if err != nil {
        l.release()
        return nil, err
    }
    return &limitListenerConn{Conn: c, release: l.release}, nil
}

type limitListenerConn struct {
    net.Conn
    releaseOnce sync.Once
    release     func()
}

so when parse cert info


if tlsConn, ok := c.rwc.(*tls.Conn); ok {
        if d := c.server.ReadTimeout; d != 0 {
            c.rwc.SetReadDeadline(time.Now().Add(d))
        }
        if d := c.server.WriteTimeout; d != 0 {
            c.rwc.SetWriteDeadline(time.Now().Add(d))
        }
        if err := tlsConn.Handshake(); err != nil {
            c.server.logf("http: TLS handshake error from %s: %v", c.rwc.RemoteAddr(), err)
            return
        }
        c.tlsState = new(tls.ConnectionState)
        *c.tlsState = tlsConn.ConnectionState()
        if proto := c.tlsState.NegotiatedProtocol; validNPN(proto) {
            if fn := c.server.TLSNextProto[proto]; fn != nil {
                h := initNPNRequest{tlsConn, serverHandler{c.server}}
                fn(c.server, tlsConn, h)
            }
            return
        }
    }

It can't convert to *tls.con. the simplest way is to add config filed in limitListenerConn.

@philips
Copy link
Contributor

philips commented Jan 6, 2016

Would you be able to write a unit test to cover this?

@ringtail
Copy link
Contributor Author

ringtail commented Jan 6, 2016

I found the problem is that &limitListenerConn{Conn: c, release: l.release} can't not convert to a *tls.con

@ringtail
Copy link
Contributor Author

ringtail commented Jan 6, 2016

I simply comment the code in etcd/etcdmain/etcd.go below,every things works fine.


        if fdLimit, err := runtimeutil.FDLimit(); err == nil {
                    if fdLimit <= reservedInternalFDNum {
                        plog.Fatalf("file descriptor limit[%d] of etcd process is too low, and should be set higher than %d to ensure internal usage", fdLimit, reservedInternalFDNum)
                    }
                    l = netutil.LimitListener(l, int(fdLimit-reservedInternalFDNum))
                }

@ringtail
Copy link
Contributor Author

ringtail commented Jan 6, 2016

TLS connection is like this


&Conn{conn: conn, config: config}

so we can simple add limitListener a field called config,and it will be fine.

@xiang90
Copy link
Contributor

xiang90 commented Jan 6, 2016

@ringtail Are you trying to embed etcd into your project? We are not going to maintain a stable API or implementation guarantees for etcd itself. We do not have the bandwidth right now and we plan to change the implementation of etcd internally quite a bit. However, if you meet any issue we are happy to discuss and review pull requests.

For your issue, do you want to get the TLSConn or just the static configuration? The static configuration is passed by the user, you can simply cache it when you pass it in. So you do not need to add a field.

@ringtail
Copy link
Contributor Author

ringtail commented Jan 7, 2016

@xiang90 Hi,Maybe I don't describe the problem clearly. The scenario is that I enhance etcd by certs with specific role in it. you can sign a client cert with some specific role. And I need to get cert info from
keysHandler in etcdserver/etcdhttp/client.go


func (h *keysHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
      // you can get certChains from r.TLS  
     certChains:= r.TLS.PeerCertificates
}

but in relase-2.2, we can't get r.TLS.PeerCertificates because of the code below


                if fdLimit, err := runtimeutil.FDLimit(); err == nil {
                    if fdLimit <= reservedInternalFDNum {
                        plog.Fatalf("file descriptor limit[%d] of etcd process is too low, and should be set higher than %d to ensure internal usage", fdLimit, reservedInternalFDNum)
                    }
                    l = netutil.LimitListener(l, int(fdLimit-reservedInternalFDNum))
                }

because of the tls listener is wrapped by netutil.LimitListener, but netutil.LimitListener can't not create a *tls.con, so the handshake will not be successful and we can't get client cert from request in keysHandler

@xiang90
Copy link
Contributor

xiang90 commented Jan 7, 2016

@ringtail If you want to make sure etcd upgrades do not break you, you'd better try to contribute your patch to upstream. If you want to fix this issue, you can try to submit a patch. We do not plan to provide any guarantees about any sub pkgs structs or API inside etcd.

@xiang90
Copy link
Contributor

xiang90 commented Jan 7, 2016

but netutil.LimitListener can't not create a *tls.con, so the handshake will not be successful and we can't get client cert from request in keysHandler

I do not quite understand this. LimitListener is just a wrapper around the original Listener. I do not think there is any issue with handshake. Can you reproduce the handshake failure with LimitListener?

@xiang90
Copy link
Contributor

xiang90 commented Jan 7, 2016

@ringtail

OK. After checking the go std lib, I found


if tlsConn, ok := c.rwc.(*tls.Conn); ok {
        if d := c.server.ReadTimeout; d != 0 {
            c.rwc.SetReadDeadline(time.Now().Add(d))
        }
        if d := c.server.WriteTimeout; d != 0 {
            c.rwc.SetWriteDeadline(time.Now().Add(d))
        }
        if err := tlsConn.Handshake(); err != nil {
            c.server.logf("http: TLS handshake error from %s: %v", c.rwc.RemoteAddr(), err)
            return
        }
        c.tlsState = new(tls.ConnectionState)
        *c.tlsState = tlsConn.ConnectionState()
        if proto := c.tlsState.NegotiatedProtocol; validNPN(proto) {
            if fn := c.server.TLSNextProto[proto]; fn != nil {
                h := initNPNRequest{tlsConn, serverHandler{c.server}}
                fn(c.server, tlsConn, h)
            }
            return
        }
    }

is in std library, not in your code. I will try to reproduce the issue myself then.

@xiang90
Copy link
Contributor

xiang90 commented Jan 7, 2016

@ringtail

Also netutil is an external dependency at https://github.com/golang/net/tree/master/netutil.

Can you try to file an issue there?

@ringtail
Copy link
Contributor Author

ringtail commented Jan 7, 2016

@xiang90 yes! That's exactly the problem. I'll open a issue in https://github.com/golang/net/tree/master/netutil.
Thanks a lot!

@xiang90
Copy link
Contributor

xiang90 commented Jan 7, 2016

@ringtail

I got a fix at etcd side at #4153.

Can you try to pull down this branch and give it a try. At meantime, I would suggest you not keep a private fork of etcd.

@ringtail
Copy link
Contributor Author

ringtail commented Jan 7, 2016

yes, I keep a private fork in my gitlab and the problem is raised when I try to upgrade my etcd version from 2.1.3 to 2.2.2☺️

@xiang90
Copy link
Contributor

xiang90 commented Jan 7, 2016

@ringtail Anyway, please give #4153 a try. Thanks!

@ringtail
Copy link
Contributor Author

ringtail commented Jan 7, 2016

OK,I'll try it. thank you!

@ringtail ringtail closed this as completed Jan 7, 2016
@xiang90
Copy link
Contributor

xiang90 commented Jan 7, 2016

@ringtail Why this is closed?

@ringtail ringtail reopened this Jan 7, 2016
@ringtail
Copy link
Contributor Author

ringtail commented Jan 7, 2016

It works☺️

@xiang90
Copy link
Contributor

xiang90 commented Jan 7, 2016

@ringtail Thanks!

@xiang90 xiang90 closed this as completed Jan 7, 2016
heyitsanthony pushed a commit to heyitsanthony/etcd that referenced this issue Jan 12, 2016
Uses gexpect to test the etcd binary directly. Tests etcd-io#4135, etcd-io#4171
heyitsanthony pushed a commit to heyitsanthony/etcd that referenced this issue Jan 13, 2016
Uses gexpect to test the etcd binary directly. Tests etcd-io#4135, etcd-io#4171
heyitsanthony pushed a commit to heyitsanthony/etcd that referenced this issue Jan 13, 2016
Uses gexpect to test the etcd binary directly. Tests etcd-io#4135, etcd-io#4171
heyitsanthony pushed a commit to heyitsanthony/etcd that referenced this issue Jan 13, 2016
Uses gexpect to test the etcd binary directly. Tests etcd-io#4135, etcd-io#4171
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

3 participants