Skip to content

Commit

Permalink
disable present bmc admin user and create new superuser (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sandro Koll authored Nov 4, 2020
1 parent cbed6b8 commit 030e594
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 112 deletions.
73 changes: 73 additions & 0 deletions cmd/grpc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package cmd

import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"github.com/metal-stack/metal-hammer/cmd/event"
"github.com/metal-stack/metal-hammer/metal-core/client/certs"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/keepalive"
"time"
)

type GrpcClient struct {
*event.EventEmitter
addr string
dialOpts []grpc.DialOption
}

// NewGrpcClient fetches the address and certificates from metal-core needed to communicate with metal-api via grpc,
// and returns a new grpc client that can be used to invoke all provided grpc endpoints.
func NewGrpcClient(certsClient *certs.Client, emitter *event.EventEmitter) (*GrpcClient, error) {
params := certs.NewGrpcClientCertParams()
resp, err := certsClient.GrpcClientCert(params)
if err != nil {
return nil, err
}

clientCert, err := tls.X509KeyPair([]byte(resp.Payload.Cert), []byte(resp.Payload.Key))
if err != nil {
return nil, err
}

caCertPool := x509.NewCertPool()
ok := caCertPool.AppendCertsFromPEM([]byte(resp.Payload.CaCert))
if !ok {
return nil, errors.New("bad certificate")
}

kacp := keepalive.ClientParameters{
Time: 10 * time.Second, // send pings every 10 seconds if there is no activity
Timeout: time.Second, // wait 1 second for ping ack before considering the connection dead
PermitWithoutStream: true, // send pings even without active streams
}

tlsConfig := &tls.Config{
RootCAs: caCertPool,
Certificates: []tls.Certificate{clientCert},
}
return &GrpcClient{
EventEmitter: emitter,
addr: resp.Payload.Address,
dialOpts: []grpc.DialOption{
grpc.WithKeepaliveParams(kacp),
grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)),
grpc.WithBlock(),
},
}, nil
}

