1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
|
package dns
import (
"context"
"fmt"
"net"
"runtime"
"time"
"github.com/crc-org/crc/v2/pkg/crc/adminhelper"
"github.com/crc-org/crc/v2/pkg/crc/constants"
"github.com/crc-org/crc/v2/pkg/crc/errors"
"github.com/crc-org/crc/v2/pkg/crc/logging"
"github.com/crc-org/crc/v2/pkg/crc/network"
"github.com/crc-org/crc/v2/pkg/crc/network/httpproxy"
"github.com/crc-org/crc/v2/pkg/crc/services"
"github.com/crc-org/crc/v2/pkg/crc/systemd"
"github.com/crc-org/crc/v2/pkg/crc/systemd/states"
)
const (
dnsServicePort = 53
publicDNSQueryURI = "quay.io"
dnsmasqService = "dnsmasq.service"
)
func init() {
}
func RunPostStart(serviceConfig services.ServicePostStartConfig) error {
if err := setupDnsmasq(serviceConfig); err != nil {
return err
}
if err := runPostStartForOS(serviceConfig); err != nil {
return err
}
resolvFileValues, err := getResolvFileValues(serviceConfig)
if err != nil {
return err
}
// override resolv.conf file
return network.CreateResolvFileOnInstance(serviceConfig.SSHRunner, resolvFileValues)
}
func setupDnsmasq(serviceConfig services.ServicePostStartConfig) error {
if serviceConfig.NetworkMode == network.UserNetworkingMode {
return nil
}
if err := createDnsmasqDNSConfig(serviceConfig); err != nil {
return err
}
sd := systemd.NewInstanceSystemdCommander(serviceConfig.SSHRunner)
if state, err := sd.Status(dnsmasqService); err != nil || state != states.Running {
if err := sd.Enable(dnsmasqService); err != nil {
return err
}
}
return sd.Start(dnsmasqService)
}
func getResolvFileValues(serviceConfig services.ServicePostStartConfig) (network.ResolvFileValues, error) {
dnsServers, err := dnsServers(serviceConfig)
if err != nil {
return network.ResolvFileValues{}, err
}
return network.ResolvFileValues{
SearchDomains: []network.SearchDomain{
{
Domain: fmt.Sprintf("%s.%s", serviceConfig.Name, serviceConfig.BundleMetadata.ClusterInfo.BaseDomain),
},
},
NameServers: dnsServers,
}, nil
}
func dnsServers(serviceConfig services.ServicePostStartConfig) ([]network.NameServer, error) {
if serviceConfig.NetworkMode == network.UserNetworkingMode {
return []network.NameServer{
{
IPAddress: constants.VSockGateway,
},
}, nil
}
orgResolvValues, err := network.GetResolvValuesFromInstance(serviceConfig.SSHRunner)
if err != nil {
return nil, err
}
return append([]network.NameServer{{IPAddress: serviceConfig.IP}}, orgResolvValues.NameServers...), nil
}
func CheckCRCLocalDNSReachable(ctx context.Context, serviceConfig services.ServicePostStartConfig) (string, error) {
appsURI := fmt.Sprintf("foo.%s", serviceConfig.BundleMetadata.ClusterInfo.AppsDomain)
// Try 30 times for 1 second interval, In nested environment most of time crc failed to get
// Internal dns query resolved for some time.
var queryOutput string
var err error
checkLocalDNSReach := func() error {
queryOutput, _, err = serviceConfig.SSHRunner.Run(fmt.Sprintf("host -R 3 %s", appsURI))
if err != nil {
return &errors.RetriableError{Err: err}
}
return nil
}
if err := errors.Retry(ctx, 30*time.Second, checkLocalDNSReach, time.Second); err != nil {
return queryOutput, err
}
return queryOutput, err
}
func CheckCRCPublicDNSReachable(serviceConfig services.ServicePostStartConfig) (string, error) {
// This does not query DNS directly to account for corporate environment where external DNS resolution
// may only be done on the host running the http(s) proxies used for internet connectivity
proxyConfig, err := httpproxy.NewProxyConfig()
if err != nil {
// try without using proxy
proxyConfig = &httpproxy.ProxyConfig{}
}
curlArgs := []string{"--head", publicDNSQueryURI}
if proxyConfig.IsEnabled() {
proxyHost := proxyConfig.HTTPProxy
if proxyConfig.HTTPSProxy != "" {
proxyHost = proxyConfig.HTTPSProxy
}
if proxyHost != "" {
curlArgs = append(curlArgs, "--proxy", proxyHost)
}
curlArgs = append(curlArgs, "--noproxy", proxyConfig.GetNoProxyString())
if proxyConfig.ProxyCAFile != "" {
// --proxy-cacert/--cacert replaces the system CAs with the specified one.
// If not using MITM proxy, --cacert must *not* be used, and if not using
// https:// proxy, --proxy-cacert must *not* be used
// ProxyCAFile is ambiguous, we cannot know if it's set because of MITM proxy,
// because of https:// proxy, or because of both
// We do not really care about transport security for this test, all that
// matters is whether or not we can resolve the hostname, so we can
// workaround this ambiguity by using an insecure connection
curlArgs = append(curlArgs, "--insecure", "--proxy-insecure")
}
}
stdout, _, err := serviceConfig.SSHRunner.Run("curl", curlArgs...)
return stdout, err
}
func CheckCRCLocalDNSReachableFromHost(serviceConfig services.ServicePostStartConfig) error {
bundle := serviceConfig.BundleMetadata
apiHostname := bundle.GetAPIHostname()
ip, err := net.LookupIP(apiHostname)
if err != nil {
return err
}
logging.Debugf("%s resolved to %s", apiHostname, ip)
if !matchIP(ip, serviceConfig.IP) {
logging.Warnf("%s resolved to %s but %s was expected", apiHostname, ip, serviceConfig.IP)
return fmt.Errorf("Invalid IP for %s", apiHostname)
}
if serviceConfig.NetworkMode == network.UserNetworkingMode {
// user-mode networking does not setup wildcard DNS on the host. It relies on admin-helper
// to create entries in /etc/hosts for routes defined in the cluster.
return nil
}
if runtime.GOOS == "darwin" {
/* This check will fail with !CGO_ENABLED builds on darwin as
* in this case, /etc/resolver/ will not be used, so we won't
* have wildcard DNS for our domains
*/
/* This can be removed when we switch to go 1.20
* https://github.com/golang/go/issues/12524
*/
return nil
}
appsHostname := bundle.GetAppHostname("foo")
ip, err = net.LookupIP(appsHostname)
if err != nil {
// Right now admin helper fallback is not implemented on windows so
// this check should still return an error.
if runtime.GOOS == "windows" {
return err
}
logging.Warnf("Wildcard DNS resolution for %s does not appear to be working", bundle.ClusterInfo.AppsDomain)
return nil
}
logging.Debugf("%s resolved to %s", appsHostname, ip)
if !matchIP(ip, serviceConfig.IP) {
logging.Warnf("%s resolved to %s but %s was expected", appsHostname, ip, serviceConfig.IP)
return fmt.Errorf("Invalid IP for %s", appsHostname)
}
return nil
}
func matchIP(ips []net.IP, expectedIP string) bool {
for _, ip := range ips {
if ip.String() == expectedIP {
return true
}
}
return false
}
func addOpenShiftHosts(serviceConfig services.ServicePostStartConfig) error {
return adminhelper.UpdateHostsFile(serviceConfig.IP, serviceConfig.BundleMetadata.GetAPIHostname(),
serviceConfig.BundleMetadata.GetAppHostname("oauth-openshift"),
serviceConfig.BundleMetadata.GetAppHostname("console-openshift-console"),
serviceConfig.BundleMetadata.GetAppHostname("downloads-openshift-console"),
serviceConfig.BundleMetadata.GetAppHostname("canary-openshift-ingress-canary"),
serviceConfig.BundleMetadata.GetAppHostname("default-route-openshift-image-registry"))
}
func AddPodmanHosts(ip string) error {
return adminhelper.UpdateHostsFile(ip, "podman.crc.testing")
}
|