icepop

package module
v0.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 4, 2022 License: MIT Imports: 4 Imported by: 0

README

Icepop

icepop

An SSH routing library compatible with charmbracelet/wish and gliderlabs/ssh!

Extend your SSH applications by routing connections to unique handlers and middleware based on various characteristics such as the login username, or entrypoint.

Compatible with charmbracelet/wish

Utilizing an Icepop Router with Wish is super simple. Icepop routers implement ssh.Middleware, which means that it fits right into any Wish application.

// source: ./examples/minimal-wish-demo/main.go
package main

import (
	"fmt"

	"github.com/charmbracelet/wish"
	"github.com/gliderlabs/ssh"

	"github.com/concerthall/icepop"
)

func main() {
	rtr := icepop.NewUsernameRouter()
	rtr.HandleFunc(
		// the expected username
		"itshotoutside", 
		// The handler to be used.
		func(s ssh.Session) {
			wish.Println(s, "I love Ice pops!")
			s.Exit(0)
			s.Close()
		}),
	)

	s, _ := wish.NewServer(
		wish.WithAddress("localhost:23234"),
		wish.WithHostKeyPath(".ssh/term_info_ed25519"),
		// Here's our router! 
		wish.WithMiddleware(rtr.Middleware),
	)

	fmt.Println("Listening!")
	if err := s.ListenAndServe(); err != nil {
		panic(err) // handle how you see fit!
	}
}

And then SSH to the demo server!

$ ssh itshotoutside@localhost -p 23234
I love Ice pops!
Connection to localhost closed.

Unregistered routes are handled automatically!

$ ssh itscoldoutside@localhost -p 23234
this route does not exist
Connection to localhost closed.

Compatible with gliderlab/ssh

Because Wish extends the Gliderlabs SSH library, Icepop also fits nicely with the gliderlabs/ssh library.

// source: ./examples/minimal-gliderlabs-demo/main.go
package main

import (
	"io"
	"log"

	"github.com/concerthall/icepop"
	"github.com/gliderlabs/ssh"
)

func main() {
	rtr := icepop.NewCommandRouter()
	rtr.HandleFunc(
		"favorite-flavor",
		func(s ssh.Session) {
			io.WriteString(s, "I like cherry!")
			s.Exit(0)
		})

	ssh.Handle(rtr.Handler())

	log.Println("starting the gliderlabs demo")
	log.Fatal(ssh.ListenAndServe(":23234", nil))
}

Output:

$ ssh localhost -p 23234 favorite-flavor
I like cherry!

Concepts & Constructs

Routers

Routers are just ssh.Handlers that perform some kind of logical routing based on some factor.

This library provides:

  • a Command Router which allows you to bind different handlers, or routes, to each registered command the user passes into their session.

  • A Username Router which allows you to bind different handlers, or routes, to each registered username the user passes into their session.

SessionHandlers

A Session Handler is an abstraction based on the ssh.Handler definition. It binds a ssh.Handler to a method ServeSSH, as you might find in the net/http package.

For compatibility, you can convert an existing ssh.Handler to a SessionHandler using icepop.NewSessionHandlerFrom. A note: the SessionHandler interface doesn't replace ssh.Handler, but simply exists to allow us to do some creative things!

Routes

Routes are components that represent a base handler along with its middleware. Routes can also be treated as ssh.Handlers by themselves.

Examples

Several annotated examples exist in the examples directory, along with those already defined above.

Take a look at our nested demo, where we take advantage of the flexibility the SessionHandler interface affords us by nesting a command router within a specific endpoint of a username router.

Docs

The source is commented throughout, and feel free to open an issue if anything is unclear.

Go Doc

Attribution

Image Credit: "Ice cream stickers" by Gohsantosadrive - Flaticon

Documentation

Overview

Package icepop is a router library for SSH applications. It is intended to be paired with the APIs provided by gliderlabs/ssh and charmbracelet/wish.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewCommandRouter

func NewCommandRouter(opts ...CommandRouterOption) *commandRouter

NewCommandRouter returns a ssh.Handler that routes to registered SessionHandlers based on the ssh.Session.Command value. Only the entrypoint, or the first entry of a given command is parsed. All other parameters are ignored.

If the command is not registered, then a configurable message is printed to the user and the session is closed.

func NewRoute

func NewRoute(h ssh.Handler, mw []wish.Middleware, opts ...RouteOption) *route

NewRoute creates an instance of a route, representing a handler and its associated middleware.

func NewUsernameRouter

func NewUsernameRouter(opts ...UsernameRouterOption) *usernameRouter

NewCommandRouter returns a ssh.Handler that routes to registered SessionHandlers based on the ssh.Session.Username value. If the username is not registered, then a configurable message is printed to the user and the session is closed.

Types

type CommandRouterOption

type CommandRouterOption func(r *commandRouter)

CommandRouterOption configures a CommandRouter

func OptCommandNotFoundMessage

func OptCommandNotFoundMessage(msg string) CommandRouterOption

OptCommandNotFoundMessage configures the message sent to the user when the command provided was not registered.

func OptCommandNotProvidedMessage

func OptCommandNotProvidedMessage(msg string) CommandRouterOption

OptCommandNotProvidedMessage configures the message sent to the user when no command was provided.

type RouteOption

type RouteOption func(*route)

RouteOption configures a Route

func OptExecuteMiddlewareInReverse

func OptExecuteMiddlewareInReverse() RouteOption

OptExecuteMiddlewareInReverse indicates that a Route should execute middleware in reverse order relative to how it was defined in source code.

This is a compatibility option for developers who are familiar with how charmbraclete/wish handles middleware.

type Router

type Router struct {
	// contains filtered or unexported fields
}

Router is basic Router structure. It can be embedded in custom routers to provide easy access to the Handle method. Custom routers will need to implement methods that use these routes.

func (*Router) Handle

func (r *Router) Handle(path string, sh SessionHandler)

Handle binds a path to a SessionHandler. If a handler already exists, this panics.

func (*Router) HandleFunc added in v0.0.2

func (r *Router) HandleFunc(path string, hf ssh.Handler)

HandleFunc binds a ssh.Handler to a SessionHandler and binds it to path. If a handler already exists, this panics.

func (*Router) HandlerFor

func (r *Router) HandlerFor(path string) (SessionHandler, error)

HandlerFor returns the handler for the given path value, or an error if it does not exist.

type SessionHandler

type SessionHandler interface {
	ServeSSH(ssh.Session)
}

SessionHandler ∂escribes a ssh.Handler implementation bound to a fixed/known method name.

func NewSessionHandlerFrom

func NewSessionHandlerFrom(handler ssh.Handler) SessionHandler

NewSessionHandlerFrom returns a SessionHandler from an existing Handler. This has a narrow scope of responsibility, aiming to remain compatible with existing ssh.Handler definitions.

type UsernameRouterOption

type UsernameRouterOption func(r *usernameRouter)

UsernameRouterOption configures a CommandRouter

func OptUsernameNotFoundMessage

func OptUsernameNotFoundMessage(msg string) UsernameRouterOption

OptUsernameNotFoundMessage configures the message sent to the user when the username provided was not registered.

func OptUsernameNotProvidedMessage

func OptUsernameNotProvidedMessage(msg string) UsernameRouterOption

OptUsernameNotProvidedMessage configures the message sent to the user when no command was provided.

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL