github.com/crowdsecurity/crowdsec@v1.6.1/docker/test/tests/test_tls.py (about) 1 #!/usr/bin/env python 2 3 """ 4 Test agent-lapi and cscli-lapi communication via TLS, on the same container. 5 """ 6 7 import uuid 8 9 from pytest_cs import Status 10 11 import pytest 12 13 pytestmark = pytest.mark.docker 14 15 16 def test_missing_key_file(crowdsec, flavor): 17 """Test that cscli and agent can communicate to LAPI with TLS""" 18 19 env = { 20 'CERT_FILE': '/etc/ssl/crowdsec/cert.pem', 21 'USE_TLS': 'true', 22 } 23 24 with crowdsec(flavor=flavor, environment=env, wait_status=Status.EXITED) as cs: 25 cs.wait_for_log("*local API server stopped with error: missing TLS key file*") 26 27 28 def test_missing_cert_file(crowdsec, flavor): 29 """Test that cscli and agent can communicate to LAPI with TLS""" 30 31 env = { 32 'KEY_FILE': '/etc/ssl/crowdsec/cert.key', 33 'USE_TLS': 'true', 34 } 35 36 with crowdsec(flavor=flavor, environment=env, wait_status=Status.EXITED) as cs: 37 cs.wait_for_log("*local API server stopped with error: missing TLS cert file*") 38 39 40 def test_tls_missing_ca(crowdsec, flavor, certs_dir): 41 """Missing CA cert, unknown authority""" 42 43 env = { 44 'CERT_FILE': '/etc/ssl/crowdsec/lapi.crt', 45 'KEY_FILE': '/etc/ssl/crowdsec/lapi.key', 46 'USE_TLS': 'true', 47 'LOCAL_API_URL': 'https://localhost:8080', 48 } 49 50 volumes = { 51 certs_dir(lapi_hostname='lapi'): {'bind': '/etc/ssl/crowdsec', 'mode': 'ro'}, 52 } 53 54 with crowdsec(flavor=flavor, environment=env, volumes=volumes, wait_status=Status.EXITED) as cs: 55 cs.wait_for_log("*certificate signed by unknown authority*") 56 57 58 def test_tls_legacy_var(crowdsec, flavor, certs_dir): 59 """Test server-only certificate, legacy variables""" 60 61 env = { 62 'CACERT_FILE': '/etc/ssl/crowdsec/ca.crt', 63 'CERT_FILE': '/etc/ssl/crowdsec/lapi.crt', 64 'KEY_FILE': '/etc/ssl/crowdsec/lapi.key', 65 'USE_TLS': 'true', 66 'LOCAL_API_URL': 'https://localhost:8080', 67 } 68 69 volumes = { 70 certs_dir(lapi_hostname='lapi'): {'bind': '/etc/ssl/crowdsec', 'mode': 'ro'}, 71 } 72 73 with crowdsec(flavor=flavor, environment=env, volumes=volumes) as cs: 74 cs.wait_for_log("*Starting processing data*") 75 # TODO: wait_for_https 76 cs.wait_for_http(8080, '/health', want_status=None) 77 x = cs.cont.exec_run('cscli lapi status') 78 assert x.exit_code == 0 79 stdout = x.output.decode() 80 assert "You can successfully interact with Local API (LAPI)" in stdout 81 82 83 def test_tls_mutual_monolith(crowdsec, flavor, certs_dir): 84 """Server and client certificates, on the same container""" 85 86 env = { 87 'CACERT_FILE': '/etc/ssl/crowdsec/ca.crt', 88 'LAPI_CERT_FILE': '/etc/ssl/crowdsec/lapi.crt', 89 'LAPI_KEY_FILE': '/etc/ssl/crowdsec/lapi.key', 90 'CLIENT_CERT_FILE': '/etc/ssl/crowdsec/agent.crt', 91 'CLIENT_KEY_FILE': '/etc/ssl/crowdsec/agent.key', 92 'USE_TLS': 'true', 93 'LOCAL_API_URL': 'https://localhost:8080', 94 } 95 96 volumes = { 97 certs_dir(lapi_hostname='lapi'): {'bind': '/etc/ssl/crowdsec', 'mode': 'ro'}, 98 } 99 100 with crowdsec(flavor=flavor, environment=env, volumes=volumes) as cs: 101 cs.wait_for_log("*Starting processing data*") 102 # TODO: wait_for_https 103 cs.wait_for_http(8080, '/health', want_status=None) 104 x = cs.cont.exec_run('cscli lapi status') 105 assert x.exit_code == 0 106 stdout = x.output.decode() 107 assert "You can successfully interact with Local API (LAPI)" in stdout 108 109 110 def test_tls_lapi_var(crowdsec, flavor, certs_dir): 111 """Test server-only certificate, lapi variables""" 112 113 env = { 114 'CACERT_FILE': '/etc/ssl/crowdsec/ca.crt', 115 'LAPI_CERT_FILE': '/etc/ssl/crowdsec/lapi.crt', 116 'LAPI_KEY_FILE': '/etc/ssl/crowdsec/lapi.key', 117 'USE_TLS': 'true', 118 'LOCAL_API_URL': 'https://localhost:8080', 119 } 120 121 volumes = { 122 certs_dir(lapi_hostname='lapi'): {'bind': '/etc/ssl/crowdsec', 'mode': 'ro'}, 123 } 124 125 with crowdsec(flavor=flavor, environment=env, volumes=volumes) as cs: 126 cs.wait_for_log("*Starting processing data*") 127 # TODO: wait_for_https 128 cs.wait_for_http(8080, '/health', want_status=None) 129 x = cs.cont.exec_run('cscli lapi status') 130 assert x.exit_code == 0 131 stdout = x.output.decode() 132 assert "You can successfully interact with Local API (LAPI)" in stdout 133 134 # TODO: bad lapi hostname 135 # the cert is valid, but has a CN that doesn't match the hostname 136 # we must set insecure_skip_verify to true to use it 137 138 139 def test_tls_split_lapi_agent(crowdsec, flavor, certs_dir): 140 """Server-only certificate, split containers""" 141 142 rand = uuid.uuid1() 143 lapiname = 'lapi-' + str(rand) 144 agentname = 'agent-' + str(rand) 145 146 lapi_env = { 147 'USE_TLS': 'true', 148 'CACERT_FILE': '/etc/ssl/crowdsec/ca.crt', 149 'LAPI_CERT_FILE': '/etc/ssl/crowdsec/lapi.crt', 150 'LAPI_KEY_FILE': '/etc/ssl/crowdsec/lapi.key', 151 'AGENT_USERNAME': 'testagent', 152 'AGENT_PASSWORD': 'testpassword', 153 'LOCAL_API_URL': 'https://localhost:8080', 154 } 155 156 agent_env = { 157 'USE_TLS': 'true', 158 'CACERT_FILE': '/etc/ssl/crowdsec/ca.crt', 159 'AGENT_USERNAME': 'testagent', 160 'AGENT_PASSWORD': 'testpassword', 161 'LOCAL_API_URL': f'https://{lapiname}:8080', 162 'DISABLE_LOCAL_API': 'true', 163 'CROWDSEC_FEATURE_DISABLE_HTTP_RETRY_BACKOFF': 'false', 164 } 165 166 volumes = { 167 certs_dir(lapi_hostname=lapiname): {'bind': '/etc/ssl/crowdsec', 'mode': 'ro'}, 168 } 169 170 cs_lapi = crowdsec(flavor=flavor, name=lapiname, environment=lapi_env, volumes=volumes) 171 cs_agent = crowdsec(flavor=flavor, name=agentname, environment=agent_env, volumes=volumes) 172 173 with cs_lapi as lapi: 174 lapi.wait_for_log([ 175 "*(tls) Client Auth Type set to VerifyClientCertIfGiven*", 176 "*CrowdSec Local API listening on *:8080*" 177 ]) 178 # TODO: wait_for_https 179 lapi.wait_for_http(8080, '/health', want_status=None) 180 with cs_agent as agent: 181 agent.wait_for_log("*Starting processing data*") 182 res = agent.cont.exec_run('cscli lapi status') 183 assert res.exit_code == 0 184 stdout = res.output.decode() 185 assert "You can successfully interact with Local API (LAPI)" in stdout 186 res = lapi.cont.exec_run('cscli lapi status') 187 assert res.exit_code == 0 188 stdout = res.output.decode() 189 assert "You can successfully interact with Local API (LAPI)" in stdout 190 191 192 def test_tls_mutual_split_lapi_agent(crowdsec, flavor, certs_dir): 193 """Server and client certificates, split containers""" 194 195 rand = uuid.uuid1() 196 lapiname = 'lapi-' + str(rand) 197 agentname = 'agent-' + str(rand) 198 199 lapi_env = { 200 'USE_TLS': 'true', 201 'CACERT_FILE': '/etc/ssl/crowdsec/ca.crt', 202 'LAPI_CERT_FILE': '/etc/ssl/crowdsec/lapi.crt', 203 'LAPI_KEY_FILE': '/etc/ssl/crowdsec/lapi.key', 204 'LOCAL_API_URL': 'https://localhost:8080', 205 } 206 207 agent_env = { 208 'USE_TLS': 'true', 209 'CACERT_FILE': '/etc/ssl/crowdsec/ca.crt', 210 'CLIENT_CERT_FILE': '/etc/ssl/crowdsec/agent.crt', 211 'CLIENT_KEY_FILE': '/etc/ssl/crowdsec/agent.key', 212 'LOCAL_API_URL': f'https://{lapiname}:8080', 213 'DISABLE_LOCAL_API': 'true', 214 'CROWDSEC_FEATURE_DISABLE_HTTP_RETRY_BACKOFF': 'false', 215 } 216 217 volumes = { 218 certs_dir(lapi_hostname=lapiname): {'bind': '/etc/ssl/crowdsec', 'mode': 'ro'}, 219 } 220 221 cs_lapi = crowdsec(flavor=flavor, name=lapiname, environment=lapi_env, volumes=volumes) 222 cs_agent = crowdsec(flavor=flavor, name=agentname, environment=agent_env, volumes=volumes) 223 224 with cs_lapi as lapi: 225 lapi.wait_for_log([ 226 "*(tls) Client Auth Type set to VerifyClientCertIfGiven*", 227 "*CrowdSec Local API listening on *:8080*" 228 ]) 229 # TODO: wait_for_https 230 lapi.wait_for_http(8080, '/health', want_status=None) 231 with cs_agent as agent: 232 agent.wait_for_log("*Starting processing data*") 233 res = agent.cont.exec_run('cscli lapi status') 234 assert res.exit_code == 0 235 stdout = res.output.decode() 236 assert "You can successfully interact with Local API (LAPI)" in stdout 237 res = lapi.cont.exec_run('cscli lapi status') 238 assert res.exit_code == 0 239 stdout = res.output.decode() 240 assert "You can successfully interact with Local API (LAPI)" in stdout 241 242 243 def test_tls_client_ou(crowdsec, flavor, certs_dir): 244 """Check behavior of client certificate vs AGENTS_ALLOWED_OU""" 245 246 rand = uuid.uuid1() 247 lapiname = 'lapi-' + str(rand) 248 agentname = 'agent-' + str(rand) 249 250 lapi_env = { 251 'USE_TLS': 'true', 252 'CACERT_FILE': '/etc/ssl/crowdsec/ca.crt', 253 'LAPI_CERT_FILE': '/etc/ssl/crowdsec/lapi.crt', 254 'LAPI_KEY_FILE': '/etc/ssl/crowdsec/lapi.key', 255 'LOCAL_API_URL': 'https://localhost:8080', 256 } 257 258 agent_env = { 259 'USE_TLS': 'true', 260 'CACERT_FILE': '/etc/ssl/crowdsec/ca.crt', 261 'CLIENT_CERT_FILE': '/etc/ssl/crowdsec/agent.crt', 262 'CLIENT_KEY_FILE': '/etc/ssl/crowdsec/agent.key', 263 'LOCAL_API_URL': f'https://{lapiname}:8080', 264 'DISABLE_LOCAL_API': 'true', 265 'CROWDSEC_FEATURE_DISABLE_HTTP_RETRY_BACKOFF': 'false', 266 } 267 268 volumes = { 269 certs_dir(lapi_hostname=lapiname, agent_ou='custom-client-ou'): {'bind': '/etc/ssl/crowdsec', 'mode': 'ro'}, 270 } 271 272 cs_lapi = crowdsec(flavor=flavor, name=lapiname, environment=lapi_env, volumes=volumes) 273 cs_agent = crowdsec(flavor=flavor, name=agentname, environment=agent_env, volumes=volumes) 274 275 with cs_lapi as lapi: 276 lapi.wait_for_log([ 277 "*(tls) Client Auth Type set to VerifyClientCertIfGiven*", 278 "*CrowdSec Local API listening on *:8080*" 279 ]) 280 # TODO: wait_for_https 281 lapi.wait_for_http(8080, '/health', want_status=None) 282 with cs_agent as agent: 283 lapi.wait_for_log([ 284 "*client certificate OU (?custom-client-ou?) doesn't match expected OU (?agent-ou?)*", 285 ]) 286 287 lapi_env['AGENTS_ALLOWED_OU'] = 'custom-client-ou' 288 289 # change container names to avoid conflict 290 # recreate certificates because they need the new hostname 291 292 rand = uuid.uuid1() 293 lapiname = 'lapi-' + str(rand) 294 agentname = 'agent-' + str(rand) 295 296 agent_env['LOCAL_API_URL'] = f'https://{lapiname}:8080' 297 298 volumes = { 299 certs_dir(lapi_hostname=lapiname, agent_ou='custom-client-ou'): {'bind': '/etc/ssl/crowdsec', 'mode': 'ro'}, 300 } 301 302 cs_lapi = crowdsec(flavor=flavor, name=lapiname, environment=lapi_env, volumes=volumes) 303 cs_agent = crowdsec(flavor=flavor, name=agentname, environment=agent_env, volumes=volumes) 304 305 with cs_lapi as lapi: 306 lapi.wait_for_log([ 307 "*(tls) Client Auth Type set to VerifyClientCertIfGiven*", 308 "*CrowdSec Local API listening on *:8080*" 309 ]) 310 # TODO: wait_for_https 311 lapi.wait_for_http(8080, '/health', want_status=None) 312 with cs_agent as agent: 313 agent.wait_for_log("*Starting processing data*") 314 res = agent.cont.exec_run('cscli lapi status') 315 assert res.exit_code == 0 316 stdout = res.output.decode() 317 assert "You can successfully interact with Local API (LAPI)" in stdout 318 res = lapi.cont.exec_run('cscli lapi status') 319 assert res.exit_code == 0 320 stdout = res.output.decode() 321 assert "You can successfully interact with Local API (LAPI)" in stdout