diff options
26 files changed, 253 insertions, 93 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index c14e38771..d7b763f6d 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -470,6 +470,10 @@ osx_alt_build_task: - make podman-remote-release-darwin_amd64.zip GOARCH=amd64 build_arm64_script: - make podman-remote-release-darwin_arm64.zip GOARCH=arm64 + build_pkginstaller_script: + - cd contrib/pkginstaller + - make ARCH=amd64 NO_CODESIGN=1 pkginstaller + - make ARCH=aarch64 NO_CODESIGN=1 pkginstaller # This task cannot make use of the shared repo.tbz artifact and must # produce a new repo.tbz artifact for consumption by 'artifacts' task. repo_prep_script: *repo_prep @@ -1093,6 +1097,7 @@ artifacts_task: - $ARTCURL/OSX%20Cross/repo/repo.tbz - tar xjf repo.tbz - mv ./podman-remote-release-darwin_*.zip $CIRRUS_WORKING_DIR/ + - mv ./contrib/pkginstaller/out/podman-installer-macos-*.pkg $CIRRUS_WORKING_DIR/ always: contents_script: ls -la $CIRRUS_WORKING_DIR # Produce downloadable files and an automatic zip-file accessible @@ -850,12 +850,20 @@ endif .PHONY: install.tools install.tools: .install.ginkgo .install.golangci-lint ## Install needed tools - make -C test/tools + $(MAKE) -C test/tools + +.PHONY: .install.goimports +.install.goimports: + $(MAKE) -C test/tools build/goimports .PHONY: .install.ginkgo .install.ginkgo: $(GO) install $(BUILDFLAGS) ./vendor/github.com/onsi/ginkgo/ginkgo +.PHONY: .install.gitvalidation +.install.gitvalidation: + $(MAKE) -C test/tools build/git-validation + .PHONY: .install.golangci-lint .install.golangci-lint: VERSION=1.46.2 ./hack/install_golangci.sh @@ -863,7 +871,7 @@ install.tools: .install.ginkgo .install.golangci-lint ## Install needed tools .PHONY: .install.md2man .install.md2man: if [ ! -x "$(GOMD2MAN)" ]; then \ - make -C test/tools build/go-md2man ; \ + $(MAKE) -C test/tools build/go-md2man ; \ fi .PHONY: .install.pre-commit diff --git a/cmd/podman/machine/list.go b/cmd/podman/machine/list.go index ddc9ce246..26db5e1a4 100644 --- a/cmd/podman/machine/list.go +++ b/cmd/podman/machine/list.go @@ -177,6 +177,7 @@ func toMachineFormat(vms []*machine.ListResponse) ([]*entities.ListReporter, err response.Port = vm.Port response.RemoteUsername = vm.RemoteUsername response.IdentityPath = vm.IdentityPath + response.Starting = vm.Starting machineResponses = append(machineResponses, response) } diff --git a/cmd/podman/machine/ssh.go b/cmd/podman/machine/ssh.go index 8534b8efa..1cadce916 100644 --- a/cmd/podman/machine/ssh.go +++ b/cmd/podman/machine/ssh.go @@ -101,7 +101,7 @@ func remoteConnectionUsername() (string, error) { if err != nil { return "", err } - dest, _, err := cfg.ActiveDestination() + dest, _, _, err := cfg.ActiveDestination() if err != nil { return "", err } diff --git a/cmd/podman/root.go b/cmd/podman/root.go index 9e3ff48aa..5c65be96d 100644 --- a/cmd/podman/root.go +++ b/cmd/podman/root.go @@ -176,7 +176,7 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error { setupConnection := func() error { var err error - cfg.URI, cfg.Identity, err = cfg.ActiveDestination() + cfg.URI, cfg.Identity, cfg.MachineMode, err = cfg.ActiveDestination() if err != nil { return fmt.Errorf("failed to resolve active destination: %w", err) } @@ -368,10 +368,13 @@ func loggingHook() { func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) { cfg := opts.Config - srv, uri, ident := resolveDestination() + srv, uri, ident, machine := resolveDestination() lFlags := cmd.Flags() + // non configurable option to help ssh dialing + opts.MachineMode = machine + sshFlagName := "ssh" lFlags.StringVar(&opts.SSHMode, sshFlagName, string(ssh.GolangMode), "define the ssh mode") _ = cmd.RegisterFlagCompletionFunc(sshFlagName, common.AutocompleteSSH) @@ -513,26 +516,26 @@ func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) { } } -func resolveDestination() (string, string, string) { +func resolveDestination() (string, string, string, bool) { if uri, found := os.LookupEnv("CONTAINER_HOST"); found { var ident string if v, found := os.LookupEnv("CONTAINER_SSHKEY"); found { ident = v } - return "", uri, ident + return "", uri, ident, false } cfg, err := config.ReadCustomConfig() if err != nil { logrus.Warning(fmt.Errorf("unable to read local containers.conf: %w", err)) - return "", registry.DefaultAPIAddress(), "" + return "", registry.DefaultAPIAddress(), "", false } - uri, ident, err := cfg.ActiveDestination() + uri, ident, machine, err := cfg.ActiveDestination() if err != nil { - return "", registry.DefaultAPIAddress(), "" + return "", registry.DefaultAPIAddress(), "", false } - return cfg.Engine.ActiveService, uri, ident + return cfg.Engine.ActiveService, uri, ident, machine } func formatError(err error) string { diff --git a/cmd/podman/system/connection/list.go b/cmd/podman/system/connection/list.go index 190a68d52..3c1a42453 100644 --- a/cmd/podman/system/connection/list.go +++ b/cmd/podman/system/connection/list.go @@ -105,8 +105,9 @@ func inspect(cmd *cobra.Command, args []string) error { r := namedDestination{ Name: k, Destination: config.Destination{ - Identity: v.Identity, - URI: v.URI, + Identity: v.Identity, + URI: v.URI, + IsMachine: v.IsMachine, }, Default: def, } diff --git a/contrib/cirrus/setup_environment.sh b/contrib/cirrus/setup_environment.sh index f84f78ee9..4c86bbcfa 100755 --- a/contrib/cirrus/setup_environment.sh +++ b/contrib/cirrus/setup_environment.sh @@ -232,8 +232,7 @@ case "$TEST_FLAVOR" in validate) dnf install -y $PACKAGE_DOWNLOAD_DIR/python3*.rpm # For some reason, this is also needed for validation - make install.tools - make .install.pre-commit + make .install.pre-commit .install.gitvalidation ;; automation) ;; altbuild) @@ -242,11 +241,9 @@ case "$TEST_FLAVOR" in if [[ "$ALT_NAME" =~ RPM ]]; then bigto dnf install -y glibc-minimal-langpack go-rpm-macros rpkg rpm-build shadow-utils-subid-devel fi - make install.tools ;; docker-py) remove_packaged_podman_files - make install.tools make install PREFIX=/usr ETCDIR=/etc msg "Installing previously downloaded/cached packages" @@ -258,16 +255,14 @@ case "$TEST_FLAVOR" in ;; build) make clean ;; unit) - make install.tools + make .install.ginkgo ;; compose_v2) - make install.tools dnf -y remove docker-compose curl -SL https://github.com/docker/compose/releases/download/v2.2.3/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose ;& # Continue with next item apiv2) - make install.tools msg "Installing previously downloaded/cached packages" dnf install -y $PACKAGE_DOWNLOAD_DIR/python3*.rpm virtualenv .venv/requests @@ -276,16 +271,16 @@ case "$TEST_FLAVOR" in pip install --requirement $GOSRC/test/apiv2/python/requirements.txt ;& # continue with next item compose) - make install.tools dnf install -y $PACKAGE_DOWNLOAD_DIR/podman-docker* ;& # continue with next item - int) ;& + int) + make .install.ginkgo + ;& sys) ;& upgrade_test) ;& bud) ;& bindings) ;& endpoint) - make install.tools # Use existing host bits when testing is to happen inside a container # since this script will run again in that environment. # shellcheck disable=SC2154 @@ -309,7 +304,6 @@ case "$TEST_FLAVOR" in machine) dnf install -y $PACKAGE_DOWNLOAD_DIR/podman-gvproxy* remove_packaged_podman_files - make install.tools make install PREFIX=/usr ETCDIR=/etc install_test_configs ;; @@ -374,7 +368,7 @@ case "$TEST_FLAVOR" in swagger) ;& # use next item consistency) make clean - make install.tools + make .install.goimports ;; release) ;; *) die_unknown TEST_FLAVOR @@ -12,7 +12,7 @@ require ( github.com/containernetworking/cni v1.1.2 github.com/containernetworking/plugins v1.1.1 github.com/containers/buildah v1.27.1-0.20220921131114-d3064796af36 - github.com/containers/common v0.49.2-0.20220920205255-8062f81c5497 + github.com/containers/common v0.49.2-0.20220926195839-590004b80685 github.com/containers/conmon v2.0.20+incompatible github.com/containers/image/v5 v5.22.1-0.20220919112403-fe51f7ffca50 github.com/containers/ocicrypt v1.1.5 @@ -419,8 +419,9 @@ github.com/containernetworking/plugins v1.1.1 h1:+AGfFigZ5TiQH00vhR8qPeSatj53eNG github.com/containernetworking/plugins v1.1.1/go.mod h1:Sr5TH/eBsGLXK/h71HeLfX19sZPp3ry5uHSkI4LPxV8= github.com/containers/buildah v1.27.1-0.20220921131114-d3064796af36 h1:LTSEbPUbs0slJSJ+IH6atAjYDe0IDzA0sPgBLjT1yAo= github.com/containers/buildah v1.27.1-0.20220921131114-d3064796af36/go.mod h1:cY3pGPyMmrNp/sEDK8ESoBOf4hoNovptZSI0oyo8eQM= -github.com/containers/common v0.49.2-0.20220920205255-8062f81c5497 h1:LB9SxcAglqSAHiiHGacN1Abi0ZL9haJpQ1numVlqtxM= github.com/containers/common v0.49.2-0.20220920205255-8062f81c5497/go.mod h1:ZnhOPR/07UOkfIg5bezUpBilGjxEUdaeoUpu7gRBGc0= +github.com/containers/common v0.49.2-0.20220926195839-590004b80685 h1:rutCgIKcew85mTUO0JTnh7XDXQfaTz/qQ3HyQHb0jZE= +github.com/containers/common v0.49.2-0.20220926195839-590004b80685/go.mod h1:ZnhOPR/07UOkfIg5bezUpBilGjxEUdaeoUpu7gRBGc0= github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg= github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= github.com/containers/image/v5 v5.22.1-0.20220907162003-651744379993/go.mod h1:/Ruurd87C6Ap45t1PWNOD8+SGwiZbk79XCgs1iUTvYA= diff --git a/hack/install_golangci.sh b/hack/install_golangci.sh index 896d59901..29d925666 100755 --- a/hack/install_golangci.sh +++ b/hack/install_golangci.sh @@ -6,7 +6,7 @@ die() { echo "${1:-No error message given} (from $(basename $0))"; exit 1; } function install() { echo "Installing golangci-lint v$VERSION into $BIN" - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v$VERSION + curl -sSL --retry 5 https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v$VERSION } # Undocumented behavior: golangci-lint installer requires $BINDIR in env, diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go index 6d7b052b7..a3677d393 100644 --- a/pkg/bindings/connection.go +++ b/pkg/bindings/connection.go @@ -59,7 +59,7 @@ func JoinURL(elements ...string) string { // NewConnection creates a new service connection without an identity func NewConnection(ctx context.Context, uri string) (context.Context, error) { - return NewConnectionWithIdentity(ctx, uri, "") + return NewConnectionWithIdentity(ctx, uri, "", false) } // NewConnectionWithIdentity takes a URI as a string and returns a context with the @@ -70,7 +70,7 @@ func NewConnection(ctx context.Context, uri string) (context.Context, error) { // For example tcp://localhost:<port> // or unix:///run/podman/podman.sock // or ssh://<user>@<host>[:port]/run/podman/podman.sock?secure=True -func NewConnectionWithIdentity(ctx context.Context, uri string, identity string) (context.Context, error) { +func NewConnectionWithIdentity(ctx context.Context, uri string, identity string, machine bool) (context.Context, error) { var ( err error ) @@ -96,10 +96,11 @@ func NewConnectionWithIdentity(ctx context.Context, uri string, identity string) return nil, err } conn, err := ssh.Dial(&ssh.ConnectionDialOptions{ - Host: uri, - Identity: identity, - User: _url.User, - Port: port, + Host: uri, + Identity: identity, + User: _url.User, + Port: port, + InsecureIsMachineConnection: machine, }, "golang") if err != nil { return nil, err diff --git a/pkg/domain/entities/engine.go b/pkg/domain/entities/engine.go index a69cf5111..d0d439a1b 100644 --- a/pkg/domain/entities/engine.go +++ b/pkg/domain/entities/engine.go @@ -54,4 +54,5 @@ type PodmanConfig struct { StorageDriver string StorageOpts []string SSHMode string + MachineMode bool } diff --git a/pkg/domain/infra/runtime_abi.go b/pkg/domain/infra/runtime_abi.go index 7b5198d2f..94565c59e 100644 --- a/pkg/domain/infra/runtime_abi.go +++ b/pkg/domain/infra/runtime_abi.go @@ -21,7 +21,7 @@ func NewContainerEngine(facts *entities.PodmanConfig) (entities.ContainerEngine, r, err := NewLibpodRuntime(facts.FlagSet, facts) return r, err case entities.TunnelMode: - ctx, err := bindings.NewConnectionWithIdentity(context.Background(), facts.URI, facts.Identity) + ctx, err := bindings.NewConnectionWithIdentity(context.Background(), facts.URI, facts.Identity, facts.MachineMode) return &tunnel.ContainerEngine{ClientCtx: ctx}, err } return nil, fmt.Errorf("runtime mode '%v' is not supported", facts.EngineMode) @@ -35,7 +35,7 @@ func NewImageEngine(facts *entities.PodmanConfig) (entities.ImageEngine, error) return r, err case entities.TunnelMode: // TODO: look at me! - ctx, err := bindings.NewConnectionWithIdentity(context.Background(), facts.URI, facts.Identity) + ctx, err := bindings.NewConnectionWithIdentity(context.Background(), facts.URI, facts.Identity, facts.MachineMode) return &tunnel.ImageEngine{ClientCtx: ctx}, err } return nil, fmt.Errorf("runtime mode '%v' is not supported", facts.EngineMode) diff --git a/pkg/domain/infra/runtime_tunnel.go b/pkg/domain/infra/runtime_tunnel.go index 8a4de032f..48e6a6773 100644 --- a/pkg/domain/infra/runtime_tunnel.go +++ b/pkg/domain/infra/runtime_tunnel.go @@ -18,12 +18,12 @@ var ( connection *context.Context ) -func newConnection(uri string, identity string) (context.Context, error) { +func newConnection(uri string, identity string, machine bool) (context.Context, error) { connectionMutex.Lock() defer connectionMutex.Unlock() if connection == nil { - ctx, err := bindings.NewConnectionWithIdentity(context.Background(), uri, identity) + ctx, err := bindings.NewConnectionWithIdentity(context.Background(), uri, identity, machine) if err != nil { return ctx, err } @@ -37,7 +37,7 @@ func NewContainerEngine(facts *entities.PodmanConfig) (entities.ContainerEngine, case entities.ABIMode: return nil, fmt.Errorf("direct runtime not supported") case entities.TunnelMode: - ctx, err := newConnection(facts.URI, facts.Identity) + ctx, err := newConnection(facts.URI, facts.Identity, facts.MachineMode) return &tunnel.ContainerEngine{ClientCtx: ctx}, err } return nil, fmt.Errorf("runtime mode '%v' is not supported", facts.EngineMode) @@ -49,7 +49,7 @@ func NewImageEngine(facts *entities.PodmanConfig) (entities.ImageEngine, error) case entities.ABIMode: return nil, fmt.Errorf("direct image runtime not supported") case entities.TunnelMode: - ctx, err := newConnection(facts.URI, facts.Identity) + ctx, err := newConnection(facts.URI, facts.Identity, facts.MachineMode) return &tunnel.ImageEngine{ClientCtx: ctx}, err } return nil, fmt.Errorf("runtime mode '%v' is not supported", facts.EngineMode) diff --git a/pkg/machine/connection.go b/pkg/machine/connection.go index 6ff761a92..93c638cc7 100644 --- a/pkg/machine/connection.go +++ b/pkg/machine/connection.go @@ -25,7 +25,8 @@ func AddConnection(uri fmt.Stringer, name, identity string, isDefault bool) erro cfg.Engine.ActiveService = name } dst := config.Destination{ - URI: uri.String(), + URI: uri.String(), + IsMachine: true, } dst.Identity = identity if cfg.Engine.ServiceDestinations == nil { diff --git a/pkg/machine/e2e/basic_test.go b/pkg/machine/e2e/basic_test.go index fa1728770..b7a11c7d9 100644 --- a/pkg/machine/e2e/basic_test.go +++ b/pkg/machine/e2e/basic_test.go @@ -1,8 +1,6 @@ package e2e_test import ( - "os" - . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" . "github.com/onsi/gomega/gexec" @@ -24,10 +22,6 @@ var _ = Describe("run basic podman commands", func() { It("Basic ops", func() { // golangci-lint has trouble with actually skipping tests marked Skip // so skip it on cirrus envs and where CIRRUS_CI isn't set. - if os.Getenv("CIRRUS_CI") != "false" { - Skip("FIXME: #15347 - ssh know hosts broken - fails on PR runs and on x86_64") - } - name := randomString() i := new(initMachine) session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath).withNow()).run() diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index fab25aa35..a6907c0df 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -405,6 +405,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { WritePath: v.getIgnitionFile(), UID: v.UID, } + err = machine.NewIgnitionFile(ign) return err == nil, err } @@ -1033,7 +1034,7 @@ func (v *MachineVM) SSH(_ string, opts machine.SSHOptions) error { sshDestination := username + "@localhost" port := strconv.Itoa(v.Port) - args := []string{"-i", v.IdentityPath, "-p", port, sshDestination, "-o", "UserKnownHostsFile=/dev/null", + args := []string{"-i", v.IdentityPath, "-p", port, sshDestination, "-o", "StrictHostKeyChecking=no", "-o", "LogLevel=ERROR", "-o", "SetEnv=LC_ALL="} if len(opts.Args) > 0 { args = append(args, opts.Args...) diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index 7d85fd2f3..b1828736d 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -500,20 +500,22 @@ func setupLivenessProbe(s *specgen.SpecGenerator, containerYAML v1.Container, re probe := containerYAML.LivenessProbe probeHandler := probe.Handler - // append `exit 1` to `cmd` so healthcheck can be marked as `unhealthy`. - // append `kill 1` to `cmd` if appropriate restart policy is configured. - if restartPolicy == "always" || restartPolicy == "onfailure" { - // container will be restarted so we can kill init. - failureCmd = "kill 1" - } - // configure healthcheck on the basis of Handler Actions. switch { case probeHandler.Exec != nil: execString := strings.Join(probeHandler.Exec.Command, " ") commandString = fmt.Sprintf("%s || %s", execString, failureCmd) case probeHandler.HTTPGet != nil: - commandString = fmt.Sprintf("curl %s://%s:%d/%s || %s", probeHandler.HTTPGet.Scheme, probeHandler.HTTPGet.Host, probeHandler.HTTPGet.Port.IntValue(), probeHandler.HTTPGet.Path, failureCmd) + // set defaults as in https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#http-probes + var uriScheme v1.URIScheme = "http" + if probeHandler.HTTPGet.Scheme != "" { + uriScheme = probeHandler.HTTPGet.Scheme + } + host := "localhost" // Kubernetes default is host IP, but with Podman there is only one node + if probeHandler.HTTPGet.Host != "" { + host = probeHandler.HTTPGet.Host + } + commandString = fmt.Sprintf("curl -f %s://%s:%d%s || %s", uriScheme, host, probeHandler.HTTPGet.Port.IntValue(), probeHandler.HTTPGet.Path, failureCmd) case probeHandler.TCPSocket != nil: commandString = fmt.Sprintf("nc -z -v %s %d || %s", probeHandler.TCPSocket.Host, probeHandler.TCPSocket.Port.IntValue(), failureCmd) } @@ -521,6 +523,10 @@ func setupLivenessProbe(s *specgen.SpecGenerator, containerYAML v1.Container, re if err != nil { return err } + // if restart policy is in place, ensure the health check enforces it + if restartPolicy == "always" || restartPolicy == "onfailure" { + s.HealthCheckOnFailureAction = define.HealthCheckOnFailureActionRestart + } return nil } return nil diff --git a/pkg/specgen/generate/kube/play_test.go b/pkg/specgen/generate/kube/play_test.go index ec0dc4bcd..efe2e51b1 100644 --- a/pkg/specgen/generate/kube/play_test.go +++ b/pkg/specgen/generate/kube/play_test.go @@ -11,6 +11,8 @@ import ( v1 "github.com/containers/podman/v4/pkg/k8s.io/api/core/v1" "github.com/containers/podman/v4/pkg/k8s.io/apimachinery/pkg/api/resource" v12 "github.com/containers/podman/v4/pkg/k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/containers/podman/v4/pkg/k8s.io/apimachinery/pkg/util/intstr" + "github.com/containers/podman/v4/pkg/specgen" "github.com/docker/docker/pkg/system" "github.com/stretchr/testify/assert" ) @@ -858,3 +860,60 @@ var ( }, } ) + +func TestHttpLivenessProbe(t *testing.T) { + tests := []struct { + name string + specGenerator specgen.SpecGenerator + container v1.Container + restartPolicy string + succeed bool + expectedURL string + }{ + { + "HttpLivenessProbeUrlSetCorrectly", + specgen.SpecGenerator{}, + v1.Container{ + LivenessProbe: &v1.Probe{ + Handler: v1.Handler{ + HTTPGet: &v1.HTTPGetAction{ + Scheme: "http", + Host: "127.0.0.1", + Port: intstr.FromInt(8080), + Path: "/health", + }, + }, + }, + }, + "always", + true, + "http://127.0.0.1:8080/health", + }, + { + "HttpLivenessProbeUrlUsesDefaults", + specgen.SpecGenerator{}, + v1.Container{ + LivenessProbe: &v1.Probe{ + Handler: v1.Handler{ + HTTPGet: &v1.HTTPGetAction{ + Port: intstr.FromInt(80), + Path: "/", + }, + }, + }, + }, + "always", + true, + "http://localhost:80/", + }, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + err := setupLivenessProbe(&test.specGenerator, test.container, test.restartPolicy) + assert.Equal(t, err == nil, test.succeed) + assert.Contains(t, test.specGenerator.ContainerHealthCheckConfig.HealthConfig.Test, test.expectedURL) + }) + } +} diff --git a/test/system/030-run.bats b/test/system/030-run.bats index 65a1150a3..6847880ab 100644 --- a/test/system/030-run.bats +++ b/test/system/030-run.bats @@ -901,22 +901,35 @@ $IMAGE--c_ok" \ run_podman rm $ctr_name } +# 15895: --privileged + --systemd = hide /dev/ttyNN @test "podman run --privileged as root with systemd will not mount /dev/tty" { skip_if_rootless "this test only makes sense as root" - ctr_name="container-$(random_string 5)" - run_podman run --rm -d --privileged --systemd=always --name "$ctr_name" "$IMAGE" /home/podman/pause + # First, confirm that we _have_ /dev/ttyNN devices on the host. + # ('skip' would be nicer in some sense... but could hide a regression. + # Fedora, RHEL, Debian, Ubuntu, Gentoo, all have /dev/ttyN, so if + # this ever triggers, it means a real problem we should know about.) + assert "$(ls /dev/tty* | grep -vx /dev/tty)" != "" \ + "Expected at least one /dev/ttyN device on host" - TTYs=$(ls /dev/tty*|sed '/^\/dev\/tty$/d') + # Ok now confirm that without --systemd, podman exposes ttyNN devices + run_podman run --rm -d --privileged $IMAGE ./pause + cid="$output" - if [[ $TTYs = "" ]]; then - die "Did not find any /dev/ttyN devices on local host" - else - run_podman exec "$ctr_name" ls /dev/ - assert "$(grep tty <<<$output)" = "tty" "There must be no /dev/ttyN devices in the container" - fi + run_podman exec $cid sh -c 'ls /dev/tty*' + assert "$output" != "/dev/tty" \ + "ls /dev/tty* without systemd; should have lots of ttyN devices" + run_podman stop -t 0 $cid + + # Actual test for 15895: with --systemd, no ttyN devices are passed through + run_podman run --rm -d --privileged --systemd=always $IMAGE ./pause + cid="$output" + + run_podman exec $cid sh -c 'ls /dev/tty*' + assert "$output" = "/dev/tty" \ + "ls /dev/tty* with --systemd=always: should have no ttyN devices" - run_podman stop "$ctr_name" + run_podman stop -t 0 $cid } # vim: filetype=sh diff --git a/vendor/github.com/containers/common/libimage/platform.go b/vendor/github.com/containers/common/libimage/platform.go index 736a193f6..274b2aa06 100644 --- a/vendor/github.com/containers/common/libimage/platform.go +++ b/vendor/github.com/containers/common/libimage/platform.go @@ -63,6 +63,9 @@ func toPlatformString(os, arch, variant string) string { // * 2) a bool indicating whether architecture, os or variant were set (some callers need that to decide whether they need to throw an error) // * 3) a fatal error that occurred prior to check for matches (e.g., storage errors etc.) func (i *Image) matchesPlatform(ctx context.Context, os, arch, variant string) (error, bool, error) { + if err := i.isCorrupted(""); err != nil { + return err, false, nil + } inspectInfo, err := i.inspectInfo(ctx) if err != nil { return nil, false, fmt.Errorf("inspecting image: %w", err) diff --git a/vendor/github.com/containers/common/pkg/config/config.go b/vendor/github.com/containers/common/pkg/config/config.go index 858f961b6..cde7cec53 100644 --- a/vendor/github.com/containers/common/pkg/config/config.go +++ b/vendor/github.com/containers/common/pkg/config/config.go @@ -613,6 +613,9 @@ type Destination struct { // Identity file with ssh key, optional Identity string `toml:"identity,omitempty"` + + // isMachine describes if the remote destination is a machine. + IsMachine bool `toml:"is_machine,omitempty"` } // NewConfig creates a new Config. It starts with an empty config and, if @@ -1235,32 +1238,32 @@ func Reload() (*Config, error) { return defConfig() } -func (c *Config) ActiveDestination() (uri, identity string, err error) { +func (c *Config) ActiveDestination() (uri, identity string, machine bool, err error) { if uri, found := os.LookupEnv("CONTAINER_HOST"); found { if v, found := os.LookupEnv("CONTAINER_SSHKEY"); found { identity = v } - return uri, identity, nil + return uri, identity, false, nil } connEnv := os.Getenv("CONTAINER_CONNECTION") switch { case connEnv != "": d, found := c.Engine.ServiceDestinations[connEnv] if !found { - return "", "", fmt.Errorf("environment variable CONTAINER_CONNECTION=%q service destination not found", connEnv) + return "", "", false, fmt.Errorf("environment variable CONTAINER_CONNECTION=%q service destination not found", connEnv) } - return d.URI, d.Identity, nil + return d.URI, d.Identity, d.IsMachine, nil case c.Engine.ActiveService != "": d, found := c.Engine.ServiceDestinations[c.Engine.ActiveService] if !found { - return "", "", fmt.Errorf("%q service destination not found", c.Engine.ActiveService) + return "", "", false, fmt.Errorf("%q service destination not found", c.Engine.ActiveService) } - return d.URI, d.Identity, nil + return d.URI, d.Identity, d.IsMachine, nil case c.Engine.RemoteURI != "": - return c.Engine.RemoteURI, c.Engine.RemoteIdentity, nil + return c.Engine.RemoteURI, c.Engine.RemoteIdentity, false, nil } - return "", "", errors.New("no service destination configured") + return "", "", false, errors.New("no service destination configured") } var ( diff --git a/vendor/github.com/containers/common/pkg/ssh/connection_golang.go b/vendor/github.com/containers/common/pkg/ssh/connection_golang.go index a5c1be89c..8ec3c45ed 100644 --- a/vendor/github.com/containers/common/pkg/ssh/connection_golang.go +++ b/vendor/github.com/containers/common/pkg/ssh/connection_golang.go @@ -3,6 +3,7 @@ package ssh import ( "bytes" "encoding/json" + "errors" "fmt" "io" "net" @@ -70,7 +71,7 @@ func golangConnectionDial(options ConnectionDialOptions) (*ConnectionDialReport, if err != nil { return nil, err } - cfg, err := ValidateAndConfigure(uri, options.Identity) + cfg, err := ValidateAndConfigure(uri, options.Identity, options.InsecureIsMachineConnection) if err != nil { return nil, err } @@ -84,12 +85,15 @@ func golangConnectionDial(options ConnectionDialOptions) (*ConnectionDialReport, } func golangConnectionExec(options ConnectionExecOptions) (*ConnectionExecReport, error) { + if !strings.HasPrefix(options.Host, "ssh://") { + options.Host = "ssh://" + options.Host + } _, uri, err := Validate(options.User, options.Host, options.Port, options.Identity) if err != nil { return nil, err } - cfg, err := ValidateAndConfigure(uri, options.Identity) + cfg, err := ValidateAndConfigure(uri, options.Identity, false) if err != nil { return nil, err } @@ -111,11 +115,15 @@ func golangConnectionScp(options ConnectionScpOptions) (*ConnectionScpReport, er return nil, err } + // removed for parsing + if !strings.HasPrefix(host, "ssh://") { + host = "ssh://" + host + } _, uri, err := Validate(options.User, host, options.Port, options.Identity) if err != nil { return nil, err } - cfg, err := ValidateAndConfigure(uri, options.Identity) + cfg, err := ValidateAndConfigure(uri, options.Identity, false) if err != nil { return nil, err } @@ -209,7 +217,7 @@ func GetUserInfo(uri *url.URL) (*url.Userinfo, error) { // ValidateAndConfigure will take a ssh url and an identity key (rsa and the like) and ensure the information given is valid // iden iden can be blank to mean no identity key // once the function validates the information it creates and returns an ssh.ClientConfig. -func ValidateAndConfigure(uri *url.URL, iden string) (*ssh.ClientConfig, error) { +func ValidateAndConfigure(uri *url.URL, iden string, insecureIsMachineConnection bool) (*ssh.ClientConfig, error) { var signers []ssh.Signer passwd, passwdSet := uri.User.Password() if iden != "" { // iden might be blank if coming from image scp or if no validation is needed @@ -272,23 +280,61 @@ func ValidateAndConfigure(uri *url.URL, iden string) (*ssh.ClientConfig, error) if err != nil { return nil, err } - keyFilePath := filepath.Join(homedir.Get(), ".ssh", "known_hosts") - known, err := knownhosts.New(keyFilePath) - if err != nil { - return nil, fmt.Errorf("creating host key callback function for %s: %w", keyFilePath, err) + + var callback ssh.HostKeyCallback + if insecureIsMachineConnection { + callback = ssh.InsecureIgnoreHostKey() + } else { + callback = ssh.HostKeyCallback(func(host string, remote net.Addr, pubKey ssh.PublicKey) error { + keyFilePath := filepath.Join(homedir.Get(), ".ssh", "known_hosts") + known, err := knownhosts.New(keyFilePath) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + logrus.Warn("please create a known_hosts file. The next time this host is connected to, podman will add it to known_hosts") + return nil + } + return err + } + // we need to check if there is an error from reading known hosts for this public key and if there is an error, what is it, and why is it happening? + // if it is a key mismatch we want to error since we know the host using another key + // however, if it is a general error not because of a known key, we want to add our key to the known_hosts file + hErr := known(host, remote, pubKey) + var keyErr *knownhosts.KeyError + // if keyErr.Want is not empty, we are receiving a different key meaning the host is known but we are using the wrong key + as := errors.As(hErr, &keyErr) + switch { + case as && len(keyErr.Want) > 0: + logrus.Warnf("ssh host key mismatch for host %s, got key %s of type %s", host, ssh.FingerprintSHA256(pubKey), pubKey.Type()) + return keyErr + // if keyErr.Want is empty that just means we do not know this host yet, add it. + case as && len(keyErr.Want) == 0: + // write to known_hosts + err := addKnownHostsEntry(host, pubKey) + if err != nil { + if os.IsNotExist(err) { + logrus.Warn("podman will soon require a known_hosts file to function properly.") + return nil + } + return err + } + case hErr != nil: + return hErr + } + return nil + }) } cfg := &ssh.ClientConfig{ User: uri.User.Username(), Auth: authMethods, - HostKeyCallback: known, + HostKeyCallback: callback, Timeout: tick, } return cfg, nil } func getUDS(uri *url.URL, iden string) (string, error) { - cfg, err := ValidateAndConfigure(uri, iden) + cfg, err := ValidateAndConfigure(uri, iden, false) if err != nil { return "", fmt.Errorf("failed to validate: %w", err) } @@ -324,3 +370,20 @@ func getUDS(uri *url.URL, iden string) (string, error) { } return info.Host.RemoteSocket.Path, nil } + +// addKnownHostsEntry adds (host, pubKey) to user’s known_hosts. +func addKnownHostsEntry(host string, pubKey ssh.PublicKey) error { + hd := homedir.Get() + known := filepath.Join(hd, ".ssh", "known_hosts") + f, err := os.OpenFile(known, os.O_APPEND|os.O_WRONLY, 0o600) + if err != nil { + return err + } + defer f.Close() + l := knownhosts.Line([]string{host}, pubKey) + if _, err = f.WriteString("\n" + l + "\n"); err != nil { + return err + } + logrus.Infof("key %s added to %s", ssh.FingerprintSHA256(pubKey), known) + return nil +} diff --git a/vendor/github.com/containers/common/pkg/ssh/types.go b/vendor/github.com/containers/common/pkg/ssh/types.go index f22b5fba9..16512c43f 100644 --- a/vendor/github.com/containers/common/pkg/ssh/types.go +++ b/vendor/github.com/containers/common/pkg/ssh/types.go @@ -27,12 +27,13 @@ type ConnectionCreateOptions struct { } type ConnectionDialOptions struct { - Host string - Identity string - User *url.Userinfo - Port int - Auth []string - Timeout time.Duration + Host string + Identity string + User *url.Userinfo + Port int + Auth []string + Timeout time.Duration + InsecureIsMachineConnection bool } type ConnectionDialReport struct { diff --git a/vendor/github.com/containers/common/pkg/ssh/utils.go b/vendor/github.com/containers/common/pkg/ssh/utils.go index c15745015..b05105d9c 100644 --- a/vendor/github.com/containers/common/pkg/ssh/utils.go +++ b/vendor/github.com/containers/common/pkg/ssh/utils.go @@ -21,6 +21,7 @@ func Validate(user *url.Userinfo, path string, port int, identity string) (*conf if strings.Contains(path, "/run") { sock = strings.Split(path, "/run")[1] } + // url.Parse NEEDS ssh://, if this ever fails or returns some nonsense, that is why. uri, err := url.Parse(path) if err != nil { return nil, nil, err @@ -33,9 +34,9 @@ func Validate(user *url.Userinfo, path string, port int, identity string) (*conf if uri.Port() == "" { if port != 0 { - uri.Host = net.JoinHostPort(uri.Hostname(), strconv.Itoa(port)) + uri.Host = net.JoinHostPort(uri.Host, strconv.Itoa(port)) } else { - uri.Host = net.JoinHostPort(uri.Hostname(), "22") + uri.Host = net.JoinHostPort(uri.Host, "22") } } diff --git a/vendor/modules.txt b/vendor/modules.txt index 17e889387..222b70cd3 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -120,7 +120,7 @@ github.com/containers/buildah/pkg/rusage github.com/containers/buildah/pkg/sshagent github.com/containers/buildah/pkg/util github.com/containers/buildah/util -# github.com/containers/common v0.49.2-0.20220920205255-8062f81c5497 +# github.com/containers/common v0.49.2-0.20220926195839-590004b80685 ## explicit; go 1.17 github.com/containers/common/libimage github.com/containers/common/libimage/define |