From 7ebd36d6943f2e515dbd844b89aabf721965ee73 Mon Sep 17 00:00:00 2001 From: Mathieu Tortuyaux Date: Mon, 22 Jan 2024 16:40:29 +0100 Subject: [PATCH 1/2] resource/url: customize local address (port) When establishing a network connection random port selection from the Kernel for local address is enough in 99.9% of the case. For the 0.1%, let's add a way to customize the local port used. Signed-off-by: Mathieu Tortuyaux --- internal/resource/url.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/internal/resource/url.go b/internal/resource/url.go index 58e0b9fcd..bf360972b 100644 --- a/internal/resource/url.go +++ b/internal/resource/url.go @@ -23,10 +23,12 @@ import ( "fmt" "hash" "io" + "net" "net/http" "net/url" "os" "strings" + "syscall" "time" "cloud.google.com/go/compute/metadata" @@ -125,6 +127,10 @@ type FetchOptions struct { // HTTPVerb is an HTTP request method to indicate the desired action to // be performed for a given resource. HTTPVerb string + + // LocalPort is a function returning a local port used to establish the TCP connection. + // Most of the time, letting the Kernel choose a random port is enough. + LocalPort func() int } // FetchToBuffer will fetch the given url into a temporary file, and then read @@ -287,6 +293,28 @@ func (f *Fetcher) fetchFromHTTP(u url.URL, dest io.Writer, opts FetchOptions) er } } + if opts.LocalPort != nil { + var ( + d net.Dialer + p int + ) + + // Assert that the port is not already used. + for { + p = opts.LocalPort() + l, err := net.Listen("tcp4", fmt.Sprintf(":%d", p)) + if err != nil && errors.Is(err, syscall.EADDRINUSE) { + continue + } else if err == nil { + l.Close() + break + } + } + d.LocalAddr = &net.TCPAddr{Port: p} + + f.client.transport.DialContext = d.DialContext + } + // We do not want to redirect HTTP headers f.client.client.CheckRedirect = func(req *http.Request, via []*http.Request) error { req.Header = make(http.Header) From ce0bf8c6439a3a9e93683a29f4b59da7b1560331 Mon Sep 17 00:00:00 2001 From: Mathieu Tortuyaux Date: Mon, 22 Jan 2024 16:20:53 +0100 Subject: [PATCH 2/2] providers: add scaleway Signed-off-by: Mathieu Tortuyaux --- docs/release-notes.md | 1 + docs/supported-platforms.md | 1 + internal/providers/scaleway/scaleway.go | 62 +++++++++++++++++++++++++ internal/register/providers.go | 1 + 4 files changed, 65 insertions(+) create mode 100644 internal/providers/scaleway/scaleway.go diff --git a/docs/release-notes.md b/docs/release-notes.md index ac88f6254..23771523f 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -13,6 +13,7 @@ nav_order: 9 ### Features +- Support Scaleway ### Changes diff --git a/docs/supported-platforms.md b/docs/supported-platforms.md index 1c9d63bba..e9f54e78c 100644 --- a/docs/supported-platforms.md +++ b/docs/supported-platforms.md @@ -26,6 +26,7 @@ Ignition is currently only supported for the following platforms: * [Equinix Metal] (`packet`) - Ignition will read its configuration from the instance userdata. Cloud SSH keys are handled separately. * [IBM Power Systems Virtual Server] (`powervs`) - Ignition will read its configuration from the instance userdata. Cloud SSH keys are handled separately. * [QEMU] (`qemu`) - Ignition will read its configuration from the 'opt/com.coreos/config' key on the QEMU Firmware Configuration Device (available in QEMU 2.4.0 and higher). +* [Scaleway] (`scaleway`) - Ignition will read its configuration from the instance userdata. Cloud SSH keys are handled separately. * [VirtualBox] (`virtualbox`) - Use the VirtualBox guest property `/Ignition/Config` to provide the config to the virtual machine. * [VMware] (`vmware`) - Use the VMware Guestinfo variables `ignition.config.data` and `ignition.config.data.encoding` to provide the config and its encoding to the virtual machine. Valid encodings are "", "base64", and "gzip+base64". Guestinfo variables can be provided directly or via an OVF environment, with priority given to variables specified directly. * [Vultr] (`vultr`) - Ignition will read its configuration from the instance userdata. Cloud SSH keys are handled separately. diff --git a/internal/providers/scaleway/scaleway.go b/internal/providers/scaleway/scaleway.go new file mode 100644 index 000000000..230519fe3 --- /dev/null +++ b/internal/providers/scaleway/scaleway.go @@ -0,0 +1,62 @@ +// Copyright 2024 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// The Scaleway provider fetches a remote configuration from the Scaleway +// user-data metadata service URL. +// NOTE: For security reason, Scaleway requires to query user data with a source port below 1024. + +package scaleway + +import ( + "math/rand" + "net/url" + + "github.com/coreos/ignition/v2/config/v3_5_experimental/types" + "github.com/coreos/ignition/v2/internal/platform" + "github.com/coreos/ignition/v2/internal/providers/util" + "github.com/coreos/ignition/v2/internal/resource" + + "github.com/coreos/vcontext/report" +) + +var ( + userdataURL = url.URL{ + Scheme: "http", + Host: "169.254.42.42", + Path: "user_data/cloud-init", + } +) + +func init() { + platform.Register(platform.Provider{ + Name: "scaleway", + Fetch: fetchConfig, + }) +} + +func fetchConfig(f *resource.Fetcher) (types.Config, report.Report, error) { + // For security reason, Scaleway requires to query user data with a source port below 1024. + port := func() int { + return rand.Intn(1022) + 1 + } + + data, err := f.FetchToBuffer(userdataURL, resource.FetchOptions{ + LocalPort: port, + }) + if err != nil && err != resource.ErrNotFound { + return types.Config{}, report.Report{}, err + } + + return util.ParseConfig(f.Logger, data) +} diff --git a/internal/register/providers.go b/internal/register/providers.go index ddebb8bb9..e7a06b382 100644 --- a/internal/register/providers.go +++ b/internal/register/providers.go @@ -35,6 +35,7 @@ import ( _ "github.com/coreos/ignition/v2/internal/providers/packet" _ "github.com/coreos/ignition/v2/internal/providers/powervs" _ "github.com/coreos/ignition/v2/internal/providers/qemu" + _ "github.com/coreos/ignition/v2/internal/providers/scaleway" _ "github.com/coreos/ignition/v2/internal/providers/virtualbox" _ "github.com/coreos/ignition/v2/internal/providers/vmware" _ "github.com/coreos/ignition/v2/internal/providers/vultr"