Golang http client handshake failure - ssl

Try get webpage:
tr := &http.Transport{
TLSHandshakeTimeout: 30 * time.Second,
DisableKeepAlives: true,
}
client := &http.Client{Transport: tr}
req, err := http.NewRequest("GET", "https://www.fl.ru/", nil)
if err != nil {
log.Fatalf("%s\n", err);
}
resp, err := client.Do(req);
if err != nil {
log.Fatalf("%s\n", err);
}
defer resp.Body.Close()
Get https://www.fl.ru/: remote error: handshake failure.
If I try to get another HTTPS page - all is OK.

That server only supports a few, weak ciphers:
TLS_DHE_RSA_WITH_AES_256_CBC_SHA (0x39) DH 1024 bits (p: 128, g: 1, Ys: 128) FS WEAK
TLS_DHE_RSA_WITH_AES_128_CBC_SHA (0x33) DH 1024 bits (p: 128, g: 1, Ys: 128) FS WEAK
TLS_RSA_WITH_RC4_128_SHA (0x5) WEAK
If you really must connect to that server, Go does support the last cipher in the list, but not by default. Create a client with a new tls.Config specifying the cipher you want:
t := &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
TLSHandshakeTimeout: 10 * time.Second,
TLSClientConfig: &tls.Config{
CipherSuites: []uint16{tls.TLS_RSA_WITH_RC4_128_SHA},
},
}

Related

How to properly register RabbitMQ exclusive-consumer

Summary
It seems pretty simple to implement an exclusive consumer in RabbitMQ. You just need to enable the exclusive flag while starting the consumption or that's what I thought. I tried this approach but for some reason, it's creating multiple exclusive consumers which actually contradicts with it's own definition.
Setup
I am using following setup -
Exchange: default
Queue: quorum, durable
Messages: durable
Here is the full code that I am using to register exclusive consumer -
package main
import (
"fmt"
amqp "github.com/rabbitmq/amqp091-go"
"sync"
"time"
)
type Config struct {
Schema string
Host string
Port string
Username string
Password string
Vhost string
}
type Rabbit struct {
config Config
connection *amqp.Connection
lock sync.Mutex
}
// NewRabbit returns a Rabbit instance.
func NewRabbit() *Rabbit {
// setup appropriate values
config := Config{
Host: "",
Username: "",
Password: "",
Port: "",
Vhost: "",
Schema: "",
}
return &Rabbit{
config: config,
}
}
// Connect connects to RabbitMQ server.
func (r *Rabbit) Connect() error {
r.lock.Lock()
defer r.lock.Unlock()
// Check if connection is already available
if r.connection == nil || r.connection.IsClosed() {
// Try connecting
con, err := amqp.DialConfig(fmt.Sprintf(
"%s://%s:%s#%s:%s/%s",
r.config.Schema,
r.config.Username,
r.config.Password,
r.config.Host,
r.config.Port,
r.config.Vhost,
), amqp.Config{})
if err != nil {
return err
}
r.connection = con
}
return nil
}
func (r *Rabbit) StartConsumer(queueName string) error {
chn, err := r.connection.Channel()
if err != nil {
return err
}
// Make sure we process 1 message at a time
if err := chn.Qos(1, 0, false); err != nil {
return err
}
_, err = chn.QueueDeclare(
queueName,
true,
false,
false,
false,
amqp.Table{"x-queue-type": "quorum"}) // This will ensure that the created queue is quorum-queue
if err != nil {
fmt.Printf("Error creating queue with name: %s, err: %s", queueName, err.Error())
return err
}
messages, err := chn.Consume(
queueName,
queueName+"-consumer",
false,
true,
false,
false,
nil,
)
if err != nil {
fmt.Printf("Unable to start consumer for webhook queue: %s, err: %s", queueName, err.Error())
return err
}
go func() {
// This for-loop will wait indefinitely or until channel is closed
for msg := range messages {
fmt.Printf("Message: %v", msg.Body)
if err = msg.Ack(false); err != nil {
fmt.Printf("Unable to acknowledge the message, err: %s", err.Error())
}
}
}()
return nil
}
Here is the main.go
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
)
func main() {
interrupt := make(chan os.Signal)
signal.Notify(interrupt, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
defer signal.Stop(interrupt)
rabbit := NewRabbit()
if err := rabbit.Connect(); err != nil {
fmt.Printf("Can't connect to RabbitMQ server, err: %s", err.Error())
} else {
fmt.Println("Successfully connected to RabbitMQ server")
}
err := rabbit.StartConsumer("test-queue")
if err != nil {
fmt.Printf("Error: %s", err.Error())
}
select {
case <-interrupt:
fmt.Println("Interrupt signal received")
break
}
fmt.Println("Application is about to close")
}
Here is go.mod
module ExclusiveRabbitMQConsumer
go 1.17
require github.com/rabbitmq/amqp091-go v1.3.4
And here is what I see after running 3 instances of this application -
RabbitMQ Management UI
All 3 consumers are shown as exclusive. The consumer tags are same however that doesn't matter according to RabbitMQ documentation.
I'm not sure what's wrong here.

How to use TLS in Go ReverseProxy?

I have sites https://a-b-c.com and https://www.x-y-z.com running behind a reverse proxy on ports 4444 and 5555 respectively.
I had them configured to use letsencrypt tls certificates, but now I get an error when using the reverse proxy and I think I need to use a &tls.config{} that includes their certificates but I don't know how to set it up.
My ReverseProxy looks like:
director := func(req *http.Request) {
log.Println(req.Host)
switch req.Host {
case "a-b-c-.com":
req.URL.Host = "localhost:4444"
req.URL.Scheme = "https"
case "x-y-z.com":
req.URL.Host = "localhost:5555"
req.URL.Scheme = "https"
}
proxy := &httputil.ReverseProxy{Director: director}
proxy.Transport = &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
TLSHandshakeTimeout: 10 * time.Second,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
log.Fatalln(http.ListenAndServe(":443", proxy))
You don't have to set up your reverse proxy. What you need to do is to set up HTTP server that you attach your reverse proxy to.
Configure TLS listener by passing two pairs of cert/keys:
abcCrt, err := tls.LoadX509KeyPair("a-b-c.crt", "a-b-c.key")
if err != nil {
panic(err)
}
xyzCrt, err := tls.LoadX509KeyPair("x-y-z.crt", "z-y-z.key")
if err != nil {
panic(err)
}
tlsConfig := &tls.Config{Certificates: []tls.Certificate{abcCrt, xyzCrt}}
ln, err := tls.Listen("tcp", ":443", tlsConfig)
if err != nil {
panic(err)
}
http.Serve(ln, proxy)

Golang : https request with tls.dial

i am trying to make https request with golang.
conf := &tls.Config{
InsecureSkipVerify: true,
MinVersion:tls.VersionTLS10,
}
//TLS connection
tlsCon, err := tls.Dial("tcp", "youtube.com:443", conf)
if err != nil {
fmt.Println("SSL Error : " + err.Error())
return
}
defer tlsCon.Close()
state := tlsCon.ConnectionState()
fmt.Println("SSL ServerName : " + state.ServerName)
fmt.Println("SSL Handshake : ", state.HandshakeComplete)
fmt.Println("SSL Mutual : ", state.NegotiatedProtocolIsMutual)
request = '
CONNECT youtube.com:443 HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0
Proxy-Connection: close
Connection: close
Host: youtube.com:443
'
n, err = io.WriteString(tlsCon, request)
if err != nil {
fmt.Println("SSL Write error :", err.Error(), n)
}
n, err = tlsCon.Read(data)
if err != nil {
fmt.Println("SSL Read error : " + err.Error())
return
}
this is my code , state.HandshakeComplete, state.NegotiatedProtocolIsMutual returns true and state.ServerName returns = ''
when i write data to tls connection no error returns also tls connection never reply my request.
any help appricated. thanks.

How to configure SSL authentication

I have certificate.pem that I use to perform client authentication with a remote server. When I access the server, normally Chrome pops up, asks if I want to use that certificate, I say yes, then I'm authenticated. I'm trying to figure out why it's not sending the certificate with the dialer when I call it programmatically:
type DialerHelper func() (io.ReadWriter, error)
func DialIt(addr string, port uint16, config *tls.Config) (Dialer, error) {
address := fmt.Sprintf("%s:%d", addr, port)
return DialerHelper(func() (io.ReadWriter, error) {
return tls.Dial("tcp", address, config)
}), nil
}
caPool := x509.NewCertPool()
cert, err := ioutil.ReadFile("certificate.pem")
if err != nil {
panic(err)
}
ok := caPool.AppendCertsFromPEM(cert)
if !ok {
panic(ok)
}
tlsconfig := &tls.Config{
InsecureSkipVerify: true,
RootCAs: caPool, }
tlsconfig.BuildNameToCertificate()
DialIt("some.address.com", 443, tlsconfig)
I keep getting an error from the server saying there is no client certificate supplied. Am I sending the SSL certificate correctly to the remote server? I'm not an expert with SSL.
Edit: this is the functionality I'm trying to replicate: curl -k --cert /home/me/.ssh/certificate.pem
If the server is using a cert generated from your own Certificate Authority, then the following code will do the trick.
I've never tried Client Cert Authentication in an environment where the server cert is from a public CA, so I'm not sure how you'd achieve that. Perhaps just leaving out setting config.RootCAs.
func loadCertificates(caFileName, certFileName, keyFileName string) (tls.Certificate, *x509.CertPool, error) {
myCert, err := tls.LoadX509KeyPair(certFileName, keyFileName)
if err != nil {
return tls.Certificate{}, nil, err
}
ca, err := ioutil.ReadFile(caFileName)
if err != nil {
return tls.Certificate{}, nil, err
}
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(ca) {
return tls.Certificate{}, nil, errors.New("Failed appending certs")
}
return myCert, certPool, nil
}
func GetClientTlsConfiguration(caFileName, certFileName, keyFileName string) (*tls.Config, error) {
config := &tls.Config{}
myCert, certPool, err := loadCertificates(caFileName, certFileName, keyFileName)
if err != nil {
return nil, err
}
config.Certificates = make([]tls.Certificate, 1)
config.Certificates[0] = myCert
config.RootCAs = certPool
config.ClientCAs = certPool
return config, nil
}
tlsConfig, err := config.GetClientTlsConfiguration("ca.crt", "client.crt", "client.key")
if err != nil {
log.Fatalf("Error loading tls config - %v", err)
}
client := &http.Client{Transport: &http.Transport{TLSClientConfig: tlsConfig}}
client.Get(.....)

Confirm TLS certificate while performing ReverseProxy in GoLang

In Go I'm using NewSingleHostReverseProxy to perform a reverse proxy, however I need to confirm the SSL certificates of the host site, to make sure I have the correct secure certificate... any ideas how I should do this? Should I be doing this with the handler or transport? I'm new to Go and still getting my head around it.
proxy := httputil.NewSingleHostReverseProxy(&url.URL{
Scheme: "https",
Host: "sha256.badssl.com",
})
http.ListenAndServe("127.0.0.1:80", proxy)
To access the certificate, you will have get access to the ConnectionState. The easiest way to do that is to provide your own version of DialTLS. In there you connect to the server using net.Dial, do the TLS handshake and then you are free to verify.
package main
import (
"crypto/tls"
"log"
"net"
"net/http"
"net/http/httputil"
"net/url"
)
func main() {
proxy := httputil.NewSingleHostReverseProxy(&url.URL{
Scheme: "https",
Host: "sha256.badssl.com",
})
// Set a custom DialTLS to access the TLS connection state
proxy.Transport = &http.Transport{DialTLS: dialTLS}
// Change req.Host so badssl.com host check is passed
director := proxy.Director
proxy.Director = func(req *http.Request) {
director(req)
req.Host = req.URL.Host
}
log.Fatal(http.ListenAndServe("127.0.0.1:3000", proxy))
}
func dialTLS(network, addr string) (net.Conn, error) {
conn, err := net.Dial(network, addr)
if err != nil {
return nil, err
}
host, _, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
}
cfg := &tls.Config{ServerName: host}
tlsConn := tls.Client(conn, cfg)
if err := tlsConn.Handshake(); err != nil {
conn.Close()
return nil, err
}
cs := tlsConn.ConnectionState()
cert := cs.PeerCertificates[0]
// Verify here
cert.VerifyHostname(host)
log.Println(cert.Subject)
return tlsConn, nil
}
To tweak the SSL to the Reverse Host, it is possible to set the transport
options. So if you want to skip the verify you can set it like this.
proxy := httputil.NewSingleHostReverseProxy(&url.URL{
Scheme: "https",
Host: "sha256.badssl.com",
})
proxy.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
http.ListenAndServe("127.0.0.1:80", proxy)