From anjali.maurya01 at indiamart.com Thu Oct 5 07:53:08 2023 From: anjali.maurya01 at indiamart.com (Anjali Maurya) Date: Thu, 5 Oct 2023 13:23:08 +0530 Subject: Request restriction based on IP and url parameter Message-ID: Hi team, We are trying to restrict unauthorized requests at varnish based on a parameter and IP associated with the parameter. The parameter value is present in the URL and the IP is present in the header. So, we want to know if it is possible to implement this restriction based on parameter value and associated IP. We have different values and associated IPs. For example: URL: hostname:port/path?source=mobile&keyword= bags There are 3 IPs associated with mobile source. mobile: IP1, IP2, IP3 So if any request comes with mobile and IP4, that is an unauthorized request and should be blocked at varnish. Can we do this at varnish? If yes, then which approach will be more appropriate handling this at the varnish level or handling it using Java code at the API level? -------------- next part -------------- An HTML attachment was scrubbed... URL: From guillaume.quintard at gmail.com Thu Oct 5 11:42:49 2023 From: guillaume.quintard at gmail.com (Guillaume Quintard) Date: Thu, 5 Oct 2023 13:42:49 +0200 Subject: Request restriction based on IP and url parameter In-Reply-To: References: Message-ID: Hi! It's completely possible, easy and recommended to do that at the varnish level (at least it you want to cache that content) How many ips are you actually allowing, are the actual ips or CIDR blocks? Cheers, It's definitely something I On Thu, Oct 5, 2023, 09:55 Anjali Maurya wrote: > Hi team, > We are trying to restrict unauthorized requests at varnish based on a > parameter and IP associated with the parameter. The parameter value is > present in the URL and the IP is present in the header. So, we want to know > if it is possible to implement this restriction based on parameter value > and associated IP. > We have different values and associated IPs. > > For example: > URL: hostname:port/path?source=mobile&keyword= bags > There are 3 IPs associated with mobile source. > mobile: IP1, IP2, IP3 > > So if any request comes with mobile and IP4, that is an unauthorized > request and should be blocked at varnish. > > Can we do this at varnish? > > If yes, then which approach will be more appropriate handling this at the > varnish level or handling it using Java code at the API level? > _______________________________________________ > varnish-misc mailing list > varnish-misc at varnish-cache.org > https://www.varnish-cache.org/lists/mailman/listinfo/varnish-misc > -------------- next part -------------- An HTML attachment was scrubbed... URL: From uday.polu at indiamart.com Thu Oct 12 17:14:21 2023 From: uday.polu at indiamart.com (Uday Kumar) Date: Thu, 12 Oct 2023 22:44:21 +0530 Subject: Block Unauthorized Requests at Varnish [Code Optimization] Message-ID: Hello everyone, We use varnish in our production environment for caching content. Our Requirement: We are trying to block unauthorized requests at varnish based on the source parameter in the URL and the client IP in the request header. For example: Sample URL: www.hostname:port/path?source=mobile&keyword= bags Let's assume there are 3 IPs [which are allowed to access varnish] associated with the above request of mobile source. i.e *IP1, IP2, IP3* So if any request comes with the source as *mobile *and client-ip as *IP4*, it's treated as an unauthorized request and should be blocked at varnish. What we have done for blocking? *Sample URL:* www.hostname:port/path?source=mobile&keyword= bags Created a map using ACL as below: acl mobile_source{ "IP1"; "IP2"; "IP3"; } If(req.url ~ "source=mobile" && client.ip !~ mobile_source) { return(Synth(403, "varnish access denied!")) } The problem we are facing: The source parameter can have different values like mobile, desktop, laptop, tablet, etc. and each value can have different IPs associated with it. ACL Rules will be as below: acl mobile_source{ "IP1"; "IP2"; "IP3"; } acl desktop_source{ "IP4"; "IP5"; "IP6"; } and so on, If we wanted to block unauthorized access from different source vs IP combinations, we would have to add that many conditions as below. If( (req.url ~ "source=mobile" && client.ip != mobile_source) || (req.url ~ "source=desktop" && client.ip != desktop_source) || (req.url ~ "source=laptop" && client.ip != laptop_source) || (req.url ~ "source=tablet" && client.ip != tablet_source) ){ return(Synth(403, "access denied!")) } This becomes worse, if we have 10's or 20's of source values. Our question: We would like to know if there is any way to optimize the code by removing redundant checks so that we can scale it even if we have many sources vs IP combinations. Thanks & Regards Uday Kumar -------------- next part -------------- An HTML attachment was scrubbed... URL: From guillaume.quintard at gmail.com Thu Oct 12 17:41:47 2023 From: guillaume.quintard at gmail.com (Guillaume Quintard) Date: Thu, 12 Oct 2023 10:41:47 -0700 Subject: Block Unauthorized Requests at Varnish [Code Optimization] In-Reply-To: References: Message-ID: Hi Uday, I don't think those are redundant checks, from what you are showing, they are all justified. Sure, there may be a bunch of them, but you have to go through to them. One thing I would do though is to generate the VCL from a source file, like a YAML one: mobile: - IP1 - IP2 - IP3 desktop: - IP4 - IP5 - IP6 >From that, you can build the VCL without having to manually write "client.ip" or "(req.url ~ "source=" every time. -- Guillaume Quintard On Thu, Oct 12, 2023 at 10:17?AM Uday Kumar wrote: > Hello everyone, > > We use varnish in our production environment for caching content. > > Our Requirement: > > We are trying to block unauthorized requests at varnish based on the > source parameter in the URL and the client IP in the request header. > > For example: > > Sample URL: > > www.hostname:port/path?source=mobile&keyword= bags > > Let's assume there are 3 IPs [which are allowed to access varnish] > associated with the above request of mobile source. > > i.e *IP1, IP2, IP3* > > So if any request comes with the source as *mobile *and client-ip as *IP4*, > it's treated as an unauthorized request and should be blocked at varnish. > > > What we have done for blocking? > > *Sample URL:* > www.hostname:port/path?source=mobile&keyword= bags > > Created a map using ACL as below: > > acl mobile_source{ > > "IP1"; > > "IP2"; > > "IP3"; > > } > > If(req.url ~ "source=mobile" && client.ip !~ mobile_source) { > > return(Synth(403, "varnish access denied!")) > > } > > > The problem we are facing: > > The source parameter can have different values like mobile, desktop, > laptop, tablet, etc. and each value can have different IPs associated with > it. > > ACL Rules will be as below: > > acl mobile_source{ > > "IP1"; > > "IP2"; > > "IP3"; > > } > > acl desktop_source{ > > "IP4"; > > "IP5"; > > "IP6"; > > } > > and so on, > > > If we wanted to block unauthorized access from different source vs IP > combinations, we would have to add that many conditions as below. > > If( > > (req.url ~ "source=mobile" && client.ip != mobile_source) || > > (req.url ~ "source=desktop" && client.ip != desktop_source) || > > (req.url ~ "source=laptop" && client.ip != laptop_source) || > > (req.url ~ "source=tablet" && client.ip != tablet_source) > > ){ > > return(Synth(403, "access denied!")) > > } > > This becomes worse, if we have 10's or 20's of source values. > > Our question: > > We would like to know if there is any way to optimize the code by > removing redundant checks so that we can scale it even if we have many > sources vs IP combinations. > > > Thanks & Regards > Uday Kumar > _______________________________________________ > varnish-misc mailing list > varnish-misc at varnish-cache.org > https://www.varnish-cache.org/lists/mailman/listinfo/varnish-misc > -------------- next part -------------- An HTML attachment was scrubbed... URL: From uday.polu at indiamart.com Thu Oct 12 18:45:50 2023 From: uday.polu at indiamart.com (Uday Kumar) Date: Fri, 13 Oct 2023 00:15:50 +0530 Subject: Block Unauthorized Requests at Varnish [Code Optimization] In-Reply-To: References: Message-ID: Hi Guillaume, I don't think those are redundant checks, from what you are showing, they are all justified. Sure, there may be a bunch of them, but you have to go through to them. By redundant I meant, I have to write multiple checks for each source and list of IPs associated with it. [which would be *worse *if the number of sources are huge] *Example:* If( (req.url ~ "source=mobile" && client.ip != mobile_source) || (req.url ~ "source=desktop" && client.ip != desktop_source) || (req.url ~ "source=laptop" && client.ip != laptop_source) || (req.url ~ "source=tablet" && client.ip != tablet_source) ){ return(Synth(403, "access denied!")) } In the above example, if the request URL is source=tablet *[for which condition is present at the end]*, still I have to check all the above conditions. One thing I would do though is to generate the VCL from a source file, like a YAML one: Didn't understand, can you please elaborate? Thanks & Regards Uday Kumar On Thu, Oct 12, 2023 at 11:11?PM Guillaume Quintard < guillaume.quintard at gmail.com> wrote: > Hi Uday, > > I don't think those are redundant checks, from what you are showing, they > are all justified. Sure, there may be a bunch of them, but you have to go > through to them. > > One thing I would do though is to generate the VCL from a source file, > like a YAML one: > > mobile: > - IP1 > - IP2 > - IP3 > desktop: > - IP4 > - IP5 > - IP6 > > > From that, you can build the VCL without having to manually write > "client.ip" or "(req.url ~ "source=" every time. > > -- > Guillaume Quintard > > > On Thu, Oct 12, 2023 at 10:17?AM Uday Kumar > wrote: > >> Hello everyone, >> >> We use varnish in our production environment for caching content. >> >> Our Requirement: >> >> We are trying to block unauthorized requests at varnish based on the >> source parameter in the URL and the client IP in the request header. >> >> For example: >> >> Sample URL: >> >> www.hostname:port/path?source=mobile&keyword= bags >> >> Let's assume there are 3 IPs [which are allowed to access varnish] >> associated with the above request of mobile source. >> >> i.e *IP1, IP2, IP3* >> >> So if any request comes with the source as *mobile *and client-ip as >> *IP4*, it's treated as an unauthorized request and should be blocked at >> varnish. >> >> >> What we have done for blocking? >> >> *Sample URL:* >> www.hostname:port/path?source=mobile&keyword= bags >> >> Created a map using ACL as below: >> >> acl mobile_source{ >> >> "IP1"; >> >> "IP2"; >> >> "IP3"; >> >> } >> >> If(req.url ~ "source=mobile" && client.ip !~ mobile_source) { >> >> return(Synth(403, "varnish access denied!")) >> >> } >> >> >> The problem we are facing: >> >> The source parameter can have different values like mobile, desktop, >> laptop, tablet, etc. and each value can have different IPs associated with >> it. >> >> ACL Rules will be as below: >> >> acl mobile_source{ >> >> "IP1"; >> >> "IP2"; >> >> "IP3"; >> >> } >> >> acl desktop_source{ >> >> "IP4"; >> >> "IP5"; >> >> "IP6"; >> >> } >> >> and so on, >> >> >> If we wanted to block unauthorized access from different source vs IP >> combinations, we would have to add that many conditions as below. >> >> If( >> >> (req.url ~ "source=mobile" && client.ip != mobile_source) || >> >> (req.url ~ "source=desktop" && client.ip != desktop_source) || >> >> (req.url ~ "source=laptop" && client.ip != laptop_source) || >> >> (req.url ~ "source=tablet" && client.ip != tablet_source) >> >> ){ >> >> return(Synth(403, "access denied!")) >> >> } >> >> This becomes worse, if we have 10's or 20's of source values. >> >> Our question: >> >> We would like to know if there is any way to optimize the code by >> removing redundant checks so that we can scale it even if we have many >> sources vs IP combinations. >> >> >> Thanks & Regards >> Uday Kumar >> _______________________________________________ >> varnish-misc mailing list >> varnish-misc at varnish-cache.org >> https://www.varnish-cache.org/lists/mailman/listinfo/varnish-misc >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From guillaume.quintard at gmail.com Thu Oct 12 19:05:36 2023 From: guillaume.quintard at gmail.com (Guillaume Quintard) Date: Thu, 12 Oct 2023 12:05:36 -0700 Subject: Block Unauthorized Requests at Varnish [Code Optimization] In-Reply-To: References: Message-ID: > In the above example, if the request URL is source=tablet [for which condition is present at the end], still I have to check all the above conditions. That's mainly how computers work, processing will be linear. You *could* create a vmod that packs ACLs into a hashmap to simplify the apparent logic, but you will pay that price developing the vmod, and for a very modest performance gain. If you have less than 50 sources, or even less than a 100, I don't think it's worth agonizing over that kind of optimization (unless you've actually measured and you did see a performance drop). > One thing I would do though is to generate the VCL from a source file, like a YAML one: All I'm saying is that you should focus on increasing the maintainability of the project before worrying about performance. I assume that the VCL is currently committed in a repo somewhere and gets edited every time you need to add a new IP or source. If so, it's not great because editing such repetitive code is error-prone, and therefore you should use templating to create the VCL from a simpler, more maintainable source. Tools like go templates or jinja can provide that feature and save you from repeating yourself when writing configuration. -- Guillaume Quintard On Thu, Oct 12, 2023 at 11:46?AM Uday Kumar wrote: > Hi Guillaume, > > I don't think those are redundant checks, from what you are showing, they > are all justified. Sure, there may be a bunch of them, but you have to go > through to them. > > By redundant I meant, I have to write multiple checks for each source and > list of IPs associated with it. [which would be *worse *if the number of > sources are huge] > > *Example:* > > If( > > (req.url ~ "source=mobile" && client.ip != mobile_source) || > > (req.url ~ "source=desktop" && client.ip != desktop_source) || > > (req.url ~ "source=laptop" && client.ip != laptop_source) || > > (req.url ~ "source=tablet" && client.ip != tablet_source) > > ){ > > return(Synth(403, "access denied!")) > > } > > > In the above example, if the request URL is source=tablet *[for which > condition is present at the end]*, still I have to check all the above > conditions. > > > > > > One thing I would do though is to generate the VCL from a source file, > like a YAML one: > > Didn't understand, can you please elaborate? > > Thanks & Regards > Uday Kumar > > > On Thu, Oct 12, 2023 at 11:11?PM Guillaume Quintard < > guillaume.quintard at gmail.com> wrote: > >> Hi Uday, >> >> I don't think those are redundant checks, from what you are showing, they >> are all justified. Sure, there may be a bunch of them, but you have to go >> through to them. >> >> One thing I would do though is to generate the VCL from a source file, >> like a YAML one: >> >> mobile: >> - IP1 >> - IP2 >> - IP3 >> desktop: >> - IP4 >> - IP5 >> - IP6 >> >> >> From that, you can build the VCL without having to manually write >> "client.ip" or "(req.url ~ "source=" every time. >> >> -- >> Guillaume Quintard >> >> >> On Thu, Oct 12, 2023 at 10:17?AM Uday Kumar >> wrote: >> >>> Hello everyone, >>> >>> We use varnish in our production environment for caching content. >>> >>> Our Requirement: >>> >>> We are trying to block unauthorized requests at varnish based on the >>> source parameter in the URL and the client IP in the request header. >>> >>> For example: >>> >>> Sample URL: >>> >>> www.hostname:port/path?source=mobile&keyword= bags >>> >>> Let's assume there are 3 IPs [which are allowed to access varnish] >>> associated with the above request of mobile source. >>> >>> i.e *IP1, IP2, IP3* >>> >>> So if any request comes with the source as *mobile *and client-ip as >>> *IP4*, it's treated as an unauthorized request and should be blocked at >>> varnish. >>> >>> >>> What we have done for blocking? >>> >>> *Sample URL:* >>> www.hostname:port/path?source=mobile&keyword= bags >>> >>> Created a map using ACL as below: >>> >>> acl mobile_source{ >>> >>> "IP1"; >>> >>> "IP2"; >>> >>> "IP3"; >>> >>> } >>> >>> If(req.url ~ "source=mobile" && client.ip !~ mobile_source) { >>> >>> return(Synth(403, "varnish access denied!")) >>> >>> } >>> >>> >>> The problem we are facing: >>> >>> The source parameter can have different values like mobile, desktop, >>> laptop, tablet, etc. and each value can have different IPs associated with >>> it. >>> >>> ACL Rules will be as below: >>> >>> acl mobile_source{ >>> >>> "IP1"; >>> >>> "IP2"; >>> >>> "IP3"; >>> >>> } >>> >>> acl desktop_source{ >>> >>> "IP4"; >>> >>> "IP5"; >>> >>> "IP6"; >>> >>> } >>> >>> and so on, >>> >>> >>> If we wanted to block unauthorized access from different source vs IP >>> combinations, we would have to add that many conditions as below. >>> >>> If( >>> >>> (req.url ~ "source=mobile" && client.ip != mobile_source) || >>> >>> (req.url ~ "source=desktop" && client.ip != desktop_source) || >>> >>> (req.url ~ "source=laptop" && client.ip != laptop_source) || >>> >>> (req.url ~ "source=tablet" && client.ip != tablet_source) >>> >>> ){ >>> >>> return(Synth(403, "access denied!")) >>> >>> } >>> >>> This becomes worse, if we have 10's or 20's of source values. >>> >>> Our question: >>> >>> We would like to know if there is any way to optimize the code by >>> removing redundant checks so that we can scale it even if we have many >>> sources vs IP combinations. >>> >>> >>> Thanks & Regards >>> Uday Kumar >>> _______________________________________________ >>> varnish-misc mailing list >>> varnish-misc at varnish-cache.org >>> https://www.varnish-cache.org/lists/mailman/listinfo/varnish-misc >>> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From uday.polu at indiamart.com Thu Oct 12 19:35:32 2023 From: uday.polu at indiamart.com (Uday Kumar) Date: Fri, 13 Oct 2023 01:05:32 +0530 Subject: Block Unauthorized Requests at Varnish [Code Optimization] In-Reply-To: References: Message-ID: > That's mainly how computers work, processing will be linear. You *could* create a vmod that packs ACLs into a hashmap to simplify the apparent logic, but you will pay that price developing the vmod, and for a very modest performance gain. If you have less than 50 sources, or even less than 100, I don't think it's worth agonizing over that kind of optimization (unless you've actually measured and you did see a performance drop). Okay, Thanks for your suggestion! > I assume that the VCL is currently committed in a repo somewhere and gets edited every time you need to add a new IP or source. If so, it's not great because editing such repetitive code is error-prone, and therefore you should use templating to create the VCL from a simpler, more maintainable source. Sure, will definitely explore! Thanks & Regards Uday Kumar On Fri, Oct 13, 2023 at 12:35?AM Guillaume Quintard < guillaume.quintard at gmail.com> wrote: > > In the above example, if the request URL is source=tablet [for which > condition is present at the end], still I have to check all the above > conditions. > > That's mainly how computers work, processing will be linear. You *could* > create a vmod that packs ACLs into a hashmap to simplify the apparent > logic, but you will pay that price developing the vmod, and for a very > modest performance gain. If you have less than 50 sources, or even less > than a 100, I don't think it's worth agonizing over that kind of > optimization (unless you've actually measured and you did see a > performance drop). > > > One thing I would do though is to generate the VCL from a source file, > like a YAML one: > > All I'm saying is that you should focus on increasing the maintainability > of the project before worrying about performance. I assume that the VCL is > currently committed in a repo somewhere and gets edited every time you need > to add a new IP or source. If so, it's not great because editing such > repetitive code is error-prone, and therefore you should use templating to > create the VCL from a simpler, more maintainable source. > > Tools like go templates or jinja can provide that feature and save you > from repeating yourself when writing configuration. > > -- > Guillaume Quintard > > > On Thu, Oct 12, 2023 at 11:46?AM Uday Kumar > wrote: > >> Hi Guillaume, >> >> I don't think those are redundant checks, from what you are showing, they >> are all justified. Sure, there may be a bunch of them, but you have to go >> through to them. >> >> By redundant I meant, I have to write multiple checks for each source and >> list of IPs associated with it. [which would be *worse *if the number of >> sources are huge] >> >> *Example:* >> >> If( >> >> (req.url ~ "source=mobile" && client.ip != mobile_source) || >> >> (req.url ~ "source=desktop" && client.ip != desktop_source) || >> >> (req.url ~ "source=laptop" && client.ip != laptop_source) || >> >> (req.url ~ "source=tablet" && client.ip != tablet_source) >> >> ){ >> >> return(Synth(403, "access denied!")) >> >> } >> >> >> In the above example, if the request URL is source=tablet *[for which >> condition is present at the end]*, still I have to check all the above >> conditions. >> >> >> >> >> >> One thing I would do though is to generate the VCL from a source file, >> like a YAML one: >> >> Didn't understand, can you please elaborate? >> >> Thanks & Regards >> Uday Kumar >> >> >> On Thu, Oct 12, 2023 at 11:11?PM Guillaume Quintard < >> guillaume.quintard at gmail.com> wrote: >> >>> Hi Uday, >>> >>> I don't think those are redundant checks, from what you are showing, >>> they are all justified. Sure, there may be a bunch of them, but you have to >>> go through to them. >>> >>> One thing I would do though is to generate the VCL from a source file, >>> like a YAML one: >>> >>> mobile: >>> - IP1 >>> - IP2 >>> - IP3 >>> desktop: >>> - IP4 >>> - IP5 >>> - IP6 >>> >>> >>> From that, you can build the VCL without having to manually write >>> "client.ip" or "(req.url ~ "source=" every time. >>> >>> -- >>> Guillaume Quintard >>> >>> >>> On Thu, Oct 12, 2023 at 10:17?AM Uday Kumar >>> wrote: >>> >>>> Hello everyone, >>>> >>>> We use varnish in our production environment for caching content. >>>> >>>> Our Requirement: >>>> >>>> We are trying to block unauthorized requests at varnish based on the >>>> source parameter in the URL and the client IP in the request header. >>>> >>>> For example: >>>> >>>> Sample URL: >>>> >>>> www.hostname:port/path?source=mobile&keyword= bags >>>> >>>> Let's assume there are 3 IPs [which are allowed to access varnish] >>>> associated with the above request of mobile source. >>>> >>>> i.e *IP1, IP2, IP3* >>>> >>>> So if any request comes with the source as *mobile *and client-ip as >>>> *IP4*, it's treated as an unauthorized request and should be blocked >>>> at varnish. >>>> >>>> >>>> What we have done for blocking? >>>> >>>> *Sample URL:* >>>> www.hostname:port/path?source=mobile&keyword= bags >>>> >>>> Created a map using ACL as below: >>>> >>>> acl mobile_source{ >>>> >>>> "IP1"; >>>> >>>> "IP2"; >>>> >>>> "IP3"; >>>> >>>> } >>>> >>>> If(req.url ~ "source=mobile" && client.ip !~ mobile_source) { >>>> >>>> return(Synth(403, "varnish access denied!")) >>>> >>>> } >>>> >>>> >>>> The problem we are facing: >>>> >>>> The source parameter can have different values like mobile, desktop, >>>> laptop, tablet, etc. and each value can have different IPs associated with >>>> it. >>>> >>>> ACL Rules will be as below: >>>> >>>> acl mobile_source{ >>>> >>>> "IP1"; >>>> >>>> "IP2"; >>>> >>>> "IP3"; >>>> >>>> } >>>> >>>> acl desktop_source{ >>>> >>>> "IP4"; >>>> >>>> "IP5"; >>>> >>>> "IP6"; >>>> >>>> } >>>> >>>> and so on, >>>> >>>> >>>> If we wanted to block unauthorized access from different source vs IP >>>> combinations, we would have to add that many conditions as below. >>>> >>>> If( >>>> >>>> (req.url ~ "source=mobile" && client.ip != mobile_source) || >>>> >>>> (req.url ~ "source=desktop" && client.ip != desktop_source) || >>>> >>>> (req.url ~ "source=laptop" && client.ip != laptop_source) || >>>> >>>> (req.url ~ "source=tablet" && client.ip != tablet_source) >>>> >>>> ){ >>>> >>>> return(Synth(403, "access denied!")) >>>> >>>> } >>>> >>>> This becomes worse, if we have 10's or 20's of source values. >>>> >>>> Our question: >>>> >>>> We would like to know if there is any way to optimize the code by >>>> removing redundant checks so that we can scale it even if we have many >>>> sources vs IP combinations. >>>> >>>> >>>> Thanks & Regards >>>> Uday Kumar >>>> _______________________________________________ >>>> varnish-misc mailing list >>>> varnish-misc at varnish-cache.org >>>> https://www.varnish-cache.org/lists/mailman/listinfo/varnish-misc >>>> >>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From guillaume.quintard at gmail.com Sun Oct 15 02:02:15 2023 From: guillaume.quintard at gmail.com (Guillaume Quintard) Date: Sat, 14 Oct 2023 19:02:15 -0700 Subject: Block Unauthorized Requests at Varnish [Code Optimization] In-Reply-To: References: Message-ID: Hello Uday, Quick follow-up as I realize that templating can be a bit scary when confronted for the first time, and you are far from the first one to be curious about, so I've committed this: https://github.com/varnish/toolbox/tree/master/gotemplate-example It probably won't get you very far, but it should at least get you started, and help understand how templating can make things a tiny be simpler but splitting data from business logic, for example to add more IPs/ACLs or source without edit the VCL manually. Hope that helps. -- Guillaume Quintard On Thu, Oct 12, 2023 at 12:36?PM Uday Kumar wrote: > > That's mainly how computers work, processing will be linear. You *could* > create a vmod that packs ACLs into a hashmap to simplify the apparent > logic, but you will pay that price developing the vmod, and for a very > modest performance gain. If you have less than 50 sources, or even less > than 100, I don't think it's worth agonizing over that kind of optimization > (unless you've actually measured and you did see a performance drop). > > Okay, Thanks for your suggestion! > > > I assume that the VCL is currently committed in a repo somewhere and > gets edited every time you need to add a new IP or source. If so, it's not > great because editing such repetitive code is error-prone, and therefore > you should use templating to create the VCL from a simpler, more > maintainable source. > > Sure, will definitely explore! > > Thanks & Regards > Uday Kumar > > > On Fri, Oct 13, 2023 at 12:35?AM Guillaume Quintard < > guillaume.quintard at gmail.com> wrote: > >> > In the above example, if the request URL is source=tablet [for which >> condition is present at the end], still I have to check all the above >> conditions. >> >> That's mainly how computers work, processing will be linear. You *could* >> create a vmod that packs ACLs into a hashmap to simplify the apparent >> logic, but you will pay that price developing the vmod, and for a very >> modest performance gain. If you have less than 50 sources, or even less >> than a 100, I don't think it's worth agonizing over that kind of >> optimization (unless you've actually measured and you did see a >> performance drop). >> >> > One thing I would do though is to generate the VCL from a source file, >> like a YAML one: >> >> All I'm saying is that you should focus on increasing the maintainability >> of the project before worrying about performance. I assume that the VCL is >> currently committed in a repo somewhere and gets edited every time you need >> to add a new IP or source. If so, it's not great because editing such >> repetitive code is error-prone, and therefore you should use templating to >> create the VCL from a simpler, more maintainable source. >> >> Tools like go templates or jinja can provide that feature and save you >> from repeating yourself when writing configuration. >> >> -- >> Guillaume Quintard >> >> >> On Thu, Oct 12, 2023 at 11:46?AM Uday Kumar >> wrote: >> >>> Hi Guillaume, >>> >>> I don't think those are redundant checks, from what you are showing, >>> they are all justified. Sure, there may be a bunch of them, but you have to >>> go through to them. >>> >>> By redundant I meant, I have to write multiple checks for each source >>> and list of IPs associated with it. [which would be *worse *if the >>> number of sources are huge] >>> >>> *Example:* >>> >>> If( >>> >>> (req.url ~ "source=mobile" && client.ip != mobile_source) || >>> >>> (req.url ~ "source=desktop" && client.ip != desktop_source) || >>> >>> (req.url ~ "source=laptop" && client.ip != laptop_source) || >>> >>> (req.url ~ "source=tablet" && client.ip != tablet_source) >>> >>> ){ >>> >>> return(Synth(403, "access denied!")) >>> >>> } >>> >>> >>> In the above example, if the request URL is source=tablet *[for which >>> condition is present at the end]*, still I have to check all the above >>> conditions. >>> >>> >>> >>> >>> >>> One thing I would do though is to generate the VCL from a source file, >>> like a YAML one: >>> >>> Didn't understand, can you please elaborate? >>> >>> Thanks & Regards >>> Uday Kumar >>> >>> >>> On Thu, Oct 12, 2023 at 11:11?PM Guillaume Quintard < >>> guillaume.quintard at gmail.com> wrote: >>> >>>> Hi Uday, >>>> >>>> I don't think those are redundant checks, from what you are showing, >>>> they are all justified. Sure, there may be a bunch of them, but you have to >>>> go through to them. >>>> >>>> One thing I would do though is to generate the VCL from a source file, >>>> like a YAML one: >>>> >>>> mobile: >>>> - IP1 >>>> - IP2 >>>> - IP3 >>>> desktop: >>>> - IP4 >>>> - IP5 >>>> - IP6 >>>> >>>> >>>> From that, you can build the VCL without having to manually write >>>> "client.ip" or "(req.url ~ "source=" every time. >>>> >>>> -- >>>> Guillaume Quintard >>>> >>>> >>>> On Thu, Oct 12, 2023 at 10:17?AM Uday Kumar >>>> wrote: >>>> >>>>> Hello everyone, >>>>> >>>>> We use varnish in our production environment for caching content. >>>>> >>>>> Our Requirement: >>>>> >>>>> We are trying to block unauthorized requests at varnish based on the >>>>> source parameter in the URL and the client IP in the request header. >>>>> >>>>> For example: >>>>> >>>>> Sample URL: >>>>> >>>>> www.hostname:port/path?source=mobile&keyword= bags >>>>> >>>>> Let's assume there are 3 IPs [which are allowed to access varnish] >>>>> associated with the above request of mobile source. >>>>> >>>>> i.e *IP1, IP2, IP3* >>>>> >>>>> So if any request comes with the source as *mobile *and client-ip as >>>>> *IP4*, it's treated as an unauthorized request and should be blocked >>>>> at varnish. >>>>> >>>>> >>>>> What we have done for blocking? >>>>> >>>>> *Sample URL:* >>>>> www.hostname:port/path?source=mobile&keyword= bags >>>>> >>>>> Created a map using ACL as below: >>>>> >>>>> acl mobile_source{ >>>>> >>>>> "IP1"; >>>>> >>>>> "IP2"; >>>>> >>>>> "IP3"; >>>>> >>>>> } >>>>> >>>>> If(req.url ~ "source=mobile" && client.ip !~ mobile_source) { >>>>> >>>>> return(Synth(403, "varnish access denied!")) >>>>> >>>>> } >>>>> >>>>> >>>>> The problem we are facing: >>>>> >>>>> The source parameter can have different values like mobile, desktop, >>>>> laptop, tablet, etc. and each value can have different IPs associated with >>>>> it. >>>>> >>>>> ACL Rules will be as below: >>>>> >>>>> acl mobile_source{ >>>>> >>>>> "IP1"; >>>>> >>>>> "IP2"; >>>>> >>>>> "IP3"; >>>>> >>>>> } >>>>> >>>>> acl desktop_source{ >>>>> >>>>> "IP4"; >>>>> >>>>> "IP5"; >>>>> >>>>> "IP6"; >>>>> >>>>> } >>>>> >>>>> and so on, >>>>> >>>>> >>>>> If we wanted to block unauthorized access from different source vs IP >>>>> combinations, we would have to add that many conditions as below. >>>>> >>>>> If( >>>>> >>>>> (req.url ~ "source=mobile" && client.ip != mobile_source) || >>>>> >>>>> (req.url ~ "source=desktop" && client.ip != desktop_source) || >>>>> >>>>> (req.url ~ "source=laptop" && client.ip != laptop_source) || >>>>> >>>>> (req.url ~ "source=tablet" && client.ip != tablet_source) >>>>> >>>>> ){ >>>>> >>>>> return(Synth(403, "access denied!")) >>>>> >>>>> } >>>>> >>>>> This becomes worse, if we have 10's or 20's of source values. >>>>> >>>>> Our question: >>>>> >>>>> We would like to know if there is any way to optimize the code by >>>>> removing redundant checks so that we can scale it even if we have many >>>>> sources vs IP combinations. >>>>> >>>>> >>>>> Thanks & Regards >>>>> Uday Kumar >>>>> _______________________________________________ >>>>> varnish-misc mailing list >>>>> varnish-misc at varnish-cache.org >>>>> https://www.varnish-cache.org/lists/mailman/listinfo/varnish-misc >>>>> >>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From uday.polu at indiamart.com Mon Oct 16 06:43:39 2023 From: uday.polu at indiamart.com (Uday Kumar) Date: Mon, 16 Oct 2023 12:13:39 +0530 Subject: Block Unauthorized Requests at Varnish [Code Optimization] In-Reply-To: References: Message-ID: Hello Guillaume, Thank you so much! I'll check it out! Thanks & Regards Uday Kumar On Sun, Oct 15, 2023 at 7:32?AM Guillaume Quintard < guillaume.quintard at gmail.com> wrote: > Hello Uday, > > Quick follow-up as I realize that templating can be a bit scary when > confronted for the first time, and you are far from the first one to be > curious about, so I've committed this: > https://github.com/varnish/toolbox/tree/master/gotemplate-example > It probably won't get you very far, but it should at least get you > started, and help understand how templating can make things a tiny be > simpler but splitting data from business logic, for example to add more > IPs/ACLs or source without edit the VCL manually. > > Hope that helps. > > -- > Guillaume Quintard > > > On Thu, Oct 12, 2023 at 12:36?PM Uday Kumar > wrote: > >> > That's mainly how computers work, processing will be linear. You >> *could* create a vmod that packs ACLs into a hashmap to simplify the >> apparent logic, but you will pay that price developing the vmod, and for a >> very modest performance gain. If you have less than 50 sources, or even >> less than 100, I don't think it's worth agonizing over that kind of >> optimization (unless you've actually measured and you did see a >> performance drop). >> >> Okay, Thanks for your suggestion! >> >> > I assume that the VCL is currently committed in a repo somewhere and >> gets edited every time you need to add a new IP or source. If so, it's not >> great because editing such repetitive code is error-prone, and therefore >> you should use templating to create the VCL from a simpler, more >> maintainable source. >> >> Sure, will definitely explore! >> >> Thanks & Regards >> Uday Kumar >> >> >> On Fri, Oct 13, 2023 at 12:35?AM Guillaume Quintard < >> guillaume.quintard at gmail.com> wrote: >> >>> > In the above example, if the request URL is source=tablet [for which >>> condition is present at the end], still I have to check all the above >>> conditions. >>> >>> That's mainly how computers work, processing will be linear. You *could* >>> create a vmod that packs ACLs into a hashmap to simplify the apparent >>> logic, but you will pay that price developing the vmod, and for a very >>> modest performance gain. If you have less than 50 sources, or even less >>> than a 100, I don't think it's worth agonizing over that kind of >>> optimization (unless you've actually measured and you did see a >>> performance drop). >>> >>> > One thing I would do though is to generate the VCL from a source file, >>> like a YAML one: >>> >>> All I'm saying is that you should focus on increasing the >>> maintainability of the project before worrying about performance. I assume >>> that the VCL is currently committed in a repo somewhere and gets edited >>> every time you need to add a new IP or source. If so, it's not great >>> because editing such repetitive code is error-prone, and therefore you >>> should use templating to create the VCL from a simpler, more maintainable >>> source. >>> >>> Tools like go templates or jinja can provide that feature and save you >>> from repeating yourself when writing configuration. >>> >>> -- >>> Guillaume Quintard >>> >>> >>> On Thu, Oct 12, 2023 at 11:46?AM Uday Kumar >>> wrote: >>> >>>> Hi Guillaume, >>>> >>>> I don't think those are redundant checks, from what you are showing, >>>> they are all justified. Sure, there may be a bunch of them, but you have to >>>> go through to them. >>>> >>>> By redundant I meant, I have to write multiple checks for each source >>>> and list of IPs associated with it. [which would be *worse *if the >>>> number of sources are huge] >>>> >>>> *Example:* >>>> >>>> If( >>>> >>>> (req.url ~ "source=mobile" && client.ip != mobile_source) || >>>> >>>> (req.url ~ "source=desktop" && client.ip != desktop_source) || >>>> >>>> (req.url ~ "source=laptop" && client.ip != laptop_source) || >>>> >>>> (req.url ~ "source=tablet" && client.ip != tablet_source) >>>> >>>> ){ >>>> >>>> return(Synth(403, "access denied!")) >>>> >>>> } >>>> >>>> >>>> In the above example, if the request URL is source=tablet *[for which >>>> condition is present at the end]*, still I have to check all the above >>>> conditions. >>>> >>>> >>>> >>>> >>>> >>>> One thing I would do though is to generate the VCL from a source file, >>>> like a YAML one: >>>> >>>> Didn't understand, can you please elaborate? >>>> >>>> Thanks & Regards >>>> Uday Kumar >>>> >>>> >>>> On Thu, Oct 12, 2023 at 11:11?PM Guillaume Quintard < >>>> guillaume.quintard at gmail.com> wrote: >>>> >>>>> Hi Uday, >>>>> >>>>> I don't think those are redundant checks, from what you are showing, >>>>> they are all justified. Sure, there may be a bunch of them, but you have to >>>>> go through to them. >>>>> >>>>> One thing I would do though is to generate the VCL from a source file, >>>>> like a YAML one: >>>>> >>>>> mobile: >>>>> - IP1 >>>>> - IP2 >>>>> - IP3 >>>>> desktop: >>>>> - IP4 >>>>> - IP5 >>>>> - IP6 >>>>> >>>>> >>>>> From that, you can build the VCL without having to manually write >>>>> "client.ip" or "(req.url ~ "source=" every time. >>>>> >>>>> -- >>>>> Guillaume Quintard >>>>> >>>>> >>>>> On Thu, Oct 12, 2023 at 10:17?AM Uday Kumar >>>>> wrote: >>>>> >>>>>> Hello everyone, >>>>>> >>>>>> We use varnish in our production environment for caching content. >>>>>> >>>>>> Our Requirement: >>>>>> >>>>>> We are trying to block unauthorized requests at varnish based on the >>>>>> source parameter in the URL and the client IP in the request header. >>>>>> >>>>>> For example: >>>>>> >>>>>> Sample URL: >>>>>> >>>>>> www.hostname:port/path?source=mobile&keyword= bags >>>>>> >>>>>> Let's assume there are 3 IPs [which are allowed to access varnish] >>>>>> associated with the above request of mobile source. >>>>>> >>>>>> i.e *IP1, IP2, IP3* >>>>>> >>>>>> So if any request comes with the source as *mobile *and client-ip as >>>>>> *IP4*, it's treated as an unauthorized request and should be blocked >>>>>> at varnish. >>>>>> >>>>>> >>>>>> What we have done for blocking? >>>>>> >>>>>> *Sample URL:* >>>>>> www.hostname:port/path?source=mobile&keyword= bags >>>>>> >>>>>> Created a map using ACL as below: >>>>>> >>>>>> acl mobile_source{ >>>>>> >>>>>> "IP1"; >>>>>> >>>>>> "IP2"; >>>>>> >>>>>> "IP3"; >>>>>> >>>>>> } >>>>>> >>>>>> If(req.url ~ "source=mobile" && client.ip !~ mobile_source) { >>>>>> >>>>>> return(Synth(403, "varnish access denied!")) >>>>>> >>>>>> } >>>>>> >>>>>> >>>>>> The problem we are facing: >>>>>> >>>>>> The source parameter can have different values like mobile, desktop, >>>>>> laptop, tablet, etc. and each value can have different IPs associated with >>>>>> it. >>>>>> >>>>>> ACL Rules will be as below: >>>>>> >>>>>> acl mobile_source{ >>>>>> >>>>>> "IP1"; >>>>>> >>>>>> "IP2"; >>>>>> >>>>>> "IP3"; >>>>>> >>>>>> } >>>>>> >>>>>> acl desktop_source{ >>>>>> >>>>>> "IP4"; >>>>>> >>>>>> "IP5"; >>>>>> >>>>>> "IP6"; >>>>>> >>>>>> } >>>>>> >>>>>> and so on, >>>>>> >>>>>> >>>>>> If we wanted to block unauthorized access from different source vs IP >>>>>> combinations, we would have to add that many conditions as below. >>>>>> >>>>>> If( >>>>>> >>>>>> (req.url ~ "source=mobile" && client.ip != mobile_source) || >>>>>> >>>>>> (req.url ~ "source=desktop" && client.ip != desktop_source) || >>>>>> >>>>>> (req.url ~ "source=laptop" && client.ip != laptop_source) || >>>>>> >>>>>> (req.url ~ "source=tablet" && client.ip != tablet_source) >>>>>> >>>>>> ){ >>>>>> >>>>>> return(Synth(403, "access denied!")) >>>>>> >>>>>> } >>>>>> >>>>>> This becomes worse, if we have 10's or 20's of source values. >>>>>> >>>>>> Our question: >>>>>> >>>>>> We would like to know if there is any way to optimize the code by >>>>>> removing redundant checks so that we can scale it even if we have many >>>>>> sources vs IP combinations. >>>>>> >>>>>> >>>>>> Thanks & Regards >>>>>> Uday Kumar >>>>>> _______________________________________________ >>>>>> varnish-misc mailing list >>>>>> varnish-misc at varnish-cache.org >>>>>> https://www.varnish-cache.org/lists/mailman/listinfo/varnish-misc >>>>>> >>>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From karim.ayari at univ-lyon1.fr Fri Oct 27 06:42:56 2023 From: karim.ayari at univ-lyon1.fr (AYARI KARIM) Date: Fri, 27 Oct 2023 06:42:56 +0000 Subject: varnish and mp4 files In-Reply-To: <8a22d5e0-bfcb-abbe-6c48-11a956b1d364@univ-lyon1.fr> References: , <8a22d5e0-bfcb-abbe-6c48-11a956b1d364@univ-lyon1.fr> Message-ID: Hello, I'm coming back to this subject because after a moment of calm my problem of memory leak and OOM killer with videos is back :( we have videos hosted on Moodle behind varnish which are themselves behind haproxy. version varnish 6.2.1-2ubuntu0.2 amd64 this expression seems to work if (req.url ~ "(?i)^/[^?]+\.mp4($|\?)") { std.log("ispiped:true"); return (pipe); } mp4 files are piped : - VCL_call RECV - VCL_Log ispiped:true - VCL_return pipe - VCL_call HASH - VCL_return lookup first graph is memory usage (total of 16GB), the second one requests fetched, we can see 2 restarts after OOM killing at 16h25 and 18h20 [cid:404902a3-6553-4f34-8b67-7b5b9853b3c3] I don't see any other file extension to add in the expression thank you for your help --- Karim Ayari ________________________________ De : varnish-misc de la part de Karim Ayari Envoy? : mardi 21 f?vrier 2023 09:05 ? : Guillaume Quintard Cc : varnish-misc at varnish-cache.org Objet : Re: varnish and mp4 files sorry I thought I noted it in my previous email. here are the good lines for using pipe : if (req.url ~ "(?i)^/[^?]+\.mp4($|\?)") { std.log("ispiped:true"); return (pipe); } thank you! Karim. Le 20/02/2023 ? 21:50, Guillaume Quintard a ?crit : Looks like Rainer replied directly to you and not to the list, would you mind sharing/highlighting the fix for people having the same issue? Cheers, -- Guillaume Quintard On Mon, Feb 20, 2023 at 12:36 PM Karim Ayari > wrote: thank you both for your replies. I forgot the most important thing: the varnish server has 16 GB of ram and the cache is 1 GB. the cache never seems full [cid:part1.hll3XDne.buey2s0m at univ-lyon1.fr][cid:part2.3Xb0PaGW.FM1miA0J at univ-lyon1.fr] the command used : /usr/sbin/varnishd -j unix,user=vcache -F -a:8080 -T localhost:6082 -f /etc/varnish/moodle.vcl -S /etc/varnish/secret -s malloc,1g -p http_max_hdr=96 I had tried using pipe it didn't work, but Rainer's lines works fine. this solution should suffice because the videos will soon have to be hosted on our video platform. my vcl (file found on github) : .... sub vcl_recv { # Keep client IP if (req.restarts == 0) { if (req.http.x-forwarded-for) { set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip; } else { unset req.http.X-Forwarded-For; set req.http.X-Forwarded-For = client.ip; } } if (req.http.X-Real-IP) { set req.http.X-Forwarded-For = req.http.X-Real-IP; } else { set req.http.X-Forwarded-For = client.ip; } # Only deal with "normal" types if (req.method != "GET" && req.method != "HEAD" && req.method != "PUT" && req.method != "POST" && req.method != "TRACE" && req.method != "OPTIONS" && req.method != "PATCH" && req.method != "DELETE") { /* Non-RFC2616 or CONNECT which is weird. */ /*Why send the packet upstream, while the visitor is using a non-valid HTTP method? */ return (synth(404, "Non-valid HTTP method!")); } # Varnish don't mess with healthchecks if (req.url ~ "^/admin/tool/heartbeat" || req.url ~ "^/healthcheck.php") { return (pass); } # Pipe requests to backup.php straight to backend - prevents problem with progress bar long polling 503 problem # This is here because backup.php is POSTing to itself - Filter before !GET&&!HEAD if (req.url ~ "^/backup/backup.php") { return (pipe); } # Varnish only deals with GET and HEAD by default. If request method is not GET or HEAD, pass request to backend if (req.method != "GET" && req.method != "HEAD") { return (pass); } if (req.http.Cookie) { # Remove any Google Analytics based cookies set req.http.Cookie = regsuball(req.http.Cookie, "^_ga$", ""); set req.http.Cookie = regsuball(req.http.Cookie, "^_gid$", ""); set req.http.Cookie = regsuball(req.http.Cookie, "__gads=[^;]+(; )?", ""); set req.http.Cookie = regsuball(req.http.Cookie, "__qc.=[^;]+(; )?", ""); set req.http.Cookie = regsuball(req.http.Cookie, "__atuv.=[^;]+(; )?", ""); set req.http.Cookie = regsuball(req.http.Cookie, "^;\s*", ""); if (req.http.Cookie ~ "^\s*$") { unset req.http.Cookie; } } ### Rules for Moodle ### # Perform lookup for selected assets that we know are static but Moodle still needs a Cookie if( req.url ~ "^/theme/.+\.?" || req.url ~ "^/webservice/pluginfile.php/.+\.(png|jpg)$" || req.url ~ "^/lib/.+\.(png|jpg|jpeg|gif|css|js|webp)$" || req.url ~ "^/pluginfile.php/[0-9]+/course/.+\.(?i)(png|jpg)$" || req.url ~ "^/pluginfile.php/[0-9]+/theme_moove/.+\.(?i)(png|jpg)$" ) { # Set internal temporary header, based on which we will do things in vcl_backend_response set req.http.X-Long-TTL = "86400"; return (hash); } # Requests containing "Cookie" or "Authorization" headers will not be cached if (req.http.Authorization || req.http.Cookie) { return (pass); } # Almost everything in Moodle correctly serves Cache-Control headers, if # needed, which varnish will honor, but there are some which don't. Rather # than explicitly finding them all and listing them here we just fail safe # and don't cache unknown urls that get this far. return (pass); } sub vcl_backend_response { # Set backend name set beresp.http.X-Backend = beresp.backend.name; if (beresp.http.Cache-Control && bereq.http.X-Long-TTL && beresp.ttl < std.duration(bereq.http.X-Long-TTL + "s", 1s) && !beresp.http.WWW-Authenticate ) { # If max-age < defined in X-Long-TTL header set beresp.http.X-Orig-Pragma = beresp.http.Pragma; unset beresp.http.Pragma; set beresp.http.X-Orig-Cache-Control = beresp.http.Cache-Control; set beresp.http.Cache-Control = "public, max-age="+bereq.http.X-Long-TTL+", no-transform"; set beresp.ttl = std.duration(bereq.http.X-Long-TTL + "s", 1s); unset bereq.http.X-Long-TTL; } else if (!beresp.http.Cache-Control && bereq.http.X-Long-TTL && !beresp.http.WWW-Authenticate ) { set beresp.http.X-Orig-Pragma = beresp.http.Pragma; unset beresp.http.Pragma; set beresp.http.Cache-Control = "public, max-age="+bereq.http.X-Long-TTL+", no-transform"; set beresp.ttl = std.duration(bereq.http.X-Long-TTL + "s", 1s); unset bereq.http.X-Long-TTL; } else { # Don't touch headers if max-age > defined in X-Long-TTL header unset bereq.http.X-Long-TTL; } # Here we set X-Trace header, prepending it to X-Trace header received from backend. Useful for troubleshooting if (beresp.http.x-trace && !beresp.was_304) { set beresp.http.X-Trace = regsub(server.identity, "^([^.]+),?.*$", "\1")+"->"+regsub(beresp.backend.name, "^(.+)\((?:[0-9]{1,3}\.){3}([0-9]{1,3})\)","\1(\2)")+"->"+beresp.http.X-Trace; } else { set beresp.http.X-Trace = regsub(server.identity, "^([^.]+),?.*$", "\1")+"->"+regsub(beresp.backend.name, "^(.+)\((?:[0-9]{1,3}\.){3}([0-9]{1,3})\)","\1(\2)"); } } sub vcl_deliver { # Revert back to original Cache-Control header before delivery to client if (resp.http.X-Orig-Cache-Control) { set resp.http.Cache-Control = resp.http.X-Orig-Cache-Control; unset resp.http.X-Orig-Cache-Control; } # Revert back to original Pragma header before delivery to client if (resp.http.X-Orig-Pragma) { set resp.http.Pragma = resp.http.X-Orig-Pragma; unset resp.http.X-Orig-Pragma; } if (obj.hits > 0) { # Add debug header to see if it's a HIT/MISS and the number of hits, disable when not needed set resp.http.X-Cache = "HIT"; } else { set resp.http.X-Cache = "MISS"; } set resp.http.X-Cache-Hits = obj.hits; # If desired "Via: 1.1 Varnish-v4" response header can be removed from response unset resp.http.Via; unset resp.http.Server; return (deliver); } sub vcl_backend_error { # More comprehensive varnish error page. Display time, instance hostname, host header, url for easier troubleshooting. set beresp.http.Content-Type = "text/html; charset=utf-8"; set beresp.http.Retry-After = "5"; synthetic( {" "} + beresp.status + " " + beresp.reason + {"

