Initial commit
This commit is contained in:
commit
06036c89d9
29 changed files with 4891 additions and 0 deletions
147
checker/types.go
Normal file
147
checker/types.go
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2026 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||
//
|
||||
// For AGPL licensing:
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// Package checker implements an SSH server security checker for
|
||||
// happyDomain. It probes each SSH endpoint associated with an
|
||||
// abstract.Server service and produces a structured report covering
|
||||
// reachability, banner/version posture, algorithm negotiation
|
||||
// (KEX/HostKey/Cipher/MAC/Compression), authentication method exposure
|
||||
// and SSHFP host-key fingerprint validation.
|
||||
package checker
|
||||
|
||||
import "time"
|
||||
|
||||
// ObservationKeySSH is the observation key this checker writes.
|
||||
const ObservationKeySSH = "ssh"
|
||||
|
||||
// Option ids on CheckerOptions.
|
||||
const (
|
||||
OptionService = "service"
|
||||
OptionPorts = "ports"
|
||||
OptionProbeTimeoutMs = "probeTimeoutMs"
|
||||
OptionIncludeAuthProbe = "includeAuthProbe"
|
||||
)
|
||||
|
||||
// Defaults.
|
||||
const (
|
||||
DefaultSSHPort = 22
|
||||
DefaultProbeTimeoutMs = 10000
|
||||
MaxConcurrentProbes = 16
|
||||
)
|
||||
|
||||
// Severity levels used in Issue.Severity.
|
||||
const (
|
||||
SeverityCrit = "crit"
|
||||
SeverityWarn = "warn"
|
||||
SeverityInfo = "info"
|
||||
SeverityOK = "ok"
|
||||
)
|
||||
|
||||
// SSHData is the full collected payload written under ObservationKeySSH.
|
||||
type SSHData struct {
|
||||
Domain string `json:"domain,omitempty"`
|
||||
Endpoints []SSHProbe `json:"endpoints"`
|
||||
SSHFP SSHFPSummary `json:"sshfp"`
|
||||
CollectedAt time.Time `json:"collected_at"`
|
||||
}
|
||||
|
||||
// SSHFPSummary captures the SSHFP records declared for the service and
|
||||
// whether a usable chain (DNSSEC) is available.
|
||||
type SSHFPSummary struct {
|
||||
Records []SSHFPRecord `json:"records,omitempty"`
|
||||
// Present indicates whether the service carries at least one SSHFP RR.
|
||||
Present bool `json:"present"`
|
||||
}
|
||||
|
||||
// SSHFPRecord is a single SSHFP record as declared in the zone.
|
||||
type SSHFPRecord struct {
|
||||
Algorithm uint8 `json:"algorithm"` // 1=RSA, 2=DSA, 3=ECDSA, 4=Ed25519
|
||||
Type uint8 `json:"type"` // 1=SHA-1, 2=SHA-256
|
||||
Fingerprint string `json:"fingerprint"` // hex, lowercase
|
||||
}
|
||||
|
||||
// SSHProbe is the outcome of probing a single SSH endpoint.
|
||||
type SSHProbe struct {
|
||||
Host string `json:"host"`
|
||||
Port uint16 `json:"port"`
|
||||
Address string `json:"address"`
|
||||
IP string `json:"ip,omitempty"`
|
||||
IsIPv6 bool `json:"ipv6,omitempty"`
|
||||
TCPConnected bool `json:"tcp_connected"`
|
||||
|
||||
// Banner is the SSH protocol banner (e.g. "SSH-2.0-OpenSSH_9.3p1").
|
||||
Banner string `json:"banner,omitempty"`
|
||||
SoftVer string `json:"software_version,omitempty"`
|
||||
ProtoVer string `json:"protocol_version,omitempty"`
|
||||
Vendor string `json:"vendor,omitempty"`
|
||||
ElapsedMS int64 `json:"elapsed_ms,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
|
||||
// Algorithms negotiated by the server.
|
||||
KEX []string `json:"kex_algorithms,omitempty"`
|
||||
HostKey []string `json:"host_key_algorithms,omitempty"`
|
||||
CiphersC2S []string `json:"ciphers_c2s,omitempty"`
|
||||
CiphersS2C []string `json:"ciphers_s2c,omitempty"`
|
||||
MACsC2S []string `json:"macs_c2s,omitempty"`
|
||||
MACsS2C []string `json:"macs_s2c,omitempty"`
|
||||
CompC2S []string `json:"compression_c2s,omitempty"`
|
||||
CompS2C []string `json:"compression_s2c,omitempty"`
|
||||
|
||||
// Host keys observed during KEX. Multiple entries can appear if the
|
||||
// server advertises several host-key types and we probe each in a
|
||||
// second pass.
|
||||
HostKeys []HostKeyInfo `json:"host_keys,omitempty"`
|
||||
|
||||
// Authentication methods advertised for a dummy "none" auth attempt.
|
||||
AuthMethods []string `json:"auth_methods,omitempty"`
|
||||
PasswordAuth bool `json:"password_auth,omitempty"`
|
||||
KeyboardInteractive bool `json:"keyboard_interactive,omitempty"`
|
||||
PublicKeyAuth bool `json:"public_key_auth,omitempty"`
|
||||
AuthProbeAttempted bool `json:"auth_probe_attempted,omitempty"`
|
||||
|
||||
// Stage is the furthest probe stage the connection reached. One of
|
||||
// "dial", "banner", "banner_write", "kexinit_read", "kexinit_parse",
|
||||
// "kexinit_ok", "handshake_ok". Empty means the dial failed before
|
||||
// even being attempted.
|
||||
Stage string `json:"stage,omitempty"`
|
||||
}
|
||||
|
||||
// HostKeyInfo captures an observed host key and its computed fingerprints.
|
||||
type HostKeyInfo struct {
|
||||
Type string `json:"type"` // e.g. "ssh-ed25519"
|
||||
Bits int `json:"bits,omitempty"` // key size (bits)
|
||||
SHA256 string `json:"sha256"` // hex fingerprint (lowercase, no colons)
|
||||
SHA1 string `json:"sha1"` // hex fingerprint (lowercase, no colons)
|
||||
SSHFPAlgo uint8 `json:"sshfp_algorithm"` // the SSHFP algorithm number matching this key type
|
||||
SSHFPMatchSHA256 bool `json:"sshfp_match_sha256"`
|
||||
SSHFPMatchSHA1 bool `json:"sshfp_match_sha1"`
|
||||
}
|
||||
|
||||
// Issue is a single SSH finding surfaced to consumers.
|
||||
type Issue struct {
|
||||
Code string `json:"code"`
|
||||
Severity string `json:"severity"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Fix string `json:"fix,omitempty"`
|
||||
// Endpoint is the "host:port" this issue applies to (empty for
|
||||
// service-level issues such as missing SSHFP).
|
||||
Endpoint string `json:"endpoint,omitempty"`
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue