Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add nix support #31

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
use flake
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@

# Dependency directories (remove the comment below to include it)
# vendor/

# Nix support
.direnv
result
19 changes: 9 additions & 10 deletions cmd/crproxy/main.go
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ignore this file when merging

Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,11 @@ func main() {
matcher.Store(&m)
})
}
opts = append(opts, crproxy.WithBlockFunc(func(info *crproxy.ImageInfo) bool {
return !(*matcher.Load()).Match(info.Host + "/" + info.Name)
opts = append(opts, crproxy.WithBlockFunc(func(info *crproxy.BlockInfo) (string, bool) {
if (*matcher.Load()).Match(info.Host + "/" + info.Name) {
return "", false
}
return blockMessage, true
}))
} else if len(blockImageList) != 0 || len(allowHostList) != 0 {
allowHostMap := map[string]struct{}{}
Expand All @@ -274,30 +277,26 @@ func main() {
for _, image := range blockImageList {
blockImageMap[image] = struct{}{}
}
opts = append(opts, crproxy.WithBlockFunc(func(info *crproxy.ImageInfo) bool {
opts = append(opts, crproxy.WithBlockFunc(func(info *crproxy.BlockInfo) (string, bool) {
if len(allowHostMap) != 0 {
_, ok := allowHostMap[info.Host]
if !ok {
return true
return blockMessage, true
}
}

if len(blockImageMap) != 0 {
image := info.Host + "/" + info.Name
_, ok := blockImageMap[image]
if ok {
return true
return blockMessage, true
}
}

return false
return "", false
}))
}

if blockMessage != "" {
opts = append(opts, crproxy.WithBlockMessage(blockMessage))
}

if len(privilegedIPList) != 0 || privilegedImageListFromFile != "" {
var matcher atomic.Pointer[hostmatcher.Matcher]
if privilegedImageListFromFile != "" {
Expand Down
49 changes: 33 additions & 16 deletions crproxy.go
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ignore this file when merging

Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ type ImageInfo struct {
Name string
}

type BlockInfo struct {
IP string
Host string
Name string
}

type CRProxy struct {
baseClient *http.Client
challengeManager challenge.Manager
Expand All @@ -59,8 +65,7 @@ type CRProxy struct {
blobsSpeedLimitDuration time.Duration
ipsSpeedLimit *geario.B
ipsSpeedLimitDuration time.Duration
blockFunc func(*ImageInfo) bool
blockMessage string
blockFunc []func(*BlockInfo) (string, bool)
retry int
retryInterval time.Duration
storageDriver storagedriver.StorageDriver
Expand Down Expand Up @@ -234,15 +239,9 @@ func WithDisableKeepAlives(disableKeepAlives []string) Option {
}
}

func WithBlockFunc(blockFunc func(info *ImageInfo) bool) Option {
return func(c *CRProxy) {
c.blockFunc = blockFunc
}
}

func WithBlockMessage(msg string) Option {
func WithBlockFunc(blockFunc func(info *BlockInfo) (string, bool)) Option {
return func(c *CRProxy) {
c.blockMessage = msg
c.blockFunc = append(c.blockFunc, blockFunc)
}
}

Expand Down Expand Up @@ -484,6 +483,16 @@ func getIP(str string) string {
return str
}

func (c *CRProxy) block(info *BlockInfo) (string, bool) {
for _, blockFunc := range c.blockFunc {
blockMessage, block := blockFunc(info)
if block {
return blockMessage, true
}
}
return "", false
}

func (c *CRProxy) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet && r.Method != http.MethodHead {
errcode.ServeJSON(rw, errcode.ErrorCodeUnsupported)
Expand Down Expand Up @@ -538,13 +547,21 @@ func (c *CRProxy) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
Name: info.Image,
}

if c.blockFunc != nil && !c.isPrivileged(r, nil) && c.blockFunc(imageInfo) {
if c.blockMessage != "" {
errcode.ServeJSON(rw, errcode.ErrorCodeDenied.WithMessage(c.blockMessage))
} else {
errcode.ServeJSON(rw, errcode.ErrorCodeDenied)
if c.blockFunc != nil && !c.isPrivileged(r, nil) {

blockMessage, block := c.block(&BlockInfo{
IP: r.RemoteAddr,
Host: info.Host,
Name: info.Image,
})
if block {
if blockMessage != "" {
errcode.ServeJSON(rw, errcode.ErrorCodeDenied.WithMessage(blockMessage))
} else {
errcode.ServeJSON(rw, errcode.ErrorCodeDenied)
}
return
}
return
}

info.Host = c.getDomainAlias(info.Host)
Expand Down
61 changes: 61 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

173 changes: 173 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
{
description = "CRProxy (Container Registry Proxy) is a generic image proxy";

inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
utils.url = "github:numtide/flake-utils";
};

outputs = { self, nixpkgs, utils }:
utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
inherit (pkgs.lib) licenses;
name = "crproxy";
version = "v0.13.0";
in
rec {
crproxy = pkgs.buildGoModule {
pname = name;
inherit version;
src = self;
doCheck = false;
proxyVendor = true;
vendorHash = "sha256-Oqaafxih9t9cggfa8PF5auMkrWq9oUDRxl8cdBLllhM=";

CGO_ENABLED = 0;

meta = {
description = "CRProxy (Container Registry Proxy) is a generic image proxy";
homepage = "https://github.com/DaoCloud/crproxy";
license = licenses.mit;
};
};

defaultPackage = crproxy;

devShell = with pkgs; mkShell {
buildInputs = [
go
];
};

nixosModule = { config, pkgs, lib, ... }:
let
cfg = config.service.crproxy;
allowImageListFile =
pkgs.writeTextFile {
name = "crproxy-allow-image-list";
text = lib.strings.concatLines cfg.allowImageList;
};
useAllowImageList = (lib.length cfg.allowImageList) != 0;
blockIPListFile = pkgs.writeTextFile {
name = "crproxy-block-ip-list";
text = lib.strings.concatLines cfg.blockIPList;
};
useBlockIPList = (lib.length cfg.blockIPList) != 0;
inherit (lib) mkIf mkEnableOption mkOption types concatStringsSep concatLists optionals literalExpression;
in
{
options = {
services.crproxy = {
enable = mkEnableOption "CRProxy (Container Registry Proxy) is a generic image proxy";

listenAddress = mkOption {
default = ":8080";
type = types.str;
description = "listen on the address (default \":8080\")";
};

behindProxy = mkOption {
default = false;
type = types.bool;
description = "Behind the reverse proxy";
};

userpass = mkOption {
default = [ ];
example = literalExpression ''
[ "user@pwd@host" ]
'';
type = types.listOf types.str;
description = "host and username and password -u user:pwd@host";
};

allowHostList = mkOption {
default = [ ];
type = types.listOf types.str;
description = "allow host list";
};

allowImageList = mkOption {
default = [ ];
type = types.listOf types.str;
description = "allow image list";
};

blockMessage = mkOption {
type = types.str;
default = "This image is not allowed for my proxy!";
description = "block message";
};

blockIPList = mkOption {
type = types.listOf types.str;
default = [ ];
description = "block ip list";
};

defaultRegistry = mkOption {
type = types.str;
default = "docker.io";
description = "default registry used for non full-path docker pull, like:docker.io";
};

simpleAuth = mkOption {
type = types.bool;
default = false;
description = "enable simple auth";
};

simpleAuthUser = mkOption {
type = types.listOf types.str;
default = [ ];
description = "simple auth user and password (default [])";
};

privilegedIPList = mkOption {
default = [ ];
type = types.listOf types.str;
description = "privileged IP list";
};

extraOptions = mkOption {
default = [ ];
type = types.listOf types.str;
description = ''
see https://github.com/DaoCloud/crproxy/blob/master/cmd/crproxy/main.go for more.
'';
};
};
};

config = mkIf cfg.enable {
systemd.services.crproxy = {
wantedBy = [
"network-online.target"
];
serviceConfig = {
RestartSec = 5;
ExecStart = concatStringsSep " " concatLists [
[
"${crproxy}/bin/crproxy"
"--default-registry=${cfg.defaultRegistry}"
"--address=${cfg.listenAddress}"
]
(optionals cfg.behindProxy [ "--behind" ])
(optionals useAllowImageList [ "--allow-image-list-from-file=${allowImageListFile}" ])
(optionals useAllowImageList [ "--block-message=${cfg.blockMessage}" ])
(optionals useBlockIPList [ "--block-ip-list-from-file=${blockIPListFile}" ])
(optionals cfg.simpleAuth [ "--simple-auth" ])
(map (e: "--simple-auth-user=${e}") cfg.simpleAuthUser)
(map (e: "--allow-host-list=${e}") cfg.allowHostList)
(map (e: "--privileged-ip-list=${e}") cfg.privilegedIPList)
(map (e: "--user=${e}") cfg.userpass)

cfg.extraOptions
];
};
};
};
};
});
}
Loading