How to change which IP address a go-fiber client is using? - go

I'm using Fiber as an HTTP client to make some requests to an http server, however I'm being rate limited. On my vm I configured 5 different IP addresses (public/private) and have confirmed that they are indeed connected to the internet.
curl --interface 10.0.0.4 ipinfo.io/json
curl --interface 10.0.0.5 ipinfo.io/json
...
curl --interface 10.0.0.8 ipinfo.io/json
each one returns a different public facing ip address.
Now I'm interested in making round-robin requests using these local addresses but I'm not so sure how to go about it.
Is there some sort of property or function I can set/call to change where the outgoing request is coming from?
I've looked around at fasthttp.HostClient which fiber.Agent extends but I didn't see anything useful.
Thanks guys.

a := fiber.AcquireAgent()
req := a.Request()
req.Header.SetMethod(fiber.MethodGet)
req.SetRequestURI(fmt.Sprintf(formatUrl, args...))
if err := a.Parse(); err != nil {
h.Logger.Error("%v", err)
return fiber.StatusInternalServerError, nil, []error{err}
}
customDialer := fasthttp.TCPDialer{
Concurrency: 1000,
LocalAddr: &net.TCPAddr{
IP: h.IPPool[atomic.AddUint32(&h.IpIdx, 1)%uint32(len(h.IPPool))],
},
}
a.HostClient.Dial = func(addr string) (net.Conn, error) {
return customDialer.Dial(addr)
}
Creating a custom dialer and dial func allows you to change the local address associated with the http request.

Related

How to connect Go API to Cassandra DB

I'm using a Go API and I need to connect it to my Cassandra DB on a Google Cloud server.
This is my Go connection code:
func SetupDBConnection() {
cluster := gocql.NewCluster("XX.XXX.XXX.XXX")
cluster.Keyspace = "afterstr"
cluster.Consistency = gocql.Quorum
s, err := cluster.CreateSession()
if err != nil {
fmt.Println(err)
}
}
But, when I run this code, I have this error:
gocql: unable to create session: control: unable to connect to initial hosts: dial tcp xx.xxx.xxx.xxx:9042: i/o timeout
This is my Google Cloud configuration:
I also modified the cassandra.yaml file in my server to change the listen address and the RPC address to put my IP with the port I use to run my Go program that I opened.
(It is opened 3 times because I was just testing something)
Try:
cluster.Authenticator = gocql.PasswordAuthenticator{Username: username, Password: password}

How to confirm gRPC traffic from Go client is TLS encrypted

I wrote a sample gRPC client a server in Go, both configured for server-authenticated TLS.
The client gRPC call succeeds, giving me the impression the TLS is configured properly, otherwise if the TLS handshake had failed, I would expect the client to fail and not make the gRPC request (i.e. not default to plaintext).
Yet I am puzzled by a result I obtain when I attach Wireshark to that network to sniff TCP packets. I do not see any packet with TLS, for e.g. I do not see the TLS CLIENT HELLO packet.
So is this because I'm misinterpreting what I see in Wireshark, or is my gRPC client actually doing plaintext gRPC?
The client code looks like this, note the grpc.withTransportCredentials which I think means it will use TLS or fail, but never plaintext:
// block the dial until connection is successful or 3 sec timeout
dialOptions := []grpc.DialOption{
grpc.WithBlock(),
grpc.WithTimeout(3 * time.Second),
}
// Load TLS Configuration
tlsCredentials, err := LoadTLSCredentials()
if err != nil {
log.Fatalf("Failed to load TLS credentials: %v", err)
}
dialOptions = append(dialOptions, grpc.WithTransportCredentials(tlsCredentials))
// Dial the gRPC server
log.Printf("Dialing %v", *address)
conn, err := grpc.Dial(*address, dialOptions...)
if err != nil {
log.Fatalf("Failed to connect to the server: %v", err)
}
defer conn.Close()
// then this application sets up a gRPC request, and logs the response to stdout,
// in my testing stdout shows the expected gRPC response, so I'd assume TLS is working.
func LoadTLSCredentials() (credentials.TransportCredentials, error) {
rootCA, err := ioutil.ReadFile("ca.cert")
if err != nil {
return nil, err
}
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(rootCA) {
return nil, fmt.Errorf("Failed to add rootCA to x509 certificate pool")
}
config := &tls.Config{
MinVersion: tls.VersionTLS12,
RootCAs: certPool,
}
return credentials.NewTLS(config), nil
}
And here's a screenshot of Wireshark showing no TLS packet
whereas I would expect something similar to the following which clearly shows some TLS activity (not my app, image is from the web for illustration purposes)
I'm running Wireshark v2.6.10 on Ubuntu 16.04. The source and destination IPs match my gRPC client and server IPs (both are docker containers on the same docker network).
Not that it really matters, but as can be seen in my client code, I'm sharing a root CA certificate on the client (self signed). I can do this because I deploy both the client and the server.
As #steffanUllrich explained in the comments, this was a case of Wireshark can be better configured to show TLS. I confirmed the gRPC exchange is indeed TLS protected.
You should right click the packet list, and select 'decode as..' menu item, then select 'tls' to force wireshark dissect traffic in this tcp port as TLS.

How can I get the client IP address and user-agent in Golang gRPC?

I set up a series of gRPC requests and responses which all work fine, but I'm stuck when I try to get the client IP address and user-agent who is calling my gRPC APIs.
I read the Go gRPC documentation and other sources, but didn't find much valuable information. Few of them are talking about gRPC in Golang.
Should I set up a key-value to store the IP address in the context when setting up the gRPC APIs?
In Golang GRPC, you can use
func (UserServicesServer) Login(ctx context.Context, request *sso.LoginRequest) (*sso.LoginResponse, error) {
p, _ := peer.FromContext(ctx)
request.Frontendip = p.Addr.String()
.
.
}
But, do not forget import "google.golang.org/grpc/peer"
For grpc-gateway is used, the client IP address may be retrieved through x-forwarded-for like this:
// Get IP from GRPC context
func GetIP(ctx context.Context) string {
if headers, ok := metadata.FromIncomingContext(ctx); ok {
xForwardFor := headers.Get("x-forwarded-for")
if len(xForwardFor) > 0 && xForwardFor[0] != "" {
ips := strings.Split(xForwardFor[0], ",")
if len(ips) > 0 {
clientIp := ips[0]
return clientIp
}
}
}
return ""
}
In Golang GRPC, context has 3 values
authority
content-type
user-agent
md,ok:=metadata.FromIncomingContext(ctx)
fmt.Printf("%+v%+v",md,ok)

Getting Unimplemented desc = unknown service error gRPC

In one of my services that happens to be my loadbalancer, I am getting the following error when calling the server method in one of my deployed services:
rpc error: code = Unimplemented desc = unknown service
fooService.FooService
I have a few other services set up with gRPC and they work fine. It just seems to be this one and I am wondering if that is because it is the loadbalancer?
func GetResult(w http.ResponseWriter, r *http.Request) {
conn, errOne := grpc.Dial("redis-gateway:10006", grpc.WithInsecure())
defer conn.Close()
rClient := rs.NewRedisGatewayClient(conn)
result , errTwo := rClient.GetData(context.Background(), &rs.KeyRequest{Key: "trump", Value: "trumpVal"}, grpc.WithInsecure())
fmt.Fprintf(w, "print result: %s \n", result) //prints nil
fmt.Fprintf(w, "print error one: %v \n", errOne) // prints nil
fmt.Fprintf(w, "print error two: %s \n", errTwo) // prints error
}
The error says there is no service called fooService.FooService which is true because the dns name for the service I am calling is called foo-service. However it is the exact same setup for my other services that use gRPC and work fine. Also my proto files are correctly configured so that is not an issue.
server I am calling:
func main() {
lis, err := net.Listen("tcp", ":10006")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
grpcServer := grpc.NewServer()
newServer := &RedisGatewayServer{}
rs.RegisterRedisGatewayServer(grpcServer, newServer)
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
The function I am trying to access from client:
func (s *RedisGatewayServer) GetData(ctx context.Context, in *rs.KeyRequest)(*rs.KeyRequest, error) {
return in, nil
}
My docker and yaml files are all correct also with the right naming and ports.
I had this exact problem, and it was because of a very simple mistake: I had put the call to the service registration after the server start. The code looked like this:
err = s.Serve(listener)
if err != nil {
log.Fatalf("[x] serve: %v", err)
}
primepb.RegisterPrimeServiceServer(s, &server{})
Obviously, the registration should have been called before the server was ran:
primepb.RegisterPrimeServiceServer(s, &server{})
err = s.Serve(listener)
if err != nil {
log.Fatalf("[x] serve: %v", err)
}
thanks #dolan's for the comment, it solved the problem.
Basically we have to make sure, the method value should be same in both server and client (you can even copy the method name from the pb.go file generated from server side)
func (cc *ClientConn) Invoke(ctx context.Context, method string, args, reply interface{}, opts ...CallOption) error {
this invoke function will be there inside all the methods which you have implemented in gRPC service.
for me, my client was connecting to the wrong server port.
My server listens to localhost:50052
My client connects to localhost:50051
and hence I see this error.
I had the same problem - I was able to perform rpc call from Postman to the service whereas apigateway was able to connect to the service but on method call It gave error code 12 unknown service and the reason was in my proto files client was under package X whereas server was under package Y.
Quite silly but yeah making the package for both proto under apigateway and service solved my problem.
There are many scenarios in which this can happen. The underlying issue seems to be the same - the GRPC server is reachable but cannot service client requests.
The two scenarios I faced that are not documented in previous answers are:
1. Client and server are not running the same contract version
A breaking change introduced in the contract can cause this error.
In my case, the server was running the older version of the contract while the client was running the latest one.
A breaking change meant that the server could not resolve the service my client was asking for thus returning the unimplemented error.
2. The client is connecting to the wrong GRPC server
The client reached the incorrect server that doesn't implement the contract.
Consider this scenario if you're running multiple different GRPC services. You might be mistakingly dialing the wrong one.

How to get remote client's IPV4 address from request object

I am using go-gin as server and rendering an html using the code like the following
func dashboardHandler(c *gin.Context) {
c.HTML(200, "dashboard", gin.H{
"title": "Dashboard"
})
Along with title I want to pass the remote client's IPV4 address as well. I tried using the following code to get the IP address but for localhost it gives me ::1:56797 as output. My server is running on localhost:8080
ip, port, err := net.SplitHostPort(c.Request.RemoteAddr)
fmt.Println(ip + ":" + port)
if err != nil {
fmt.Println(err.Error())
}
I followed Correct way of getting Client's IP Addresses from http.Request (Golang) for reference. Is there any way I get the IPV4 address from the request?
you can use this function to get the ip and user agent, but it will give a bracket character if you are trying from localhost but if you try from somewhere else it will work.
func GetIPAndUserAgent(r *http.Request) (ip string, user_agent string) {
ip = r.Header.Get("X-Forwarded-For")
if ip == "" {
ip = strings.Split(r.RemoteAddr, ":")[0]
}
user_agent = r.UserAgent()
return ip, user_agent
}

Resources