github.com/freiheit-com/kuberpult@v1.24.2-0.20240328135542-315d5630abe6/services/cd-service/pkg/repository/certificates.go (about) 1 /*This file is part of kuberpult. 2 3 Kuberpult is free software: you can redistribute it and/or modify 4 it under the terms of the Expat(MIT) License as published by 5 the Free Software Foundation. 6 7 Kuberpult is distributed in the hope that it will be useful, 8 but WITHOUT ANY WARRANTY; without even the implied warranty of 9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 MIT License for more details. 11 12 You should have received a copy of the MIT License 13 along with kuberpult. If not, see <https://directory.fsf.org/wiki/License:Expat>. 14 15 Copyright 2023 freiheit.com*/ 16 17 package repository 18 19 import ( 20 "bufio" 21 "bytes" 22 "context" 23 "crypto/sha256" 24 "fmt" 25 "os" 26 27 "github.com/freiheit-com/kuberpult/pkg/logger" 28 git "github.com/libgit2/git2go/v34" 29 "go.uber.org/zap" 30 "golang.org/x/crypto/ssh" 31 ) 32 33 type Certificates struct { 34 KnownHostsFile string 35 } 36 37 func (c *Certificates) load() (*certificateStore, error) { 38 store := &certificateStore{ 39 sha256Hashes: map[string][]byte{}, 40 } 41 if c.KnownHostsFile != "" { 42 file, err := os.Open(c.KnownHostsFile) 43 if err != nil { 44 return nil, err 45 } 46 defer file.Close() 47 scanner := bufio.NewScanner(file) 48 for scanner.Scan() { 49 _, hosts, pubKey, _, _, err := ssh.ParseKnownHosts(scanner.Bytes()) 50 if err != nil { 51 return nil, err 52 } 53 hasher := sha256.New() 54 hasher.Write(pubKey.Marshal()) 55 sum := hasher.Sum(nil) 56 for _, h := range hosts { 57 store.sha256Hashes[h] = sum 58 } 59 60 } 61 } 62 return store, nil 63 } 64 65 type certificateStore struct { 66 sha256Hashes map[string][]byte 67 } 68 69 func (store *certificateStore) CertificateCheckCallback(ctx context.Context) func(cert *git.Certificate, valid bool, hostname string) error { 70 if store == nil { 71 return func(cert *git.Certificate, valid bool, hostname string) error { 72 return fmt.Errorf("certificates error") // should never be called 73 } 74 } 75 logger := logger.FromContext(ctx) 76 return func(cert *git.Certificate, valid bool, hostname string) error { 77 if cert.Kind == git.CertificateHostkey { 78 if hsh, ok := store.sha256Hashes[hostname]; ok { 79 if bytes.Equal(hsh, cert.Hostkey.HashSHA256[:]) { 80 return nil 81 } else { 82 logger.Error("git.ssh.hostkeyMismatch", 83 zap.String("hostname", hostname), 84 zap.String("hostkey.expected", fmt.Sprintf("%x", hsh)), 85 zap.String("hostkey.actual", fmt.Sprintf("%x", cert.Hostkey.HashSHA256)), 86 ) 87 } 88 } else { 89 logger.Error("git.ssh.hostnameUnknown", 90 zap.String("hostname", hostname), 91 ) 92 } 93 } 94 return fmt.Errorf("certificates error") 95 } 96 }