- Share info on incidents (anything that impacts CIA according to 44 U.S.C. 3552(b)(2), which could be read incredibly broadly) by amending gov contractual language.
- Zero trust all the things. Make a plan to adopt zero trust as defined by NIST. It's basically defense in depth plus least privilege and seems about as likely to make progress as a result of this order as those general ideas have made in the last 15 years.
- Use FedRAMP to set a cloud security strategy and adopt new cloud security principles. FedRAMP will develop "cloud-security technical reference architecture documentation that illustrates recommended approaches to cloud migration and data protection for agency data collection and reporting."
- CISA to develop "a cloud services governance framework" which sounds like it's to help with gov IR: "identify a range of services and protections available to agencies based on incident severity".
- Gov agencies must identify and report sensitive unclass data. I interpret this as the beginning of a process to adjust thinking from "unclass data doesn't matter" to a more sensible data classification that isn't solely focused on impact to national security.
- MFA and "encryption at rest and in transit" within 180 days for all gov agencies. Reports every 60 days after.
- Train gov agencies on FedRAMP and automate fedramp comms/forms with CSPs. Map compliance requirements onto FedRAMP authorization requirements and rely on the compliance certs instead of re-doing work for FedRAMP.
- Publish secure software supply chain guidelines for "critical software" within 180 days, NIST to publish 90 days after that. Preview of requirements around providing purchaser a software bill of materials, proof of provenance, vuln disclosure etc. Format of BOM to be decided and go into contract language within a year. This whole section is very optimistic.
- Consider consumer labelling for IoT re secure supply chain. This isn't my field but if I was buying one of these devices I would love to know what the security patch frequency and EOL is.
- Build a cyber safety review board that looks at big incidents modeled after the NTSB. This is great.
- CISA to write an incident response playbook for all gov agencies. This might be helpful for agencies that have no such playbooks, and may be a hindrance for those that already have good agency-specific ones. A bad, and likely, outcome would be to force sophisticated response private sector companies to do worse security response because they need to follow the letter of the official government playbook.
- EDR initiative: "CISA, to engage in cyber hunt, detection, and response activities". They get access to all data they need to do it, without any pre-authorization. This seems big. The lawfare blog points out that "Congress actually granted CISA expanded (and clarified) centralized threat-hunting authority in Section 1705 of the fiscal 2021 National Defense Authorization Act". Will we see a gov EDR product that has to be able to run on all gov-owned infra, including cloud?
- Gov agencies need logs from CSPs and to be able to provide those logs to DHS for analysis. Logs need to be signed at export time to prove authenticity as they pass through multiple hands.
- Classified systems should do the same or better as this exec order, without upsetting the existing rules/authorities.
Thursday, June 3, 2021
Quick summary of the cybersecurity executive order
Wednesday, May 12, 2021
Debug logs for nest wifi and google wifi
There are no logs available in the app, but there's quite a lot available from the diagnostic report API. It's in protobuf format, so someone wrote a handy little parser.
go get github.com/benmanns/onhub/cmd/onhubdump ~/go/bin/onhubdump http://192.168.86.1/api/v1/diagnostic-report > logs.json $ jq 'keys' logs.json [ "commandOutputs", "fileLengths", "files", "networkConfig", "stormVersion", "unixTime", "unknown1", "unknownPairs", "version", "wanInfo", "whirlwindVersion" ] $ jq -r '.files[].path' logs.json /etc/lsb-release /etc/resolv.conf /proc/net/arp /proc/slabinfo /proc/meminfo /sys/firmware/log /var/log/debug-log/debug-log /var/log/boot.log /var/log/net.log /var/log/update_engine/update_engine.20200102-000001 /var/log/update_engine/update_engine.20190102-000001 /var/lib/ap/monitor/wan_idle_usage /var/lib/ap/monitor/child_idle_usage /var/lib/ap/health-monitor/wan_connectivity_history /var/log/ap_fresh_dns_messages /var/log/ap_https_server_messages /var/log/critical_events.log /var/log/messages # Get /var/log/messages content: $ jq -r '.files[17].content' logs.json | less
Friday, August 21, 2020
DNS CNAME at the root of a domain
TIL about DNS CNAME "flattening" which is a cloudflare feature that allows you to put a CNAME at the domain root: something that is actually not allowed by the DNS RFC spec.
Why does that matter? I was helping a nonprofit who is using a website hosting company, but the nonprofit has its own domain name. The hosting company tells them to "create a CNAME pointing at blahblah.somehosting.com" which works great, I created "www.example.com" as a CNAME pointing to the hosting address.
But what about the "naked" domain? i.e. if I type example.com like a normal person instead of www.example.com it doesn't resolve, and I don't have anywhere to point it. The cloudflare solution as above is to allow you to do something non-RFC compliant and also put a CNAME record at the root.
Google domains takes a different approach, to solve the problem you can create a subdomain forward for "@" and point it at your www.example.com target. With "forward path" enabled on the entry the URLs will also be preserved through the redirect.
Under the hood Google creates A records for the root domain, so a request for the root domain will resolve to a Google service that then resolves your www.example.com CNAME, establishes a HTTP session with the requester and issues the requesting browser a 301/302 redirect.
Monday, May 25, 2020
Measuring internet bandwidth in a cron script speedtest.net
The speedtest-cli is a python CLI for speedtest.net that has been dockerized by some kind folks.
To get a self-appending CSV just run:
/usr/bin/docker run --rm robertcsapo/speedtest --csv >> speedtest.csvAssuming you run docker passwordless (actually a bad idea for security) you can then add this to your crontab. You may want to consider running it outside of when you need your network bandwidth the most:
0 1 * * * /usr/bin/docker run --rm robertcsapo/speedtest --csv >> /home/myuser/speedtest.csvTo do this with better security don't run docker passwordless, but allow sudo to run this specific command, where the userid 12345 is your userid that you can get by running 'id'. This will allow you to run this command as a non-root user:
myuser ALL=(ALL) NOPASSWD: /usr/bin/docker run -u 12345 --rm robertcsapo/speedtest --csvAnd then in your cron put:
0 1 * * * sudo /usr/bin/docker run -u 12345 --rm robertcsapo/speedtest --csv >> /home/me/speedtest.csv
Thursday, January 2, 2020
Gatekeeper, OPA, rego: notes from testing and debugging policies
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/master/deploy/gatekeeper.yamlWe can see this creates configs and constrainttemplates CRDs:
$ kubectl api-resources | grep gatekeeper.sh configs config.gatekeeper.sh true Config constrainttemplates templates.gatekeeper.sh false ConstraintTemplateThe constraints based off the constraintemplates will themselves will be their own CRDs. In the gatekeeper-system namespace we have the controller manager and webhook that will serve the validating webhook requests from the API server:
$ kubectl get all -n gatekeeper-system NAME READY STATUS RESTARTS AGE pod/gatekeeper-controller-manager-77ff8cc995-sh8xq 1/1 Running 1 6d21h NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/gatekeeper-webhook-service ClusterIP 10.0.5.112The next step is creating constraint templates, here's one that denies all Ingress:443/TCP 6d21h NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/gatekeeper-controller-manager 1/1 1 1 6d21h NAME DESIRED CURRENT READY AGE replicaset.apps/gatekeeper-controller-manager-77ff8cc995 1 1 1 6d21h
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: k8snoexternalservices
spec:
crd:
spec:
names:
kind: K8sNoExternalServices
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8snoexternalservices
violation[{"msg": msg}] {
input.review.kind.kind == "Ingress"
re_match("^(extensions|networking.k8s.io)$", input.review.kind.group)
msg := sprintf("No external service exposure is allowed via ingress: %v", [input.review.object.metadata.name])
}
But how did we know that it was input.review.kind.kind? That's pretty unintuitive. Turns out it's because that's the structure of the object passed to the authenticating webhook. We can see this by creating a constraint template that just blocks everything and logs the full object:
$ cat template.yaml
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: k8sdebugtemplate
spec:
crd:
spec:
names:
kind: K8sDebugTemplate
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package debugging
violation[{"msg": msg}] {
msg := sprintf("Review object: %v", [input.review])
}
$ kubectl apply -f template.yaml
And then apply that to ingress objects:
$ cat constraint.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sDebugTemplate
metadata:
name: debuggingdeny
spec:
match:
kinds:
- apiGroups: ["extensions", "networking.k8s.io"]
kinds: ["Ingress"]
$ kubectl apply -f constraint.yaml
$ kubectl api-resources --api-group=constraints.gatekeeper.sh
NAME SHORTNAMES APIGROUP NAMESPACED KIND
k8sdebugtemplate constraints.gatekeeper.sh false K8sDebugTemplate
Then when we create any ingress we get a full object logged:
$ cat basic-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: basic-ingress
namespace: frontend
spec:
backend:
serviceName: web
servicePort: 8080
If we patch an existing object we get to see old object filled out too:
$ kubectl patch -f basic-ingress.yaml -p '{"spec":{"backend":{"servicePort":8081}}}' 2>&1 | sed s'/): admission webhook.*//' | sed s'/.* Review object: //' | jq
{
"operation": "UPDATE",
"userInfo": {
"username": "me@example.com",
"groups": [
"system:authenticated"
],
"extra": {
"user-assertion.cloud.google.com": [
"XX=="
]
}
},
"object": {
"kind": "Ingress",
"apiVersion": "extensions/v1beta1",
"metadata": {
"annotations": {
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"extensions/v1beta1\",\"kind\":\"Ingress\",\"metadata\":{\"annotations\":{},\"name\":\"basic-ingress\",\"namespace\":\"frontend\"},\"spec\":{\"backend\":{\"serviceName\":\"web\",\"servicePort\":8080}}}\n"
},
"finalizers": [
"finalizers.gatekeeper.sh/sync"
],
"name": "basic-ingress",
"namespace": "frontend",
"uid": "403d4a8f-2db0-11ea-828b-42010a80004c",
"resourceVersion": "2135434",
"generation": 2,
"creationTimestamp": "2020-01-02T22:36:00Z"
},
"spec": {
"backend": {
"serviceName": "web",
"servicePort": 8081
}
},
"status": {
"loadBalancer": {
"ingress": [
{
"ip": "1.2.3.4"
}
]
}
}
},
"oldObject": {
"kind": "Ingress",
"apiVersion": "extensions/v1beta1",
"metadata": {
"name": "basic-ingress",
"namespace": "frontend",
"uid": "403d4a8f-2db0-11ea-828b-42010a80004c",
"resourceVersion": "2135434",
"generation": 1,
"creationTimestamp": "2020-01-02T22:36:00Z",
"annotations": {
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"extensions/v1beta1\",\"kind\":\"Ingress\",\"metadata\":{\"annotations\":{},\"name\":\"basic-ingress\",\"namespace\":\"frontend\"},\"spec\":{\"backend\":{\"serviceName\":\"web\",\"servicePort\":8080}}}\n"
},
"finalizers": [
"finalizers.gatekeeper.sh/sync"
]
},
"spec": {
"backend": {
"serviceName": "web",
"servicePort": 8080
}
},
"status": {
"loadBalancer": {
"ingress": [
{
"ip": "1.2.3.4"
}
]
}
}
},
"uid": "6410973b-2db1-11ea-828b-42010a80004c",
"kind": {
"group": "extensions",
"version": "v1beta1",
"kind": "Ingress"
},
"resource": {
"group": "extensions",
"version": "v1beta1",
"resource": "ingresses"
},
"options": null,
"_unstable": {
"namespace": {
"kind": "Namespace",
"apiVersion": "v1",
"metadata": {
"creationTimestamp": "2019-12-26T23:29:47Z",
"annotations": {
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Namespace\",\"metadata\":{\"annotations\":{},\"name\":\"frontend\"}}\n"
},
"name": "frontend",
"selfLink": "/api/v1/namespaces/frontend",
"uid": "9a96616e-2837-11ea-8e60-42010a80017b",
"resourceVersion": "33039"
},
"spec": {
"finalizers": [
"kubernetes"
]
},
"status": {
"phase": "Active"
}
}
},
"name": "basic-ingress",
"namespace": "frontend",
"dryRun": false
}
We can clean up the debugging rule by deleting the constraint:
kubectl delete k8sdebugtemplate.constraints.gatekeeper.sh debuggingdenyIn terms of policy writing you can test individual policies like this:
$ docker run -v /Users/gcastle/git/gatekeeper/library/general/uniqueingresshost:/tests openpolicyagent/opa test /tests/src.rego /tests/src_test.rego PASS: 12/12The current testing relies on rego assertions as tests, which is a bit of a PITA when you need to create a lot of test permutations on objects because you need to re-create the admission object above to some extent. It's also not super obvious what passing and failing are, it could do with a higher-level test framework:
- "count(results) == 0" means pass this test if there were no violations
- "count(results) == 1" means pass this test if there was exactly one violation
kustomize build templates | kubectl apply -f -
Monday, December 30, 2019
Vim regex examples
# Put a quote in front of the first alphabetic character on each line s!\([a-z]\)!"\1!c
Sunday, March 24, 2019
Finding DNS servers provided by DHCP using network manager on Linux
- Nothing in /etc/resolv.conf
- `dig google.com` shows your machine uses a local DNS server (local dnsmasq)
- /var/lib/dhcp/dhclient.leases looks...wrong
sudo tcpdump -i eth0 -s0 -n 'udp port 53'Which is guaranteed to show you what's going on. It turns out network manager stashes leases under its own directory. If you use
ps aux | grep dhclientyou can see network manager running dhclient (perhaps multiple instances per interface) and the -lf (lease file) parameter will point you at something like
/var/lib/NetworkManager/dhclient-[guid].leaseMystery solved!