bastion 0.63.1
ci / personal-data (push) Successful in 4s
ci / test (push) Successful in 4m58s
ci / fuzz (push) Successful in 3m33s

This commit is contained in:
Privatae
2026-11-03 22:56:18 +00:00
parent 2043fa5e9e
commit 48899b41a9
78 changed files with 10366 additions and 307 deletions
+29 -14
View File
@@ -219,20 +219,8 @@ func tcpAnswerRefusal(rr tcpRedeemResp, redeem *url.URL, hostCA, opKey string, e
if !sameCAs(rr.caCert(), hostCA) {
return errors.New("the Host answered with a CA other than the --host-ca it was paired with — refusing")
}
pair, err := tls.X509KeyPair([]byte(rr.operatorCert()), []byte(opKey))
if err != nil {
return fmt.Errorf("the Host's operator_cert is not for this bastion's key: %v", err)
}
leaf, err := x509.ParseCertificate(pair.Certificate[0])
if err != nil {
return fmt.Errorf("the Host's operator_cert does not parse: %v", err)
}
pool := x509.NewCertPool()
for _, c := range pemCerts(hostCA) {
pool.AddCert(c)
}
if _, err := leaf.Verify(x509.VerifyOptions{Roots: pool, KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}}); err != nil {
return fmt.Errorf("the Host's operator_cert is not signed by its CA for client use: %v", err)
if err := operatorCertRefusal(rr.operatorCert(), hostCA, opKey); err != nil {
return err
}
// Again here: the pairing the answer's name lands on may not be the one
// the token's handle found before the redeem.
@@ -250,6 +238,27 @@ func tcpAnswerRefusal(rr tcpRedeemResp, redeem *url.URL, hostCA, opKey string, e
return nil
}
// operatorCertRefusal: the operator certificate a Host answered with is for
// this bastion's key, and its CA signed it for client use.
func operatorCertRefusal(certPEM, hostCA, opKey string) error {
pair, err := tls.X509KeyPair([]byte(certPEM), []byte(opKey))
if err != nil {
return fmt.Errorf("the Host's operator_cert is not for this bastion's key: %v", err)
}
leaf, err := x509.ParseCertificate(pair.Certificate[0])
if err != nil {
return fmt.Errorf("the Host's operator_cert does not parse: %v", err)
}
pool := x509.NewCertPool()
for _, c := range pemCerts(hostCA) {
pool.AddCert(c)
}
if _, err := leaf.Verify(x509.VerifyOptions{Roots: pool, KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}}); err != nil {
return fmt.Errorf("the Host's operator_cert is not signed by its CA for client use: %v", err)
}
return nil
}
// tcpPinRefusal is THE PIN, as for a unix pairing: a --replace keeps the CA
// the pairing was made with. pairWithTokenVia asks it before the redeem
// dials, because --host-ca is the only root that redeem trusts: under
@@ -328,6 +337,9 @@ func tcpLaneDialer(cfg *Config) (func(ctx context.Context) (net.Conn, error), *t
// localLaneDialer is the lane transport of a pairing with a Host that has no
// platform: the lane socket or the lane port.
func localLaneDialer(cfg *Config) (func(ctx context.Context) (net.Conn, error), *tls.Config, error) {
if cfg.hostDials() {
return hostDialsLaneDialer(cfg)
}
if cfg.tcpTransport() {
return tcpLaneDialer(cfg)
}
@@ -336,6 +348,9 @@ func localLaneDialer(cfg *Config) (func(ctx context.Context) (net.Conn, error),
// laneWhere is where a local pairing's lanes go, for the log and status.
func (c *Config) laneWhere() string {
if c.hostDials() {
return "connections your Host opens to this server's port " + doorPort(c.doorAddr())
}
if c.tcpTransport() {
return c.LaneAddr
}