Error "} + beresp.status + " " + beresp.reason + {"

"} + beresp.reason + {"

Guru Meditation:

Time: "} + now + {"

Node: "} + server.hostname + {"

Host: "} + bereq.http.host + {"

URL: "} + bereq.url + {"

XID: "} + bereq.xid + {"


Varnish cache server "} ); return (deliver); } sub vcl_synth { #Redirect using '301 - Permanent Redirect', permanent redirect if (resp.status == 851) { set resp.http.Location = req.http.x-redir; set resp.http.X-Varnish-Redirect = true; set resp.status = 301; return (deliver); } #Redirect using '302 - Found', temporary redirect if (resp.status == 852) { set resp.http.Location = req.http.x-redir; set resp.http.X-Varnish-Redirect = true; set resp.status = 302; return (deliver); } #Redirect using '307 - Temporary Redirect', !GET&&!HEAD requests, dont change method on redirected requests if (resp.status == 857) { set resp.http.Location = req.http.x-redir; set resp.http.X-Varnish-Redirect = true; set resp.status = 307; return (deliver); } #Respond with 403 - Forbidden if (resp.status == 863) { set resp.http.X-Varnish-Error = true; set resp.status = 403; return (deliver); } } sub vcl_purge { if (req.method != "PURGE") { set req.http.X-Purge = "Yes"; return (restart); } } ... Le 20/02/2023 ? 18:55, Guillaume Quintard a ?crit : Hello Karim, You VCL would be useful to debug this (as well as the command line you are running Varnish with), but it sounds like Varnish is using the Transient storage (https://varnish-cache.org/docs/trunk/users-guide/storage-backends.html#transient-storage) to store the file, and as the storage isn't bounded, it explodes. We can fix this in a couple of ways, from storing the file in the regular cache storage, to using pipe, to waiting a few days for https://github.com/varnishcache/varnish-cache/pull/3572#issuecomment-1305736643 to be released. Question is: should that file be cached? Cheers, -- Guillaume Quintard On Mon, Feb 20, 2023 at 7:14 AM Karim Ayari > wrote: Hi! I am currently experiencing a memory load problem with video playback. here is the infrastructure : client --> haproxy --> varnish --> moodle workers (x5) a teacher uploaded a 400MB video to Moodle, when we start playing the video with browser player, Varnish consumes all the memory until it runs out and oom killer to kill varnishd. i have no configuration for mp4 files in my vcl file, so by default they are not hidden (?). I can't find a solution :( I can give my vcl file if necessary. (I am a beginner on varnish :)) thank you for your support. Karim _______________________________________________ varnish-misc mailing list varnish-misc at varnish-cache.org https://www.varnish-cache.org/lists/mailman/listinfo/varnish-misc -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: psCUnmMoUHIDeDdC.png Type: image/png Size: 49126 bytes Desc: psCUnmMoUHIDeDdC.png URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: dKCCKWp0GtXF0le5.png Type: image/png Size: 42447 bytes Desc: dKCCKWp0GtXF0le5.png URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 48105 bytes Desc: pastedImage.png URL: From ruud.peters at kpn.com Fri Oct 27 07:52:23 2023 From: ruud.peters at kpn.com (ruud.peters at kpn.com) Date: Fri, 27 Oct 2023 07:52:23 +0000 Subject: Cache poisening Message-ID: Hi, Is there anything known that Varnish has problems with cache poisening? And if yes, how can this be avoided in the config? We are running a old version of Varnish (varnish-4.1.8 revision d266ac5c6) Met vriendelijke groet / With kind regards, [cid:image001.png at 01DA08BB.1B030420] Ruud Peters Technisch Beheerder TAM3 Integration SA DevOps 3 Email : ruud.peters at kpn.com Phone : +31630736741 Stationsplein 18 6221 BT, Maastricht (On Mondays and Thursdays I'm in the office until about 14:00) Handelsregister KvK Den Haag Nr. 27124701 [twitter] [facebook] [linkedin] [youtube] -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: image001.png Type: image/png Size: 5940 bytes Desc: image001.png URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: image002.jpg Type: image/jpeg Size: 938 bytes Desc: image002.jpg URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: image003.jpg Type: image/jpeg Size: 915 bytes Desc: image003.jpg URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: image004.jpg Type: image/jpeg Size: 945 bytes Desc: image004.jpg URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: image005.jpg Type: image/jpeg Size: 896 bytes Desc: image005.jpg URL: From guillaume.quintard at gmail.com Fri Oct 27 16:24:44 2023 From: guillaume.quintard at gmail.com (Guillaume Quintard) Date: Fri, 27 Oct 2023 09:24:44 -0700 Subject: varnish and mp4 files In-Reply-To: References: <8a22d5e0-bfcb-abbe-6c48-11a956b1d364@univ-lyon1.fr> Message-ID: Hi, Check the varnishncsa logs to see if something fishy stands out, or even just the backend logs. Notably, try varnishncsa -q 'not VCL_call eq PIPE' That will give you all the transactions that go through vcl_pipe{} (more log tips here: https://docs.varnish-software.com/tutorials/vsl-query/) If that doesn't work we can try to filter big files and whatnot. -- Guillaume Quintard On Thu, Oct 26, 2023 at 11:47?PM AYARI KARIM wrote: > Hello, > > > I'm coming back to this subject because after a moment of calm my problem > of memory leak and OOM killer with videos is back :( > > we have videos hosted on Moodle behind varnish which are themselves behind > haproxy. > > > version varnish > 6.2.1-2ubuntu0.2 amd64 > > > this expression seems to work > > > *if (req.url ~ "(?i)^/[^?]+\.mp4($|\?)") {* > * std.log("ispiped:true");* > * return (pipe);* > *}* > > > mp4 files are piped : > > > > > > > *- VCL_call RECV - VCL_Log ispiped:true - > VCL_return pipe - VCL_call HASH - VCL_return lookup* > > first graph is memory usage (total of 16GB), the second one requests > fetched, we can see 2 restarts after OOM killing at 16h25 and 18h20 > > > > > I don't see any other file extension to add in the expression > > > thank you for your help > > > --- > Karim Ayari > > ------------------------------ > *De :* varnish-misc univ-lyon1.fr at varnish-cache.org> de la part de Karim Ayari < > karim.ayari at univ-lyon1.fr> > *Envoy? :* mardi 21 f?vrier 2023 09:05 > *? :* Guillaume Quintard > *Cc :* varnish-misc at varnish-cache.org > *Objet :* Re: varnish and mp4 files > > > sorry I thought I noted it in my previous email. > here are the good lines for using pipe : > > > *if (req.url ~ "(?i)^/[^?]+\.mp4($|\?)") {* > * std.log("ispiped:true");* > * return (pipe);* > *}* > > thank you! > > Karim. > > > > Le 20/02/2023 ? 21:50, Guillaume Quintard a ?crit : > > Looks like Rainer replied directly to you and not to the list, would you > mind sharing/highlighting the fix for people having the same issue? > > Cheers, > > -- > Guillaume Quintard > > > On Mon, Feb 20, 2023 at 12:36 PM Karim Ayari > wrote: > >> thank you both for your replies. I forgot the most important thing: the >> varnish server has 16 GB of ram and the cache is 1 GB. >> >> the cache never seems full >> >> >> the command used : */usr/sbin/varnishd -j unix,user=vcache -F -a:8080 -T >> localhost:6082 -f /etc/varnish/moodle.vcl -S /etc/varnish/secret -s >> malloc,1g* *-p http_max_hdr=96* >> >> I had tried using pipe it didn't work, but Rainer's lines works fine. >> >> this solution should suffice because the videos will soon have to be >> hosted on our video platform. >> >> >> my vcl (file found on github) : >> >> >> >> *.... * >> >> *sub vcl_recv {* >> >> * # Keep client IP* >> * if (req.restarts == 0) {* >> * if (req.http.x-forwarded-for) {* >> * set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", >> " + client.ip;* >> * } else {* >> * unset req.http.X-Forwarded-For;* >> * set req.http.X-Forwarded-For = client.ip;* >> * }* >> * }* >> >> * if (req.http.X-Real-IP) {* >> * set req.http.X-Forwarded-For = req.http.X-Real-IP;* >> * } else {* >> * set req.http.X-Forwarded-For = client.ip;* >> * }* >> >> * # Only deal with "normal" types* >> * if (req.method != "GET" &&* >> * req.method != "HEAD" &&* >> * req.method != "PUT" &&* >> * req.method != "POST" &&* >> * req.method != "TRACE" &&* >> * req.method != "OPTIONS" &&* >> * req.method != "PATCH" &&* >> * req.method != "DELETE") {* >> * /* Non-RFC2616 or CONNECT which is weird. */* >> * /*Why send the packet upstream, while the visitor is using a >> non-valid HTTP method? */* >> * return (synth(404, "Non-valid HTTP method!"));* >> * }* >> >> * # Varnish don't mess with healthchecks* >> * if (req.url ~ "^/admin/tool/heartbeat" || req.url ~ >> "^/healthcheck.php") {* >> * return (pass);* >> * }* >> * # Pipe requests to backup.php straight to backend - prevents problem >> with progress bar long polling 503 problem* >> * # This is here because backup.php is POSTing to itself - Filter >> before !GET&&!HEAD* >> * if (req.url ~ "^/backup/backup.php")* >> * {* >> * return (pipe);* >> * }* >> >> * # Varnish only deals with GET and HEAD by default. If request method >> is not GET or HEAD, pass request to backend* >> * if (req.method != "GET" && req.method != "HEAD") {* >> * return (pass);* >> * }* >> >> * if (req.http.Cookie) {* >> * # Remove any Google Analytics based cookies* >> * set req.http.Cookie = regsuball(req.http.Cookie, "^_ga$", "");* >> * set req.http.Cookie = regsuball(req.http.Cookie, "^_gid$", "");* >> * set req.http.Cookie = regsuball(req.http.Cookie, "__gads=[^;]+(; >> )?", "");* >> * set req.http.Cookie = regsuball(req.http.Cookie, "__qc.=[^;]+(; >> )?", "");* >> * set req.http.Cookie = regsuball(req.http.Cookie, "__atuv.=[^;]+(; >> )?", "");* >> * set req.http.Cookie = regsuball(req.http.Cookie, "^;\s*", "");* >> * if (req.http.Cookie ~ "^\s*$") {* >> * unset req.http.Cookie;* >> * }* >> * }* >> >> * ### Rules for Moodle ###* >> >> * # Perform lookup for selected assets that we know are static but >> Moodle still needs a Cookie* >> * if( req.url ~ "^/theme/.+\.?" ||* >> * req.url ~ "^/webservice/pluginfile.php/.+\.(png|jpg)$" ||* >> * req.url ~ "^/lib/.+\.(png|jpg|jpeg|gif|css|js|webp)$" ||* >> * req.url ~ "^/pluginfile.php/[0-9]+/course/.+\.(?i)(png|jpg)$" >> ||* >> * req.url ~ "^/pluginfile.php/[0-9]+/theme_moove/.+\.(?i)(png|jpg)$"* >> * )* >> * {* >> * # Set internal temporary header, based on which we will do >> things in vcl_backend_response* >> * set req.http.X-Long-TTL = "86400";* >> * return (hash);* >> * }* >> * # Requests containing "Cookie" or "Authorization" headers will not >> be cached* >> * if (req.http.Authorization || req.http.Cookie) {* >> * return (pass);* >> * }* >> * # Almost everything in Moodle correctly serves Cache-Control >> headers, if* >> * # needed, which varnish will honor, but there are some which don't. >> Rather* >> * # than explicitly finding them all and listing them here we just >> fail safe* >> * # and don't cache unknown urls that get this far.* >> * return (pass);* >> >> *}* >> >> *sub vcl_backend_response {* >> * # Set backend name* >> * set beresp.http.X-Backend = beresp.backend.name >> ;* >> >> * if (beresp.http.Cache-Control && bereq.http.X-Long-TTL && beresp.ttl >> < std.duration(bereq.http.X-Long-TTL + "s", 1s) && >> !beresp.http.WWW-Authenticate ) { # If max-age < defined in X-Long-TTL >> header* >> * set beresp.http.X-Orig-Pragma = beresp.http.Pragma; unset >> beresp.http.Pragma;* >> * set beresp.http.X-Orig-Cache-Control = >> beresp.http.Cache-Control;* >> * set beresp.http.Cache-Control = "public, >> max-age="+bereq.http.X-Long-TTL+", no-transform";* >> * set beresp.ttl = std.duration(bereq.http.X-Long-TTL + "s", 1s);* >> * unset bereq.http.X-Long-TTL;* >> * }* >> * else if (!beresp.http.Cache-Control && bereq.http.X-Long-TTL && >> !beresp.http.WWW-Authenticate ) {* >> * set beresp.http.X-Orig-Pragma = beresp.http.Pragma; unset >> beresp.http.Pragma;* >> * set beresp.http.Cache-Control = "public, >> max-age="+bereq.http.X-Long-TTL+", no-transform";* >> * set beresp.ttl = std.duration(bereq.http.X-Long-TTL + "s", 1s);* >> * unset bereq.http.X-Long-TTL;* >> * }* >> * else { # Don't touch headers if max-age > defined in X-Long-TTL >> header* >> * unset bereq.http.X-Long-TTL;* >> * }* >> * # Here we set X-Trace header, prepending it to X-Trace header >> received from backend. Useful for troubleshooting* >> * if (beresp.http.x-trace && !beresp.was_304) {* >> * set beresp.http.X-Trace = regsub(server.identity, >> "^([^.]+),?.*$", "\1")+"->"+regsub(beresp.backend.name >> , >> "^(.+)\((?:[0-9]{1,3}\.){3}([0-9]{1,3})\)","\1(\2)")+"->"+beresp.http.X-Trace;* >> * }* >> * else {* >> * set beresp.http.X-Trace = regsub(server.identity, >> "^([^.]+),?.*$", "\1")+"->"+regsub(beresp.backend.name >> , >> "^(.+)\((?:[0-9]{1,3}\.){3}([0-9]{1,3})\)","\1(\2)");* >> * }* >> *}* >> >> *sub vcl_deliver {* >> >> *# Revert back to original Cache-Control header before delivery to client* >> * if (resp.http.X-Orig-Cache-Control)* >> * {* >> * set resp.http.Cache-Control = resp.http.X-Orig-Cache-Control;* >> * unset resp.http.X-Orig-Cache-Control;* >> * }* >> * # Revert back to original Pragma header before delivery to client* >> * if (resp.http.X-Orig-Pragma)* >> * {* >> * set resp.http.Pragma = resp.http.X-Orig-Pragma;* >> * unset resp.http.X-Orig-Pragma;* >> * }* >> >> * if (obj.hits > 0) { # Add debug header to see if it's a HIT/MISS and >> the number of hits, disable when not needed* >> * set resp.http.X-Cache = "HIT";* >> * } else {* >> * set resp.http.X-Cache = "MISS";* >> * }* >> * set resp.http.X-Cache-Hits = obj.hits;* >> >> *# If desired "Via: 1.1 Varnish-v4" response header can be removed from >> response* >> * unset resp.http.Via;* >> * unset resp.http.Server;* >> >> * return (deliver);* >> *}* >> >> *sub vcl_backend_error {* >> * # More comprehensive varnish error page. Display time, instance >> hostname, host header, url for easier troubleshooting.* >> * set beresp.http.Content-Type = "text/html; charset=utf-8";* >> * set beresp.http.Retry-After = "5";* >> * synthetic( {"* >> * * >> * * >> * * >> * "} + beresp.status + " " + beresp.reason + {"* >> * * >> * * >> *

