Boone Putney bio photo

Boone Putney

Software Development
Random Musings
Austin, Texas

HumanPlanet Soleer

Email LinkedIn Github

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:

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.

  1. Login to WHM
  2. Search for/navigation to: Plugins > ConfigServer Security & Firewall
  3. Click on “ConfigServer Firewall” Tab
  4. Click on “Firewall Configuration” button
  5. Search within the page for “LF_PERMBLOCK_ALERT”
  6. Set to “Off”
  7. Click “Change” at bottom of the page
  8. Click “Restart csf+lfd” to restart services

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

Start-ups still standing

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.

  • American Gods (American Gods, #1) by Gaiman, Neil - Unique tale intertwined with powerful critique on our changing culture of worship.
  • Book of Numbers: A Novel by Cohen, Joshua - Postmodern fiction, but more accessible than David Foster Wallace, especially with the focus on Silicon Valley Culture.
  • Tarzan of the Apes (Tarzan, #1) by Burroughs, Edgar Rice - Timelessly crafted tale with every angle well thought out and explained.
  • The Ascent of Money: A Financial History of the World by Ferguson, Niall - Intriguing history of money and finance and its powerful impact on the course of human history.
  • The Beginning of Infinity: Explanations That Transform the World by Deutsch, David - Thought provoking combination of mathematics, physics, and philosophy.

TV Series Watched

  • Walking Dead - Still. Love dystopian enough to stick with it, but didn’t love the season.
  • Game of Thrones - Still. Top 5 series of all time.
  • Daredevil - Not historically a comic book fan, but NetFlix’s superhero series are drawing me in with their character depth and production value.
  • Jessica Jones - Thought I was a comic book series convert, but didn’t love this one. Weaker character development and storytelling.
  • Sons of Anarchy - Soap opera for those who love violence. Over the course of the entire series, oscillated between intrigue and disgust. Stuck with it to the end, but not highly recommended.

Courses Taken

As Human Planet took up more time, continued education took a hit. Hope to work in more in the coming year.

Programming Languages/Frameworks/Technologies/etc. used

  • Golang
  • React
  • Angular
  • GraphQL
  • Relay
  • PostgreSQL
  • PHP
  • MySQL
  • Drupal
  • Wordpress
  • Python
  • Ruby
  • Jekyll
  • JavaScript
  • jQuery
  • SASS
  • CSS
  • HTML
  • AWS [Amazon Web Services]
    • S3
    • EC2
    • CloudFront
    • Route 53
      • SNS
  • Heroku
  • Azure
  • Websockets

Review Plans for 2016

Implement daily planning and time management

Grade: C-

Went through streaks, but didn’t settle into a nice rhythm. Definitely see better flow when doing daily planning, but when the tasks start piling up, I tend to grind instead of plan.

25+ Blog posts (setting a more realistic goal this time)

Grade: F+

I posted, but was once again considerably short of my goal.

Secure angel-funding for Human Planet

Grade: D-

Schedule was pushed back a bit, just now hitting the road trying to raise a seed round. Did manage to make some great progress launching iPhone app, taking part in Techstar’s Startup Next Program, and launching our beta.

Beta launch of Human Planet

Grade: A

By the skin of our teeth, but we launched with Kome Sushi in December.

Plans for 2017

  • Implement daily planning and time management (carry over)
  • 12+ Blog posts (for real setting a more realistic goal this time)
  • Secure seed-funding for Human Planet (carry over)
  • Work out at least 3 times per week. Fitness has taken a back seat to work this year, and it’s time to right the ship.

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 *

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 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).

When a request is made to the graphql handler, the requireAuth() middleware parses the JWT, and if valid, passes the (UserID & IsAdmin) variables via context.

The passing of context to GraphQL is explained in this post.

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 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().

This way, we can access these values within our GraphQL objects.