func (c *GrpcClient) newConnection() (*grpc.ClientConn, error) {
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()

conn, err := grpc.DialContext(ctx, c.addr, c.dialOpts...)
if err != nil {
return nil, err
}

return conn, err
}
22 changes: 9 additions & 13 deletions cmd/register/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,28 +202,24 @@ func readIPMIDetails(eth0Mac string, hal hal.InBand) (*models.ModelsV1MachineIPM
details := &models.ModelsV1MachineIPMI{
Interface: &intf,
}
bmcversion := "unknown"
if hal.BMCPresent() {
bmcVersion := "unknown"
bmcConn := hal.BMCConnection()
if bmcConn.Present() {
log.Info("ipmi details from bmc")
board := hal.Board()
bmc := board.BMC
if bmc == nil {
return nil, errors.New("unable to read ipmi bmc info configuration")
}
bmcUser := hal.BMCUser().Name

// FIXME userid should be verified if available
pw, err := hal.BMCCreateUser(hal.BMCUser().ChannelNumber, bmcUser, hal.BMCUser().Uid, api.AdministratorPrivilege, api.PasswordConstraints{
Length: 10,
NumDigits: 3,
NumSymbols: 0,
NoUpper: false,
AllowRepeat: false,
})
pw, err := bmcConn.CreateUserAndPassword(bmcConn.User(), api.AdministratorPrivilege)
if err != nil {
return nil, errors.Wrap(err, "ipmi create user failed")
}

bmcversion = bmc.FirmwareRevision
bmcUser := bmcConn.User().Name
bmcVersion = bmc.FirmwareRevision
fru := models.ModelsV1MachineFru{
ChassisPartNumber: bmc.ChassisPartNumber,
ChassisPartSerial: bmc.ChassisPartSerial,
Expand All @@ -238,7 +234,7 @@ func readIPMIDetails(eth0Mac string, hal hal.InBand) (*models.ModelsV1MachineIPM
details.Mac = &bmc.MAC
details.User = &bmcUser
details.Password = &pw
details.Bmcversion = &bmcversion
details.Bmcversion = &bmcVersion
details.Fru = &fru
return details, nil
}
Expand All @@ -265,6 +261,6 @@ func readIPMIDetails(eth0Mac string, hal hal.InBand) (*models.ModelsV1MachineIPM
details.Mac = &bmcMAC
details.User = &user
details.Password = &pw
details.Bmcversion = &bmcversion
details.Bmcversion = &bmcVersion
return details, nil
}
30 changes: 21 additions & 9 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@ import (

// Hammer is the machine which forms a bare metal to a working server
type Hammer struct {
Hal hal.InBand
Client *machine.Client
CertsClient *certs.Client
Spec *Specification
Disk storage.Disk
LLDPClient *network.LLDPClient
Spec *Specification
Hal hal.InBand
Client *machine.Client
GrpcClient *GrpcClient
EventEmitter *event.EventEmitter
Disk storage.Disk
LLDPClient *network.LLDPClient
// IPAddress is the ip of the eth0 interface during installation
IPAddress string
Started time.Time
EventEmitter *event.EventEmitter
ChrootPrefix string
OsImageDestination string
}
Expand All @@ -57,7 +57,6 @@ func Run(spec *Specification, hal hal.InBand) (*event.EventEmitter, error) {
hammer := &Hammer{
Hal: hal,
Client: client,
CertsClient: certsClient,
Spec: spec,
IPAddress: spec.IP,
EventEmitter: eventEmitter,
Expand Down Expand Up @@ -108,6 +107,19 @@ func Run(spec *Specification, hal hal.InBand) (*event.EventEmitter, error) {
return eventEmitter, errors.Wrap(err, "register")
}

grpcClient, err := NewGrpcClient(certsClient, eventEmitter)
if err != nil {
log.Error("failed to fetch GRPC certificates", "error", err)
return eventEmitter, err
}
hammer.GrpcClient = grpcClient

err = hammer.createBmcSuperuser()
if err != nil {
log.Error("failed to update bmc superuser password", "error", err)
return eventEmitter, err
}

m, err := hammer.fetchMachine(spec.MachineUUID)
if err == nil && m != nil && m.Allocation != nil && m.Allocation.Reinstall != nil && *m.Allocation.Reinstall {
primaryDiskWiped := false
Expand Down Expand Up @@ -214,7 +226,7 @@ func Run(spec *Specification, hal hal.InBand) (*event.EventEmitter, error) {
},
}
} else {
err := hammer.WaitForInstallation(spec.MachineUUID)
err := hammer.GrpcClient.WaitForAllocation(spec.MachineUUID)
if err != nil {
return eventEmitter, errors.Wrap(err, "wait for installation")
}
Expand Down
44 changes: 44 additions & 0 deletions cmd/supwd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package cmd

import (
"context"
"github.com/metal-stack/go-hal/pkg/api"
v1 "github.com/metal-stack/metal-api/pkg/api/v1"
"github.com/pkg/errors"
"io"
)

func (c *GrpcClient) newSuperUserPasswordClient() (v1.SuperUserPasswordClient, io.Closer, error) {
conn, err := c.newConnection()
if err != nil {
return nil, nil, err
}
return v1.NewSuperUserPasswordClient(conn), conn, nil
}

// createBmcSuperuser creates the bmc super user.
func (h *Hammer) createBmcSuperuser() error {
client, closer, err := h.GrpcClient.newSuperUserPasswordClient()
if err != nil {
return err
}
defer closer.Close()

req := &v1.SuperUserPasswordRequest{}
resp, err := client.FetchSuperUserPassword(context.Background(), req)
if err != nil {
return errors.Wrap(err, "failed to fetch SuperUser password")
}

if resp.FeatureDisabled {
return nil
}

bmcConn := h.Hal.BMCConnection()
err = bmcConn.CreateUser(bmcConn.SuperUser(), api.AdministratorPrivilege, resp.SuperUserPassword)
if err != nil {
return errors.Wrapf(err, "failed to create bmc superuser: %s", bmcConn.SuperUser().Name)
}

return nil
}
84 changes: 11 additions & 73 deletions cmd/wait.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,97 +2,35 @@ package cmd

import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
log "github.com/inconshreveable/log15"
v1 "github.com/metal-stack/metal-api/pkg/api/v1"
"github.com/metal-stack/metal-hammer/cmd/event"
"github.com/metal-stack/metal-hammer/metal-core/client/certs"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/keepalive"
"io"
"time"
)

// Wait until a machine create request was fired
func (h *Hammer) WaitForInstallation(uuid string) error {
params := certs.NewGrpcClientCertParams()
resp, err := h.CertsClient.GrpcClientCert(params)
func (c *GrpcClient) NewWaitClient() (v1.WaitClient, io.Closer, error) {
conn, err := c.newConnection()
if err != nil {
return err
}

clientCert, err := tls.X509KeyPair([]byte(resp.Payload.Cert), []byte(resp.Payload.Key))
if err != nil {
return err
}
caCertPool := x509.NewCertPool()
ok := caCertPool.AppendCertsFromPEM([]byte(resp.Payload.CaCert))
if !ok {
return errors.New("bad certificate")
return nil, nil, err
}
tlsConfig := &tls.Config{
RootCAs: caCertPool,
Certificates: []tls.Certificate{clientCert},
}
c, err := NewClient(resp.Payload.Address, tlsConfig, h.EventEmitter)
if err != nil {
return err
}
defer c.Close()
c.WaitForAllocation(uuid)
return nil
}

type Client struct {
v1.WaitClient
conn *grpc.ClientConn
emitter *event.EventEmitter
return v1.NewWaitClient(conn), conn, nil
}

func NewClient(addr string, tlsConfig *tls.Config, emitter *event.EventEmitter) (*Client, error) {
kacp := keepalive.ClientParameters{
Time: 10 * time.Second, // send pings every 10 seconds if there is no activity
Timeout: time.Second, // wait 1 second for ping ack before considering the connection dead
PermitWithoutStream: true, // send pings even without active streams
}

opts := []grpc.DialOption{
grpc.WithKeepaliveParams(kacp),
grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)),
grpc.WithBlock(),
}
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
conn, err := grpc.DialContext(ctx, addr, opts...)
func (c *GrpcClient) WaitForAllocation(machineID string) error {
client, closer, err := c.NewWaitClient()
if err != nil {
return nil, err
return err
}
defer closer.Close()

c := &Client{
WaitClient: v1.NewWaitClient(conn),
conn: conn,
emitter: emitter,
}
c.Emit(event.ProvisioningEventWaiting, "waiting for allocation")

return c, nil
}

func (c *Client) Close() error {
return c.conn.Close()
}

func (c *Client) WaitForAllocation(machineID string) {
req := &v1.WaitRequest{
MachineID: machineID,
}

c.emitter.Emit(event.ProvisioningEventWaiting, "waiting for allocation")

for {
stream, err := c.Wait(context.Background(), req)
stream, err := client.Wait(context.Background(), req)
if err != nil {
log.Error("failed waiting for allocation, retry in 2sec", "error", err)
time.Sleep(2 * time.Second)
Expand All @@ -103,7 +41,7 @@ func (c *Client) WaitForAllocation(machineID string) {
_, err := stream.Recv()
if err == io.EOF {
log.Info("machine has been requested for allocation", "machineID", machineID)
return
return nil
}

if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ require (
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7
github.com/mdlayher/lldp v0.0.0-20150915211757-afd9f83164c5
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065
github.com/metal-stack/go-hal v0.1.10
github.com/metal-stack/metal-api v0.8.2
github.com/metal-stack/go-hal v0.2.1
github.com/metal-stack/metal-api v0.10.2
github.com/metal-stack/metal-lib v0.6.3
github.com/metal-stack/v v1.0.2
// archiver must stay in version v2.1.0, see replace below
Expand Down
Loading

0 comments on commit 030e594

Please sign in to comment.