Coder Social home page Coder Social logo

containerssh / libcontainerssh Goto Github PK

View Code? Open in Web Editor NEW
57.0 2.0 21.0 4.54 MB

Embedded ContainerSSH and webhook helper library

Home Page: https://containerssh.io/

License: Apache License 2.0

Go 98.96% HTML 0.82% Dockerfile 0.08% Shell 0.13%

libcontainerssh's Introduction

ContainerSSH - Launch Containers on Demand

libcontainerssh

FOSSA Status

This library is the core library of ContainerSSH and simultaneously serves as a library to integrate with ContainerSSH. This readme outlines the basics of using this library, for more detailed documentation please head to containerssh.io.

Embedding ContainerSSH

You can fully embed ContainerSSH into your own application. First, you will need to create the configuration structure:

cfg := config.AppConfig{}
// Set the default configuration:
cfg.Default()

You can then populate this config with your options and create a ContainerSSH instance like this:

pool, lifecycle, err := containerssh.New(cfg, loggerFactory)
if err != nil {
    return err
}

You will receive a service pool and a lifecycle as a response. You can use these to start the service pool of ContainerSSH. This will block execution until ContainerSSH stops.

err := lifecycle.Run()

This will run ContainerSSH in the current Goroutine. You can also use the lifecycle to add hooks to lifecycle states of ContainerSSH. You must do this before you call Run(). For example:

lifecycle.OnStarting(
    func(s service.Service, l service.Lifecycle) {
        print("ContainerSSH is starting...")
    },
)

You can also have ContainerSSH stop gracefully by using the Stop() function on the lifecycle. This takes a context as an argument, which is taken as a timeout for the graceful shutdown.

Finally, you can use the returned pool variable to rotate the logs. This will trigger all ContainerSSH services to close and reopen their log files.

pool.RotateLogs()

Building an authentication webhook server

Building a configuration webhook server

The configuration webhook lets you dynamically configure ContainerSSH. This library contains the tools to create a tiny webserver to serve these webhook requests.

First, you need to fetch this library as a dependency using go modules:

go get github.com/containerssh/libcontainerssh

Next, you will have to write an implementation for the following interface:

package main

import (
	"github.com/containerssh/libcontainerssh/config"
)

type ConfigRequestHandler interface {
	OnConfig(request config.Request) (config.AppConfig, error)
}

The best way to do this is creating a struct and adding a method with a receiver:

type myConfigReqHandler struct {
}

func (m *myConfigReqHandler) OnConfig(
    request configuration.ConfigRequest,
) (config configuration.AppConfig, err error) {
    // We recommend using an IDE to discover the possible options here.
    if request.Username == "foo" {
        config.Docker.Config.ContainerConfig.Image = "yourcompany/yourimage"
    }
    return config, err
}

Warning! Your OnConfig method should only return an error if it can genuinely not serve the request. This should not be used as a means to reject users. This should be done using the authentication server. If you return an error ContainerSSH will retry the request several times in an attempt to work around network failures.

Once you have your handler implemented you must decide which method you want to use for integration.

The full server method

This method is useful if you don't want to run anything else on the webserver, only the config endpoint. You can create a new server like this:

package main

import (
	"signal"
	
	"github.com/containerssh/libcontainerssh/config"
	"github.com/containerssh/libcontainerssh/config/webhook"
	"github.com/containerssh/libcontainerssh/log"
	"github.com/containerssh/libcontainerssh/service"
)

func main() {
	logger := log.NewLogger(&config.LogConfig{
		// Add logging configuration here
    })
	// Create the webserver service
    srv, err := webhook.NewServer(
        config.HTTPServerConfiguration{
            Listen: "0.0.0.0:8080",
        },
        &myConfigReqHandler{},
        logger,
    )
	if err != nil {
		panic(err)
    }

	// Set up the lifecycle handler
	lifecycle := service.NewLifecycle(srv)
	
	// Launch the webserver in the background
	go func() {
		//Ignore error, handled later.
		_ = lifecycle.Run()
	}()

    // Handle signals and terminate webserver gracefully when needed.
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		if _, ok := <-signals; ok {
			// ok means the channel wasn't closed, let's trigger a shutdown.
			// The context given is the timeout for the shutdown.
			lifecycle.Stop(
				context.WithTimeout(
					context.Background(),
					20 * time.Second,
				),
			)
		}
	}()
	// Wait for the service to terminate.
	lastError := lifecycle.Wait()
	// We are already shutting down, ignore further signals
	signal.Ignore(syscall.SIGINT, syscall.SIGTERM)
	// close signals channel so the signal handler gets terminated
	close(signals)

	if lastError != nil {
		// Exit with a non-zero signal
		fmt.Fprintf(
			os.Stderr,
			"an error happened while running the server (%v)",
			lastError,
		)
		os.Exit(1)
	}
	os.Exit(0)
}

