I have a secure websocket server running on localhost:443/server-demo ( jetty websocket server).
Now I am writing a go client that can communicate with the websocket server. I am able to connect to the websocket server using right certificates. Here is the sample code.
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"log"
)
func main() {
cert, err := tls.LoadX509KeyPair("nifi-1.10.0-bin/nifi-1.10.0/extras/gen-certs/certs/admin.pem", "nifi-1.10.0-bin/nifi-1.10.0/extras/gen-certs/certs/admin-key.pem")
if err != nil {
log.Fatalf("server: loadkeys: %s", err)
}
config := tls.Config{Certificates: []tls.Certificate{cert}, InsecureSkipVerify: true}
conn, err := tls.Dial("tcp", "127.0.0.1:443", &config)
if err != nil {
log.Fatalf("client: dial: %s", err)
}
defer conn.Close()
log.Println("client: connected to: ", conn.RemoteAddr())
state := conn.ConnectionState()
for _, v := range state.PeerCertificates {
fmt.Println(x509.MarshalPKIXPublicKey(v.PublicKey))
fmt.Println(v.Subject)
}
log.Println("client: handshake: ", state.HandshakeComplete)
log.Println("client: mutual: ", state.NegotiatedProtocolIsMutual)
message := "Hello\n"
n, err := io.WriteString(conn, message)
if err != nil {
log.Fatalf("client: write: %s", err)
}
log.Printf("client: wrote %q (%d bytes)", message, n)
reply := make([]byte, 256)
n, err = conn.Read(reply)
log.Printf("client: read %q (%d bytes)", string(reply[:n]), n)
log.Print("client: exiting")
}
The above code throws this error:
"HTTP/1.1 400 No URI\r\nContent-Type: text/html;charset=iso-8859-1\r\nContent-Length: 49\r\nConnection: close\r\nServer: Jetty(9.4.19.v20190610)\r\n\r\n<h1>Bad Message 400</h1><pre>reason: No URI</pre>" (188 bytes)
My question is after making the connection how can I send message to particular URI? i.e I want to send a message to wss://localhost:443/server-demo.
The code in a question does not establish a WebSocket connection to the server.
To establish the WebSocket connection, the application must write a WebSocket handshake to conn and receive the handshake response. See the RFC for the details.
Most applications use a websocket package than handles all of these details. The gorilla/websocket package is a popular choice.
This code should get you started with gorilla:
cert, err := tls.LoadX509KeyPair("nifi-1.10.0-bin/nifi-1.10.0/extras/gen-certs/certs/admin.pem", "nifi-1.10.0-bin/nifi-1.10.0/extras/gen-certs/certs/admin-key.pem")
if err != nil {
log.Fatalf("server: loadkeys: %s", err)
}
config := tls.Config{Certificates: []tls.Certificate{cert}, InsecureSkipVerify: true}
d := websocket.Dialer{
TLSClientConfig: &config,
}
c, _, err := d.Dial("wss://localhost:443/server-demo", nil)
if err != nil {
log.Fatal(err)
}
defer c.Close()
// Use `c` to send and receive messages
Related
Implementing an ssh proxy in Go, errors out with bad packet length, these are the errors with ssh in debug mode:
debug1: SSH2_MSG_KEXINIT sent
Bad packet length 1231976033.
ssh_dispatch_run_fatal: Connection to ::1 port 8080: message authentication code incorrect
Code:
func handleSSH(conn net.Conn, r *bufio.Reader, protocol string) {
target, err := url.Parse("ssh://localhost:3333")
if err != nil {
fmt.Println("Error parsing target", err)
conn.Close()
return
}
targetConn, err := net.Dial("tcp", target.Host)
if err != nil {
fmt.Println("error dialing SSH target:", err)
conn.Close()
return
}
defer targetConn.Close()
var wg sync.WaitGroup
wg.Add(2)
go func() {
_, err := io.Copy(targetConn, conn)
if err != nil {
fmt.Println("error copying data to target:", err)
}
wg.Done()
}()
go func() {
_, err := io.Copy(conn, targetConn)
if err != nil {
fmt.Println("error copying data from target:", err)
}
wg.Done()
}()
wg.Wait()
conn.Close()
}
// EDIT
func connection(conn net.Conn) {
r := bufio.NewReader(conn)
protocol, err := r.ReadString('\n')
if err != nil {
fmt.Println("Error reading first line", err)
conn.Close()
return
}
if protocol[0:3] == "SSH" {
handleSSH(conn, r, protocol)
}
}
func main() {
ln, err := net.Listen("tcp", ":8080")
if err != nil {
panic(err)
}
defer ln.Close()
for {
conn, err := ln.Accept()
if err != nil {
panic(err)
}
go connection(conn)
}
}
EDIT: added code for relevant information on how the connection is initiated and reproduce the error.
My best guess is the ssh negotiation process is being interrupted, and things goes out of sync.
The code is reading the first line from the client and checks the kind of protocol in order to call the appropriate handler:
protocol, err := r.ReadString('\n')
...
if protocol[0:3] == "SSH" {
handleSSH(conn, r, protocol)
}
}
But the code fails to forward the already read bytes to the connected server. These bytes are in protocol and are given to handleSSH. But it fails to send these bytes to the connected server once the connection is established. Instead it only copies new data between client and server.
This means the server does not get the first line from the client. It therefore likely complains about a protocol error with something like Invalid SSH identification string. which gets forwarded to the client and misinterpreted as valid data from an SSH connection.
so I am trying to implement Use a WebSocket client to exec commands in a Kubernetes pod in Go (so I create a WebSocket app to remotely run commands in a container's pod for debugging/monitoring/code-practice) but I am getting:
dial:tls: first record does not look like a TLS handshake exit status
1
What am I missing? I am using this as an example cluster. There is a way to do it by passing a bearer token but that doesn't look very safe.
package main
import (
"io/ioutil"
"log"
"github.com/gorilla/websocket"
)
func main() {
ca, err := ioutil.ReadFile("/home/george/.minikube/ca.crt")
if err != nil {
log.Fatal(err)
}
// read client cert
clientCert, err := ioutil.ReadFile("/home/george/.minikube/profiles/minikube/client.crt")
if err != nil {
log.Fatal("Error loading client cert", err)
}
// read client key
clientKey, err := ioutil.ReadFile("/home/george/.minikube/profiles/minikube/client.key")
if err != nil {
log.Fatal("Error loading client key", err)
}
value1 := "ca:" + string(ca)
value2 := "cert: " + string(clientCert)
value3 := "key: " + string(clientKey)
dialer := websocket.DefaultDialer // use default dialer
dialer.Subprotocols = []string{value1, value2, value3}
//dialer.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
url := "wss://192.168.49.2:30110/api/v1/namespaces/default/pods/hello-minikube-6ddfcc9757-g4484/exec?command=echo&command=ls&stderr=true&stdout=true"
//dial websocket
c, _, err := dialer.Dial(url, nil)
if err != nil {
// print response body
//fmt.Println(response.Body)
log.Fatal("dial:", err)
}
// receive websocket message
defer c.Close()
}
I am trying to make a TLS connection to RabbitMQ with authentication provided by self-signed certificates through the SASL EXTERNAL mechanism using the golang implementation provided by https://github.com/apache/qpid-proton. The goal is to be able to connect to RabbitMQ without specifying the username and password in the URI.
RabbitMQ is running with the following configuration:
auth_mechanisms.1 = EXTERNAL
auth_mechanisms.2 = PLAIN
auth_mechanisms.3 = AMQPLAIN
and plugins:
rabbitmq_amqp1_0
rabbitmq_auth_mechanism_ssl
I have confirmed that I am able to connect with SASL EXTERNAL using a Node.js library (https://github.com/amqp/rhea) and I have confirmed that connecting with PLAIN and ANONYMOUS works with Go in the qpid-proton library but have been unable to connect with SASL EXTERNAL with Go.
My client code does not return any errors, but the RabbitMQ error logs tell me that the client closed the TCP connection
2021-06-24 18:57:22.029 [info] <0.16358.106> accepting AMQP connection <0.16358.106> (127.0.0.1:50610 -> 127.0.0.1:5671)
2021-06-24 18:57:23.030 [warning] <0.16358.106> closing AMQP connection <0.16358.106> (127.0.0.1:50610 -> 127.0.0.1:5671):
client unexpectedly closed TCP connection
My client code is as follows:
package main
import (
"fmt"
"github.com/apache/qpid-proton/go/pkg/amqp"
"github.com/apache/qpid-proton/go/pkg/electron"
"os"
"crypto/tls"
"io/ioutil"
"crypto/x509"
"time"
)
func main() {
keyPair, err := tls.LoadX509KeyPair("client.crt", "client.key")
if err != nil {
fmt.Println("Failed to load certificate:", err)
os.Exit(1)
}
rootCa, err := ioutil.ReadFile("rootCA.crt")
if err != nil {
fmt.Println("Failed to read root CA:", err)
os.Exit(1)
}
certPool := x509.NewCertPool()
certPool.AppendCertsFromPEM(rootCa)
tlsConfig := &tls.Config{
RootCAs: certPool,
InsecureSkipVerify: true,
Certificates: []tls.Certificate{keyPair},
}
container := electron.NewContainer("myContainer")
tlsConn, err := tls.Dial("tcp", "rabbitmq.default.svc.cluster.local:5671", tlsConfig)
if err != nil {
fmt.Println("Failed to open TLS connection:", err)
os.Exit(1)
}
defer tlsConn.Close()
conn, err := container.Connection(
tlsConn,
electron.SASLEnable(),
electron.SASLAllowedMechs("EXTERNAL"),
)
defer conn.Close(err)
if err != nil {
fmt.Println("Failed to open AMQP connection", err)
os.Exit(1)
}
sess, err := conn.Session()
sender, err := sess.Sender(electron.Target("demo-queue"))
if err != nil {
fmt.Println("Creating sender failed:", err)
os.Exit(1)
}
for i := int64(0); i < 100000 ; i++ {
msg := amqp.NewMessage()
body := fmt.Sprintf("Test message %d", i)
msg.Marshal(body)
sender.SendSync(msg)
time.Sleep(1*time.Second)
}
}
This isn't a solution for using the qpid-proton client library but I ended up using https://github.com/Azure/go-amqp to connect to RabbitMQ through SASL EXTERNAL. This library recently had the functionality added.
i can send msg from client to server successfully. But when i try to reply message back to client, somehow client cant receive the message.
Client:
conn, _:= net.Dial("udp", serv_addr:port)
defer conn.close()
buf:= []byte("Hey, server")
conn.Write(buf)
recv:= make([]byte, 1024)
fmt.Println("Reading...\n")
conn.Read(recv)
Server:
addr, _:= net.ResolveUDPAddr("udp", addr:port)
msg := make([]byte, 1024)
conn,_: net.ListenUDP("udp", addr)
_, ret_addr, _: conn.ReadFromUDP(msg)
//print msg
conn.WriteToUDP([]byte("got your msg", ret)
It's a very simple udp connection. server can printout the msg from client, but on client side, it keeps waiting before conn.read() while server finished its task.
I followed this article. my implementation is almost identical.
Here is a minimum code for you to play around with.
start a server part in one terminal:
go run main.go server :53033
start client in the other terminal:
go run main.go client :53033
now type messages at the client terminal and you should see both client and server exchanging messages. You can start multiple clients (in new terminal windows) and communicate with the same server.
package main
import (
"bufio"
"fmt"
"io"
"log"
"net"
"os"
)
func server(url string) {
log.Printf("serving on %s\n", url)
addr, err := net.ResolveUDPAddr("udp", url)
errcheck(err)
conn, err := net.ListenUDP("udp", addr)
errcheck(err)
defer close(conn)
msg := make([]byte, 1024)
for {
n, retAddr, err := conn.ReadFromUDP(msg)
errcheck(err)
log.Printf("received %v bytes, ret addr %v, msg %s", n, retAddr, string(msg[:n]))
reply := []byte(fmt.Sprintf("received from you: %v bytes", n))
n, err = conn.WriteToUDP(reply, retAddr)
errcheck(err)
log.Printf("sent reply %v bytes\n", n)
}
}
func client(url string) {
log.Printf("client for server url: %s\n", url)
addr, err := net.ResolveUDPAddr("udp", url)
errcheck(err)
conn, err := net.DialUDP("udp", nil, addr)
errcheck(err)
defer close(conn)
msg := make([]byte, 512)
scanner := bufio.NewScanner(os.Stdin)
for {
if scanner.Scan() {
line := scanner.Text()
n, err := conn.Write([]byte(line))
errcheck(err)
log.Printf("sent %d bytes \n", n)
n, err = conn.Read(msg)
errcheck(err)
log.Printf("server replied with: %s \n", string(msg[:n]))
}
}
}
func main() {
args := os.Args[1:]
if len(args) < 2 {
log.Fatal("expecting 2 arguments client or server first followed by the address:port")
}
switch args[0] {
case "server":
server(args[1])
case "client":
client(args[1])
default:
log.Fatal("first argument must be server or client")
}
}
func errcheck(err error) {
if err != nil {
log.Fatal(err)
}
}
func close(c io.Closer) {
err := c.Close()
if err != nil {
log.Fatal()
}
}
Have fun!
i have a problem with my golang server in which i'm using websockets.
The server opens the connection and the client could connect to it, but the problem is that when the server starts sending the data to the client, the client connection is closed after a small period of time. i suggest that the problem is with the server and not with the client because i tried to connect to the server with another web client, and it's the same issue. I didn't understand the cause ! Can someone help me?
server.go:
func Echo(ws *websocket.Conn) {
fmt.Println("Echoing")
for {
msg := MessageReceived{Name: "OrderCommand", Nbmsg: 3}
if err := websocket.JSON.Send(ws, msg); err != nil {
fmt.Println("Can't send")
break
}
//os.Exit(0)
}
}
func checkError(err error) {
if err != nil {
Log("Fatal error ", err.Error())
os.Exit(1)
}
}
func main() {
http.HandleFunc("/save", saveHandler)
http.Handle("/", websocket.Handler(Echo))
err:= http.ListenAndServe(":8081", nil)
checkError(err)
}
and client.go:
import (
"code.google.com/p/go.net/websocket"
"fmt"
"log"
)
func main() {
origin := "http://localhost/"
url := "ws://localhost:8081/echo"
ws, err := websocket.Dial(url, "", origin)
if err != nil {
log.Fatal(err)
}
var msg = make([]byte, 512)
var n int
if n, err = ws.Read(msg); err != nil {
log.Fatal(err)
}
fmt.Printf("Received: %s.\n", msg[:n])
}
Your problem, as others have pointed out, is that you must receive a message as well.
Currently, when someone connects, your program will step into the for-loop and start bombarding the client with messages. Probably not the intended behaviour of an echo server.
First you want to Receive a message, then Send a reply:
func Echo(ws *websocket.Conn) {
fmt.Println("Echoing")
msg := new(MessageReceived)
for {
// The server blocks here until a message from the client is received
websocket.JSON.Receive(ws, &msg)
fmt.Printf("Received message: %+v\n", msg)
// Reencode the same message and send it back
if err := websocket.JSON.Send(ws, msg); err != nil {
fmt.Println("Can't send echo")
break
}
}
}
A full working version can be found at Playground: http://play.golang.org/p/nQ3fJ5Nb0I
Since it uses websockets, you must compile it on your local computer.
Why using ws.Read when you can use websocket.JSON.Receive to deserialize the message?
Here are the server: http://play.golang.org/p/NZ6VJ4daGm
and the client: http://play.golang.org/p/rkJVKGhrGk (that I have changed to receive 10 messages before exiting).
The string "Can't send" will be printed by the server once the client closes the websocket connection.