// 2019 Rackspace, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. package tsip import ( "encoding/json" "fmt" "net" "net/http" "os" "github.com/gin-gonic/gin" "github.com/julienschmidt/httprouter" "github.com/sirupsen/logrus" "github.rackspace.com/gorax/libs/nsiuuid" "github.rackspace.com/gorax/tools/passkey" "github.rackspace.com/gorax/tools/raxmiddleware" "github.rackspace.com/gorax/tools/rgin" "github.rackspace.com/gorax/tools/rbac" ) // HTTPRouterLookup variable is an exportable list of the routes that this package provides. var HTTPRouterLookup = []rbac.Route{ { Methods: map[string]rbac.Handler{http.MethodGet: {lookupHTTPRouter, rbac.BasicRead}}, Paths: []string{"/tsip"}, }, } // GinLookup variable is an exportable list of the routes that this package provides. var GinLookup = []rgin.GinRouteGroup{ { BasePath: "/tsip", Middleware: []gin.HandlerFunc{raxmiddleware.PassKey(nil)}, Methods: []rgin.GinRoute{ { RelativePath: "/", Method: http.MethodGet, Handlers: func(c *gin.Context) { lookupGin(c) }, }, }, }, } // lookupHTTPRouter function is the main receiver for the /tsip HTTP router. func lookupHTTPRouter(rw http.ResponseWriter, r *http.Request, ps httprouter.Params) { // swagger:operation GET /tsip TrueSourceIP lookupIPAddress // // This resource will perform a lookup for the edge device of the IP Address provided, then look for the // translated (real) IP Address. // // --- // parameters: // - name: address // in: query // description: Public/Routable Rackspace IP Address // required: true // type: string // - name: port // in: query // description: Source port of connection to filter for PAT // required: false // type: string // // responses: // 200: // description: Successful action // schema: // $ref: '#/definitions/RealIP' // 400: // description: Bad Request // 404: // description: NAT translation not found on gateway device // 500: // description: Unknown Server-Side error // 501: // description: The gateway platform is not yet implemented // 502: // description: Bad gateway upstream issues communicating with external clients // 504: // description: Gateway timeout issues upstream on Network Device connectivity var respHeaders = map[string]string{ "Content-Type": "application/json", "Server": os.Getenv("SERVER"), "X-Request-ID": nsiuuid.FromContext(r.Context()).String(), } for k, v := range respHeaders { rw.Header().Set(k, v) } resp, code := func() (*RealIP, int) { var ip = new(RealIP) pk := passkey.FromContext(r.Context()) if pk == nil { ip.Error = append(ip.Error, "unable to initiate auth client. contact GTS AE NetSec (NSI)") return ip, http.StatusInternalServerError } client, err := NewClient(pk) if err != nil { ip.Error = append(ip.Error, err.Error()) return ip, http.StatusInternalServerError } ipAddr, err := GetAddress(r.URL.Query().Get("address")) if err != nil { ip.Error = append(ip.Error, err.Error()) return ip, http.StatusBadRequest } ip, code, err := client.GetRealIP(ipAddr, r.URL.Query().Get("port")) if err != nil { ip.Error = append(ip.Error, err.Error()) return ip, code } return ip, http.StatusOK }() rw.WriteHeader(code) b, err := json.Marshal(resp) if err != nil { if _, err := fmt.Fprintf(rw, err.Error()); err != nil { logrus.Fatal(err) } return } if _, err := fmt.Fprintf(rw, string(b)); err != nil { logrus.Fatal(err) } return } // lookupGin function is the main receiver for the /tsip Gin router. func lookupGin(c *gin.Context) { // Grab the passkey client from the Gin Context key := c.MustGet("passkey").(*passkey.Client) resp, code := func() (*RealIP, int) { var ip *RealIP client, err := NewClient(key) if err != nil { ip.Error = append(ip.Error, err.Error()) return ip, http.StatusInternalServerError } ipAddr, err := GetAddress(c.Request.URL.Query().Get("address")) if err != nil { ip.Error = append(ip.Error, err.Error()) return ip, http.StatusBadRequest } ip, code, err := client.GetRealIP(ipAddr, c.Request.URL.Query().Get("port")) if err != nil { ip.Error = append(ip.Error, err.Error()) return ip, code } return ip, http.StatusOK }() c.JSON(code, resp) return } // GetAddress is a small helper function to convert an IP address into a IP object. // This function will return an error if there are issues parsing or if there is an // empty address. func GetAddress(ipAddr string) (net.IP, error) { if ipAddr == "" { logrus.Error("Nil:GetAddress:MissingAddress") return nil, fmt.Errorf("Nil:GetAddress:MissingAddress") } if ip := net.ParseIP(ipAddr); ip != nil { return ip, nil } logrus.Errorf("%s:GetAddress:UnableToParseIP", ipAddr) return nil, fmt.Errorf("%s:GetAddress:UnableToParseIP", ipAddr) }