diff --git a/gcsweb/Dockerfile.in b/gcsweb/Dockerfile.in index 7ec7f0c78899..69b00a0ae18f 100644 --- a/gcsweb/Dockerfile.in +++ b/gcsweb/Dockerfile.in @@ -12,15 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM ARG_FROM +FROM gcr.io/distroless/static MAINTAINER Tim Hockin -RUN apk update --no-cache && apk add ca-certificates +ADD passwd /etc/passwd +USER nobody:nobody + ADD bin/ARG_ARCH/ARG_BIN /ARG_BIN ADD icons /icons ADD styles /styles -RUN chmod -R go+r /icons /styles -USER nobody:nobody ENTRYPOINT ["/ARG_BIN"] diff --git a/gcsweb/Makefile b/gcsweb/Makefile index 6191ba9d96c4..b580c5ee7593 100644 --- a/gcsweb/Makefile +++ b/gcsweb/Makefile @@ -25,7 +25,7 @@ REGISTRY ?= staging-k8s.gcr.io ARCH ?= amd64 # The version string. -VERSION := v1.0.6 +VERSION := v1.1.0 ### ### These variables should not need tweaking. @@ -33,28 +33,14 @@ VERSION := v1.0.6 SRC_DIRS := cmd pkg # directories which hold app source (not vendored) -# Other architectures are not supported because I don't know how to get CA -# certs for busybox. -# arm arm64 ppc64le -ALL_ARCH := amd64 - -# Set default base image dynamically for each arch -ifeq ($(ARCH),amd64) - BASEIMAGE?=alpine -endif -ifeq ($(ARCH),arm) - BASEIMAGE?=armel/busybox -endif -ifeq ($(ARCH),arm64) - BASEIMAGE?=aarch64/busybox -endif -ifeq ($(ARCH),ppc64le) - BASEIMAGE?=ppc64le/busybox -endif - -IMAGE := $(REGISTRY)/$(BIN)-$(ARCH) - -BUILD_IMAGE ?= golang:1.9.3-alpine +ALL_ARCH := amd64 arm arm64 ppc64le s390x + +IMAGE := $(REGISTRY)/$(BIN) + +BUILD_IMAGE ?= golang:1.11.5 + +# This option is for running docker manifest command +export DOCKER_CLI_EXPERIMENTAL := enabled # If you want to build all binaries, see the 'all-build' rule. # If you want to build all containers, see the 'all-container' rule. @@ -74,7 +60,7 @@ all-build: $(addprefix build-, $(ALL_ARCH)) all-container: $(addprefix container-, $(ALL_ARCH)) -all-push: $(addprefix push-, $(ALL_ARCH)) +all-push-images: $(addprefix push-, $(ALL_ARCH)) build: bin/$(ARCH)/$(BIN) @@ -97,28 +83,28 @@ bin/$(ARCH)/$(BIN): build-dirs ./build/build.sh \ " -DOTFILE_IMAGE = $(subst /,_,$(IMAGE))-$(VERSION) +DOTFILE_IMAGE = $(subst /,_,$(IMAGE))-$(ARCH)-$(VERSION) container: .container-$(DOTFILE_IMAGE) container-name .container-$(DOTFILE_IMAGE): bin/$(ARCH)/$(BIN) Dockerfile.in @sed \ -e 's|ARG_BIN|$(BIN)|g' \ -e 's|ARG_ARCH|$(ARCH)|g' \ - -e 's|ARG_FROM|$(BASEIMAGE)|g' \ Dockerfile.in > .dockerfile-$(ARCH) - @docker build -t $(IMAGE):$(VERSION) -f .dockerfile-$(ARCH) . - @docker images -q $(IMAGE):$(VERSION) > $@ + @chmod -R go+r icons styles + @docker build --pull -t $(IMAGE)-$(ARCH):$(VERSION) -f .dockerfile-$(ARCH) . + @docker images -q $(IMAGE)-$(ARCH):$(VERSION) > $@ container-name: - @echo "container: $(IMAGE):$(VERSION)" + @echo "container: $(IMAGE)-$(ARCH):$(VERSION)" push: .push-$(DOTFILE_IMAGE) push-name .push-$(DOTFILE_IMAGE): .container-$(DOTFILE_IMAGE) - @docker push $(IMAGE):$(VERSION) - @docker images -q $(IMAGE):$(VERSION) > $@ + @docker push $(IMAGE)-$(ARCH):$(VERSION) + @docker images -q $(IMAGE)-$(ARCH):$(VERSION) > $@ push-name: - @echo "pushed: $(IMAGE):$(VERSION)" + @echo "pushed: $(IMAGE)-$(ARCH):$(VERSION)" version: @echo $(VERSION) @@ -148,3 +134,10 @@ container-clean: bin-clean: rm -rf .go bin + +all-push: all-push-images push-manifest + +push-manifest: + docker manifest create --amend $(IMAGE):$(VERSION) $(shell echo $(ALL_ARCH) | sed -e "s~[^ ]*~$(IMAGE)\-&:$(VERSION)~g") + @for arch in $(ALL_ARCH); do docker manifest annotate --arch $${arch} $(IMAGE):$(VERSION) $(IMAGE)-$${arch}:$(VERSION); done + docker manifest push --purge $(IMAGE):$(VERSION) diff --git a/gcsweb/build/build.sh b/gcsweb/build/build.sh index d56981baa940..5c028fc5258b 100755 --- a/gcsweb/build/build.sh +++ b/gcsweb/build/build.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash # Copyright 2016 The Kubernetes Authors. # diff --git a/gcsweb/build/test.sh b/gcsweb/build/test.sh index a272dbc72c7f..55d43879fa11 100755 --- a/gcsweb/build/test.sh +++ b/gcsweb/build/test.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash # Copyright 2016 The Kubernetes Authors. # diff --git a/gcsweb/cmd/gcsweb/gcsweb.go b/gcsweb/cmd/gcsweb/gcsweb.go index fb1ce5fb5cb0..0c66a7d3488e 100644 --- a/gcsweb/cmd/gcsweb/gcsweb.go +++ b/gcsweb/cmd/gcsweb/gcsweb.go @@ -45,6 +45,8 @@ var flPort = flag.Int("p", 8080, "port number on which to listen") var flIcons = flag.String("i", "/icons", "path to the icons directory") var flStyles = flag.String("s", "/styles", "path to the styles directory") var flVersion = flag.Bool("version", false, "print version and exit") +var flUpgradeProxiedHTTPtoHTTPS = flag.Bool("upgrade-proxied-http-to-https", false, + "upgrade any proxied request (e.g. from GCLB) from http to https") const ( iconFile = "/icons/file.png" @@ -86,7 +88,7 @@ func main() { log.Printf("allowing %s", bucket) http.HandleFunc(bucket+"/", gcsRequest) http.HandleFunc(bucket, func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, bucket+"/", http.StatusMovedPermanently) + http.Redirect(w, r, bucket+"/", http.StatusPermanentRedirect) }) } // Handle unknown buckets. @@ -95,6 +97,9 @@ func main() { // Serve icons and styles. longCacheServer := func(h http.Handler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + if upgradeToHTTPS(w, r, newTxnLogger(r)) { + return + } // Mark as never expiring as per https://www.ietf.org/rfc/rfc2616.txt w.Header().Add("Cache-Control", "max-age=31536000") h.ServeHTTP(w, r) @@ -111,6 +116,20 @@ func main() { log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *flPort), nil)) } +func upgradeToHTTPS(w http.ResponseWriter, r *http.Request, logger txnLogger) bool { + if *flUpgradeProxiedHTTPtoHTTPS && r.Header.Get("X-Forwarded-Proto") == "http" { + newURL := *r.URL + newURL.Scheme = "https" + if newURL.Host == "" { + newURL.Host = r.Host + } + logger.Printf("redirect to %s [https upgrade]", newURL.String()) + http.Redirect(w, r, newURL.String(), http.StatusPermanentRedirect) + return true + } + return false +} + func healthzRequest(w http.ResponseWriter, r *http.Request) { newTxnLogger(r) @@ -123,8 +142,11 @@ func healthzRequest(w http.ResponseWriter, r *http.Request) { } func robotsRequest(w http.ResponseWriter, r *http.Request) { - newTxnLogger(r) + logger := newTxnLogger(r) + if upgradeToHTTPS(w, r, logger) { + return + } if r.Method != "GET" { w.WriteHeader(http.StatusMethodNotAllowed) return @@ -134,8 +156,11 @@ func robotsRequest(w http.ResponseWriter, r *http.Request) { } func unknownBucketRequest(w http.ResponseWriter, r *http.Request) { - newTxnLogger(r) + logger := newTxnLogger(r) + if upgradeToHTTPS(w, r, logger) { + return + } if r.Method != "GET" { w.WriteHeader(http.StatusMethodNotAllowed) return @@ -154,13 +179,19 @@ func unknownBucketRequest(w http.ResponseWriter, r *http.Request) { } func otherRequest(w http.ResponseWriter, r *http.Request) { - newTxnLogger(r) + logger := newTxnLogger(r) + if upgradeToHTTPS(w, r, logger) { + return + } http.NotFound(w, r) } func gcsRequest(w http.ResponseWriter, r *http.Request) { logger := newTxnLogger(r) + if upgradeToHTTPS(w, r, logger) { + return + } if r.Method != "GET" { w.WriteHeader(http.StatusMethodNotAllowed) return diff --git a/gcsweb/passwd b/gcsweb/passwd new file mode 100644 index 000000000000..6bb6b7ec013c --- /dev/null +++ b/gcsweb/passwd @@ -0,0 +1 @@ +nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin