local api unix socket support
This commit is contained in:
parent
f9ca14f010
commit
c9d383dbad
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -61,3 +61,5 @@ msi
|
||||||
__pycache__
|
__pycache__
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
*.egg-info
|
*.egg-info
|
||||||
|
|
||||||
|
.idea
|
|
@ -100,22 +100,7 @@ func runLapiRegister(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
password := strfmt.Password(generatePassword(passwordLength))
|
password := strfmt.Password(generatePassword(passwordLength))
|
||||||
if apiURL == "" {
|
apiurl, err := prepareApiURl(csConfig.API.Client, apiURL)
|
||||||
if csConfig.API.Client != nil && csConfig.API.Client.Credentials != nil && csConfig.API.Client.Credentials.URL != "" {
|
|
||||||
apiURL = csConfig.API.Client.Credentials.URL
|
|
||||||
} else {
|
|
||||||
log.Fatalf("No Local API URL. Please provide it in your configuration or with the -u parameter")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*URL needs to end with /, but user doesn't care*/
|
|
||||||
if !strings.HasSuffix(apiURL, "/") {
|
|
||||||
apiURL += "/"
|
|
||||||
}
|
|
||||||
/*URL needs to start with http://, but user doesn't care*/
|
|
||||||
if !strings.HasPrefix(apiURL, "http://") && !strings.HasPrefix(apiURL, "https://") {
|
|
||||||
apiURL = "http://" + apiURL
|
|
||||||
}
|
|
||||||
apiurl, err := url.Parse(apiURL)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("parsing api url: %s", err)
|
log.Fatalf("parsing api url: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -164,6 +149,25 @@ func runLapiRegister(cmd *cobra.Command, args []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func prepareApiURl(clientConfig *csconfig.LocalApiClientCfg, apiURL string) (*url.URL, error) {
|
||||||
|
if apiURL == "" {
|
||||||
|
if clientConfig != nil && clientConfig.Credentials != nil && clientConfig.Credentials.URL != "" {
|
||||||
|
apiURL = clientConfig.Credentials.URL
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("No Local API URL. Please provide it in your configuration or with the -u parameter")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasSuffix(apiURL, "/") {
|
||||||
|
apiURL += "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(apiURL, "http://") && !strings.HasPrefix(apiURL, "https://") && !strings.HasPrefix(apiURL, "/") {
|
||||||
|
apiURL = "http://" + apiURL
|
||||||
|
}
|
||||||
|
return url.Parse(apiURL)
|
||||||
|
}
|
||||||
|
|
||||||
func NewLapiStatusCmd() *cobra.Command {
|
func NewLapiStatusCmd() *cobra.Command {
|
||||||
cmdLapiStatus := &cobra.Command{
|
cmdLapiStatus := &cobra.Command{
|
||||||
Use: "status",
|
Use: "status",
|
||||||
|
|
58
cmd/crowdsec-cli/lapi_test.go
Normal file
58
cmd/crowdsec-cli/lapi_test.go
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPrepareApiURl_NoProtocol(t *testing.T) {
|
||||||
|
|
||||||
|
url, err := prepareApiURl(nil, "localhost:81")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "http://localhost:81/", url.String())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrepareApiURl_Http(t *testing.T) {
|
||||||
|
|
||||||
|
url, err := prepareApiURl(nil, "http://localhost:81")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "http://localhost:81/", url.String())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrepareApiURl_Https(t *testing.T) {
|
||||||
|
|
||||||
|
url, err := prepareApiURl(nil, "https://localhost:81")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "https://localhost:81/", url.String())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrepareApiURl_UnixSocket(t *testing.T) {
|
||||||
|
|
||||||
|
url, err := prepareApiURl(nil, "/path/socket")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "/path/socket/", url.String())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrepareApiURl_Empty(t *testing.T) {
|
||||||
|
|
||||||
|
_, err := prepareApiURl(nil, "")
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrepareApiURl_Empty_ConfigOverride(t *testing.T) {
|
||||||
|
|
||||||
|
url, err := prepareApiURl(&csconfig.LocalApiClientCfg{
|
||||||
|
Credentials: &csconfig.ApiCredentialsCfg{
|
||||||
|
URL: "localhost:80",
|
||||||
|
},
|
||||||
|
}, "")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "http://localhost:80/", url.String())
|
||||||
|
|
||||||
|
}
|
|
@ -288,7 +288,7 @@ func runMachinesAdd(cmd *cobra.Command, args []string) error {
|
||||||
if csConfig.API.Client != nil && csConfig.API.Client.Credentials != nil && csConfig.API.Client.Credentials.URL != "" {
|
if csConfig.API.Client != nil && csConfig.API.Client.Credentials != nil && csConfig.API.Client.Credentials.URL != "" {
|
||||||
apiURL = csConfig.API.Client.Credentials.URL
|
apiURL = csConfig.API.Client.Credentials.URL
|
||||||
} else if csConfig.API.Server != nil && csConfig.API.Server.ListenURI != "" {
|
} else if csConfig.API.Server != nil && csConfig.API.Server.ListenURI != "" {
|
||||||
apiURL = "http://" + csConfig.API.Server.ListenURI
|
apiURL = csConfig.API.Server.ClientUrl()
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("unable to dump an api URL. Please provide it in your configuration or with the -u parameter")
|
return fmt.Errorf("unable to dump an api URL. Please provide it in your configuration or with the -u parameter")
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,9 +174,14 @@ func (t *JWTTransport) refreshJwtToken() error {
|
||||||
return fmt.Errorf("could not create request: %w", err)
|
return fmt.Errorf("could not create request: %w", err)
|
||||||
}
|
}
|
||||||
req.Header.Add("Content-Type", "application/json")
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
|
||||||
|
transport := t.Transport
|
||||||
|
if transport == nil {
|
||||||
|
transport = http.DefaultTransport
|
||||||
|
}
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Transport: &retryRoundTripper{
|
Transport: &retryRoundTripper{
|
||||||
next: http.DefaultTransport,
|
next: transport,
|
||||||
maxAttempts: 5,
|
maxAttempts: 5,
|
||||||
withBackOff: true,
|
withBackOff: true,
|
||||||
retryStatusCodes: []int{http.StatusTooManyRequests, http.StatusServiceUnavailable, http.StatusGatewayTimeout, http.StatusInternalServerError},
|
retryStatusCodes: []int{http.StatusTooManyRequests, http.StatusServiceUnavailable, http.StatusGatewayTimeout, http.StatusInternalServerError},
|
||||||
|
|
|
@ -6,11 +6,12 @@ import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/models"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
"github.com/crowdsecurity/crowdsec/pkg/models"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -52,11 +53,16 @@ func NewClient(config *Config) (*ApiClient, error) {
|
||||||
MachineID: &config.MachineID,
|
MachineID: &config.MachineID,
|
||||||
Password: &config.Password,
|
Password: &config.Password,
|
||||||
Scenarios: config.Scenarios,
|
Scenarios: config.Scenarios,
|
||||||
URL: config.URL,
|
|
||||||
UserAgent: config.UserAgent,
|
UserAgent: config.UserAgent,
|
||||||
VersionPrefix: config.VersionPrefix,
|
VersionPrefix: config.VersionPrefix,
|
||||||
UpdateScenario: config.UpdateScenario,
|
UpdateScenario: config.UpdateScenario,
|
||||||
}
|
}
|
||||||
|
transport, baseUrl := CreateTransport(config.URL)
|
||||||
|
if transport != nil {
|
||||||
|
t.Transport = transport
|
||||||
|
}
|
||||||
|
t.URL = baseUrl
|
||||||
|
|
||||||
tlsconfig := tls.Config{InsecureSkipVerify: InsecureSkipVerify}
|
tlsconfig := tls.Config{InsecureSkipVerify: InsecureSkipVerify}
|
||||||
tlsconfig.RootCAs = CaCertPool
|
tlsconfig.RootCAs = CaCertPool
|
||||||
if Cert != nil {
|
if Cert != nil {
|
||||||
|
@ -65,7 +71,7 @@ func NewClient(config *Config) (*ApiClient, error) {
|
||||||
if ht, ok := http.DefaultTransport.(*http.Transport); ok {
|
if ht, ok := http.DefaultTransport.(*http.Transport); ok {
|
||||||
ht.TLSClientConfig = &tlsconfig
|
ht.TLSClientConfig = &tlsconfig
|
||||||
}
|
}
|
||||||
c := &ApiClient{client: t.Client(), BaseURL: config.URL, UserAgent: config.UserAgent, URLPrefix: config.VersionPrefix, PapiURL: config.PapiURL}
|
c := &ApiClient{client: t.Client(), BaseURL: baseUrl, UserAgent: config.UserAgent, URLPrefix: config.VersionPrefix, PapiURL: config.PapiURL}
|
||||||
c.common.client = c
|
c.common.client = c
|
||||||
c.Decisions = (*DecisionsService)(&c.common)
|
c.Decisions = (*DecisionsService)(&c.common)
|
||||||
c.Alerts = (*AlertsService)(&c.common)
|
c.Alerts = (*AlertsService)(&c.common)
|
||||||
|
@ -79,19 +85,24 @@ func NewClient(config *Config) (*ApiClient, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultClient(URL *url.URL, prefix string, userAgent string, client *http.Client) (*ApiClient, error) {
|
func NewDefaultClient(URL *url.URL, prefix string, userAgent string, client *http.Client) (*ApiClient, error) {
|
||||||
|
transport, baseUrl := CreateTransport(URL)
|
||||||
if client == nil {
|
if client == nil {
|
||||||
client = &http.Client{}
|
client = &http.Client{}
|
||||||
if ht, ok := http.DefaultTransport.(*http.Transport); ok {
|
if transport != nil {
|
||||||
tlsconfig := tls.Config{InsecureSkipVerify: InsecureSkipVerify}
|
client.Transport = transport
|
||||||
tlsconfig.RootCAs = CaCertPool
|
} else {
|
||||||
if Cert != nil {
|
if ht, ok := http.DefaultTransport.(*http.Transport); ok {
|
||||||
tlsconfig.Certificates = []tls.Certificate{*Cert}
|
tlsconfig := tls.Config{InsecureSkipVerify: InsecureSkipVerify}
|
||||||
|
tlsconfig.RootCAs = CaCertPool
|
||||||
|
if Cert != nil {
|
||||||
|
tlsconfig.Certificates = []tls.Certificate{*Cert}
|
||||||
|
}
|
||||||
|
ht.TLSClientConfig = &tlsconfig
|
||||||
|
client.Transport = ht
|
||||||
}
|
}
|
||||||
ht.TLSClientConfig = &tlsconfig
|
|
||||||
client.Transport = ht
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c := &ApiClient{client: client, BaseURL: URL, UserAgent: userAgent, URLPrefix: prefix}
|
c := &ApiClient{client: client, BaseURL: baseUrl, UserAgent: userAgent, URLPrefix: prefix}
|
||||||
c.common.client = c
|
c.common.client = c
|
||||||
c.Decisions = (*DecisionsService)(&c.common)
|
c.Decisions = (*DecisionsService)(&c.common)
|
||||||
c.Alerts = (*AlertsService)(&c.common)
|
c.Alerts = (*AlertsService)(&c.common)
|
||||||
|
@ -105,16 +116,24 @@ func NewDefaultClient(URL *url.URL, prefix string, userAgent string, client *htt
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterClient(config *Config, client *http.Client) (*ApiClient, error) {
|
func RegisterClient(config *Config, client *http.Client) (*ApiClient, error) {
|
||||||
|
transport, baseUrl := CreateTransport(config.URL)
|
||||||
if client == nil {
|
if client == nil {
|
||||||
client = &http.Client{}
|
client = &http.Client{}
|
||||||
|
if transport != nil {
|
||||||
|
client.Transport = transport
|
||||||
|
} else {
|
||||||
|
tlsconfig := tls.Config{InsecureSkipVerify: InsecureSkipVerify}
|
||||||
|
if Cert != nil {
|
||||||
|
tlsconfig.RootCAs = CaCertPool
|
||||||
|
tlsconfig.Certificates = []tls.Certificate{*Cert}
|
||||||
|
}
|
||||||
|
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tlsconfig
|
||||||
|
}
|
||||||
|
} else if client.Transport == nil && transport != nil {
|
||||||
|
client.Transport = transport
|
||||||
}
|
}
|
||||||
tlsconfig := tls.Config{InsecureSkipVerify: InsecureSkipVerify}
|
|
||||||
if Cert != nil {
|
c := &ApiClient{client: client, BaseURL: baseUrl, UserAgent: config.UserAgent, URLPrefix: config.VersionPrefix}
|
||||||
tlsconfig.RootCAs = CaCertPool
|
|
||||||
tlsconfig.Certificates = []tls.Certificate{*Cert}
|
|
||||||
}
|
|
||||||
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tlsconfig
|
|
||||||
c := &ApiClient{client: client, BaseURL: config.URL, UserAgent: config.UserAgent, URLPrefix: config.VersionPrefix}
|
|
||||||
c.common.client = c
|
c.common.client = c
|
||||||
c.Decisions = (*DecisionsService)(&c.common)
|
c.Decisions = (*DecisionsService)(&c.common)
|
||||||
c.Alerts = (*AlertsService)(&c.common)
|
c.Alerts = (*AlertsService)(&c.common)
|
||||||
|
@ -132,6 +151,26 @@ func RegisterClient(config *Config, client *http.Client) (*ApiClient, error) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CreateTransport(url *url.URL) (*http.Transport, *url.URL) {
|
||||||
|
urlString := url.String()
|
||||||
|
if strings.HasPrefix(urlString, "/") {
|
||||||
|
ToUnixSocketUrl(url)
|
||||||
|
return &http.Transport{
|
||||||
|
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
|
||||||
|
return net.Dial("unix", strings.TrimSuffix(urlString, "/"))
|
||||||
|
},
|
||||||
|
}, url
|
||||||
|
} else {
|
||||||
|
return nil, url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToUnixSocketUrl(url *url.URL) {
|
||||||
|
url.Path = "/"
|
||||||
|
url.Host = "unix"
|
||||||
|
url.Scheme = "http"
|
||||||
|
}
|
||||||
|
|
||||||
type Response struct {
|
type Response struct {
|
||||||
Response *http.Response
|
Response *http.Response
|
||||||
//add our pagination stuff
|
//add our pagination stuff
|
||||||
|
|
|
@ -3,9 +3,11 @@ package apiclient
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"path"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -32,12 +34,26 @@ func setupWithPrefix(urlPrefix string) (mux *http.ServeMux, serverURL string, te
|
||||||
apiHandler := http.NewServeMux()
|
apiHandler := http.NewServeMux()
|
||||||
apiHandler.Handle(baseURLPath+"/", http.StripPrefix(baseURLPath, mux))
|
apiHandler.Handle(baseURLPath+"/", http.StripPrefix(baseURLPath, mux))
|
||||||
|
|
||||||
// server is a test HTTP server used to provide mock API responses.
|
|
||||||
server := httptest.NewServer(apiHandler)
|
server := httptest.NewServer(apiHandler)
|
||||||
|
|
||||||
return mux, server.URL, server.Close
|
return mux, server.URL, server.Close
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupUnixSocketWithPrefix(socket string, urlPrefix string) (mux *http.ServeMux, serverURL string, teardown func()) {
|
||||||
|
mux = http.NewServeMux()
|
||||||
|
baseURLPath := "/" + urlPrefix
|
||||||
|
|
||||||
|
apiHandler := http.NewServeMux()
|
||||||
|
apiHandler.Handle(baseURLPath+"/", http.StripPrefix(baseURLPath, mux))
|
||||||
|
|
||||||
|
server := httptest.NewUnstartedServer(apiHandler)
|
||||||
|
l, _ := net.Listen("unix", socket)
|
||||||
|
_ = server.Listener.Close()
|
||||||
|
server.Listener = l
|
||||||
|
server.Start()
|
||||||
|
return mux, socket, server.Close
|
||||||
|
}
|
||||||
|
|
||||||
func testMethod(t *testing.T, r *http.Request, want string) {
|
func testMethod(t *testing.T, r *http.Request, want string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if got := r.Method; got != want {
|
if got := r.Method; got != want {
|
||||||
|
@ -82,6 +98,49 @@ func TestNewClientOk(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewClientOk_UnixSocket(t *testing.T) {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
t.Skip("skipping on windows")
|
||||||
|
}
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
socket := path.Join(tmpDir, "socket")
|
||||||
|
|
||||||
|
mux, urlx, teardown := setupUnixSocketWithPrefix(socket, "v1")
|
||||||
|
defer teardown()
|
||||||
|
apiURL, err := url.Parse(urlx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("parsing api url: %s", apiURL)
|
||||||
|
}
|
||||||
|
client, err := NewClient(&Config{
|
||||||
|
MachineID: "test_login",
|
||||||
|
Password: "test_password",
|
||||||
|
UserAgent: fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
|
||||||
|
URL: apiURL,
|
||||||
|
VersionPrefix: "v1",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("new api client: %s", err)
|
||||||
|
}
|
||||||
|
/*mock login*/
|
||||||
|
mux.HandleFunc("/watchers/login", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write([]byte(`{"code": 200, "expire": "2030-01-02T15:04:05Z", "token": "oklol"}`))
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.HandleFunc("/alerts", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
testMethod(t, r, "GET")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
})
|
||||||
|
|
||||||
|
_, resp, err := client.Alerts.List(context.Background(), AlertsListOpts{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("test Unable to list alerts : %+v", err)
|
||||||
|
}
|
||||||
|
if resp.Response.StatusCode != http.StatusOK {
|
||||||
|
t.Fatalf("Alerts.List returned status: %d, want %d", resp.Response.StatusCode, http.StatusCreated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewClientKo(t *testing.T) {
|
func TestNewClientKo(t *testing.T) {
|
||||||
mux, urlx, teardown := setup()
|
mux, urlx, teardown := setup()
|
||||||
defer teardown()
|
defer teardown()
|
||||||
|
@ -135,6 +194,31 @@ func TestNewDefaultClient(t *testing.T) {
|
||||||
log.Printf("err-> %s", err)
|
log.Printf("err-> %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewDefaultClient_UnixSocket(t *testing.T) {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
t.Skip("skipping on windows")
|
||||||
|
}
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
socket := path.Join(tmpDir, "socket")
|
||||||
|
mux, urlx, teardown := setupUnixSocketWithPrefix(socket, "v1")
|
||||||
|
defer teardown()
|
||||||
|
apiURL, err := url.Parse(urlx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("parsing api url: %s", apiURL)
|
||||||
|
}
|
||||||
|
client, err := NewDefaultClient(apiURL, "/v1", "", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("new api client: %s", err)
|
||||||
|
}
|
||||||
|
mux.HandleFunc("/alerts", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
w.Write([]byte(`{"code": 401, "message" : "brr"}`))
|
||||||
|
})
|
||||||
|
_, _, err = client.Alerts.List(context.Background(), AlertsListOpts{})
|
||||||
|
assert.Contains(t, err.Error(), `performing request: API error: brr`)
|
||||||
|
log.Printf("err-> %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewClientRegisterKO(t *testing.T) {
|
func TestNewClientRegisterKO(t *testing.T) {
|
||||||
apiURL, err := url.Parse("http://127.0.0.1:4242/")
|
apiURL, err := url.Parse("http://127.0.0.1:4242/")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -183,6 +267,40 @@ func TestNewClientRegisterOK(t *testing.T) {
|
||||||
log.Printf("->%T", client)
|
log.Printf("->%T", client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewClientRegisterOK_UnixSocket(t *testing.T) {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
t.Skip("skipping on windows")
|
||||||
|
}
|
||||||
|
log.SetLevel(log.TraceLevel)
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
socket := path.Join(tmpDir, "socket")
|
||||||
|
mux, urlx, teardown := setupUnixSocketWithPrefix(socket, "v1")
|
||||||
|
defer teardown()
|
||||||
|
|
||||||
|
/*mock login*/
|
||||||
|
mux.HandleFunc("/watchers", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
testMethod(t, r, "POST")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write([]byte(`{"code": 200, "expire": "2030-01-02T15:04:05Z", "token": "oklol"}`))
|
||||||
|
})
|
||||||
|
|
||||||
|
apiURL, err := url.Parse(urlx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("parsing api url: %s", apiURL)
|
||||||
|
}
|
||||||
|
client, err := RegisterClient(&Config{
|
||||||
|
MachineID: "test_login",
|
||||||
|
Password: "test_password",
|
||||||
|
UserAgent: fmt.Sprintf("crowdsec/%s", cwversion.VersionStr()),
|
||||||
|
URL: apiURL,
|
||||||
|
VersionPrefix: "v1",
|
||||||
|
}, &http.Client{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("while registering client : %s", err)
|
||||||
|
}
|
||||||
|
log.Printf("->%T", client)
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewClientBadAnswer(t *testing.T) {
|
func TestNewClientBadAnswer(t *testing.T) {
|
||||||
log.SetLevel(log.TraceLevel)
|
log.SetLevel(log.TraceLevel)
|
||||||
mux, urlx, teardown := setup()
|
mux, urlx, teardown := setup()
|
||||||
|
|
|
@ -39,6 +39,7 @@ var (
|
||||||
|
|
||||||
type APIServer struct {
|
type APIServer struct {
|
||||||
URL string
|
URL string
|
||||||
|
isUnixSocket bool
|
||||||
TLS *csconfig.TLSCfg
|
TLS *csconfig.TLSCfg
|
||||||
dbClient *database.Client
|
dbClient *database.Client
|
||||||
logFile string
|
logFile string
|
||||||
|
@ -246,6 +247,7 @@ func NewServer(config *csconfig.LocalApiServerCfg) (*APIServer, error) {
|
||||||
|
|
||||||
return &APIServer{
|
return &APIServer{
|
||||||
URL: config.ListenURI,
|
URL: config.ListenURI,
|
||||||
|
isUnixSocket: config.IsUnixSocket(),
|
||||||
TLS: config.TLS,
|
TLS: config.TLS,
|
||||||
logFile: logFile,
|
logFile: logFile,
|
||||||
dbClient: dbClient,
|
dbClient: dbClient,
|
||||||
|
@ -395,19 +397,30 @@ func (s *APIServer) Run(apiReady chan bool) error {
|
||||||
go func() {
|
go func() {
|
||||||
apiReady <- true
|
apiReady <- true
|
||||||
log.Infof("CrowdSec Local API listening on %s", s.URL)
|
log.Infof("CrowdSec Local API listening on %s", s.URL)
|
||||||
if s.TLS != nil && (s.TLS.CertFilePath != "" || s.TLS.KeyFilePath != "") {
|
if s.isUnixSocket {
|
||||||
if s.TLS.KeyFilePath == "" {
|
_ = os.RemoveAll(s.URL)
|
||||||
log.Fatalf("while serving local API: %v", errors.New("missing TLS key file"))
|
listener, err := net.Listen("unix", s.URL)
|
||||||
} else if s.TLS.CertFilePath == "" {
|
if err != nil {
|
||||||
log.Fatalf("while serving local API: %v", errors.New("missing TLS cert file"))
|
log.Fatalf("while creating unix listener: %v", err)
|
||||||
}
|
}
|
||||||
|
if err = s.httpServer.Serve(listener); err != http.ErrServerClosed {
|
||||||
if err := s.httpServer.ListenAndServeTLS(s.TLS.CertFilePath, s.TLS.KeyFilePath); err != nil {
|
log.Fatalf("while serving local API (unix socket): %v", err)
|
||||||
log.Fatalf("while serving local API: %v", err)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := s.httpServer.ListenAndServe(); err != http.ErrServerClosed {
|
if s.TLS != nil && (s.TLS.CertFilePath != "" || s.TLS.KeyFilePath != "") {
|
||||||
log.Fatalf("while serving local API: %v", err)
|
if s.TLS.KeyFilePath == "" {
|
||||||
|
log.Fatalf("while serving local API: %v", errors.New("missing TLS key file"))
|
||||||
|
} else if s.TLS.CertFilePath == "" {
|
||||||
|
log.Fatalf("while serving local API: %v", errors.New("missing TLS cert file"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.httpServer.ListenAndServeTLS(s.TLS.CertFilePath, s.TLS.KeyFilePath); err != nil {
|
||||||
|
log.Fatalf("while serving local API (tcp tls): %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err = s.httpServer.ListenAndServe(); err != http.ErrServerClosed {
|
||||||
|
log.Fatalf("while serving local API (tcp): %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
|
@ -110,10 +110,8 @@ func (l *LocalApiClientCfg) Load() error {
|
||||||
return fmt.Errorf("no credentials or URL found in api client configuration '%s'", l.CredentialsFilePath)
|
return fmt.Errorf("no credentials or URL found in api client configuration '%s'", l.CredentialsFilePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.Credentials != nil && l.Credentials.URL != "" {
|
if !strings.HasSuffix(l.Credentials.URL, "/") {
|
||||||
if !strings.HasSuffix(l.Credentials.URL, "/") {
|
l.Credentials.URL += "/"
|
||||||
l.Credentials.URL += "/"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.Credentials.Login != "" && (l.Credentials.CertPath != "" || l.Credentials.KeyPath != "") {
|
if l.Credentials.Login != "" && (l.Credentials.CertPath != "" || l.Credentials.KeyPath != "") {
|
||||||
|
@ -155,9 +153,9 @@ func (l *LocalApiClientCfg) Load() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lapiCfg *LocalApiServerCfg) GetTrustedIPs() ([]net.IPNet, error) {
|
func (c *LocalApiServerCfg) GetTrustedIPs() ([]net.IPNet, error) {
|
||||||
trustedIPs := make([]net.IPNet, 0)
|
trustedIPs := make([]net.IPNet, 0)
|
||||||
for _, ip := range lapiCfg.TrustedIPs {
|
for _, ip := range c.TrustedIPs {
|
||||||
cidr := toValidCIDR(ip)
|
cidr := toValidCIDR(ip)
|
||||||
_, ipNet, err := net.ParseCIDR(cidr)
|
_, ipNet, err := net.ParseCIDR(cidr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -211,6 +209,16 @@ type LocalApiServerCfg struct {
|
||||||
CapiWhitelists *CapiWhitelist `yaml:"-"`
|
CapiWhitelists *CapiWhitelist `yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *LocalApiServerCfg) IsUnixSocket() bool {
|
||||||
|
return strings.HasPrefix(c.ListenURI, "/")
|
||||||
|
}
|
||||||
|
func (c *LocalApiServerCfg) ClientUrl() string {
|
||||||
|
if c.IsUnixSocket() {
|
||||||
|
return c.ListenURI
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("http://%s", c.ListenURI)
|
||||||
|
}
|
||||||
|
|
||||||
type TLSCfg struct {
|
type TLSCfg struct {
|
||||||
CertFilePath string `yaml:"cert_file"`
|
CertFilePath string `yaml:"cert_file"`
|
||||||
KeyFilePath string `yaml:"key_file"`
|
KeyFilePath string `yaml:"key_file"`
|
||||||
|
@ -332,39 +340,39 @@ type capiWhitelists struct {
|
||||||
Cidrs []string `yaml:"cidrs"`
|
Cidrs []string `yaml:"cidrs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LocalApiServerCfg) LoadCapiWhitelists() error {
|
func (c *LocalApiServerCfg) LoadCapiWhitelists() error {
|
||||||
if s.CapiWhitelistsPath == "" {
|
if c.CapiWhitelistsPath == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if _, err := os.Stat(s.CapiWhitelistsPath); os.IsNotExist(err) {
|
if _, err := os.Stat(c.CapiWhitelistsPath); os.IsNotExist(err) {
|
||||||
return fmt.Errorf("capi whitelist file '%s' does not exist", s.CapiWhitelistsPath)
|
return fmt.Errorf("capi whitelist file '%s' does not exist", c.CapiWhitelistsPath)
|
||||||
}
|
}
|
||||||
fd, err := os.Open(s.CapiWhitelistsPath)
|
fd, err := os.Open(c.CapiWhitelistsPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to open capi whitelist file '%s': %s", s.CapiWhitelistsPath, err)
|
return fmt.Errorf("unable to open capi whitelist file '%s': %s", c.CapiWhitelistsPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var fromCfg capiWhitelists
|
var fromCfg capiWhitelists
|
||||||
s.CapiWhitelists = &CapiWhitelist{}
|
c.CapiWhitelists = &CapiWhitelist{}
|
||||||
|
|
||||||
defer fd.Close()
|
defer fd.Close()
|
||||||
decoder := yaml.NewDecoder(fd)
|
decoder := yaml.NewDecoder(fd)
|
||||||
if err := decoder.Decode(&fromCfg); err != nil {
|
if err := decoder.Decode(&fromCfg); err != nil {
|
||||||
return fmt.Errorf("while parsing capi whitelist file '%s': %s", s.CapiWhitelistsPath, err)
|
return fmt.Errorf("while parsing capi whitelist file '%s': %s", c.CapiWhitelistsPath, err)
|
||||||
}
|
}
|
||||||
for _, v := range fromCfg.Ips {
|
for _, v := range fromCfg.Ips {
|
||||||
ip := net.ParseIP(v)
|
ip := net.ParseIP(v)
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
return fmt.Errorf("unable to parse ip whitelist '%s'", v)
|
return fmt.Errorf("unable to parse ip whitelist '%s'", v)
|
||||||
}
|
}
|
||||||
s.CapiWhitelists.Ips = append(s.CapiWhitelists.Ips, ip)
|
c.CapiWhitelists.Ips = append(c.CapiWhitelists.Ips, ip)
|
||||||
}
|
}
|
||||||
for _, v := range fromCfg.Cidrs {
|
for _, v := range fromCfg.Cidrs {
|
||||||
_, tnet, err := net.ParseCIDR(v)
|
_, tnet, err := net.ParseCIDR(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to parse cidr whitelist '%s' : %v", v, err)
|
return fmt.Errorf("unable to parse cidr whitelist '%s' : %v", v, err)
|
||||||
}
|
}
|
||||||
s.CapiWhitelists.Cidrs = append(s.CapiWhitelists.Cidrs, tnet)
|
c.CapiWhitelists.Cidrs = append(c.CapiWhitelists.Cidrs, tnet)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue