Howdy đź‘‹

I’m Boone. This is my personal and unaffiliated space for thoughts on software development, leadership, fatherhood, and nonsense (not necessarily in that order).

WHM: Disable LFD Notifications

Disabling LFD Notifications in WHM When administrating a server, I prefer to focus on meaningful log messages. If too many alerts are coming through, the important messages can be lost in the struggle. On one of our servers, there was a high volume of lfd perm block notifications. Email notifications similar to: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Subject: lfd on host.thegillagency.com: blocked 190.179.171.170 (AR/Argentina/190-179-171-170.speedy.com.ar) Time: Tue Jan 10 15:50:12 2017 -0500 IP: 190.179.171.170 (AR/Argentina/190-179-171-170.speedy.com.ar) Failures: 5 (sshd) Interval: 3600 seconds Blocked: Permanent Block Log entries: Jan 10 15:49:55 host sshd[2159]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=190.179.171.170 user=root Jan 10 15:49:57 host sshd[2159]: Failed password for root from 190.179.171.170 port 48435 ssh2 Jan 10 15:49:59 host sshd[2159]: Failed password for root from 190.179.171.170 port 48435 ssh2 Jan 10 15:50:01 host sshd[2159]: Failed password for root from 190.179.171.170 port 48435 ssh2 Jan 10 15:50:10 host sshd[2178]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=190.179.171.170 user=root Resolution This is how to disable these messages within WHM/Cpanel. ...

January 9, 2017 Â· 2 min Â· 221 words

2016 In Review

2016 In continuation of last year’s post, I present my year in review. Open Source Projects Published go-json-rest-middleware-formjson - This package provides a Go-Json-Rest middleware useful for converting request data with the content type “application/x-www-form-urlencoded” to “application/json” Open Source Projects Contributed to GoBenchUI Microsoft Azure SDK for Go Start-ups still standing Human Planet Cool start-up checkboxes checked Selected as one of 5 companies to participate in Techstar’s Startup Next 2016 Austin program. Great experience and connections, can’t say enough good things about the program or the organization. New side projects in the works DoorMatX - Very excited to be collaborating with Daniel Johnston on getting his iconic “Hi How Are You” artwork onto doormats all over the world. Favorite books read Full list of books read here. ...

January 6, 2017 Â· 3 min Â· 603 words

2016 Reading list

In continuation of last year’s reading list, here is my third-annual reading list. Thanks to goodreads, making this list was much easier this time! As usual, there’s an eclectic mix of genres. In alphabetical order by title… A Game of Thrones (A Song of Ice and Fire, #1) by Martin, George R.R. A Perfect Spy by le Carré, John American Gods (American Gods, #1) by Gaiman, Neil * April 4, 1968: Martin Luther King, Jr.’s Death and How It Changed America by Dyson, Michael Eric Book of Numbers: A Novel by Cohen, Joshua Childhood’s End by Clarke, Arthur C. Crossing to Safety by Stegner, Wallace Flow by Csikszentmihalyi, Mihaly Growth Hacker Marketing: A Primer on the Future of PR, Marketing, and Advertising by Holiday, Ryan * Ham on Rye by Bukowski, Charles I Know Why the Caged Bird Sings by Angelou, Maya In the Heart of the Sea: The Tragedy of the Whaleship Essex by Philbrick, Nathaniel * John Adams by McCullough, David Leviathan Wakes (Expanse, #1) by Corey, James S.A. * Losing My Virginity: How I’ve Survived, Had Fun, and Made a Fortune Doing Business My Way by Branson, Richard Mindfulness in Plain English by Gunaratana, Henepola Missoula: Rape and the Justice System in a College Town by Krakauer, Jon * Nine Stories by Salinger, J.D. Not My Father’s Son by Cumming, Alan Shakespeare Saved My Life: Ten Years in Solitary with the Bard by Bates, Laura Sick in the Head: Conversations About Life and Comedy by Apatow, Judd Steve Jobs: 50 Life and Business Lessons from Steve Jobs by Ilian, George Tarzan of the Apes (Tarzan, #1) by Burroughs, Edgar Rice Team of Rivals: The Political Genius of Abraham Lincoln by Goodwin, Doris Kearns The Art of Living: The Classical Manual on Virtue, Happiness and Effectiveness by Lebell, Sharon The Ascent of Money: A Financial History of the World by Ferguson, Niall The Atlantis World (The Origin Mystery, #3) by Riddle, A.G. * The Beginning of Infinity: Explanations That Transform the World by Deutsch, David The Bell Tolls for No One by Bukowski, Charles The Cryptographer’s Way by III, Dr. Bradford Hardie The End of Average: How We Succeed in a World That Values Sameness by Rose, Todd The Good Earth by Buck, Pearl S. The Life and Times of the Thunderbolt Kid by Bryson, Bill The Man in the High Castle by Dick, Philip K. The Miracle of Mindfulness: An Introduction to the Practice of Meditation by Nhất Hạnh, Thích The New New Thing: A Silicon Valley Story by Lewis, Michael The Snowball: Warren Buffett and the Business of Life by Schroeder, Alice Then We Came to the End by Ferris, Joshua Transcendence: Healing and Transformation Through Transcendental Meditation by Rosenthal, Norman E. * Twenty Thousand Leagues Under the Sea by Verne, Jules Written in Fire (Brilliance Saga, #3) by Sakey, Marcus Yes Please by Poehler, Amy Your Heart Is a Muscle the Size of a Fist by Yapa, Sunil *

January 6, 2017 Â· 3 min Â· 494 words

JWT Authentication with Golang GraphQL & Relay

Background github.com/graphql-go/graphql and github.com/graphql-go/relay provide a great starting point for creating your own Relay-compliant GraphQL server with Go. Authorization, however, is left up completely to the developer. Here is how we implemented a /login endpoint and passed the resulting context to the GraphQL Handler. We’ll leave the handling of these context values within GraphQL to another post… Code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 1 package main 2 3 import ( 4 "encoding/json" 5 "net/http" 6 7 jwt "github.com/dgrijalva/jwt-go" 8 "github.com/graphql-go/graphql" 9 "github.com/graphql-go/handler" 10 "github.com/jinzhu/gorm" 11 _ "github.com/jinzhu/gorm/dialects/postgres" 12 ) 13 14 func main() { 15 setupServer() 16 } 17 18 func setupMux() *http.ServeMux { 19 mux := http.NewServeMux() 20 21 // graphql Handler 22 graphqlHandler := http.HandlerFunc(graphqlHandlerFunc) 23 24 // login Handler 25 mux.HandleFunc("/login", loginFunc) 26 27 // add in addContext middlware 28 mux.Handle("/graphql", requireAuth(graphqlHandler)) 29 30 return mux 31 } 32 33 func setupServer() { 34 http.ListenAndServe(":8080", setupMux()) 35 } 36 37 // graphqlHandlerFunc creates the graphql handler 38 func graphqlHandlerFunc(w http.ResponseWriter, r *http.Request) { 39 // get query 40 opts := handler.NewRequestOptions(r) 41 42 // execute graphql query 43 params := graphql.Params{ 44 Schema: Schema, // defined in another file 45 RequestString: opts.Query, 46 VariableValues: opts.Variables, 47 OperationName: opts.OperationName, 48 Context: r.Context(), // pass http.Request.Context() to our graphql object 49 } 50 result := graphql.Do(params) 51 52 // output JSON 53 var buff []byte 54 w.WriteHeader(http.StatusOK) 55 if prettyPrintGraphQL { 56 buff, _ = json.MarshalIndent(result, "", "\t") 57 } else { 58 buff, _ = json.Marshal(result) 59 } 60 w.Write(buff) 61 } 62 63 // type definition for our claims 64 type Claims struct { 65 UserID uint64 `json:"userID"` 66 IsAdmin bool `json:"isAdmin"` 67 jwt.StandardClaims 68 } 69 70 // secret string for signing requests 71 var jwtSecret = []byte("secret") // make sure you change this to something secure 72 73 // key type is not exported to prevent collisions with context keys defined in 74 // other packages. 75 type key int 76 77 // userAuthKey is the context key for our added struct. Its value of zero is 78 // arbitrary. If this package defined other context keys, they would have 79 // different integer values. 80 const userAuthKey key = 0 81 82 // validate JWT submitted via Authorization Header and set context claims 83 func requireAuth(next http.Handler) http.Handler { 84 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 85 // extract jwt 86 authorizationHeader := r.Header.Get("Authorization") 87 authRegex, _ := regexp.Compile("(?:Bearer *)([^ ]+)(?: *)") 88 authRegexMatches := authRegex.FindStringSubmatch(authorizationHeader) 89 if len(authRegexMatches) != 2 { 90 // didn't match valid Authorization header pattern 91 httpError(w, "not authorized", http.StatusUnauthorized) 92 return 93 } 94 jwtToken := authRegexMatches[1] 95 96 // parse tokentoken 97 token, err := jwt.ParseWithClaims(jwtToken, &Claims{}, func(token *jwt.Token) (interface{}, error) { 98 if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { 99 return nil, fmt.Errorf("Unexpected signing method") 100 } 101 return jwtSecret, nil 102 }) 103 if err != nil { 104 httpError(w, "not authorized", http.StatusUnauthorized) 105 return 106 } 107 108 // extract claims 109 claims, ok := token.Claims.(*Claims) 110 if !ok || !token.Valid { 111 httpError(w, "not authorized", http.StatusUnauthorized) 112 return 113 } 114 115 // load userID & isAdmin into context 116 authContext := struct { 117 UserID uint64 `json:"userId"` 118 IsAdmin bool `json:"isAdmin"` 119 }{ 120 claims.UserID, 121 claims.IsAdmin, 122 } 123 ctx := context.WithValue(r.Context(), userAuthKey, authContext) 124 next.ServeHTTP(w, r.WithContext(ctx)) 125 }) 126 } 127 128 // loginFunc confirms login credentials and creates JWT if valid 129 func loginFunc(w http.ResponseWriter, req *http.Request) { 130 // get username & password 131 decoder := json.NewDecoder(req.Body) 132 requestBody := struct { 133 Username string `json:"username"` 134 Password string `json:"password"` 135 }{} 136 err := decoder.Decode(&requestBody) 137 if err != nil { 138 http.Error(w, err.Error(), http.StatusInternalServerError) 139 return 140 } 141 defer req.Body.Close() 142 143 // confirmLogin is up to you to define 144 user, err := confirmLogin(requestBody) 145 if err != nil { 146 http.Error(w, "invalid login", http.StatusUnauthorized) 147 return 148 } 149 150 //generate token 151 expireToken := time.Now().Add(time.Hour * 1).Unix() 152 claims := Claims{ 153 user.ID, 154 user.IsAdmin, 155 jwt.StandardClaims{ 156 ExpiresAt: expireToken, 157 Issuer: "localhost:8080", 158 }, 159 } 160 token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 161 signedToken, _ := token.SignedString(jwtSecret) 162 163 //output token 164 tokenResponse := struct { 165 Token string `json:"token"` 166 }{signedToken} 167 json.NewEncoder(w).Encode(tokenResponse) 168 } Explanation The loginFunc() function at /login receives the posted request and extracts a username and password from it. ConfirmLogin() is not outlined above, but is easy enough to implement. In our scenario, we validate the login against our database and return the user or an error. If the login is valid, we create and return the JWT with our encoded custom claim values (UserID & IsAdmin). ...

October 11, 2016 Â· 5 min Â· 980 words

Pass HTTP request Context to GraphQL Handler

Background Go’s “net/http” package provides great out of the box server capabilites with extensible middleware. In order to leverage http middleware in combination with the “github.com/graphql-go/graphql” package, we needed to pass the http.Request.Context() to the graphql handler. Here is how we did so: Code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 1 package main 2 3 import ( 4 "encoding/json" 5 "net/http" 6 7 "github.com/graphql-go/graphql" 8 "github.com/graphql-go/handler" 9 "github.com/jinzhu/gorm" 10 _ "github.com/jinzhu/gorm/dialects/postgres" 11 ) 12 13 func main() { 14 setupServer() 15 } 16 17 func setupMux() *http.ServeMux { 18 mux := http.NewServeMux() 19 20 // graphql Handler 21 graphqlHandler := http.HandlerFunc(graphqlHandlerFunc) 22 23 // add in addContext middlware 24 mux.Handle("/graphql", addContext(graphqlHandler)) 25 26 return mux 27 } 28 29 func setupServer() { 30 http.ListenAndServe(":8080", setupMux()) 31 } 32 33 func graphqlHandlerFunc(w http.ResponseWriter, r *http.Request) { 34 // get query 35 opts := handler.NewRequestOptions(r) 36 37 // execute graphql query 38 params := graphql.Params{ 39 Schema: Schema, // defined in another file 40 RequestString: opts.Query, 41 VariableValues: opts.Variables, 42 OperationName: opts.OperationName, 43 Context: r.Context(), 44 } 45 result := graphql.Do(params) 46 47 // output JSON 48 var buff []byte 49 w.WriteHeader(http.StatusOK) 50 if prettyPrintGraphQL { 51 buff, _ = json.MarshalIndent(result, "", "\t") 52 } else { 53 buff, _ = json.Marshal(result) 54 } 55 w.Write(buff) 56 } 57 58 // Key type is not exported to prevent collisions with context keys defined in 59 // other packages. 60 type key int 61 62 // userAuthKey is the context key for our added struct. Its value of zero is 63 // arbitrary. If this package defined other context keys, they would have 64 // different integer values. 65 const userAuthKey key = 0 66 67 // add values to context 68 func addContext(next http.Handler) http.Handler { 69 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 70 authContext := map[string]interface{}{ 71 "key1": 23, 72 "key2": "another value", 73 } 74 ctx := context.WithValue(r.Context(), userAuthKey, authContext) 75 next.ServeHTTP(w, r.WithContext(ctx)) 76 }) 77 } Explanation The AddContext() function is taking the standard http.Request.Context() and supplementing it with our additonal “authContext” map. This Context is then passed to the graphqlHandlerFunc where we setup a new GraphQL instance with the Context initialized to http.Request.Context(). ...

October 10, 2016 Â· 3 min Â· 447 words