Note: We recommend securing client-server communication with certificates. Please see the Securing webhooks section below/

Integrating with an existing HTTP server

Use this method if you want to integrate your handler with an existing Go HTTP server. This is rather simple:

handler, err := configuration.NewHandler(&myConfigReqHandler{}, logger)

You can now use the handler variable as a handler for the http package or a MUX like gorilla/mux.

Using the config client

This library also contains the components to call the configuration server in a simplified fashion. To create a client simply call the following method:

client, err := configuration.NewClient(
	configuration.ClientConfig{
        http.ClientConfiguration{
            URL: "http://your-server/config-endpoint/"
        }
    },
	logger,
    metricsCollector,
)

The logger is a logger from the log library, the metricsCollector is supplied by the metrics library.

You can now use the client variable to fetch the configuration specific to a connecting client:

connectionID := "0123456789ABCDEF"
appConfig, err := client.Get(
    ctx,
    "my-name-is-trinity",
    net.TCPAddr{
        IP: net.ParseIP("127.0.0.1"),
        Port: 2222,
    },
    connectionID,
) (AppConfig, error)

Now you have the client-specific configuration in appConfig.

Note: We recommend securing client-server communication with certificates. The details about securing your HTTP requests are documented in the HTTP library.

Loading the configuration from a file

This library also provides simplified methods for reading the configuration from an io.Reader and writing it to an io.Writer.

file, err := os.Open("file.yaml")
// ...
loader, err := configuration.NewReaderLoader(
	file,
    logger,
    configuration.FormatYAML,
)
// Read global config
appConfig := &configuration.AppConfig{}
err := loader.Load(ctx, appConfig)
// Read connection-specific config:
err := loader.LoadConnection(
    ctx,
    "my-name-is-trinity",
    net.TCPAddr{
        IP: net.ParseIP("127.0.0.1"),
        Port: 2222,
    },
    connectionID,
    appConfig,
)

As you can see these loaders are designed to be chained together. For example, you could add an HTTP loader after the file loader:

httpLoader, err := configuration.NewHTTPLoader(clientConfig, logger)

This HTTP loader calls the HTTP client described above.

Conversely, you can write the configuration to a YAML format:

saver, err := configuration.NewWriterSaver(
    os.Stdout,
    logger,
    configuration.FormatYAML,
)
err := saver.Save(appConfig)

Building a combined configuration-authentication webhook server

Securing webhooks

Reading audit logs

libcontainerssh's People

Contributors

bencurio avatar dependabot[bot] avatar gaocegege avatar hezhizhen avatar hongkunyoo avatar janosdebugs avatar kim-hyo-bin avatar mhmxs avatar nik736 avatar paseaf avatar tsipinakis avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

libcontainerssh's Issues

Document struct and list merging in configuration server

What would you like to add/change in the documentation?

Investigate the behaviour of struct merging, especially when it pertains to lists, when returning results from the configuration server and the configs have to be merged. A user reported that lists with multiple items overrode entire objects if e.g. only the 3rd item needed updating.

The underlying backend is the mergo library.

Where did you try to find the information?

https://containerssh.io/reference/configserver/?h=server

Config-server build fails with missing dependencies

Describe the bug

go mod init container-ssh/config-server
go get github.com/containerssh/libcontainerssh
go mod tidy

fails with

container-ssh/config-server imports
	github.com/containerssh/kubernetes imports
	k8s.io/client-go/kubernetes/scheme imports
	k8s.io/api/batch/v2alpha1: module k8s.io/api@latest found (v0.29.2), but does not contain package k8s.io/api/batch/v2alpha1
container-ssh/config-server imports
	github.com/containerssh/kubernetes imports
	k8s.io/client-go/kubernetes/scheme imports
	k8s.io/api/discovery/v1alpha1: module k8s.io/api@latest found (v0.29.2), but does not contain package k8s.io/api/discovery/v1alpha1
container-ssh/config-server imports
	github.com/containerssh/kubernetes imports
	k8s.io/client-go/kubernetes/scheme imports
	k8s.io/api/flowcontrol/v1alpha1: module k8s.io/api@latest found (v0.29.2), but does not contain package k8s.io/api/flowcontrol/v1alpha1
container-ssh/config-server imports
	github.com/containerssh/kubernetes imports
	k8s.io/client-go/rest imports
	k8s.io/apimachinery/pkg/util/clock: module k8s.io/apimachinery@latest found (v0.29.2), but does not contain package k8s.io/apimachinery/pkg/util/clock

To Reproduce

see above

Expected behavior

Dependencies are successfully resolved

Version

Try to build a config-server for containerssh 0.5 with go 1.22.0 (worked fine under go 1.17 and containerssh 0.4)

Workaround

I'm not sure if I missed something. When I add a

require (
     k8s.io/client-go v0.29.2
)

before calling "go mod tidy" there is no error logged.

Flaky sshproxy tests

Describe the bug

The tests for the sshproxy module are flaky and sometimes meet their end at the hands of the 10m timeout.

To Reproduce

Not reproducible.

Expected behavior

The sshproxy tests should be stable.

feature request: Multiple audit log storage

Please describe what you would like to see in ContainerSSH

Support multiple audit log storage in one containerssh instance.

Please describe your use case

I expect to keep the audit log in s3 and the local file system.

feat(metrics): Add remote IP label to containerssh_ssh_current_connections

Please describe what you would like to see in ContainerSSH

The remote server IP in ssh-proxy backend may change with the config webhook. We cannot get the current connections for a specific server. Thus I propose to add a new label server-ip to the metric containerssh_ssh_current_connections

Please describe your use case

I will create multiple ssh backends. And use them in ssh-proxy mode. The server IP is updated by the config server webhook. And I wanna know how many connections are alive in the server.

[Version Update] Go Language 1.18 -> 1.21 Upgrade

Please describe what you would like to see in ContainerSSH

go lang version up 1.18 -> 1.21

some package depandency upgrade

Please describe your use case

A clear and concise description of how you would use this feature.

Change kind download

Change the KinD utility download to:

  1. Perform a hash check
  2. Don't run curl with sudo, move the kind utility later

Webhook For Teardown

Please describe what you would like to see in ContainerSSH

Currently ContainerSSH provides a webhook for authentication and configuration of a container (or pod) that are triggered by an SSH connection to the ContainerSSH server. Upon successful authentication, the container described by the configuration is instantiated. Upon SSH disconnect, the container is removed by the server.

We would like a third webhook which would receive the status of the removal.

Please describe your use case

In order to run a ConatinerSSH created container successfully in a k8s environment, we choose to spin up auxiliary services (VMs, pods, etc). We would like some signal to know when we can tear down those extra resources.

feature request: Disable host key fingerprints check for sshproxy mode

Please describe what you would like to see in ContainerSSH

sshproxy:
  server: localhost
  port: 22222
  username: envd
  privateKey: /home/gaocegege/.config/envd/id_rsa_envd
  allowedHostKeyFingerprints:
   - SHA256:

The allowedHostKeyFingerprints cannot be empty now. It will be better to add a new config to disable the host key fingerprints check

Please describe your use case

A clear and concise description of how you would use this feature.

Prometheus scrape error: "unit not a suffix of metric"

Describe the bug

Prometheus seems to require all metrics to end with the unit name.

Error: unit not a suffix of metric "containerssh_config_server_failures"

To Reproduce

If you can please provide the steps to reproduce the issue.

  1. Run ContainerSSH with metrics enabled
  2. Setup a kubernetes ServiceMonitor to scrape ContainerSSH services

Expected behavior

ContainerSSH metrics are imported into Prometheus.

Version

0.5

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.