diff --git a/cmd/crio/config.go b/cmd/crio/config.go index f0a91700..fdce55bc 100644 --- a/cmd/crio/config.go +++ b/cmd/crio/config.go @@ -95,6 +95,10 @@ pause_command = "{{ .PauseCommand }}" # unspecified so that the default system-wide policy will be used. signature_policy = "{{ .SignaturePolicyPath }}" +# insecure_registries is used to skip TLS verification when pulling images. +insecure_registries = [ +{{ range $opt := .InsecureRegistries }}{{ printf "\t%q,\n" $opt }}{{ end }}] + # The "crio.network" table contains settings pertaining to the # management of CNI plugins. [crio.network] diff --git a/cmd/crio/main.go b/cmd/crio/main.go index 6d009b03..2d02c0ab 100644 --- a/cmd/crio/main.go +++ b/cmd/crio/main.go @@ -62,6 +62,9 @@ func mergeConfig(config *server.Config, ctx *cli.Context) error { if ctx.GlobalIsSet("storage-opt") { config.StorageOptions = ctx.GlobalStringSlice("storage-opt") } + if ctx.GlobalIsSet("insecure-registry") { + config.InsecureRegistries = ctx.GlobalStringSlice("insecure-registries") + } if ctx.GlobalIsSet("default-transport") { config.DefaultTransport = ctx.GlobalString("default-transport") } @@ -180,6 +183,10 @@ func main() { Name: "storage-opt", Usage: "storage driver option", }, + cli.StringSliceFlag{ + Name: "insecure-registry", + Usage: "whether to disable TLS verification for the given registry", + }, cli.StringFlag{ Name: "default-transport", Usage: "default transport", diff --git a/pkg/storage/image.go b/pkg/storage/image.go index cdb02e87..b27565d5 100644 --- a/pkg/storage/image.go +++ b/pkg/storage/image.go @@ -1,6 +1,8 @@ package storage import ( + "net" + "github.com/containers/image/copy" "github.com/containers/image/docker/reference" "github.com/containers/image/image" @@ -19,9 +21,16 @@ type ImageResult struct { Size *uint64 } +type indexInfo struct { + name string + secure bool +} + type imageService struct { - store storage.Store - defaultTransport string + store storage.Store + defaultTransport string + insecureRegistryCIDRs []*net.IPNet + indexConfigs map[string]*indexInfo } // ImageServer wraps up various CRI-related activities into a reusable @@ -40,7 +49,7 @@ type ImageServer interface { // when it's asked to pull an image. GetStore() storage.Store // CanPull preliminary checks whether we're allowed to pull an image - CanPull(imageName string, sourceCtx *types.SystemContext) (bool, error) + CanPull(imageName string, options *copy.Options) (bool, error) } func (svc *imageService) ListImages(filter string) ([]ImageResult, error) { @@ -119,22 +128,12 @@ func imageSize(img types.Image) *uint64 { return nil } -func (svc *imageService) CanPull(imageName string, sourceCtx *types.SystemContext) (bool, error) { - if imageName == "" { - return false, storage.ErrNotAnImage - } - srcRef, err := alltransports.ParseImageName(imageName) +func (svc *imageService) CanPull(imageName string, options *copy.Options) (bool, error) { + srcRef, err := svc.prepareImage(imageName, options) if err != nil { - if svc.defaultTransport == "" { - return false, err - } - srcRef2, err2 := alltransports.ParseImageName(svc.defaultTransport + imageName) - if err2 != nil { - return false, err - } - srcRef = srcRef2 + return false, err } - rawSource, err := srcRef.NewImageSource(sourceCtx, nil) + rawSource, err := srcRef.NewImageSource(options.SourceCtx, nil) if err != nil { return false, err } @@ -148,21 +147,13 @@ func (svc *imageService) CanPull(imageName string, sourceCtx *types.SystemContex return true, nil } -func (svc *imageService) PullImage(systemContext *types.SystemContext, imageName string, options *copy.Options) (types.ImageReference, error) { - policy, err := signature.DefaultPolicy(systemContext) - if err != nil { - return nil, err - } - policyContext, err := signature.NewPolicyContext(policy) - if err != nil { - return nil, err - } +// prepareImage creates an image reference from an image string and set options +// for the source context +func (svc *imageService) prepareImage(imageName string, options *copy.Options) (types.ImageReference, error) { if imageName == "" { return nil, storage.ErrNotAnImage } - if options == nil { - options = ©.Options{} - } + srcRef, err := alltransports.ParseImageName(imageName) if err != nil { if svc.defaultTransport == "" { @@ -174,6 +165,36 @@ func (svc *imageService) PullImage(systemContext *types.SystemContext, imageName } srcRef = srcRef2 } + + if options.SourceCtx == nil { + options.SourceCtx = &types.SystemContext{} + } + + hostname := reference.Domain(srcRef.DockerReference()) + if secure := svc.isSecureIndex(hostname); !secure { + options.SourceCtx.DockerInsecureSkipTLSVerify = !secure + } + return srcRef, nil +} + +func (svc *imageService) PullImage(systemContext *types.SystemContext, imageName string, options *copy.Options) (types.ImageReference, error) { + policy, err := signature.DefaultPolicy(systemContext) + if err != nil { + return nil, err + } + policyContext, err := signature.NewPolicyContext(policy) + if err != nil { + return nil, err + } + if options == nil { + options = ©.Options{} + } + + srcRef, err := svc.prepareImage(imageName, options) + if err != nil { + return nil, err + } + dest := imageName if srcRef.DockerReference() != nil { dest = srcRef.DockerReference().Name() @@ -215,11 +236,47 @@ func (svc *imageService) GetStore() storage.Store { return svc.store } +func (svc *imageService) isSecureIndex(indexName string) bool { + if index, ok := svc.indexConfigs[indexName]; ok { + return index.secure + } + + host, _, err := net.SplitHostPort(indexName) + if err != nil { + // assume indexName is of the form `host` without the port and go on. + host = indexName + } + + addrs, err := net.LookupIP(host) + if err != nil { + ip := net.ParseIP(host) + if ip != nil { + addrs = []net.IP{ip} + } + + // if ip == nil, then `host` is neither an IP nor it could be looked up, + // either because the index is unreachable, or because the index is behind an HTTP proxy. + // So, len(addrs) == 0 and we're not aborting. + } + + // Try CIDR notation only if addrs has any elements, i.e. if `host`'s IP could be determined. + for _, addr := range addrs { + for _, ipnet := range svc.insecureRegistryCIDRs { + // check if the addr falls in the subnet + if (*net.IPNet)(ipnet).Contains(addr) { + return false + } + } + } + + return true +} + // GetImageService returns an ImageServer that uses the passed-in store, and // which will prepend the passed-in defaultTransport value to an image name if // a name that's passed to its PullImage() method can't be resolved to an image // in the store and can't be resolved to a source on its own. -func GetImageService(store storage.Store, defaultTransport string) (ImageServer, error) { +func GetImageService(store storage.Store, defaultTransport string, insecureRegistries []string) (ImageServer, error) { if store == nil { var err error store, err = storage.GetStore(storage.DefaultStoreOptions) @@ -227,8 +284,30 @@ func GetImageService(store storage.Store, defaultTransport string) (ImageServer, return nil, err } } - return &imageService{ - store: store, - defaultTransport: defaultTransport, - }, nil + + is := &imageService{ + store: store, + defaultTransport: defaultTransport, + indexConfigs: make(map[string]*indexInfo, 0), + insecureRegistryCIDRs: make([]*net.IPNet, 0), + } + + insecureRegistries = append(insecureRegistries, "127.0.0.0/8") + // Split --insecure-registry into CIDR and registry-specific settings. + for _, r := range insecureRegistries { + // Check if CIDR was passed to --insecure-registry + _, ipnet, err := net.ParseCIDR(r) + if err == nil { + // Valid CIDR. + is.insecureRegistryCIDRs = append(is.insecureRegistryCIDRs, ipnet) + } else { + // Assume `host:port` if not CIDR. + is.indexConfigs[r] = &indexInfo{ + name: r, + secure: false, + } + } + } + + return is, nil } diff --git a/server/config.go b/server/config.go index 09bfc5a3..aea4bf7b 100644 --- a/server/config.go +++ b/server/config.go @@ -117,6 +117,9 @@ type ImageConfig struct { // that this be left unspecified so that the default system-wide policy // will be used. SignaturePolicyPath string `toml:"signature_policy"` + // InsecureRegistries is a list of registries that must be contacted w/o + // TLS verification. + InsecureRegistries []string `toml:"insecure_registries"` } // NetworkConfig represents the "crio.network" TOML config table diff --git a/server/image_pull.go b/server/image_pull.go index b53f4988..e30e7954 100644 --- a/server/image_pull.go +++ b/server/image_pull.go @@ -38,8 +38,6 @@ func (s *Server) PullImage(ctx context.Context, req *pb.PullImageRequest) (*pb.P } } options := ©.Options{ - // TODO: we need a way to specify insecure registries like docker - //DockerInsecureSkipTLSVerify: true, SourceCtx: &types.SystemContext{}, } // a not empty username should be sufficient to decide whether to send auth @@ -53,7 +51,7 @@ func (s *Server) PullImage(ctx context.Context, req *pb.PullImageRequest) (*pb.P } } - canPull, err := s.storageImageServer.CanPull(image, options.SourceCtx) + canPull, err := s.storageImageServer.CanPull(image, options) if err != nil && !canPull { return nil, err } diff --git a/server/server.go b/server/server.go index b6d61dc6..8c7f4b0e 100644 --- a/server/server.go +++ b/server/server.go @@ -545,7 +545,7 @@ func New(config *Config) (*Server, error) { return nil, err } - imageService, err := storage.GetImageService(store, config.DefaultTransport) + imageService, err := storage.GetImageService(store, config.DefaultTransport, config.InsecureRegistries) if err != nil { return nil, err }