Add use_forwarded_for_headers configuration option for LAPI (#610)

* Add use_forwarded_for_headers configuration option for LAPI

* update documentation
This commit is contained in:
blotus 2021-02-09 19:10:14 +01:00 committed by GitHub
parent 9f515cb7ef
commit 260332c726
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 179 additions and 13 deletions

View file

@ -50,6 +50,7 @@ api:
log_level: info
listen_uri: 127.0.0.1:8080
profiles_path: /etc/crowdsec/profiles.yaml
use_forwarded_for_headers: false
online_client: # Crowdsec API
credentials_path: /etc/crowdsec/online_api_credentials.yaml
# tls:
@ -132,6 +133,7 @@ api:
log_level: (error|info|debug|trace>)
listen_uri: <listen_uri> # host:port
profiles_path: <path_to_profile_file>
use_forwarded_for_headers: <true|false>
online_client:
credentials_path: <path_to_crowdsec_api_client_credential_file>
tls:
@ -304,6 +306,7 @@ api:
log_level: (error|info|debug|trace>)
listen_uri: <listen_uri> # host:port
profiles_path: <path_to_profile_file>
use_forwarded_for_headers: (true|false)
online_client:
credentials_path: <path_to_crowdsec_api_client_credential_file>
tls:
@ -340,6 +343,7 @@ server:
log_level: (error|info|debug|trace)
listen_uri: <listen_uri> # host:port
profiles_path: <path_to_profile_file>
use_forwarded_for_headers: (true|false)
online_client:
credentials_path: <path_to_crowdsec_api_client_credential_file>
tls:
@ -357,6 +361,11 @@ Address and port listen configuration, the form `host:port`.
The path to the {{v1X.profiles.htmlname}} configuration.
#### `use_forwarded_for_headers`
> string
Allow the usage of `X-Forwarded-For` or `X-Real-IP` to get the client IP address. Do not enable if you are not running the LAPI behind a trusted reverse-proxy or LB.
#### `online_client`
Configuration to push signals and receive bad IPs from Crowdsec API.

View file

@ -61,10 +61,13 @@ func NewServer(config *csconfig.LocalApiServerCfg) (*APIServer, error) {
}
log.Debugf("starting router, logging to %s", logFile)
router := gin.New()
/*related to https://github.com/gin-gonic/gin/pull/2474
Gin team doesn't seem to be willing to have a opt-in/opt-out on the trusted proxies.
For now, let's not trust that. */
router.ForwardedByClientIP = false
/* See https://github.com/gin-gonic/gin/pull/2474:
Gin does not handle safely X-Forwarded-For or X-Real-IP.
We do not trust them by default, but the user can opt-in
if they host LAPI behind a trusted proxy which sanitize
X-Forwarded-For and X-Real-IP.
*/
router.ForwardedByClientIP = config.UseForwardedForHeaders
/*The logger that will be used by handlers*/
clog := log.New()

View file

@ -57,6 +57,33 @@ func LoadTestConfig() csconfig.GlobalConfig {
return config
}
func LoadTestConfigForwardedFor() csconfig.GlobalConfig {
config := csconfig.GlobalConfig{}
maxAge := "1h"
flushConfig := csconfig.FlushDBCfg{
MaxAge: &maxAge,
}
dbconfig := csconfig.DatabaseCfg{
Type: "sqlite",
DbPath: "./ent",
Flush: &flushConfig,
}
apiServerConfig := csconfig.LocalApiServerCfg{
ListenURI: "http://127.0.0.1:8080",
DbConfig: &dbconfig,
ProfilesPath: "./tests/profiles.yaml",
UseForwardedForHeaders: true,
}
apiConfig := csconfig.APICfg{
Server: &apiServerConfig,
}
config.API = &apiConfig
if err := config.API.Server.LoadProfiles(); err != nil {
log.Fatalf("failed to load profiles: %s", err)
}
return config
}
func NewAPITest() (*gin.Engine, error) {
config := LoadTestConfig()
@ -74,6 +101,23 @@ func NewAPITest() (*gin.Engine, error) {
return router, nil
}
func NewAPITestForwardedFor() (*gin.Engine, error) {
config := LoadTestConfigForwardedFor()
os.Remove("./ent")
apiServer, err := NewServer(config.API.Server)
if err != nil {
return nil, fmt.Errorf("unable to run local API: %s", err)
}
log.Printf("Creating new API server")
gin.SetMode(gin.TestMode)
router, err := apiServer.Router()
if err != nil {
return nil, fmt.Errorf("unable to run local API: %s", err)
}
return router, nil
}
func ValidateMachine(machineID string) error {
config := LoadTestConfig()
dbClient, err := database.NewClient(config.API.Server.DbConfig)
@ -86,6 +130,24 @@ func ValidateMachine(machineID string) error {
return nil
}
func GetMachineIP(machineID string) (string, error) {
config := LoadTestConfig()
dbClient, err := database.NewClient(config.API.Server.DbConfig)
if err != nil {
return "", fmt.Errorf("unable to create new database client: %s", err)
}
machines, err := dbClient.ListMachines()
if err != nil {
return "", fmt.Errorf("Unable to list machines: %s", err)
}
for _, machine := range machines {
if machine.MachineId == machineID {
return machine.IpAddress, nil
}
}
return "", nil
}
func CreateTestMachine(router *gin.Engine) (string, error) {
b, err := json.Marshal(MachineTest)
if err != nil {

View file

@ -52,6 +52,96 @@ func TestCreateMachine(t *testing.T) {
}
func TestCreateMachineWithForwardedFor(t *testing.T) {
router, err := NewAPITestForwardedFor()
if err != nil {
log.Fatalf("unable to run local API: %s", err)
}
// Create machine
b, err := json.Marshal(MachineTest)
if err != nil {
log.Fatalf("unable to marshal MachineTest")
}
body := string(b)
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/v1/watchers", strings.NewReader(body))
req.Header.Add("User-Agent", UserAgent)
req.Header.Add("X-Real-IP", "1.1.1.1")
router.ServeHTTP(w, req)
assert.Equal(t, 201, w.Code)
assert.Equal(t, "", w.Body.String())
ip, err := GetMachineIP(*MachineTest.MachineID)
if err != nil {
log.Fatalf("Could not get machine IP : %s", err)
}
assert.Equal(t, "1.1.1.1", ip)
}
func TestCreateMachineWithForwardedForNoConfig(t *testing.T) {
router, err := NewAPITest()
if err != nil {
log.Fatalf("unable to run local API: %s", err)
}
// Create machine
b, err := json.Marshal(MachineTest)
if err != nil {
log.Fatalf("unable to marshal MachineTest")
}
body := string(b)
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/v1/watchers", strings.NewReader(body))
req.Header.Add("User-Agent", UserAgent)
req.Header.Add("X-Real-IP", "1.1.1.1")
router.ServeHTTP(w, req)
assert.Equal(t, 201, w.Code)
assert.Equal(t, "", w.Body.String())
ip, err := GetMachineIP(*MachineTest.MachineID)
if err != nil {
log.Fatalf("Could not get machine IP : %s", err)
}
//For some reason, the IP is empty when running tests
//if no forwarded-for headers are present
assert.Equal(t, "", ip)
}
func TestCreateMachineWithoutForwardedFor(t *testing.T) {
router, err := NewAPITestForwardedFor()
if err != nil {
log.Fatalf("unable to run local API: %s", err)
}
// Create machine
b, err := json.Marshal(MachineTest)
if err != nil {
log.Fatalf("unable to marshal MachineTest")
}
body := string(b)
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/v1/watchers", strings.NewReader(body))
req.Header.Add("User-Agent", UserAgent)
router.ServeHTTP(w, req)
assert.Equal(t, 201, w.Code)
assert.Equal(t, "", w.Body.String())
ip, err := GetMachineIP(*MachineTest.MachineID)
if err != nil {
log.Fatalf("Could not get machine IP : %s", err)
}
//For some reason, the IP is empty when running tests
//if no forwarded-for headers are present
assert.Equal(t, "", ip)
}
func TestCreateMachineAlreadyExist(t *testing.T) {
router, err := NewAPITest()
if err != nil {

View file

@ -28,14 +28,15 @@ type LocalApiClientCfg struct {
/*local api service configuration*/
type LocalApiServerCfg struct {
ListenURI string `yaml:"listen_uri,omitempty"` //127.0.0.1:8080
TLS *TLSCfg `yaml:"tls"`
DbConfig *DatabaseCfg `yaml:"-"`
LogDir string `yaml:"-"`
OnlineClient *OnlineApiClientCfg `yaml:"online_client"`
ProfilesPath string `yaml:"profiles_path,omitempty"`
Profiles []*ProfileCfg `yaml:"-"`
LogLevel *log.Level `yaml:"log_level"`
ListenURI string `yaml:"listen_uri,omitempty"` //127.0.0.1:8080
TLS *TLSCfg `yaml:"tls"`
DbConfig *DatabaseCfg `yaml:"-"`
LogDir string `yaml:"-"`
OnlineClient *OnlineApiClientCfg `yaml:"online_client"`
ProfilesPath string `yaml:"profiles_path,omitempty"`
Profiles []*ProfileCfg `yaml:"-"`
LogLevel *log.Level `yaml:"log_level"`
UseForwardedForHeaders bool `yaml:"use_forwarded_for_headers,omitempty"`
}
type TLSCfg struct {

View file

@ -232,7 +232,8 @@ func NewDefaultConfig() *GlobalConfig {
CredentialsFilePath: "/etc/crowdsec/config/lapi-secrets.yaml",
},
Server: &LocalApiServerCfg{
ListenURI: "127.0.0.1:8080",
ListenURI: "127.0.0.1:8080",
UseForwardedForHeaders: false,
OnlineClient: &OnlineApiClientCfg{
CredentialsFilePath: "/etc/crowdsec/config/online-api-secrets.yaml",
},