Skip to content

Commit

Permalink
Move bmc console proxy from metal-console project (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
majst01 authored Jun 30, 2022
1 parent 17366fb commit 781843e
Show file tree
Hide file tree
Showing 7 changed files with 260 additions and 96 deletions.
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ RUN apt update \
&& apt install --yes --no-install-recommends \
ca-certificates \
ipmitool \
libvirt-clients \
# /usr/bin/sum is provided by busybox
&& rm /usr/bin/sum

Expand Down
13 changes: 11 additions & 2 deletions domain/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import (

type Config struct {
// Valid log levels are: DEBUG, INFO, WARN, ERROR, FATAL and PANIC
LogLevel string `required:"false" default:"debug" desc:"set log level" split_words:"true"`
PartitionID string `required:"true" desc:"set the partition ID" envconfig:"partition_id"`
LogLevel string `required:"false" default:"debug" desc:"set log level" split_words:"true"`
PartitionID string `required:"true" desc:"set the partition ID" envconfig:"partition_id"`

// ipmi details reporting parameters
LeaseFile string `required:"false" default:"/var/lib/dhcp/dhcpd.leases" desc:"the dhcp lease file to read" split_words:"true"`
ReportInterval time.Duration `required:"false" default:"5m" desc:"the interval for periodical reports" split_words:"true"`
MetalAPIURL *url.URL `required:"true" desc:"endpoint for the metal-api" envconfig:"metal_api_url"`
Expand All @@ -18,10 +20,17 @@ type Config struct {
IpmiPassword string `required:"false" default:"ADMIN" desc:"the ipmi password" split_words:"true"`
IgnoreMacs []string `required:"false" desc:"mac addresses to ignore" split_words:"true"`

// NSQ connection parameters
MQAddress string `required:"false" default:"localhost:4161" desc:"set the MQ server address" envconfig:"mq_address"`
MQCACertFile string `required:"false" default:"" desc:"the CA certificate file for verifying MQ certificate" envconfig:"mq_ca_cert_file"`
MQClientCertFile string `required:"false" default:"" desc:"the client certificate file for accessing MQ" envconfig:"mq_client_cert_file"`
MQLogLevel string `required:"false" default:"warn" desc:"sets the MQ loglevel (debug, info, warn, error)" envconfig:"mq_loglevel"`
MachineTopic string `required:"false" default:"machine" desc:"set the machine topic name" split_words:"true"`
MachineTopicTTL time.Duration `required:"false" default:"30s" desc:"sets the TTL for MachineTopic" envconfig:"machine_topic_ttl"`

// Console Proxy parameters
ConsolePort int `required:"false" default:"3333" desc:"defines the port where to listen for incoming console connections from metal-console" envconfig:"console_port"`
ConsoleCACertFile string `required:"false" default:"ca.pem" desc:"ca cert file" envconfig:"console_ca_cert_file"`
ConsoleCertFile string `required:"false" default:"cert.pem" desc:"cert file" envconfig:"console_cert_file"`
ConsoleKeyFile string `required:"false" default:"key.pem" desc:"key file" envconfig:"console_key_file"`
}
14 changes: 7 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ module github.com/metal-stack/bmc-catcher
go 1.18

require (
github.com/gliderlabs/ssh v0.3.4
github.com/kelseyhightower/envconfig v1.4.0
github.com/metal-stack/go-hal v0.4.2
github.com/metal-stack/metal-go v0.18.5
github.com/metal-stack/metal-lib v0.9.1
github.com/metal-stack/metal-lib v0.9.2
github.com/metal-stack/v v1.0.3
github.com/stretchr/testify v1.7.4
github.com/stretchr/testify v1.8.0
go.uber.org/zap v1.21.0
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
)

require (
Expand All @@ -20,7 +22,6 @@ require (
github.com/creack/pty v1.1.18 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/gliderlabs/ssh v0.3.4 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-openapi/analysis v0.21.3 // indirect
github.com/go-openapi/errors v0.20.2 // indirect
Expand Down Expand Up @@ -61,10 +62,9 @@ require (
go.mongodb.org/mongo-driver v1.9.1 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
golang.org/x/net v0.0.0-20220622184535-263ec571b305 // indirect
golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2 // indirect
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664 // indirect
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e // indirect
golang.org/x/oauth2 v0.0.0-20220628200809-02e64fa58f26 // indirect
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.0 // indirect
Expand Down
20 changes: 10 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -250,8 +250,8 @@ github.com/metal-stack/masterdata-api v0.8.12 h1:hfg+g0W1tzs/5mV9RG4+l6pAtH/SMae
github.com/metal-stack/masterdata-api v0.8.12/go.mod h1:le/cvZ9nvw9QQ/eVJSrO7rNdwMMuIrQxFalaIcv/GUg=
github.com/metal-stack/metal-go v0.18.5 h1:ySdF2S/bb+C1qdc2nmEgpRYJEDBN0OaE8DHrM+nthbs=
github.com/metal-stack/metal-go v0.18.5/go.mod h1:2JU77TT7PrN9PUwCBYreYEOeziCrYvwfzQ4DLFyKSMM=
github.com/metal-stack/metal-lib v0.9.1 h1:6sPRlbD80XVXWbC0rVOtsF42qciF6UR2Il41Q/pr+hY=
github.com/metal-stack/metal-lib v0.9.1/go.mod h1:qViw4NXMx8x3Qn8jSKIBreXL25JUrDSZG2McYlK2V/g=
github.com/metal-stack/metal-lib v0.9.2 h1:wgJuXkOmrHfwV1zfFmz3IzKicCZ6TnarYMfQjf9lDLc=
github.com/metal-stack/metal-lib v0.9.2/go.mod h1:qViw4NXMx8x3Qn8jSKIBreXL25JUrDSZG2McYlK2V/g=
github.com/metal-stack/security v0.6.4 h1:nCr1Hf2a4qWRCBsNCifPA4Ic4sHfQlrGjxUvksWcRvM=
github.com/metal-stack/security v0.6.4/go.mod h1:vP9TLOtIETfvqq6FvvYSHCKHrDXomuba2dSV3pS6BO0=
github.com/metal-stack/v v1.0.3 h1:Sh2oBlnxrCUD+mVpzfC8HiqL045YWkxs0gpTvkjppqs=
Expand Down Expand Up @@ -305,8 +305,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM=
github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/vmware/goipmi v0.0.0-20181114221114-2333cd82d702 h1:yx587LNBbOpIxzCBHBiI94Wx8ryIAFlu1w0lDwm64cA=
Expand Down Expand Up @@ -417,15 +417,15 @@ golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220622184535-263ec571b305 h1:dAgbJ2SP4jD6XYfMNLVj0BF21jo2PjChrtGaAvF5M3I=
golang.org/x/net v0.0.0-20220622184535-263ec571b305/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ=
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2 h1:+jnHzr9VPj32ykQVai5DNahi9+NSp7yYuCsl5eAQtL0=
golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
golang.org/x/oauth2 v0.0.0-20220628200809-02e64fa58f26 h1:uBgVQYJLi/m8M0wzp+aGwBWt90gMRoOVf+aWTW10QHI=
golang.org/x/oauth2 v0.0.0-20220628200809-02e64fa58f26/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down Expand Up @@ -477,8 +477,8 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211107104306-e0b2ad06fe42/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664 h1:wEZYwx+kK+KlZ0hpvP2Ls1Xr4+RWnlzGFwPP0aiDjIU=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b h1:2n253B2r0pYSmEV+UNCQoPfU/FiaizQEK5Gu4Bq4JE8=
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
Expand Down
137 changes: 137 additions & 0 deletions internal/bmc/console.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package bmc

import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"io"
"os"
"strconv"
"strings"

"github.com/metal-stack/go-hal/connect"
halzap "github.com/metal-stack/go-hal/pkg/logger/zap"
metalgo "github.com/metal-stack/metal-go"
"github.com/metal-stack/metal-go/api/client/machine"

"github.com/gliderlabs/ssh"
"go.uber.org/zap"
gossh "golang.org/x/crypto/ssh"
)

type console struct {
log *zap.SugaredLogger
tlsConfig *tls.Config
port int
hostKey gossh.Signer
client metalgo.Client
}

func NewConsole(log *zap.SugaredLogger, client metalgo.Client, caCertFile, certFile, keyFile string, port int) (*console, error) {

caCert, err := os.ReadFile(caCertFile)
if err != nil {
return nil, fmt.Errorf("failed to load cert: %w", err)
}

caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)

cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, err
}

tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert}, // server certificate which is validated by the client
ClientCAs: caCertPool, // used to verify the client cert is signed by the CA and is therefore valid
ClientAuth: tls.RequireAndVerifyClientCert, // this requires a valid client certificate to be supplied during handshake
MinVersion: tls.VersionTLS13,
}

bb, err := os.ReadFile(keyFile)
if err != nil {
return nil, fmt.Errorf("failed to load ssh server key:%w", err)
}
hostKey, err := gossh.ParsePrivateKey(bb)
if err != nil {
return nil, fmt.Errorf("failed to parse ssh server key:%w", err)
}

return &console{
log: log,
tlsConfig: tlsConfig,
port: port,
hostKey: hostKey,
client: client,
}, nil
}

// ListenAndServe starts ssh server and listen for console connections.
func (c *console) ListenAndServe() error {
s := &ssh.Server{
Handler: c.sessionHandler,
}
s.AddHostKey(c.hostKey)
addr := fmt.Sprintf(":%d", c.port)
listener, err := tls.Listen("tcp", addr, c.tlsConfig)
if err != nil {
return fmt.Errorf("failed to create listener: %w", err)
}
c.log.Infow("starting ssh server", "address", addr)
return s.Serve(listener)
}

// FIXME broken error handling, should also be printed to the session
func (c *console) sessionHandler(s ssh.Session) {
c.log.Infow("ssh session handler called", "machineID", s.User())
machineID := s.User()

resp, err := c.client.Machine().FindIPMIMachine(machine.NewFindIPMIMachineParams().WithID(machineID), nil)
if err != nil || resp.Payload == nil || resp.Payload.Ipmi == nil {
c.log.Errorw("failed to receive IPMI data", "machineID", machineID, "error", err)
return
}
metalIPMI := resp.Payload.Ipmi

c.log.Infow("connection to", "machineID", machineID)
if metalIPMI == nil {
c.log.Errorw("failed to receive IPMI data", "machineID", machineID)
return
}
if metalIPMI.Address == nil {
c.log.Errorw("failed to receive IPMI.Address data", "machineID", machineID)
return
}
_, err = io.WriteString(s, fmt.Sprintf("Connecting to console of %q (%s)\n", machineID, *metalIPMI.Address))
if err != nil {
c.log.Warnw("failed to write to console", "machineID", machineID)
}

host, portStr, found := strings.Cut(*metalIPMI.Address, ":")
if !found {
c.log.Errorw("invalid ipmi address", "address", *metalIPMI.Address)
return
}
port, err := strconv.Atoi(portStr)
if err != nil {
c.log.Errorw("invalid port", "port", port, "address", *metalIPMI.Address)
return
}

ob, err := connect.OutBand(host, port, *metalIPMI.User, *metalIPMI.Password, halzap.New(c.log))
if err != nil {
c.log.Errorw("failed to out-band connect", "host", host, "port", port, "machineID", machineID, "ipmiuser", *metalIPMI.User)
return
}

err = ob.Console(s)
if err != nil {
if errors.Is(err, io.EOF) {
c.log.Infow("console access terminated")
} else {
c.log.Errorw("failed to access console", "machineID", machineID, "error", err)
}
}
}
Loading

0 comments on commit 781843e

Please sign in to comment.