Skip to content

Instantly share code, notes, and snippets.

@artyom
Last active October 9, 2023 15:44
Show Gist options
  • Star 54 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save artyom/6897140 to your computer and use it in GitHub Desktop.
Save artyom/6897140 to your computer and use it in GitHub Desktop.
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
Copy link

JayZeeGP commented Jun 30, 2017

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

@JalfResi
Copy link

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