API: /info: remove SecurityOptions re-formatting for API < 1.25

On docker 1.12 (API v1.24) and older, the `SecurityOptions` field of the
`/info` response would only list names of the security options that are
enabled in the daemon. API v1.25 added additional information to this
information. Initially, this included a change to return the information
in structured format (b237189e6c), which
was a backward-incompatible change, so an alternative format was introduced
in 514ca09426 to used a string-slice, but
prefixing options with `name=`, followed by the name of the security-options
and any config options related to it as `key[=<value>]` pairs.

On current API versions:

    curl -s --unix-socket /var/run/docker.sock 'http://localhost/v1.51/info' | jq .SecurityOptions
    [
      "name=seccomp,profile=builtin",
      "name=cgroupns"
    ]

On API version v1.24:

    curl -s --unix-socket /var/run/docker.sock 'http://localhost/v1.24/info' | jq .SecurityOptions
    [
      "seccomp",
      "cgroupns"
    ]

The Docker CLI unconditionally handles either format when presenting the
information; for backward-compatibility, it contains fallback code to handle
cases where no `name=` prefix is present, but this logic is not based on
API version.

Given that any current version of the CLI is handling either format, and
versions of the CLI that did not have this handling are at least 9 Years
old (and long EOL), removing the old format is unlikely to be causing
issues and we can remove this special handling, and return the information
in the current format.

If we consider this information to be relevant for clients, we should
ultimately consider making it available in a more structured format as
was the original intent of b237189e6c.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2025-09-15 14:56:51 +02:00
parent 689decba9a
commit a83d91f427
3 changed files with 4 additions and 56 deletions

View File

@@ -175,6 +175,10 @@ func (daemon *Daemon) fillPluginsInfo(ctx context.Context, v *system.Info, cfg *
}
}
// fillSecurityOptions fills the [system.Info.SecurityOptions] field based
// on the daemon configuration.
//
// TODO(thaJeztah): consider making [system.Info.SecurityOptions] a structured response as originally intended in https://github.com/moby/moby/pull/26276
func (daemon *Daemon) fillSecurityOptions(v *system.Info, sysInfo *sysinfo.SysInfo, cfg *config.Config) {
var securityOptions []string
if sysInfo.AppArmor {

View File

@@ -5,7 +5,6 @@ import (
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
"github.com/containerd/log"
@@ -22,7 +21,6 @@ import (
"github.com/moby/moby/v2/daemon/server/backend"
"github.com/moby/moby/v2/daemon/server/httputils"
"github.com/moby/moby/v2/daemon/server/router/build"
"github.com/moby/moby/v2/daemon/server/systembackend"
"github.com/moby/moby/v2/pkg/ioutils"
"github.com/pkg/errors"
"golang.org/x/sync/errgroup"
@@ -75,18 +73,6 @@ func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *ht
info.Warnings = append(info.Warnings, info.Swarm.Warnings...)
}
if versions.LessThan(version, "1.25") {
// TODO: handle this conversion in engine-api
kvSecOpts, err := decodeSecurityOptions(info.SecurityOptions)
if err != nil {
info.Warnings = append(info.Warnings, err.Error())
}
var nameOnly []string
for _, so := range kvSecOpts {
nameOnly = append(nameOnly, so.Name)
}
info.SecurityOptions = nameOnly
}
if versions.LessThan(version, "1.44") {
for k, rt := range info.Runtimes {
// Status field introduced in API v1.44.
@@ -137,36 +123,6 @@ func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *ht
return httputils.WriteJSON(w, http.StatusOK, info)
}
// decodeSecurityOptions decodes a security options string slice to a
// type-safe [systembackend.SecurityOption].
func decodeSecurityOptions(opts []string) ([]systembackend.SecurityOption, error) {
so := []systembackend.SecurityOption{}
for _, opt := range opts {
// support output from a < 1.13 docker daemon
if !strings.Contains(opt, "=") {
so = append(so, systembackend.SecurityOption{Name: opt})
continue
}
secopt := systembackend.SecurityOption{}
for _, s := range strings.Split(opt, ",") {
k, v, ok := strings.Cut(s, "=")
if !ok {
return nil, fmt.Errorf("invalid security option %q", s)
}
if k == "" || v == "" {
return nil, errors.New("invalid empty security option")
}
if k == "name" {
secopt.Name = v
continue
}
secopt.Options = append(secopt.Options, systembackend.KeyValue{Key: k, Value: v})
}
so = append(so, secopt)
}
return so, nil
}
func (s *systemRouter) getVersion(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
info, err := s.backend.SystemVersion(ctx)
if err != nil {

View File

@@ -1,12 +0,0 @@
package systembackend
// SecurityOption contains the name and options of a security option
type SecurityOption struct {
Name string
Options []KeyValue
}
// KeyValue holds a key/value pair.
type KeyValue struct {
Key, Value string
}