Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Go RPC over TLS.You have to create the following keys: certs/client.crt, certs/client.key, certs/server.crt, certs/server.key. client.crt and server.crt should be signed with ca.crt, which should be concatenated to both client.crt and server.crt. It's easier to do with easy-rsa: http://openvpn.net/index.php/open-source/documentation/howto.html#pki
package main
import (
"crypto/tls"
"crypto/x509"
"log"
"net/rpc"
)
func main() {
cert, err := tls.LoadX509KeyPair("certs/client.crt", "certs/client.key")
if err != nil {
log.Fatalf("client: loadkeys: %s", err)
}
if len(cert.Certificate) != 2 {
log.Fatal("client.crt should have 2 concatenated certificates: client + CA")
}
ca, err := x509.ParseCertificate(cert.Certificate[1])
if err != nil {
log.Fatal(err)
}
certPool := x509.NewCertPool()
certPool.AddCert(ca)
config := tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: certPool,
}
conn, err := tls.Dial("tcp", "127.0.0.1:8000", &config)
if err != nil {
log.Fatalf("client: dial: %s", err)
}
defer conn.Close()
log.Println("client: connected to: ", conn.RemoteAddr())
rpcClient := rpc.NewClient(conn)
res := new(Result)
if err := rpcClient.Call("Foo.Bar", "twenty-three", &res); err != nil {
log.Fatal("Failed to call RPC", err)
}
log.Printf("Returned result is %d", res.Data)
}
type Result struct {
Data int
}
package main
import (
"crypto/rand"
"crypto/tls"
"crypto/x509"
//"fmt"
"log"
"net"
"net/rpc"
)
func main() {
if err := rpc.Register(new(Foo)); err != nil {
log.Fatal("Failed to register RPC method")
}
cert, err := tls.LoadX509KeyPair("certs/server.crt", "certs/server.key")
if err != nil {
log.Fatalf("server: loadkeys: %s", err)
}
if len(cert.Certificate) != 2 {
log.Fatal("server.crt should have 2 concatenated certificates: server + CA")
}
ca, err := x509.ParseCertificate(cert.Certificate[1])
if err != nil {
log.Fatal(err)
}
certPool := x509.NewCertPool()
certPool.AddCert(ca)
config := tls.Config{
Certificates: []tls.Certificate{cert},
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: certPool,
}
config.Rand = rand.Reader
service := "127.0.0.1:8000"
listener, err := tls.Listen("tcp", service, &config)
if err != nil {
log.Fatalf("server: listen: %s", err)
}
log.Print("server: listening")
for {
conn, err := listener.Accept()
if err != nil {
log.Printf("server: accept: %s", err)
break
}
log.Printf("server: accepted from %s", conn.RemoteAddr())
go handleClient(conn)
}
}
func handleClient(conn net.Conn) {
defer conn.Close()
rpc.ServeConn(conn)
log.Println("server: conn: closed")
}
type Foo bool
type Result struct {
Data int
}
func (f *Foo) Bar(args *string, res *Result) error {
res.Data = len(*args)
log.Printf("Received %q, its length is %d", *args, res.Data)
//return fmt.Errorf("Whoops, error happened")
return nil
}

JayZeeGP commented Jun 30, 2017 edited

I'd like to know which modifications would be needed if there are intermediate certificates. How would the tls.Config change in that case?

Would it be possible to do this with certificates generated with LetsEncrypt?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment