Thanks Neil & Daniel. Those solutions are not really "easy" and not really easy to read, but they work. BTW: I think putting the constraints after the body end is less readable then putting them directly into the "function declaration".
Hence I will write according to Neil:
ClearAll[f, x, a, b];
f[x_, syms_List /; VectorQ[syms, Head[#] == Symbol &]] :=
Style[x*syms, Red];
{f[2, 3], f[2, a], f[2, {a, 4}], f[2, {a, b}]}
= {f[2,3],f[2,a],f[2,{a,4}],{2 a,2 b}}
or Daniel's proposal, which is a bit shorter but perhaps less readable:
ClearAll[f, x, a, b];
f[x_, syms_List /; MatchQ[syms, {_Symbol ..}]] := Style[x*syms, Red];
{f[2, 3], f[2, a], f[2, {a, 4}], f[2, {a, b}]}
={f[2,3],f[2,a],f[2,{a,4}],{2 a,2 b}}
Extending those constarints to anything else is clear.