Merge pull request #123841 from hercules-ci/podman-socket
nixos/podman: Add docker socket supportlaunchpad/nixpkgs/master
commit
774fe1878b
@ -0,0 +1,34 @@ |
|||||||
|
{ config, lib, pkg, ... }: |
||||||
|
let |
||||||
|
inherit (lib) |
||||||
|
mkOption |
||||||
|
types |
||||||
|
; |
||||||
|
|
||||||
|
cfg = config.virtualisation.podman.networkSocket; |
||||||
|
|
||||||
|
in |
||||||
|
{ |
||||||
|
options.virtualisation.podman.networkSocket = { |
||||||
|
server = mkOption { |
||||||
|
type = types.enum [ "ghostunnel" ]; |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
config = { |
||||||
|
|
||||||
|
services.ghostunnel = lib.mkIf (cfg.enable && cfg.server == "ghostunnel") { |
||||||
|
enable = true; |
||||||
|
servers."podman-socket" = { |
||||||
|
inherit (cfg.tls) cert key cacert; |
||||||
|
listen = "${cfg.listenAddress}:${toString cfg.port}"; |
||||||
|
target = "unix:/run/podman/podman.sock"; |
||||||
|
allowAll = lib.mkDefault true; |
||||||
|
}; |
||||||
|
}; |
||||||
|
systemd.services.ghostunnel-server-podman-socket.serviceConfig.SupplementaryGroups = ["podman"]; |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
meta.maintainers = lib.teams.podman.members ++ [ lib.maintainers.roberth ]; |
||||||
|
} |
@ -0,0 +1,91 @@ |
|||||||
|
{ config, lib, pkg, ... }: |
||||||
|
let |
||||||
|
inherit (lib) |
||||||
|
mkOption |
||||||
|
types |
||||||
|
; |
||||||
|
|
||||||
|
cfg = config.virtualisation.podman.networkSocket; |
||||||
|
|
||||||
|
in |
||||||
|
{ |
||||||
|
options.virtualisation.podman.networkSocket = { |
||||||
|
enable = mkOption { |
||||||
|
type = types.bool; |
||||||
|
default = false; |
||||||
|
description = '' |
||||||
|
Make the Podman and Docker compatibility API available over the network |
||||||
|
with TLS client certificate authentication. |
||||||
|
|
||||||
|
This allows Docker clients to connect with the equivalents of the Docker |
||||||
|
CLI <code>-H</code> and <code>--tls*</code> family of options. |
||||||
|
|
||||||
|
For certificate setup, see https://docs.docker.com/engine/security/protect-access/ |
||||||
|
|
||||||
|
This option is independent of <xref linkend="opt-virtualisation.podman.dockerSocket.enable"/>. |
||||||
|
''; |
||||||
|
}; |
||||||
|
|
||||||
|
server = mkOption { |
||||||
|
type = types.enum []; |
||||||
|
description = '' |
||||||
|
Choice of TLS proxy server. |
||||||
|
''; |
||||||
|
example = "ghostunnel"; |
||||||
|
}; |
||||||
|
|
||||||
|
openFirewall = mkOption { |
||||||
|
type = types.bool; |
||||||
|
default = false; |
||||||
|
description = '' |
||||||
|
Whether to open the port in the firewall. |
||||||
|
''; |
||||||
|
}; |
||||||
|
|
||||||
|
tls.cacert = mkOption { |
||||||
|
type = types.path; |
||||||
|
description = '' |
||||||
|
Path to CA certificate to use for client authentication. |
||||||
|
''; |
||||||
|
}; |
||||||
|
|
||||||
|
tls.cert = mkOption { |
||||||
|
type = types.path; |
||||||
|
description = '' |
||||||
|
Path to certificate describing the server. |
||||||
|
''; |
||||||
|
}; |
||||||
|
|
||||||
|
tls.key = mkOption { |
||||||
|
type = types.path; |
||||||
|
description = '' |
||||||
|
Path to the private key corresponding to the server certificate. |
||||||
|
|
||||||
|
Use a string for this setting. Otherwise it will be copied to the Nix |
||||||
|
store first, where it is readable by any system process. |
||||||
|
''; |
||||||
|
}; |
||||||
|
|
||||||
|
port = mkOption { |
||||||
|
type = types.port; |
||||||
|
default = 2376; |
||||||
|
description = '' |
||||||
|
TCP port number for receiving TLS connections. |
||||||
|
''; |
||||||
|
}; |
||||||
|
listenAddress = mkOption { |
||||||
|
type = types.str; |
||||||
|
default = "0.0.0.0"; |
||||||
|
description = '' |
||||||
|
Interface address for receiving TLS connections. |
||||||
|
''; |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
config = { |
||||||
|
networking.firewall.allowedTCPPorts = |
||||||
|
lib.optional (cfg.enable && cfg.openFirewall) cfg.port; |
||||||
|
}; |
||||||
|
|
||||||
|
meta.maintainers = lib.teams.podman.members ++ [ lib.maintainers.roberth ]; |
||||||
|
} |
@ -0,0 +1,150 @@ |
|||||||
|
/* |
||||||
|
This test runs podman as a backend for the Docker CLI. |
||||||
|
*/ |
||||||
|
import ./make-test-python.nix ( |
||||||
|
{ pkgs, lib, ... }: |
||||||
|
|
||||||
|
let gen-ca = pkgs.writeScript "gen-ca" '' |
||||||
|
# Create CA |
||||||
|
PATH="${pkgs.openssl}/bin:$PATH" |
||||||
|
openssl genrsa -out ca-key.pem 4096 |
||||||
|
openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -subj '/C=NL/ST=Zuid-Holland/L=The Hague/O=Stevige Balken en Planken B.V./OU=OpSec/CN=Certificate Authority' -out ca.pem |
||||||
|
|
||||||
|
# Create service |
||||||
|
openssl genrsa -out podman-key.pem 4096 |
||||||
|
openssl req -subj '/CN=podman' -sha256 -new -key podman-key.pem -out service.csr |
||||||
|
echo subjectAltName = DNS:podman,IP:127.0.0.1 >> extfile.cnf |
||||||
|
echo extendedKeyUsage = serverAuth >> extfile.cnf |
||||||
|
openssl x509 -req -days 365 -sha256 -in service.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out podman-cert.pem -extfile extfile.cnf |
||||||
|
|
||||||
|
# Create client |
||||||
|
openssl genrsa -out client-key.pem 4096 |
||||||
|
openssl req -subj '/CN=client' -new -key client-key.pem -out client.csr |
||||||
|
echo extendedKeyUsage = clientAuth > extfile-client.cnf |
||||||
|
openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -extfile extfile-client.cnf |
||||||
|
|
||||||
|
# Create CA 2 |
||||||
|
PATH="${pkgs.openssl}/bin:$PATH" |
||||||
|
openssl genrsa -out ca-2-key.pem 4096 |
||||||
|
openssl req -new -x509 -days 365 -key ca-2-key.pem -sha256 -subj '/C=NL/ST=Zuid-Holland/L=The Hague/O=Stevige Balken en Planken B.V./OU=OpSec/CN=Certificate Authority' -out ca-2.pem |
||||||
|
|
||||||
|
# Create client signed by CA 2 |
||||||
|
openssl genrsa -out client-2-key.pem 4096 |
||||||
|
openssl req -subj '/CN=client' -new -key client-2-key.pem -out client-2.csr |
||||||
|
echo extendedKeyUsage = clientAuth > extfile-client.cnf |
||||||
|
openssl x509 -req -days 365 -sha256 -in client-2.csr -CA ca-2.pem -CAkey ca-2-key.pem -CAcreateserial -out client-2-cert.pem -extfile extfile-client.cnf |
||||||
|
|
||||||
|
''; |
||||||
|
in |
||||||
|
{ |
||||||
|
name = "podman-tls-ghostunnel"; |
||||||
|
meta = { |
||||||
|
maintainers = lib.teams.podman.members ++ [ lib.maintainers.roberth ]; |
||||||
|
}; |
||||||
|
|
||||||
|
nodes = { |
||||||
|
podman = |
||||||
|
{ pkgs, ... }: |
||||||
|
{ |
||||||
|
virtualisation.podman.enable = true; |
||||||
|
virtualisation.podman.dockerSocket.enable = true; |
||||||
|
virtualisation.podman.networkSocket = { |
||||||
|
enable = true; |
||||||
|
openFirewall = true; |
||||||
|
server = "ghostunnel"; |
||||||
|
tls.cert = "/root/podman-cert.pem"; |
||||||
|
tls.key = "/root/podman-key.pem"; |
||||||
|
tls.cacert = "/root/ca.pem"; |
||||||
|
}; |
||||||
|
|
||||||
|
environment.systemPackages = [ |
||||||
|
pkgs.docker-client |
||||||
|
]; |
||||||
|
|
||||||
|
users.users.alice = { |
||||||
|
isNormalUser = true; |
||||||
|
home = "/home/alice"; |
||||||
|
description = "Alice Foobar"; |
||||||
|
extraGroups = ["podman"]; |
||||||
|
}; |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
client = { ... }: { |
||||||
|
environment.systemPackages = [ |
||||||
|
# Installs the docker _client_ only |
||||||
|
# Normally, you'd want `virtualisation.docker.enable = true;`. |
||||||
|
pkgs.docker-client |
||||||
|
]; |
||||||
|
environment.variables.DOCKER_HOST = "podman:2376"; |
||||||
|
environment.variables.DOCKER_TLS_VERIFY = "1"; |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
testScript = '' |
||||||
|
import shlex |
||||||
|
|
||||||
|
|
||||||
|
def su_cmd(user, cmd): |
||||||
|
cmd = shlex.quote(cmd) |
||||||
|
return f"su {user} -l -c {cmd}" |
||||||
|
|
||||||
|
def cmd(command): |
||||||
|
print(f"+{command}") |
||||||
|
r = os.system(command) |
||||||
|
if r != 0: |
||||||
|
raise Exception(f"Command {command} failed with exit code {r}") |
||||||
|
|
||||||
|
start_all() |
||||||
|
cmd("${gen-ca}") |
||||||
|
|
||||||
|
podman.copy_from_host("ca.pem", "/root/ca.pem") |
||||||
|
podman.copy_from_host("podman-cert.pem", "/root/podman-cert.pem") |
||||||
|
podman.copy_from_host("podman-key.pem", "/root/podman-key.pem") |
||||||
|
|
||||||
|
client.copy_from_host("ca.pem", "/root/.docker/ca.pem") |
||||||
|
# client.copy_from_host("podman-cert.pem", "/root/podman-cert.pem") |
||||||
|
client.copy_from_host("client-cert.pem", "/root/.docker/cert.pem") |
||||||
|
client.copy_from_host("client-key.pem", "/root/.docker/key.pem") |
||||||
|
|
||||||
|
# TODO (ghostunnel): add file watchers so the restart isn't necessary |
||||||
|
podman.succeed("systemctl reset-failed && systemctl restart ghostunnel-server-podman-socket.service") |
||||||
|
|
||||||
|
podman.wait_for_unit("sockets.target") |
||||||
|
podman.wait_for_unit("ghostunnel-server-podman-socket.service") |
||||||
|
|
||||||
|
with subtest("Create default network"): |
||||||
|
podman.succeed("docker network create default") |
||||||
|
|
||||||
|
with subtest("Root docker cli also works"): |
||||||
|
podman.succeed("docker version") |
||||||
|
|
||||||
|
with subtest("A podman member can also still use the docker cli"): |
||||||
|
podman.succeed(su_cmd("alice", "docker version")) |
||||||
|
|
||||||
|
with subtest("Run container remotely via docker cli"): |
||||||
|
client.succeed("docker version") |
||||||
|
|
||||||
|
# via socket would be nicer |
||||||
|
podman.succeed("tar cv --files-from /dev/null | podman import - scratchimg") |
||||||
|
|
||||||
|
client.succeed( |
||||||
|
"docker run -d --name=sleeping -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin scratchimg /bin/sleep 10" |
||||||
|
) |
||||||
|
client.succeed("docker ps | grep sleeping") |
||||||
|
podman.succeed("docker ps | grep sleeping") |
||||||
|
client.succeed("docker stop sleeping") |
||||||
|
client.succeed("docker rm sleeping") |
||||||
|
|
||||||
|
with subtest("Clients without cert will be denied"): |
||||||
|
client.succeed("rm /root/.docker/{cert,key}.pem") |
||||||
|
client.fail("docker version") |
||||||
|
|
||||||
|
with subtest("Clients with wrong cert will be denied"): |
||||||
|
client.copy_from_host("client-2-cert.pem", "/root/.docker/cert.pem") |
||||||
|
client.copy_from_host("client-2-key.pem", "/root/.docker/key.pem") |
||||||
|
client.fail("docker version") |
||||||
|
|
||||||
|
''; |
||||||
|
} |
||||||
|
) |
Loading…
Reference in new issue