Error "} + beresp.status + " " + beresp.reason + {"

* >> *

"} + beresp.reason + {"

* >> *

Guru Meditation:

* >> *

Time: "} + now + {"

* >> *

Node: "} + server.hostname + {"

* >> *

Host: "} + bereq.http.host + {"

* >> *

URL: "} + bereq.url + {"

* >> *

XID: "} + bereq.xid + {"

* >> *
* >> *

Varnish cache server* >> * * >> * * >> * "} );* >> * return (deliver);* >> *}* >> >> *sub vcl_synth {* >> * #Redirect using '301 - Permanent Redirect', permanent redirect* >> * if (resp.status == 851) { * >> * set resp.http.Location = req.http.x-redir;* >> * set resp.http.X-Varnish-Redirect = true;* >> * set resp.status = 301;* >> * return (deliver);* >> * }* >> * #Redirect using '302 - Found', temporary redirect* >> * if (resp.status == 852) { * >> * set resp.http.Location = req.http.x-redir;* >> * set resp.http.X-Varnish-Redirect = true;* >> * set resp.status = 302;* >> * return (deliver);* >> * }* >> * #Redirect using '307 - Temporary Redirect', !GET&&!HEAD requests, >> dont change method on redirected requests* >> * if (resp.status == 857) { * >> * set resp.http.Location = req.http.x-redir;* >> * set resp.http.X-Varnish-Redirect = true;* >> * set resp.status = 307;* >> * return (deliver);* >> * }* >> * #Respond with 403 - Forbidden* >> * if (resp.status == 863) {* >> * set resp.http.X-Varnish-Error = true;* >> * set resp.status = 403;* >> * return (deliver);* >> * }* >> *}* >> >> *sub vcl_purge {* >> * if (req.method != "PURGE") {* >> * set req.http.X-Purge = "Yes";* >> * return (restart);* >> * }* >> *}* >> >> ... >> >> >> Le 20/02/2023 ? 18:55, Guillaume Quintard a ?crit : >> >> Hello Karim, >> >> You VCL would be useful to debug this (as well as the command line you >> are running Varnish with), but it sounds like Varnish is using the >> Transient storage ( >> https://varnish-cache.org/docs/trunk/users-guide/storage-backends.html#transient-storage) >> to store the file, and as the storage isn't bounded, it explodes. >> We can fix this in a couple of ways, from storing the file in the regular >> cache storage, to using pipe, to waiting a few days for >> https://github.com/varnishcache/varnish-cache/pull/3572#issuecomment-1305736643 >> to be released. >> >> Question is: should that file be cached? >> >> Cheers, >> >> -- >> Guillaume Quintard >> >> >> On Mon, Feb 20, 2023 at 7:14 AM Karim Ayari >> wrote: >> >>> Hi! >>> >>> I am currently experiencing a memory load problem with video playback. >>> >>> here is the infrastructure : >>> >>> client --> haproxy --> varnish --> moodle workers (x5) >>> >>> a teacher uploaded a 400MB video to Moodle, when we start playing the >>> video with browser player, Varnish consumes all the memory until it >>> runs out and oom killer to kill varnishd. i have no configuration for >>> mp4 files in my vcl file, so by default they are not hidden (?). I >>> can't find a solution :( >>> >>> I can give my vcl file if necessary. >>> >>> (I am a beginner on varnish :)) >>> >>> thank you for your support. >>> >>> Karim >>> _______________________________________________ >>> varnish-misc mailing list >>> varnish-misc at varnish-cache.org >>> https://www.varnish-cache.org/lists/mailman/listinfo/varnish-misc >>> >> _______________________________________________ > varnish-misc mailing list > varnish-misc at varnish-cache.org > https://www.varnish-cache.org/lists/mailman/listinfo/varnish-misc > -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: psCUnmMoUHIDeDdC.png Type: image/png Size: 49126 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: dKCCKWp0GtXF0le5.png Type: image/png Size: 42447 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 48105 bytes Desc: not available URL: From guillaume.quintard at gmail.com Sun Oct 29 23:01:56 2023 From: guillaume.quintard at gmail.com (Guillaume Quintard) Date: Sun, 29 Oct 2023 16:01:56 -0700 Subject: Cache poisening In-Reply-To: References: Message-ID: Hi Rudd, Sorry for the delay, for some reason your email ended up in my spam folder, I just saw it today. Cache poisoning is a vast subject, and in absence of more context the answer to your question is probably going to be "yes, but no but still, intrinsically yes". Yes, because you can mess up your configuration with something like: sub vcl_hash { hash_data("foo"); return(lookup); } and boom, all objects are basically going to be cached under the same cache key, which is super bad, don't do that. The freedom you get through configuration can turn against you. Here's my favorite example to explain it: sub vcl_hash { hash_data(req.url); hash_data(req.http.host); if (req.http.a) { hash_data(req.http.a); } if (req.http.b) { hash_data(req.http.b) } return(lookup); } Which isn't nearly as dumb as the original example, but which will hash these two requests the same way: curl example.com/foo -H "a: bar" curl example.com/foo -H "b: bar" And if somebody knows about how you hash your object and there's a similar flaw in the hashing logic, you can get cache No, because Varnish is an extremely secure piece of software with an excellent security track record and I don't think it ever got a CVE that poisoned the cache. not to say it can't/won't happen, but sometimes past performance is a good indicator of future results. So, even though the software is safe and secure, you can still shoot yourself in the foot if you want to (or are not careful). Thousands of cases of cache poisoning happens yearly because somebody forgot to tell their CDN that the querystring needs to be part of the cache key AND sorted. Hopefully this helps, let me know if you have more context to narrow the scope of that very vast topic :-) Ah, and while I'm here: please don't use massively antiquated Varnish versions. 4.1 has been EOL a while ago, it's really not recommended to use. Cheers, -- Guillaume Quintard On Fri, Oct 27, 2023 at 12:54?AM wrote: > Hi, > > > > Is there anything known that Varnish has problems with cache poisening? > And if yes, how can this be avoided in the config? > > We are running a old version of Varnish (varnish-4.1.8 revision d266ac5c6) > > > > > > Met vriendelijke groet / With kind regards, > > > > > > *Ruud Peters* > > *Technisch Beheerder TAM3* > > Integration SA DevOps 3 > > > > Email : ruud.peters at kpn.com > > Phone : +31630736741 > > > > Stationsplein 18 6221 BT, Maastricht > > > (On Mondays and Thursdays I?m in the office until about 14:00) > > > > Handelsregister KvK Den Haag > > Nr. 27124701 > > > > [image: twitter] > [image: > facebook] > [image: > linkedin] > [image: > youtube] > > > > _______________________________________________ > varnish-misc mailing list > varnish-misc at varnish-cache.org > https://www.varnish-cache.org/lists/mailman/listinfo/varnish-misc > -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: image001.png Type: image/png Size: 5940 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: image002.jpg Type: image/jpeg Size: 938 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: image003.jpg Type: image/jpeg Size: 915 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: image004.jpg Type: image/jpeg Size: 945 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: image005.jpg Type: image/jpeg Size: 896 bytes Desc: not available URL: