Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cmd/nclet/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"sigs.k8s.io/controller-runtime/pkg/metrics"
"sigs.k8s.io/controller-runtime/pkg/metrics/server"

netconv1alpha1 "github.com/janog-netcon/netcon-problem-management-subsystem/api/v1alpha1"
Expand Down Expand Up @@ -71,6 +72,8 @@ func init() {
}

func main() {
controllers.RegisterMetrics(metrics.Registry)

flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
flag.StringVar(&sshAddr, "ssh-bind-address", ":2222", "The address SSH server binds to.")
Expand Down
35 changes: 35 additions & 0 deletions controllers/nclet/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package controllers

import "github.com/prometheus/client_golang/prometheus"

func RegisterMetrics(reg prometheus.Registerer) {
reg.MustRegister(sshAuthTotal)
reg.MustRegister(sshSessionDuration)
}

var (
sshAuthTotal = prometheus.NewCounterVec(
prometheus.CounterOpts{
Namespace: "netcon",
Subsystem: "nclet",
Name: "ssh_auth_total",
Help: "Total number of SSH authentication attempts",
},
[]string{"result"},
)

sshAuthTotalSucceeded = sshAuthTotal.WithLabelValues("succeeded")
sshAuthTotalFailed = sshAuthTotal.WithLabelValues("failed")

sshSessionDuration = prometheus.NewHistogram(
prometheus.HistogramOpts{
Namespace: "netcon",
Subsystem: "nclet",
Name: "ssh_session_duration_seconds",
Help: "Duration of SSH sessions in seconds",
Buckets: []float64{
10, 30, 60, 180, 300, 600, 900, 1200, 1800, 2700, 3600, 7200, 10800, 14400, 21600, 28800, 36000, 43200,
},
},
)
)
63 changes: 43 additions & 20 deletions controllers/nclet/ssh_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"os/exec"
"path"
"strings"
"time"

"github.com/creack/pty"
"github.com/gliderlabs/ssh"
Expand Down Expand Up @@ -150,35 +151,42 @@ func (r *SSHServer) injectHostKeys(server *ssh.Server) error {
return nil
}

func (r *SSHServer) handlePasswordAuthentication(ctx context.Context, sCtx ssh.Context, password string) bool {
func (r *SSHServer) handlePasswordAuthentication(ctx context.Context, sCtx ssh.Context, password string) error {
user, err := parseUser(sCtx.User())
if err != nil {
return false
return errors.New("invalid user format")
}

if user.Admin {
return password == r.adminPassword
} else {
problemEnvironment := netconv1alpha1.ProblemEnvironment{}
if err := r.Get(ctx, types.NamespacedName{
Namespace: "netcon",
Name: user.ProblemEnvironmentName,
}, &problemEnvironment); err != nil {
return false
if password != r.adminPassword {
return errors.New("invalid password")
}
return nil
}

if util.GetProblemEnvironmentCondition(
&problemEnvironment,
netconv1alpha1.ProblemEnvironmentConditionAssigned,
) != metav1.ConditionTrue {
return false
}
problemEnvironment := netconv1alpha1.ProblemEnvironment{}
if err := r.Get(ctx, types.NamespacedName{
Namespace: "netcon",
Name: user.ProblemEnvironmentName,
}, &problemEnvironment); err != nil {
return errors.New("problem environment not found")
}

return password == problemEnvironment.Status.Password
if util.GetProblemEnvironmentCondition(
&problemEnvironment,
netconv1alpha1.ProblemEnvironmentConditionAssigned,
) != metav1.ConditionTrue {
return errors.New("problem environment not assigned")
}

if password != problemEnvironment.Status.Password {
return errors.New("invalid password")
}

return nil
}

func (r *SSHServer) handle(ctx context.Context, s ssh.Session) {
func (r *SSHServer) handle(_ context.Context, s ssh.Session) {
user, err := parseUser(s.User())
if err != nil {
return
Expand Down Expand Up @@ -217,10 +225,25 @@ func (r *SSHServer) Start(ctx context.Context) error {
server := &ssh.Server{
Addr: r.sshAddr,
PasswordHandler: func(sctx ssh.Context, password string) bool {
// TODO: Add logging
return r.handlePasswordAuthentication(ctx, sctx, password)
log := log.FromContext(ctx)
if err := r.handlePasswordAuthentication(ctx, sctx, password); err != nil {
log.Info("ssh authentication failed", "remoteAddr", sctx.RemoteAddr().String(), "user", sctx.User(), "reason", err)
sshAuthTotalFailed.Inc()
return false
}
log.Info("ssh authentication successful", "remoteAddr", sctx.RemoteAddr().String(), "user", sctx.User())
sshAuthTotalSucceeded.Inc()
return true
},
Handler: func(s ssh.Session) {
log := log.FromContext(ctx)
start := time.Now()
defer func() {
duration := time.Since(start).Seconds()
log.Info("ssh session finished", "remoteAddr", s.RemoteAddr().String(), "user", s.User(), "durationSecond", duration)
sshSessionDuration.Observe(duration)
}()

r.handle(ctx, s)
},
}
Expand Down